😎增加字典互斥配置

This commit is contained in:
zuohuaijun 2025-08-18 11:39:06 +08:00
parent 417a0a19d2
commit a871c31e90

View File

@ -40,10 +40,22 @@ interface DictItem {
tagType?: TagType; tagType?: TagType;
styleSetting?: string; styleSetting?: string;
classSetting?: string; classSetting?: string;
disabled?: boolean;
label?: string; label?: string;
value?: string | number; value?: string | number;
} }
/**
* 互斥选项配置
* @type {Object}
* @property {string|number} value - 触发互斥的选项值
* @property {Array<string|number>} excludes - 被互斥的选项值列表
*/
interface MutexConfig {
value: string | number;
excludes: (string | number)[];
}
/** /**
* 多选值模式枚举 * 多选值模式枚举
* @enum {string} * @enum {string}
@ -81,7 +93,7 @@ import { reactive, watch, computed, PropType } from 'vue';
import { useUserInfo } from '/@/stores/userInfo'; import { useUserInfo } from '/@/stores/userInfo';
const userStore = useUserInfo(); const userStore = useUserInfo();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue', 'change']);
/** /**
* 组件属性定义 * 组件属性定义
@ -206,6 +218,19 @@ const props = defineProps({
default: MultipleModel.Array, default: MultipleModel.Array,
validator: isMultipleModel, validator: isMultipleModel,
}, },
/**
* 互斥配置项仅在多选模式下有效
* @type {Array<MutexConfig>}
* @example
* :mutex-configs="[
* { value: 'all', excludes: ['1', '2', '3'] },
* { value: '1', excludes: ['all'] }
* ]"
*/
mutexConfigs: {
type: Array as PropType<MutexConfig[]>,
default: () => [],
},
}); });
/** /**
@ -214,11 +239,26 @@ const props = defineProps({
* @returns {DictItem[]} - 过滤并格式化后的字典数据 * @returns {DictItem[]} - 过滤并格式化后的字典数据
*/ */
const formattedDictData = computed(() => { const formattedDictData = computed(() => {
return state.dictData.filter(props.onItemFilter).map((item) => ({ const baseData = state.dictData.filter(props.onItemFilter).map((item) => ({
...item, ...item,
label: item[props.propLabel], label: item[props.propLabel],
value: item[props.propValue], value: item[props.propValue],
})); }));
//
if (!props.multiple || !props.mutexConfigs || props.mutexConfigs.length === 0) {
return baseData;
}
//
return baseData.map((item) => {
//
const isDisabled = isItemDisabled(item.value, state.value, props.mutexConfigs);
return {
...item,
disabled: isDisabled || item.disabled, // disabled
};
});
}); });
/** /**
@ -337,16 +377,83 @@ const parseMultipleValue = (value: any): any => {
return value; return value;
}; };
/**
* 检查选项是否应该被禁用
* @function
* @param {string|number} itemValue - 当前选项的值
* @param {any} currentValue - 当前选中的值
* @param {MutexConfig[]} mutexConfigs - 互斥配置列表
* @returns {boolean} - 是否应该禁用
*/
const isItemDisabled = (itemValue: string | number, currentValue: any, mutexConfigs: MutexConfig[]): boolean => {
//
if (!mutexConfigs || mutexConfigs.length === 0) {
return false;
}
//
const selectedValues = Array.isArray(currentValue) ? currentValue : currentValue ? [currentValue] : [];
//
for (const config of mutexConfigs) {
//
if (selectedValues.includes(config.value) && config.excludes.includes(itemValue)) {
return true;
}
//
if (itemValue === config.value && config.excludes.some((exclude) => selectedValues.includes(exclude))) {
return true;
}
}
return false;
};
/**
* 互斥处理函数
* @function
* @param {any} newValue - 新选中的值
* @param {MutexConfig[]} mutexConfigs - 互斥配置列表
* @returns {any} - 处理后的值
*/
const handleMutex = (newValue: any, mutexConfigs: MutexConfig[]): any => {
//
if (!mutexConfigs || mutexConfigs.length === 0) {
return newValue;
}
//
if (!props.multiple) {
return newValue;
}
//
// formattedDictData
let resultValue = Array.isArray(newValue) ? [...newValue] : newValue ? [newValue] : [];
//
const validValues = formattedDictData.value.filter((item) => !item.disabled).map((item) => item.value);
return resultValue.filter((val) => validValues.includes(val));
};
/** /**
* 更新绑定值修复逗号模式问题 * 更新绑定值修复逗号模式问题
* @function * @function
* @param {any} newValue - 新值 * @param {any} newValue - 新值
*/ */
const updateValue = (newValue: any) => { const updateValue = (newValue: any) => {
// //
let processedValue = newValue; let processedValue = newValue;
if (Array.isArray(newValue)) { if (props.mutexConfigs && props.mutexConfigs.length > 0) {
processedValue = [...new Set(newValue.filter((v) => v !== null && v !== undefined && v !== ''))]; processedValue = handleMutex(newValue, props.mutexConfigs);
// console.log('[g-sys-dict] :', state.dictData);
}
//
if (Array.isArray(processedValue)) {
processedValue = [...new Set(processedValue.filter((v) => v !== null && v !== undefined && v !== ''))];
} }
let emitValue = processedValue; let emitValue = processedValue;
@ -361,6 +468,7 @@ const updateValue = (newValue: any) => {
state.value = processedValue; state.value = processedValue;
emit('update:modelValue', emitValue); emit('update:modelValue', emitValue);
emit('change', state.value, state.dictData);
}; };
/** /**
@ -416,7 +524,7 @@ watch(
} }
); );
watch(() => [userStore.dictList, userStore.constList], initData, { immediate: true }); watch(() => [userStore.dictList, userStore.constList, state], initData, { immediate: true });
</script> </script>
<template> <template>
@ -437,12 +545,12 @@ watch(() => [userStore.dictList, userStore.constList], initData, { immediate: tr
<!-- 渲染选择器 --> <!-- 渲染选择器 -->
<el-select v-else-if="props.renderAs === 'select'" v-model="state.value" v-bind="$attrs" :multiple="props.multiple" @change="updateValue" clearable> <el-select v-else-if="props.renderAs === 'select'" v-model="state.value" v-bind="$attrs" :multiple="props.multiple" @change="updateValue" clearable>
<el-option v-for="(item, index) in formattedDictData" :key="index" :label="getDisplayText(item)" :value="item.value" /> <el-option v-for="(item, index) in formattedDictData" :key="index" :label="getDisplayText(item)" :value="item.value" :disabled="item.disabled" />
</el-select> </el-select>
<!-- 渲染复选框多选 --> <!-- 渲染复选框多选 -->
<el-checkbox-group v-else-if="props.renderAs === 'checkbox'" v-model="state.value" v-bind="$attrs" @change="updateValue"> <el-checkbox-group v-else-if="props.renderAs === 'checkbox'" v-model="state.value" v-bind="$attrs" @change="updateValue">
<el-checkbox-button v-for="(item, index) in formattedDictData" :key="index" :value="item.value"> <el-checkbox-button v-for="(item, index) in formattedDictData" :key="index" :value="item.value" :disabled="item.disabled">
{{ getDisplayText(item) }} {{ getDisplayText(item) }}
</el-checkbox-button> </el-checkbox-button>
</el-checkbox-group> </el-checkbox-group>