Răsfoiți Sursa

元素库修改

duy 10 luni în urmă
părinte
comite
2d0de41806

+ 16 - 0
src/api/modules/exctab/exceltab.js

@@ -222,6 +222,14 @@ export default {
             params: form,
         });
     },
+    //元素库获取表
+    async getNodeTables(form) {
+        return HcApi({
+            url: "/api/blade-manager/wbsFormElement/get-node-tables",
+            method: "get",
+            params: form,
+        });
+    },
     //保存关联
     async saveRelation(form) {
         return HcApi({
@@ -230,6 +238,14 @@ export default {
             data: form,
         });
     },
+    //关联
+    async saveLinkTableInfo(form) {
+        return HcApi({
+            url: "/api/blade-manager/exceltab/save-link-tableInfo",
+            method: "get",
+            params: form,
+        });
+    },
     //取消关联
     async cancelRelation(form) {
         return HcApi({

+ 74 - 65
src/utils/tools.js

@@ -1,38 +1,37 @@
-import { nextTick } from 'vue'
-import { encode } from 'js-base64'
-import { fullDrawer } from 'hc-vue3-ui'
-import { getArrValue, getObjValue, isNullES } from 'js-fast-way'
-import { getBizDictionary, getDictionary } from '~api/other'
-
+import { nextTick } from "vue";
+import { encode } from "js-base64";
+import { fullDrawer } from "hc-vue3-ui";
+import { getArrValue, getObjValue, isNullES } from "js-fast-way";
+import { getBizDictionary, getDictionary } from "~api/other";
 
 // 跳转到PDF页面
 export const toPdfPage = (url) => {
     if (isNullES(url)) {
-        window.$message.warning('pdf地址为空')
-        return
+        window.$message.warning("pdf地址为空");
+        return;
     }
     fullDrawer({
-        type: 'url',
-        content: '/#/pdf?code=' + encode(url),
-    }).then()
-}
+        type: "url",
+        content: "/#/pdf?code=" + encode(url),
+    }).then();
+};
 
 //设置分割滑块
 export const setSplit = (ids, data) => {
     //配置参考: https://split.js.org/#/?direction=vertical&snapOffset=0
     nextTick(() => {
         setTimeout(() => {
-            window.$split(ids, data)
-        }, 300)
-    }).then()
-}
+            window.$split(ids, data);
+        }, 300);
+    }).then();
+};
 
 //刷新页面
 export const reloadPage = () => {
     setTimeout(() => {
-        window.location.reload()
-    }, 1500)
-}
+        window.location.reload();
+    }, 1500);
+};
 
 /**
  * 效验是否为数字或小数的数字
@@ -41,83 +40,93 @@ export const reloadPage = () => {
  * @returns {boolean}
  */
 export const isNumberReg = (text, lose = true) => {
-    let pattern = lose ? /^-?\d+(.\d{1,2})?$/ : /^[0-9]+([.]{1}[0-9]+){0,1}$/
-    return pattern.test(text)
-}
+    let pattern = lose ? /^-?\d+(.\d{1,2})?$/ : /^[0-9]+([.]{1}[0-9]+){0,1}$/;
+    return pattern.test(text);
+};
 
 //获取字典的名称
 export const getDictionaryName = (arr, val) => {
-    if (isNullES(val)) return ''
-    const data = arr.find(item => {
-        return Number(item.value) === Number(val)
-    })
-    const res = getObjValue(data)
-    return res.label || ''
-}
-
+    if (isNullES(val)) return "";
+    const data = arr.find((item) => {
+        return Number(item.value) === Number(val);
+    });
+    const res = getObjValue(data);
+    return res.label || "";
+};
+//获取字典的dictKey
+export const getDictionaryVal = (arr, val) => {
+    if (isNullES(val)) return "";
+    const data = arr.find((item) => {
+        return item.label === val;
+    });
+    const res = getObjValue(data);
+    return res.value || "";
+};
 //获取字典数据
 export const getDictionaryData = async (code, biz = false) => {
-    let res = []
+    let res = [];
     if (biz) {
-        const { data } = await getBizDictionary({ code: code })
-        res = getArrValue(data)
+        const { data } = await getBizDictionary({ code: code });
+        res = getArrValue(data);
     } else {
-        const { data } = await getDictionary({ code: code })
-        res = getArrValue(data)
+        const { data } = await getDictionary({ code: code });
+        res = getArrValue(data);
     }
     //处理数据
-    let newArr = []
+    let newArr = [];
     for (let i = 0; i < res.length; i++) {
-        const val = isNumberReg(res[i]['dictKey']) ? Number(res[i]['dictKey']) : res[i]['dictKey']
+        const val = isNumberReg(res[i]["dictKey"])
+            ? Number(res[i]["dictKey"])
+            : res[i]["dictKey"];
         newArr.push({
-            label: res[i]['dictValue'],
+            label: res[i]["dictValue"],
             value: val,
-        })
+        });
     }
-    return newArr
-}
+    return newArr;
+};
 
 //操作确认
 export const actionConfirm = (cbk) => {
-    window?.$messageBox?.alert('请确认要执行此操作吗?', '操作确认', {
+    window?.$messageBox?.alert("请确认要执行此操作吗?", "操作确认", {
         showCancelButton: true,
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning',
+        confirmButtonText: "确认",
+        cancelButtonText: "取消",
+        type: "warning",
         callback: (action) => {
-            if (action === 'confirm') {
-                cbk()
+            if (action === "confirm") {
+                cbk();
             }
         },
-    })
-}
+    });
+};
 
 //动态加载线上js文件
 export const addDocumentsJs = () => {
     return new Promise((resolve) => {
-        const script = document.createElement('script')
-        script.src = 'http://47.110.251.215:6831/web-apps/apps/api/documents/api.js'
-        script.type = 'text/javascript'
-        document.head.appendChild(script)
+        const script = document.createElement("script");
+        script.src =
+            "http://47.110.251.215:6831/web-apps/apps/api/documents/api.js";
+        script.type = "text/javascript";
+        document.head.appendChild(script);
         script.onload = () => {
-            resolve()
-        }
-    })
-}
+            resolve();
+        };
+    });
+};
 
 //判断是否为网址
 export const isPathUrl = (path) => {
-    return /^(https?:|mailto:|tel:)/.test(path)
-}
-
+    return /^(https?:|mailto:|tel:)/.test(path);
+};
 
 //获取当前域名
 export const getTopUrl = () => {
-    return window.location.href.split('/#/')[0]
-}
+    return window.location.href.split("/#/")[0];
+};
 
 //设置系统名称
 export const setAppName = (name) => {
-    const title = window.document.title
-    window.document.title = `${title}${name ? ' - ' + name : ''}`
-}
+    const title = window.document.title;
+    window.document.title = `${title}${name ? " - " + name : ""}`;
+};

+ 104 - 74
src/views/desk/wbs/element-lib.vue

@@ -1,17 +1,32 @@
 <template>
-    <hc-drawer v-model="isShow" to-id="hc-main-box" is-close @close="drawerClose">
+    <hc-drawer
+        v-model="isShow"
+        to-id="hc-main-box"
+        is-close
+        @close="drawerClose"
+    >
         <hc-body split :options="{ sizes: [14, 96] }">
             <template #left>
                 <hc-card scrollbar>
-                    <hc-data-tree :h-props="treeProps" :datas="treeData" tree-key="id" @node-tap="treeNodeTap">
-                        <template #default="{ node }">{{ node.label }}</template>
+                    <hc-data-tree
+                        :h-props="treeProps"
+                        :datas="treeData"
+                        tree-key="id"
+                        @node-tap="treeNodeTap"
+                    >
+                        <template #default="{ node }">{{
+                            node.label
+                        }}</template>
                     </hc-data-tree>
                 </hc-card>
             </template>
             <hc-card>
                 <template #header>
                     <div class="w-400px">
-                        <hc-search-input v-model="searchForm.wbsName" @search="searchClick" />
+                        <hc-search-input
+                            v-model="searchForm.titleName"
+                            @search="searchClick"
+                        />
                     </div>
                 </template>
                 <template #extra>
@@ -20,13 +35,24 @@
                     <!-- el-button hc-btn type="warning">排序</el-button -->
                 </template>
                 <hc-table
-                    :column="tableColumn" :datas="tableData" :loading="tableLoading" :index-style="{ width: 60 }"
-                    is-check :check-style="{ width: 29 }" @selection-change="tableCheckChange"
+                    :column="tableColumn"
+                    :datas="tableData"
+                    :loading="tableLoading"
+                    :index-style="{ width: 60 }"
+                    is-check
+                    :check-style="{ width: 29 }"
+                    @selection-change="tableCheckChange"
                 >
                     <template #action="{ row }">
-                        <el-link type="primary" @click="rowNodeClick(row)">分配节点</el-link>
-                        <el-link type="warning" @click="rowFormulaClick(row)">公式配置</el-link>
-                        <el-link type="danger" @click="rowEditClick(row)">编辑元素</el-link>
+                        <el-link type="primary" @click="rowNodeClick(row)"
+                            >分配节点</el-link
+                        >
+                        <el-link type="warning" @click="rowFormulaClick(row)"
+                            >公式配置</el-link
+                        >
+                        <el-link type="danger" @click="rowEditClick(row)"
+                            >编辑元素</el-link
+                        >
                     </template>
                 </hc-table>
                 <template #action>
@@ -34,129 +60,133 @@
                 </template>
             </hc-card>
         </hc-body>
+
+        <!-- 分配节点 -->
+        <!-- 关联公共WBS模板 -->
+        <HcWebTemplate v-model="webTemplateShow" :info="webTemplateInfo" />
     </hc-drawer>
 </template>
 
 <script setup>
-import { ref, watch } from 'vue'
-import { HcDelMsg } from 'hc-vue3-ui'
-import { getArrValue, getObjValue, isNullES } from 'js-fast-way'
-import mainApi from '~api/desk/wbs'
+import { ref, watch } from "vue";
+import { HcDelMsg } from "hc-vue3-ui";
+import { getArrValue, getObjValue, isNullES } from "js-fast-way";
+import mainApi from "~api/desk/wbs";
+import HcWebTemplate from "./web-temp.vue";
 
 //事件
-const emit = defineEmits(['close'])
+const emit = defineEmits(["close"]);
 
 //双向绑定
-const isShow = defineModel('modelValue', {
+const isShow = defineModel("modelValue", {
     default: false,
-})
+});
 
 //监听显示
 watch(isShow, (val) => {
-    if (val) getDataApi()
-})
+    if (val) getDataApi();
+});
 
 //处理相关数据
 const getDataApi = () => {
-    getTreeData()
-}
+    getTreeData();
+};
 
 //树配置
-const treeProps = { label: 'title' }
+const treeProps = { label: "title" };
 
 //获取树接口
-const treeData = ref([])
+const treeData = ref([]);
 const getTreeData = async () => {
     const { data } = await mainApi.tabTypeLazyTreeAll({
-        parentId: '12345678910',
+        parentId: "12345678910",
         current: 1,
         size: 1000,
-    })
-    treeData.value = getArrValue(data?.records)
-}
+    });
+    treeData.value = getArrValue(data?.records);
+};
 
 //树节点被点击
-const nodeInfo = ref({})
+const nodeInfo = ref({});
 const treeNodeTap = ({ data }) => {
-    nodeInfo.value = data
-    searchForm.value.parentId = data.id
-    searchClick()
-}
+    nodeInfo.value = data;
+    searchForm.value.parentId = data.id;
+    searchClick();
+};
 
 //搜索表单
-const searchForm = ref({ current: 1, size: 30, total: 0 })
+const searchForm = ref({ current: 1, size: 30, total: 0 });
 
 //搜索
 const searchClick = () => {
-    const { parentId } = searchForm.value
+    const { parentId } = searchForm.value;
     if (isNullES(parentId)) {
-        window?.$message?.warning('请先在左侧点击一个节点')
-        return
+        window?.$message?.warning("请先在左侧点击一个节点");
+        return;
     }
-    searchForm.value.current = 1
-    getTableData()
-}
+    searchForm.value.current = 1;
+    getTableData();
+};
 
 //分页
 const pageChange = ({ current, size }) => {
-    const { parentId } = searchForm.value
+    const { parentId } = searchForm.value;
     if (isNullES(parentId)) {
-        window?.$message?.warning('请先在左侧点击一个节点')
-        return
+        window?.$message?.warning("请先在左侧点击一个节点");
+        return;
     }
-    searchForm.value.current = current
-    searchForm.value.size = size
-    getTableData()
-}
+    searchForm.value.current = current;
+    searchForm.value.size = size;
+    getTableData();
+};
 
 //表格数据
-const tableData = ref([])
+const tableData = ref([]);
 const tableColumn = ref([
-    { key: 'title', name: '名称' },
-    { key: 'elementTotal', name: '总量', width: 80, align: 'center' },
-    { key: 'tabOwner', name: '所属方', width: 140, align: 'center' },
-    { key: 'fillRate', name: '填报率', width: 80, align: 'center' },
-    { key: 'action', name: '操作', width: 220, align: 'center' },
-])
+    { key: "title", name: "名称" },
+    { key: "elementTotal", name: "总量", width: 80, align: "center" },
+    { key: "tabOwner", name: "所属方", width: 140, align: "center" },
+    { key: "fillRate", name: "填报率", width: 80, align: "center" },
+    { key: "action", name: "操作", width: 220, align: "center" },
+]);
 
 //获取表格数据
-const tableLoading = ref(false)
+const tableLoading = ref(false);
 const getTableData = async () => {
-    tableData.value = []
-    tableLoading.value = true
+    tableData.value = [];
+    tableLoading.value = true;
     const { data } = await mainApi.tabTypeLazyTreeAll({
         ...searchForm.value,
         total: null,
-    })
-    tableLoading.value = false
-    tableData.value = getArrValue(data?.records)
-    searchForm.value.total = data?.total || 0
-}
+    });
+    tableLoading.value = false;
+    tableData.value = getArrValue(data?.records);
+    searchForm.value.total = data?.total || 0;
+};
 
 //表格被选择
-const tableCheckKeys = ref([])
+const tableCheckKeys = ref([]);
 const tableCheckChange = (rows) => {
-    tableCheckKeys.value = rows
-}
+    tableCheckKeys.value = rows;
+};
 
 //分配节点
+const webTemplateShow = ref(false);
+const webTemplateInfo = ref({});
 const rowNodeClick = (row) => {
-
-}
+    webTemplateInfo.value = row;
+    webTemplateShow.value = true;
+};
 
 //公式配置
-const rowFormulaClick = (row) => {
-
-}
+const rowFormulaClick = (row) => {};
 
 //编辑元素
-const rowEditClick = (row) => {
-
-}
+const rowEditClick = (row) => {};
 
 //关闭抽屉
 const drawerClose = () => {
-    isShow.value = false
-    emit('close')
-}
+    isShow.value = false;
+    emit("close");
+};
 </script>

+ 532 - 0
src/views/desk/wbs/web-temp.vue

@@ -0,0 +1,532 @@
+<template>
+    <hc-dialog
+        v-model="isShow"
+        ui="hc-exctab-element-web-temp"
+        title="关联公共WBS模板"
+        widths="1200px"
+        :padding="false"
+        is-table
+        @close="dialogClose"
+    >
+        <div class="hc-exctab-element-web-temp-box hc-flex h-full">
+            <div class="left h-full flex-1">
+                <hc-card scrollbar>
+                    <template #header>
+                        <div class="w-full">
+                            <el-select
+                                v-model="formModelName"
+                                filterable
+                                block
+                                placeholder="请选择清表模板"
+                                @change="formNameChange"
+                            >
+                                <el-option
+                                    v-for="item in excelData"
+                                    :key="item.id"
+                                    :label="item.wbsName"
+                                    :value="item.id"
+                                />
+                            </el-select>
+                        </div>
+                    </template>
+                    <template #search>
+                        <div v-if="formModelName" class="w-full">
+                            <hc-search-input
+                                v-model="filterText"
+                                placeholder="输入关键字搜索"
+                                @search="treeFilterClick"
+                            />
+                        </div>
+                    </template>
+                    <template v-if="formModelName">
+                        <template v-if="isTreeMode === 1">
+                            <hc-lazy-tree
+                                v-if="isShow"
+                                ref="treeRef1"
+                                :h-props="treeProps"
+                                tree-key="id"
+                                :show-checkbox="tabsKey === '2'"
+                                check-strictly
+                                :default-checked-keys="treeNodeIds"
+                                @load="treeLoadNode"
+                                @node-tap="treeNodeTap"
+                            >
+                                <template #name="{ data }">
+                                    <span class="text-16px font-400">{{
+                                        data.title
+                                    }}</span>
+                                </template>
+                            </hc-lazy-tree>
+                        </template>
+                        <template v-if="isTreeMode === 2">
+                            <hc-data-tree
+                                ref="treeRef2"
+                                :h-props="treeProps"
+                                tree-key="id"
+                                :show-checkbox="tabsKey === '2'"
+                                :datas="treeData"
+                                check-strictly
+                                :default-checked-keys="treeNodeIds"
+                                @node-tap="treeNodeTap"
+                            >
+                                <template #name="{ data }">
+                                    <span class="text-16px font-400">{{
+                                        data.title
+                                    }}</span>
+                                </template>
+                            </hc-data-tree>
+                        </template>
+                    </template>
+                </hc-card>
+            </div>
+            <div class="right h-full flex-1">
+                <hc-tab-card
+                    :tabs="tabsData"
+                    :tab-key="tabsKey"
+                    @change="tabsChange"
+                >
+                    <template v-if="tabsKey === '1'">
+                        <hc-table
+                            :column="nodeTableColumn"
+                            :datas="nodeTableData"
+                            :is-index="false"
+                        >
+                            <template #action="{ row }">
+                                <el-link
+                                    v-if="row.checknd"
+                                    type="warning"
+                                    @click="rowUnlinkClick(row)"
+                                    >取消关联</el-link
+                                >
+                                <el-link
+                                    v-else
+                                    type="primary"
+                                    @click="rowLinkClick(row)"
+                                    >选择关联</el-link
+                                >
+                            </template>
+                        </hc-table>
+                    </template>
+                    <div v-if="tabsKey === '2'">
+                        <el-form
+                            ref="formRef"
+                            :model="formModel"
+                            :rules="formRules"
+                            label-position="top"
+                            label-width="auto"
+                            size="large"
+                        >
+                            <el-form-item label="元素表名:" prop="nodeName">
+                                <el-input
+                                    v-model="formModel.nodeName"
+                                    type="textarea"
+                                    placeholder="请输入元素表名"
+                                />
+                            </el-form-item>
+                            <el-form-item label="清表类型:" prop="tableType">
+                                <el-select
+                                    v-model="formModel.tableType"
+                                    filterable
+                                    block
+                                    placeholder="请选择清表类型"
+                                >
+                                    <el-option
+                                        v-for="item in excelTypeData"
+                                        :key="item.value"
+                                        :label="item.label"
+                                        :value="item.value"
+                                    />
+                                </el-select>
+                            </el-form-item>
+                            <el-form-item label="所属方:" prop="tableOwner">
+                                <el-select
+                                    v-model="formModel.tableOwner"
+                                    filterable
+                                    block
+                                    placeholder="请选择所属方"
+                                >
+                                    <el-option
+                                        v-for="item in ownerTypeList"
+                                        :key="item.value"
+                                        :label="item.label"
+                                        :value="item.value"
+                                    />
+                                </el-select>
+                            </el-form-item>
+                        </el-form>
+                    </div>
+                </hc-tab-card>
+            </div>
+        </div>
+        <template #footer>
+            <el-button hc-btn @click="dialogClose">取消</el-button>
+            <el-button
+                hc-btn
+                type="primary"
+                :loading="submitLoading"
+                @click="dialogSubmit"
+                >提交</el-button
+            >
+        </template>
+    </hc-dialog>
+</template>
+
+<script setup>
+import { ref, watch } from "vue";
+import { getDictionaryData, getDictionaryVal } from "~uti/tools";
+import { formValidate, getArrValue, getObjValue, isNullES } from "js-fast-way";
+import mainApi from "~api/exctab/exceltab";
+
+const props = defineProps({
+    info: {
+        type: Object,
+        default: () => ({}),
+    },
+    data: {
+        type: Array,
+        default: () => [],
+    },
+});
+
+//事件
+const emit = defineEmits(["finish", "close"]);
+
+//双向绑定
+const isShow = defineModel("modelValue", {
+    default: false,
+});
+
+//监听数据
+const dataInfo = ref(props.info);
+watch(
+    () => props.info,
+    (data) => {
+        dataInfo.value = getObjValue(data);
+    },
+    { immediate: true, deep: true }
+);
+
+//监听数据
+const datas = ref(props.data);
+watch(
+    () => props.data,
+    (data) => {
+        datas.value = getArrValue(data);
+    },
+    { immediate: true, deep: true }
+);
+
+//监听显示
+watch(isShow, (val) => {
+    if (val) getInfoData();
+});
+
+//获取数据详情
+const tableData = ref([]);
+const formModelName = ref("");
+const getInfoData = () => {
+    getWbsTypeList();
+    //处理元素字段信息
+    const data = getArrValue(datas.value);
+    for (let i = 0; i < data.length; i++) {
+        data[i].eName = data[i].textInfo;
+        data[i].eType = data[i].textElementType;
+        data[i].eAllowDeviation = data[i].textDeviation;
+    }
+    tableData.value = data;
+};
+
+//获取清表模板信息
+const excelData = ref([]);
+const getWbsTypeList = async () => {
+    const { data } = await mainApi.getWbsTypeList({ wbstype: 1 });
+    excelData.value = getArrValue(data);
+};
+
+//清表模板被改变
+const formNameChange = () => {
+    isTreeMode.value = 5;
+    filterText.value = "";
+    nodeTableData.value = [];
+    formModel.value = {};
+    tabsKey.value = "1";
+    nodeInfo.value = {};
+    treeNodeIds.value = [];
+    treeData.value = [];
+    setTimeout(() => {
+        isTreeMode.value = 1;
+    }, 200);
+};
+
+//树配置
+const treeRef1 = ref(null);
+const treeRef2 = ref(null);
+const treeProps = {
+    label: "title",
+    children: "children",
+    isLeaf: (item) => {
+        const isExistForm = Number(item.isExistForm);
+        if (item.hasChildren && isExistForm !== 1) {
+            return false;
+        } else if (item.hasChildren && isExistForm === 1) {
+            return true;
+        } else {
+            return true;
+        }
+    },
+};
+
+//筛选搜索
+const filterText = ref("");
+const isTreeMode = ref(1); //1懒加载,2全加载
+const treeFilterClick = async () => {
+    if (isNullES(filterText.value)) {
+        isTreeMode.value = 1;
+    } else {
+        isTreeMode.value = 2;
+        await getTreeAllData();
+        treeRef2.value?.treeRef?.filter(filterText.value);
+    }
+};
+
+//懒加载树
+const treeLoadNode = async ({ item, level }, resolve) => {
+    const parentId = level === 0 ? 0 : item.id;
+    const { data } = await mainApi.getLazyTree({
+        parentId: parentId,
+        wbsId: formModelName.value,
+        wbsType: "1",
+    });
+    const res = getArrValue(data);
+    res.forEach((item) => {
+        item.isExistForm = !!item.isExistForm;
+    });
+    resolve(res);
+};
+
+//全加载树
+const treeData = ref([]);
+const getTreeAllData = async () => {
+    const { data } = await mainApi.getAllTree({
+        tenantId: "000000",
+        type: 1,
+        wbsId: formModelName.value,
+    });
+    treeData.value = getArrValue(data);
+};
+
+//树节点被点击
+const nodeInfo = ref({});
+const treeNodeIds = ref([]);
+const treeNodeTap = ({ data, keys }) => {
+    nodeInfo.value = data;
+    treeNodeIds.value = keys;
+    getNodeTable(data.id);
+};
+
+//获取清表信息
+const nodeTableColumn = [
+    { key: "tableName", name: "已有元素表名称" },
+    { key: "action", name: "操作", width: 90, align: "center" },
+];
+const nodeTableData = ref([]);
+const getNodeTable = async (id) => {
+    const { data } = await mainApi.getNodeTables({
+        nodeId: nodeInfo.value.id,
+        excelTabId: dataInfo.value.id,
+    });
+    const res = getArrValue(data);
+    for (let i = 0; i < res.length; i++) {
+        res[i].checknd = Number(res[i].isLinkTable) === 2;
+    }
+    nodeTableData.value = res;
+};
+
+//选择关联
+const rowLinkClick = async (row) => {
+    const { isRes } = await mainApi.saveLinkTableInfo({
+        type: 1,
+        linkids: row.id,
+        tabId: dataInfo.value.id,
+        wbsId: formModelName.value,
+    });
+    if (!isRes) return;
+    row.checknd = true;
+    row.isLinkTable = 2;
+};
+
+//取消关联
+const rowUnlinkClick = async (row) => {
+    const { isRes } = await mainApi.cancelRelation({
+        type: 4,
+        linkids: row.id,
+        tabId: dataInfo.value.id,
+        wbsId: formModelName.value,
+    });
+    if (!isRes) return;
+    row.checknd = false;
+    row.isLinkTable = 1;
+};
+
+//选项卡
+const tabsKey = ref("1");
+const tabsData = ref([
+    { key: "1", name: "关联元素表" },
+    { key: "2", name: "新增元素表" },
+]);
+const tabsChange = async ({ key }) => {
+    tabsKey.value = key;
+    const form = getObjValue(dataInfo.value);
+    const tree = getObjValue(nodeInfo.value);
+    console.log(form, "form");
+    console.log(tree, "tree");
+
+    //默认表单数据
+    formModel.value = {
+        wbsId: formModelName.value,
+        parentId: tree.id,
+        nodeName: form.title,
+        excelTabId: form.id,
+        elementList: tableData.value,
+    };
+    //判断当前选项卡
+    if (key === "1") {
+        formModel.value.submitStatus = 1;
+        getNodeTable(nodeInfo.value.id).then();
+    } else if (key === "2") {
+        //默认表单数据
+        formModel.value.submitStatus = 2;
+        //获取下拉数据
+        excelTypeData.value = await getDictionaryData("table_type");
+        ownerTypeList.value = await getDictionaryData("owner_type");
+        //设置默认类型
+        formModel.value.tableType = getDictionaryVal(
+            excelTypeData.value,
+            form.tabType
+        );
+        formModel.value.tableOwner = getDictionaryVal(
+            ownerTypeList.value,
+            form.tabOwner
+        );
+    }
+};
+
+const excelTypeData = ref([]);
+const ownerTypeList = ref([]);
+
+//基础表单
+const formRef = ref(null);
+const formModel = ref({});
+const formRules = {
+    nodeName: {
+        required: true,
+        trigger: "blur",
+        message: "请输入元素表名",
+    },
+    tableType: {
+        required: true,
+        trigger: "blur",
+        message: "请选择清表类型",
+    },
+    tableOwner: {
+        required: true,
+        trigger: "blur",
+        message: "请选择所属方",
+    },
+};
+
+//提交
+const submitLoading = ref(false);
+const dialogSubmit = async () => {
+    const isForm = await formValidate(formRef.value);
+    if (!isForm) return false;
+    if (tabsKey.value === "1") {
+        submitLoading.value = true;
+        const { isRes } = await mainApi.submitExcelElement(formModel.value);
+        submitLoading.value = false;
+        if (!isRes) return;
+        window.$message.success("操作成功");
+        emit("finish");
+    } else {
+        submitLoading.value = true;
+        let nodeIds1, nodeIds2;
+        if (treeRef1.value) {
+            nodeIds1 = treeRef1.value?.treeRef?.getCheckedKeys();
+        }
+        if (treeRef1.value) {
+            nodeIds2 = treeRef2.value?.treeRef?.getCheckedKeys();
+        }
+        const selectNodeIds = uniqueArr(nodeIds1, nodeIds2);
+        if (selectNodeIds.length <= 0) {
+            window?.$message.warning("至少勾选一个节点");
+            submitLoading.value = false;
+            return;
+        }
+        formModel.value.nodeIds = selectNodeIds;
+        const { isRes } = await mainApi.submitExcelElement(formModel.value);
+        submitLoading.value = false;
+        if (!isRes) return;
+        window.$message.success("操作成功");
+        dialogClose();
+        emit("finish");
+    }
+};
+
+//数组去重
+const uniqueArr = (arr1, arr2) => {
+    //合并两个数组
+    arr1.push(...arr2); //或者arr1 = [...arr1,...arr2]
+    //去重
+    return Array.from(new Set(arr1)); //let arr3 = [...new Set(arr1)]
+};
+
+//关闭弹窗
+const dialogClose = () => {
+    isShow.value = false;
+    submitLoading.value = false;
+    emit("close");
+};
+</script>
+
+<style lang="scss" scoped>
+.hc-exctab-element-web-temp-box {
+    .left {
+        position: relative;
+        border-right: 1px solid #eee;
+        padding: 12px 12px 12px 0;
+    }
+    .right {
+        position: relative;
+        padding: 12px 0 12px 12px;
+    }
+}
+</style>
+
+<style lang="scss">
+.el-overlay-dialog
+    .el-dialog.hc-new-dialog.hc-exctab-element-web-temp
+    .el-dialog__body
+    .hc-new-dialog-body {
+    padding: 0;
+}
+.el-overlay-dialog
+    .hc-exctab-element-web-temp-box
+    .el-card.hc-card-box.hc-new-card-box {
+    --el-card-bg-color: transparent;
+    --el-card-padding: 0;
+    box-shadow: none;
+}
+.hc-exctab-element-web-temp-box
+    .hc-div-new-card-box.hc-tab-card-box
+    .el-card
+    .hc-card-header-box {
+    margin-top: 0;
+}
+.hc-exctab-element-web-temp-box
+    .hc-div-new-card-box.hc-tab-card-box
+    .el-card
+    .tab-card-header-tabs
+    .item {
+    height: 30px;
+}
+</style>