Procházet zdrojové kódy

[Mod 0000] 模型管理高级编辑模式添加配置支持

houzekong před 3 dny
rodič
revize
686d1643bd

+ 2 - 0
public/js/global.js

@@ -403,12 +403,14 @@ const __STATIC_ROUTES__ = [
         },
         name: 'cadFile-mapView',
       },
+      // 预警模型管理:配置煤层/采空区预警判据的关联表达式
       {
         path: '/manage/model',
         component: '/system/algorithm/index',
         meta: {
           keepAlive: true,
           internalOrExternal: false,
+          // compact: true,
           icon: '',
           componentName: 'index',
           title: '预警模型管理',

+ 45 - 423
src/views/system/algorithm/components/RelationBuilder.vue

@@ -10,15 +10,6 @@
           <a-tag v-else-if="item.type === 'logic'" class="expr-tag">|</a-tag>
         </template>
       </a-space>
-      <!-- <div class="flex flex-wrap" :style="{ width: `calc(100% - ${isOperator ? '100' : '0'}px)` }" wrap>
-        <div v-for="(group, id) in splitedItems" :key="`temp${id}`" class="w-50% flex justify-start items-center mb-10px" compact>
-          <template v-for="(item, index) in group" :key="index">
-            <a-input-number v-if="item.type === 'number'" v-model:value="item.value" style="width: 30px" placeholder="-" @blur="confirm" />
-            <span v-else-if="item.type === 'operand'" class="w-90px text-center">{{ formatDisplay(item) }}</span>
-            <span v-else class="p-5px">{{ formatDisplay(item) }}</span>
-          </template>
-        </div>
-      </div> -->
       <a-button v-if="isOperator" class="w-100px" type="primary" @click="enterAdvancedMode"> 高级编辑 </a-button>
     </div>
     <a-row v-else :gutter="8">
@@ -28,7 +19,6 @@
           <a-collapse class="relation-card" v-model:activeKey="activeKey" :bordered="false">
             <a-collapse-panel v-for="group in elementGroups" :key="group.key" :header="group.header">
               <div class="chip-container">
-                <!-- @dragstart="onDragStart($event, item)" -->
                 <div
                   v-for="item in group.items"
                   :key="item.value"
@@ -44,7 +34,6 @@
                 <div v-for="item in group.extra" :key="item.type" class="mt-8px">
                   <a-input-number v-model:value="item.value" :defaultValue="0">
                     <template #addonAfter>
-                      <!-- @dragstart="onDragStart($event, item)" -->
                       <span style="cursor: pointer" draggable="true" @click="appendItem(item)"> 追加 </span>
                     </template>
                   </a-input-number>
@@ -72,7 +61,6 @@
           </template>
 
           <div class="relation-card">
-            <!-- :class="{ 'drag-over': isDragOver }" @dragover.prevent="onDragOver" @dragleave="onDragLeave"  @drop="onDrop"-->
             <div class="canvas-container">
               <a-empty v-if="!items.length" description="从左侧点击或拖拽元素" />
               <div v-else class="expression-list">
@@ -121,142 +109,33 @@
 
 <script lang="ts" setup>
   import { ref, computed, watch, h } from 'vue';
+  import { useRoute } from 'vue-router';
   import { cloneDeep } from 'lodash-es';
   import { message, Modal } from 'ant-design-vue';
   import { DeleteOutlined } from '@ant-design/icons-vue';
+  import {
+    parseExpression,
+    formatDisplay as _formatDisplay,
+    validateExpression,
+    checkTopLevelMixed,
+    addParenthesesForAnd,
+    DEFAULT_TEMPLATE_OPTIONS,
+    DEFAULT_ELEMENT_GROUPS,
+    DEFAULT_DISPLAY_MAP,
+    DEFAULT_OPERAND_SET,
+  } from '../utils';
+  import type { Item, ElementGroup } from '../utils';
+
+  // 常量配置(从当前路由 meta.compact 读取,默认 false)
+  const route = useRoute();
+  const isOperator = computed(() => !!route.meta?.compact);
+  const activeKey = ref<string[]>(['gas', 'operator', 'logic', 'number', 'paren']);
 
-  // 类型定义
-  interface Item {
-    type: 'operand' | 'operator' | 'logic' | 'number' | 'leftParen' | 'rightParen';
-    value: string;
-    label?: string;
-  }
+  const templateOptions = ref(DEFAULT_TEMPLATE_OPTIONS);
 
-  interface ElementGroup {
-    key: string;
-    header: string;
-    type: string;
-    items: Item[];
-    extra: Item[];
-  }
+  const elementGroups: ElementGroup[] = DEFAULT_ELEMENT_GROUPS;
 
-  // 常量配置
-  const isOperator = ref(false);
-  const activeKey = ref<string[]>(['gas', 'operator', 'logic', 'number', 'paren']);
-
-  const templateOptions = ref([
-    { label: '压差预警1', value: '20<=sourcePressureChange && sourcePressureChange<=40' },
-    { label: '压差预警2', value: '40<sourcePressureChange && sourcePressureChange<=60' },
-    { label: '压差预警3', value: '60<sourcePressureChange && sourcePressureChange<=80' },
-    { label: '压差预警4', value: '80<sourcePressureChange' },
-    { label: '火灾预警1', value: '((coRzl<=20||coZf<=0.5*coAvg)&&coVal<=10)||o2Val<=5&&temperatureDifference<2&&co2Val<0.5' },
-    {
-      label: '火灾预警2',
-      value:
-        '((coRzl>20&&coRzl<=50)||(0.5*coAvg<coZf&&coZf<=coAvg)||(coVal>10&&coVal<=50)||(temperatureDifference>=2&&temperatureDifference<=4))&&co2Val<0.5',
-    },
-    {
-      label: '火灾预警3',
-      value:
-        '((coRzl>50&&coRzl<=100)||(coAvg<coZf&&coZf<=2*coAvg)||(coVal>=50&&coVal<=100)||(temperatureDifference>4&&temperatureDifference<=10))&&co2Val<0.5',
-    },
-    { label: '火灾预警4', value: 'coRzl>100||2*coAvg<coZf||coVal>100||temperatureDifference>10||(co2Val>=0.5&&co2Trend)' },
-    { label: '闭外火灾预警1', value: 'temperatureDifference<2||ch4Val<0.5||coVal<5||o2Val<20' },
-    { label: '闭外火灾预警2', value: 'temperatureDifference>=2&&temperatureDifference<=4||ch4Val>=0.5&&ch4Val<=1||coVal>=5&&coVal<10' },
-    {
-      label: '闭外火灾预警3',
-      value: 'temperatureDifference>4&&temperatureDifference<=10&&temperature>=26||ch4Val>=1&&ch4Val<1.5||coVal>=10&&coVal<24||c2h4Val>0&&c2h2Val==0',
-    },
-    { label: '闭外火灾预警4', value: 'temperatureDifference>10&&temperature>=30||ch4Val>=1.5&&ch4Val<2||coVal>=24||c2h2Val>0' },
-    {
-      label: 'OPEN',
-      value:
-        '(temperature<30||-3<temperature-fireAirTemperature&&temperature-fireAirTemperature<3)&&o2Val<5&&c2h4Val<0.001&&c2h2Val<0.001&&coVal<0.001&&(temperature<25||-3<temperature-fireWaterTemperature&&temperature-fireWaterTemperature<3)&&stableDays>30',
-    },
-  ]);
-
-  const elementGroups: ElementGroup[] = [
-    {
-      key: 'gas',
-      header: '监测变量',
-      type: 'operand',
-      items: [
-        { type: 'operand', value: 'sourcePressureChange', label: '压差变化' },
-        { type: 'operand', value: 'coRzl', label: 'CO日增率' },
-        { type: 'operand', value: 'coZf', label: 'CO增幅' },
-        { type: 'operand', value: 'coAvg', label: 'CO平均值' },
-        { type: 'operand', value: 'coVal', label: 'CO值' },
-        { type: 'operand', value: 'co2Val', label: 'CO2值' },
-        { type: 'operand', value: 'co2Trend', label: 'CO2存在上升趋势' },
-        { type: 'operand', value: 'o2Val', label: 'O2值' },
-        { type: 'operand', value: 'ch4Val', label: 'CH4值' },
-        { type: 'operand', value: 'c2h4Val', label: 'C2H4值' },
-        { type: 'operand', value: 'c2h2Val', label: 'C2H2值' },
-        { type: 'operand', value: 'temperature', label: '温度' },
-        { type: 'operand', value: 'fireAirTemperature', label: '气温' },
-        { type: 'operand', value: 'fireWaterTemperature', label: '水温' },
-        { type: 'operand', value: 'temperatureDifference', label: '温差' },
-        { type: 'operand', value: 'stableDays', label: '稳定天数' },
-      ],
-      extra: [],
-    },
-    {
-      key: 'operator',
-      header: '计算符',
-      type: 'operator',
-      items: [
-        { type: 'operator', value: '>', label: '>' },
-        { type: 'operator', value: '>=', label: '≥' },
-        { type: 'operator', value: '<', label: '<' },
-        { type: 'operator', value: '<=', label: '≤' },
-        { type: 'operator', value: '==', label: '=' },
-        { type: 'operator', value: '+', label: '+' },
-        { type: 'operator', value: '-', label: '-' },
-        { type: 'operator', value: '*', label: '*' },
-        { type: 'operator', value: '/', label: '/' },
-      ],
-      extra: [],
-    },
-    {
-      key: 'logic',
-      header: '逻辑符',
-      type: 'logic',
-      items: [
-        { type: 'logic', value: '&&', label: '且' },
-        { type: 'logic', value: '||', label: '或' },
-      ],
-      extra: [],
-    },
-    {
-      key: 'paren',
-      header: '括号',
-      type: 'paren',
-      items: [
-        { type: 'leftParen', value: '(', label: '(' },
-        { type: 'rightParen', value: ')', label: ')' },
-      ],
-      extra: [],
-    },
-    {
-      key: 'number',
-      header: '数值',
-      type: 'number',
-      items: [
-        { type: 'number', value: '0', label: '0' },
-        { type: 'number', value: '10', label: '10' },
-        { type: 'number', value: '20', label: '20' },
-        { type: 'number', value: '30', label: '30' },
-        { type: 'number', value: '40', label: '40' },
-        { type: 'number', value: '50', label: '50' },
-        { type: 'number', value: '60', label: '60' },
-        { type: 'number', value: '70', label: '70' },
-        { type: 'number', value: '80', label: '80' },
-        { type: 'number', value: '90', label: '90' },
-        { type: 'number', value: '100', label: '100' },
-      ],
-      extra: [{ type: 'number', value: '0', label: '追加自定义值' }],
-    },
-  ];
+  const operandSet = DEFAULT_OPERAND_SET;
 
   const tagColors: Record<string, string> = {
     operand: 'blue',
@@ -267,29 +146,10 @@
     rightParen: 'default',
   };
 
-  const displayMap: Record<string, string> = {
-    '&&': '且',
-    '||': '或',
-    '>=': '≥',
-    '<=': '≤',
-    '==': '=',
-    sourcePressureChange: '压差变化',
-    coRzl: 'CO日增率',
-    coZf: 'CO增幅',
-    coAvg: 'CO平均值',
-    coVal: 'CO值',
-    co2Val: 'CO2值',
-    co2Trend: 'CO2存在上升趋势',
-    o2Val: 'O2值',
-    ch4Val: 'CH4值',
-    c2h4Val: 'C2H4值',
-    c2h2Val: 'C2H2值',
-    temperature: '温度',
-    fireAirTemperature: '气温',
-    fireWaterTemperature: '水温',
-    temperatureDifference: '温差',
-    stableDays: '稳定天数',
-  };
+  const displayMap: Record<string, string> = DEFAULT_DISPLAY_MAP;
+
+  // 模板调用的 formatDisplay,闭包捕获 displayMap
+  const formatDisplay = (item: Item) => _formatDisplay(item, displayMap);
 
   // Props 和 Emits
   const props = defineProps<{ modelValue: string }>();
@@ -309,78 +169,10 @@
 
   // Computed
   const expressionString = computed(() => items.value.map((i) => i.value).join(' '));
-  // const splitedItems = computed(() => {
-  //   return items.value.reduce(
-  //     (arr: Item[][], ele) => {
-  //       console.log('debug rr', arr, ele);
-  //       if (ele.type === 'logic') {
-  //         arr.push([]);
-  //       }
-  //       if (['operand', 'operator', 'number'].includes(ele.type)) {
-  //         last(arr)!.push(ele);
-  //       }
-  //       return arr;
-  //     },
-  //     [[]]
-  //   );
-  // });
-
-  // 工具函数
-  const buildOperandSet = (): Set<string> => {
-    const set = new Set<string>();
-    elementGroups.forEach((group) => {
-      if (group.type === 'operand') group.items.forEach((item) => set.add(item.value));
-    });
-    return set;
-  };
-
-  const formatDisplay = (item: Item): string => {
-    if (item.type === 'number') return item.value;
-    return displayMap[item.value] || item.value;
-  };
-
-  const parseExpression = (str: string): Item[] => {
-    if (!str?.trim()) return [];
-    const operandSet = buildOperandSet();
-    const operandPattern = [...operandSet].map((v) => v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
-    const regex = new RegExp(`(${operandPattern}|==|>=|<=|&&|\\|\\||-?\\d+(?:\\.\\d+)?|[>+\\-*/<()])`, 'g');
-    const rawTokens = str.match(regex) || [];
-    const tokens = rawTokens.map((t) => t.trim()).filter((t) => t !== '');
-
-    const items: Item[] = [];
-    for (const token of tokens) {
-      if (token === '(') {
-        items.push({ type: 'leftParen', value: '(' });
-        continue;
-      }
-      if (token === ')') {
-        items.push({ type: 'rightParen', value: ')' });
-        continue;
-      }
-      if (token === '&&' || token === '||') {
-        items.push({ type: 'logic', value: token });
-        continue;
-      }
-      if (['>', '>=', '<', '<=', '==', '+', '-', '*', '/'].includes(token)) {
-        items.push({ type: 'operator', value: token });
-        continue;
-      }
-      if (operandSet.has(token)) {
-        items.push({ type: 'operand', value: token });
-        continue;
-      }
-      if (/^-?\d+(?:\.\d+)?$/.test(token)) {
-        items.push({ type: 'number', value: token });
-        continue;
-      }
-    }
 
-    return items;
-  };
-
-  // 初始化:将表达式字符串同步到画布与内部状态
+  /** 将表达式字符串解析并同步到画布 */
   const initializeFromExpression = (expr: string): void => {
-    const parsed = parseExpression(expr);
+    const parsed = parseExpression(expr, operandSet);
     items.value = parsed.length ? parsed : [];
     // 同步简易模式输入框(如果处于简易模式,显示最新表达式)
     inputString.value = expr;
@@ -388,196 +180,34 @@
     errorMessage.value = '';
   };
 
-  // 画布操作(追加、删除、清空、插入括号)
+  /** 追加一个元素到画布末尾 */
   const appendItem = (item: Item): void => {
     items.value.push(cloneDeep(item));
     syncAfterChange();
   };
 
+  /** 移除指定索引处的元素 */
   const removeItem = (index: number): void => {
     items.value.splice(index, 1);
     syncAfterChange();
   };
 
+  /** 清空画布所有元素 */
   const clearCanvas = (): void => {
     items.value = [];
     syncAfterChange();
   };
 
-  // 画布变更后同步:更新简易模式输入框,并向上通知(但不提交)
+  /** 画布变更后同步到简易模式输入框(不触发提交) */
   const syncAfterChange = (): void => {
     const expr = expressionString.value;
     inputString.value = expr;
   };
 
-  // 拖拽逻辑
-  // const isDragOver = ref<boolean>(false);
-  // const onDragStart = (e: DragEvent, item: Item): void => {
-  //   e.dataTransfer?.setData('text/plain', JSON.stringify(item));
-  // };
-  // const onDragOver = (e: DragEvent): void => {
-  //   e.preventDefault();
-  //   isDragOver.value = true;
-  // };
-  // const onDragLeave = (): void => {
-  //   isDragOver.value = false;
-  // };
-  // const onDrop = (e: DragEvent): void => {
-  //   e.preventDefault();
-  //   isDragOver.value = false;
-  //   try {
-  //     const data = e.dataTransfer?.getData('text/plain');
-  //     if (!data) return;
-  //     const item: Item = JSON.parse(data);
-  //     let droppedItem = cloneDeep(item);
-  //     const container = e.currentTarget as HTMLElement;
-  //     const tags = [...container.querySelectorAll('.expr-tag')] as HTMLElement[];
-  //     let index = tags.length;
-  //     const mouseX = e.clientX;
-  //     for (let i = 0; i < tags.length; i++) {
-  //       const rect = tags[i].getBoundingClientRect();
-  //       if (mouseX < rect.left + rect.width / 2) {
-  //         index = i;
-  //         break;
-  //       }
-  //     }
-  //     items.value.splice(index, 0, droppedItem);
-  //     syncAfterChange();
-  //   } catch {
-  //     message.error('拖拽失败');
-  //   }
-  // };
-
-  // 校验与优先级处理
-  const validateExpression = (expr: string): { valid: boolean; message?: string } => {
-    if (!expr?.trim()) return { valid: false, message: '表达式不能为空' };
-    // 括号匹配
-    let count = 0;
-    for (const char of expr) {
-      if (char === '(') count++;
-      if (char === ')') count--;
-      if (count < 0) return { valid: false, message: '括号不匹配' };
-    }
-    if (count !== 0) return { valid: false, message: '括号不匹配' };
-
-    // 基本语法:检查操作符是否孤立(简单启发)
-    const tokens = parseExpression(expr);
-    if (tokens.length === 0) return { valid: false, message: '无法解析表达式' };
-    for (let i = 0; i < tokens.length - 1; i++) {
-      const cur = tokens[i];
-      const next = tokens[i + 1];
-      if (cur.type === 'operand' && (next.type === 'number' || next.type === 'operand')) {
-        return { valid: false, message: `监测变量 ${cur.value} 位置可能不正确` };
-      }
-      if (cur.type === 'operator' && (next.type === 'operator' || next.type === 'logic' || next.type === 'rightParen')) {
-        return { valid: false, message: `运算符 ${cur.value} 后缺少操作数` };
-      }
-      if (cur.type === 'logic' && (next.type === 'logic' || next.type === 'rightParen')) {
-        return { valid: false, message: `逻辑符 ${cur.value} 位置可能不正确` };
-      }
-      if (cur.type === 'number' && (next.type === 'operand' || next.type === 'number')) {
-        return { valid: false, message: `数值 ${cur.value} 位置可能不正确` };
-      }
-    }
-    return { valid: true };
-  };
-
-  /**
-   * 基于 items 为混合优先级表达式自动添加括号
-   * 仅在最外层 || 分隔的段中,对未加括号且包含 && 的段添加括号
-   */
-  const addParenthesesForAnd = (_expr: string): string => {
-    const tokens = items.value;
-    if (!tokens.length) return _expr;
-
-    // 1. 计算每个 token 在整体表达式中的嵌套深度
-    const depths: number[] = [];
-    let depth = 0;
-    for (const t of tokens) {
-      if (t.type === 'leftParen') {
-        depths.push(depth);
-        depth++;
-      } else if (t.type === 'rightParen') {
-        depth--;
-        depths.push(depth);
-      } else {
-        depths.push(depth);
-      }
-    }
-
-    // 2. 按最外层 (depth === 0) 的 || 切分成段
-    const segments: Item[][] = [];
-    let current: Item[] = [];
-    for (let i = 0; i < tokens.length; i++) {
-      if (tokens[i].type === 'logic' && tokens[i].value === '||' && depths[i] === 0) {
-        if (current.length) segments.push(current);
-        current = [];
-      } else {
-        current.push(tokens[i]);
-      }
-    }
-    if (current.length) segments.push(current);
-
-    // 3. 辅助:判断段是否已被一整对括号包裹
-    const isEnclosed = (seg: Item[]): boolean => {
-      if (seg.length < 2) return false;
-      if (seg[0].type !== 'leftParen' || seg[seg.length - 1].type !== 'rightParen') return false;
-      let bal = 0;
-      for (let i = 1; i < seg.length - 1; i++) {
-        if (seg[i].type === 'leftParen') bal++;
-        else if (seg[i].type === 'rightParen') bal--;
-        if (bal < 0) return false;
-      }
-      return bal === 0;
-    };
-
-    // 4. 辅助:判断段内是否存在最外层 (深度为0) 的 &&
-    const hasTopLevelAnd = (seg: Item[]): boolean => {
-      let d = 0;
-      for (const t of seg) {
-        if (t.type === 'leftParen') d++;
-        else if (t.type === 'rightParen') d--;
-        else if (t.type === 'logic' && t.value === '&&' && d === 0) return true;
-      }
-      return false;
-    };
-
-    // 5. 重建表达式,对需要的段添加括号
-    return segments
-      .map((seg) => {
-        const str = seg.map((i) => i.value).join(' ');
-        if (!isEnclosed(seg) && hasTopLevelAnd(seg)) {
-          return `( ${str} )`;
-        }
-        return str;
-      })
-      .join(' || ');
-  };
-
-  /**
-   * 判断 items 最外层(depth=0)是否同时存在 && 和 ||
-   */
-  const checkTopLevelMixed = (): boolean => {
-    let depth = 0;
-    let hasAnd = false;
-    let hasOr = false;
-    for (const item of items.value) {
-      if (item.type === 'leftParen') {
-        depth++;
-      } else if (item.type === 'rightParen') {
-        depth--;
-      } else if (item.type === 'logic' && depth === 0) {
-        if (item.value === '&&') hasAnd = true;
-        else if (item.value === '||') hasOr = true;
-      }
-    }
-    return hasAnd && hasOr;
-  };
-
-  // 提交逻辑
+  /** 提交逻辑 */
   const confirm = async (): Promise<void> => {
     const expr = expressionString.value;
-    const validation = validateExpression(expr);
+    const validation = validateExpression(expr, operandSet);
     if (!validation.valid) {
       errorMessage.value = validation.message || '表达式不合法';
       return;
@@ -585,13 +215,13 @@
     errorMessage.value = '';
 
     // 基于 items 判断最外层是否存在未加括号的 && 与 || 混合
-    const hasMixed = checkTopLevelMixed();
+    const hasMixed = checkTopLevelMixed(items.value);
     let finalExpr = expr;
 
     if (hasMixed) {
       await new Promise<void>((resolve) => {
         const originalExpr = expr;
-        const suggestedExpr = addParenthesesForAnd(expr);
+        const suggestedExpr = addParenthesesForAnd(items.value);
 
         Modal.confirm({
           title: '运算符优先级提醒',
@@ -612,7 +242,7 @@
           onOk: () => {
             finalExpr = suggestedExpr;
             // 同步到画布(仅当用户选择自动添加时才更新画布)
-            const parsed = parseExpression(finalExpr);
+            const parsed = parseExpression(finalExpr, operandSet);
             if (parsed.length) items.value = parsed;
             resolve();
           },
@@ -629,7 +259,7 @@
     message.success('已更新');
   };
 
-  // 重置
+  /** 重置整个组件状态 */
   const reset = (): void => {
     items.value = [];
     inputString.value = '';
@@ -639,14 +269,14 @@
     emit('change', '');
   };
 
-  // 进入高级模式
+  /** 切换至高级编辑模式,将外部传入的表达式加载到画布 */
   const enterAdvancedMode = (): void => {
     // 将当前 modelValue 初始化到画布
     initializeFromExpression(props.modelValue);
     compact.value = false;
   };
 
-  // 返回简易模式
+  /** 从高级模式返回简易模式,如有变更则提示用户确认 */
   const backToCompact = async (): Promise<void> => {
     const currentExpr = expressionString.value;
     if (currentExpr === props.modelValue) {
@@ -670,6 +300,7 @@
     });
   };
 
+  /** 应用选中的快捷模板 */
   const applyTemplate = (): void => {
     if (!selectedTemplate.value) return;
     initializeFromExpression(selectedTemplate.value);
@@ -677,10 +308,11 @@
     message.success('模板已应用');
   };
 
+  /** 应用手动输入的表达式字符串 */
   const applyManualInput = (): void => {
     const expr = manualInput.value.trim();
     if (!expr) return;
-    const validation = validateExpression(expr);
+    const validation = validateExpression(expr, operandSet);
     if (!validation.valid) {
       errorMessage.value = validation.message || '表达式不合法';
       return;
@@ -695,29 +327,19 @@
     (newVal) => {
       if (newVal !== undefined) {
         inputString.value = newVal;
-        // 如果当前处于高级模式,也同步画布
-        // if (!compact.value) {
-        const parsed = parseExpression(newVal);
+        const parsed = parseExpression(newVal, operandSet);
         items.value = parsed.length ? parsed : [];
-        // }
       }
     },
     { immediate: true }
   );
 
-  // 暴露校验方法供表单 rules 使用
-  defineExpose({ validate: () => validateExpression(props.modelValue) });
+  /** 对外暴露校验方法,供表单 rules 使用 */
+  defineExpose({ validate: () => validateExpression(props.modelValue, operandSet) });
 </script>
 
 <style scoped lang="less">
   .relation-builder {
-    // :deep(.ant-card) {
-    //   height: 100%;
-    // }
-    // :deep(.ant-card-body) {
-    //   height: calc(100% - 57px);
-    //   overflow-y: auto;
-    // }
     .relation-card {
       height: 680px;
       overflow-y: auto;