zhzf/client/src/components/DictSelector.vue

303 lines
8.0 KiB
Vue
Raw Normal View History

2025-02-21 11:25:09 +08:00
<template>
<base-selector v-model="cvalue"
ref="bselect"
:config="config"
:optionProps="optionProps"
:data="data"
:total="total"
:disabled-options="disabledOptions"
:disabled-options-by="disabledOptionsBy"
:use-select-all="useSelectAll"
@show-more="showMore"
@remote-filter="queryDictItems"
@selected-clear="clear"
@visible-change="visibleChange"
v-bind="$attrs"
@change="change"
@select-load-option="initData">
</base-selector>
</template>
<script setup>
import BaseSelector from './BaseSelector.vue'
import { DICTS } from '@/utils/Constants'
import dictItems from '../api/system/DictItems'
import { computed, ref, watch, reactive, toRefs } from 'vue'
import { useConstantStore } from '@/stores/modules/constant'
const props = defineProps({
dictCode: String,
sort: { type: String, default: 'orderNum' },
dir: { type: String, default: 'asc' },
filterable: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
defaultOptions: { type: Array, default: () => [] },
modelValue: { // Vue3 v-model 替换原value
type: [Array, String, Number],
default: null
},
limit: {
type: Number,
default: 0
},
valueMarray: { type: String, default: null },
remote: { type: Boolean, default: false },
dictFilter: {
type: Function,
default: (dictItem, query, optionProps = { value: 'xxdm', label: 'xxdmmc' }) =>
!query ||
query === dictItem[optionProps.value] ||
dictItem[optionProps.label]?.includes(query) ||
(dictItem.simplePinyin?.includes(query)) ||
(dictItem.allPinyin?.includes(query))
},
banFilter: {
type: Function,
default: () => false
},
disabledOptions: Array,
disabledOptionsBy: Function,
useSelectAll: { type: Boolean, default: true }
})
const emit = defineEmits([
'update:modelValue',
'selected',
'visible-change',
'select-load-option'
])
const store = useConstantStore()
const bselect = ref(null)
// 响应式状态
const state = reactive({
params: {
dictCode: props.dictCode,
sort: props.sort,
dir: props.dir,
valueMarray: props.valueMarray
},
optionProps: { key: 'xxdmbh', label: 'xxdmmc', value: 'xxdm' },
page: 1,
lQueryParam: null,
cacheFirstData: { data: [], total: 0 },
data: [],
total: 0,
config: {
filterable: props.filterable,
multiple: props.multiple,
remote: props.remote
}
})
// 方法定义
const localFilterMethod = (q) => {
if (q) {
if (props.filterMethod && typeof props.filterMethod === 'function') {
state.data = props.filterMethod(q, state.cacheFirstData.data)
} else {
state.data = state.cacheFirstData.data.filter(dictItem => {
const value = dictItem[state.optionProps.value] || ''
const label = dictItem[state.optionProps.label] || ''
return value.includes(q) ||
label.includes(q) ||
dictItem.simplePinyin?.includes(q) ||
dictItem.allPinyin?.includes(q)
})
}
} else {
state.data = state.cacheFirstData.data
}
}
const change = (v) => {
if (props.multiple) {
const temp = v.map(vl =>
state.data.find(d => d[state.optionProps.value] === vl)
)
emit('selected', v, temp, state.data)
} else {
emit('selected', v, state.data.find(d => d[state.optionProps.value] === v), state.data)
}
}
const visibleChange = (opened) => {
if (!opened && !state.data.length) {
queryDictItems()
}
}
const initData = () => {
if (props.remote && props.modelValue) {
queryDictItems(null, props.modelValue)
} else {
queryDictItems()
}
}
const queryDictItems = async (select, query, isShowMore = false) => {
if (!props.dictCode) return
if (state.lQueryParam !== query) {
state.page = 1
state.lQueryParam = query
}
try {
// 根据不同的字典类型调用不同的接口
let res;
if (state.params.dictCode === DICTS.DM_XZQH) {
res = await dictItems.query({
page: state.page,
pagesize: props.limit > 0 ? props.limit : undefined,
...state.params,
queryParam: query
})
} else {
// 使用新接口 queryGzt
res = await dictItems.queryGzt({
...state.params,
queryParam: query
})
}
let pageable = null
let data = []
// 处理不同类型的返回结果
if (Array.isArray(res)) {
// 如果直接返回数组store.queryDictData 的情况)
data = res
} else if (res?.success) {
if (state.params.dictCode !== DICTS.DM_XZQH) {
// 处理 queryGzt 返回的数据
data = res.data || []
} else {
// 如果返回对象dictItems.query 的情况)
pageable = {
page: res.page,
pagesize: res.pagesize,
limit: res.pagesize,
total: res.total
}
data = res.data
}
}
if (state.params.dictCode !== DICTS.DM_XZQH && props.valueMarray) {
const valueArray = props.valueMarray.split(',')
data = data.filter(resmap =>
valueArray.some(va => resmap[state.optionProps.value].startsWith(va))
)
if (pageable) pageable.total = data.length
}
responseProcess([data, isShowMore, query, pageable])
} catch (error) {
console.error('字典项查询失败:', error)
}
}
const showMore = (select) => {
state.page += 1
queryDictItems(select, state.lQueryParam, true)
}
const clear = (select) => {
state.total = state.cacheFirstData.total
state.data = state.cacheFirstData.data
state.page = 1
queryDictItems(select, '', false)
}
const responseProcess = ([data, isShowMore, query, pageable]) => {
// 确保数据是数组
if (!Array.isArray(data)) {
console.error('字典数据格式错误:', data)
return
}
if (props.limit > 0) {
if (isShowMore) {
if (pageable) {
state.data = [...state.data, ...data]
state.total = pageable.total
} else {
const filterData = data
.filter(d => props.dictFilter(d, query, state.optionProps))
.filter(d => !props.banFilter(d))
state.data.push(...filterData.slice(
(state.page - 1) * props.limit,
state.page * props.limit
))
state.total = filterData.length
}
} else {
if (pageable) {
state.data = data
state.total = pageable.total
} else {
state.page = 1
const filterData = data
.filter(d => props.dictFilter(d, query, state.optionProps))
.filter(d => !props.banFilter(d))
state.data = filterData.slice(
(state.page - 1) * props.limit,
state.page * props.limit
)
state.total = filterData.length
state.cacheFirstData = { data: state.data, total: state.total }
}
}
} else {
state.data = data.filter(d => !props.banFilter(d))
state.total = 0
state.page = 1
state.cacheFirstData = { data: state.data, total: 0 }
}
}
// 计算属性
const cvalue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// 暴露组件方法给父组件
defineExpose({
queryDictItems,
clear,
showMore
})
// 监听器
watch(() => props.dictCode, (code) => {
if (code) {
state.params.dictCode = code
queryDictItems(bselect.value)
}
})
watch(() => props.modelValue, (v) => {
if (props.remote && v) {
// 检查当前选中的值是否在已加载的数据中
const hasValue = Array.isArray(v)
? v.every(item => state.data.some(d => d[state.optionProps.value] === item))
: state.data.some(d => d[state.optionProps.value] === v)
if (!hasValue) {
state.page = 1
queryDictItems(bselect.value, v)
}
}
})
watch(() => props.valueMarray, (v) => {
state.params.valueMarray = v
queryDictItems(bselect.value)
})
const {data,total,page,cacheFirstData,optionProps,config} = toRefs(state)
</script>