hookehuyr

fix(表单规则): 优化规则检查逻辑并修复级联隐藏问题

重构规则检查逻辑,支持多条规则评估和级联隐藏
修复条件判断逻辑错误,增加对空值的处理
添加对表单数据变化的监听,实时更新规则状态
<!--
* @Date: 2022-07-18 10:22:22
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-06-10 15:42:37
* @LastEditTime: 2025-07-03 11:08:36
* @FilePath: /data-table/src/views/index.vue
* @Description: 首页
-->
......@@ -575,6 +575,11 @@ onMounted(async () => {
approval_actions.value = formSetting.value.flow_node_action_list?.map((item) => { return { name: item.btnText, id: item.id, parent_nodes: item.parent_nodes } });
}, 1000);
// 初始化完成后执行规则检查
nextTick(() => {
checkRules();
});
setTimeout(() => {
const width = $('body').width();
// 固定表单宽度
......@@ -700,118 +705,190 @@ const mergeAndDeduplicate = (data) => {
});
}
// 根据规则隐藏相应字段
/**
* 根据规则隐藏相应字段
* 优化版本:修复了条件判断逻辑和规则处理顺序问题
*/
const checkRules = () => {
const rule_list = formInfo.value['rule_list'] ? [...formInfo.value['rule_list']] : [];
// TAG:处理同一字段多个规则情况
formData.value.forEach(item => {
item.x_rules = []; // 字段的规则
});
const rule_list = formInfo.value['rule_list'] ? [...formInfo.value['rule_list']] : [];
rule_list.forEach(rule => {
// 初始化所有字段的规则
formData.value.forEach(item => {
if (rule.field_names?.includes(item.key) && !rule.is_invalid) {
// 匹配字段的规则集合
item.x_rules.push({
mode: rule.mode,
logical_op: rule.logical_op,
expr_list: rule.expr_list,
})
}
})
});
item.x_rules = [];
// 重置disabled状态,避免上次规则影响
if (item.component_props) {
item.component_props.disabled = false;
}
});
formData.value.forEach(item => {
// 把规则合并,同一字段下的mode/logical_op必须一致
item.field_rules = mergeAndDeduplicate(item.x_rules)[0];
});
// 收集每个字段的规则
rule_list.forEach(rule => {
formData.value.forEach(item => {
if (rule.field_names?.includes(item.key) && !rule.is_invalid) {
item.x_rules.push({
mode: rule.mode,
logical_op: rule.logical_op,
expr_list: rule.expr_list,
});
}
});
});
formData.value.forEach(item => {
// 给受作用的字段绑定判断规则
// 规则失效需要踢出
// rule_list.forEach(rule => {
// if (rule.field_names?.includes(item.key) && !rule.is_invalid) {
// item.field_rules = {
// mode: rule.mode,
// logical_op: rule.logical_op,
// expr_list: rule.expr_list,
// }
// }
// });
// 只检查存在规则的字段
if (item.field_rules) {
let condition = '';
// 多个规则的满足条件,为全且或者全或
const op = item.field_rules?.logical_op === 'AND' ? '&&' : '||';
item.field_rules?.expr_list?.forEach(expr => {
let form_submission_value = postData.value[expr['field_name']]; // 表单提交值, field_12 : "" || field_13 : []
let rule_matching_value = expr['values']; // 规则匹配值 values : ['x']
if (typeof form_submission_value === 'string') { // 表单值为字符串(单选,下拉)
// 处理单选项带补充信息时判断,去除补充信息
if (form_submission_value.indexOf(':') !== -1) {
let parts = form_submission_value.split(':');
form_submission_value = parts[0];
}
const k = !!rule_matching_value.includes(form_submission_value); // 转换为布尔值
condition += `${k}${op}`;
}
if (typeof form_submission_value === 'object') { // 表单值为数组(多选)
// 处理多选项带补充信息时判断,去除补充信息
form_submission_value = form_submission_value?.map(item => {
if (item.includes(':')) {
return item.split(':')[0].trim(); // 去除冒号及其后面的部分并去除前后空格
}
return item;
});
const k = !!(_.intersection(rule_matching_value, form_submission_value)).length; // 转换为布尔值
condition += `${k}${op}`
// 合并规则
formData.value.forEach(item => {
const mergedRules = mergeAndDeduplicate(item.x_rules);
item.field_rules = mergedRules; // 保存所有合并后的规则
});
// 处理规则逻辑 - 支持多条规则
formData.value.forEach(item => {
if (item.field_rules && item.field_rules.length > 0 && item.component_props) {
// 处理多条规则的逻辑
const finalDisabledState = evaluateMultipleRules(item.field_rules);
item.component_props.disabled = finalDisabledState;
}
});
// 把结果转换为布尔值
if (item.field_rules?.logical_op === 'AND') {
if (condition.indexOf('false') >= 0) {
condition = false;
} else {
condition = true;
});
// 处理级联隐藏:如果规则字段被隐藏,其控制的字段也应该被隐藏
handleCascadeHiding();
};
/**
* 评估多条规则并确定最终的disabled状态
* @param {Array} rulesList - 字段的所有规则数组
* @returns {boolean} - 最终的disabled状态
*/
const evaluateMultipleRules = (rulesList) => {
if (!rulesList || rulesList.length === 0) {
return false; // 没有规则时默认显示
}
let showRules = [];
let hideRules = [];
// 分离SHOW和HIDE规则
rulesList.forEach(rule => {
if (rule.mode === 'SHOW') {
showRules.push(rule);
} else if (rule.mode === 'HIDE') {
hideRules.push(rule);
}
}
if (item.field_rules?.logical_op === 'OR') {
if (condition.indexOf('true') >= 0) {
condition = true;
} else {
condition = false;
});
// 评估SHOW规则
let showResult = true; // 默认显示
if (showRules.length > 0) {
// 如果有SHOW规则,需要至少一个SHOW规则条件满足才显示
showResult = showRules.some(rule => evaluateRuleCondition(rule));
}
// 评估HIDE规则
let hideResult = false; // 默认不隐藏
if (hideRules.length > 0) {
// 如果有HIDE规则,任何一个HIDE规则条件满足就隐藏
hideResult = hideRules.some(rule => evaluateRuleCondition(rule));
}
// 最终逻辑:HIDE规则优先级更高
// 如果任何HIDE规则满足,则隐藏
// 否则根据SHOW规则决定
if (hideResult) {
return true; // 隐藏
} else {
return !showResult; // SHOW规则不满足时隐藏
}
};
/**
* 评估单条规则条件
* @param {Object} fieldRules - 字段规则对象
* @returns {boolean} - 条件是否满足
*/
const evaluateRuleCondition = (fieldRules) => {
if (!fieldRules || !fieldRules.expr_list || fieldRules.expr_list.length === 0) {
return false;
}
const results = [];
fieldRules.expr_list.forEach(expr => {
let form_submission_value = postData.value[expr.field_name];
const rule_matching_value = expr.values || [];
// 处理空值情况
if (form_submission_value === null || form_submission_value === undefined) {
results.push(false);
return;
}
}
item['component_props']['disabled'] = item.field_rules?.mode === 'SHOW' ? !condition : condition;
/**
* 处理规则问题:收作用的规则字段如果隐藏,它设置的相应字段也需要隐藏
*/
// 有规则的字段集合
let rule_keys = formInfo.value['rule_list']
.map(item => item.expr_list[0])
.map(item => item.field_name);
//
let hide_fields = []; // 规则字段下面可以应用规则的字段
formData.value?.forEach(item => {
item.field_rules?.expr_list?.forEach(expr => {
if (rule_keys?.includes(item.key)) {
// 已隐藏字段
if (item.component_props.disabled) {
formInfo.value['rule_list']?.forEach(rule => {
if (rule.expr_list[0]['field_name'] === item.key) {
hide_fields = rule.field_names;
}
})
let matchResult = false;
if (typeof form_submission_value === 'string') {
// 处理字符串类型(单选,下拉)
let cleanValue = form_submission_value;
if (form_submission_value.indexOf(':') !== -1) {
cleanValue = form_submission_value.split(':')[0];
}
}
});
})
// 隐藏字段集合里面有当前字段
if (hide_fields.includes(item.key)) {
item['component_props']['disabled'] = true;
}
matchResult = rule_matching_value.includes(cleanValue);
} else if (Array.isArray(form_submission_value)) {
// 处理数组类型(多选)
const cleanValues = form_submission_value.map(item => {
if (typeof item === 'string' && item.includes(':')) {
return item.split(':')[0].trim();
}
return item;
});
matchResult = _.intersection(rule_matching_value, cleanValues).length > 0;
}
results.push(matchResult);
});
// 根据逻辑操作符计算最终结果
if (fieldRules.logical_op === 'AND') {
return results.every(result => result === true);
} else { // OR
return results.some(result => result === true);
}
});
};
/**
* 处理级联隐藏逻辑
*/
const handleCascadeHiding = () => {
const rule_list = formInfo.value['rule_list'] || [];
// 获取所有规则控制字段的映射
const ruleControlMap = new Map();
rule_list.forEach(rule => {
if (rule.expr_list && rule.expr_list.length > 0) {
const controlField = rule.expr_list[0].field_name;
if (!ruleControlMap.has(controlField)) {
ruleControlMap.set(controlField, []);
}
ruleControlMap.get(controlField).push({
field_names: rule.field_names || [],
mode: rule.mode
});
}
});
// 检查级联隐藏
formData.value.forEach(item => {
if (ruleControlMap.has(item.key) && item.component_props && item.component_props.disabled) {
// 如果控制字段被隐藏,则隐藏其控制的所有字段
const controlledRules = ruleControlMap.get(item.key);
controlledRules.forEach(rule => {
rule.field_names.forEach(fieldName => {
const targetField = formData.value.find(field => field.key === fieldName);
if (targetField && targetField.component_props) {
targetField.component_props.disabled = true;
}
});
});
}
});
}
......@@ -1188,6 +1265,22 @@ watch(
}
);
// 监听postData的变化,当数据更新时检查规则
watch(
() => postData.value,
() => {
// 延迟执行checkRules,确保数据更新完成
nextTick(() => {
checkRules();
});
},
{
deep: true, // 深度监听对象属性变化
immediate: false,
flush: 'post'
}
);
// 为每个表单字段创建单独的监听器
// 可以监听到简单组件的字段的值变化,自定义的组件无法监听到
const setupFieldWatchers = () => {
......@@ -1203,6 +1296,10 @@ const setupFieldWatchers = () => {
oldValue: oldValue,
newValue: newValue
});
// 延迟执行checkRules,确保数据更新完成
nextTick(() => {
checkRules();
});
// TAG: 大义工新增功能
/**
* 必须表单是新增表单时才触发
......