hookehuyr

refactor(components): 重构 SearchBar 组件并更新搜索页面

- 将 SearchBar 组件从原生 input 重构为 NutUI Input 实现,简化样式和逻辑
- 更新 components.d.ts 类型声明,移除未使用的 NutConfigProvider,修正 PlanPopup 导入路径
- 重构搜索页面,使用 NutTabs 实现分类切换,支持长列表测试数据
- 为搜索结果项添加动画效果,优化用户体验
......@@ -16,7 +16,6 @@ declare module 'vue' {
NavHeader: typeof import('./src/components/NavHeader.vue')['default']
NutAvatar: typeof import('@nutui/nutui-taro')['Avatar']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutPicker: typeof import('@nutui/nutui-taro')['Picker']
NutPopup: typeof import('@nutui/nutui-taro')['Popup']
......@@ -28,7 +27,7 @@ declare module 'vue' {
OfficeViewer: typeof import('./src/components/OfficeViewer.vue')['default']
PdfPreview: typeof import('./src/components/PdfPreview.vue')['default']
Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
PlanPopup: typeof import('./src/components/PlanSchemes/PlanPopup.vue')['default']
PlanPopup: typeof import('./src/components/PlanPopup/index.vue')['default']
PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
QrCode: typeof import('./src/components/qrCode.vue')['default']
QrCodeSearch: typeof import('./src/components/qrCodeSearch.vue')['default']
......
<template>
<view
class="search-bar flex items-center"
:class="containerClass"
>
<!-- Search Icon -->
<IconFont
name="search"
:size="iconSize"
:color="iconColor"
class="flex-shrink-0 mr-[16rpx]"
/>
<!-- Input -->
<input
<view class="search-bar" :class="containerClass">
<!-- NutUI Input 组件 -->
<nut-input
v-model="internalValue"
:type="inputType"
:placeholder="placeholder"
:placeholder-class="placeholderClass"
:class="inputClass"
:disabled="disabled"
@focus="handleFocus"
confirm-type="search"
:clearable="showClear"
class="search-input"
@clear="handleClear"
@blur="handleBlur"
@input="handleInput"
@focus="handleFocus"
@confirm="handleSearch"
/>
<!-- Clear Button -->
<IconFont
v-if="showClear && internalValue"
name="close"
:size="clearIconSize"
:color="clearIconColor"
class="flex-shrink-0 ml-[16rpx]"
@tap="handleClear"
/>
>
<template #left>
<IconFont
name="search"
:size="iconSize"
:color="iconColor"
class="mr-[8rpx]"
/>
</template>
</nut-input>
</view>
</template>
......@@ -42,9 +31,9 @@ import { ref, watch, computed } from 'vue'
import IconFont from '@/components/IconFont.vue'
/**
* SearchBar 组件
* SearchBar 组件(基于 NutUI Input)
*
* @description 可复用的搜索栏组件,支持多种样式变体
* @description 可复用的搜索栏组件,使用 NutUI Input 组件实现
* @author Claude Code
* @example
* <SearchBar
......@@ -136,24 +125,6 @@ const props = defineProps({
iconColor: {
type: String,
default: '#9CA3AF'
},
/**
* 清除图标大小
* @type {number|string}
* @default 16
*/
clearIconSize: {
type: [Number, String],
default: 16
},
/**
* 清除图标颜色
* @type {string}
* @default '#9CA3AF'
*/
clearIconColor: {
type: String,
default: '#9CA3AF'
}
})
......@@ -198,41 +169,17 @@ const internalValue = ref(props.modelValue)
// 容器样式类
const containerClass = computed(() => {
const base = [
'bg-white',
'shadow-sm',
'px-[40rpx]', // 左右 padding
props.variant === 'rounded' ? 'rounded-full' : 'rounded-[20rpx]'
]
if (props.showBorder) {
base.push('border', 'border-gray-200')
} else {
base.push('border', 'border-gray-50')
}
const classes = ['search-bar']
// 高度样式
if (props.variant === 'rounded') {
base.push('h-[88rpx]')
} else {
base.push('py-[24rpx]')
classes.push('search-bar-rounded')
}
return base.join(' ')
})
// 占位符样式类
const placeholderClass = 'text-gray-400 text-[28rpx]'
if (props.showBorder) {
classes.push('search-bar-bordered')
}
// 输入框样式类
const inputClass = computed(() => {
return [
'flex-1',
'text-[28rpx]',
'bg-transparent',
'outline-none',
props.disabled ? 'opacity-50' : ''
].filter(Boolean).join(' ')
return classes.join(' ')
})
// 监听 modelValue 变化
......@@ -243,12 +190,14 @@ watch(() => props.modelValue, (newValue) => {
// 监听内部值变化,触发更新
watch(internalValue, (newValue) => {
emit('update:modelValue', newValue)
emit('input', newValue)
})
/**
* 处理获得焦点
*/
function handleFocus() {
console.log('[SearchBar Component] 获得焦点')
emit('focus')
}
......@@ -256,27 +205,25 @@ function handleFocus() {
* 处理失去焦点
*/
function handleBlur() {
console.log('[SearchBar Component] 失去焦点')
emit('blur')
}
/**
* 处理输入
*/
function handleInput(e) {
emit('input', internalValue.value)
}
/**
* 处理搜索(回车)
*/
function handleSearch() {
console.log('[SearchBar Component] handleSearch 被调用')
console.log('[SearchBar Component] 当前输入值:', internalValue.value)
emit('search', internalValue.value)
console.log('[SearchBar Component] search 事件已发送')
}
/**
* 清除输入
*/
function handleClear() {
console.log('[SearchBar Component] 清除输入')
internalValue.value = ''
emit('clear')
emit('update:modelValue', '')
......@@ -284,5 +231,30 @@ function handleClear() {
</script>
<style lang="less" scoped>
/* 样式通过 TailwindCSS 类控制 */
.search-bar {
padding: 0;
background: white;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
:deep(.nut-input) {
padding: 24rpx 32rpx;
background-color: transparent;
}
&.search-bar-rounded {
border-radius: 9999rpx;
:deep(.nut-input) {
border-radius: 9999rpx;
}
}
&.search-bar-bordered {
border: 1px solid #e5e7eb;
}
}
.search-bar-rounded {
height: 88rpx;
}
</style>
......
This diff is collapsed. Click to expand it.