Bläddra i källkod

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

lvy 3 veckor sedan
förälder
incheckning
551c668bc5

+ 1 - 1
public/version.json

@@ -1,3 +1,3 @@
 {
-  "value": "20250905163729"
+  "value": "20251022164630"
 }

+ 16 - 0
src/api/exctab/excelmodel.js

@@ -289,4 +289,20 @@ export const exctabSort = (form) => {
         method: 'post',
         params: form
     })
+}
+//元素识别关联公共WBS模板-查询节点下已关联的所有表单并按照所属方分组
+export const getNodeGroupTables = (form) => {
+    return request({
+        url: '/api//blade-manager/wbsFormElement/get-group-node-tables',
+        method: 'get',
+        params: form
+    })
+}
+//元素识别关联公共WBS模板-查询节点下是否存在当前元素表
+export const checkTableExist = (form) => {
+    return request({
+        url: '/api/blade-manager/wbsFormElement/checkTableExist',
+        method: 'get',
+        params: form
+    })
 }

+ 9 - 0
src/api/manager/wbstree.js

@@ -386,4 +386,13 @@ export const getTreeByType = (params) => {
         method: 'get',
         params
     })
+}
+
+//获取推荐表单
+export const getRecommendTable = (params) => {
+    return request({
+        url: '/api/blade-manager/synchronousRecord/getRecommendTable',
+        method: 'get',
+        params
+    })
 }

+ 2 - 3
src/api/system/user.js

@@ -1,6 +1,6 @@
 import request from '@/router/axios';
 
-export const getList = (current, size, params) => {
+export const getList = (current, size, params, deptId) => {
     return request({
         url: '/api/blade-user/page',
         method: 'get',
@@ -8,11 +8,10 @@ export const getList = (current, size, params) => {
             ...params,
             current,
             size,
-          
+            deptId,
         }
     })
 }
-
 export const remove = (ids) => {
     return request({
         url: '/api/blade-user/remove',

+ 8 - 0
src/api/tentative/testcollect.js

@@ -189,4 +189,12 @@ export const getLinkData = (params) => {
     method: 'get',
     params
   })
+}
+
+export const removeLinkData = (params) => {
+  return request({
+    url: '/api/blade-manager/trial/summary/reflection/remove',
+    method: 'post',
+    params
+  })
 }

+ 1 - 1
src/config/index.json

@@ -1,6 +1,6 @@
 {
   "target22": "http://39.108.216.210:8090",
-  "target": "http://192.168.0.6:8090",
+  "target": "http://192.168.0.109:8090",
   "target11": "http://219.151.181.73:8090",
   "target3": "http://183.247.216.148:28090",
   "target2": "http://192.168.0.109:8090",

+ 81 - 8
src/views/exctab/ElementIdentification/index.vue

@@ -466,7 +466,7 @@
         append-to-body
         :close-on-click-modal="false"
       >
-        <el-row :gutter="20">
+        <el-row :gutter="20" >
           <el-col :span="12">
             <el-select
               style="width: 100%"
@@ -482,7 +482,7 @@
               >
               </el-option>
             </el-select>
-            <el-scrollbar style="height: 50vh">
+     
               <div class="flex" style="margin-top: 10px" v-if="isShowInput">
                 <el-input
                   size="small"
@@ -496,7 +496,9 @@
                   >搜索</el-button
                 >
               </div>
-              <el-tree
+
+            <div style="max-height: 500px; overflow-y: auto; width: auto">
+                <el-tree
                 v-if="GLExcelFromtag"
                 @node-click="handleNodeClickExcel"
                 ref="tree"
@@ -529,9 +531,9 @@
                 :default--checked-keys="selectNodeIds"
               >
               </el-tree>
-            </el-scrollbar>
+            </div>
           </el-col>
-          <el-col :span="12" v-if="addElementForm.wbsId">
+          <el-col :span="12" v-if="addElementForm.wbsId" >
             <el-tabs v-model="activeName" type="card">
               <el-tab-pane label="关联元素表" name="link">
                 <el-table
@@ -539,6 +541,7 @@
                   border
                   style="width: 100%"
                   class="martop20"
+                  height="500"
                 >
                   <el-table-column
                     prop="tableName"
@@ -604,13 +607,35 @@
                     ></el-option>
                   </el-select>
                 </div>
-                <div class="martop10 text-green">
+                <div class="martop10 text-green" v-if="!addinfoShow">
                   系统检测:当前节点不存在当前元素表,允许新增
                 </div>
-                <div class="martop10 text-red">
+                <div class="martop10 text-red" v-else>
                   系统检测:当前节点已存在当前元素表,不允许新增,请请联系项目负责人处理!!!
                 </div>
                 <div class="martop20">已有元素表</div>
+                <div style="max-height: 350px;overflow-y: auto;">
+                           <template v-if="tableListByType.length > 0">
+                                    <div class="category-section"  v-for="(item, index) in tableListByType" :key="index">
+                                        <div class="category-header">{{item.title}}</div>
+                                        <el-table
+                                            :data="item.list"
+                                            border
+                                            style="width: 100%"
+                                           
+                                        >
+                                            <el-table-column
+                                                align="center"
+                                                prop="tableName"
+                                                label="元素表名称"
+                                            ></el-table-column>
+                                  
+                                        </el-table>
+                                    </div>
+                         </template>
+                         <el-empty description="暂无数据" v-else ></el-empty>
+
+                </div>
               </el-tab-pane>
             </el-tabs>
           </el-col>
@@ -696,6 +721,8 @@ import {
   exctabcellSave,
   exctabcellRemove,
   exctabcellUpdate,
+  getNodeGroupTables,
+  checkTableExist
 } from "@/api/exctab/excelmodel";
 import { dictionarydataType } from "@/api/exctab/editelement";
 import { getColByTabId } from "@/api/manager/AdjustForm";
@@ -805,7 +832,10 @@ export default {
         tableType:'',
         tableOwner:""
       },
-      clickData:{}
+      clickData:{},
+      addTableTreeId:'',
+      addinfoShow:false,//新增元素是否显示
+      tableListByType:[],
 
     };
   },
@@ -1282,10 +1312,42 @@ export default {
     },
     handleNodeClickExcel(data) {
       //点击节点事件
+      this.addTableTreeId=data.id
       this.addElementForm.wbsId = this.GLExcelFrom.name;
       this.addElementForm.parentId = data.id;
       //this.selectByNodeTable(data.id)
       this.searchNodeTables(data.id);
+      this.getTipsInfo()
+      this.getTableListByType()
+    },
+  async  getTipsInfo(){
+            const { data: res } = await checkTableExist({
+              nodeId: this.addTableTreeId,
+              tableName: this.addElementForm.nodeName
+            });
+            console.log(res);
+            if (res.code == 200) {
+             console.log(res.data,'res.data');
+              this.addinfoShow=res.data
+             
+            }else{
+              this.addinfoShow=false
+            }
+    },
+    async getTableListByType() { 
+            const { data: res } = await getNodeGroupTables({
+              nodeId: this.addTableTreeId,
+              excelTabId: this.from.id
+            });
+            console.log(res);
+            if (res.code == 200) {
+    
+              this.tableListByType=res.data
+             
+            }else{
+              this.tableListByType=[]
+            }
+
     },
     async selectByNodeTable(id) {
       //获取清表信息
@@ -1806,6 +1868,17 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+  .category-section {
+            margin-bottom: 20px;
+    }
+        .category-header {
+            padding: 12px 20px;
+            background-color: #E8E8E8;
+       
+            margin: 15px 0;
+            font-weight: bold;
+            font-size: 16px;
+        }
 .text-green{
   color:rgba(82, 178, 0, 1);
 }

+ 20 - 2
src/views/formula/component/deviationRange/deviationRange.vue

@@ -21,12 +21,12 @@
       <span style="width:60px"></span>
       <template v-if="!(symbol == '<' || symbol== '≤')">
         <span class="textblod mg-r-10"></span>
-        <vue-tags-input v-model="tag1" :tags="tags1" @focus="curFocusIndex = 1" @blur="inputBlur(1)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"/>
+        <vue-tags-input v-model="tag1" :tags="tags1" @focus="curFocusIndex = 1" @blur="inputBlur(1)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"  @tag-clicked="handleTagClick($event,1)" ref="tagsInput1"/>
         <span style="width:20px"></span>
       </template>
       <template v-if="!(symbol == '>' || symbol== '≥')">
         <span class="textblod mg-r-10"></span>
-        <vue-tags-input v-model="tag2" :tags="tags2" @focus="curFocusIndex = 2" @blur="inputBlur(2)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"/>
+        <vue-tags-input v-model="tag2" :tags="tags2" @focus="curFocusIndex = 2" @blur="inputBlur(2)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"  @tag-clicked="handleTagClick($event,2)" ref="tagsInput2"/>
       </template>
     </div>
 
@@ -478,6 +478,24 @@ export default {
       })
       this.inputVisible = false;
     },
+    handleTagClick(info,index) {
+     
+       const {tag}=info;
+
+       
+      this.curFocusIndex = index
+     
+      
+      // 调用对应输入框的 focus() 方法
+        if (this.curFocusIndex === 1) {
+          this.$refs.tagsInput1.focus(); // 注意:确保组件已挂载,使用可选链避免报错
+        } else {
+          this.$refs.tagsInput2.focus();
+        }
+
+  
+       this.$emit('clickTag',tag)
+    },
 
   }
 }

+ 111 - 37
src/views/formula/component/formulaItem.vue

@@ -1,9 +1,28 @@
 <template>
-  <span :class="getItemClass(item)" @click="itemClick(item)">
-    {{item.name}}
+  <div class="inline-container">
+    <!-- 元素标签 -->
+    <el-tag 
+      v-if="item.type === 'Element'"
+      type="info"   
+      :effect="item.selected ? 'dark' : 'plain'"
+      @click="itemClick(item)"
+      class="ellipse-tag"
+      :class="{'selected-tag': item.selected}"
+    > 
+      {{ item.name }}
+    </el-tag>
+    <span 
+      :class="getItemClass(item)" 
+      @click="itemClick(item)" 
+      v-else
+      class="element-span"
+    >
+      {{ item.name }}
+    </span>
     
-  </span>
-  
+    <!-- 光标指示器 - 选中时或强制显示时显示 -->
+    <span v-if="isShowCursor&&(item.selected || showCursor)" class="cursor-blink"></span>
+  </div>
 </template>
 
 <script>
@@ -12,48 +31,103 @@ export default {
   props: {
     item: {
       type: Object,
-      default: function () {
-        return {};
-      }
+      default: () => ({})
     },
-   
-  },
-  data(){
-    return{
-
-    }
+    // 新增属性:用于强制显示光标
+    showCursor: {
+      type: Boolean,
+      default: false
+    },
+    isShowCursor: {
+      type: Boolean,
+      default: true
+    }//是否显示光标
   },
-  methods:{
-    itemClick(item){
+  methods: {
+    itemClick(item) {
+      // 切换选中状态
       item.selected = !item.selected;
-      this.$emit('click',{
-        selected:item.selected,
+      this.$emit('click', {
+        selected: item.selected,
         item
-      })
+      });
     },
-    getItemClass(item){
-      let obj = {};
-      // obj[item.type.toLocaleLowerCase()+'-class']=true;
-      obj['element-class']=true;
-      if(item.selected){
-        obj.selected = true;
-      }
-      return obj;
+    getItemClass(item) {
+      return {
+        'element-class': true,
+        'selected': item.selected
+      };
     }
   }
 }
 </script>
+
 <style scoped lang="scss">
-  .element-class{
-    cursor: pointer;
-    padding: 0 3px;
-    &.selected{
-      color: rgba(64,149,229,1);
-    }
+// 共用样式
+.element-class, .ellipse-tag {
+  display: inline-flex;
+  align-items: center; // 纵向居中对齐
+  justify-content: center;
+  vertical-align: middle; // 确保与其他元素对齐
+  box-sizing: border-box;
+}
+
+.element-class {
+  cursor: pointer;
+  padding: 0 3px;
+  height: 32px; // 与tag保持一致高度
+  
+  &.selected {
+    color: #f0720a;
   }
-  .text-blue{
-    font-size: 20px;
-    color: rgba(64,149,229,1);
-    font-weight:bold;
+}
+
+// 标签样式
+.ellipse-tag {
+  border-radius: 50px !important; // 椭圆样式
+  padding: 0 12px !important;
+  height: 32px; // 固定高度确保对齐
+  transition: all 0.2s ease; // 平滑过渡效果
+  background-color: #E7E7E7 !important; // 默认背景色
+  border-color: #E7E7E7 !important;    // 默认边框色
+  color: #333 !important;              // 默认文字色
+  
+  &.selected-tag {
+    background-color: #909399 !important; // 选中状态背景色
+    border-color: #909399 !important;    // 选中状态边框色
+    color: #fff !important;              // 选中状态文字色
+    transform: scale(1.05);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+    margin-left: 8px;
+    margin-right: 5px;
   }
-</style>
+}
+
+// 确保span和tag在同一行时对齐
+.element-span {
+  display: inline-flex;
+  align-items: center;
+  height: 32px;
+}
+
+// 新增样式
+.inline-container {
+  display: inline-flex;
+  align-items: center;
+}
+
+// 光标样式 - 模拟输入框闪烁效果
+.cursor-blink {
+  width: 2px;
+  height: 1.2em;
+  background-color: #333;
+  margin: 0 1px;
+  animation: blink 1s step-end infinite;
+}
+
+@keyframes blink {
+  from, to { background-color: transparent }
+  50% { background-color: #333 }
+}
+
+</style>

+ 83 - 6
src/views/formula/component/funComponent/ifelse.vue

@@ -31,11 +31,11 @@
         <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 == '<=&&<='">
-              <vue-tags-input v-model="tag1" :tags="tags1" @focus="curFocusIndex = 1" @blur="inputBlur(1)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"/>
+              <vue-tags-input v-model="tag1" :tags="tags1" @focus="curFocusIndex = 1" @blur="inputBlur(1)" placeholder="输入/参数111" @before-adding-tag="beforeAddingTag"/>
               <span v-if="symbol == '<&&<'" class="mg-l-20 mg-r-20">&lt;</span>
               <span v-if="symbol == '<=&&<='" class="mg-l-20 mg-r-20">≤</span>
             </div>
-            <vue-tags-input v-model="tag2" :tags="tags2" @focus="curFocusIndex = 2" @blur="inputBlur(2)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"/>
+            <vue-tags-input v-model="tag2" :tags="tags2" @focus="curFocusIndex = 2" @blur="inputBlur(2)" placeholder="输入/参数222" @before-adding-tag="beforeAddingTag"  @tag-clicked="handleTagClick($event,2)" ref="tagsInput2"/>
             <span class="mg-l-20 mg-r-20">
               <span v-if="symbol == '>='">≥</span>
               <span v-if="symbol == '<='">≤</span>
@@ -46,13 +46,13 @@
               <span v-if="symbol == '|'">|</span>
         
             </span>
-            <vue-tags-input v-model="tag3" :tags="tags3" @focus="curFocusIndex = 3" @blur="inputBlur(3)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"/>
+            <vue-tags-input v-model="tag3" :tags="tags3" @focus="curFocusIndex = 3" @blur="inputBlur(3)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag"  @tag-clicked="handleTagClick($event,3)"  ref="tagsInput3"/>
         </div>
          <div class="flex jc-al-c mg-t-20" v-if="result == 1">
           <span class="textblod mg-r-10">真值</span>
-          <vue-tags-input v-model="tag4" :tags="tags4" @focus="curFocusIndex = 4" @blur="inputBlur(4)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag" @keyup.native="btKeyUp"/>
+          <vue-tags-input v-model="tag4" :tags="tags4" @focus="curFocusIndex = 4" @blur="inputBlur(4)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag" @keyup.native="btKeyUp"  @tag-clicked="handleTagClick($event,4)"  ref="tagsInput4"/>
           <span class="textblod mg-r-10 mg-l-20">假值</span>
-          <vue-tags-input v-model="tag5" :tags="tags5" @focus="curFocusIndex = 5" @blur="inputBlur(5)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag" @keyup.native="btKeyUp"/>
+          <vue-tags-input v-model="tag5" :tags="tags5" @focus="curFocusIndex = 5" @blur="inputBlur(5)" placeholder="输入/参数" @before-adding-tag="beforeAddingTag" @keyup.native="btKeyUp"  @tag-clicked="handleTagClick($event,5)"  ref="tagsInput5"/>
         </div>
         <div class="mg-t-20" v-if="result == 2">
           <div class="flex">
@@ -138,12 +138,14 @@
                 <span class="textblod mg-r-20" >IF</span>
                 
                 <vue-tags-input 
+                 :ref="`multiTagsInput_${conditionIndex}_${innerIndex}_tag2`"
                   v-model="conditionList[conditionIndex].formulaIfElse[innerIndex].tag2" 
                   :tags="conditionList[conditionIndex].formulaIfElse[innerIndex].tags2" 
                   @focus="setMultiFocus(conditionIndex, innerIndex, 'tag2')" 
                   @blur="inputBlurMulti(conditionIndex, innerIndex, 'tag2')" 
                   placeholder="输入/参数" 
                   @before-adding-tag="beforeAddingTag"
+                  @tag-clicked="handleMultiTagClick($event, conditionIndex, innerIndex, 'tag2')"
                 />
                 <span class="mg-l-20 mg-r-20">
                 <el-select 
@@ -164,12 +166,14 @@
                 </el-select>
                 </span>
                 <vue-tags-input 
+                  :ref="`multiTagsInput_${conditionIndex}_${innerIndex}_tag3`"
                   v-model="conditionList[conditionIndex].formulaIfElse[innerIndex].tag3" 
                   :tags="conditionList[conditionIndex].formulaIfElse[innerIndex].tags3" 
                   @focus="setMultiFocus(conditionIndex, innerIndex, 'tag3')" 
                   @blur="inputBlurMulti(conditionIndex, innerIndex, 'tag3')" 
                   placeholder="输入/参数" 
                   @before-adding-tag="beforeAddingTag"
+                  @tag-clicked="handleMultiTagClick($event, conditionIndex, innerIndex, 'tag3')"
                 />
                 <i class="el-icon-circle-plus-outline mg-l-20" style="color: #409EFF;cursor: pointer;" @click="addConditionItem(conditionIndex, innerIndex)"></i>
                 <i class="el-icon-delete mg-l-20" style="color: red;cursor: pointer;" @click="removeConditionItem(conditionIndex, innerIndex)" v-if="innerIndex > 0 || conditionIndex > 0"></i>
@@ -178,6 +182,7 @@
          <div class="flex jc-al-c mg-t-20" v-if="result == 1">
           <span class="textblod mg-r-10">满足</span>
           <vue-tags-input 
+            :ref="`multiTagsInput_${conditionIndex}_${innerIndex}_tag4`"
             v-model="conditionList[conditionIndex].tag4" 
             :tags="conditionList[conditionIndex].tags4" 
             @focus="setMultiFocus(conditionIndex, null, 'tag4')" 
@@ -185,12 +190,14 @@
             placeholder="输入/参数" 
             @before-adding-tag="beforeAddingTag" 
             @keyup.native="btKeyUp"
+            @tag-clicked="handleMultiTagClick($event, conditionIndex, innerIndex, 'tag4')"
           />
           
           <!-- 只在最后一个条件显示"不满足"输入框 -->
           <template v-if="conditionIndex === conditionList.length - 1">
             <span class="textblod mg-r-10 mg-l-20">不满足</span>
             <vue-tags-input 
+              :ref="`multiTagsInput_${conditionIndex}_${innerIndex}_tag5`"
               v-model="conditionList[conditionIndex].tag5" 
               :tags="conditionList[conditionIndex].tags5" 
               @focus="setMultiFocus(conditionIndex, null, 'tag5')" 
@@ -198,6 +205,7 @@
               placeholder="输入/参数" 
               @before-adding-tag="beforeAddingTag" 
               @keyup.native="btKeyUp"
+              @tag-clicked="handleMultiTagClick($event, conditionIndex, innerIndex, 'tag5')"
             />
           </template>
           
@@ -291,6 +299,8 @@
                       placeholder="输入/参数" 
                       @before-adding-tag="beforeAddingTag" 
                       @keyup.native="btKeyUp"
+                      @tag-clicked="handleDataTagClick($event, index, 'tagWhenLeft')"
+                      :ref="`dataTag_${index}_tagWhenLeft`"
 
                     />
                   </div>
@@ -321,6 +331,8 @@
                       placeholder="输入/参数" 
                       @before-adding-tag="beforeAddingTag" 
                       @keyup.native="btKeyUp"
+                       @tag-clicked="handleDataTagClick($event, index, 'tagWhenRight')"
+                       :ref="`dataTag_${index}_tagWhenRight`"
                     />
                   </div>
                   
@@ -333,6 +345,8 @@
                       placeholder="输入/参数" 
                       @before-adding-tag="beforeAddingTag" 
                       @keyup.native="btKeyUp"
+                      @tag-clicked="handleDataTagClick($event, index, 'tagThenLeft')"
+                      :ref="`dataTag_${index}_tagThenLeft`"
                     />
                     <span  class="mg-r-10 mg-l-10">=</span>
                     <div class="mg-r-10">
@@ -344,6 +358,8 @@
                           placeholder="输入/参数" 
                           @before-adding-tag="beforeAddingTag" 
                           @keyup.native="btKeyUp"
+                          @tag-clicked="handleDataTagClick($event, index, 'tagThenRight')"
+                          :ref="`dataTag_${index}_tagThenRight`"
                         />
                     </div>
                       <i class="el-icon-circle-plus-outline mg-l-20" style="color: #409EFF;cursor: pointer;" @click="addDataItem(index)"></i>
@@ -363,6 +379,8 @@
                       @before-adding-tag="beforeAddingTag" 
                       @keyup.native="btKeyUp"
                       class="mg-r-10"
+                      @tag-clicked="handleElseTagClick($event, 'left')"
+                      ref="elseTagLeft"
                     />
                     <span class="mg-r-10">=</span>
                     <vue-tags-input 
@@ -373,6 +391,8 @@
                       placeholder="请选择/输入参数" 
                       @before-adding-tag="beforeAddingTag" 
                       @keyup.native="btKeyUp"
+                       @tag-clicked="handleElseTagClick($event, 'left')"
+                      ref="elseTagLeft"
                     />
                   </div>
                 </div>
@@ -1900,7 +1920,64 @@ export default {
     
 
     };
-  }
+  },
+  //选择元素回显处理
+    handleTagClick(info,index) {
+     console.log(info,'info');
+       const {tag}=info;
+      this.curFocusIndex = index
+      const refName = `tagsInput${this.curFocusIndex}`;
+      this.$refs[refName].focus(); // 动态获取ref并调用focus方法
+       this.$emit('clickTag',tag)
+    },
+      // 处理多条件下的标签点击事件
+    handleMultiTagClick(info, conditionIndex, innerIndex, field) {
+      const { tag } = info;
+      // 设置当前焦点信息
+      this.setMultiFocus(conditionIndex, innerIndex, field);
+      // 获取对应的ref并聚焦
+      const refName = `multiTagsInput_${conditionIndex}_${innerIndex}_${field}`;
+      console.log(refName,'refName');
+      console.log(this.$refs,'this.$refs');
+      
+      if (this.$refs[refName]) {
+        this.$refs[refName][0].focus();
+      }
+      // 向父组件传递点击的标签信息
+      this.$emit('clickTag', tag);
+    },
+     // 处理数据获取部分(循环项)的标签点击
+  handleDataTagClick(info, index, field) {
+    const { tag } = info;
+    // 1. 设置当前焦点
+    this.setDataFocus(index, field);
+    // 2. 聚焦到当前输入框
+    const refName = `dataTag_${index}_${field}`;
+    const tagInput = this.$refs[refName];
+    if (tagInput) {
+      const instance = Array.isArray(tagInput) ? tagInput[0] : tagInput;
+      if (instance && typeof instance.focus === 'function') {
+        instance.focus();
+      }
+    }
+    // 3. 向父组件传递选中的tag
+    this.$emit('clickTag', tag);
+  },
+
+  // 处理“否则”部分的标签点击
+  handleElseTagClick(info, position) {
+    const { tag } = info;
+    // 1. 设置当前焦点
+    this.setElseFocus(position);
+    // 2. 聚焦到当前输入框
+    const refName = `elseTag${position.charAt(0).toUpperCase() + position.slice(1)}`;
+    const instance = this.$refs[refName];
+    if (instance && typeof instance.focus === 'function') {
+      instance.focus();
+    }
+    // 3. 向父组件传递选中的tag
+    this.$emit('clickTag', tag);
+  },
 
   }
 }

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

@@ -8,6 +8,8 @@ export function convertArrayToExpression(arr) {
   
   // 遍历输入数组的每个元素
   arr.forEach(item => {
+
+    
     switch (item.type) {
       case 'Element':
         // 对于Element类型,提取tableElementKey并包裹在E[]中
@@ -21,6 +23,10 @@ export function convertArrayToExpression(arr) {
         // 对于Text类型,直接使用name属性
         result.push(item.name);
         break;
+     case 'Brackets':
+        // 对于Text类型,直接使用name属性
+        result.push(item.name);
+        break;
       default:
         // 处理未知类型
         console.warn('未知类型:', item.type);

+ 446 - 7
src/views/formula/component/table-form-write.vue

@@ -1,19 +1,458 @@
 <template>
-  <div>66666</div>
+  <div class="excelHtnl">
+    <div
+      class="excelBox hc-excel-table-form"
+      style="margin-top:40px;height: 100%;"
+      @click="parentClick($event)"
+    >
+      <div style="width:100%;height: 100%;overflow: scroll;" class='parent' :id="containerId"></div>
+    </div>
+  </div>
 </template>
 
 <script>
+import Vue from 'vue'
+import { getExcelHtml } from '@/api/exctab/excelmodel'
+
 export default {
-  name: "table-form-write",
-  props: {
-  
+  props: [
+    'pkeyId', 
+    'initTableName',
+    'selectedTableKey',
+    'containerId',
+    'multipleSelect'  // 是否允许多选,默认为false
+  ],
+  data() {
+    return {
+      selectedElements: [],
+      exHtml: '',
+      currentComponent: null
+    }
+  },
+  created() {
+    if (!this.containerId) {
+      this.containerId = `excel-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+    }
+  },
+  watch: {
+    pkeyId: {
+      immediate: true,
+      deep: true,
+      handler(newVal) {
+        if (newVal) {
+          this.exHtml = ''
+          this.getExcelHtml(newVal)
+        } else {
+          this.exHtml = ''
+          this.clearForm()
+        }
+      }
+    },
+    selectedTableKey: {
+      handler(newKey) {
+        this.clearAllSelected();
+        if (newKey && newKey.startsWith('key_')) {
+          this.highlightElementsByKey(newKey);
+        }
+      },
+      immediate: true
+    },
+    containerId: {
+      handler(newVal) {
+        this.clearForm()
+        this.selectedElements = [];
+      },
+      immediate: true
+    },
+    multipleSelect: {
+      handler(newVal) {
+        // 当多选模式改变时,清空当前选择
+        if (!newVal && this.selectedElements.length > 1) {
+          // 如果关闭多选且当前有多个选中,只保留最后一个
+          const lastElement = this.selectedElements[this.selectedElements.length - 1];
+          this.clearAllSelected();
+          this.selectElements([lastElement]);
+        }
+      },
+      immediate: true
+    }
   },
   methods: {
+    // 获取当前组件容器元素,封装为工具方法
+    getContainer() {
+      return document.getElementById(this.containerId);
+    },
+    
+    // 获取当前所有有橙色背景且去重的元素列表
+    getCurrentSelectedElements() {
+      const container = this.getContainer();
+      if (!container) return [];
+      
+      // 获取所有有橙色背景的元素
+      const orangeElements = container.querySelectorAll('[style*="background-color: rgb(255, 165, 0)"], [style*="background-color:#ffa500"]');
+      
+      // 根据 tableElementKey 去重
+      const uniqueElements = [];
+      const seenKeys = new Set();
+      
+      orangeElements.forEach(el => {
+        if (el.id) {
+          const tableElementKey = this.initTableName + ':' + this.extractKeyPrefix(el.id);
+          if (!seenKeys.has(tableElementKey)) {
+            seenKeys.add(tableElementKey);
+            const keyName = el.placeholder || '';
+            uniqueElements.push({
+              tableElementKey: tableElementKey,
+              eName: keyName,
+              id: el.id
+            });
+          }
+        }
+      });
+      
+      return uniqueElements;
+    },
+    
+    highlightElementsByKey(key) {
+      if (!key) return;
+      
+      const container = this.getContainer();
+      if (!container) return;
+      
+      const keyPrefix = key.split('__')[0];
+      // 只在当前容器内查找元素
+      const samePrefixElements = container.querySelectorAll(`[id^="${keyPrefix}__"]`);
+      
+      if (samePrefixElements.length === 0) {
+        const element = container.querySelector(`#${key}`);
+        if (element) {
+          this.selectElements([element]);
+          return;
+        }
+      }
+      
+      this.selectElements(samePrefixElements);
+    },
+    
+    async getExcelHtml(pkeyId) {
+      this.clearForm();
+      try {
+        const { data: res } = await getExcelHtml({ pkeyId })
+        if (res.code === 200) {
+          this.exHtml = res.data
+          this.cop();
+        }
+      } catch (error) {
+        console.error('获取HTML失败:', error)
+      }
+    },
+    
+    clearForm() {
+      const container = this.getContainer();
+      if (container) {
+        container.innerHTML = '';
+      }
+      this.clearAllSelected();
+    },
+    
+    async cop() {
+      const container = this.getContainer();
+      if (!container) {
+        console.error('父容器不存在:', this.containerId);
+        return;
+      }
+      
+      try {
+        const MyComponent = Vue.extend({
+          template: this.exHtml,
+          data() {
+            return {
+              formData: {},
+              getTokenHeader: {},
+              dap_site_data: {}
+            }
+          },
+          methods: {
+            contextmenuClick() {},
+            getInformation() {},
+            formUploadSuccess() {},
+            formUploadExceed() {},
+            formUploadLoading() {},
+            delTableFormFile() {},
+            formUploadError() {},
+            uploadprogress() {},
+            formRemoteMethod() {},
+            getRegularExpression() {},
+            checkboxGroupChange() {},
+            formRemoteChange() {},
+            dateKeydown() {},
+            keyupShiftUp() {},
+            keyupShiftDown() {},
+            keyupShiftLeft() {},
+            keyupShiftRight() {},
+            inputLeftClick() {},
+          }
+        });
+        
+        // 销毁旧组件
+        if (this.currentComponent) {
+          this.currentComponent.$destroy();
+        }
+        
+        // 创建新组件
+        this.currentComponent = new MyComponent().$mount();
+        
+        // 添加到当前容器
+        container.innerHTML = '';
+        container.appendChild(this.currentComponent.$el);
+        
+        this.$nextTick(() => {
+          if (this.selectedTableKey) {
+            this.highlightElementsByKey(this.selectedTableKey);
+          }
+        });
+      } catch (error) {
+        console.error('组件渲染失败:', error);
+      }
+    },
+    
+    async parentClick(e) {
+      e.stopPropagation(); // 阻止事件冒泡到其他组件
+      
+      const container = this.getContainer();
+      if (!container || !container.contains(e.target)) {
+        return; // 不是当前组件内的点击,不处理
+      }
+      
+      // 检测Control键是否按下 (e.ctrlKey 对Windows/Linux有效,e.metaKey对Mac的Command键有效)
+      const isCtrlPressed = e.ctrlKey || e.metaKey;
+      // 只有在multipleSelect为true时才允许通过Control键进行多选
+      const isMultiSelectMode = this.multipleSelect && isCtrlPressed;
+      
+      // 只在当前容器内查找目标元素
+      let target = e.target;
+      while (target && target !== container && !target.id) {
+        target = target.parentNode;
+      }
+      
+      if (!target || !target.id || !container.contains(target)) {
+        return;
+      }
+      
+      // 检查警告样式
+      let hasWarnStyle = false;
+      if (target.classList.contains('warnstyle') || 
+          (target.parentNode && target.parentNode.classList.contains('warnstyle'))) {
+        hasWarnStyle = true;
+      }
 
+      if (hasWarnStyle) {
+        this.$message({
+          type: "warning",
+          message: "当前位置未配置元素,请重新选择或配置元素后再试"
+        });
+        return;
+      }
+      
+      const id = target.id;
+      const elementId = this.initTableName + ':' + this.extractKeyPrefix(id);
+      
+      // 检查是否已经有相同 tableElementKey 的元素被选中
+      const isSameKeySelected = this.selectedElements.some(item => 
+        item.tableElementKey === elementId
+      );
+      
+      // 根据是否按下Control键决定是否清空已选元素
+      if (!isMultiSelectMode) {
+        this.clearAllSelected();
+      }
+      
+      if (!isSameKeySelected) {
+        if (id.startsWith('key_')) {
+          const keyPrefix = this.extractKeyPrefix(id);
+          // 只在当前容器内查找同前缀元素
+          const samePrefixElements = container.querySelectorAll(`[id^="${keyPrefix}__"]`);
+          this.selectElements(samePrefixElements);
+        } else {
+          this.handleNormalElement(target, id);
+        }
+      } else {
+        // 如果已经有相同 tableElementKey 的元素被选中,在多选模式下点击会取消选择所有相同key的元素
+        if (isMultiSelectMode) {
+          this.deselectElementsByKey(elementId);
+        }
+      }
+    },
+    
+    extractKeyPrefix(id) {
+      const parts = id.split('__');
+      return parts.length > 0 ? parts[0] : id;
+    },
+    
+    selectElements(elements) {
+      const container = this.getContainer();
+      if (!container) return;
+      
+      // 先取消选择所有相同 tableElementKey 的元素(避免重复)
+      if (elements.length > 0) {
+        const firstElementId = this.initTableName + ':' + this.extractKeyPrefix(elements[0].id);
+        this.deselectElementsByKey(firstElementId);
+      }
+      
+      elements.forEach(el => {
+        // 确保元素属于当前容器
+        if (!container.contains(el)) return;
+        
+        const index = this.selectedElements.findIndex(item => item.id === el.id);
+        
+        if (index === -1) {
+          el.style.backgroundColor = '#ffa500';
+          const elementId = this.initTableName + ':' + this.extractKeyPrefix(el.id);
+          const keyName = el.placeholder || '';
+          const newElement = { tableElementKey: elementId, eName: keyName, id: el.id };
+          this.selectedElements.push(newElement);
+          
+          // 传递当前所有有橙色背景且去重的元素
+          const currentSelected = this.getCurrentSelectedElements();
+          this.$emit('element-selected', newElement, true, currentSelected);
+        }
+      });
+    },
+    
+    // 取消选择所有具有相同 tableElementKey 的元素
+    deselectElementsByKey(tableElementKey) {
+      const container = this.getContainer();
+      if (!container) return;
+      
+      // 找出所有相同 tableElementKey 的元素
+      const elementsToDeselect = this.selectedElements.filter(item => 
+        item.tableElementKey === tableElementKey
+      );
+      
+      if (elementsToDeselect.length === 0) return;
+      
+      // 清除样式并从选中列表中移除
+      elementsToDeselect.forEach(item => {
+        const element = container.querySelector(`#${item.id}`);
+        if (element) {
+          element.style.backgroundColor = '';
+        }
+        // 传递当前所有有橙色背景且去重的元素
+        const currentSelected = this.getCurrentSelectedElements();
+        this.$emit('element-selected', item, false, currentSelected);
+      });
+      
+      // 从选中列表中移除这些元素
+      this.selectedElements = this.selectedElements.filter(item => 
+        item.tableElementKey !== tableElementKey
+      );
+    },
+    
+    handleNormalElement(target, id) {
+      const container = this.getContainer();
+      if (!container || !container.contains(target)) return;
+      
+      const elementId = this.initTableName + ':' + target.id.split('__')[0];
+      const keyName = target.placeholder || '';
+      
+      if(!keyName){
+        this.$message({
+          type: "warning",
+          message: "当前位置未配置元素,请重新选择或配置元素后再试"
+        });
+        return;
+      }
+      
+      // 先取消选择相同 tableElementKey 的元素
+      this.deselectElementsByKey(elementId);
+      
+      target.style.backgroundColor = '#ffa500';
+      const newElement = { tableElementKey: elementId, eName: keyName, id };
+      this.selectedElements.push(newElement);
+      
+      // 传递当前所有有橙色背景且去重的元素
+      const currentSelected = this.getCurrentSelectedElements();
+      this.$emit('element-selected', newElement, true, currentSelected);
+    },
+    
+    clearAllSelected() {
+      const container = this.getContainer();
+      if (!container) return;
+      
+      this.selectedElements.forEach(item => {
+        // 只清除当前容器内的元素样式
+        const element = container.querySelector(`#${item.id}`);
+        if (element) {
+          element.style.backgroundColor = '';
+          // 传递当前所有有橙色背景且去重的元素
+          const currentSelected = this.getCurrentSelectedElements();
+          this.$emit('element-selected', { 
+            tableElementKey: item.tableElementKey, 
+            eName: item.eName, 
+            id: item.id 
+          }, false, currentSelected);
+        }
+      });
+      this.selectedElements = [];
+    }
   }
 }
 </script>
 
-<style scoped lang="scss">
-</style>
-    
+<style lang="scss" scoped>
+/* 样式保持不变 */
+.excelHtnl {
+  margin: 0 0 0 10px;
+  background-color: #fff;
+  box-sizing: border-box;
+  padding: 0 20px 100px 20px;
+  height: 100%;
+}
+
+.hc-upload-table-form {
+  position: relative;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.hc-upload-table-form .el-upload {
+  position: relative;
+  flex: 1;
+  height: 100%;
+  color: #ccc;
+}
+.hc-upload-table-form .el-upload .hc-table-form-icon {
+  font-size: 24px;
+  font-weight: 100;
+}
+.hc-upload-table-form .el-upload .hc-table-form-img {
+  width: 100%;
+  height: 100%;
+}
+
+.excelBox {
+  ::v-deep .oldlace-bg {
+    background-color: oldlace;
+  }
+  ::v-deep .select-td {
+    border-width: 4px;
+    border-color: #E6A23C;
+    border-style: solid;
+  }
+}
+
+::v-deep .warnstyle .el-input__inner {
+  background-image: url('/img/login/warn.png') !important;
+  background-repeat: no-repeat;
+  background-size: cover;
+  background-position: center
+}
+
+::v-deep .warnstyle .el-textarea__inner {
+  background-image: url('/img/login/warn.png') !important;
+  background-repeat: no-repeat;
+  background-position-x: 45%;
+  background-position-y: 46%;
+}
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 474 - 310
src/views/formula/edit.vue


+ 3 - 1
src/views/formula/formulaStringToArray.js

@@ -185,7 +185,9 @@ export const formulaStringToArray = (text,elemap,formulaMap) => {
   const commaReg = /^,/;//逗号
   const operatorReg = /^ (\+|-|\*|%|\/) /;//加减乘除
   // const wordReg = /^[\u4e00-\u9fa5\w0-9'"-]+/;//文本
-  const wordReg = /^[\u4e00-\u9fa5\{\}\w\\.0-9'"-]+/;//文本
+  // const wordReg = /^[\u4e00-\u9fa5\{\}\w\\.0-9'"-]+/;//文本
+  const wordReg = /^[±\u4e00-\u9fa5\{\}\w\\.0-9'"-]+/; // 新增±符号到文本匹配正则
+
  const sxReg = /^[|]/; // 匹配字符串开头竖线
 
   // const logicalReg = /^(>=|<=|>|<|=|&&)/;//逻辑符号

+ 8 - 8
src/views/manager/contractinfo/detail.vue

@@ -328,17 +328,17 @@
                 <el-col :span="12">
                   <el-form-item
                     label="客户编号"
-                    prop="sealCustomerld"
+                    prop="sealCustomerId"
                   >
-                    <el-input v-model="contractForm.sealCustomerld"></el-input>
+                    <el-input v-model="contractForm.sealCustomerId"></el-input>
                   </el-form-item>
                 </el-col>
                 <el-col :span="12">
                   <el-form-item
                     label="项目ID"
-                    prop="sealProjectld"
+                    prop="sealProjectId"
                   >
-                    <el-input v-model="contractForm.sealProjectld"></el-input>
+                    <el-input v-model="contractForm.sealProjectId"></el-input>
                   </el-form-item>
                 </el-col>
               </el-row>
@@ -346,17 +346,17 @@
                 <el-col :span="12">
                   <el-form-item
                     label="账号ID"
-                    prop="sealAccountld"
+                    prop="sealAccountId"
                   >
-                    <el-input v-model="contractForm.sealAccountld"></el-input>
+                    <el-input v-model="contractForm.sealAccountId"></el-input>
                   </el-form-item>
                 </el-col>
                 <el-col :span="12">
                   <el-form-item
                     label="通信KeyB64"
-                    prop="sealCommkey"
+                    prop="sealCommKey"
                   >
-                    <el-input v-model="contractForm.sealCommkey"></el-input>
+                    <el-input v-model="contractForm.sealCommKey"></el-input>
                   </el-form-item>
                 </el-col>
               </el-row>

+ 4 - 2
src/views/manager/projectinfo/detail.vue

@@ -809,11 +809,13 @@ export default {
     })
   },
   methods: {
-    getCreateUserItems(){
+ getCreateUserItems(){
       getList(
         1,
         1000,
-        { deptId:'1536982621165592577'},
+        {},
+
+       '1536982621165592577',
      
       ).then(res => {
        

+ 2 - 2
src/views/manager/projectinfo/list.vue

@@ -433,8 +433,8 @@ export default {
        const typeToLabelMap = {
         1: '质检',
         2: '试验',
-        3: '计量',
-        4: '日志',
+        3: '日志',
+        4: '计量',
         5: '征拆',
     };
       let tagItems = [];

+ 244 - 53
src/views/manager/projectinfo/tree.vue

@@ -489,7 +489,17 @@
                                                         @click="handleEditFormula(scope.$index, scope.row)"
                                                         >编辑元素公式
                                                     </el-link>
-
+                                                      <el-link
+                                                                  class="mg-l-10"
+                                                                  type="primary"
+                                                                  :loading="handlesyncLoad"
+                                                                  @click="handleLinkNodes(scope.$index, scope.row)"
+                                                                  v-if="
+                                                                    curTreeData.nodeType === 1000 ||
+                                                                    curTreeData.nodeType === 1001
+                                                                  "
+                                                                  >关联节点
+                                                        </el-link>
                                                     <el-link
                                                         class="mg-l-10"
                                                         type="danger"
@@ -710,6 +720,17 @@
                                                         <el-dropdown-item command="b">同步其他表单配置</el-dropdown-item>
                                                       </el-dropdown-menu>
                                                     </el-dropdown>
+                                                      <el-link
+                                                                  class="mg-l-10"
+                                                                  type="primary"
+                                                                  :loading="handlesyncLoad"
+                                                                  @click="handleLinkNodes(scope.$index, scope.row)"
+                                                                  v-if="
+                                                                    curTreeData.nodeType === 1000 ||
+                                                                    curTreeData.nodeType === 1001
+                                                                  "
+                                                                  >关联节点
+                                                        </el-link>
                                                     <el-link
                                                         class="mg-l-10"
                                                         type="danger"
@@ -952,6 +973,17 @@
                       @click="handleEditFormula(scope.$index, scope.row)"
                       >编辑元素公式
                     </el-link>
+                      <el-link
+                                class="mg-l-10"
+                                type="primary"
+                                :loading="handlesyncLoad"
+                                @click="handleLinkNodes(scope.$index, scope.row)"
+                                v-if="
+                                  curTreeData.nodeType === 1000 ||
+                                  curTreeData.nodeType === 1001
+                                "
+                                >关联节点
+                      </el-link>
 
                     <el-link
                       class="mg-l-10"
@@ -1213,6 +1245,7 @@
           type="primary"
           v-throttle="2000"
           @click="saveLinkTab()"
+          :loading="GLExcelLoadingSave"
           >确 定</el-button
         >
       </span>
@@ -1831,6 +1864,7 @@
           <el-table-column align="center" prop="describe" label="描述">
             <template slot-scope="scope">
               <el-input v-model="scope.row.remark"></el-input>
+            
             </template>
           </el-table-column>
           <el-table-column label="操作" width="120" align="center">
@@ -1974,7 +2008,7 @@
       ></FormulaEdit>
     </el-dialog>
     <!-- 全局公式 -->
-    <el-dialog
+    <!-- <el-dialog
       title=""
       :visible.sync="formulaCompVisible1"
       fullscreen
@@ -1996,7 +2030,7 @@
         v-if="formulaCompVisible1"
       >
       </FormulaEditone>
-    </el-dialog>
+    </el-dialog> -->
 
     <!-- 编辑元素 -->
     <el-dialog
@@ -2300,6 +2334,23 @@
         </div>
       </template>
     </el-table-column>
+    <el-table-column
+      label="资料类型"
+      prop="remark"
+     
+      >
+      <template #default="scope">
+          <el-select v-model="scope.row.remark" placeholder="请选择"  multiple   @change="handleSelectChange(scope.row)" :disabled="!scope.row.isEdit">
+                <el-option
+                  v-for="(item, key) in nodeNameList"
+                  :key="key"
+                  :label="item.dictValue"
+                  :value="item.dictKey"
+                >
+                </el-option>
+              </el-select>
+      </template>
+    </el-table-column>
     <el-table-column
       label="操作"
       width="150px"
@@ -2616,7 +2667,7 @@
                     lazy
                     :load="loadNode"
                    @node-click="handleNodeClick"
-                    :default-expanded-keys="defaultExpandedKeys"
+                
                     :props="defaultProps"
                     :expand-on-click-node="false"
                     highlight-current
@@ -2629,39 +2680,85 @@
         </el-card>
         <el-card class="box-card-2">
          
-            <div class="table-box">
-              <h4 style="margin-left: 4px;">表单预览</h4>
-              <el-table
-              size="small"
-              ref="proTable1"
-                  :data="preTableData1"
-                  stripe
-                  style="width: 100%"
-                  max-height="300"
-                  @selection-change="handleSelectionChange1"
-                  
-                  >
-                  <el-table-column
-                    type="selection"
-                    width="55">
-                  </el-table-column>
-                  <el-table-column
-                    prop="tableName"
-                    label="表单名称"
-                    width="180">
-                  </el-table-column>
-                  <el-table-column
-                   prop="tableType"
-                  :formatter="formatTableType"
-                    label="表单类型"
-                    width="180">
-                  </el-table-column>
-                  <el-table-column
-                      prop="tableOwner"
-                     :formatter="formatOwner"
-                    label="所属方">
-                  </el-table-column>
-                </el-table>
+            <div >
+             
+              <el-tabs type="border-card" v-model="activeName" >
+                <el-tab-pane label="表单预览" name="表单预览">
+                  <el-table
+                                  size="small"
+                                  ref="proTable1"
+                                      :data="preTableData1"
+                                      stripe
+                                      style="width: 100%"
+                                     
+                                      @selection-change="handleSelectionChange1"
+                                      
+                                      >
+                                      <el-table-column
+                                        type="selection"
+                                        width="55">
+                                      </el-table-column>
+                                      <el-table-column
+                                        prop="tableName"
+                                        label="表单名称"
+                                      >
+                                      </el-table-column>
+                                      <el-table-column
+                                      prop="tableType"
+                                      :formatter="formatTableType"
+                                        label="表单类型"
+                                        width="100">
+                                      </el-table-column>
+                                      <el-table-column
+                                          prop="tableOwner"
+                                          width="100"
+                                        :formatter="formatOwner"
+                                        label="所属方">
+                                      </el-table-column>
+                                      
+                  </el-table> 
+                </el-tab-pane>
+                <el-tab-pane label="推荐表单" name="推荐表单">
+                                <el-table
+                                  size="small"
+                                  ref="proTable2"
+                                      :data="recomandData"
+                                      stripe
+                                      style="width: 100%"
+                                    
+                                      @selection-change="handleSelectionChange2"
+                                      
+                                      >
+                                      <el-table-column
+                                        type="selection"
+                                        width="55">
+                                      </el-table-column>
+                                      <el-table-column
+                                        prop="tableName"
+                                        label="表单名称"
+                                      >
+                                      </el-table-column>
+                                      <el-table-column
+                                      prop="tableType"
+                                      :formatter="formatTableType"
+                                        label="表单类型"
+                                        width="100">
+                                      </el-table-column>
+                                      <el-table-column
+                                          prop="tableOwner"
+                                          width="100"
+                                        :formatter="formatOwner"
+                                        label="所属方">
+                                      </el-table-column>
+                                      <el-table-column
+                                        prop="parentName"
+                                        label="父节点"
+                                      >
+                                      </el-table-column>
+                                  </el-table> 
+                </el-tab-pane>
+                
+              </el-tabs>
             </div>
         </el-card>
       </div>
@@ -2831,7 +2928,7 @@ import {
 import { getStore, setStore } from "@/util/store";
 
 import { getTempProject,addSync,getNodeStatus,getById } from "@/api/manager/ledger";
-import { selectByNodeTable   as findNodeTableByCondition1 } from "@/api/manager/wbstree";
+import { selectByNodeTable   as findNodeTableByCondition1,getRecommendTable } from "@/api/manager/wbstree";
 
 import { getDictionaryBiz } from "@/api/other";
 import tableSortByType from './tableSortByType'
@@ -2875,6 +2972,8 @@ export default {
       //#region 关联清表
       loading: false,
       GLExcel: false,
+      GLExcelLoadingSave: false,
+
       GLExcelFrom: {
         id: "",
         name: "",
@@ -3223,6 +3322,8 @@ export default {
       saveProTagLoading:false,
       syncTableDialog:false,
       preTableData1:[],
+      recomandData:[],
+      activeName:'推荐表单',
       formIds:'',
       saveTableSyncLoad:false,
       checkRow:null,
@@ -4431,17 +4532,32 @@ clearSearch1() {
     },
 
     //跳转到公式配置页面
-    toFormulaEdit(row, type) {
+    // toFormulaEdit(row, type) {
+    //   this.curEleTable.hasPartFormula = row.hasPartFormula;
+    //   this.formulaCurRow = row;
+    //   this.formulaCurRow.globaltype = type;
+    //   if (type === 10) {
+    //     this.formulaCompVisible1 = true;
+    //     this.formulaCurRow.elementType = false;
+    //     this.istableType = true;
+    //   } else {
+    //     this.formulaCompVisible = true;
+    //   }
+    // },
+      toFormulaEdit(row, type) {
       this.curEleTable.hasPartFormula = row.hasPartFormula;
       this.formulaCurRow = row;
       this.formulaCurRow.globaltype = type;
-      if (type === 10) {
-        this.formulaCompVisible1 = true;
+      // if (type === 10) {
+      //   this.formulaCompVisible1 = true;
+      //   this.formulaCurRow.elementType = false;
+      //   this.istableType = true;
+      // } else {
+      //   this.formulaCompVisible1 = true;
+      // }
+       this.formulaCompVisible = true;
         this.formulaCurRow.elementType = false;
         this.istableType = true;
-      } else {
-        this.formulaCompVisible = true;
-      }
     },
     //关闭公式弹窗
     closeformulaComp() {
@@ -4834,14 +4950,14 @@ async saveLinkTab() {
       });
       return;
     }
-    
+    this.GLExcelLoadingSave=true;
     try {
       const { data: res } = await saveLinkTab({
         exceTabId: node.id,
         tabId: this.GLExcelFrom.id,
         updateAll:this.isNodeType? false:true,
       });
-      
+       this.GLExcelLoadingSave=false;
       if (res.code === 200) {
         this.$message({
           type: "success",
@@ -4883,6 +4999,7 @@ async saveLinkTab() {
         this.GLExcel = false;
       }
     } catch (error) {
+      this.GLExcelLoadingSave=false;
       console.error("关联清表失败:", error);
       this.$message({
         type: "error",
@@ -5855,9 +5972,35 @@ async saveLinkTab() {
     },
     //文件题名(全局按钮)
     fileTitleHandle(){
+        this.getNodeNameList();
       this.getFileTableData()
+    
       this.fileTitleVisible = true;
     },
+    handleSelectChange(row) {
+    // 如果选中了dictKey为'0'的选项
+    if (row.remark && row.remark.includes('0')) {
+      // 强制只保留'0'这一个选项
+      row.remark = ['0'];
+    } else {
+      // 处理其他情况(单个值不拼接逗号,多个值用逗号拼接)
+      // 由于v-model绑定的是数组,这里无需手动拼接,提交时按需处理即可
+      // 例如:提交时转换为字符串 row.remark.join(',')
+    }
+  },
+    getNodeNameList() {
+      let code = "node_name_type";
+
+      getDictionary({
+        code,
+      }).then((res) => {
+        this.nodeNameList = res.data.data;
+        console.log(this.nodeNameList,'this.nodeNameList');
+        
+ 
+        
+      });
+    },
     changeRadio(val){
       console.log(val,'val');
       const fileItem = this.fileTableData[this.chooseFileIndex];
@@ -5875,6 +6018,11 @@ async saveLinkTab() {
           this.fileTableData.forEach((element) => {
             element.roleNameVal = element.nameRule.split(",");
             element.ruleName=element.nameVaule.split("-")
+            console.log(element.remark,'element.ruleName');
+            
+            if(element.remark){
+              element.remark=element.remark.split(",")
+            }
             if(element.type===2){
               const list=element.list
               let arr=[]
@@ -5999,7 +6147,9 @@ async saveLinkTab() {
         nameRule:ruleName,
         nodeId:ruleSizeVal,
         projectId:this.projectid,
-        type:this.fileTitleModel.type
+        type:this.fileTitleModel.type,
+        remark:row.remark.join(',')
+       
       }
       saveOrUpdateNameRule(
       [nameRuleDtos]
@@ -6074,7 +6224,8 @@ async saveLinkTab() {
           nameRule:ruleName,
           nodeId:ruleSizeVal,
           projectId:this.projectid,
-          type:ele.type
+          type:ele.type,
+          remark:ele.remark.join(',')
         })
       })
       if(!isCansave){
@@ -6399,11 +6550,18 @@ async saveLinkTab() {
         
         this.syncTableDialog=true
         this.isShowLeft=true
-
+    
+        this.activeName='推荐表单'
         if(this.$refs.proTable1){
           this.$refs.proTable1.clearSelection()
         }
-        
+             if(this.$refs.proTable2){
+          this.$refs.proTable2.clearSelection()
+        }
+        this.recomandData = [];
+   
+        this.formIds=''
+        this.getRecommendTableData()
       }
       
     },
@@ -6424,6 +6582,7 @@ async saveLinkTab() {
                     this.$message.success(res.data.msg)
                     this.syncTableDialog=false
                     this.$refs.proTable1.clearSelection()
+                     this.$refs.proTable2.clearSelection()
                     this.updateNodeTable()
                    
                     }else{
@@ -6434,15 +6593,33 @@ async saveLinkTab() {
     },
     handleNodeClick(data) {
    
-      selectByNodeTable(data.id, this.projectid, this.id).then((res) => {
-          if (res.data.data.length) {
+      selectByNodeTable(
+        data.id, this.projectid, this.id
+      ).then((res) => {
+          if (res.data.data&&res.data.data.length) {
             this.preTableData1 = res.data.data;
           } else {
             this.preTableData1 = [];
           }
-        });
+       });
      
     },
+    getRecommendTableData(){
+      getRecommendTable(
+        {
+          parentId:0,
+          projectId:this.projectid,
+          wbsId:this.id,
+          pKeyId:this.checkRow.pkeyId
+        }
+      ).then((res) => {
+          if (res.data.data&&res.data.data.length) {
+            this.recomandData = res.data.data;
+          } else {
+            this.recomandData = [];
+          }
+       });
+    },
     handleSelectionChange1(val) {
 
       
@@ -6461,6 +6638,20 @@ async saveLinkTab() {
        
   
     },
+    handleSelectionChange2(val) {
+        if (val.length > 1) {
+          // 如果选择了多个,只保留最后一个
+          this.$nextTick(() => {
+            this.$refs.proTable2.clearSelection();
+            this.$refs.proTable2.toggleRowSelection(val.pop());
+          });
+      }
+      if(val.length>0){
+        this.formIds=val[0].pkeyId
+      }else{
+        this.formIds=''
+      }
+    },
    async refreshData(){
     this.refreshLoading=true
       getNodeStatus({ id:this.curTreeData.primaryKeyId}).then((res) => {

+ 7 - 3
src/views/manager/wbsinfo.vue

@@ -272,6 +272,7 @@ export default {
             display: false,
             search: true,
             slot:true,
+            
           },
    
         ]
@@ -331,8 +332,8 @@ export default {
        const typeToLabelMap = {
         1: '质检',
         2: '试验',
-        3: '计量',
-        4: '日志',
+        3: '日志',
+        4: '计量',
         5: '征拆',
         6: '底层节点',
        
@@ -371,6 +372,7 @@ export default {
       })
     },
     rowSave (row, done, loading) {
+      row.projectInfoList=null
       add(row).then(() => {
         this.onLoad(this.page);
         this.$message({
@@ -540,11 +542,13 @@ export default {
 
     },
     searchClick(){ 
+       this.page.currentPage = 1;
       this.onLoad(this.page,this.query);
     },
     clearSearch(){
+       this.page.currentPage = 1;
       this.query = {};
-      this.onLoad(this.page,this.searchParams);
+      this.onLoad(this.page,this.query);
     }
   }
 };

+ 75 - 99
src/views/manager/wbsinfo/TreeCopyModal.vue

@@ -5,13 +5,11 @@
     :width="dialogWidth"
     :before-close="handleClose"
     append-to-body
-
     class="tree-copy-modal"
   >
     <!-- 提示信息 -->
     <div class="alert-message">
-       <span>温馨提示:允许单对多、多对单、多对多的关联关系。关联后可在对应节点查看。【关联并继续】可以继续续进行关联。</span>
-     
+      <span>温馨提示:允许单对多、多对单、多对多的关联关系。关联后可在对应节点查看。【关联并继续】可以继续进行关联。</span>
     </div>
 
     <!-- 树形结构容器 -->
@@ -20,7 +18,7 @@
       <div class="tree-wrapper left-tree">
         <div class="tree-title">复制源</div>
         <el-tree
-         v-if="isShowTree"
+          v-if="isShowTree"
           ref="sourceTree"
           :data="sourceTreeData"
           :props="treeProps"
@@ -31,9 +29,7 @@
           :expand-on-click-node="false"
           @check="handleSourceCheck"
           check-strictly
-          :default-expanded-keys="defaultExpandedKeys" 
-           
-
+          :default-expanded-keys="defaultExpandedKeys"
         ></el-tree>
       </div>
 
@@ -46,8 +42,8 @@
       <div class="tree-wrapper right-tree">
         <div class="tree-title">复制到</div>
         <el-tree
-         v-if="isShowRight"
-        check-strictly
+          v-if="isShowRight"
+          check-strictly
           ref="targetTree"
           :data="targetTreeData"
           :props="treeProps"
@@ -57,9 +53,7 @@
           lazy
           :expand-on-click-node="false"
           @check="handleTargetCheck"
-          :default-expanded-keys="defaultExpandedKeys" 
-        
-           
+          :default-expanded-keys="defaultExpandedKeys"
         ></el-tree>
       </div>
     </div>
@@ -74,25 +68,19 @@
 </template>
 
 <script>
-import {
-  getLazytree,
-
-} from "@/api/manager/wbstree";
+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
@@ -101,7 +89,6 @@ export default {
       type: [String, Number],
       required: true
     },
-    // 用户信息,包含tenant_id
     userInfo: {
       type: Object,
       required: true
@@ -114,23 +101,18 @@ export default {
       type: Array,
       default: () => []
     },
-    checkId:{
-         type: [String, Number],
+    checkId: {
+      type: [String, Number],
       required: true
     }
- 
   },
   data() {
     return {
-      // 弹窗显示状态
       visible: false,
-      // 源树数据
       sourceTreeData: [],
-      // 目标树数据
       targetTreeData: [],
-      // 树的配置项
       treeProps: {
-              children: "children",
+        children: "children",
         label: "title",
         isLeaf: function (data) {
           if (data.hasChildren && data.isExistForm != 1) {
@@ -142,23 +124,20 @@ export default {
           }
         },
       },
-      
-      // 选中的源节点
       selectedSourceNodes: [],
-      // 选中的目标节点
       selectedTargetNodes: [],
-      isShowTree:true,
-      isShowRight:true
+      isShowTree: true,
+      isShowRight: true
     };
   },
-  watch:{
-    linkLoading(val){
+  watch: {
+    linkLoading(val) {
       this.linkLoading = val;
     },
-    defaultExpandedKeys(val){
+    defaultExpandedKeys(val) {
       this.defaultExpandedKeys = val;
     },
-    checkId(val){
+    checkId(val) {
       this.checkId = val;
     }
   },
@@ -178,12 +157,12 @@ export default {
         // 设置默认选中的源节点
         if (this.checkId && this.$refs.sourceTree) {
           this.$refs.sourceTree.setCheckedKeys([this.checkId]);
-        
-           setTimeout(() => {
-              const checkedNodes = this.$refs.sourceTree.getCheckedNodes();
-              this.selectedSourceNodes = checkedNodes;
-              console.log('选中的节点:', this.selectedSourceNodes);
-            }, 3000);
+          
+          setTimeout(() => {
+            const checkedNodes = this.$refs.sourceTree.getCheckedNodes();
+            this.selectedSourceNodes = checkedNodes;
+            console.log('选中的节点:', this.selectedSourceNodes);
+          }, 3000);
         }
         
         // 重置目标树
@@ -193,15 +172,14 @@ export default {
       });
     },
 
-
     // 关闭弹窗
     handleClose() {
       this.visible = false;
       this.$emit('close');
-      this.isShowTree=false
-      this.isShowRight=false
-      this.checkId = ''
-
+      this.isShowTree = false;
+      this.isShowRight = false;
+      this.checkId = '';
+      this.clearSelections();
     },
 
     // 加载源树节点
@@ -211,13 +189,11 @@ export default {
         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
             }));
           }
@@ -253,11 +229,8 @@ export default {
     },
 
     // 处理源树节点选择
- // 处理源树节点选择
     handleSourceCheck(data, checked, indeterminate) {
-      // 确保树组件已渲染
       if (this.$refs.sourceTree) {
-        // 只获取叶子节点,根据你的业务逻辑调整
         this.selectedSourceNodes = this.$refs.sourceTree.getCheckedNodes(false, true);
         console.log('选中的源节点:', this.selectedSourceNodes);
       }
@@ -265,9 +238,7 @@ export default {
 
     // 处理目标树节点选择
     handleTargetCheck(data, checked, indeterminate) {
-     // 确保树组件已渲染
       if (this.$refs.targetTree) {
-        // 只获取叶子节点,根据你的业务逻辑调整
         this.selectedTargetNodes = this.$refs.targetTree.getCheckedNodes(false, true);
         console.log('选中的目标节点:', this.selectedTargetNodes);
       }
@@ -283,61 +254,68 @@ export default {
     // 关联并退出
     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)
-  };
+    handleLink(exitAfterLink) {
+      if (this.selectedSourceNodes.length === 0 || this.selectedTargetNodes.length === 0) {
+        this.$message.warning('请至少选择一个源节点和一个目标节点');
+        return;
+      }
 
-  // 触发父组件的关联事件,将刷新操作移到回调中
-  this.$emit('link', linkData, () => {
-    this.$message.success('复制成功');
-    
-    // 接口调用成功后执行的操作
-    this.isShowTree = false;
-    setTimeout(() => {
-      this.isShowTree = true;
-    }, 1000);
-    
-    this.isShowRight = false;
-    setTimeout(() => {
-      this.isShowRight = true;
-      this.selectedSourceNodes = [];
-      this.selectedTargetNodes = [];
-      this.checkId = '';
-    }, 1000);
+      // 准备关联数据
+      const linkData = {
+        sourceNodes: this.selectedSourceNodes.map(node => node.id),
+        targetNodes: this.selectedTargetNodes.map(node => node.id)
+      };
+
+      // 触发父组件的关联事件
+      this.$emit('link', linkData, (success) => {
+        if (success) {
+          if (exitAfterLink) {
+            // 复制并退出:关闭弹窗并清空选择
+            this.handleClose();
+          } else {
+            // 复制并继续:清空选择并重新加载树
+            this.reloadTrees();
+          }
+        }
+      });
+    },
 
-    if (exitAfterLink) {
-      this.handleClose();
-    } else {
-      // 清空选择但不关闭弹窗
+    // 重新加载树
+    reloadTrees() {
+      // 先隐藏树
+      this.isShowTree = false;
+      this.isShowRight = false;
+      
+      // 清空选择
       this.clearSelections();
-    }
-  });
-},
-    // 新增清空选择的方法
+      
+      // 延迟显示树,触发重新渲染
+      setTimeout(() => {
+        this.isShowTree = true;
+        this.isShowRight = true;
+        
+        // 确保DOM更新后再操作
+        this.$nextTick(() => {
+          // 如果有默认选中的节点,重新选中
+          if (this.checkId && this.$refs.sourceTree) {
+            // this.$refs.sourceTree.setCheckedKeys([this.checkId]);
+          }
+        });
+      }, 300);
+    },
+
+    // 清空选择
     clearSelections() {
-      // 确保组件已加载
       if (this.$refs.sourceTree) {
-        // 对于懒加载树,先获取所有已勾选的节点ID
         const sourceCheckedKeys = this.$refs.sourceTree.getCheckedKeys();
-        // 逐个取消勾选
         sourceCheckedKeys.forEach(key => {
           this.$refs.sourceTree.setChecked(key, false, true);
         });
@@ -353,8 +331,7 @@ export default {
       // 手动清空数据
       this.selectedSourceNodes = [];
       this.selectedTargetNodes = [];
-
-       this.checkId = ''
+      this.checkId = '';
     }
   }
 };
@@ -437,7 +414,6 @@ export default {
     display: flex;
     justify-content: center;
     padding-top: 15px;
-
   }
 }
 </style>

+ 44 - 26
src/views/manager/wbsinfo/edit.vue

@@ -69,7 +69,7 @@
           </div>
           <div class="flex1 ov-hidden" v-loading="treeLoad">
             <el-scrollbar class="h-100p">
-              <div>
+              <div v-if="isShowTree">
                 <el-tree
                   v-show="!isSearch"
                   class="filter-tree"
@@ -1724,6 +1724,8 @@ export default {
       unitOptions: [], //单位名称
       copyDialogVisible: false, //复制节点弹框
       linkLoading: false, //复制节点加载
+      isShowTree: true, //是否显示树组件
+
 
 
     };
@@ -3403,36 +3405,52 @@ export default {
      handleModalClose() {
       console.log('树形复制弹窗已关闭');
       // 可以在这里处理弹窗关闭后的逻辑
+      this.reloadTree();
+    },
+    reloadTree() {
+      this.isShowTree = false;
+      setTimeout(() => {
+        this.isShowTree = true;
+      }, 100);
     },
 
     // 处理节点关联
-   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
-        })
-      }
-      
+ async handleLinkNodes(linkData, callback) {
+  console.log('准备关联的节点数据:', linkData);
+
+  this.linkLoading = true;
+  try {
+    const { data: res } = await copyNode({ 
+      leftIds: linkData.sourceNodes,
+      rightIds: linkData.targetNodes,
+    });
     
+    console.log(res);
+    if (res.code == 200) {
+      this.$message({
+        type: 'success',
+        message: res.msg
+      });
+      // 调用回调函数通知成功
+      if (callback) callback(true);
+    } else {
+      this.$message({
+        type: 'error',
+        message: res.msg
+      });
+      if (callback) callback(false);
     }
+  } catch (error) {
+    console.error('关联节点失败:', error);
+    // this.$message({
+    //   type: 'error',
+    //   message: '关联失败,请重试'
+    // });
+    if (callback) callback(false);
+  } finally {
+    this.linkLoading = false;
+  }
+}
       //表单排序
 
   },

+ 23 - 6
src/views/system/user.vue

@@ -642,10 +642,16 @@ export default {
                 label: "登录账号",
                 prop: "account",
                 rules: [{
-                  required: true,
-                  message: "请输入登录账号",
-                  trigger: "blur"
-                }],
+                // 动态校验:若手机号为空则账号必填,否则可选(后续会自动用手机号填充)
+                validator: (rule, value, callback) => {
+                  if (!value && !this.form.phone) {
+                    callback(new Error("请输入登录账号")); // 账号和手机号都为空时提示
+                  } else {
+                    callback(); // 有一个不为空则通过
+                  }
+                },
+                trigger: "blur"
+              }],
               },
               {
                 label: '密码',
@@ -1012,6 +1018,7 @@ export default {
       this.form.deptName = this.addData.deptId
       const column = this.findObject(this.option.group, 'deptId')
       column.value = this.addData.deptId
+
       this.$refs.crud.rowAdd()
     },
     addProject(type) {//确定添加参建项目
@@ -1337,7 +1344,10 @@ export default {
     },
     rowSave(row, done, loading) { //新增保存
       console.log('保存',this.fromss.projectAndUserList);
-
+       // 新增时:若未输入登录账号但输入了手机号,则自动用手机号作为登录账号
+      if (!row.account && row.phone) {
+        row.account = row.phone; // 将手机号赋值给登录账号
+      }
         let projectAndUserList = []
         this.fromss.projectAndUserList.forEach(val => {
           if (val.dataInfo.length > 0) {
@@ -1564,7 +1574,14 @@ export default {
             this.form.postId = this.form.postId.split(",");
           }
         });
-      } else {
+      }else if (type === 'add') {
+        // 新增时设置默认账号和密码
+     
+        this.form.password = 'user123456';
+        this.form.password2 = 'user123456';
+      
+      }
+       else {
         this.form.tenantId = this.addData.tenantId
       }
 

+ 172 - 147
src/views/tentative/components/dataMap.vue

@@ -15,54 +15,66 @@
           >保存</el-button
         >
       </div>
-      <div class="right_box">
-        <div>
-          <span>选择表单</span>
-          <el-select
-            v-model="formVal"
-            placeholder="请选择"
-            style="width: 65%; margin-left: 10px"
-            @change="changeform"
-          >
-            <el-option
-              v-for="item in formoptions"
-              :key="item.initTabId"
-              :label="item.tabName"
-              :value="item.initTabId"
-            >
-            </el-option>
-          </el-select>
-        </div>
-        <div>
-          <span>选择元素</span>
-          <el-select
-            v-model="eleVal"
-            placeholder="请选择"
-            style="width: 65%; margin-left: 10px; margin-top: 15px"
-            @change="changeEle"
-            filterable
-          >
-            <el-option
-              v-for="item in eleOptions"
-              :key="item.ekey"
-              :label="item.eName"
-              :value="item.ekey"
-            >
-            </el-option>
-          </el-select>
-        </div>
+      <template v-if="listArr.length>0">
+         <div class="right_box" v-for="(eleItem,index) in listArr" :key="index">
+     
+             <div>
+                <span>选择表单</span>
+                <el-select
+                  v-model="eleItem.initTabId"
+                  placeholder="请选择"
+                  style="width: 65%; margin-left: 10px"
+                  @change="changeform($event,index)"
+                >
+                  <el-option
+                    v-for="item in formoptions"
+                    :key="item.initTabId"
+                    :label="item.tabName"
+                    :value="item.initTabId"
+                  >
+                  </el-option>
+                </el-select>
+              </div>
+              <div>
+                <span>选择元素</span>
+                <el-select
+                  v-model="eleItem.elementId"
+                  placeholder="请选择"
+                  style="width: 65%; margin-left: 10px; margin-top: 15px"
+                  @change="changeEle($event,index)"
+                  filterable
+                >
+                  <el-option
+                    v-for="item in eleOptions"
+                    :key="item.id"
+                    :label="item.eName"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </div>
 
-        <div class="martop20 replace_box">
-          <div style="width: 100%; height: 100%; overflow-y: auto">
-            <el-row class="mb-4">
-              <div class="form_text">
-                <div>表单名称:{{ infoDetail.tabName }}</div>
-                <div>元素字段:{{ infoDetail.elementName }}</div>
+              <div class="martop10 replace_box">
+                <div style="width: 100%; height: 100%; overflow-y: auto">
+                  <el-row class="mb-4">
+                    <div class="form_text">
+                      <div>表单名称:{{ eleItem.tabName }}</div>
+                      <div>元素字段:{{ eleItem.elementName }}</div>
+                    </div>
+                  </el-row>
+                  <div class="martop20">
+                    <el-button type="primary" size="small" @click="addList(index)">新增元素</el-button>
+                    <el-button type="danger"  size="small" v-if="index>0" @click="delList(index,eleItem)">删除元素</el-button>
+                  </div>
+                
+                </div>
               </div>
-            </el-row>
-          </div>
-        </div>
+       
       </div>
+      </template>
+      <el-empty description="暂无数据" v-else ></el-empty>
+
+     
     </div>
   </div>
 </template>
@@ -72,8 +84,9 @@ import {
   getLinkTablist,
   getLinkElelist,
   getLinkDetail,
-  saveLinkDetail,
+  removeLinkData,
   getLinkData,
+  saveLinkDetail 
 } from "@/api/tentative/testcollect";
 import Vue from "vue";
 import Split from "split.js";
@@ -83,11 +96,11 @@ export default {
       formoptions: [],
       eleOptions: [],
       heights: "",
-      formVal: "",
+      initTabId: "",
       formpid: "",
       formname: "",
       classId: "",
-      eleVal: "",
+      elementId: "",
       eleId: "",
       clickKeyname: "",
       infoDetail: {
@@ -105,55 +118,16 @@ export default {
         trialTabName: "",
       },
       linkedData: [], //已经绑定的数组
+      listArr: [
+      {}
+],
     };
   },
   watch: {
-    curItem: {
-      handler(newVal, oldVal) {
-        if (
-          this.curItem.elementId &&
-          this.curItem.elementKey &&
-          this.curItem.htmlKeyName &&
-          this.curItem.trialTabId &&
-          this.curItem.trialTabName
-        ) {
-          const newItem = Object.assign({}, newVal);
-          const isContained = this.containsObject(
-            this.clickArr,
-            "htmlKeyName",
-            newItem.htmlKeyName
-          );
-        
-          
-          if (isContained) {
-            this.clickArr.forEach((ele) => {
-              if (ele.htmlKeyName === newItem.htmlKeyName) {
-                ele.elementId = newItem.elementId;
-                ele.elementKey = newItem.elementKey;
-                ele.htmlKeyName = newItem.htmlKeyName;
-                ele.trialTabId = newItem.trialTabId;
-                ele.trialTabName = newItem.trialTabName;
-              }
-            });
-          } else {
-            this.clickArr.push(newItem);
-          }
-        }
-        // 在这里执行你想要的操作
-      },
-      deep: true, // 指定为深度监听,
-    },
+ 
   },
   methods: {
-    // 判断数组中是否包含目标对象的函数
-    containsObject(array, keyName, targetValue) {
-      for (let i = 0; i < array.length; i++) {
-        if (array[i][keyName] === targetValue) {
-          return true;
-        }
-      }
-      return false;
-    },
+  
     async getExcelHtmlCol(classId, form) {
       //获取excel模板
       const { data: res } = await getLinkHtml({ classId });
@@ -178,12 +152,7 @@ export default {
           },
           template: res.data,
           methods: {
-            //改变表单数据
-            //  setFormData(obj) {
-            //         this.formData = obj
-            //         _that.setFormData2(obj)
-            //         console.log(this.formData,'this.formData1111111');
-            //  },
+     
             contextmenuClick() {},
             getInformation() {},
             formUploadSuccess() {},
@@ -262,9 +231,12 @@ export default {
             }
           });
         }
-        this.clickKeyname =
-          target.getAttribute("keyname") || target1.getAttribute("keyname");
+        this.clickKeyname = target.getAttribute("keyname") || target1.getAttribute("keyname");
           this.$set(this.curItem, "htmlKeyName", this.clickKeyname);
+          this.listArr.forEach((ele)=>{
+            ele.htmlKeyName= this.clickKeyname
+        
+          })
         await this.getInfoDEtail();   
 
      
@@ -314,25 +286,25 @@ export default {
       }
     },
     //选择表单获
-    changeform(val) {
+    changeform(val,index) {
       this.getEleList(val);
       this.formoptions.forEach((ele) => {
         if (ele.initTabId === val) {
-          this.formpid = ele.pkeyId;
-          this.formname = ele.initTableName;
-          this.infoDetail.tabName = ele.tabName;
-          this.$set(this.curItem, "trialTabId", ele.pkeyId);
-          this.$set(this.curItem, "trialTabName", ele.initTableName);
+       
+   
+          this.listArr[index].trialTabId= ele.pkeyId
+          this.listArr[index].trialTabName= ele.initTableName
+          this.listArr[index].tabName= ele.tabName
         }
       });
     },
-    changeEle(val) {
+    changeEle(val,index) {
       this.eleOptions.forEach((ele) => {
-        if (ele.ekey === val) {
-          this.eleId = ele.id;
-          this.infoDetail.elementName = ele.eName;
-          this.$set(this.curItem, "elementKey", val);
-          this.$set(this.curItem, "elementId", ele.id);
+        if (ele.id === val) {
+       
+       
+          this.listArr[index].elementName= ele.eName
+          this.listArr[index].elementId= ele.id
         }
       });
     },
@@ -341,7 +313,7 @@ export default {
       const { data: res } = await getLinkElelist({
         id: val,
       });
-      console.log(res);
+
       if (res.code === 200) {
         this.eleOptions = res.data;
       } else {
@@ -355,53 +327,56 @@ export default {
         keyName: this.clickKeyname,
       });
       if (res.code === 200) {
-        this.infoDetail = res.data;
+        this.listArr = res.data.length>0?res.data:[{}]
+          this.listArr.forEach((ele)=>{
+            ele.htmlKeyName= this.clickKeyname
+        
+          })
+        this.formoptions.forEach((ele)=>{
+         this.listArr.forEach(async(eleItem,eleIndex)=>{
+          if(ele.pkeyId===eleItem.trialTabId){
+            eleItem.initTabId=ele.initTabId
+            await this.getEleList(ele.initTabId)
+           this.changeEle(eleItem.elementId,eleIndex)
 
-        if (this.infoDetail && this.infoDetail.id) {
-          this.formoptions.forEach((ele) => {
-            if (ele.tabName === this.infoDetail.tabName) {
-              this.formVal = ele.initTabId;
-              this.$set(this.curItem, "trialTabName", ele.initTableName);
-              this.$set(this.curItem, "trialTabId", ele.pkeyId);
-            }
-          });
-          await this.getEleList(this.formVal);
-          this.eleOptions.forEach((ele) => {
-            if (ele.eName === this.infoDetail.elementName) {
-              this.eleVal = ele.ekey;
-              // this.curItem.elementId=ele.id
-              // this.curItem.elementKey=ele.ekey
-              this.$set(this.curItem, "elementId", ele.id);
-              this.$set(this.curItem, "elementKey", ele.ekey);
-            }
-          });
-        } else {
-          this.infoDetail = {};
-          this.formVal = "";
-          this.eleVal = "";
-          this.curItem = {};
-          // this.curItem.htmlKeyName=this.clickKeyname
-          this.$set(this.curItem, "htmlKeyName", this.clickKeyname);
-        }
+            
+          }
+
+         })
+        })
+      
+       
+        
+
+    
       } else {
-        this.infoDetail = {};
-      }
+          this.listArr = [{}]
+
+        }
     },
 
     //映射数据保存
     async saveInfo() {
-      console.log(this.clickArr, "this.clickArr");
-      if (this.clickArr.length === 0) {
+  
+      
+      if (this.listArr.length === 0) {
         this.$message.warning("请进行数据映射配置");
         return;
       }
-
+    const isValid = this.listArr.every(item => 
+    item.elementId && item.initTabId
+  );
+  
+  if (!isValid) {
+    this.$message.warning("请确保选择了所有表单和元素字段!");
+    return;
+  }
       this.saveLoaing = true;
       const { data: res } = await saveLinkDetail({
         classId: this.classId,
-        reflectionBeanList: this.clickArr,
+        reflectionBeanList: this.listArr,
       });
-      console.log(res);
+
       this.saveLoaing = false;
       if (res.code === 200) {
         this.$message({
@@ -414,6 +389,53 @@ export default {
         this.curItem={}
       }
     },
+    addList(index){
+        const currentItem = this.listArr[index];
+        if (!currentItem.elementId || !currentItem.initTabId) {
+          this.$message({
+            type: "warning",
+            message: "请选择元素和表单",
+          });
+          return; // 不满足条件则不添加
+        }
+       this.listArr.splice(index + 1, 0, { 
+          htmlKeyName: this.clickKeyname
+        ,
+
+          tabName: '',
+          elementName: ''
+        });
+    },
+    delList(index,item){
+   
+      if(!item.id){
+        this.listArr.splice(index,1)
+        return
+      }else{
+         this.$confirm('删除后,数据将无法恢复,是否确认删除!', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+  
+            removeLinkData({id:item.id}).then((res) => {
+              if(res.data.code==200){
+                this.$message.success(res.data.msg);
+              
+                this.listArr.splice(index, 1);
+              this.getLinkedData();
+              this.getExcelHtmlCol(this.classId, this.linkedData); //获取excel模板
+              }else{
+                this.$message.error(res.data.msg);
+              }
+            });
+          }).catch(() => {
+            this.$message.info('已取消删除');
+          })
+      }
+
+      
+    }
   },
   async created() {
     this.classId = this.$route.query.classId;
@@ -472,4 +494,7 @@ export default {
     background-repeat: no-repeat;
     background-position: center;
 }
+.right_box{
+  margin-bottom: 15px;
+}
 </style>

Vissa filer visades inte eftersom för många filer har ändrats