fix(表单规则): 优化规则检查逻辑并修复级联隐藏问题
重构规则检查逻辑,支持多条规则评估和级联隐藏 修复条件判断逻辑错误,增加对空值的处理 添加对表单数据变化的监听,实时更新规则状态
Showing
1 changed file
with
174 additions
and
77 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-07-18 10:22:22 | 2 | * @Date: 2022-07-18 10:22:22 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-10 15:42:37 | 4 | + * @LastEditTime: 2025-07-03 11:08:36 |
| 5 | * @FilePath: /data-table/src/views/index.vue | 5 | * @FilePath: /data-table/src/views/index.vue |
| 6 | * @Description: 首页 | 6 | * @Description: 首页 |
| 7 | --> | 7 | --> |
| ... | @@ -575,6 +575,11 @@ onMounted(async () => { | ... | @@ -575,6 +575,11 @@ onMounted(async () => { |
| 575 | approval_actions.value = formSetting.value.flow_node_action_list?.map((item) => { return { name: item.btnText, id: item.id, parent_nodes: item.parent_nodes } }); | 575 | approval_actions.value = formSetting.value.flow_node_action_list?.map((item) => { return { name: item.btnText, id: item.id, parent_nodes: item.parent_nodes } }); |
| 576 | }, 1000); | 576 | }, 1000); |
| 577 | 577 | ||
| 578 | + // 初始化完成后执行规则检查 | ||
| 579 | + nextTick(() => { | ||
| 580 | + checkRules(); | ||
| 581 | + }); | ||
| 582 | + | ||
| 578 | setTimeout(() => { | 583 | setTimeout(() => { |
| 579 | const width = $('body').width(); | 584 | const width = $('body').width(); |
| 580 | // 固定表单宽度 | 585 | // 固定表单宽度 |
| ... | @@ -700,116 +705,188 @@ const mergeAndDeduplicate = (data) => { | ... | @@ -700,116 +705,188 @@ const mergeAndDeduplicate = (data) => { |
| 700 | }); | 705 | }); |
| 701 | } | 706 | } |
| 702 | 707 | ||
| 703 | -// 根据规则隐藏相应字段 | 708 | +/** |
| 709 | + * 根据规则隐藏相应字段 | ||
| 710 | + * 优化版本:修复了条件判断逻辑和规则处理顺序问题 | ||
| 711 | + */ | ||
| 704 | const checkRules = () => { | 712 | const checkRules = () => { |
| 705 | const rule_list = formInfo.value['rule_list'] ? [...formInfo.value['rule_list']] : []; | 713 | const rule_list = formInfo.value['rule_list'] ? [...formInfo.value['rule_list']] : []; |
| 706 | - // TAG:处理同一字段多个规则情况 | 714 | + |
| 715 | + // 初始化所有字段的规则 | ||
| 707 | formData.value.forEach(item => { | 716 | formData.value.forEach(item => { |
| 708 | - item.x_rules = []; // 字段的规则 | 717 | + item.x_rules = []; |
| 718 | + // 重置disabled状态,避免上次规则影响 | ||
| 719 | + if (item.component_props) { | ||
| 720 | + item.component_props.disabled = false; | ||
| 721 | + } | ||
| 709 | }); | 722 | }); |
| 710 | 723 | ||
| 724 | + // 收集每个字段的规则 | ||
| 711 | rule_list.forEach(rule => { | 725 | rule_list.forEach(rule => { |
| 712 | formData.value.forEach(item => { | 726 | formData.value.forEach(item => { |
| 713 | if (rule.field_names?.includes(item.key) && !rule.is_invalid) { | 727 | if (rule.field_names?.includes(item.key) && !rule.is_invalid) { |
| 714 | - // 匹配字段的规则集合 | ||
| 715 | item.x_rules.push({ | 728 | item.x_rules.push({ |
| 716 | mode: rule.mode, | 729 | mode: rule.mode, |
| 717 | logical_op: rule.logical_op, | 730 | logical_op: rule.logical_op, |
| 718 | expr_list: rule.expr_list, | 731 | expr_list: rule.expr_list, |
| 719 | - }) | 732 | + }); |
| 720 | } | 733 | } |
| 721 | - }) | 734 | + }); |
| 722 | }); | 735 | }); |
| 723 | 736 | ||
| 737 | + // 合并规则 | ||
| 724 | formData.value.forEach(item => { | 738 | formData.value.forEach(item => { |
| 725 | - // 把规则合并,同一字段下的mode/logical_op必须一致 | 739 | + const mergedRules = mergeAndDeduplicate(item.x_rules); |
| 726 | - item.field_rules = mergeAndDeduplicate(item.x_rules)[0]; | 740 | + item.field_rules = mergedRules; // 保存所有合并后的规则 |
| 727 | }); | 741 | }); |
| 728 | 742 | ||
| 743 | + // 处理规则逻辑 - 支持多条规则 | ||
| 729 | formData.value.forEach(item => { | 744 | formData.value.forEach(item => { |
| 730 | - // 给受作用的字段绑定判断规则 | 745 | + if (item.field_rules && item.field_rules.length > 0 && item.component_props) { |
| 731 | - // 规则失效需要踢出 | 746 | + // 处理多条规则的逻辑 |
| 732 | - // rule_list.forEach(rule => { | 747 | + const finalDisabledState = evaluateMultipleRules(item.field_rules); |
| 733 | - // if (rule.field_names?.includes(item.key) && !rule.is_invalid) { | 748 | + item.component_props.disabled = finalDisabledState; |
| 734 | - // item.field_rules = { | ||
| 735 | - // mode: rule.mode, | ||
| 736 | - // logical_op: rule.logical_op, | ||
| 737 | - // expr_list: rule.expr_list, | ||
| 738 | - // } | ||
| 739 | - // } | ||
| 740 | - // }); | ||
| 741 | - // 只检查存在规则的字段 | ||
| 742 | - if (item.field_rules) { | ||
| 743 | - let condition = ''; | ||
| 744 | - // 多个规则的满足条件,为全且或者全或 | ||
| 745 | - const op = item.field_rules?.logical_op === 'AND' ? '&&' : '||'; | ||
| 746 | - item.field_rules?.expr_list?.forEach(expr => { | ||
| 747 | - let form_submission_value = postData.value[expr['field_name']]; // 表单提交值, field_12 : "" || field_13 : [] | ||
| 748 | - let rule_matching_value = expr['values']; // 规则匹配值 values : ['x'] | ||
| 749 | - if (typeof form_submission_value === 'string') { // 表单值为字符串(单选,下拉) | ||
| 750 | - // 处理单选项带补充信息时判断,去除补充信息 | ||
| 751 | - if (form_submission_value.indexOf(':') !== -1) { | ||
| 752 | - let parts = form_submission_value.split(':'); | ||
| 753 | - form_submission_value = parts[0]; | ||
| 754 | } | 749 | } |
| 755 | - const k = !!rule_matching_value.includes(form_submission_value); // 转换为布尔值 | 750 | + }); |
| 756 | - condition += `${k}${op}`; | 751 | + |
| 752 | + | ||
| 753 | + // 处理级联隐藏:如果规则字段被隐藏,其控制的字段也应该被隐藏 | ||
| 754 | + handleCascadeHiding(); | ||
| 755 | +}; | ||
| 756 | + | ||
| 757 | +/** | ||
| 758 | + * 评估多条规则并确定最终的disabled状态 | ||
| 759 | + * @param {Array} rulesList - 字段的所有规则数组 | ||
| 760 | + * @returns {boolean} - 最终的disabled状态 | ||
| 761 | + */ | ||
| 762 | +const evaluateMultipleRules = (rulesList) => { | ||
| 763 | + if (!rulesList || rulesList.length === 0) { | ||
| 764 | + return false; // 没有规则时默认显示 | ||
| 757 | } | 765 | } |
| 758 | - if (typeof form_submission_value === 'object') { // 表单值为数组(多选) | 766 | + |
| 759 | - // 处理多选项带补充信息时判断,去除补充信息 | 767 | + let showRules = []; |
| 760 | - form_submission_value = form_submission_value?.map(item => { | 768 | + let hideRules = []; |
| 761 | - if (item.includes(':')) { | 769 | + |
| 762 | - return item.split(':')[0].trim(); // 去除冒号及其后面的部分并去除前后空格 | 770 | + // 分离SHOW和HIDE规则 |
| 771 | + rulesList.forEach(rule => { | ||
| 772 | + if (rule.mode === 'SHOW') { | ||
| 773 | + showRules.push(rule); | ||
| 774 | + } else if (rule.mode === 'HIDE') { | ||
| 775 | + hideRules.push(rule); | ||
| 763 | } | 776 | } |
| 764 | - return item; | ||
| 765 | }); | 777 | }); |
| 766 | - const k = !!(_.intersection(rule_matching_value, form_submission_value)).length; // 转换为布尔值 | 778 | + |
| 767 | - condition += `${k}${op}` | 779 | + // 评估SHOW规则 |
| 780 | + let showResult = true; // 默认显示 | ||
| 781 | + if (showRules.length > 0) { | ||
| 782 | + // 如果有SHOW规则,需要至少一个SHOW规则条件满足才显示 | ||
| 783 | + showResult = showRules.some(rule => evaluateRuleCondition(rule)); | ||
| 768 | } | 784 | } |
| 769 | - }); | 785 | + |
| 770 | - // 把结果转换为布尔值 | 786 | + // 评估HIDE规则 |
| 771 | - if (item.field_rules?.logical_op === 'AND') { | 787 | + let hideResult = false; // 默认不隐藏 |
| 772 | - if (condition.indexOf('false') >= 0) { | 788 | + if (hideRules.length > 0) { |
| 773 | - condition = false; | 789 | + // 如果有HIDE规则,任何一个HIDE规则条件满足就隐藏 |
| 790 | + hideResult = hideRules.some(rule => evaluateRuleCondition(rule)); | ||
| 791 | + } | ||
| 792 | + | ||
| 793 | + // 最终逻辑:HIDE规则优先级更高 | ||
| 794 | + // 如果任何HIDE规则满足,则隐藏 | ||
| 795 | + // 否则根据SHOW规则决定 | ||
| 796 | + if (hideResult) { | ||
| 797 | + return true; // 隐藏 | ||
| 774 | } else { | 798 | } else { |
| 775 | - condition = true; | 799 | + return !showResult; // SHOW规则不满足时隐藏 |
| 776 | } | 800 | } |
| 801 | +}; | ||
| 802 | + | ||
| 803 | +/** | ||
| 804 | + * 评估单条规则条件 | ||
| 805 | + * @param {Object} fieldRules - 字段规则对象 | ||
| 806 | + * @returns {boolean} - 条件是否满足 | ||
| 807 | + */ | ||
| 808 | +const evaluateRuleCondition = (fieldRules) => { | ||
| 809 | + if (!fieldRules || !fieldRules.expr_list || fieldRules.expr_list.length === 0) { | ||
| 810 | + return false; | ||
| 777 | } | 811 | } |
| 778 | - if (item.field_rules?.logical_op === 'OR') { | 812 | + |
| 779 | - if (condition.indexOf('true') >= 0) { | 813 | + const results = []; |
| 780 | - condition = true; | 814 | + |
| 781 | - } else { | 815 | + fieldRules.expr_list.forEach(expr => { |
| 782 | - condition = false; | 816 | + let form_submission_value = postData.value[expr.field_name]; |
| 817 | + const rule_matching_value = expr.values || []; | ||
| 818 | + | ||
| 819 | + // 处理空值情况 | ||
| 820 | + if (form_submission_value === null || form_submission_value === undefined) { | ||
| 821 | + results.push(false); | ||
| 822 | + return; | ||
| 783 | } | 823 | } |
| 824 | + | ||
| 825 | + let matchResult = false; | ||
| 826 | + | ||
| 827 | + if (typeof form_submission_value === 'string') { | ||
| 828 | + // 处理字符串类型(单选,下拉) | ||
| 829 | + let cleanValue = form_submission_value; | ||
| 830 | + if (form_submission_value.indexOf(':') !== -1) { | ||
| 831 | + cleanValue = form_submission_value.split(':')[0]; | ||
| 784 | } | 832 | } |
| 785 | - item['component_props']['disabled'] = item.field_rules?.mode === 'SHOW' ? !condition : condition; | 833 | + matchResult = rule_matching_value.includes(cleanValue); |
| 786 | - /** | 834 | + } else if (Array.isArray(form_submission_value)) { |
| 787 | - * 处理规则问题:收作用的规则字段如果隐藏,它设置的相应字段也需要隐藏 | 835 | + // 处理数组类型(多选) |
| 788 | - */ | 836 | + const cleanValues = form_submission_value.map(item => { |
| 789 | - // 有规则的字段集合 | 837 | + if (typeof item === 'string' && item.includes(':')) { |
| 790 | - let rule_keys = formInfo.value['rule_list'] | 838 | + return item.split(':')[0].trim(); |
| 791 | - .map(item => item.expr_list[0]) | ||
| 792 | - .map(item => item.field_name); | ||
| 793 | - // | ||
| 794 | - let hide_fields = []; // 规则字段下面可以应用规则的字段 | ||
| 795 | - formData.value?.forEach(item => { | ||
| 796 | - item.field_rules?.expr_list?.forEach(expr => { | ||
| 797 | - if (rule_keys?.includes(item.key)) { | ||
| 798 | - // 已隐藏字段 | ||
| 799 | - if (item.component_props.disabled) { | ||
| 800 | - formInfo.value['rule_list']?.forEach(rule => { | ||
| 801 | - if (rule.expr_list[0]['field_name'] === item.key) { | ||
| 802 | - hide_fields = rule.field_names; | ||
| 803 | } | 839 | } |
| 804 | - }) | 840 | + return item; |
| 841 | + }); | ||
| 842 | + matchResult = _.intersection(rule_matching_value, cleanValues).length > 0; | ||
| 843 | + } | ||
| 844 | + | ||
| 845 | + results.push(matchResult); | ||
| 846 | + }); | ||
| 847 | + | ||
| 848 | + // 根据逻辑操作符计算最终结果 | ||
| 849 | + if (fieldRules.logical_op === 'AND') { | ||
| 850 | + return results.every(result => result === true); | ||
| 851 | + } else { // OR | ||
| 852 | + return results.some(result => result === true); | ||
| 805 | } | 853 | } |
| 854 | +}; | ||
| 855 | + | ||
| 856 | +/** | ||
| 857 | + * 处理级联隐藏逻辑 | ||
| 858 | + */ | ||
| 859 | +const handleCascadeHiding = () => { | ||
| 860 | + const rule_list = formInfo.value['rule_list'] || []; | ||
| 861 | + | ||
| 862 | + // 获取所有规则控制字段的映射 | ||
| 863 | + const ruleControlMap = new Map(); | ||
| 864 | + rule_list.forEach(rule => { | ||
| 865 | + if (rule.expr_list && rule.expr_list.length > 0) { | ||
| 866 | + const controlField = rule.expr_list[0].field_name; | ||
| 867 | + if (!ruleControlMap.has(controlField)) { | ||
| 868 | + ruleControlMap.set(controlField, []); | ||
| 869 | + } | ||
| 870 | + ruleControlMap.get(controlField).push({ | ||
| 871 | + field_names: rule.field_names || [], | ||
| 872 | + mode: rule.mode | ||
| 873 | + }); | ||
| 806 | } | 874 | } |
| 807 | }); | 875 | }); |
| 808 | - }) | 876 | + |
| 809 | - // 隐藏字段集合里面有当前字段 | 877 | + // 检查级联隐藏 |
| 810 | - if (hide_fields.includes(item.key)) { | 878 | + formData.value.forEach(item => { |
| 811 | - item['component_props']['disabled'] = true; | 879 | + if (ruleControlMap.has(item.key) && item.component_props && item.component_props.disabled) { |
| 880 | + // 如果控制字段被隐藏,则隐藏其控制的所有字段 | ||
| 881 | + const controlledRules = ruleControlMap.get(item.key); | ||
| 882 | + controlledRules.forEach(rule => { | ||
| 883 | + rule.field_names.forEach(fieldName => { | ||
| 884 | + const targetField = formData.value.find(field => field.key === fieldName); | ||
| 885 | + if (targetField && targetField.component_props) { | ||
| 886 | + targetField.component_props.disabled = true; | ||
| 812 | } | 887 | } |
| 888 | + }); | ||
| 889 | + }); | ||
| 813 | } | 890 | } |
| 814 | }); | 891 | }); |
| 815 | } | 892 | } |
| ... | @@ -1188,6 +1265,22 @@ watch( | ... | @@ -1188,6 +1265,22 @@ watch( |
| 1188 | } | 1265 | } |
| 1189 | ); | 1266 | ); |
| 1190 | 1267 | ||
| 1268 | +// 监听postData的变化,当数据更新时检查规则 | ||
| 1269 | +watch( | ||
| 1270 | + () => postData.value, | ||
| 1271 | + () => { | ||
| 1272 | + // 延迟执行checkRules,确保数据更新完成 | ||
| 1273 | + nextTick(() => { | ||
| 1274 | + checkRules(); | ||
| 1275 | + }); | ||
| 1276 | + }, | ||
| 1277 | + { | ||
| 1278 | + deep: true, // 深度监听对象属性变化 | ||
| 1279 | + immediate: false, | ||
| 1280 | + flush: 'post' | ||
| 1281 | + } | ||
| 1282 | +); | ||
| 1283 | + | ||
| 1191 | // 为每个表单字段创建单独的监听器 | 1284 | // 为每个表单字段创建单独的监听器 |
| 1192 | // 可以监听到简单组件的字段的值变化,自定义的组件无法监听到 | 1285 | // 可以监听到简单组件的字段的值变化,自定义的组件无法监听到 |
| 1193 | const setupFieldWatchers = () => { | 1286 | const setupFieldWatchers = () => { |
| ... | @@ -1203,6 +1296,10 @@ const setupFieldWatchers = () => { | ... | @@ -1203,6 +1296,10 @@ const setupFieldWatchers = () => { |
| 1203 | oldValue: oldValue, | 1296 | oldValue: oldValue, |
| 1204 | newValue: newValue | 1297 | newValue: newValue |
| 1205 | }); | 1298 | }); |
| 1299 | + // 延迟执行checkRules,确保数据更新完成 | ||
| 1300 | + nextTick(() => { | ||
| 1301 | + checkRules(); | ||
| 1302 | + }); | ||
| 1206 | // TAG: 大义工新增功能 | 1303 | // TAG: 大义工新增功能 |
| 1207 | /** | 1304 | /** |
| 1208 | * 必须表单是新增表单时才触发 | 1305 | * 必须表单是新增表单时才触发 | ... | ... |
-
Please register or login to post a comment