2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
import { withNoopInstall } from '../../utils'
import Switch from './src/switch.vue'
export const TnSwitch = withNoopInstall(Switch)
export default TnSwitch
export * from './src/switch'
export type { TnSwitchInstance } from './src/instance'
@@ -0,0 +1,3 @@
export * from './switch-custom'
export * from './use-switch-common-props'
export * from './use-switch'
@@ -0,0 +1,96 @@
import { computed, toRef } from 'vue'
import { formatDomSizeValue, isEmpty } from '../../../../utils'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { useSwitchCommonProps } from './use-switch-common-props'
import type { CSSProperties, Ref } from 'vue'
import type { SwitchProps } from '../switch'
export const useSwitchCustomStyle = (
props: SwitchProps,
selected: Ref<boolean>
) => {
const ns = useNamespace('switch')
const { size, disabled } = useSwitchCommonProps(props)
// 解析颜色
const [activeBgColorClass, activeBgColorStyle] = useComponentColor(
toRef(props, 'activeColor'),
'bg'
)
const [inactiveBgColorClass, inactiveBgColorStyle] = useComponentColor(
toRef(props, 'inactiveColor'),
'bg'
)
const [activeTextColorClass, activeTextColorStyle] = useComponentColor(
toRef(props, 'activeColor'),
'text'
)
const [inactiveTextColorClass, inactiveTextColorStyle] = useComponentColor(
toRef(props, 'inactiveColor'),
'text'
)
// switch所属类
const switchClass = computed<string>(() => {
const cls: string[] = [ns.b()]
// switch的形状
if (props.shape) cls.push(ns.m(props.shape))
// switch的尺寸
if (size.value) cls.push(ns.m(size.value))
// 禁止状态
if (disabled.value) cls.push(ns.m('disabled'))
// 设置switch按钮的激活与未激活状态的颜色
if (selected.value) {
if (activeBgColorClass.value) cls.push(activeBgColorClass.value)
if (activeTextColorClass.value) cls.push(activeTextColorClass.value)
} else {
if (inactiveBgColorClass.value) cls.push(inactiveBgColorClass.value)
if (inactiveTextColorClass.value) cls.push(inactiveTextColorClass.value)
}
if (props.customClass) cls.push(props.customClass)
return cls.join(' ')
})
// switch所属样式
const switchStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
// 设置switch按钮的宽度
if (props.width) style.width = formatDomSizeValue(props.width)
// 设置switch按钮的激活与未激活状态的颜色
if (selected.value) {
if (activeBgColorStyle.value || !activeBgColorClass.value) {
style.backgroundColor =
activeBgColorStyle.value || 'var(--tn-color-primary)'
}
if (activeTextColorStyle.value) {
style.color = activeTextColorStyle.value
}
} else {
if (inactiveBgColorStyle.value || !inactiveBgColorClass.value) {
style.backgroundColor =
inactiveBgColorStyle.value || 'var(--tn-color-gray-disabled)'
}
if (inactiveTextColorStyle.value) {
style.color = inactiveTextColorStyle.value
}
}
if (!isEmpty(props.customStyle)) Object.assign(style, props.customStyle)
return style
})
return {
ns,
switchClass,
switchStyle,
}
}
@@ -0,0 +1,15 @@
import { useFormDisabled, useFormSize } from '../../../form'
import type { SwitchProps } from '../switch'
export const useSwitchCommonProps = (props: SwitchProps) => {
// Switch开关的尺寸
const size = useFormSize(props.size)
// Switch开关是否禁用
const disabled = useFormDisabled(props.disabled)
return {
size,
disabled,
}
}
@@ -0,0 +1,83 @@
import { computed, getCurrentInstance, nextTick, watch } from 'vue'
import { debugWarn, isBoolean, isPromise } from '../../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { useFormItem } from '../../../form'
import { useSwitchCommonProps } from './use-switch-common-props'
import type { SwitchProps } from '../switch'
export const useSwitch = (props: SwitchProps) => {
const { emit } = getCurrentInstance()!
const { formItem } = useFormItem()
const { disabled } = useSwitchCommonProps(props)
// 判断是否为选中状态
const selected = computed<boolean>(
() => props.modelValue === props.activeValue
)
// 处理切换事件
const handleSwitch = () => {
const value =
props.modelValue === props.activeValue
? props.inactiveValue
: props.activeValue
emit(UPDATE_MODEL_EVENT, value)
nextTick(() => emit(CHANGE_EVENT, value))
}
// switch按钮的点击切换事件
const switchClickEvent = () => {
if (disabled.value || props.loading) return
const { beforeChange } = props
if (!beforeChange) {
handleSwitch()
return
}
const shouldChange = beforeChange()
const isPromiseOrBool = [
isPromise(shouldChange),
isBoolean(shouldChange),
].includes(true)
if (!isPromiseOrBool) {
throw new Error(
'[TnSwitch] beforeChange 返回值必须是 Promise 或者 boolean'
)
}
if (isPromise(shouldChange)) {
shouldChange
.then((res) => {
if (res) handleSwitch()
})
.catch((err) => {
// eslint-disable-next-line no-console
console.warn(`[TnSwitch] some error occured: ${err}`)
})
} else if (shouldChange) {
handleSwitch()
}
}
watch(
() => props.modelValue,
() => {
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => {
debugWarn(err)
})
}
}
)
return {
selected,
switchClickEvent,
}
}
@@ -0,0 +1,3 @@
import type Switch from './switch.vue'
export type TnSwitchInstance = InstanceType<typeof Switch>
+125
View File
@@ -0,0 +1,125 @@
import {
buildProps,
definePropType,
isBoolean,
isNumber,
isString,
} from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import {
useComponentCustomStyleProp,
useFormSizeProps,
} from '../../base/composables/use-component-common-props'
import type { ExtractPropTypes } from 'vue'
export const switchShapes = ['square', 'round'] as const
export type switchValueType = string | number | boolean
export const switchProps = buildProps({
/**
* @description 状态绑定的值
*/
modelValue: {
type: [String, Number, Boolean],
default: false,
},
/**
* @description 按钮形状
*/
shape: {
type: String,
values: switchShapes,
default: 'round',
},
/**
* @description 按钮尺寸
*/
size: useFormSizeProps,
/**
* @description 按钮的宽度
*/
width: {
type: [String, Number],
},
/**
* @description 自定义样式
*/
customStyle: useComponentCustomStyleProp,
/**
* @description 自定义类名
*/
customClass: String,
/**
* @description 是否禁用
*/
disabled: Boolean,
/**
* @description 是否显示加载动画
*/
loading: Boolean,
/**
* @description 未选中时的颜色,以tn开头则使用图鸟内置的颜色只支持普通颜色
*/
inactiveColor: String,
/**
* @description 选中时的颜色,以tn开头则使用图鸟内置的颜色只支持普通颜色
*/
activeColor: String,
/**
* @description 未选中时显示的文本,如果设置了该值,则不显示图标
*/
inactiveText: String,
/**
* @description 选中时显示的文本,如果设置了该值,则不显示图标
*/
activeText: String,
/**
* @description 未选中时显示的图标
*/
inactiveIcon: String,
/**
* @description 选中时显示的图标
*/
activeIcon: String,
/**
* @description 未选中时的值
*/
inactiveValue: {
type: [String, Number, Boolean],
default: false,
},
/**
* @description 选中时的值
*/
activeValue: {
type: [String, Number, Boolean],
default: true,
},
/**
* @description 值发生修改时是否触发表单验证
*/
validateEvent: {
type: Boolean,
default: true,
},
/**
* @description 状态改变前的钩子, 返回 false 或者返回 Promise 且被 reject 则停止切换
*/
beforeChange: {
type: definePropType<() => Promise<boolean> | boolean>(Function),
},
})
export const switchEmits = {
[UPDATE_MODEL_EVENT]: (value: switchValueType) =>
isString(value) || isNumber(value) || isBoolean(value),
[CHANGE_EVENT]: (value: switchValueType) =>
isString(value) || isNumber(value) || isBoolean(value),
}
export type SwitchProps = ExtractPropTypes<typeof switchProps>
export type SwitchEmits = typeof switchEmits
export type SwitchShape = (typeof switchShapes)[number]
+69
View File
@@ -0,0 +1,69 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import TnLoading from '../../loading/src/loading.vue'
import { switchEmits, switchProps } from './switch'
import { useSwitch, useSwitchCustomStyle } from './composables'
const props = defineProps(switchProps)
defineEmits(switchEmits)
const { selected, switchClickEvent } = useSwitch(props)
const { ns, switchClass, switchStyle } = useSwitchCustomStyle(props, selected)
</script>
<template>
<view
:class="[
switchClass,
{
[ns.is('content')]:
props.activeText ||
props.activeIcon ||
props.inactiveText ||
props.inactiveIcon,
},
]"
:style="switchStyle"
@tap.stop="switchClickEvent"
>
<!-- switch小圆圈 -->
<view :class="[ns.e('dot'), { [ns.em('dot', 'checked')]: selected }]">
<TnLoading :show="loading" animation type="info" mode="flower" />
</view>
<view v-if="selected" :class="[ns.e('active-content')]">
<!-- 激活时的文本 -->
<view
v-if="props.activeText && !props.activeIcon"
class="tn-text-ellipsis-1"
:class="[ns.em('active-content', 'text')]"
>
{{ props.activeText }}
</view>
<!-- 激活时的图标 -->
<view v-if="props.activeIcon" :class="[ns.em('active-content', 'icon')]">
<TnIcon :name="props.activeIcon" />
</view>
</view>
<view v-else :class="[ns.e('inactive-content')]">
<!-- 未激活时的文本 -->
<view
v-if="props.inactiveText && !props.inactiveIcon"
class="tn-text-ellipsis-1"
:class="[ns.em('inactive-content', 'text')]"
>
{{ props.inactiveText }}
</view>
<!-- 激活时的图标 -->
<view
v-if="props.inactiveIcon"
:class="[ns.em('inactive-content', 'icon')]"
>
<TnIcon :name="props.inactiveIcon" />
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/switch.scss';
</style>