瀏覽代碼

Merge branch 'test-dev' of http://219.151.181.73:3000/web/saber into test-dev

lvy 1 月之前
父節點
當前提交
ad697176d0

+ 1 - 1
public/index.html

@@ -17,7 +17,7 @@
   <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/saber/iconfont.css">
   <link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/2.9.5/index.css">
 
-  <link href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css" rel="stylesheet">
+
   <script src="<%= BASE_URL %>cdn/xlsx/FileSaver.min.js"></script>
   <script src="<%= BASE_URL %>cdn/xlsx/xlsx.full.min.js"></script>
   <link rel="icon" href="<%= BASE_URL %>favicon.png">

+ 33 - 0
src/api/manager/wbsformelement.js

@@ -80,3 +80,36 @@ export const importWbsElement = (formData) => {
     data: formData
   })
 }
+//同步查询功能
+export const querySyncRecord = (data) => {
+  return request({
+    url: '/api/blade-manager/synchronousRecord/querySyncRecord',
+    method: 'get',
+    params:data
+  })
+}
+
+//复制节点
+export const copyNode = (formData) => {
+  return request({
+    url: '/api/blade-manager/wbsTree/copyNode',
+    method: 'post',
+    data: formData
+  })
+}
+//删除同步记录
+export const deleteRecord = (data) => {
+  return request({
+    url: '/api/blade-manager/synchronousRecord/delete',
+    method: 'get',
+    params:data
+  })
+}
+//刷新同步记录状态
+export const reFlush = (data) => {
+  return request({
+    url: '/api/blade-manager/synchronousRecord/reFlush',
+    method: 'get',
+    params:data
+  })
+}

+ 1 - 0
src/assets/heart-fill.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="rgba(234,46,46,1)"><path d="M12.001 4.52853C14.35 2.42 17.98 2.49 20.2426 4.75736C22.5053 7.02472 22.583 10.637 20.4786 12.993L11.9999 21.485L3.52138 12.993C1.41705 10.637 1.49571 7.01901 3.75736 4.75736C6.02157 2.49315 9.64519 2.41687 12.001 4.52853Z"></path></svg>

+ 1 - 0
src/assets/heart-line.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.001 4.52853C14.35 2.42 17.98 2.49 20.2426 4.75736C22.5053 7.02472 22.583 10.637 20.4786 12.993L11.9999 21.485L3.52138 12.993C1.41705 10.637 1.49571 7.01901 3.75736 4.75736C6.02157 2.49315 9.64519 2.41687 12.001 4.52853ZM18.827 6.1701C17.3279 4.66794 14.9076 4.60701 13.337 6.01687L12.0019 7.21524L10.6661 6.01781C9.09098 4.60597 6.67506 4.66808 5.17157 6.17157C3.68183 7.66131 3.60704 10.0473 4.97993 11.6232L11.9999 18.6543L19.0201 11.6232C20.3935 10.0467 20.319 7.66525 18.827 6.1701Z"></path></svg>

+ 16 - 8
src/views/exctab/excelmodel/excelmodel.vue

@@ -385,6 +385,7 @@ export default {
       pdfDrawerExcelRef: null,
       saveExcelJsonLoadPdf: false,
       saveDataInfo: {},
+      pdfDataUrl:''
     }
   },
   computed: {
@@ -1156,17 +1157,12 @@ async saveExcelInfo() {
         
         if (res.code === 200) {
           console.log(res.data,'data');
-        const { data: res1 } = await getExcelPdf({
-          key: this.from.id,
-          url: res1.data
-        });
+          this.pdfDataUrl = res.data;
+          this.saveExcelInfoPdfUrL()
 
-          this.$nextTick(() => {
-            this.$refs.pdfDrawerExcelRef.show(res1.data,true)
-          
-          })
           
         } else {
+          this.pdfDataUrl=''
           this.$message.error(res.msg);
         
         }
@@ -1178,6 +1174,18 @@ async saveExcelInfo() {
       }
 
 
+},
+ async saveExcelInfoPdfUrL() { 
+    const { data: res } = await getExcelPdf({
+          key: this.from.id,
+          url: this.pdfDataUrl
+        });
+        console.log(res.data,'res1');
+        
+          this.$nextTick(() => {
+            this.$refs.pdfDrawerExcelRef.show(res.data,true)
+          
+          })
 },
 async saveExcelInfoPdf() {
   this.saveExcelJsonLoadPdf = true; // 开始加载

+ 2 - 2
src/views/exctab/exceltab.vue

@@ -507,7 +507,7 @@ export default {
       });
     },
     rowDel (row) {
-      this.$confirm("确定将选择数据删除?", {
+      this.$confirm('删除后,数据无法恢复,是否确认删除?', '删除确认', {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"
@@ -528,7 +528,7 @@ export default {
         this.$message.warning("请选择至少一条数据");
         return;
       }
-      this.$confirm("确定将选择数据删除?", {
+      this.$confirm('删除后,数据无法恢复,是否确认删除?', '删除确认', {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"

+ 2 - 2
src/views/formula/component/formulaTemplate.vue

@@ -8,8 +8,8 @@
         <span v-if="JSON.stringify(item)!=='null'">
  
             <el-radio-group v-model="formulainfo.arguments[index]"  v-if="formulainfo.template.args[index].m=='是否同行/列计算(选填)'">
-                <el-radio label="x">同行</el-radio>
-                <el-radio label="y">同列</el-radio>
+                <el-radio label="y">同行</el-radio>
+                <el-radio label="x">同列</el-radio>
                
               </el-radio-group>
 

+ 188 - 63
src/views/formula/component/funComponent/dataChange.vue

@@ -1,23 +1,22 @@
 <template>
   <div class="data-change-form">
-    <div class="form-title">数据自变</div>
-    
+   
+    <div class="mg-b-20">中文输入方式:(xx),括号为英文括号</div>
     <el-form :model="formData" label-width="100px" class="form-container" size="small">
       <!-- 变化方式和数据格式行 -->
       <el-row :gutter="20">
         <el-col :span="8">
           <el-form-item label="变化方式:">
             <el-select v-model="formData.changeMethod" placeholder="请选择">
-              <el-option label="自增" value="increment"></el-option>
-              <el-option label="自减" value="decrement"></el-option>
+              <el-option label="自增" value="+"></el-option>
+              <el-option label="自减" value="-"></el-option>
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="8">
           <el-form-item label="数据格式:">
             <el-select v-model="formData.dataFormat" placeholder="请选择">
-              <el-option label="时间" value="time"></el-option>
-              <el-option label="日期" value="number"></el-option>
+              <el-option label="时间" value="date"></el-option>
             </el-select>
           </el-form-item>
         </el-col>
@@ -32,13 +31,15 @@
       <el-row :gutter="20">
         <el-col :span="24">
           <el-form-item label="协同元素:">
-            <el-input 
-              v-model="formData.coordinationElement" 
-              placeholder="请选择参数(非必填)" 
-              clearable
-              @focus="setFocusedField('coordinationElement')"
-              @blur="clearFocusedField"
-            ></el-input>
+            <vue-tags-input 
+                v-model="coordinationTag" 
+                :tags="coordinationTags" 
+                @focus="setElseFocus('coordination')" 
+                @blur="elseInputBlur('coordination')" 
+                placeholder="请选择/输入参数" 
+                @before-adding-tag="beforeAddingTag" 
+                @keyup.native="btKeyUp"
+              />
           </el-form-item>
         </el-col>
       </el-row>
@@ -47,18 +48,26 @@
       <el-row :gutter="1" class="frequency-row">
         <el-col :span="12">
           <el-form-item label="频率:">
-            <el-input v-model="formData.frequency" placeholder="输入自增频率" clearable></el-input>
+            <vue-tags-input 
+                v-model="frequencyTag" 
+                :tags="frequencyTags" 
+                @focus="setElseFocus('frequency')" 
+                @blur="elseInputBlur('frequency')" 
+                placeholder="请选择/输入频率" 
+                @before-adding-tag="beforeAddingTag" 
+                @keyup.native="btKeyUp"
+              />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label-width="0px" >
             <el-select v-model="formData.frequencyUnit" placeholder="单位" clearable style="width: 120px;">
-                <el-option label="年" value="second"></el-option>
-                <el-option label="月" value="minute"></el-option>
-                <el-option label="日" value="hour"></el-option>
-                <el-option label="时" value="day"></el-option>
-                <el-option label="分" value="month"></el-option>
-                <el-option label="秒" value="year"></el-option>
+                <el-option label="年" value="1"></el-option>
+                <el-option label="月" value="2"></el-option>
+                <el-option label="日" value="3"></el-option>
+                <el-option label="时" value="4"></el-option>
+                <el-option label="分" value="5"></el-option>
+                <el-option label="秒" value="6"></el-option>
             </el-select>
             </el-form-item>
         </el-col>
@@ -68,13 +77,15 @@
         <el-row :gutter="10" class="stop-condition-row">
         <el-col :span="9">
             <el-form-item label="停止条件:">
-            <el-input 
-                v-model="formData.stopConditionParam" 
-                placeholder="请选择参数(非必填)" 
-                clearable
-                @focus="setFocusedField('stopConditionParam')"
-                @blur="clearFocusedField"
-            ></el-input>
+              <vue-tags-input 
+                  v-model="stopConditionParamTag" 
+                  :tags="stopConditionParamTags" 
+                  @focus="setElseFocus('stopConditionParam')" 
+                  @blur="elseInputBlur('stopConditionParam')" 
+                  placeholder="请选择参数" 
+                  @before-adding-tag="beforeAddingTag" 
+                  @keyup.native="btKeyUp"
+                />
             </el-form-item>
         </el-col>
         <el-col :span="4">
@@ -84,24 +95,26 @@
                 placeholder="请选择" 
                 clearable
             >
-                <el-option label="=" value="equal"></el-option>
-                <el-option label=">" value="greater"></el-option>
-                <el-option label="<" value="less"></el-option>
-                <el-option label="≥" value="greaterEqual"></el-option>
-                <el-option label="≤" value="lessEqual"></el-option>
-                <el-option label="≠" value=""></el-option>
+                <el-option label="=" value="="></el-option>
+                <el-option label=">" value=">"></el-option>
+                <el-option label="<" value="<"></el-option>
+                <el-option label="≥" value=""></el-option>
+                <el-option label="≤" value=""></el-option>
+                <el-option label="≠" value="!="></el-option>
             </el-select>
             </el-form-item>
         </el-col>
         <el-col :span="8">
             <el-form-item>
-            <el-input 
-                v-model="formData.stopConditionValue" 
-                placeholder="请选择/输入参数值" 
-                clearable
-                @focus="setFocusedField('stopConditionValue')"
-                @blur="clearFocusedField"
-            ></el-input>
+              <vue-tags-input 
+                  v-model="stopConditionValueTag" 
+                  :tags="stopConditionValueTags" 
+                  @focus="setElseFocus('stopConditionValue')" 
+                  @blur="elseInputBlur('stopConditionValue')" 
+                  placeholder="请选择/输入参数值" 
+                  @before-adding-tag="beforeAddingTag" 
+                  @keyup.native="btKeyUp"
+                />
             </el-form-item>
         </el-col>
         </el-row>
@@ -110,8 +123,11 @@
 </template>
 
 <script>
+import {convertToFCDataChange,extractElementMap,parseFCDataChange,restoreOriginalData} from './multiIfElseTools'
+
 export default {
   name: "DatasChange",
+
   props: {
     formulainfo: {
       type: Object,
@@ -125,20 +141,38 @@ export default {
         return {};
       }
     },
+    map:{
+        type: String,
+        default:''
+    },
+    dataForm:{
+      type: Object,
+      default: function () {
+        return {};
+      }
+    }
+    
   },
   data() {
     return {
       value1: true,
-      focusedField: null, // 记录当前聚焦的字段
+      elseFocus: null, // 当前聚焦的标签输入框
+      
+      // 标签输入框相关数据
+      coordinationTag: '',
+      coordinationTags: [],
+      frequencyTag: '',
+      frequencyTags: [],
+      stopConditionParamTag: '',
+      stopConditionParamTags: [],
+      stopConditionValueTag: '',
+      stopConditionValueTags: [],
+      
       formData: {
         changeMethod: '',
-        dataFormat: 'time',
-        coordinationElement: '',
-        frequency: '',
+        dataFormat: '',
         frequencyUnit: '',
-        stopConditionParam: '',
-        stopConditionOperator: 'equal',
-        stopConditionValue: ''
+        stopConditionOperator: ''
       }
     };
   },
@@ -146,6 +180,25 @@ export default {
     // 从props初始化表单数据
     if (this.formulainfo) {
       this.formData = { ...this.formData, ...this.formulainfo };
+      console.log(this.formulainfo,'this.formulainfo');
+      console.log(this.dataForm,'this.dataForm11111111');
+      const resObj=restoreOriginalData(this.dataForm,JSON.parse(this.map));
+      console.log(JSON.parse(this.map),'this.map');
+      
+      console.log(resObj,'resObj');
+      this.formData.changeMethod=resObj.changeMethod;
+      this.formData.dataFormat=resObj.dataFormat;
+      this.formData.frequencyUnit=resObj.frequencyUnit;
+      this.formData.stopConditionOperator=resObj.stopConditionOperator;
+      this.coordinationTag=resObj.coordinationTag;
+      this.coordinationTags=resObj.coordinationTags;
+      this.frequencyTag=resObj.frequencyTag;
+      this.frequencyTags=resObj.frequencyTags;
+      this.stopConditionParamTag=resObj.stopConditionParamTag;
+      this.stopConditionParamTags=resObj.stopConditionParamTags;
+      this.stopConditionValueTag=resObj.stopConditionValueTag;
+      this.stopConditionValueTags=resObj.stopConditionValueTags;
+
     }
   },
   methods: {
@@ -154,27 +207,98 @@ export default {
       this.$emit('select-element');
     },    
     setELe(ele) {
-      console.log(ele, 'ele');
+      console.log(this.elseFocus,'this.elseFocus');
+      
+      if (!this.elseFocus) return;
+      
+      let obj = {};
+      if(ele.k){
+        obj = {
+          type:'ParamData',
+          name:ele.name,
+          id:ele.id,
+          selected:false,
+          v:ele.v,
+          k:ele.k,
+          children:[],
+        }
+      }else{
+        obj = {
+          type:'Element',
+          name:ele.eName,
+          id:ele.id,
+          selected:false,
+          tableElementKey:ele.tableElementKey,
+          children:[],
+        }
+      }
       
-      // 当有元素被选择且有聚焦的字段时,填充对应的值
-      if (ele && ele.eName && this.focusedField) {
-        this.$set(this.formData, this.focusedField, ele.eName);
-        console.log(this.formData,'this.formData');
-        
-        // 可以选择在填充后取消聚焦状态
-        this.focusedField = null;
+      // 根据当前焦点位置设置对应的标签
+      const position = this.elseFocus;
+      const tagField = `${position}Tag`;
+      const tagsField = `${position}Tags`;
+      
+      // 清除之前选择的元素
+      if (this[tagsField] && this[tagsField].length > 0) {
+        this[tagsField].forEach(item => {
+          this.$emit('uncheck', item.id);
+        });
       }
+      
+      // 设置新选择的元素
+      obj.text = obj.name;
+      obj.style = 'background-color: #409EFF';
+      this[tagsField] = [obj]; // 每次选择一个新元素替换现有元素
+      this[tagField] = '';
+      
+      // 可以在这里添加其他需要的逻辑
     },
     // 设置当前聚焦的字段
-    setFocusedField(fieldName) {
-
-        
-      this.focusedField = fieldName;
+    setElseFocus(position) {
+      console.log(position,'position');
+      
+      this.elseFocus = position;
     },
     // 清除聚焦状态
-    clearFocusedField() {
-      // 可以保留聚焦状态直到下一次操作,或立即清除
-      // this.focusedField = null;
+    elseInputBlur(position) {
+      // if (this.elseFocus === position) {
+      //   this.elseFocus = null;
+      // }
+    },
+    // 添加标签前的处理
+    beforeAddingTag(tag) {
+      // 可以在这里验证标签格式等
+      return true;
+    },
+    // 键盘事件处理
+    btKeyUp(e) {
+      // 可以添加键盘事件处理逻辑
+    },
+    getDataChangeList(){
+      // 整合表单数据,包括标签信息
+      const result = {
+        ...this.formData,
+        coordinationTag: this.coordinationTag,
+        coordinationTags: this.coordinationTags,
+        frequencyTags: this.frequencyTags,
+        frequencyTag: this.frequencyTag,
+        stopConditionParamTags: this.stopConditionParamTags,
+        stopConditionParamTag: this.stopConditionParamTag,
+        stopConditionValueTags: this.stopConditionValueTags,
+        stopConditionValueTag: this.stopConditionValueTag
+      };
+      console.log(result, '表单数据');
+      let test = convertToFCDataChange(result);
+      console.log(test,'resObj');
+      let map=extractElementMap(result);
+      console.log(map,'map');
+      
+      let resObj={
+        test,
+        map
+      }
+      
+      return resObj;
     }
   }
 };
@@ -207,7 +331,8 @@ export default {
   
   // 确保输入框占满列宽
   ::v-deep .el-input,
-  ::v-deep .el-select {
+  ::v-deep .el-select,
+  ::v-deep .vue-tags-input {
     width: 100%;
   }
 }

+ 599 - 65
src/views/formula/component/funComponent/ifelse.vue

@@ -12,17 +12,22 @@
         <el-option label="小于等于且大于等于" value="<=&&<="></el-option>
         <el-option label="试验判断" value="|"></el-option>
         <el-option label="多条件" value="more"></el-option>
+        <el-option label="数据获取" value="getData"></el-option>
       </el-select>
       <span class="mg-l-20 mg-r-10" v-if="symbol!=='|'">执行结果</span>
-      <el-select v-model="result" @change="setTF()" size="medium" placeholder="请选择" v-if="symbol!=='|'">
+      <el-select v-model="result" @change="setTF()" size="medium" placeholder="请选择" v-if="symbol!=='|'&&symbol!=='getData'" >
         <el-option label="真假值" value="1"></el-option>
         <el-option label="运算" value="2"></el-option>
+      </el-select>
+        <el-select v-model="result" size="medium" placeholder="请选择"    v-if="symbol=='getData'">
+        <el-option label="数据获取" value="3"></el-option>
+      
       </el-select>
       <el-button class="mg-l-10" size="small" type="info" @click="showSelectEle">选择参数</el-button>
     </div>
 
     <!-- 多条件其他的 -->
-    <template v-if="symbol !== 'more'"> 
+    <template v-if="symbol !== 'more'&&symbol !== 'getData'"> 
         <div class="mg-t-20 flex jc-al-c">
             <span class="textblod mg-r-20"  v-if="symbol!=='|'">IF</span>
             <div class="flex jc-al-c" v-if="symbol == '<&&<' || symbol == '<=&&<='">
@@ -105,7 +110,8 @@
         </div>
     </template>
     <!-- 多条件 -->
-     <template v-else>
+     <template v-if="symbol == 'more'"> 
+
       <div v-for="(item, conditionIndex) in conditionList" :key="conditionIndex">
         <!-- 从第二个条件开始显示“条件X”和删除按钮 -->
         <div v-if="conditionIndex >= 1" class="condition-header flex al-c  mg-t-20">
@@ -152,7 +158,7 @@
                       <el-option label="=" value="="></el-option>
                       <el-option label=">" value=">"></el-option>
                       <el-option label="<" value="<"></el-option>
-                      <el-option label="≠" value=""></el-option>
+                      <el-option label="≠" value="!= "></el-option>
                       <el-option label="包含" value="包含"></el-option>
                       <el-option label="不包含" value="不包含"></el-option>
                 </el-select>
@@ -180,17 +186,23 @@
             @before-adding-tag="beforeAddingTag" 
             @keyup.native="btKeyUp"
           />
-          <span class="textblod mg-r-10 mg-l-20">不满足</span>
-          <vue-tags-input 
-            v-model="conditionList[conditionIndex].tag5" 
-            :tags="conditionList[conditionIndex].tags5" 
-            @focus="setMultiFocus(conditionIndex, null, 'tag5')" 
-            @blur="inputBlurMulti(conditionIndex, null, 'tag5')" 
-            placeholder="输入/参数" 
-            @before-adding-tag="beforeAddingTag" 
-            @keyup.native="btKeyUp"
-          />
-          <div class="mg-l-20">
+          
+          <!-- 只在最后一个条件显示"不满足"输入框 -->
+          <template v-if="conditionIndex === conditionList.length - 1">
+            <span class="textblod mg-r-10 mg-l-20">不满足</span>
+            <vue-tags-input 
+              v-model="conditionList[conditionIndex].tag5" 
+              :tags="conditionList[conditionIndex].tags5" 
+              @focus="setMultiFocus(conditionIndex, null, 'tag5')" 
+              @blur="inputBlurMulti(conditionIndex, null, 'tag5')" 
+              placeholder="输入/参数" 
+              @before-adding-tag="beforeAddingTag" 
+              @keyup.native="btKeyUp"
+            />
+          </template>
+          
+          <!-- 只在最后一个条件显示"添加其他条件"按钮 -->
+          <div class="mg-l-20" v-if="conditionIndex === conditionList.length - 1">
               <el-button  size="small" type="primary" @click="addCondition(conditionIndex)">添加其他条件</el-button>
           </div>
         </div>
@@ -224,7 +236,9 @@
                         </draggable>
                       </div>
                     </div>
-                    <div class="flex mg-t-10">
+                    
+                    <!-- 只在最后一个条件显示"不满足"公式框 -->
+                    <div class="flex mg-t-10" v-if="conditionIndex === conditionList.length - 1">
                       <span class="textblod mg-r-10">不满足</span>
                       <div class="border-grey sele-ele-box flex1" 
                           :class="{'border-green': isCurFocus(conditionIndex, null, 'formula2')}" 
@@ -252,12 +266,119 @@
                         </draggable>
                       </div>
                     </div>
-                  <el-button size="small" @click="addCondition(conditionIndex)" type="primary">添加条件</el-button>
+                    
+                    <!-- 只在最后一个条件显示"添加条件"按钮 -->
+                    <el-button size="small" @click="addCondition(conditionIndex)" type="primary" v-if="conditionIndex === conditionList.length - 1">添加条件</el-button>
               </div>
         </template>
       
       </div>
      </template>
+     <!-- 数据获取配置框 - 修改部分 -->
+     <template v-if="symbol==='getData'">
+        <div class="mg-t-10">
+              <div v-for="(item, index) in getDataList" :key="index"  class="mg-t-10">
+                <div class="flex jc-al-c">
+                  <div class="textblod mg-r-10" :style="{ opacity: index === 0 ? 1 : 0 }">
+                    当
+                  </div>
+                  <div class="mg-r-10" :style="{ opacity: index === 0 ? 1 : 0 }">
+                    <vue-tags-input 
+                      v-model="item.tagWhenLeft" 
+                      :tags="item.tagsWhenLeft" 
+                      @focus="setDataFocus(index, 'tagWhenLeft')" 
+                      @blur="dataInputBlur(index, 'tagWhenLeft')" 
+                      placeholder="输入/参数" 
+                      @before-adding-tag="beforeAddingTag" 
+                      @keyup.native="btKeyUp"
+
+                    />
+                  </div>
+                  <div  class="mg-r-10">
+                      <el-select 
+                      v-model="item.condition" 
+                      @change="setDataCondition(index)" 
+                      size="medium" 
+                      placeholder="请选择"  
+                      style="width:120px"
+                    >
+                          <el-option label=">=" value=">="></el-option>
+                          <el-option label="<=" value="<="></el-option>
+                          <el-option label="=" value="="></el-option>
+                          <el-option label=">" value=">"></el-option>
+                          <el-option label="<" value="<"></el-option>
+                          <el-option label="≠" value="≠"></el-option>
+                          <el-option label="包含" value="包含"></el-option>
+                          <el-option label="不包含" value="不包含"></el-option>
+                    </el-select>
+                  </div>
+                  <div  class="mg-r-10">
+                    <vue-tags-input 
+                      v-model="item.tagWhenRight" 
+                      :tags="item.tagsWhenRight" 
+                      @focus="setDataFocus(index, 'tagWhenRight')" 
+                      @blur="dataInputBlur(index, 'tagWhenRight')" 
+                      placeholder="输入/参数" 
+                      @before-adding-tag="beforeAddingTag" 
+                      @keyup.native="btKeyUp"
+                    />
+                  </div>
+                  
+                  <div  class="mg-r-10 textblod">则</div>
+                    <vue-tags-input 
+                      v-model="item.tagThenLeft" 
+                      :tags="item.tagsThenLeft" 
+                      @focus="setDataFocus(index, 'tagThenLeft')" 
+                      @blur="dataInputBlur(index, 'tagThenLeft')" 
+                      placeholder="输入/参数" 
+                      @before-adding-tag="beforeAddingTag" 
+                      @keyup.native="btKeyUp"
+                    />
+                    <span  class="mg-r-10 mg-l-10">=</span>
+                    <div class="mg-r-10">
+                        <vue-tags-input 
+                          v-model="item.tagThenRight" 
+                          :tags="item.tagsThenRight" 
+                          @focus="setDataFocus(index, 'tagThenRight')" 
+                          @blur="dataInputBlur(index, 'tagThenRight')" 
+                          placeholder="输入/参数" 
+                          @before-adding-tag="beforeAddingTag" 
+                          @keyup.native="btKeyUp"
+                        />
+                    </div>
+                      <i class="el-icon-circle-plus-outline mg-l-20" style="color: #409EFF;cursor: pointer;" @click="addDataItem(index)"></i>
+                    <i class="el-icon-delete mg-l-20" style="color: red;cursor: pointer;" @click="removeDataItem(index)" v-if="getDataList.length > 1"></i>
+                </div>
+               
+              </div>
+              <div class="mg-t-10  ">
+                  <div class="textblod "  >否则</div>
+                  <div class="flex jc-al-c "  style="float: right; margin-top: -28px;" >
+                    <vue-tags-input 
+                      v-model="elseTagLeft" 
+                      :tags="elseTagsLeft" 
+                      @focus="setElseFocus('left')" 
+                      @blur="elseInputBlur('left')" 
+                      placeholder="请选择参数" 
+                      @before-adding-tag="beforeAddingTag" 
+                      @keyup.native="btKeyUp"
+                      class="mg-r-10"
+                    />
+                    <span class="mg-r-10">=</span>
+                    <vue-tags-input 
+                      v-model="elseTagRight" 
+                      :tags="elseTagsRight" 
+                      @focus="setElseFocus('right')" 
+                      @blur="elseInputBlur('right')" 
+                      placeholder="请选择/输入参数" 
+                      @before-adding-tag="beforeAddingTag" 
+                      @keyup.native="btKeyUp"
+                    />
+                  </div>
+                </div>
+            </div>
+      </template>
+
 
     <el-dialog title="输入值" :visible.sync="inputVisible" width="300px" append-to-body :close-on-click-modal="false">
       <el-input v-model="inputText" placeholder="请输入内容"></el-input>
@@ -273,6 +394,7 @@
 import VueTagsInput from '@johmun/vue-tags-input';
 import formulaItem from "../formulaItem";
 import draggable from 'vuedraggable';
+import {transformData,generateElementMap,restoreData} from './multiIfElseTools'
 
 export default {
   name: "ifelse",
@@ -314,12 +436,32 @@ export default {
     conditionIndex:{//当前是多条件中的哪一个
       type: Number,
       default: 0
-    }
+    },
+    isGetData:{
+      type: Boolean,
+      default: false
+    },
+    dataListGet:{
+      type: String,
+      default:''
+      },
+      map:{
+        type: String,
+        default:''
+      },
+      remark:{
+        type: String,
+        default:'',
+      },
+    
+    
+
   },
   data(){
     return{
       symbol:'>=',
       result:'1',
+     
 
       selectEleFormula:[],
       selectEleFormula2:[],
@@ -332,6 +474,11 @@ export default {
         innerIndex: -1,
         field: ''
       },
+      // 数据获取部分的焦点信息
+      dataFocus: {
+        index: -1,
+        field: ''
+      },
       symbolReg:/(\+|-|\*|\/)(.+)/,
       inputVisible:false,
       inputText:'',
@@ -357,7 +504,7 @@ export default {
               tag3: '',
               tags3: [],
               symbol1: '',
-              logicSymbol: '&&'
+              logicSymbol: ''
             }
           ],
           tag4: '',
@@ -367,9 +514,60 @@ export default {
           formula1: [],
           formula2: []
         }
-      ]//多条件的条件列表
+      ],//多条件的条件列表
+      // 修改getDataList结构,为每个项添加独立属性
+      getDataList:[
+        {
+          tagWhenLeft: '',
+          tagsWhenLeft: [],
+          condition: '',
+          tagWhenRight: '',
+          tagsWhenRight: [],
+          tagThenLeft: '',
+          tagsThenLeft: [],
+          tagThenRight: '',
+          tagsThenRight: []
+        }
+      ],//数据获取列表
+        // 否则部分的属性
+      elseTagLeft: '',
+      elseTagsLeft: [],
+      elseTagRight: '',
+      elseTagsRight: [],
+      elseFocus: '' // 用于跟踪否则部分的焦点
+
     }
   },
+  watch: {
+  // 监视getDataList的变化
+  getDataList: {
+    deep: true,
+    handler(newVal) {
+      // 只有当数组中有至少一个元素时才处理
+      if (newVal.length > 0) {
+        // 获取第一个元素的tagWhenLeft值
+        const firstTagWhenLeft = newVal[0].tagWhenLeft;
+        const firstTagsWhenLeft = newVal[0].tagsWhenLeft;
+        
+        // 如果第一个元素有值,同步到其他元素
+        if ((firstTagWhenLeft && firstTagWhenLeft.trim() !== '') || 
+            (firstTagsWhenLeft && firstTagsWhenLeft.length > 0)) {
+          
+          // 遍历从第二个元素开始的所有元素
+          for (let i = 1; i < newVal.length; i++) {
+            // 避免触发不必要的watch循环
+            if (newVal[i].tagWhenLeft !== firstTagWhenLeft || 
+                JSON.stringify(newVal[i].tagsWhenLeft) !== JSON.stringify(firstTagsWhenLeft)) {
+              
+              this.$set(newVal[i], 'tagWhenLeft', firstTagWhenLeft);
+              this.$set(newVal[i], 'tagsWhenLeft', [...firstTagsWhenLeft]);
+            }
+          }
+        }
+      }
+    }
+  }
+},
   mounted(){
     console.log(this.formulainfo,'this.formulainfo333333');
     
@@ -383,23 +581,168 @@ export default {
     ){
       this.initTFInput(args1,args2);
     }else if(Array.isArray(args1) || Array.isArray(args2)){
-      this.result = '2';
+      //
+      if(!this.isMore){
+         this.result = '2';
+      }
       this.initTFFormula(args1,args2)
     }
     if(this.isMore){
       this.symbol = 'more';
+
       console.log( this.moreConditions,' this.moreConditions;');
-      
+  
+      // if(formula1.length>1 || formula2>1){
+      //   this.result = '2';
+      // }else{
+      //   this.result = '1';
+      // }
+      this.result=this.remark||'1'
+     
       this.conditionList = this.moreConditions.map(item => this.clearTagsIfHasValue(item));
 
        
       
-    }else{
+    }else if(this.isGetData){
+      this.symbol = 'getData';
+      this.result = '3';
+       console.log(this.dataListGet,'dataListGet');
+       const str=this.dataListGet;
+        let resultStr=''
+          // 找到第一个左括号的位置
+          const startIndex = str.indexOf('(');
+          // 找到最后一个右括号的位置
+          const endIndex = str.lastIndexOf(')');
+
+          // 提取括号内的内容
+          if (startIndex !== -1 && endIndex !== -1 && startIndex < endIndex) {
+            const result = str.substring(startIndex + 1, endIndex);
+            resultStr=result
+            console.log(result);
+          } else {
+            console.log("未找到有效的括号对");
+          }
+   
+          const resObj=restoreData(resultStr,JSON.parse(this.map))
+          console.log(resObj,'resObj');
+          this.getDataList=resObj.getDataList
+          this.elseTagLeft = resObj.elseTagLeft;
+          this.elseTagsLeft = resObj.elseTagsLeft;
+          this.elseTagRight = resObj.elseTagRight;
+          this.elseTagsRight = resObj.elseTagsRight;
+
+          
+          
+    }
+    else{
       // this.conditionList=[]
     }
     
+    // // 初始化数据获取列表
+    // if (this.symbol === 'getData' && this.formulainfo.arguments && this.formulainfo.arguments.length > 0) {
+    //   console.log(this.dataListGet,'dataListGet');
+      
+    //   // this.initGetDataList(this.formulainfo.arguments[0]);
+    // }
   },
   methods:{
+    // 初始化数据获取列表
+    initGetDataList(data) {
+      if (Array.isArray(data)) {
+        this.getDataList = data.map(item => ({
+          tagWhenLeft: item.tagWhenLeft || '',
+          tagsWhenLeft: item.tagsWhenLeft || [],
+          condition: item.condition || '',
+          tagWhenRight: item.tagWhenRight || '',
+          tagsWhenRight: item.tagsWhenRight || [],
+          tagThenLeft: item.tagThenLeft || '',
+          tagsThenLeft: item.tagsThenLeft || [],
+          tagThenRight: item.tagThenRight || '',
+          tagsThenRight: item.tagsThenRight || []
+        }));
+      }
+    },
+    
+    // 设置数据获取部分的焦点
+    setDataFocus(index, field) {
+      this.elseFocus=false
+      this.dataFocus = { index, field };
+    },
+    
+    // 数据获取部分输入框失焦处理
+    dataInputBlur(index, field) {
+      const item = this.getDataList[index];
+      const tagNumber = field.replace('tag', '');
+      
+      if (item[field]) {
+        if (item[`tags${tagNumber}`] && item[`tags${tagNumber}`][0]) {
+          this.$emit('uncheck', item[`tags${tagNumber}`][0].id);
+        }
+        item[`tags${tagNumber}`] = [];
+      }
+      
+      this.setGetDataCondition();
+    },
+    
+    // 添加数据获取项
+    addDataItem(index) {
+          // 获取第一个元素的值作为模板
+        const firstItem = this.getDataList[0];
+        
+        const newItem = {
+          // 新添加的项直接使用第一个元素的tagWhenLeft相关值
+          tagWhenLeft: firstItem.tagWhenLeft,
+          tagsWhenLeft: [...firstItem.tagsWhenLeft],
+          condition: '',
+          tagWhenRight: '',
+          tagsWhenRight: [],
+          tagThenLeft: '',
+          tagsThenLeft: [],
+          tagThenRight: '',
+          tagsThenRight: []
+        };
+        
+        this.getDataList.splice(index + 1, 0, newItem);
+        this.setGetDataCondition();
+          },
+    
+    // 删除数据获取项
+    removeDataItem(index) {
+      if (this.getDataList.length > 1) {
+        this.getDataList.splice(index, 1);
+        this.setGetDataCondition();
+      } else {
+        this.$message({ type: "warning", message: "至少保留一个数据获取条件" });
+      }
+    },
+    
+    // 设置数据获取条件
+    setDataCondition(index) {
+      this.setGetDataCondition();
+    },
+    
+    // 保存数据获取条件到公式信息
+    setGetDataCondition() {
+      this.formulainfo.arguments[0] = {
+        conditions: this.getDataList.map(item => ({
+          tagWhenLeft: item.tagWhenLeft,
+          tagsWhenLeft: item.tagsWhenLeft,
+          condition: item.condition,
+          tagWhenRight: item.tagWhenRight,
+          tagsWhenRight: item.tagsWhenRight,
+          tagThenLeft: item.tagThenLeft,
+          tagsThenLeft: item.tagsThenLeft,
+          tagThenRight: item.tagThenRight,
+          tagsThenRight: item.tagsThenRight
+        })),
+        elseCase: {
+          tagLeft: this.elseTagLeft,
+          tagsLeft: this.elseTagsLeft,
+          tagRight: this.elseTagRight,
+          tagsRight: this.elseTagsRight
+        }
+      };
+    },
 
     clearTagsIfHasValue(obj) {
            // 复制对象以避免修改原对象
@@ -860,6 +1203,48 @@ export default {
         this.inputVisible = false;
         this.inputText = '';
         return;
+      }else if(this.symbol === 'getData' && this.dataFocus.index !== -1) {
+        // 数据获取部分处理
+        const { index, field } = this.dataFocus;
+        const item = this.getDataList[index];
+        const formulaType = field;
+        
+        //简单语法判断
+        if(item[formulaType] && item[formulaType].length != 0){
+          let lastEle = item[formulaType][item[formulaType].length-1];
+          if(lastEle.type == 'Element'){
+            this.$message({ type: "warning", message: "输入值无法连续出现在元素后面" });
+            this.inputVisible = false;
+            return;
+          }
+          if(lastEle.type == 'Text'){
+            this.$message({ type: "warning", message: "输入值无法连续出现在输入值后面" });
+            this.inputVisible = false;
+            return;
+          }
+          if(lastEle.type == 'Brackets' && lastEle.name == ')'){
+            this.$message({ type: "warning", message: "输入值无法连续出现在右括号后面" });
+            this.inputVisible = false;
+            return;
+          }
+        }
+
+        // 添加输入值到相应字段
+        const tagField = field;
+        const tagsField = `tags${field.replace('tag', '')}`;
+        
+        item[tagsField].push({
+          type: 'Text',
+          name: this.inputText,
+          text: this.inputText,
+          style: 'background-color: #409EFF'
+        });
+        item[tagField] = '';
+        
+        this.setGetDataCondition();
+        this.inputVisible = false;
+        this.inputText = '';
+        return;
       }else{
         return;
       }
@@ -906,6 +1291,92 @@ export default {
     },
     
     setELe(ele){
+
+      if (this.symbol === 'getData' && this.elseFocus) {
+        let obj = {};
+        if(ele.k){
+          obj = {
+            type:'ParamData',
+            name:ele.name,
+            id:ele.id,
+            selected:false,
+            v:ele.v,
+            k:ele.k,
+            children:[],
+          }
+        }else{
+          obj = {
+            type:'Element',
+            name:ele.eName,
+            id:ele.id,
+            selected:false,
+            tableElementKey:ele.tableElementKey,
+            children:[],
+          }
+        }
+        
+        // 根据当前焦点位置设置对应的标签
+        const position = this.elseFocus;
+        const tagField = `elseTag${position.charAt(0).toUpperCase() + position.slice(1)}`;
+        const tagsField = `elseTags${position.charAt(0).toUpperCase() + position.slice(1)}`;
+        
+        // 清除之前选择的元素
+        if (this[tagsField] && this[tagsField][0]) {
+          this.$emit('uncheck', this[tagsField][0].id);
+        }
+        
+        // 设置新选择的元素
+        obj.text = obj.name;
+        obj.style = 'background-color: #409EFF';
+        this[tagsField] = [obj];
+        this[tagField] = '';
+        
+        this.setGetDataCondition();
+        return;
+      }
+      // 处理数据获取情况
+      if (this.symbol === 'getData' && this.dataFocus.index !== -1) {
+        const { index, field } = this.dataFocus;
+        const item = this.getDataList[index];
+        
+        let obj = {};
+        if(ele.k){
+          obj = {
+            type:'ParamData',
+            name:ele.name,
+            id:ele.id,
+            selected:false,
+            v:ele.v,
+            k:ele.k,
+            children:[],
+          }
+        }else{
+          obj = {
+            type:'Element',
+            name:ele.eName,
+            id:ele.id,
+            selected:false,
+            tableElementKey:ele.tableElementKey,
+            children:[],
+          }
+        }
+        
+        // 清除之前选择的元素
+        const tagsField = `tags${field.replace('tag', '')}`;
+        if (item[tagsField] && item[tagsField][0]) {
+          this.$emit('uncheck', item[tagsField][0].id);
+        }
+        
+        // 设置新选择的元素
+        obj.text = obj.name;
+        obj.style = 'background-color: #409EFF';
+        item[tagsField] = [obj];
+        item[field] = '';
+        
+        this.setGetDataCondition();
+        return;
+      }
+      
       // 处理多条件情况
       if (this.symbol === 'more' && this.multiFocus.conditionIndex !== -1) {
         const { conditionIndex, innerIndex, field } = this.multiFocus;
@@ -1121,14 +1592,14 @@ export default {
               tag3: inner.tag3 || '',
               tags3: inner.tags3 || [],
               symbol1: inner.symbol1 || '',
-              logicSymbol: inner.logicSymbol || '&&'
+              logicSymbol: inner.logicSymbol || ''
             })) : [{
               tag2: '',
               tags2: [],
               tag3: '',
               tags3: [],
               symbol1: '',
-              logicSymbol: '&&'
+              logicSymbol: ''
             }];
           
           return {
@@ -1146,45 +1617,49 @@ export default {
 
     //写入参数真假值判断if(xxx)部分
     setCondition(){
-      if(this.symbol == '<&&<' || this.symbol == '<=&&<='){
-        let arr = new Array(7).fill('');
-        let symbolArr = this.symbol.split('&&');
-        if(this.tags1.length == 0){
-          arr[0] = this.tag1;
-        }else{
-          arr[0] = this.tags1[0];
-        }
-        arr[1] = symbolArr[0];
-        if(this.tags2.length == 0){
-          arr[2] = this.tag2;
-          arr[4] = this.tag2;
-        }else{
-          arr[2] = this.tags2[0];
-          arr[4] = this.tags2[0];
-        }
-        arr[3] = '&&';
-        arr[5] = symbolArr[1];
-        if(this.tags3.length == 0){
-          arr[6] = this.tag3;
-        }else{
-          arr[6] = this.tags3[0];
-        }
-        this.formulainfo.arguments[0] = arr;
-      }else if(this.symbol !== 'more'){
-        let arr = new Array(3).fill('');
-        if(this.tags2.length == 0){
-          arr[0] = this.tag2;
-        }else{
-          arr[0] = this.tags2[0];
-        }
-        arr[1] = this.symbol;
-        if(this.tags3.length == 0){
-          arr[2] = this.tag3;
+      if(this.symbol == '<&&<' || this.symbol == '<=&&<=' || this.symbol !== 'more' && this.symbol !== 'getData') {
+        // 处理单条件情况
+        if(this.symbol == '<&&<' || this.symbol == '<=&&<='){
+          let arr = new Array(7).fill('');
+          let symbolArr = this.symbol.split('&&');
+          if(this.tags1.length == 0){
+            arr[0] = this.tag1;
+          }else{
+            arr[0] = this.tags1[0];
+          }
+          arr[1] = symbolArr[0];
+          if(this.tags2.length == 0){
+            arr[2] = this.tag2;
+            arr[4] = this.tag2;
+          }else{
+            arr[2] = this.tags2[0];
+            arr[4] = this.tags2[0];
+          }
+          arr[3] = '&&';
+          arr[5] = symbolArr[1];
+          if(this.tags3.length == 0){
+            arr[6] = this.tag3;
+          }else{
+            arr[6] = this.tags3[0];
+          }
+          this.formulainfo.arguments[0] = arr;
         }else{
-          arr[2] = this.tags3[0];
+          let arr = new Array(3).fill('');
+          if(this.tags2.length == 0){
+            arr[0] = this.tag2;
+          }else{
+            arr[0] = this.tags2[0];
+          }
+          arr[1] = this.symbol;
+          if(this.tags3.length == 0){
+            arr[2] = this.tag3;
+          }else{
+            arr[2] = this.tags3[0];
+          }
+          this.formulainfo.arguments[0] = arr;
         }
-        this.formulainfo.arguments[0] = arr;
-      } else {
+      } else if(this.symbol == 'more'){
+        this.result=this.remark||'1'
         // 处理多条件情况
         this.formulainfo.arguments[0] = this.conditionList.map(condition => ({
           formulaIfElse: condition.formulaIfElse.map(inner => ({
@@ -1202,6 +1677,9 @@ export default {
           formula1: condition.formula1,
           formula2: condition.formula2
         }));
+      } else if(this.symbol == 'getData'){
+        this.result='3'
+        // 数据获取情况已在setGetDataCondition中处理
       }
     },
 
@@ -1282,7 +1760,7 @@ export default {
 
     //写入参数真假值
     setTF(){
-      if(this.symbol !== 'more') {
+      if(this.symbol !== 'more' && this.symbol !== 'getData') {
         if(this.result == 1){
           if(this.tags4.length == 0){
             this.formulainfo.arguments[1] = this.tag4;
@@ -1299,6 +1777,11 @@ export default {
           this.formulainfo.arguments[2] = this.selectEleFormula2;
         }
       }
+      if(this.symbol == 'more'){
+        console.log(this.result,'this.result');
+        
+        this.remark=this.result
+      }
       // 多条件情况在setCondition中处理
     },
 
@@ -1319,7 +1802,7 @@ export default {
           tag3: '',
           tags3: [],
           symbol1: '',
-          logicSymbol: '&&'
+          logicSymbol: ''
         }
       ];
       newCondition.tag4 = '';
@@ -1351,11 +1834,12 @@ export default {
         tag3: '',
         tags3: [],
         symbol1: '',
-        logicSymbol: '&&'
+        logicSymbol: ''
       };
       
       this.conditionList[conditionIndex].formulaIfElse.splice(innerIndex + 1, 0, newInnerItem);
       this.setCondition();
+
     },
 
     // 删除内部条件项
@@ -1366,7 +1850,57 @@ export default {
       } else {
         this.$message({ type: "warning", message: "至少保留一个条件项" });
       }
+    },
+    // 设置否则部分的焦点
+  setElseFocus(position) {
+    this.elseFocus = position;
+  },
+  
+  // 否则部分输入框失焦处理
+  elseInputBlur(position) {
+    const tagField = `elseTag${position.charAt(0).toUpperCase() + position.slice(1)}`;
+    const tagsField = `elseTags${position.charAt(0).toUpperCase() + position.slice(1)}`;
+    
+    if (this[tagField]) {
+      if (this[tagsField] && this[tagsField][0]) {
+        this.$emit('uncheck', this[tagsField][0].id);
+      }
+      this[tagsField] = [];
+    }
+    
+    this.setGetDataCondition();
+  },
+  //返回数据获取相关的数据
+  getDataConditionData() {
+    const inputData = {
+      // 获取数据列表的函数
+        getDataList: this.getDataList,
+      // 左侧else标签
+        elseTagLeft: this.elseTagLeft,
+          elseTagsLeft: this.elseTagsLeft,
+      // 左侧else标签集合
+      elseTagRight:this.elseTagRight,
+      elseTagsRight:this.elseTagsRight,
     }
+    console.log(inputData,'inputData');
+    
+    const result = transformData(inputData);
+    const mapEle=generateElementMap(inputData)
+    return {
+      // 获取数据列表的函数
+        getDataList: this.getDataList,
+        elseTagsLeft: this.elseTagsLeft,
+      // 左侧else标签
+        elseTagLeft: this.elseTagLeft,
+      // 左侧else标签集合
+      elseTagRight:this.elseTagRight,
+      elseTagsRight:this.elseTagsRight,
+      result,
+      mapEle
+    
+
+    };
+  }
 
   }
 }

+ 470 - 0
src/views/formula/component/funComponent/multiIfElseTools.js

@@ -379,3 +379,473 @@ export function generateResult(input) {
   
   return result;
 }
+
+
+
+export function transformData(data) {
+    // 辅助函数:获取值并根据类型决定是否用E[]包裹
+    const getValue = (directValue, arrayValues) => {
+        if (directValue) {
+            return directValue;
+        }
+        
+        // 检查数组中第一个元素
+        if (arrayValues && arrayValues.length > 0) {
+            const firstItem = arrayValues[0];
+            // 如果类型是Element,用E[]包裹tableElementKey
+            if (firstItem.type === "Element" && firstItem.tableElementKey) {
+                return `E[${firstItem.tableElementKey}]`;
+            }
+            return firstItem.tableElementKey || '';
+        }
+        
+        return '';
+    };
+    
+    // 处理getDataList中的每个对象
+    const conditions = data.getDataList.map(item => {
+        // 获取左侧值 a
+        const leftValue = getValue(item.tagWhenLeft, item.tagsWhenLeft);
+        // 获取条件符号
+        const condition = item.condition || '';
+        // 获取右侧值 x
+        const rightValue = getValue(item.tagWhenRight, item.tagsWhenRight);
+        // 获取结果左侧 b
+        const thenLeft = getValue(item.tagThenLeft, item.tagsThenLeft);
+        // 获取结果右侧
+        const thenRight = getValue(item.tagThenRight, item.tagsThenRight);
+        
+        return `${leftValue}${condition}${rightValue},${thenLeft}=${thenRight}`;
+    });
+    
+    // 处理else部分
+    const elseLeft = getValue(data.elseTagLeft, data.elseTagsLeft);
+    const elseRight = getValue(data.elseTagRight, data.elseTagsRight);
+    const elsePart = `else,${elseLeft}=${elseRight}`;
+    
+    // 组合所有部分
+    return [...conditions, elsePart].join(',');
+}
+
+ export function generateElementMap(originalData) {
+    const elementMap = {};
+    
+    // 提取Element的工具函数
+    const extractElements = (elements) => {
+        if (elements && Array.isArray(elements)) {
+            elements.forEach(element => {
+                if (element.type === "Element" && element.tableElementKey) {
+                    elementMap[element.tableElementKey] = {
+                        id: element.id,
+                        name: element.name,
+                        tableElementKey: element.tableElementKey,
+                        type: element.type
+                    };
+                }
+            });
+        }
+    };
+    
+    // 处理getDataList中的元素
+    if (originalData.getDataList && Array.isArray(originalData.getDataList)) {
+        originalData.getDataList.forEach(item => {
+            // 处理tagsWhenLeft(新增)
+            extractElements(item.tagsWhenLeft);
+            // 处理tagsWhenRight
+            extractElements(item.tagsWhenRight);
+            // 处理tagsThenLeft(新增)
+            extractElements(item.tagsThenLeft);
+            // 处理tagsThenRight(新增)
+            extractElements(item.tagsThenRight);
+        });
+    }
+    
+    // 处理elseTagsLeft中的元素
+    extractElements(originalData.elseTagsLeft);
+    
+    // 处理elseTagsRight中的元素
+    extractElements(originalData.elseTagsRight);
+    
+
+    
+    return elementMap;
+
+
+}
+export function restoreData(str,map) {
+    // 辅助函数:创建元素对象
+    const createElement = (tableElementKey, text) => ({
+        "type": "Element",
+        "name": text,  // 确保name是字符串
+        "id": Date.now().toString() + Math.random().toString(36).substr(2, 9),
+        "selected": false,
+        "tableElementKey": tableElementKey,
+        "children": [],
+        "text": text,  // 确保text是字符串
+        "style": "background-color: #409EFF"
+    });
+    
+    // 辅助函数:解析值并决定是直接值还是元素数组
+    const parseValue = (value) => {
+        // 检查是否是E[]包裹的元素
+        const elementMatch = value.match(/^E\[(.+)\]$/);
+        if (elementMatch) {
+            const tableElementKey = elementMatch[1];
+            // 映射tableElementKey到对应的text
+            const textMap = map
+            const text = textMap[tableElementKey].name || tableElementKey;
+            return {
+                directValue: "",
+                arrayValues: [createElement(tableElementKey, text)]
+            };
+        }
+        
+        // 普通值
+        return {
+            directValue: value,
+            arrayValues: []
+        };
+    };
+    
+    // 分割字符串为各个部分
+    const parts = str.split(',');
+    const elseIndex = parts.findIndex(part => part === 'else');
+    
+    if (elseIndex === -1) {
+        // 如果没有else部分,返回合理的默认结构
+        return {
+            "getDataList": [],
+            "elseTagLeft": "",
+            "elseTagsLeft": [],
+            "elseTagRight": "",
+            "elseTagsRight": []
+        };
+    }
+    
+    // 提取条件部分和else部分
+    const conditionParts = parts.slice(0, elseIndex);
+    const elseParts = parts.slice(elseIndex + 1);
+    
+    // 处理条件部分
+    const getDataList = [];
+    for (let i = 0; i < conditionParts.length; i += 2) {
+        const conditionStr = conditionParts[i];
+        const resultStr = conditionParts[i + 1];
+        
+        if (!resultStr) continue; // 跳过无效条目
+        
+        // 解析条件部分 (a[condition]x)
+        let leftValue, condition, rightValue;
+        
+        // 查找条件符号,按长度排序避免"=="被误解析为"="
+        const conditions = ['!=', '>=', '<=', '=', '>', '<'];
+        let foundCondition = null;
+        
+        for (const cond of conditions) {
+            if (conditionStr.includes(cond) && conditionStr.indexOf(cond) > 0) {
+                foundCondition = cond;
+                break;
+            }
+        }
+        
+        if (foundCondition) {
+            const conditionIndex = conditionStr.indexOf(foundCondition);
+            leftValue = conditionStr.substring(0, conditionIndex);
+            condition = foundCondition;
+            rightValue = conditionStr.substring(conditionIndex + foundCondition.length);
+        } else {
+            // 如果没有找到条件符号,默认使用=
+            leftValue = conditionStr;
+            condition = '=';
+            rightValue = '';
+        }
+        
+        // 解析结果部分 (b=y)
+        const [thenLeftStr, thenRightStr = ''] = resultStr.split('=');
+        
+        // 解析各个值
+        const leftParsed = parseValue(leftValue);
+        const rightParsed = parseValue(rightValue);
+        const thenLeftParsed = parseValue(thenLeftStr);
+        const thenRightParsed = parseValue(thenRightStr);
+        
+        // 添加到getDataList
+        getDataList.push({
+            "tagWhenLeft": leftParsed.directValue,
+            "tagsWhenLeft": leftParsed.arrayValues,
+            "condition": condition,
+            "tagWhenRight": rightParsed.directValue,
+            "tagsWhenRight": rightParsed.arrayValues,
+            "tagThenLeft": thenLeftParsed.directValue,
+            "tagsThenLeft": thenLeftParsed.arrayValues,
+            "tagThenRight": thenRightParsed.directValue,
+            "tagsThenRight": thenRightParsed.arrayValues
+        });
+    }
+    
+    // 处理else部分 - 修复主要问题点
+    const elseKeyValue = elseParts[0] || '';
+    const [elseLeftStr, elseRightStr = ''] = elseKeyValue.split('=');
+    
+    const elseLeftParsed = parseValue(elseLeftStr);
+    const elseRightParsed = parseValue(elseRightStr);
+    
+    // 构建并返回原始对象结构
+    return {
+        "getDataList": getDataList,
+        "elseTagLeft": elseLeftParsed.directValue,
+        "elseTagsLeft": elseLeftParsed.arrayValues,
+        "elseTagRight": elseRightParsed.directValue,
+        "elseTagsRight": elseRightParsed.arrayValues
+    };
+}
+export function convertToFCDataChange(data) {
+    // 1. 元素:取arguments[0].tableElementKey(非空则用E[]包裹)
+    let element = '';
+    // 传统判断:先确保data、arguments、arguments[0]存在,再取tableElementKey
+    if (data && data.arguments && data.arguments[0] && data.arguments[0].tableElementKey) {
+        element = `E[${data.arguments[0].tableElementKey}]`;
+    }
+
+    // 2. 变化方式:取changeMethod(默认空字符串)
+    const changeMethod = data && data.changeMethod ? data.changeMethod : '';
+
+    // 3. 数据格式:取dataFormat(默认空字符串)
+    const dataFormat = data && data.dataFormat ? data.dataFormat : '';
+
+    // 4. 协同元素:优先coordinationTags数组 → 再fallback到coordinationTag单个标签
+    let coordinationElement = '';
+    // 先判断coordinationTags数组是否有有效数据
+    if (data && data.coordinationTags && data.coordinationTags.length > 0 && data.coordinationTags[0].tableElementKey) {
+        coordinationElement = `E[${data.coordinationTags[0].tableElementKey}]`;
+    } 
+    // 数组无值时,取coordinationTag单个标签(需非空)
+    else if (data && data.coordinationTag && data.coordinationTag.trim() !== '') {
+        coordinationElement = data.coordinationTag;
+    }
+
+    // 5. 频率:优先frequencyTags数组 → 再fallback到frequencyTag单个标签
+    let frequency = '';
+    // 先判断frequencyTags数组是否有有效数据
+    if (data && data.frequencyTags && data.frequencyTags.length > 0 && data.frequencyTags[0].tableElementKey) {
+        frequency = `E[${data.frequencyTags[0].tableElementKey}]`;
+    } 
+    // 数组无值时,取frequencyTag单个标签(需非空)
+    else if (data && data.frequencyTag && data.frequencyTag.trim() !== '') {
+        frequency = data.frequencyTag;
+    }
+
+    // 6. 单位:取frequencyUnit(默认空字符串)
+    const unit = data && data.frequencyUnit ? data.frequencyUnit : '';
+
+    // 7. stopConditionParamTags → 再stopConditionParamTags数组 → 最后fallback到stopConditionParamTag单个标签
+    let stopElement = '';
+    // stopConditionParamTags
+    if (data && data.stopConditionParamTags && data.stopConditionParamTags.length > 0 && data.stopConditionParamTags[0].tableElementKey) {
+        stopElement = `E[${data.stopConditionParamTags[0].tableElementKey}]`;
+    } 
+ 
+    // 两个数组都无值时,取stopConditionParamTag单个标签(需非空)
+    else if (data && data.stopConditionParamTag && data.stopConditionParamTag.trim() !== '') {
+        stopElement = data.stopConditionParamTag;
+    }
+
+    // 8. 判断条件:取stopConditionOperator(默认空字符串)
+    const conditionOperator = data && data.stopConditionOperator ? data.stopConditionOperator : '';
+
+    // 9. 值:优先stopConditionValueTags数组 → 再fallback到stopConditionValueTag单个标签
+    let value = '';
+    // 先判断stopConditionValueTags数组是否有有效数据
+    if (data && data.stopConditionValueTags && data.stopConditionValueTags.length > 0 && data.stopConditionValueTags[0].tableElementKey) {
+        value = `E[${data.stopConditionValueTags[0].tableElementKey}]`;
+    } 
+    // 数组无值时,取stopConditionValueTag单个标签(需非空)
+    else if (data && data.stopConditionValueTag && data.stopConditionValueTag.trim() !== '') {
+        value = data.stopConditionValueTag
+    }
+
+    // 组合成最终的FC.dataChange格式
+    return `FC.dataChange(${element},${changeMethod},${dataFormat},${coordinationElement},${frequency},${unit},${stopElement},${conditionOperator},${value})`;
+}
+
+// 从data中提取所有type为Element的对象,生成以tableElementKey为键的映射
+export function extractElementMap(data) {
+    const elementMap = {};
+    
+    // 需要检查的包含Element对象的数组字段
+    const elementArrays = [
+        'arguments',
+        'coordinationTags',
+        'frequencyTags',
+        'stopConditionParamTags',
+        'stopConditionValueTags'
+    ];
+    
+    // 遍历所有可能包含Element对象的数组
+    elementArrays.forEach(field => {
+        if (data && data[field] && Array.isArray(data[field])) {
+            data[field].forEach(item => {
+                // 检查是否是有效的Element对象
+                if (item && item.type === 'Element' && item.tableElementKey) {
+                    // 以tableElementKey为键,存储对象的关键信息
+                    elementMap[item.tableElementKey] = {
+                        id: item.id,
+                        name: item.name,
+                        tableElementKey: item.tableElementKey,
+                        type: item.type
+                        // 可根据需要添加其他属性
+                    };
+                }
+            });
+        }
+    });
+
+    // 如果你提供的示例中"deviationRangeJson"是固定需要添加的字段,可以在这里补充
+    // elementMap.deviationRangeJson = "{\"symbol\":\"【min,max】\",\"model\":\"1\",\"arguments1\":\"\",\"arguments2\":\"\"}";
+    
+    return elementMap;
+}
+
+
+
+export function restoreOriginalData(fcString,mapObj) {
+    // 提取括号内的参数部分
+    const paramsStr = fcString.replace(/^FC\.dataChange\((.*)\)$/, '$1');
+    
+    // 分割参数(处理可能包含逗号的情况)
+    const params = [];
+    let currentParam = '';
+    let bracketCount = 0;
+    
+    for (let char of paramsStr) {
+        if (char === ',' && bracketCount === 0) {
+            params.push(currentParam.trim());
+            currentParam = '';
+        } else {
+            if (char === '[') bracketCount++;
+            if (char === ']') bracketCount--;
+            currentParam += char;
+        }
+    }
+    params.push(currentParam.trim());
+    
+    // 创建结果对象
+    const result = {
+        changeMethod: '',
+        dataFormat: '',
+        frequencyUnit: '',
+        stopConditionOperator: '',
+        name: '数据自变', // 保持原始名称
+        template: {
+            ft: "FC.dataChange(#1,#2,#3,#4,#5,#6,#7,#8,#9)",
+            args: [
+                { "key": "#1", "m": "元素" },
+                { "key": "#2", "m": "变化方式" },
+                { "key": "#3", "m": "数据格式" },
+                { "key": "#4", "m": "协同元素" },
+                { "key": "#5", "m": "频率" },
+                { "key": "#6", "m": "单位(1-年,2-月,3-日,4-时,5-分,6-秒)" },
+                { "key": "#7", "m": "停止元素" },
+                { "key": "#8", "m": "判断条件" },
+                { "key": "#9", "m": "值" }
+            ]
+        },
+        type: 0,
+        example: "对时间元素进行自增或自减",
+        number: null,
+        arguments: [null, null, null, null, null, null, null, null, null],
+        coordinationTag: "",
+        coordinationTags: [],
+        frequencyTags: [],
+        frequencyTag: "",
+        stopConditionParamTags: [],
+        stopConditionParamTag: "",
+        stopConditionValueTags: [],
+        stopConditionValueTag: ""
+    };
+    
+    // 解析参数并填充结果对象
+    // 参数0: 元素
+    if (params[0].startsWith('E[')) {
+        const key = params[0].replace(/^E\[([^\]]+)\]$/, '$1');
+        result.arguments[0] = mapObj[key] ? { ...mapObj[key], selected: false } : null;
+    }
+    
+    // 参数1: 变化方式
+    result.changeMethod = params[1] || '';
+    
+    // 参数2: 数据格式
+    result.dataFormat = params[2] || '';
+    
+    // 参数3: 协同元素
+    if (params[3].startsWith('E[')) {
+        const key = params[3].replace(/^E\[([^\]]+)\]$/, '$1');
+        if (mapObj[key]) {
+            result.coordinationTags = [{ 
+                ...mapObj[key], 
+                selected: false,
+                children: [],
+                text: mapObj[key].name,
+                style: "background-color: #409EFF"
+            }];
+        }
+    } else if (params[3]) {
+        result.coordinationTag = params[3];
+    }
+    
+    // 参数4: 频率
+    if (params[4].startsWith('E[')) {
+        const key = params[4].replace(/^E\[([^\]]+)\]$/, '$1');
+        if (mapObj[key]) {
+            result.frequencyTags = [{ 
+                ...mapObj[key], 
+                selected: false,
+                children: [],
+                text: mapObj[key].name,
+                style: "background-color: #409EFF"
+            }];
+        }
+    } else if (params[4]) {
+        result.frequencyTag = params[4];
+    }
+    
+    // 参数5: 单位
+    result.frequencyUnit = params[5] || '';
+    
+    // 参数6: 停止元素
+    if (params[6].startsWith('E[')) {
+        const key = params[6].replace(/^E\[([^\]]+)\]$/, '$1');
+        if (mapObj[key]) {
+            result.stopConditionParamTags = [{ 
+                ...mapObj[key], 
+                selected: false,
+                children: [],
+                text: mapObj[key].name,
+                style: "background-color: #409EFF"
+            }];
+        }
+    } else if (params[6]) {
+        result.stopConditionParamTag = params[6];
+    }
+    
+    // 参数7: 判断条件
+    result.stopConditionOperator = params[7] || '';
+    
+    // 参数8: 值
+    if (params[8].startsWith('E[')) {
+        const key = params[8].replace(/^E\[([^\]]+)\]$/, '$1');
+        if (mapObj[key]) {
+            result.stopConditionValueTags = [{ 
+                ...mapObj[key], 
+                selected: false,
+                children: [],
+                text: mapObj[key].name,
+                style: "background-color: #409EFF"
+            }];
+        }
+    } else if (params[8]) {
+        result.stopConditionValueTag = params[8];
+    }
+    
+    return result;
+}

+ 165 - 7
src/views/formula/edit.vue

@@ -213,7 +213,13 @@
                  @uncheck="unCheckEleComp" 
                  class="flex1" 
                  :moreConditions="moreConditions" 
-                :isMore="isMore">
+                :isMore="isMore"
+                :isGetData="isGetData"
+                :dataListGet="dataListGet"
+                :isDataChange="isDataChange"
+                :dataForm="dataForm"
+                :remark="remark"
+                >
               </component>
                 <div class="flex1" v-show="item.showSelectEle" style="margin-top:10px;margin-bottom:30px">
                   <el-scrollbar style="height: 400px">
@@ -457,6 +463,7 @@ import {formulaStringToArray} from "./formulaStringToArray"
 
 import draggable from 'vuedraggable'
 import {formatArrayMore,restoreArrayMore,generateResult} from './component/funComponent/multiIfElseTools'
+import dataChange from "./component/funComponent/dataChange"
 
 export default {
   components: {
@@ -475,7 +482,8 @@ export default {
     datasJoin,
     ifelse,
  
-    deviationRange
+    deviationRange,
+    dataChange
   },
   props: {
     wbsid:{
@@ -571,6 +579,7 @@ export default {
         '下标取数':'datas-getlist',
         // '数组转字符串':"datas-join",
         '判断':'ifelse',
+        '数据自变':'data-change'
       
       },
 
@@ -601,6 +610,12 @@ export default {
       isMore:false,//是否是多条件判断
       moreConditions:[],//多条件判断数组
       formulaDetailMap:{},//公式详情map
+      dataListGet:'',
+      isGetData:false,//是否获取数据
+      isDataChange:false,//是否数据自变
+      dataForm:'',
+      remark:'',//备注
+
 
     };
   },
@@ -1390,6 +1405,9 @@ export default {
         console.log('this.$refs.conditionList[0]',this.$refs.dynamiccomponent[0].conditionList);
         let resMore=formatArrayMore(this.$refs.dynamiccomponent[0].conditionList);
         const resJson=generateResult(this.$refs.dynamiccomponent[0].conditionList);
+        let remark=this.$refs.dynamiccomponent[0].remark;
+console.log(remark,'remark');
+
         console.log(resJson,'resJson');
         
      
@@ -1398,7 +1416,7 @@ export default {
                 id:this.formulaid,
                 formula:'',
                 formulas:resMore,
-                remark:'',
+                remark:remark,
                 nodeId:this.nodeid,
                 elementId:this.eleid,
                 scale:this.isRetain?this.retainNum:'',
@@ -1419,7 +1437,7 @@ export default {
               saveFormula({
                 formula:'',
                 formulas:resMore,
-                remark:'',
+                remark:remark,
                 nodeId:this.nodeid,
                 elementId:this.eleid,
                 scale:this.isRetain?this.retainNum:'',
@@ -1445,6 +1463,120 @@ export default {
             return;
         
         
+      }else if(this.$refs.dynamiccomponent&&this.$refs.dynamiccomponent.length>0&&this.$refs.dynamiccomponent[0]&& this.$refs.dynamiccomponent[0].symbol=='getData'){
+       //数据获取保存公式
+        const dataRes = this.$refs.dynamiccomponent[0].getDataConditionData();
+       console.log(dataRes,'dataRes');
+       const result =dataRes.result;
+       const mapEle = JSON.stringify(dataRes.mapEle);
+       console.log(mapEle,'mapEle');
+       
+
+            if(this.formulaid){
+              updateFormula({
+                id:this.formulaid,
+               formula:"FC.switchCase"+'('+result+')',
+             
+                remark:'',
+                nodeId:this.nodeid,
+                elementId:this.eleid,
+                scale:this.isRetain?this.retainNum:'',
+                number:number,
+                map:mapEle,
+                scope:this.globaltype,
+                // projectId:this.curProjiect.id||this.projectId,
+                projectId:this.curProjiect.id||this.pid,
+                dev:deviationRangeText
+              }).then(()=>{
+                this.formulaStringToArray();
+                this.$message({
+                  type: "success",
+                  message: "修改成功"
+                });
+              })
+            }else{
+              saveFormula({
+                  formula:"FC.switchCase" +'('+result+')',
+          
+                remark:'',
+                nodeId:this.nodeid,
+                elementId:this.eleid,
+                scale:this.isRetain?this.retainNum:'',
+                number:number,
+                map:mapEle,
+                scope:this.globaltype,
+                dev:deviationRangeText,
+                // projectId:this.curProjiect.id||this.projectId,
+                projectId:this.curProjiect.id||this.pid,
+                ver:this.version
+              }).then((res)=>{
+                if(res.data.data){
+                  this.formulaid = res.data.data;
+                }
+                this.formulaStringToArray();
+                this.$message({
+                  type: "success",
+                  message: "保存成功"
+                });
+              })
+            }
+            
+            return;
+       
+      }else if(this.$refs.dynamiccomponent&&this.$refs.dynamiccomponent.length>0&&this.$refs.dynamiccomponent[0]&& this.$refs.dynamiccomponent[0].formData){
+        console.log(this.$refs.dynamiccomponent[0],'this.$refs.dynamiccomponent[0');
+        const resForm=this.$refs.dynamiccomponent[0].getDataChangeList();
+        console.log(resForm,'resForm');
+        
+        //数据自变保存
+
+             if(this.formulaid){
+              updateFormula({
+                id:this.formulaid,
+                formula:resForm.test,
+                remark:'',
+                nodeId:this.nodeid,
+                elementId:this.eleid,
+                scale:this.isRetain?this.retainNum:'',
+                number:number,
+                map:JSON.stringify(resForm.map),
+                scope:this.globaltype,
+                // projectId:this.curProjiect.id||this.projectId,
+                projectId:this.curProjiect.id||this.pid,
+                dev:deviationRangeText
+              }).then(()=>{
+                this.formulaStringToArray();
+                this.$message({
+                  type: "success",
+                  message: "修改成功"
+                });
+              })
+            }else{
+              saveFormula({
+                formula:resForm.test,
+                remark:'',
+                nodeId:this.nodeid,
+                elementId:this.eleid,
+                scale:this.isRetain?this.retainNum:'',
+                number:number,
+                map:JSON.stringify(resForm.map),
+                scope:this.globaltype,
+                dev:deviationRangeText,
+                // projectId:this.curProjiect.id||this.projectId,
+                projectId:this.curProjiect.id||this.pid,
+                ver:this.version
+              }).then((res)=>{
+                if(res.data.data){
+                  this.formulaid = res.data.data;
+                }
+                this.formulaStringToArray();
+                this.$message({
+                  type: "success",
+                  message: "保存成功"
+                });
+              })
+            }
+            return
       }
       
       
@@ -1455,7 +1587,7 @@ export default {
       obj = formulaArrayToString(this.processFormula,this.resultFormula);
       obj2 = formulaArrayToString(filteredArr,this.resultFormula);
         // 判断 obj2.text 是否包含 FC.ifelse
-      if (obj2.text && obj2.text.includes('FC.ifelse')) {
+      if (obj2.text && (obj2.text.includes('FC.ifelse'))|| obj2.text.includes("''")) {
         obj = obj2; // 如果包含 FC.ifelse,使用 obj2
       }
       let deviationRangeText = rangeToString(this.deviationRange.datas,obj.eleMap);
@@ -1549,16 +1681,39 @@ export default {
     async formulaStringToArray(){
       let detail = (await formulaDetail({elementId:this.eleid,scope:this.globaltype,nodeId:this.nodeid,projectId:this.curProjiect.id||this.pid})).data.data;
       console.log(detail,'detail');
+      this.remark = detail.remark;
+
       if(detail&&detail.formula.includes('FC.ifelseMulti')){
         detail.formula = detail.formula.replace('FC.ifelseMulti','FC.ifelse');
         this.isMore = true;
             this.formulaDetailMap = detail.map;
         this.moreConditions =  restoreArrayMore(detail.formulas,this.formulaDetailMap)
     
-      }else{
+      }else if(detail&&detail.formula.includes('FC.switchCase')){
+        //数据获取
+        this.formulaDetailMap = detail.map;
+         detail.formula = detail.formula.replace('FC.switchCase','FC.ifelse');
+         this.isGetData = true;
+         this.dataListGet=detail.formula
+         console.log( this.dataListGet,' this.dataListGet');
+         
+      }else if(detail&&detail.formula.includes('FC.dataChange')){
+        //数据自变
+        this.formulaDetailMap = detail.map;
+      
+         this.isDataChange = true;
+         this.dataForm=detail.formula
+         console.log( this.dataForm,' this.dataListGet');
+         
+      }
+      else{
         this.isMore = false;
         this.moreConditions = []
         this.formulaDetailMap ={}
+        this.isGetData = false;
+        this.dataListGet=''
+        this.isDataChange = false;
+        this.dataForm='';
 
       }
 
@@ -1577,9 +1732,12 @@ export default {
         
         this.formulaid = detail.id;
         //let formula = formulaStringToArray('FC.sum(FC.repeat(E[测试测试_222]))+FC.ifelse(3<E[测试测试_333]&&E[测试测试_333]<10,E[测试测试_222]+E[测试测试_333],E[测试测试_333])',detail.map,this.formulaMap);
-        console.log(this.formulaMap,'this.formulaMap');
+      
+        
         
         let formula = formulaStringToArray(detail.formula,detail.map,this.formulaMap);
+        console.log(formula,'formula');
+        
         this.processFormula = formula.processFormula;
         formula.resultFormula[0].id = this.resultFormula[0].id;
         formula.resultFormula[0].name = this.resultFormula[0].name;

+ 1 - 1
src/views/manager/projectinfo/editElement/editElement.vue

@@ -841,7 +841,7 @@ export default {
           if (!target.classList.contains('select-td')) {
     
 
-            if(ctrlKey){
+            if(ctrlKey||metaKey){
                       target.classList.add("select-td");
               this.checkList.push({
                 tr: target.getAttribute('trindex') || target1.getAttribute('trindex'),

+ 39 - 31
src/views/manager/projectinfo/list.vue

@@ -67,12 +67,13 @@
                               {{ item.label }}
                             </el-tag>
                         </template>
-                          <el-tag type="info"effect="dark" v-else class="custom-ellipse-tag">未关联WBS</el-tag>
+                          <el-tag effect="dark" v-else class="custom-ellipse-tag "    :class="`custom-tag-type-8`">未关联WBS</el-tag>
                       </div>
                       <div class="project-like" @click.stop="toggleLike(item)" v-loading="item.load">
                     
-                        <i class="ri-heart-fill"  v-if="item.isCollect"    style="color: red;" ></i>
-                        <i class="ri-heart-line" v-else></i>
+                        <!-- <i   v-if="item.isCollect"    style="color: red;"   src="@/assets/heart-line.svg" ></i> -->
+                         <img height="16" width="16" src="@/assets/heart-fill.svg"   v-if="item.isCollect" />
+                        <img height="16" width="16" src="@/assets/heart-line.svg"  v-else />
                       </div>
                     </div>
                   <div class="project-title" >
@@ -231,32 +232,31 @@
                   </template>
                 
               </el-table-column>
-               <el-table-column
-                prop=""
-                label="合同段权限">
-                <template slot-scope="scope">
-                  <template v-if="scope.row.relationContractInfo > 0">
+                 <el-table-column
+                    prop=""
+                    label="合同段权限"
+                  >
+                    <template slot-scope="scope">
+                     
+                      <template v-if="scope.row.relationContractInfo && scope.row.relationContractInfo.length > 0">
+                 
                         <el-tooltip 
-                          v-for="(item,index) in scope.row.relationContractInfo" 
-                          :key="key"
-                          :content="item.contractName"
+                          v-for="(item, index) in scope.row.relationContractInfo.filter(Boolean)" 
+                          :key="index"  
+                          :content="item.contractName || ''" 
                           placement="top"
-                        
                         >
                           <el-tag  
                             size="small" 
                             :type="item.contractType===1?'warning':item.contractType===2?'primary':'success'"   
                             class="custom-ellipse-tag2 ellipsis-tag1"
-                            
                           >
-                            {{item.contractName}}
+                            {{ item.contractName || '' }}  
                           </el-tag>
                         </el-tooltip>
-                  </template>
-                
-                </template>
-
-              </el-table-column>
+                      </template>
+                    </template>
+                  </el-table-column>
                <el-table-column
                width="180px"
                 prop="address"
@@ -469,7 +469,7 @@ export default {
         break;
     }
   },
-    getProjectList () {
+    getProjectList (callback) {
       getProjectListPage({
         current:1,
         size:999,
@@ -477,6 +477,7 @@ export default {
 
       }).then((res) => {
         this.projectList = res.data.data.records;
+          callback();
       })
     },
     getProjectPageList () {
@@ -709,6 +710,9 @@ export default {
         this.searchForm.isCollect = 1
 
       }
+      this.searchForm.current = 1
+      this.page.currentPage = 1;
+
       this.getProjectPageList();
       this.getProjectList();
 
@@ -733,19 +737,19 @@ export default {
      this. saveSort(ids);
 
     },
-    sortPro(){
-       this.sortContractList = JSON.parse(JSON.stringify(this.projectList));
-      if(this.searchForm.isCollect===1){
-        this.sortTitle = '收藏项目排序';
-      }else{
-        
-          this.sortTitle = '项目排序';
-      }
+   async sortPro(){
 
-       
+       this.getProjectList(() => {
+        if(this.searchForm.isCollect === 1){
+          this.sortTitle = '收藏项目排序';
+        }else{
+          this.sortTitle = '项目排序';
+        }
 
-      this.$nextTick(() => {
-        this.$refs.contractSortRef.show(this.sortContractList);
+        this.$nextTick(() => {
+          this.sortContractList = JSON.parse(JSON.stringify(this.projectList));
+          this.$refs.contractSortRef.show(this.sortContractList);
+        });
       });
     },
     saveSort(ids){
@@ -948,6 +952,7 @@ export default {
   background-color: #2550A2 !important;
   border-color: #2550A2 !important;
 }
+
 .el-tag.el-tag--info {
   background-color: #eee !important;
 }
@@ -1079,6 +1084,9 @@ export default {
 .custom-tag-type-6 {
   background-color: blue !important; /* 征拆 - 粉色 */
 }
+.custom-tag-type-8 {
+  background-color: lightgray !important; /*未关联 - 粉色 */
+}
 /* 图标样式 */
 .custom-ellipse-tag .el-icon-success {
   margin-right: 4px;

+ 2 - 1
src/views/manager/projectinfo/tableSortByType.vue

@@ -25,9 +25,10 @@
           <!-- 使用draggable包裹每个分类下的项目列表 -->
           <draggable 
             v-model="category.list" 
-            :options="{ group: `category-${categoryIndex}`, animation: 150 }"
+            :options="{ group: `category-${categoryIndex}`, animation: 150, handle: '.drag-handle' }"
             @end="handleDragEnd(categoryIndex)"
             class="draggable-container"
+              
           >
             <div 
               v-for="(item, itemIndex) in category.list" 

+ 609 - 98
src/views/manager/projectinfo/tree.vue

@@ -31,6 +31,13 @@
               @click="proSyncbtn"
               >项目数据同步
             </el-button>
+            <el-button
+              size="medium"
+              style="margin-right: 10px"
+             icon="el-icon-s-data"
+             @click="SyncbtnProGress"
+              >同步进度
+            </el-button>
             <el-button
               size="medium"
               icon="el-icon-s-grid"
@@ -100,7 +107,7 @@
               >
               <el-tooltip content="查询工序节点无内业资料类型" placement="top">
                 <el-button type="success" @click="filterSearchClick"  v-if="isNodeType&&!filterByType" size="mini">筛选</el-button>
-                <el-button type="success" @click="filterSearchClickCancel" v-else size="mini">取消筛选</el-button>
+                <el-button type="success" @click="filterSearchClickCancel" v-if="isNodeType&&filterByType" size="mini">取消筛选</el-button>
               </el-tooltip>
               <el-button
                 v-if="isNodeType"
@@ -380,7 +387,7 @@
                   style="margin-left: 10px"
                   icon="el-icon-edit-outline"
                   class="text-icon"
-                  @click="editEditElementForm()"
+                  @click="editEditElementForm"
                 ></el-button>
                 <el-button
              v-if="isNodeType"
@@ -576,43 +583,25 @@
                                   :formatter="formatOwner"
                                   label="所属方"
                                 ></el-table-column>
-                                <el-table-column label="操作" align="center" width="250">
-                                  <template slot-scope="scope">
-                                    <el-link type="primary" @click="Associationlist(scope)"
-                                      >关联清表
-                                    </el-link>
-                                    <el-link
-                                      class="mg-l-10"
-                                    
-                                      type="primary"
-                                      :disabled="
-                                        scope.row.excelId == -1 || scope.row.excelId == null
-                                      "
-                                      @click="
-                                        rightClick(
-                                          scope.row.pkeyId,
-                                          scope.row.excelId,
-                                          scope.row.id,
-                                          scope.row.initTableName,
-                                          scope.row.initTableId
-                                        )
-                                      "
-                                      >编辑元素
-                                    </el-link>
-                                
-                                    <el-link
-                                      class="mg-l-10"
-                                      type="primary"
-                                      :disabled="
-                                        scope.row.excelId == -1 || scope.row.excelId == null
-                                      "
-                                      @click="adjustExcel(scope.row.pkeyId, scope.row.excelId)"
-                                      >调整表单
-                                    </el-link>
-                                   
+                                 <el-table-column label="操作" align="center"  >
+                                                <template slot-scope="scope"> 
 
-                                  </template>
-                                </el-table-column>
+                                                    <el-button
+                                                      size="mini"
+                                                    
+
+                                                      @click="handlePreview(scope.$index, scope.row)"
+                                                      >预览
+                                                    </el-button>
+                                                    <el-button
+                                                      size="mini"
+                                                      @click="handleEdit(scope.$index, scope.row)"
+                                                      >编辑
+                                                    </el-button>
+                                                   
+                                                </template>
+                                            
+                                            </el-table-column>
                               </el-table>
                          </template>
                        
@@ -780,6 +769,85 @@
                      
                     </div>
           </template>
+                <template v-if="leftType == 4&&!isNodeType" >
+                             <el-table
+                                :data="formData"
+                                border
+                                height="100%"
+                              
+                              >
+                                <el-table-column
+                                  align="center"
+                                  prop="tableName"
+                                  label="表单名称"
+                                ></el-table-column>
+                                <el-table-column
+                                  align="center"
+                                  prop="tableType"
+                                  :formatter="formatTableType"
+                                  label="表单类型"
+                                ></el-table-column>
+                                <el-table-column
+                                  align="center"
+                                  prop="fillRate"
+                                  label="填报率"
+                                ></el-table-column>
+                                <el-table-column
+                                  label="是否关联清表"
+                                  prop="isLinkTable"
+                                  align="center"
+                                >
+                                  <template slot-scope="scope">
+                                    <span v-if="scope.row.isLinkTable == 2">是</span>
+                                    <span v-else>否</span>
+                                  </template>
+                                </el-table-column>
+                                <el-table-column
+                                  align="center"
+                                  prop="tableOwner"
+                                  :formatter="formatOwner"
+                                  label="所属方"
+                                ></el-table-column>
+                          
+                                                <el-table-column label="操作" align="center" width="250">
+                                  <template slot-scope="scope">
+                                    <el-link type="primary" @click="Associationlist(scope)"
+                                      >关联清表
+                                    </el-link>
+                                    <el-link
+                                      class="mg-l-10"
+                                    
+                                      type="primary"
+                                      :disabled="
+                                        scope.row.excelId == -1 || scope.row.excelId == null
+                                      "
+                                      @click="
+                                        rightClick(
+                                          scope.row.pkeyId,
+                                          scope.row.excelId,
+                                          scope.row.id,
+                                          scope.row.initTableName,
+                                          scope.row.initTableId
+                                        )
+                                      "
+                                      >编辑元素
+                                    </el-link>
+                                
+                                    <el-link
+                                      class="mg-l-10"
+                                      type="primary"
+                                      :disabled="
+                                        scope.row.excelId == -1 || scope.row.excelId == null
+                                      "
+                                      @click="adjustExcel(scope.row.pkeyId, scope.row.excelId)"
+                                      >调整表单
+                                    </el-link>
+                                   
+
+                                  </template>
+                                </el-table-column>
+                              </el-table>
+                         </template>
         </el-col>
       </el-row>
     </basic-container>
@@ -913,7 +981,7 @@
       width="60%"
       :modal-append-to-body="false"
     >
-      <div>
+      <div v-if="!isNodeType">
         <el-table :data="formDatass" border style="width: 100%">
           <el-table-column prop="tableName" label="表名">
             <template slot-scope="scope">
@@ -971,6 +1039,74 @@
           </el-table-column>
         </el-table>
       </div>
+      <div v-else>
+         <div class="category-section"  v-for="(item, index) in formDatass" :key="index">
+                                        <div class="category-header">{{item.title}}</div>
+                                        <el-table
+                                            :data="item.list"
+                                            border
+                                            style="width: 100%"
+                                        >
+                                                 <el-table-column prop="tableName" label="表名">
+                                                  <template slot-scope="scope">
+                                                    <el-input
+                                                      v-model="scope.row.tableName"
+                                                      placeholder="请输入表名称"
+                                                    ></el-input>
+                                                  </template>
+                                                </el-table-column>
+                                                <el-table-column prop="tableType" label="表类型">
+                                                  <template slot-scope="scope">
+                                                    <el-select v-model="scope.row.tableType" placeholder="请选择">
+                                                      <el-option
+                                                        v-for="(item, key) in tableTypelist"
+                                                        :key="key"
+                                                        :label="item.dictValue"
+                                                        :value="item.dictKey"
+                                                      >
+                                                      </el-option>
+                                                    </el-select>
+                                                  </template>
+                                                </el-table-column>
+                                                <el-table-column prop="tableOwner" label="所属方">
+                                                  <template slot-scope="scope">
+                                                    <el-select v-model="scope.row.tableOwner" placeholder="请选择">
+                                                      <el-option
+                                                        v-for="(item, key) in ownerTypeList"
+                                                        :key="key"
+                                                        :label="item.dictValue"
+                                                        :value="item.dictKey"
+                                                      >
+                                                      </el-option>
+                                                    </el-select>
+                                                  </template>
+                                                </el-table-column>
+                                                <el-table-column prop="fillRate" label="填报率">
+                                                  <template slot-scope="scope">
+                                                    <el-input
+                                                      v-model="scope.row.fillRate"
+                                                      placeholder="请输入填报率"
+                                                    ></el-input>
+                                                  </template>
+                                                </el-table-column>
+                                                <el-table-column prop="nodeType" label="节点类型" v-if="wbsType == 5">
+                                                  <template slot-scope="scope">
+                                                    <el-select v-model="scope.row.nodeType" placeholder="请选择" >
+                                                      <el-option
+                                                        v-for="item in nodeTypelist"
+                                                        :key="item.id"
+                                                        :label="item.dictValue"
+                                                        :value="item.dictKey"
+                                                      ></el-option>
+                                                    </el-select>
+                                                  </template>
+                                                </el-table-column>
+                                           
+                                           
+                                     
+                                        </el-table>
+                                    </div>
+      </div>
       <span slot="footer" class="dialog-footer">
         <el-button @click="editElementFormTag = false">取 消</el-button>
         <el-button
@@ -2363,35 +2499,106 @@
       </span>
     </el-dialog>
     <!-- 同步队列 -->
-    <el-dialog
-      title="同步队列"
-      :visible.sync="syncListDialog"
-      width="30%"
-      append-to-body
-      :close-on-click-modal="false"
-      @close="closeSyncListTag"
-    >
-    <div class="content-box" v-loading="refreshLoading||refreshLoading1">
-      <el-image
-      style="width: 100px; height: 100px"
-     :src="require('@/assets/loader.svg')"
-      fit="contain"></el-image>
-      <div class="mt-14">同步中</div>
-      <div  class="mt-14">
-        同步数量{{ syncListData.nodeNum }} 剩余数量{{ syncListData.nodeNumEnd }}
-      </div>
-     <div  class="mt-14">
-      <el-button type="primary" @click="syncListDialog=false">好的,我已知晓</el-button>
-      </div>
-      <div  class="mt-14">
-      <el-link type="primary" @click="refreshData" v-loading="refreshLoading" v-if="!isAdd">刷新数据</el-link>
-      <el-link type="primary" @click="refreshData1" v-loading="refreshLoading1" v-else>刷新数据</el-link>
-     </div>
-    </div>
+      <el-dialog
+          title="同步队列"
+          :visible.sync="syncListDialog"
+          width="40%"
+          append-to-body
+          :close-on-click-modal="false"
+          @close="closeSyncListTag"
+          custom-class="sync-queue-dialog"
+        >
+          <!-- 有同步任务时显示 -->
+          <div v-if="syncListDataNew && syncListDataNew.length > 0" class="sync-list">
+            <div 
+              v-for="(item, index) in syncListDataNew" 
+              :key="index" 
+              class="sync-item"
+            >
+              <!-- 同步方信息和状态 -->
+              <div class="sync-header">
+                <span class="sync-party">{{ item.nodeName }}</span>
+                <span class="sync-status">{{ item.rangeName }}</span>
+              </div>
+                <div class="progress-status">
+                 
+                  <div class="progress-reason-text">
+                    <span>{{ item.errorMsg }}</span>
+                  </div>
+                   <div class="progress-status-text">
+                    <el-tag 
+                    size="small"
+                      :type="{'2': 'success', '3': 'danger', '1': 'warning'}[item.status]"
+                    >
+                      {{ {'2': '完成', '3': '失败', '1': '同步中', '0': '等待'}[item.status] }}
+                    </el-tag>
+                  </div>
+                </div>
+              <!-- 进度条 -->
+              <div class="progress-container">
       
-   
+                <div class="progress-text">
+                  <div class="progress-text-btn">
+                    <div style="font-weight: bold;">同步进度</div>
+                    
+                  </div>
+                
+                   <div>
+                   
+                     <el-button type="text" icon="el-icon-refresh" style="color:orange" @click="refreshSyncList(item.id)" :loading="refreshListLoad"></el-button>
+                    <el-button type="text" icon="el-icon-delete" style="color:red" @click="delSyncItem(item.id)" :loading="delSyncLoad"></el-button>
+                   </div>
+                </div>
      
-    </el-dialog>
+                  <div style="padding-right: 10px;padding-left: 10px;">
+                    <el-progress 
+                    text-inside
+                      class="custom-progress" 
+                    :percentage="item.progress"
+                    text-color="#2550A2"
+                    color="#2550A2"
+                    stroke-width="20"
+                    stroke-linecap="round"
+                  ></el-progress>
+                  </div>
+       
+              </div>
+              
+              <!-- 统计数据 -->
+              <div class="sync-stats" style="display: flex;">
+                <div class="stat-item">
+                  <div class="stat-label">已同步</div>
+                  <div class="stat-value synced">{{ item.nodeNumEnd }}</div>
+                </div>
+                <div class="stat-item">
+                  <div class="stat-label">剩余</div>
+                  <div class="stat-value remaining">{{ item.surplusCount }}</div>
+                </div>
+              </div>
+            </div>
+          </div>
+          
+          <!-- 无同步任务时显示 -->
+          <div v-else class="no-data">
+            <div class="no-data-icon">
+              <i class="el-icon-information"></i>
+            </div>
+            <div class="no-data-text">暂无同步任务...</div>
+          </div>
+          
+          <!-- 底部按钮 -->
+          <div slot="footer" class="dialog-footer">
+            <el-button 
+              type="primary" 
+            @click="closeSyncListTag"
+              class="confirm-btn"
+              round
+
+            >
+              好的,我已知晓
+            </el-button>
+          </div>
+        </el-dialog>
      <!-- 同步表单其他配置 -->
      <el-dialog
       title="同步表单其他配置"
@@ -2585,6 +2792,9 @@ import {
   updateBatchElements,
   getTemplate,
   importWbsElement,
+  querySyncRecord,
+  reFlush,
+  deleteRecord
 } from "@/api/manager/wbsformelement";
 import {
  
@@ -3022,6 +3232,7 @@ export default {
         nodeNum:'',//同步节点数量
         nodeNumEnd:''//已同步数量
       },
+      syncListDataNew:[],
       isAdd:'',
       refreshLoading1:false,
       codeDialog:false,
@@ -3040,8 +3251,10 @@ export default {
 
       },
       tableListByType:[],
-      tableListByTypeLoad:false
-      
+      tableListByTypeLoad:false,
+      refreshListLoad:false,
+      delSyncLoad:false,
+       syncListTimer: null // 用于存储定时器ID
     };
   },
   computed: {
@@ -3414,7 +3627,11 @@ export default {
     updateNodeTable() {
 
       if(this.isNodeType){
+           if(!this.curTreeData.id){
+          return
+        }
         this.tableListByTypeLoad=true
+     
           getGroupNodeTables( 
            
             {
@@ -4933,6 +5150,21 @@ async saveLinkTab() {
 
     //#region 元素表单信息
     editEditElementForm() {
+
+      if(this.isNodeType){
+          this.formDatass = JSON.parse(JSON.stringify(this.tableListByType));
+          this.formDatass.forEach((ele)=>{
+          
+            const listArr=ele.list
+            listArr.forEach((ele)=>{
+                ele.tableType=Number(ele.tableType)
+                ele.tableOwner=Number(ele.tableOwner)
+            })
+          })
+            this.editElementFormTag = true;
+          return
+      }
+      
       //标记元素
       if (this.formData.length) {
         let da = [];
@@ -4960,29 +5192,55 @@ async saveLinkTab() {
       //保存按钮
       let da = [];
       let tag = true;
-      this.formDatass.forEach((val) => {
-        if (!val.tableName || !val.tableType || !val.tableOwner) {
-          tag = false;
-          return;
+      console.log( this.formDatass,' this.formDatass');
+      if(!this.isNodeType){
+          this.formDatass.forEach((val) => {
+            if (!val.tableName || !val.tableType || !val.tableOwner) {
+              tag = false;
+              return;
+            } else {
+              da.push({
+                pkeyId: val.pkeyId,
+                nodeName: val.tableName,
+                tableType: val.tableType,
+                tableOwner: val.tableOwner,
+                fillRate: val.fillRate,
+                nodeType: val.nodeType,
+              });
+            }
+          });
+    
+      }else{
+        this.formDatass.forEach((ele)=>{
+          
+            const listArr=ele.list
+            listArr.forEach((ele)=>{
+                if (!ele.tableName || !ele.tableType || !ele.tableOwner) {
+                    tag = false;
+                    return;
+                  } else {
+                da.push({
+                  pkeyId: ele.pkeyId,
+                  nodeName: ele.tableName,
+                  tableType: ele.tableType,
+                  tableOwner: ele.tableOwner,
+                  fillRate: ele.fillRate,
+           
+                });
+              }
+            })
+          })
+
+      }
+      if (tag) {
+          this.updateBatchNodeTableInfo2(da);
         } else {
-          da.push({
-            pkeyId: val.pkeyId,
-            nodeName: val.tableName,
-            tableType: val.tableType,
-            tableOwner: val.tableOwner,
-            fillRate: val.fillRate,
-            nodeType: val.nodeType,
+          this.$message({
+            type: "success",
+            message: "请填写完整元素表单的所有信息!",
           });
         }
-      });
-      if (tag) {
-        this.updateBatchNodeTableInfo2(da);
-      } else {
-        this.$message({
-          type: "success",
-          message: "请填写完整元素表单的所有信息!",
-        });
-      }
+    
     },
     async updateBatchNodeTableInfo2(da) {
       this.editeditElementFormMFLoading = true;
@@ -5622,6 +5880,7 @@ async saveLinkTab() {
       getTitleRange(
        { projectId:this.projectid,
         nameId:row.nameId,
+        wbsType:this.wbsType
   
 
        }
@@ -6009,6 +6268,11 @@ async saveLinkTab() {
 
 
         this.syncListDialog=true
+          // 启动定时器,每5秒请求一次
+        this.syncListTimer = setInterval(() => {
+          this.getSyncListDataNew();
+        }, 5000);
+        this.getSyncListDataNew ()
         this.closeProSyncTag()
        
          
@@ -6025,6 +6289,11 @@ async saveLinkTab() {
     },
     closeSyncListTag(){
       this.syncListDialog=false;
+       // 清除定时器,停止请求
+      if (this.syncListTimer) {
+        clearInterval(this.syncListTimer);
+        this.syncListTimer = null;
+      }
        this.updateNodeTable()
     },
     handleCheckAllChange(val) {
@@ -6252,24 +6521,31 @@ async saveLinkTab() {
         if(res.data.code==200){
                     if(res.data.data){
                     
-                      this.syncListData.nodeNum=res.data.data.nodeNum
-                      this.syncListData.nodeNumEnd=res.data.data.nodeNumEnd
+                  
                       this.syncListDialog=true
+                              this.syncListTimer = setInterval(() => {
+                            this.getSyncListDataNew();
+                          }, 5000);
+                              this.getSyncListDataNew ()
                     }else{
                      
-                      this.syncListData={
-                        nodeNum:'',//同步节点数量
-                        nodeNumEnd:''//已同步数量
-                      }
+               
                       this.syncListDialog=false
+                       // 清除定时器,停止请求
+                        if (this.syncListTimer) {
+                          clearInterval(this.syncListTimer);
+                          this.syncListTimer = null;
+                        }
                     }
                     
                     }else{
                       this.syncListDialog=false
-                      this.syncListData={
-                        nodeNum:'',//同步节点数量
-                        nodeNumEnd:''//已同步数量
+                       // 清除定时器,停止请求
+                      if (this.syncListTimer) {
+                        clearInterval(this.syncListTimer);
+                        this.syncListTimer = null;
                       }
+                    
                       this.$message.error(res.data.msg)
                     }
         });
@@ -6306,7 +6582,73 @@ async saveLinkTab() {
         this.handleEditFormula(0, row)
       }
       
-    }
+    },
+    //同步进度按钮
+   SyncbtnProGress(){
+    this.syncListDialog=true
+    this.getSyncListDataNew()
+     this.syncListTimer = setInterval(() => {
+      this.getSyncListDataNew();
+    }, 5000);
+
+   },
+   getSyncListDataNew(){
+        querySyncRecord({ projectId:this.projectid}).then((res) => {
+
+        if(res.data.code==200){
+                    if(res.data.data){
+                    
+                    this.syncListDataNew=res.data.data
+                    }else{
+                     
+                      this.syncListDataNew=[]
+                    }
+                    
+                    }else{
+                      this.syncListDataNew=[]
+                    }
+        });
+   },
+   //刷新 
+   refreshSyncList(id){
+    this.refreshListLoad=true
+         reFlush({ id}).then((res) => {
+
+        if(res.data.code==200){
+          this.$message.success(res.data.msg)
+                   this.getSyncListDataNew()
+                    }else{
+                     this.$message.error(res.data.msg)
+                    }
+        }).finally(()=>{
+          this.refreshListLoad=false
+        })
+
+   },
+   delSyncItem(id){
+        this.delSyncLoad=true
+
+       this.$confirm("是否删除该条数据" , {
+        distinguishCancelAndClose: true,
+        confirmButtonText: "删除",
+        cancelButtonText: "取消",
+      }).then(() => {
+       
+       
+
+         deleteRecord({ id}).then((res) => {
+
+        if(res.data.code==200){
+          this.$message.success(res.data.msg)
+                   this.getSyncListDataNew()
+                    }else{
+                     this.$message.error(res.data.msg)
+                    }
+        }).finally(()=>{
+          this.delSyncLoad=false
+        })
+      });
+   }
   },
   watch: {
     "GLExcelFrom.search"(val) {
@@ -6623,3 +6965,172 @@ async saveLinkTab() {
 
 }
 </style>
+
+<style lang="scss" scoped>
+.progress-status{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  padding: 5px;
+}
+.progress-text-btn{
+  display: flex;
+ 
+  align-items: center;
+  justify-content: space-between;
+}
+.rogress-text{
+  font-size: 14px;
+  color: #333;
+}
+.noData{
+  text-align: center;
+
+
+}
+.circle-btn{
+  border-radius: 200px;
+}
+</style>
+
+<style scoped>
+.sync-queue-dialog {
+  .el-dialog__header {
+    padding: 18px 20px;
+    border-bottom: 1px solid #eee;
+  }
+  
+  .el-dialog__title {
+    font-size: 16px;
+    font-weight: 500;
+  }
+  
+  .el-dialog__body {
+    padding: 20px;
+    max-height: 400px;
+    overflow-y: auto;
+  }
+}
+
+.sync-list {
+  max-height:600px;
+  overflow-y: auto;
+  .sync-item {
+    padding: 15px 0;
+    border-bottom: 1px dashed #eee;
+    
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+  
+  .sync-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+    font-size: 14px;
+    background-color: #F2F2F2;
+
+    height: 30px;
+    overflow-y: auto;
+    padding: 10px 10px;
+    border-radius: 10px;
+    
+    .sync-party {
+      color: #333;
+      font-weight: 500;
+    }
+    
+    .sync-status {
+      color: #666;
+      font-size: 13px;
+    }
+  }
+  
+  .progress-container {
+    position: relative;
+    margin-bottom: 12px;
+    
+    .progress-text {
+      display: flex;
+      justify-content: space-between;
+      padding: 5px;
+      .progress-num{
+        color:#2550A2;
+        font-weight: bold;
+      }
+
+    }
+  }
+  
+  .sync-stats {
+    display: flex;
+    justify-content: space-between;
+    padding: 0 5px;
+    
+    .stat-item {
+      display: flex;
+      flex-direction: column;
+      /* text-align: left; */
+      /* align-items: center;
+      justify-content: flex-end; */
+    
+      width: 45%;
+      padding: 10px 15px;
+
+      border-radius: 4px;
+      background-color: #f5f7fa;
+      
+      .stat-label {
+        font-size: 12px;
+        color: #999;
+     
+        
+      }
+      
+      .stat-value {
+        font-size: 20px;
+        font-weight: 800;
+      }
+      
+      .synced {
+        color: #42b983; /* Element UI success color */
+        
+
+      }
+      
+      .remaining {
+        color: #e6a23c; /* Element UI warning color */
+      }
+    }
+  }
+}
+
+.no-data {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+
+}
+
+.dialog-footer {
+  text-align: center;
+  padding: 15px 20px;
+  border-top: 1px solid #eee;
+  
+  .confirm-btn {
+    width: 160px;
+  }
+}
+
+</style>
+
+<style scoped>
+/* 使用自定义类名定位,避免影响全局样式 */
+::v-deep .custom-progress .el-progress__text {
+  color: #2550A2; /* 进度文字颜色 */
+}
+</style>

+ 405 - 0
src/views/manager/wbsinfo/TreeCopyModal.vue

@@ -0,0 +1,405 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="visible"
+    :width="dialogWidth"
+    :before-close="handleClose"
+    append-to-body
+
+    class="tree-copy-modal"
+  >
+    <!-- 提示信息 -->
+    <div class="alert-message">
+       <span>温馨提示:允许单对多、多对单、多对多的关联关系。关联后可在对应节点查看。【关联并继续】可以继续续进行关联。</span>
+     
+    </div>
+
+    <!-- 树形结构容器 -->
+    <div class="tree-container">
+      <!-- 左侧复制源树 -->
+      <div class="tree-wrapper left-tree">
+        <div class="tree-title">复制源</div>
+        <el-tree
+         v-if="isShowTree"
+          ref="sourceTree"
+          :data="sourceTreeData"
+          :props="treeProps"
+          :load="loadSourceNode"
+          node-key="id"
+          show-checkbox
+          lazy
+          :expand-on-click-node="false"
+          @check="handleSourceCheck"
+          check-strictly
+
+        ></el-tree>
+      </div>
+
+      <!-- 中间箭头 -->
+      <div class="tree-arrow">
+        <i class="el-icon-arrow-right"></i>
+      </div>
+
+      <!-- 右侧复制到树 -->
+      <div class="tree-wrapper right-tree">
+        <div class="tree-title">复制到</div>
+        <el-tree
+         v-if="isShowRight"
+        check-strictly
+          ref="targetTree"
+          :data="targetTreeData"
+          :props="treeProps"
+          :load="loadTargetNode"
+          node-key="id"
+          show-checkbox
+          lazy
+          :expand-on-click-node="false"
+          @check="handleTargetCheck"
+        ></el-tree>
+      </div>
+    </div>
+
+    <!-- 底部按钮 -->
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="handleClose">取消</el-button>
+      <el-button type="primary" @click="handleLinkAndExit" :loading="linkLoading">关联并退出</el-button>
+      <el-button type="success" @click="handleLinkAndContinue" :loading="linkLoading">关联并继续</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import {
+  getLazytree,
+
+} from "@/api/manager/wbstree";
+
+export default {
+  name: 'TreeCopyModal',
+  props: {
+    // 弹窗标题
+    title: {
+      type: String,
+      default: '复制节点'
+    },
+    // 弹窗宽度
+    dialogWidth: {
+      type: String,
+      default: '80%'
+    },
+    // 源树和目标树的ID参数
+    sourceId: {
+      type: [String, Number],
+      required: true
+    },
+    targetId: {
+      type: [String, Number],
+      required: true
+    },
+    // 用户信息,包含tenant_id
+    userInfo: {
+      type: Object,
+      required: true
+    },
+    linkLoading: {
+      type: Boolean,
+      default: false
+    },
+ 
+  },
+  data() {
+    return {
+      // 弹窗显示状态
+      visible: false,
+      // 源树数据
+      sourceTreeData: [],
+      // 目标树数据
+      targetTreeData: [],
+      // 树的配置项
+      treeProps: {
+              children: "children",
+        label: "title",
+        isLeaf: function (data) {
+          if (data.hasChildren && data.isExistForm != 1) {
+            return false;
+          } else if (data.hasChildren && data.isExistForm == 1) {
+            return true;
+          } else {
+            return true;
+          }
+        },
+      },
+      
+      // 选中的源节点
+      selectedSourceNodes: [],
+      // 选中的目标节点
+      selectedTargetNodes: [],
+      isShowTree:true,
+      isShowRight:true
+    };
+  },
+  watch:{
+    linkLoading(val){
+      this.linkLoading = val;
+    }
+  },
+  methods: {
+    // 显示弹窗
+    show() {
+    this.isShowTree = true;
+    this.isShowRight = true;
+      this.visible = true;
+      // 重置选中状态
+      this.selectedSourceNodes = [];
+      this.selectedTargetNodes = [];
+      // 重置树
+      if (this.$refs.sourceTree) {
+        this.$refs.sourceTree.setCheckedKeys([]);
+      }
+      if (this.$refs.targetTree) {
+        this.$refs.targetTree.setCheckedKeys([]);
+      }
+    },
+
+    // 关闭弹窗
+    handleClose() {
+      this.visible = false;
+      this.$emit('close');
+      this.isShowTree=false
+      this.isShowRight=false
+    },
+
+    // 加载源树节点
+    loadSourceNode(node, resolve) {
+      let pid = 0;
+      if (node.level !== 0) {
+        pid = node.data.id;
+      }
+      getLazytree(this.sourceId, pid, this.userInfo.tenant_id)
+      
+        .then(res => {
+          let arr = [];
+          if (Array.isArray(res.data.data)) {
+            arr = res.data.data.map(item => ({
+              ...item,
+              // 如果后端没有返回isLeaf,这里可以根据实际业务逻辑判断
+              isLeaf: item.isLeaf !== undefined ? item.isLeaf : false
+            }));
+          }
+          resolve(arr);
+        })
+        .catch(err => {
+          console.error('加载源树节点失败:', err);
+          resolve([]);
+        });
+    },
+
+    // 加载目标树节点
+    loadTargetNode(node, resolve) {
+      let pid = 0;
+      if (node.level !== 0) {
+        pid = node.data.id;
+      }
+      getLazytree(this.sourceId, pid, this.userInfo.tenant_id)
+        .then(res => {
+          let arr = [];
+          if (Array.isArray(res.data.data)) {
+            arr = res.data.data.map(item => ({
+              ...item,
+              isLeaf: item.isLeaf !== undefined ? item.isLeaf : false
+            }));
+          }
+          resolve(arr);
+        })
+        .catch(err => {
+          console.error('加载目标树节点失败:', err);
+          resolve([]);
+        });
+    },
+
+    // 处理源树节点选择
+ // 处理源树节点选择
+    handleSourceCheck(data, checked, indeterminate) {
+      // 确保树组件已渲染
+      if (this.$refs.sourceTree) {
+        // 只获取叶子节点,根据你的业务逻辑调整
+        this.selectedSourceNodes = this.$refs.sourceTree.getCheckedNodes(false, true);
+        console.log('选中的源节点:', this.selectedSourceNodes);
+      }
+    },
+
+    // 处理目标树节点选择
+    handleTargetCheck(data, checked, indeterminate) {
+     // 确保树组件已渲染
+      if (this.$refs.targetTree) {
+        // 只获取叶子节点,根据你的业务逻辑调整
+        this.selectedTargetNodes = this.$refs.targetTree.getCheckedNodes(false, true);
+        console.log('选中的目标节点:', this.selectedTargetNodes);
+      }
+    },
+
+    // 显示关联规则提示
+    showLinkTips() {
+      this.$alert('关联规则说明:\n1. 允许单对单、多对单、多对多的关联关系\n2. 关联后可在对应节点查看关联信息\n3. 复制操作不会影响原节点数据', '关联规则', {
+        confirmButtonText: '确定'
+      });
+    },
+
+    // 关联并退出
+    handleLinkAndExit() {
+      this.handleLink(true);
+      
+    },
+
+    // 关联并继续
+    handleLinkAndContinue() {
+      this.handleLink(false);
+      
+    },
+
+    // 处理关联操作
+    handleLink(exitAfterLink) {
+      if (this.selectedSourceNodes.length === 0 || this.selectedTargetNodes.length === 0) {
+        this.$message.warning('请至少选择一个源节点和一个目标节点');
+        return;
+      }
+
+      // 准备关联数据
+      const linkData = {
+        sourceNodes: this.selectedSourceNodes.map(node => node.id),
+        targetNodes: this.selectedTargetNodes.map(node => node.id)
+      };
+
+      // 触发父组件的关联事件
+      this.$emit('link', linkData, () => {
+        this.$message.success('关联成功');
+        if (exitAfterLink) {
+          this.handleClose();
+        } else {
+          // 清空选择但不关闭弹窗
+          this.clearSelections(); // 清空选择但不关闭弹窗
+         
+
+        }
+      });
+       this.isShowTree=false
+          
+          setTimeout(() => {
+            this.isShowTree=true
+         
+          }, 1000);
+            this.isShowRight=false
+          
+          setTimeout(() => {
+            this.isShowRight=true
+         
+          }, 1000);
+    },
+    // 新增清空选择的方法
+    clearSelections() {
+      // 确保组件已加载
+      if (this.$refs.sourceTree) {
+        // 对于懒加载树,先获取所有已勾选的节点ID
+        const sourceCheckedKeys = this.$refs.sourceTree.getCheckedKeys();
+        // 逐个取消勾选
+        sourceCheckedKeys.forEach(key => {
+          this.$refs.sourceTree.setChecked(key, false, true);
+        });
+      }
+      
+      if (this.$refs.targetTree) {
+        const targetCheckedKeys = this.$refs.targetTree.getCheckedKeys();
+        targetCheckedKeys.forEach(key => {
+          this.$refs.targetTree.setChecked(key, false, true);
+        });
+      }
+      
+      // 手动清空数据
+      this.selectedSourceNodes = [];
+      this.selectedTargetNodes = [];
+    }
+  }
+};
+</script>
+
+<style scoped>
+.tree-copy-modal {
+  .alert-message {
+    padding: 10px 15px;
+    background-color: #f5f7fa;
+    border-left: 4px solid orange;
+    margin-bottom: 20px;
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    color: orange;
+
+    .el-icon-info-circle {
+      color: #409eff;
+      margin-right: 8px;
+    }
+
+    .el-button {
+      margin-left: auto;
+      color: #409eff;
+      padding: 0;
+    }
+  }
+
+  .tree-container {
+    display: flex;
+    justify-content: space-between;
+    height: 500px;
+    overflow: hidden;
+  }
+
+  .tree-wrapper {
+    flex: 1;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    .tree-title {
+      padding: 10px 15px;
+      background-color: #f5f7fa;
+      border-bottom: 1px solid #e4e7ed;
+      font-weight: 500;
+    }
+
+    .el-tree {
+      flex: 1;
+      overflow: auto;
+      padding: 10px;
+    }
+  }
+
+  .left-tree {
+    margin-right: 15px;
+  }
+
+  .right-tree {
+    margin-left: 15px;
+  }
+
+  .tree-arrow {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 40px;
+    color: #c0c4cc;
+
+    .el-icon-arrow-right {
+      font-size: 20px;
+    }
+  }
+
+  .dialog-footer {
+    display: flex;
+    justify-content: center;
+    padding-top: 15px;
+
+  }
+}
+</style>

+ 57 - 1
src/views/manager/wbsinfo/edit.vue

@@ -1489,6 +1489,16 @@
       >
       </FormulaEditone>
     </el-dialog>
+     <!-- 引入树形复制弹窗组件 -->
+    <tree-copy-modal
+      ref="treeCopyModal"
+      :source-id="id"
+       :default-expanded-keys="defaultExpandedKeys" 
+      :user-info="userInfo"
+      @close="handleModalClose"
+      @link="handleLinkNodes"
+      :linkLoading="linkLoading"
+    ></tree-copy-modal>
   </basic-container>
 </template>
 
@@ -1496,6 +1506,7 @@
 import ManualSorting from "@/components/WbsTree/ManualSorting";
 import FormulaEdit from "@/views/formula/edit.vue";
 import FormulaEditone from "@/views/formula/edit1.vue";
+import TreeCopyModal from './TreeCopyModal.vue';
 import {
   getLazytree,
   getDetail,
@@ -1532,11 +1543,15 @@ import {
   updateBatchElements,
   getTemplate,
   importWbsElement,
+  copyNode
 } from "@/api/manager/wbsformelement";
 import { getDictionary, getChildList } from "@/api/system/dict";
 import { mapGetters } from "vuex";
 import { getStore, setStore } from "@/util/store";
 export default {
+    components: {
+    TreeCopyModal
+  },
   data() {
     var checkMajorDataType = (rule, value, callback) => {
       //console.log(this.nodeDetail.nodeType)
@@ -1706,6 +1721,10 @@ export default {
       dynamicDictList: [],
       standardTypeOptions: [], //标准分类
       unitOptions: [], //单位名称
+      copyDialogVisible: false, //复制节点弹框
+      linkLoading: false, //复制节点加载
+
+
     };
   },
   computed: {
@@ -3373,17 +3392,54 @@ export default {
         this.menuKey = "del";
         this.delNodeHandle();
       } else if (item.key === "copy") {
-        this.$message.info("暂无相关接口");
+      
+        this.$refs.treeCopyModal.show();
       } else if (item.key === "sort") {
         this.menuKey = "sort";
         this.paixuMD(data);
       }
     },
+     handleModalClose() {
+      console.log('树形复制弹窗已关闭');
+      // 可以在这里处理弹窗关闭后的逻辑
+    },
+
+    // 处理节点关联
+   async handleLinkNodes(linkData) {
+      console.log('准备关联的节点数据:', linkData);
+
+      this.linkLoading = true;
+      const { data: res } = await copyNode({ 
+        leftIds: linkData.sourceNodes,
+        rightIds: linkData.targetNodes,
+      
+      }).finally (()=>{
+        this.linkLoading = false;
+      })
+      console.log(res);
+      this.linkLoading = false;
+      if (res.code == 200) {
+        this.$message({
+          type: 'success',
+          message: res.msg
+        })
+      }else {
+        this.$message({
+          type: 'error',
+          message: res.msg
+        })
+      }
+      
+    
+    }
+      //表单排序
+
   },
   components: {
     ManualSorting,
     FormulaEdit,
     FormulaEditone,
+     TreeCopyModal
   },
 };
 </script>