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
@@ -0,0 +1,2 @@
export * from './search-box-custom'
export * from './use-search-box'
@@ -0,0 +1,124 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import type { CSSProperties } from 'vue'
import type { SearchBoxProps } from '../search-box'
export const useSearchBoxCustomStyle = (props: SearchBoxProps) => {
const ns = useNamespace('search-box')
// 解析颜色
const [textColorClass, textColorStyle] = useComponentColor(
toRef(props, 'textColor'),
'text'
)
const [borderColorClass, borderColorStyle] = useComponentColor(
toRef(props, 'borderColor'),
'border'
)
const [placeholderColorClass, placeholderColorStyle] = useComponentColor(
toRef(props, 'placeholderColor'),
'text'
)
const [searchButtonTextColorClass, searchButtonTextColorStyle] =
useComponentColor(toRef(props, 'searchButtonTextColor'), 'text')
const [searchButtonBgColorClass, searchButtonBgColorStyle] =
useComponentColor(toRef(props, 'searchButtonBgColor'), 'bg')
// 搜索框的类
const searchBoxClass = computed<string>(() => {
const cls: string[] = [
ns.b(),
ns.m(props.shape),
ns.is('no-search-btn', !props.searchButton),
ns.is('disabled', props.disabled),
]
if (props.border) {
cls.push(ns.m('border'))
if (borderColorClass.value) cls.push(borderColorClass.value)
}
if (props.size) cls.push(ns.m(props.size))
if (textColorClass.value) cls.push(textColorClass.value)
return cls.join(' ')
})
// 搜索框的样式
const searchBoxStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!textColorClass.value)
style.color = textColorStyle.value || 'var(--tn-text-color-primary)'
if (props.border) {
if (!borderColorClass.value) {
style.borderColor = borderColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
// 搜索框占位文字的类
const placeholderClass = computed<string>(() => {
const cls: string[] = [
ns.e('placeholder'),
ns.em('placeholder', props.textAlign),
]
if (placeholderColorClass.value) cls.push(placeholderColorClass.value)
return cls.join(' ')
})
// 搜索框占位文字的样式
const placeholderStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!placeholderColorClass.value)
style.color =
placeholderColorStyle.value || 'var(--tn-text-color-secondary)'
return style
})
// 搜索按钮的类
const searchButtonClass = computed<string>(() => {
const cls: string[] = [ns.e('search-button')]
if (searchButtonBgColorClass.value) cls.push(searchButtonBgColorClass.value)
if (searchButtonTextColorClass.value)
cls.push(searchButtonTextColorClass.value)
return cls.join(' ')
})
// 搜索按钮的样式
const searchButtonStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!searchButtonBgColorClass.value)
style.backgroundColor =
searchButtonBgColorStyle.value || 'var(--tn-color-primary)'
if (searchButtonTextColorStyle.value) {
style.color = searchButtonTextColorStyle.value
} else if (
!searchButtonBgColorClass.value &&
!searchButtonTextColorClass.value
) {
style.color = 'var(--tn-color-white)'
}
return style
})
return {
ns,
searchBoxClass,
searchBoxStyle,
placeholderClass,
placeholderStyle,
searchButtonClass,
searchButtonStyle,
}
}
@@ -0,0 +1,96 @@
import { nextTick, ref, watch } from 'vue'
import { debounce, throttle } from '../../../../libs/lodash'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import type { SetupContext } from 'vue'
import type { SearchBoxEmits, SearchBoxProps } from '../search-box'
export const useSearchBox = (
props: SearchBoxProps,
emits: SetupContext<SearchBoxEmits>['emit']
) => {
// 是否显示placeholder
const showPlaceholder = ref<boolean>(!props.modelValue)
// 输入框的值
const inputValue = ref<string>(props.modelValue)
watch(
() => props.modelValue,
(val) => {
if (props.modelValue === inputValue.value) return
inputValue.value = val
showPlaceholder.value = !val
}
)
// 输入框聚焦
const inputFocus = ref<boolean>(false)
if (props.focus) {
showPlaceholder.value = false
nextTick(() => {
inputFocus.value = true
})
}
// searchBox点击事件
const searchBoxClickEvent = () => {
emits('click')
if (props.disabled) return
showPlaceholder.value = false
inputFocus.value = true
}
// 输入框获取焦点事件
const inputFocusEvent = () => {
emits('focus')
}
// 输入框失去焦点事件
const inputBlurEvent = () => {
showPlaceholder.value = !inputValue.value
inputFocus.value = false
emits('blur')
}
// 输入框输入事件
const inputHandle = () => {
emits(UPDATE_MODEL_EVENT, inputValue.value)
nextTick(() => {
emits(CHANGE_EVENT, inputValue.value)
emits('input', inputValue.value)
})
}
const inputValueEvent = props.throllte
? throttle(inputHandle, props.throllteTime)
: inputHandle
// 清除按钮点击事件
const clearClickEvent = () => {
inputValue.value = ''
emits(UPDATE_MODEL_EVENT, '')
nextTick(() => {
inputFocus.value = true
emits(CHANGE_EVENT, '')
emits('clear')
})
}
// 点击search按钮事件
const searchBtnClickEvent = debounce(() => {
if (props.disabled) return
emits('search', inputValue.value)
}, 250)
return {
showPlaceholder,
inputValue,
inputFocus,
searchBoxClickEvent,
inputFocusEvent,
inputBlurEvent,
inputValueEvent,
clearClickEvent,
searchBtnClickEvent,
}
}
@@ -0,0 +1,3 @@
import type SearchBox from './search-box.vue'
export type TnSearchBoxInstance = InstanceType<typeof SearchBox>
@@ -0,0 +1,156 @@
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import { buildProps, isString } from '../../../utils'
import { useComponentSizeProp } from '../../base/composables/use-component-common-props'
import type { ExtractPropTypes } from 'vue'
export const searchBoxShape = ['square', 'round'] as const
export const searchBoxAlign = ['left', 'center', 'right'] as const
export const searchBoxProps = buildProps({
/**
* @description 搜索框绑定的值
*/
modelValue: {
type: String,
default: '',
},
/**
* @description 搜索框的占位符
*/
placeholder: {
type: String,
default: '请输入搜索内容',
},
/**
* @description 搜索框占位符旁边的图标
*/
placeholderIcon: {
type: String,
default: 'search',
},
/**
* @description 搜索框的形状
*/
shape: {
type: String,
values: searchBoxShape,
default: 'square',
},
/**
* @description 搜索框的尺寸,可以设置 `sm`、`lg`、`xl`
*/
size: useComponentSizeProp,
/**
* @description 搜索框文字的颜色,以tn开头使用图鸟的颜色
*/
textColor: String,
/**
* @description 搜索框占位文字颜色,以tn开头使用图鸟的颜色
*/
placeholderColor: String,
/**
* @description 搜索框文字对齐方式
*/
textAlign: {
type: String,
values: searchBoxAlign,
default: 'left',
},
/**
* @description 显示边框
*/
border: {
type: Boolean,
default: true,
},
/**
* @description 边框颜色
*/
borderColor: String,
/**
* @description 获取搜索框焦点
*/
focus: Boolean,
/**
* @description 是否禁用搜索框
*/
disabled: Boolean,
/**
* @description 是否显示清除按钮
*/
clearable: {
type: Boolean,
default: true,
},
/**
* @description 是否显示搜索按钮
*/
searchButton: {
type: Boolean,
default: true,
},
/**
* @description 搜索按钮的文字
*/
searchButtonText: {
type: String,
default: '搜 索',
},
/**
* @description 搜索按钮字体颜色,以tn开头使用图鸟的颜色
*/
searchButtonTextColor: String,
/**
* @description 搜索按钮背景颜色,以tn开头使用图鸟的颜色
*/
searchButtonBgColor: String,
/**
* @description 输入是否节流
*/
throllte: {
type: Boolean,
default: true,
},
/**
* @description 节流延迟时间,单位毫秒
*/
throllteTime: {
type: Number,
default: 1000,
},
})
export const searchBoxEmits = {
[UPDATE_MODEL_EVENT]: (value: string) => isString(value),
[CHANGE_EVENT]: (value: string) => isString(value),
/**
* @description 搜索框输入时触发
*/
input: (value: string) => isString(value),
/**
* @description 点击搜索框时触发
*/
click: () => true,
/**
* @description 聚焦搜索输入框时触发
*/
focus: () => true,
/**
* @description 搜索输入框失去焦点时触发
*/
blur: () => true,
/**
* @description 点击搜索按钮时触发
*/
search: (value: string) => isString(value),
/**
* @description 点击清除按钮时触发
*/
clear: () => true,
}
export type SearchBoxProps = ExtractPropTypes<typeof searchBoxProps>
export type SearchBoxEmits = typeof searchBoxEmits
export type SearchBoxShape = (typeof searchBoxShape)[number]
@@ -0,0 +1,94 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import { searchBoxEmits, searchBoxProps } from './search-box'
import { useSearchBox, useSearchBoxCustomStyle } from './composables'
const props = defineProps(searchBoxProps)
const emits = defineEmits(searchBoxEmits)
const {
showPlaceholder,
inputValue,
inputFocus,
searchBoxClickEvent,
inputFocusEvent,
inputBlurEvent,
inputValueEvent,
clearClickEvent,
searchBtnClickEvent,
} = useSearchBox(props, emits)
const {
ns,
searchBoxClass,
searchBoxStyle,
placeholderClass,
placeholderStyle,
searchButtonClass,
searchButtonStyle,
} = useSearchBoxCustomStyle(props)
</script>
<template>
<view
:class="[searchBoxClass]"
:style="searchBoxStyle"
@tap.stop="searchBoxClickEvent"
>
<!-- 搜索框内容输入区 -->
<view :class="[ns.e('content')]">
<view
v-if="showPlaceholder"
:class="[placeholderClass]"
:style="placeholderStyle"
>
<slot name="placeholder">
<view v-if="placeholderIcon" :class="[ns.e('placeholder-icon')]">
<TnIcon :name="placeholderIcon" />
</view>
<view v-if="placeholder" :class="[ns.e('placeholder-text')]">
{{ placeholder }}
</view>
</slot>
</view>
<template v-else>
<!-- input输入框 -->
<input
v-model="inputValue"
:class="[ns.e('input'), ns.em('input', textAlign)]"
:focus="inputFocus"
confirm-type="search"
@focus="inputFocusEvent"
@blur="inputBlurEvent"
@input="inputValueEvent"
@confirm="searchBtnClickEvent"
/>
<!-- 清除按钮 -->
<view
v-if="clearable && inputValue"
:class="[ns.e('clear-button')]"
@tap.stop="clearClickEvent"
>
<TnIcon name="close-fill" />
</view>
</template>
</view>
<!-- 搜索按钮 -->
<view
v-if="searchButton"
:class="[searchButtonClass]"
:style="searchButtonStyle"
hover-class="tn-u-btn-hover"
:hover-stay-time="150"
@tap.stop="searchBtnClickEvent"
>
<slot name="search">
{{ searchButtonText }}
</slot>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/search-box.scss';
</style>