zhzf/client/src/components/DictSelector.vue

303 lines
8.0 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.

<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>