hookehuyr

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

重构规则检查逻辑,支持多条规则评估和级联隐藏
修复条件判断逻辑错误,增加对空值的处理
添加对表单数据变化的监听,实时更新规则状态
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,118 +705,190 @@ const mergeAndDeduplicate = (data) => { ...@@ -700,118 +705,190 @@ 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:处理同一字段多个规则情况
707 - formData.value.forEach(item => {
708 - item.x_rules = []; // 字段的规则
709 - });
710 714
711 - rule_list.forEach(rule => { 715 + // 初始化所有字段的规则
712 formData.value.forEach(item => { 716 formData.value.forEach(item => {
713 - if (rule.field_names?.includes(item.key) && !rule.is_invalid) { 717 + item.x_rules = [];
714 - // 匹配字段的规则集合 718 + // 重置disabled状态,避免上次规则影响
715 - item.x_rules.push({ 719 + if (item.component_props) {
716 - mode: rule.mode, 720 + item.component_props.disabled = false;
717 - logical_op: rule.logical_op, 721 + }
718 - expr_list: rule.expr_list, 722 + });
719 - })
720 - }
721 - })
722 - });
723 723
724 - formData.value.forEach(item => { 724 + // 收集每个字段的规则
725 - // 把规则合并,同一字段下的mode/logical_op必须一致 725 + rule_list.forEach(rule => {
726 - item.field_rules = mergeAndDeduplicate(item.x_rules)[0]; 726 + formData.value.forEach(item => {
727 - }); 727 + if (rule.field_names?.includes(item.key) && !rule.is_invalid) {
728 + item.x_rules.push({
729 + mode: rule.mode,
730 + logical_op: rule.logical_op,
731 + expr_list: rule.expr_list,
732 + });
733 + }
734 + });
735 + });
728 736
729 - formData.value.forEach(item => { 737 + // 合并规则
730 - // 给受作用的字段绑定判断规则 738 + formData.value.forEach(item => {
731 - // 规则失效需要踢出 739 + const mergedRules = mergeAndDeduplicate(item.x_rules);
732 - // rule_list.forEach(rule => { 740 + item.field_rules = mergedRules; // 保存所有合并后的规则
733 - // if (rule.field_names?.includes(item.key) && !rule.is_invalid) { 741 + });
734 - // item.field_rules = { 742 +
735 - // mode: rule.mode, 743 + // 处理规则逻辑 - 支持多条规则
736 - // logical_op: rule.logical_op, 744 + formData.value.forEach(item => {
737 - // expr_list: rule.expr_list, 745 + if (item.field_rules && item.field_rules.length > 0 && item.component_props) {
738 - // } 746 + // 处理多条规则的逻辑
739 - // } 747 + const finalDisabledState = evaluateMultipleRules(item.field_rules);
740 - // }); 748 + item.component_props.disabled = finalDisabledState;
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 - }
755 - const k = !!rule_matching_value.includes(form_submission_value); // 转换为布尔值
756 - condition += `${k}${op}`;
757 - }
758 - if (typeof form_submission_value === 'object') { // 表单值为数组(多选)
759 - // 处理多选项带补充信息时判断,去除补充信息
760 - form_submission_value = form_submission_value?.map(item => {
761 - if (item.includes(':')) {
762 - return item.split(':')[0].trim(); // 去除冒号及其后面的部分并去除前后空格
763 - }
764 - return item;
765 - });
766 - const k = !!(_.intersection(rule_matching_value, form_submission_value)).length; // 转换为布尔值
767 - condition += `${k}${op}`
768 } 749 }
769 - }); 750 + });
770 - // 把结果转换为布尔值 751 +
771 - if (item.field_rules?.logical_op === 'AND') { 752 +
772 - if (condition.indexOf('false') >= 0) { 753 + // 处理级联隐藏:如果规则字段被隐藏,其控制的字段也应该被隐藏
773 - condition = false; 754 + handleCascadeHiding();
774 - } else { 755 +};
775 - condition = true; 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; // 没有规则时默认显示
765 + }
766 +
767 + let showRules = [];
768 + let hideRules = [];
769 +
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);
776 } 776 }
777 - } 777 + });
778 - if (item.field_rules?.logical_op === 'OR') { 778 +
779 - if (condition.indexOf('true') >= 0) { 779 + // 评估SHOW规则
780 - condition = true; 780 + let showResult = true; // 默认显示
781 - } else { 781 + if (showRules.length > 0) {
782 - condition = false; 782 + // 如果有SHOW规则,需要至少一个SHOW规则条件满足才显示
783 + showResult = showRules.some(rule => evaluateRuleCondition(rule));
784 + }
785 +
786 + // 评估HIDE规则
787 + let hideResult = false; // 默认不隐藏
788 + if (hideRules.length > 0) {
789 + // 如果有HIDE规则,任何一个HIDE规则条件满足就隐藏
790 + hideResult = hideRules.some(rule => evaluateRuleCondition(rule));
791 + }
792 +
793 + // 最终逻辑:HIDE规则优先级更高
794 + // 如果任何HIDE规则满足,则隐藏
795 + // 否则根据SHOW规则决定
796 + if (hideResult) {
797 + return true; // 隐藏
798 + } else {
799 + return !showResult; // SHOW规则不满足时隐藏
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;
811 + }
812 +
813 + const results = [];
814 +
815 + fieldRules.expr_list.forEach(expr => {
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 }
784 - } 824 +
785 - item['component_props']['disabled'] = item.field_rules?.mode === 'SHOW' ? !condition : condition; 825 + let matchResult = false;
786 - /** 826 +
787 - * 处理规则问题:收作用的规则字段如果隐藏,它设置的相应字段也需要隐藏 827 + if (typeof form_submission_value === 'string') {
788 - */ 828 + // 处理字符串类型(单选,下拉)
789 - // 有规则的字段集合 829 + let cleanValue = form_submission_value;
790 - let rule_keys = formInfo.value['rule_list'] 830 + if (form_submission_value.indexOf(':') !== -1) {
791 - .map(item => item.expr_list[0]) 831 + cleanValue = form_submission_value.split(':')[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 - }
804 - })
805 } 832 }
806 - } 833 + matchResult = rule_matching_value.includes(cleanValue);
807 - }); 834 + } else if (Array.isArray(form_submission_value)) {
808 - }) 835 + // 处理数组类型(多选)
809 - // 隐藏字段集合里面有当前字段 836 + const cleanValues = form_submission_value.map(item => {
810 - if (hide_fields.includes(item.key)) { 837 + if (typeof item === 'string' && item.includes(':')) {
811 - item['component_props']['disabled'] = true; 838 + return item.split(':')[0].trim();
812 - } 839 + }
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);
813 } 853 }
814 - }); 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 + });
874 + }
875 + });
876 +
877 + // 检查级联隐藏
878 + formData.value.forEach(item => {
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;
887 + }
888 + });
889 + });
890 + }
891 + });
815 } 892 }
816 893
817 894
...@@ -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 * 必须表单是新增表单时才触发
......