diff --git a/Web/src/components/sysDict/sysDict.vue b/Web/src/components/sysDict/sysDict.vue index ea9da424..0cfd44d3 100644 --- a/Web/src/components/sysDict/sysDict.vue +++ b/Web/src/components/sysDict/sysDict.vue @@ -40,10 +40,22 @@ interface DictItem { tagType?: TagType; styleSetting?: string; classSetting?: string; + disabled?: boolean; label?: string; value?: string | number; } +/** + * 互斥选项配置 + * @type {Object} + * @property {string|number} value - 触发互斥的选项值 + * @property {Array} excludes - 被互斥的选项值列表 + */ +interface MutexConfig { + value: string | number; + excludes: (string | number)[]; +} + /** * 多选值模式枚举 * @enum {string} @@ -81,7 +93,7 @@ import { reactive, watch, computed, PropType } from 'vue'; import { useUserInfo } from '/@/stores/userInfo'; const userStore = useUserInfo(); -const emit = defineEmits(['update:modelValue']); +const emit = defineEmits(['update:modelValue', 'change']); /** * 组件属性定义 @@ -206,6 +218,19 @@ const props = defineProps({ default: MultipleModel.Array, validator: isMultipleModel, }, + /** + * 互斥配置项(仅在多选模式下有效) + * @type {Array} + * @example + * :mutex-configs="[ + * { value: 'all', excludes: ['1', '2', '3'] }, + * { value: '1', excludes: ['all'] } + * ]" + */ + mutexConfigs: { + type: Array as PropType, + default: () => [], + }, }); /** @@ -214,11 +239,26 @@ const props = defineProps({ * @returns {DictItem[]} - 过滤并格式化后的字典数据 */ const formattedDictData = computed(() => { - return state.dictData.filter(props.onItemFilter).map((item) => ({ + const baseData = state.dictData.filter(props.onItemFilter).map((item) => ({ ...item, label: item[props.propLabel], 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; }; +/** + * 检查选项是否应该被禁用 + * @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 * @param {any} newValue - 新值 */ const updateValue = (newValue: any) => { - // 先对值进行去重处理 + // 如果有互斥配置,先处理互斥 let processedValue = newValue; - if (Array.isArray(newValue)) { - processedValue = [...new Set(newValue.filter((v) => v !== null && v !== undefined && v !== ''))]; + if (props.mutexConfigs && props.mutexConfigs.length > 0) { + 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; @@ -361,6 +468,7 @@ const updateValue = (newValue: any) => { state.value = processedValue; 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 });