303 lines
8.0 KiB
Vue
303 lines
8.0 KiB
Vue
<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>
|