|
|
@@ -0,0 +1,467 @@
|
|
|
+<template>
|
|
|
+ <Touchable
|
|
|
+ :innerStyle="{
|
|
|
+ ...currentStyle.style,
|
|
|
+ ...innerStyle,
|
|
|
+ }"
|
|
|
+ :innerClass="['nana-button', props.block ? 'nana-button-block' : 'nana-button-auto']"
|
|
|
+ center
|
|
|
+ direction="row"
|
|
|
+ v-bind="viewProps"
|
|
|
+ :pressedColor="finalPressedColor"
|
|
|
+ :touchable="touchable && !loading"
|
|
|
+ @state="(v) => state = v"
|
|
|
+ @click="emit('click', $event)"
|
|
|
+ >
|
|
|
+ <slot name="leftIcon">
|
|
|
+ <ActivityIndicator
|
|
|
+ v-if="loading"
|
|
|
+ :size="selectStyleType(size, 'medium', FonstSizes)"
|
|
|
+ :color="themeContext.resolveThemeColor(loadingColor) || currentStyle.color"
|
|
|
+ :innerStyle="{
|
|
|
+ marginRight: iconMargin ? '10rpx': undefined,
|
|
|
+ }"
|
|
|
+ />
|
|
|
+ <Icon
|
|
|
+ v-else-if="icon"
|
|
|
+ :icon="icon"
|
|
|
+ :size="selectStyleType(size, 'medium', FonstSizes)"
|
|
|
+ :color="currentStyle.color"
|
|
|
+ :innerStyle="{
|
|
|
+ marginRight: iconMargin ? '10rpx': undefined,
|
|
|
+ }"
|
|
|
+ v-bind="iconProps"
|
|
|
+ />
|
|
|
+ </slot>
|
|
|
+ <slot>
|
|
|
+ <Text
|
|
|
+ :color="textColorFinal"
|
|
|
+ :fontSize="selectStyleType(size, 'medium', FonstSizes)"
|
|
|
+ :fontWeight="type === 'text' ? 'bold' : undefined"
|
|
|
+ :innerStyle="textStyle"
|
|
|
+ :text="currentText"
|
|
|
+ />
|
|
|
+ </slot>
|
|
|
+ <slot name="rightIcon">
|
|
|
+ <Icon
|
|
|
+ v-if="rightIcon"
|
|
|
+ :icon="rightIcon"
|
|
|
+ :size="selectStyleType(size, 'medium', FonstSizes)"
|
|
|
+ :color="currentStyle.color"
|
|
|
+ :innerStyle="{
|
|
|
+ marginLeft: iconMargin ? '10rpx' : undefined,
|
|
|
+ }"
|
|
|
+ v-bind="rightIconProps"
|
|
|
+ />
|
|
|
+ </slot>
|
|
|
+ </Touchable>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, ref } from 'vue';
|
|
|
+import { useTheme, type ViewStyle } from '../theme/ThemeDefine';
|
|
|
+import { configPadding, DynamicColor, DynamicSize, selectStyleType } from '../theme/ThemeTools';
|
|
|
+import type { IconProps } from './Icon.vue';
|
|
|
+import type { FlexProps } from '../layout/FlexView.vue';
|
|
|
+import Text from './Text.vue';
|
|
|
+import ActivityIndicator from './ActivityIndicator.vue';
|
|
|
+import Icon from './Icon.vue';
|
|
|
+import Touchable from '../feedback/Touchable.vue';
|
|
|
+
|
|
|
+export type ButtomType = 'default'|'primary'|'success'|'warning'|'danger'|'custom'|'text';
|
|
|
+export type ButtomSizeType = 'small'|'medium'|'large'|'larger'|'mini';
|
|
|
+
|
|
|
+export interface ButtonProp {
|
|
|
+ /**
|
|
|
+ * 按钮文字
|
|
|
+ */
|
|
|
+ text?: string,
|
|
|
+ /**
|
|
|
+ * 按钮支持 default、primary、success、warning、danger、custom 自定义 六种类型
|
|
|
+ * @default 'default'
|
|
|
+ */
|
|
|
+ type?: ButtomType,
|
|
|
+ /**
|
|
|
+ * 占满父级主轴
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
+ block?: boolean,
|
|
|
+ /**
|
|
|
+ * * plain 将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色,背景为白色。
|
|
|
+ * * light 将按钮设置为浅色按钮,浅色按钮的文字为按钮颜色,背景为主色调浅色。
|
|
|
+ * @default 'default'
|
|
|
+ */
|
|
|
+ scheme?: 'default'|'plain'|'light',
|
|
|
+ /**
|
|
|
+ * 通过 loading 属性设置按钮为加载状态,加载状态下默认会隐藏按钮文字,可以通过 loadingText 设置加载状态下的文字。
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
+ loading?: boolean,
|
|
|
+ /**
|
|
|
+ * 加载状态下的文字。
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
+ loadingText?: string,
|
|
|
+ /**
|
|
|
+ * 加载状态圆圈颜色
|
|
|
+ */
|
|
|
+ loadingColor?: string,
|
|
|
+ /**
|
|
|
+ * 按钮形状 通过 square 设置方形按钮,通过 round 设置圆形按钮。
|
|
|
+ * @default 'round'
|
|
|
+ */
|
|
|
+ shape?: 'square'|'round',
|
|
|
+ /**
|
|
|
+ * 左侧图标。支持 Icon 组件里的所有图标,也可以传入图标的图片 URL(http/https)。
|
|
|
+ */
|
|
|
+ icon?: string,
|
|
|
+ /**
|
|
|
+ * 当使用图标时,左侧图标的附加属性
|
|
|
+ */
|
|
|
+ iconProps?: IconProps;
|
|
|
+ /**
|
|
|
+ * 右侧图标。支持 Icon 组件里的所有图标,也可以传入图标的图片 URL(http/https)。
|
|
|
+ */
|
|
|
+ rightIcon?: string,
|
|
|
+ /**
|
|
|
+ * 当使用图标时,右侧图标的附加属性
|
|
|
+ */
|
|
|
+ rightIconProps?: IconProps;
|
|
|
+ /**
|
|
|
+ * 是否可以点击
|
|
|
+ * @default true
|
|
|
+ */
|
|
|
+ touchable?: boolean,
|
|
|
+ /**
|
|
|
+ * 当按扭为round圆形按扭时的圆角大小。
|
|
|
+ * @default 5
|
|
|
+ */
|
|
|
+ radius?: number,
|
|
|
+ /**
|
|
|
+ * 按钮尺寸. 支持 large、medium、small、mini 四种尺寸。
|
|
|
+ * @default 'medium'
|
|
|
+ */
|
|
|
+ size?: ButtomSizeType,
|
|
|
+ /**
|
|
|
+ * 通过 color 属性可以自定义按钮的背景颜色,仅在 type 为 `custom` 时有效
|
|
|
+ * @default grey
|
|
|
+ */
|
|
|
+ color?: string;
|
|
|
+ /**
|
|
|
+ * 按钮文字的颜色。
|
|
|
+ */
|
|
|
+ textColor?: string;
|
|
|
+ /**
|
|
|
+ * 按下时按钮文字的颜色。
|
|
|
+ */
|
|
|
+ pressedTextColor?: string;
|
|
|
+ /**
|
|
|
+ * 按钮文字的样式。
|
|
|
+ */
|
|
|
+ textStyle?: object;
|
|
|
+ /**
|
|
|
+ * 按下时的颜色,仅在 type 为 `custom` 时有效
|
|
|
+ * @default PressedColor(primary)
|
|
|
+ */
|
|
|
+ pressedColor?: string;
|
|
|
+ /**
|
|
|
+ * 禁用时的颜色,仅在 type 为 `custom` 时有效
|
|
|
+ * @default grey
|
|
|
+ */
|
|
|
+ disabledColor?: string;
|
|
|
+ /**
|
|
|
+ * 自定义样式
|
|
|
+ */
|
|
|
+ innerStyle?: object,
|
|
|
+ /**
|
|
|
+ * 按扭的文字,等同于 text 属性
|
|
|
+ */
|
|
|
+ children?: string;
|
|
|
+ /**
|
|
|
+ * 强制控制按钮的边距
|
|
|
+ * * 如果是数字,则设置所有方向边距
|
|
|
+ * * 两位数组 [vetical,horizontal]
|
|
|
+ * * 四位数组 [top,right,down,left]
|
|
|
+ */
|
|
|
+ padding?: number|number[],
|
|
|
+ /**
|
|
|
+ * 外层容器参数
|
|
|
+ */
|
|
|
+ viewProps?: FlexProps,
|
|
|
+
|
|
|
+ formType?: string;
|
|
|
+ openType?: string;
|
|
|
+ appParameter?: string;
|
|
|
+}
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ options: {
|
|
|
+ styleIsolation: "shared",
|
|
|
+ virtualHost: true,
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const emit = defineEmits([
|
|
|
+ 'click',
|
|
|
+])
|
|
|
+
|
|
|
+const themeContext = useTheme();
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<ButtonProp>(), {
|
|
|
+ touchable: true,
|
|
|
+ loading: false,
|
|
|
+ color: 'primary',
|
|
|
+ pressedColor: 'pressed.primary',
|
|
|
+ disabledColor: 'grey',
|
|
|
+ type: 'default',
|
|
|
+ size: 'medium',
|
|
|
+ block: false,
|
|
|
+ radius: 16,
|
|
|
+ shape: "round",
|
|
|
+});
|
|
|
+
|
|
|
+const FonstSizes = computed(() => ({
|
|
|
+ mini: themeContext.resolveThemeSize('ButtonMiniFonstSize', 'fontSize.mini'),
|
|
|
+ small: themeContext.resolveThemeSize('ButtonSmallFonstSize', 'fontSize.small'),
|
|
|
+ medium: themeContext.resolveThemeSize('ButtonMediumFonstSize', 'fontSize.medium'),
|
|
|
+ large: themeContext.resolveThemeSize('ButtonLargeFonstSize', 'fontSize.large'),
|
|
|
+ larger: themeContext.resolveThemeSize('ButtonLargerFonstSize', 'fontSize.larger'),
|
|
|
+}));
|
|
|
+const themeVars = themeContext.getVars({
|
|
|
+ ButtonBorderWidth: 1.5,
|
|
|
+ ButtonDisableOpacity: 0.5,
|
|
|
+});
|
|
|
+const themeStyles = themeContext.useThemeStyles({
|
|
|
+ plainButtonDefault: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
|
|
|
+ borderColor: DynamicColor('ButtonPlainDefaultBorderColor', 'border'),
|
|
|
+ color: DynamicColor('ButtonPlainDefaultColor', 'text'),
|
|
|
+ },
|
|
|
+ plainButtonPrimary: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
|
|
|
+ borderColor: DynamicColor('ButtonPlainPrimaryBorderColor', 'primary'),
|
|
|
+ color: DynamicColor('ButtonPlainPrimaryColor', 'primary'),
|
|
|
+ },
|
|
|
+ plainButtonSuccess: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
|
|
|
+ borderColor: DynamicColor('ButtonPlainSuccessBorderColor', 'success'),
|
|
|
+ color: DynamicColor('ButtonPlainSuccessColor', 'success'),
|
|
|
+ },
|
|
|
+ plainButtonWarning: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
|
|
|
+ borderColor: DynamicColor('ButtonPlainWarningBorderColor', 'warning'),
|
|
|
+ color: DynamicColor('ButtonPlainWarningColor', 'warning'),
|
|
|
+ },
|
|
|
+ plainButtonDanger: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
|
|
|
+ borderColor: DynamicColor('ButtonPlainDangerBorderColor', 'danger'),
|
|
|
+ color: DynamicColor('ButtonPlainDangerColor', 'danger'),
|
|
|
+ },
|
|
|
+ lightButtonDefault: {
|
|
|
+ backgroundColor: DynamicColor('ButtonLightDefaultBackgroundColor', 'background.button'),
|
|
|
+ color: DynamicColor('ButtonLightDefaultColor', 'text'),
|
|
|
+ },
|
|
|
+ lightButtonPrimary: {
|
|
|
+ backgroundColor: DynamicColor('ButtonLightPrimaryBackgroundColor', 'background.primary'),
|
|
|
+ color: DynamicColor('ButtonLightPrimaryColor', 'text.primary'),
|
|
|
+ },
|
|
|
+ lightButtonSuccess: {
|
|
|
+ backgroundColor: DynamicColor('ButtonLightSuccessBackgroundColor', 'background.success'),
|
|
|
+ color: DynamicColor('ButtonLightSuccessColor', 'text.success'),
|
|
|
+ },
|
|
|
+ lightButtonWarning: {
|
|
|
+ backgroundColor: DynamicColor('ButtonLightWarningBackgroundColor', 'background.warning'),
|
|
|
+ color: DynamicColor('ButtonLightWarningColor', 'text.warning'),
|
|
|
+ },
|
|
|
+ lightButtonDanger: {
|
|
|
+ backgroundColor: DynamicColor('ButtonLightDangerBackgroundColor', 'background.danger'),
|
|
|
+ color: DynamicColor('ButtonLightDangerColor', 'text.danger'),
|
|
|
+ },
|
|
|
+ buttonSizeLarger: {
|
|
|
+ paddingVertical: DynamicSize('ButtonPaddingVerticalLarger', 25),
|
|
|
+ paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarger', 30),
|
|
|
+ },
|
|
|
+ buttonSizeLarge: {
|
|
|
+ paddingVertical: DynamicSize('ButtonPaddingVerticalLarge', 20),
|
|
|
+ paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarge', 25),
|
|
|
+ },
|
|
|
+ buttonSizeMedium: {
|
|
|
+ paddingVertical: DynamicSize('ButtonPaddingVerticalMedium', 15),
|
|
|
+ paddingHorizontal: DynamicSize('ButtonPaddingHorizontalMedium', 20),
|
|
|
+ },
|
|
|
+ buttonSizeSmall: {
|
|
|
+ paddingVertical: DynamicSize('ButtonPaddingVerticalSmall', 10),
|
|
|
+ paddingHorizontal: DynamicSize('ButtonPaddingHorizontalSmall', 15),
|
|
|
+ },
|
|
|
+ buttonSizeMini: {
|
|
|
+ paddingVertical: DynamicSize('ButtonPaddingVerticalMini', 5),
|
|
|
+ paddingHorizontal: DynamicSize('ButtonPaddingHorizontalMini', 6),
|
|
|
+ },
|
|
|
+ buttonDefault: {
|
|
|
+ backgroundColor: DynamicColor('ButtonDefaultBackgroundColor', 'button'),
|
|
|
+ color: DynamicColor('ButtonDefaultColor', 'black'),
|
|
|
+ },
|
|
|
+ buttonPrimary: {
|
|
|
+ backgroundColor: DynamicColor('ButtonPrimaryBackgroundColor', 'primary'),
|
|
|
+ color: DynamicColor('ButtonPrimaryColor', 'white'),
|
|
|
+ },
|
|
|
+ buttonSuccess: {
|
|
|
+ backgroundColor: DynamicColor('ButtonSuccessBackgroundColor', 'success'),
|
|
|
+ color: DynamicColor('ButtonSuccessColor', 'white'),
|
|
|
+ },
|
|
|
+ buttonWarning: {
|
|
|
+ backgroundColor: DynamicColor('ButtonWarningBackgroundColor', 'warning'),
|
|
|
+ color: DynamicColor('ButtonWarningColor', 'white'),
|
|
|
+ },
|
|
|
+ buttonDanger: {
|
|
|
+ backgroundColor: DynamicColor('ButtonDangerBackgroundColor', 'danger'),
|
|
|
+ color: DynamicColor('ButtonDangerColor', 'white'),
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+//按钮样式生成
|
|
|
+const currentStyle = computed(() => {
|
|
|
+ const colorStyle = selectStyleType<ViewStyle, ButtomType>(props.type, 'default',
|
|
|
+ selectStyleType(props.scheme, 'default', {
|
|
|
+ default: {
|
|
|
+ default: themeStyles.buttonDefault.value,
|
|
|
+ primary: themeStyles.buttonPrimary.value,
|
|
|
+ success: themeStyles.buttonSuccess.value,
|
|
|
+ warning: themeStyles.buttonWarning.value,
|
|
|
+ danger: themeStyles.buttonDanger.value,
|
|
|
+ custom: {
|
|
|
+ backgroundColor: themeContext.resolveThemeColor(props.touchable ? props.color : props.disabledColor),
|
|
|
+ color: themeContext.resolveThemeColor(props.textColor),
|
|
|
+ },
|
|
|
+ text: {
|
|
|
+ color: themeContext.resolveThemeColor(props.textColor),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ plain: {
|
|
|
+ default: themeStyles.plainButtonDefault.value,
|
|
|
+ primary: themeStyles.plainButtonPrimary.value,
|
|
|
+ success: themeStyles.plainButtonSuccess.value,
|
|
|
+ warning: themeStyles.plainButtonWarning.value,
|
|
|
+ danger: themeStyles.plainButtonDanger.value,
|
|
|
+ custom: {
|
|
|
+ borderStyle: 'solid',
|
|
|
+ borderWidth: themeContext.resolveThemeSize(themeVars.ButtonBorderWidth),
|
|
|
+ borderColor: themeContext.resolveThemeColor(props.color),
|
|
|
+ color: themeContext.resolveThemeColor(props.color),
|
|
|
+ },
|
|
|
+ text: {
|
|
|
+ color: themeContext.resolveThemeColor(props.color),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ light: {
|
|
|
+ default: themeStyles.lightButtonDefault.value,
|
|
|
+ primary: themeStyles.lightButtonPrimary.value,
|
|
|
+ success: themeStyles.lightButtonSuccess.value,
|
|
|
+ warning: themeStyles.lightButtonWarning.value,
|
|
|
+ danger: themeStyles.lightButtonDanger.value,
|
|
|
+ custom: {
|
|
|
+ backgroundColor: themeContext.resolveThemeColor(props.touchable ? props.color : props.disabledColor),
|
|
|
+ color: themeContext.resolveThemeColor(props.textColor),
|
|
|
+ },
|
|
|
+ text: {
|
|
|
+ color: themeContext.resolveThemeColor(props.color),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ const speicalStyle : ViewStyle = {
|
|
|
+ opacity: props.touchable ? 1 : themeVars.ButtonDisableOpacity,
|
|
|
+ borderRadius: props.shape === 'round' ? themeContext.resolveThemeSize(props.radius) : 0,
|
|
|
+ };
|
|
|
+
|
|
|
+ //自定义状态下的禁用颜色
|
|
|
+ if (props.disabledColor && !props.touchable && props.type === 'custom')
|
|
|
+ speicalStyle.backgroundColor = themeContext.resolveThemeColor(props.disabledColor);
|
|
|
+
|
|
|
+ const sizeStyle = selectStyleType<ViewStyle, ButtomSizeType>(props.size, 'medium', {
|
|
|
+ large: themeStyles.buttonSizeLarge.value,
|
|
|
+ larger: themeStyles.buttonSizeLarger.value,
|
|
|
+ medium: themeStyles.buttonSizeMedium.value,
|
|
|
+ small: themeStyles.buttonSizeSmall.value,
|
|
|
+ mini: themeStyles.buttonSizeMini.value,
|
|
|
+ });
|
|
|
+ //if (props.shape === 'round')
|
|
|
+ // sizeStyle.paddingHorizontal = `calc(${sizeStyle.paddingHorizontal} + ${themeContext.resolveSize(props.radius / 4)})`;
|
|
|
+
|
|
|
+ //内边距样式的强制设置
|
|
|
+ configPadding(speicalStyle, themeContext.theme, props.padding);
|
|
|
+
|
|
|
+ return {
|
|
|
+ color: (colorStyle).color,
|
|
|
+ style: {
|
|
|
+ ...colorStyle,
|
|
|
+ ...sizeStyle,
|
|
|
+ ...speicalStyle,
|
|
|
+ },
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const state = ref('');
|
|
|
+
|
|
|
+const currentText = computed(() => (props.loading ? (props.loadingText || props.text) : props.text));
|
|
|
+const iconMargin = computed(() => Boolean(currentText.value));
|
|
|
+const textColorFinal = computed(() => (
|
|
|
+ state.value === 'active' ?
|
|
|
+ themeContext.resolveThemeColor(props.pressedTextColor) :
|
|
|
+ themeContext.resolveThemeColor(props.textColor)
|
|
|
+) || currentStyle.value.color);
|
|
|
+const finalPressedColor = computed(() => {
|
|
|
+ if (props.type === 'custom')
|
|
|
+ return themeContext.resolveThemeColor(props.pressedColor);
|
|
|
+ return (themeContext.resolveThemeColor((
|
|
|
+ props.scheme === 'plain'
|
|
|
+ || props.scheme === 'light'
|
|
|
+ || props.type === 'text'
|
|
|
+ ) ?
|
|
|
+ 'pressed.notice' :
|
|
|
+ 'pressed.' + props.type))
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.nana-button {
|
|
|
+ width: auto;
|
|
|
+ user-select: none;
|
|
|
+ cursor: pointer;
|
|
|
+ appearance: none;
|
|
|
+}
|
|
|
+.nana-button-inner {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background-color: transparent;
|
|
|
+ border: none;
|
|
|
+ outline: none;
|
|
|
+ line-height: auto;
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+.nana-button-inner::after {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+.nana-button-auto {
|
|
|
+ flex-shrink: 0;
|
|
|
+ flex-grow: 0;
|
|
|
+ flex-basis: auto;
|
|
|
+}
|
|
|
+.nana-button-block {
|
|
|
+ align-self: stretch;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|