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 NumberBox from './src/number-box.vue'
export const TnNumberBox = withNoopInstall(NumberBox)
export default TnNumberBox
export * from './src/number-box'
export type { TnNumberBoxInstance } from './src/instance'
@@ -0,0 +1,2 @@
export * from './number-box-custom'
export * from './use-number-box'
@@ -0,0 +1,106 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { formatDomSizeValue } from '../../../../utils'
import type { CSSProperties, Ref } from 'vue'
import type { NumberBoxProps } from '../number-box'
type OperationWrapperType = 'minus' | 'input' | 'plus'
type OperationWrapperClass = (type: OperationWrapperType) => string
type OperationWrapperStyle = (type: OperationWrapperType) => CSSProperties
export const useNumberBoxCustomStyle = (
props: NumberBoxProps,
inputValue: Ref<number>
) => {
const ns = useNamespace('number-box')
// 解析颜色
const [bgColorClass, bgColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'bg'
)
const [textColorClass, textColorStyle] = useComponentColor(
toRef(props, 'textColor'),
'text'
)
// 步进器对应的类
const numberBoxClass = computed<string>(() => {
const cls: string[] = [ns.b()]
// 设置尺寸
if (props.size) cls.push(ns.m(props.size))
// 是否禁止操作
if (props.disabled) cls.push(ns.m('disabled'))
return cls.join(' ')
})
// 步进器对应样式
const numberBoxStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
// 设置容器的宽高
if (props.width) style.width = formatDomSizeValue(props.width)
if (props.height) style.height = formatDomSizeValue(props.height)
// 设置字体大小
if (props.fontSize) style.fontSize = formatDomSizeValue(props.fontSize)
return style
})
// 步进器操作区域对应的类
const numberBoxOperationWrapperClass = computed<OperationWrapperClass>(() => {
return (type: OperationWrapperType) => {
const cls: string[] = []
// 设置背景颜色和字体颜色
if (bgColorClass.value) cls.push(bgColorClass.value)
if (textColorClass.value) cls.push(textColorClass.value)
if (
(type === 'minus' && inputValue.value <= props.min) ||
(type === 'plus' && inputValue.value >= props.max)
) {
cls.push(ns.is('disabled'))
}
return cls.join(' ')
}
})
// 步进器操作区域对应的样式
const numberBoxOperationWrapperStyle = computed<OperationWrapperStyle>(() => {
return (type: OperationWrapperType) => {
const style: CSSProperties = {}
// 设置背景颜色和字体颜色
if (!bgColorClass.value)
style.backgroundColor =
bgColorStyle.value || 'var(--tn-color-gray-light)'
if (textColorStyle.value) style.color = textColorStyle.value
// 设置操作按钮的宽高
if (type === 'minus' || type === 'plus') {
if (props.height) {
style.width = formatDomSizeValue(props.height)
style.height = style.width
}
if (props.fontSize) {
style.fontSize = `calc(${formatDomSizeValue(props.fontSize)} * 1.2)`
}
}
return style
}
})
return {
ns,
numberBoxClass,
numberBoxStyle,
numberBoxOperationWrapperClass,
numberBoxOperationWrapperStyle,
}
}
@@ -0,0 +1,117 @@
import { computed, getCurrentInstance, nextTick, ref, toRef, watch } from 'vue'
import {
CHANGE_EVENT,
INPUT_EVENT,
UPDATE_MODEL_EVENT,
} from '../../../../constants'
import { useLongPress } from '../../../../hooks'
import { debugWarn, isEmptyVariableInDefault } from '../../../../utils'
import { useFormItem } from '../../../form'
import type { NumberBoxProps } from '../number-box'
export const useNumberBox = (props: NumberBoxProps) => {
const { emit } = getCurrentInstance()!
const { formItem } = useFormItem()
// 输入框的值
const inputValue = ref<number>(0)
// 更新输入框的值
watch(
() => props.modelValue,
(val) => {
const value = isEmptyVariableInDefault(val, 0)
inputValue.value = Math.max(props.min, Math.min(value, props.max))
},
{
immediate: true,
}
)
// 步进值
const step = computed<number>(() => props.step || 1)
const operationEvent = (type: 'minus' | 'plus') => {
if (props.disabled) return
let value = inputValue.value
if (type === 'minus') value -= step.value
else if (type === 'plus') value += step.value
if (value < props.min) {
value = props.min
props.longPress && clearLongPressTimer()
}
if (value > props.max) {
value = props.max
props.longPress && clearLongPressTimer()
}
updateNumberBoxValue(value)
}
const { clearLongPressTimer, handleLongPressEvent: handleOperationEvent } =
useLongPress<['minus' | 'plus']>(
operationEvent,
toRef(props, 'longPress'),
props.longPressInterval
)
// input输入框输入事件
const numberBoxInputEvent = (e: any) => {
const inputEventValue = e.detail.value || 0
let value = Number(inputEventValue)
// 判断边缘值
if (value < props.min) {
value = props.min
}
if (value > props.max) {
value = props.max
}
emit(INPUT_EVENT, inputEventValue)
if (props.validateEvent) {
// eslint-disable-next-line @typescript-eslint/no-empty-function
formItem?.validate('input').catch(() => {})
}
// isInnerUpdate = true
// inputValue.value = inputEventValue
// nextTick(() => {
// setTimeout(() => {
// inputValue.value = value
// }, 0)
// })
updateNumberBoxValue(value)
}
// 更新步进器的值
const updateNumberBoxValue = (value: number) => {
// 获取step的小数位
const stepValueArray: string[] = step.value.toString().split('.')
const decimalCount: number =
stepValueArray.length > 1 ? stepValueArray[1].length : 0
value = Number(value.toFixed(decimalCount))
nextTick(() => {
setTimeout(() => {
inputValue.value = value
}, 0)
})
emit(UPDATE_MODEL_EVENT, value)
nextTick(() => {
emit(CHANGE_EVENT, value)
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => {
debugWarn(err)
})
}
})
}
return {
inputValue,
handleOperationEvent,
clearLongPressTimer,
numberBoxInputEvent,
}
}
@@ -0,0 +1,3 @@
import type NumberBox from './number-box.vue'
export type TnNumberBoxInstance = InstanceType<typeof NumberBox>
@@ -0,0 +1,109 @@
import { useComponentSizeProp } from '../../base/composables/use-component-common-props'
import {
CHANGE_EVENT,
INPUT_EVENT,
UPDATE_MODEL_EVENT,
} from '../../../constants'
import { buildProps, isNumber } from '../../../utils'
import type { ExtractPropTypes } from 'vue'
export const numberBoxProps = buildProps({
/**
* @description 步进器绑定的值
*/
modelValue: {
type: Number,
default: 0,
},
/**
* @description 步进器的尺寸
*/
size: useComponentSizeProp,
/**
* @description 步进器的宽度
*/
width: String,
/**
* @description 步进器的高度
*/
height: String,
/**
* @description 文字大小
*/
fontSize: String,
/**
* @description 步进器背景颜色,以tn开头则使用图鸟内置的颜色只支持普通颜色
*/
bgColor: String,
/**
* @description 步进器字体颜色,以tn开头则使用图鸟内置的颜色只支持普通颜色
*/
textColor: String,
/**
* @description 步进器的最小值
*/
min: {
type: Number,
default: 0,
},
/**
* @description 步进器的最大值
*/
max: {
type: Number,
default: 100,
},
/**
* @description 步进器的步长
*/
step: {
type: Number,
default: 1,
},
/**
* @description 禁止步进器操作
*/
disabled: Boolean,
/**
* @description 禁止步进器输入
*/
inputDisabled: Boolean,
/**
* @description 输入框与键盘的间距,单位px
*/
inputSpacing: {
type: Number,
default: 20,
},
/**
* @description 长按递增减
*/
longPress: {
type: Boolean,
default: true,
},
/**
* @description 长按递增减的间隔时间,单位ms
*/
longPressInterval: {
type: Number,
default: 250,
},
/**
* @description 值发生修改时是否触发表单验证
*/
validateEvent: {
type: Boolean,
default: true,
},
})
export const numberBoxEmits = {
[UPDATE_MODEL_EVENT]: (val: number) => isNumber(val),
[CHANGE_EVENT]: (val: number) => isNumber(val),
[INPUT_EVENT]: (val: number) => isNumber(val),
}
export type NumberBoxProps = ExtractPropTypes<typeof numberBoxProps>
export type NumberBoxEmits = typeof numberBoxEmits
@@ -0,0 +1,79 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import { numberBoxEmits, numberBoxProps } from './number-box'
import { useNumberBox, useNumberBoxCustomStyle } from './composables'
const props = defineProps(numberBoxProps)
defineEmits(numberBoxEmits)
const {
inputValue,
handleOperationEvent,
clearLongPressTimer,
numberBoxInputEvent,
} = useNumberBox(props)
const {
ns,
numberBoxClass,
numberBoxStyle,
numberBoxOperationWrapperClass,
numberBoxOperationWrapperStyle,
} = useNumberBoxCustomStyle(props, inputValue)
</script>
<template>
<view :class="[numberBoxClass]" :style="numberBoxStyle">
<!-- 减操作按钮 -->
<view
:class="[
ns.e('operation-btn'),
ns.em('operation-btn', 'minus'),
numberBoxOperationWrapperClass('minus'),
]"
:style="numberBoxOperationWrapperStyle('minus')"
hover-class="tn-u-btn-hover"
:hover-stay-time="250"
@touchstart.stop.prevent="handleOperationEvent('minus')"
@touchend.stop.prevent="clearLongPressTimer"
>
<slot name="minus">
<TnIcon name="reduce" />
</slot>
</view>
<!-- 输入框 -->
<view
:class="[ns.e('input'), numberBoxOperationWrapperClass('input')]"
:style="numberBoxOperationWrapperStyle('input')"
>
<input
v-model.number="inputValue"
type="digit"
:disabled="disabled || inputDisabled"
@input="numberBoxInputEvent"
/>
</view>
<!-- 加操作按钮 -->
<view
:class="[
ns.e('operation-btn'),
ns.em('operation-btn', 'plus'),
numberBoxOperationWrapperClass('plus'),
]"
:style="numberBoxOperationWrapperStyle('plus')"
hover-class="tn-u-btn-hover"
:hover-stay-time="250"
@touchstart.stop.prevent="handleOperationEvent('plus')"
@touchend.stop.prevent="clearLongPressTimer"
>
<slot name="plus">
<TnIcon name="add" />
</slot>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/number-box.scss';
</style>