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
+13
View File
@@ -0,0 +1,13 @@
import { withInstall, withNoopInstall } from '../../utils'
import Checkbox from './src/checkbox.vue'
import CheckboxGroup from './src/checkbox-group.vue'
export const TnCheckbox = withInstall(Checkbox, {
CheckboxGroup,
})
export default TnCheckbox
export const TnCheckboxGroup = withNoopInstall(CheckboxGroup)
export * from './src/checkbox'
export * from './src/checkbox-group'
export type { CheckboxInstance, CheckboxGroupInstance } from './src/instance'
@@ -0,0 +1,49 @@
import { buildProps, definePropType, isArray } from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import { checkboxBaseProps } from '../../base/common-props/checkbox'
import type { ExtractPropTypes } from 'vue'
import type { CheckboxValueType } from '../../base/types/checkbox'
export type CheckboxGroupValueType = Exclude<CheckboxValueType, boolean>[]
export const checkboxGroupProps = buildProps({
...checkboxBaseProps,
/**
* @description 绑定的值
*/
modelValue: {
type: definePropType<CheckboxGroupValueType>(Array),
default: () => [],
},
/**
* @description 可被勾选的复选框最小值
*/
min: Number,
/**
* @description 可被勾选的复选框最大值
*/
max: Number,
/**
* @description 每个选项独占一行
*/
wrap: {
type: Boolean,
default: false,
},
/**
* @description 值发生修改时是否触发表单验证
*/
validateEvent: {
type: Boolean,
default: true,
},
})
export const checkboxGroupEmits = {
[UPDATE_MODEL_EVENT]: (value: CheckboxGroupValueType) => isArray(value),
[CHANGE_EVENT]: (value: CheckboxGroupValueType) => isArray(value),
}
export type CheckboxGroupProps = ExtractPropTypes<typeof checkboxGroupProps>
export type CheckboxGroupEmits = typeof checkboxGroupEmits
@@ -0,0 +1,32 @@
<script lang="ts" setup>
import { provide, reactive, toRefs } from 'vue'
import { checkboxGroupKey } from '../../../tokens'
import { useNamespace } from '../../../hooks'
import { checkboxGroupEmits, checkboxGroupProps } from './checkbox-group'
import { useCheckboxGroup } from './composables'
const props = defineProps(checkboxGroupProps)
const emits = defineEmits(checkboxGroupEmits)
const ns = useNamespace('checkbox-group')
const { changeEvent } = useCheckboxGroup(props, emits)
provide(
checkboxGroupKey,
reactive({
...toRefs(props),
changeEvent,
})
)
</script>
<template>
<view :class="[ns.b(), ns.is('wrap', props.wrap)]">
<slot />
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/checkbox-group.scss';
</style>
@@ -0,0 +1,67 @@
import { buildProps, isBoolean, isNumber, isString } from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import { checkboxBaseProps } from '../../base/common-props/checkbox'
import { useComponentCustomStyleProp } from '../../base/composables/use-component-common-props'
import type { ExtractPropTypes } from 'vue'
import type { CheckboxValueType } from '../../base/types/checkbox'
export const checkboxProps = buildProps({
...checkboxBaseProps,
/**
* @description 绑定的值
*/
modelValue: {
type: [String, Number, Boolean],
default: undefined,
},
/**
* @description 用于标记多个复选框时的唯一标识
*/
label: {
type: [String, Number],
},
/**
* @description 用于标记当前复选框是否为不确定状态,一般用于全选
*/
indeterminate: Boolean,
/**
* @description 复选框选中时的值
*/
activeValue: {
type: [String, Number, Boolean],
default: true,
},
/**
* @description 复选框未选中时的值
*/
inactiveValue: {
type: [String, Number, Boolean],
default: false,
},
/**
* @description 自定义样式
*/
customStyle: useComponentCustomStyleProp,
/**
* @description 自定义类名
*/
customClass: String,
/**
* @description 值发生修改时是否触发表单验证
*/
validateEvent: {
type: Boolean,
default: true,
},
})
export const checkboxEmits = {
[UPDATE_MODEL_EVENT]: (value: CheckboxValueType) =>
isString(value) || isNumber(value) || isBoolean(value),
[CHANGE_EVENT]: (value: CheckboxValueType) =>
isString(value) || isNumber(value) || isBoolean(value),
}
export type CheckboxProps = ExtractPropTypes<typeof checkboxProps>
export type CheckboxEmits = typeof checkboxEmits
@@ -0,0 +1,53 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import { checkboxEmits, checkboxProps } from './checkbox'
import { useCheckbox, useCheckboxCustomStyle } from './composables'
const props = defineProps(checkboxProps)
defineEmits(checkboxEmits)
const { isGroup, selected, handleCheckboxClick } = useCheckbox(props)
const {
ns,
checkboxClass,
checkboxStyle,
checkboxCheckedBoxClass,
checkboxCheckedBoxStyle,
} = useCheckboxCustomStyle(Object.assign(props))
</script>
<template>
<view
:class="[checkboxClass(selected), { [ns.m('group')]: isGroup }]"
:style="checkboxStyle(selected)"
@tap.stop="handleCheckboxClick('label')"
>
<!-- 左边内容 -->
<view
v-if="$slots.left && !$slots.default"
:class="[ns.em('content', 'left')]"
>
<slot name="left" />
</view>
<!-- 选择框 -->
<view
:class="[
checkboxCheckedBoxClass(selected),
{ [ns.em('checked-box', 'indeterminate')]: !selected && indeterminate },
]"
:style="checkboxCheckedBoxStyle(selected)"
@tap.stop="handleCheckboxClick('checkbox')"
>
<TnIcon v-if="selected" name="check" />
</view>
<!-- 右边内容 -->
<view v-if="$slots.default" :class="[ns.em('content', 'right')]">
<slot />
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/checkbox.scss';
</style>
@@ -0,0 +1,116 @@
import { computed } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { isEmpty } from '../../../../utils'
import { useCheckboxCommonProps } from './use-checkbox-common-props'
import type { CSSProperties } from 'vue'
import type { CheckboxProps } from '../checkbox'
type selectedClass = (selected: boolean) => string
type selectedStyle = (selected: boolean) => CSSProperties
export const useCheckboxCustomStyle = (props: CheckboxProps) => {
const ns = useNamespace('checkbox')
const { activeColor, disabled, maxDisabled, size, border, checkedShape } =
useCheckboxCommonProps(props)
// 解析颜色
const [bgColorClass, bgColorStyle] = useComponentColor(activeColor, 'bg')
const [textColorClass, textColorStyle] = useComponentColor(
activeColor,
'text'
)
const [borderColorClass, borderColorStyle] = useComponentColor(
activeColor,
'border'
)
// 复选框所属类
const checkboxClass = computed<selectedClass>(() => {
return (selected: boolean) => {
const cls: string[] = [ns.b()]
// 禁止选择
if (disabled.value || maxDisabled.value) cls.push(ns.m('disabled'))
// 设置尺寸
if (size.value) cls.push(ns.m(size.value))
// 激活样式
if (selected) {
cls.push(ns.m('selected'))
if (textColorClass.value) cls.push(textColorClass.value)
}
// 设置激活时的边框颜色
if (border.value) {
cls.push('tn-border')
if (selected && borderColorClass.value) cls.push(borderColorClass.value)
else cls.push('tn-gray-disabled_border')
} else {
cls.push(ns.m('no-border'))
}
if (props.customClass) cls.push(props.customClass)
return cls.join(' ')
}
})
// 复选框所属样式
const checkboxStyle = computed<selectedStyle>(() => {
return (selected: boolean) => {
const style: CSSProperties = {}
// 设置激活时的颜色
if (selected) {
if (border.value && !borderColorClass.value)
style.borderColor =
borderColorStyle.value || 'var(--tn-color-primary)'
if (!textColorClass.value) {
style.color = textColorStyle.value || 'var(--tn-color-primary)'
}
}
if (!isEmpty(props.customStyle)) Object.assign(style, props.customStyle)
return style
}
})
// 复选框选框所属类
const checkboxCheckedBoxClass = computed<selectedClass>(() => {
return (selected: boolean) => {
const cls: string[] = [ns.e('checked-box')]
// 复选框选框的形状
if (checkedShape.value) cls.push(ns.em('checked-box', checkedShape.value))
if (selected || props.indeterminate) {
cls.push(ns.em('checked-box', 'selected'))
if (bgColorClass.value) cls.push(bgColorClass.value)
} else {
cls.push('tn-border tn-gray-disabled_border')
}
return cls.join(' ')
}
})
// 复选框选框所属样式
const checkboxCheckedBoxStyle = computed<selectedStyle>(() => {
return (selected: boolean) => {
const style: CSSProperties = {}
if ((selected || props.indeterminate) && !bgColorClass.value) {
style.backgroundColor = bgColorStyle.value || 'var(--tn-color-primary)'
}
return style
}
})
return {
ns,
checkboxClass,
checkboxStyle,
checkboxCheckedBoxClass,
checkboxCheckedBoxStyle,
}
}
@@ -0,0 +1,4 @@
export * from './use-checkbox-common-props'
export * from './use-checkbox'
export * from './use-checkbox-group'
export * from './checkbox-custom'
@@ -0,0 +1,63 @@
import { computed, inject } from 'vue'
import { checkboxGroupKey } from '../../../../tokens'
import { useFormDisabled, useFormSize } from '../../../form'
import { isEmptyDoubleVariableInDefault } from '../../../../utils'
import type { CheckboxProps } from '../checkbox'
export const useCheckboxCommonProps = (props: CheckboxProps) => {
const checkboxGroup = inject(checkboxGroupKey, undefined)
// 组件尺寸
const size = useFormSize(
computed(() =>
isEmptyDoubleVariableInDefault(props?.size, checkboxGroup?.size)
)
)
// 复选框选框的形状
const checkedShape = computed(() =>
isEmptyDoubleVariableInDefault(
props?.checkedShape,
checkboxGroup?.checkedShape,
'square'
)
)
// 是否禁用
const disabled = useFormDisabled(
computed(() => props?.disabled || checkboxGroup?.disabled || false)
)
const maxDisabled = computed(
() =>
checkboxGroup?.modelValue &&
checkboxGroup?.max &&
checkboxGroup?.modelValue.length >= checkboxGroup?.max &&
!checkboxGroup?.modelValue.includes(props.label!)
)
// 禁止点击标签进行选择
const labelDisabled = computed(
() => props?.labelDisabled || checkboxGroup?.labelDisabled || false
)
// 是否显示边框
const border = computed(() => props?.border || checkboxGroup?.border || false)
// radio激活时的颜色
const activeColor = computed(
() => props?.activeColor || checkboxGroup?.activeColor
)
return {
checkboxGroup,
size,
checkedShape,
disabled,
maxDisabled,
labelDisabled,
border,
activeColor,
}
}
@@ -0,0 +1,56 @@
import { nextTick, watch } from 'vue'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { useFormItem } from '../../../form'
import { debugWarn } from '../../../../utils'
import type { SetupContext } from 'vue'
import type {
CheckboxGroupEmits,
CheckboxGroupProps,
CheckboxGroupValueType,
} from '../checkbox-group'
export const useCheckboxGroup = (
props: CheckboxGroupProps,
emits: SetupContext<CheckboxGroupEmits>['emit']
) => {
const { formItem } = useFormItem()
// 更新复选框组的值
const changeEvent = (val: CheckboxGroupValueType[number]) => {
const { modelValue, max } = props
const selectValues = [...modelValue]
// 判断更新的值是否已经存在于复选框组中
const hasLabel = selectValues.includes(val)
if (hasLabel) {
// 存在则删除
selectValues.splice(selectValues.indexOf(val), 1)
} else {
// 不存在则添加,判断是否超过最大值
if (max && selectValues.length >= max) {
return
}
// 如果没有设置最大值,则直接添加
selectValues.push(val)
}
emits(UPDATE_MODEL_EVENT, selectValues)
nextTick(() => {
emits(CHANGE_EVENT, selectValues)
})
}
watch(
() => props.modelValue,
() => {
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => {
debugWarn(err)
})
}
}
)
return {
changeEvent,
}
}
@@ -0,0 +1,64 @@
import { computed, getCurrentInstance, nextTick } from 'vue'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { useFormItem } from '../../../form'
import { debugWarn } from '../../../../utils'
import { useCheckboxCommonProps } from './use-checkbox-common-props'
import type { CheckboxProps } from '../checkbox'
import type { CheckboxValueType } from '../../../base/types/checkbox'
// 判断复选框组中是否包含某个值
const hasLabelInGroup = (
groupValue: CheckboxValueType[],
label: CheckboxValueType
) => groupValue.includes(label)
export const useCheckbox = (props: CheckboxProps) => {
const { emit } = getCurrentInstance()!
const { checkboxGroup, disabled, maxDisabled, labelDisabled } =
useCheckboxCommonProps(props)
const { formItem } = useFormItem()
// 判断是否为复选组
const isGroup = computed(() => !!checkboxGroup)
// 在复选组中是否选中当前复选框
const selected = computed(() => {
if (isGroup.value) {
return hasLabelInGroup(checkboxGroup!.modelValue, props.label!)
} else {
return props.modelValue === props.activeValue
}
})
// 复选框点击事件
const handleCheckboxClick = (type: 'checkbox' | 'label') => {
if (disabled.value || maxDisabled.value) return
if (type === 'label' && labelDisabled.value) return
if (isGroup.value) {
checkboxGroup!.changeEvent(props.label!)
} else {
const modelValue = selected.value
? props.inactiveValue
: props.activeValue
emit(UPDATE_MODEL_EVENT, modelValue)
nextTick(() => {
emit(CHANGE_EVENT, modelValue)
})
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => {
debugWarn(err)
})
}
}
}
return {
isGroup,
selected,
handleCheckboxClick,
}
}
@@ -0,0 +1,5 @@
import type Checkbox from './checkbox.vue'
import type CheckboxGroup from './checkbox-group.vue'
export type CheckboxInstance = InstanceType<typeof Checkbox>
export type CheckboxGroupInstance = InstanceType<typeof CheckboxGroup>