chat-pc/src/components/x-naive-ui/x-search-form/index.vue

272 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { cloneDeep, debounce } from "lodash-es";
import { computed, onBeforeUnmount, ref } from "vue";
import { NButton, NForm, NFormItemGi, NGrid, NInput, NSelect, NDatePicker } from 'naive-ui'
const props = defineProps({
title: {
type: String,
default: '',
},
cols: {
type: [Number, String],
default: 4,
},
searchConfig: {
type: Array,
default: () => ([]),
},
xGap: {
type: [Number, String],
default: 81,
},
yGap: {
type: [Number, String],
default: 20,
},
autoSearch: {
type: Boolean,
default: true,
},
debounceWait: {
type: Number,
default: 300,
},
loading: {
type: Boolean,
default: false,
}
})
const emit = defineEmits(['change', 'init'])
const resetForm = ref({})
const formData = ref({})
const indexRef = ref(0)
const datePickerRef = ref(null)
const formRef = ref(null)
// 防抖搜索函数
const debouncedSearch = debounce(() => {
emit('change', formData.value)
}, props.debounceWait)
// 清理防抖函数
onBeforeUnmount(() => {
debouncedSearch.cancel()
})
// 优化计算属性
const calculateEmptySpans = computed(() => {
const num = props.searchConfig?.reduce((acc, cur) =>
acc + (cur.col || 1), 0
)
const remainder = num % props.cols
return remainder === 0 ? props.cols - 1 : props.cols - remainder - 1
})
// 优化表单值变更处理
const manualChange = () => {
if (props.autoSearch) {
debouncedSearch()
}
}
const handleInputChange = (value, item) => {
// 如果是pair类型的输入
if (item.props?.pair && Array.isArray(item.key)) {
// 确保value是数组如果不是则转换为数组
const valueArray = Array.isArray(value) ? value : [value, value];
// 获取默认值来判断数据类型
const defaultValues = Array.isArray(item.default) ? item.default : [];
// 分别赋值给对应的key并根据default的类型进行转换
item.key.forEach((key, index) => {
const defaultValue = defaultValues[index];
const currentValue = valueArray[index] || '';
// 如果默认值是数字类型,则转换为数字
if (typeof defaultValue === 'number') {
formData.value[key] = currentValue === '' ? defaultValue : Number(currentValue);
} else {
formData.value[key] = currentValue;
}
});
} else {
// 处理普通输入
formData.value[item.key] = value;
}
manualChange();
};
const handleSelectChange = (value, item) => {
formData.value[item.key] = value
console.log('formData.value[item.key]',formData.value[item.key]);
manualChange()
}
const handleDateChange = (value, key) => {
if (Array.isArray(key)) {
key.forEach((k, i) => {
formData.value[k] = value?.[i]
})
} else {
formData.value[key] = value
}
manualChange()
}
const search = async () => {
if (!formRef.value) {
emit('change', formData.value)
return
}
try {
await formRef.value?.validate()
emit('change', formData.value)
} catch (errors) {
console.error('表单验证失败:', errors)
}
}
// 优化重置函数
const reset = () => {
formData.value = cloneDeep(resetForm.value)
emit('change', formData.value)
indexRef.value++
if (formRef.value) {
formRef.value?.restoreValidation()
}
}
// 优化表单对象生成
const generateFormObject = (config) => {
return config.reduce((formObject, field) => {
const defaultValue = field.default ?? undefined;
if (Array.isArray(field.key)) {
if (Array.isArray(defaultValue)) {
field.key.forEach((subKey, index) => {
// 使用原始默认值,保持类型一致
formObject[subKey] = defaultValue[index];
});
} else {
field.key.forEach(subKey => {
formObject[subKey] = defaultValue;
});
}
} else {
formObject[field.key] = defaultValue;
}
return formObject;
}, {});
};
// 初始化函数
const initData = () => {
const initialData = generateFormObject(props.searchConfig)
formData.value = initialData
resetForm.value = cloneDeep(initialData)
emit('init', initialData)
}
// 初始化
initData()
</script>
<template>
<div class="search-form w-[100%] pb-[20px] bg-[#fff] rounded-[3px] overflow-hidden">
<div
v-if="title"
class="search-form__header h-[59px] border-b-[2px] border-[#EAEAEA]"
>
<div class="text-[#1F2225] text-[18px] font-[600] h-[100%] flex justify-center align-center w-fit border-b-[4px] border-[#46299D]">
{{ title }}
</div>
</div>
<n-form
ref="formRef"
class="mt-[20px]"
:show-feedback="false"
label-placement="left"
label-align="left"
label-width="110"
:key="indexRef"
>
<n-grid :cols="cols" :x-gap="xGap" :y-gap="yGap">
<n-form-item-gi
v-for="item in searchConfig"
:key="item.key"
:span="item.col || 1"
:label="item.label"
:rule="item.rule"
>
<!-- Input 类型 -->
<template v-if="item.type === 'input'">
<n-input
:value="formData[item.key]"
placeholder="请输入"
@input="value => handleInputChange(value, item)"
clearable
v-bind="item.props"
/>
</template>
<!-- Select 类型 -->
<template v-else-if="item.type === 'select'">
<n-select
:value="formData[item.key]"
placeholder="请选择"
@update:value="value => handleSelectChange(value, item)"
clearable
v-bind="item.props"
/>
</template>
<!-- DatePicker 类型 -->
<template v-else-if="item.type === 'date-picker'">
<n-date-picker
ref="datePickerRef"
class="w-[100%]"
clearable
:value="formData[item.key]"
@update-formatted-value="value => handleDateChange(value, item.key)"
v-bind="item.props"
/>
</template>
</n-form-item-gi>
<!-- 空白占位 -->
<n-form-item-gi :span="calculateEmptySpans" />
<!-- 操作按钮 -->
<n-form-item-gi :span="1">
<div class="flex justify-end w-full">
<n-button
class="w-[145px] h-[34px] mr-[20px]"
@click="search"
type="primary"
:loading="loading"
>
查询
</n-button>
<n-button
class="w-[145px] h-[34px]"
color="#EEE9F8"
text-color="#46299D"
@click="reset"
>
重置
</n-button>
</div>
</n-form-item-gi>
</n-grid>
</n-form>
</div>
</template>
<style scoped lang="scss">
.search-form {
&__header {
margin-bottom: 16px;
}
}
</style>