Browse Source

资料填报,表单弹窗

ZaiZai 2 years ago
parent
commit
e815d8de18

File diff suppressed because it is too large
+ 5 - 0
src/assets/view/load.svg


File diff suppressed because it is too large
+ 5 - 0
src/assets/view/no-data.svg


File diff suppressed because it is too large
+ 0 - 0
src/assets/view/null.svg


+ 37 - 18
src/components/table-form/index.vue

@@ -62,14 +62,14 @@ const tableFormItemStyle = ref('')
 
 //监听
 watch(() => [
-    props.tid,
-    props.kid,
     useAppState.getProjectId,
     useAppState.getContractId,
+    props.tid,
+    props.kid,
     props.classify,
     props.width,
     props.height,
-], ([tree_id, key_id, project_id, contract_id, cid, width, height]) => {
+], ([project_id, contract_id, tree_id, key_id, cid, width, height]) => {
     projectId.value = project_id
     contractId.value = contract_id
     treeId.value = tree_id
@@ -102,10 +102,11 @@ const setItemStyle = (width, height) => {
     tableFormItemStyle.value = `width: ${width}; height: ${height};`
 }
 
+//事件
+const emit = defineEmits(['rightTap', 'render'])
+
 //渲染状态变量
 const isTableForm = ref(false)
-const isTableRender = ref(false)
-const isRenderForm = ref(false)
 const tableFormInfo = ref({})
 
 //获取表单初始数据
@@ -115,7 +116,8 @@ const getFormDataInit = () => {
         contractId: contractId.value,
         classify: classify.value,
         pkeyId: keyId.value,
-        nodeId: treeId.value
+        nodeId: treeId.value,
+        isRenderForm: false
     }
 }
 
@@ -135,7 +137,6 @@ const getTableFormInfo = async (pkeyId, func) => {
         } else {
             tableFormInfo.value = getFormDataInit()
         }
-        console.log(tableFormInfo.value)
     } else {
         tableFormInfo.value = {}
         window?.$message?.warning('pkeyId为空')
@@ -187,23 +188,21 @@ const getExcelHtml = async (pkeyId, func, keys) => {
                     setTableFormBlurReg(pkeyId, event, key, reg, val, msg)
                 }
             })
-            isTableRender.value = true
-            isRenderForm.value = true
+            tableFormInfo.value.isRenderForm = true
             await nextTick(() => {
                 HTableForm.setByClassKeyup(keys)
             })
         } else {
             isTableForm.value = false
-            isTableRender.value = false
-            isRenderForm.value = false
+            tableFormInfo.value.isRenderForm = false
             window?.$message?.warning('暂无表单')
         }
     } else {
         isTableForm.value = false
-        isTableRender.value = false
-        isRenderForm.value = false
+        tableFormInfo.value.isRenderForm = false
         window?.$message?.warning('pkeyId为空')
     }
+    emit('render', tableFormInfo.value)
 }
 
 //正则效验
@@ -232,9 +231,6 @@ const setTableFormBlurReg = (pkeyId, event, key, reg, val, msg) => {
     }
 }
 
-//事件
-const emit = defineEmits(['rightTap'])
-
 //鼠标右键事件
 const onRightClick = (pkeyId, event, KeyName) => {
     //取光标位置
@@ -249,19 +245,43 @@ const getFormData = () => {
     return tableFormInfo.value
 }
 
+//设置表单数据
+const setFormData = (data) => {
+    tableFormInfo.value = data
+}
+
 //获取表单效验数据
 const getRegExpJson = () => {
     return deepClone(formRegExpJson.value);
 }
 
+//正则效验
+const isFormRegExp = () => {
+    const isRegExp = !!getObjNullValue(formRegExpJson.value)
+    if (!isRegExp) {
+        return true
+    } else {
+        window?.$message?.warning('请先修改完红色输入框的数据')
+        return false
+    }
+}
+
 // 暴露出去
 defineExpose({
     getFormData,
-    getRegExpJson
+    setFormData,
+    getRegExpJson,
+    isFormRegExp
 })
 </script>
 
 <style lang="scss">
+//插入特殊字符弹窗的输入框
+.hc-table-form-data-item .hc-excel-table-form td,
+.hc-table-form-data-item .hc-excel-table-form td .el-input .el-input__wrapper .el-input__inner,
+.el-form-item.special-form-item .el-form-item__content .el-input .el-input__wrapper .el-input__inner {
+    font-family: "EUDC", 宋体, v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
+}
 .hc-table-form-data-item {
     position: relative;
     padding: 24px;
@@ -274,7 +294,6 @@ defineExpose({
         justify-content: center;
         td {
             padding: 6px;
-            font-family: "EUDC", 宋体, v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
             .el-input {
                 background-color: #ffffff !important;
                 border-radius: 3px;

+ 16 - 9
src/global/components/hc-context-menu/index.vue

@@ -1,14 +1,18 @@
 <template>
-    <div :id="uuid" v-click-outside="onClickOutside" :class="ui" class="hc-context-menu-box">
-        <template v-for="item in menus">
-            <div class="hc-context-menu-item" @click.stop="optionClicked(item)">
-                <slot v-if="item.isSlot" :item="item" :name='item.key'/>
-                <template v-else>
-                    <HcIcon v-if="item.icon" :name="item.icon" class="menu-item-icon"/>
-                    <span class="menu-item-name">{{ item.label }}</span>
+    <div v-if="isBody" class="hc-context-menu-hide">
+        <Teleport :disabled="!isBody" to="#app">
+            <div :id="uuid" v-click-outside="onClickOutside" :class="ui" class="hc-context-menu-box">
+                <template v-for="item in menus">
+                    <div class="hc-context-menu-item" @click.stop="optionClicked(item)">
+                        <slot v-if="item.isSlot" :item="item" :name='item.key'/>
+                        <template v-else>
+                            <HcIcon v-if="item.icon" :name="item.icon" class="menu-item-icon"/>
+                            <span class="menu-item-name">{{ item.label }}</span>
+                        </template>
+                    </div>
                 </template>
             </div>
-        </template>
+        </Teleport>
     </div>
 </template>
 
@@ -31,6 +35,7 @@ const props = defineProps({
 //初始变量
 const uuid = getRandom()
 const menus = ref(props.datas)
+const isBody = ref(false)
 
 //监听表头
 watch(() => [
@@ -41,6 +46,8 @@ watch(() => [
 
 //加载完成
 nextTick(() => {
+    //页面渲染完成后,再让 vue3 的 Teleport,把弹出框挂载到外部节点上。
+    isBody.value = true
     setIsSlots(props.datas)
 })
 
@@ -132,7 +139,7 @@ defineExpose({
     left: 0;
     top: 0;
     box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
-    z-index: 1000000;
+    z-index: 999999;
     &.active {
         display: block;
     }

+ 8 - 3
src/global/components/hc-dialog/index.vue

@@ -1,10 +1,11 @@
 <template>
     <el-dialog :id="uuid"
                v-model="isShow"
+               :append-to-body="isToBody"
                :before-close="beforeClose"
-               :class="[isTable?'hc-modal-table':'', isSlotExtra?'hc-modal-header-extra':'', padding ? '':'hc-modal-no-padding', ui]"
-               :close-on-click-modal="false" :destroy-on-close="!isLoading" :show-close="isClose"
-               :style="isBgColor?'--el-dialog-bg-color:' + isBgColor:''" :title="title" :width="isWidth"
+               :class="[isTable?'hc-modal-table':'', isSlotExtra?'hc-modal-header-extra':'', padding ? '':'hc-modal-no-padding', ui]" :close-on-click-modal="false" :destroy-on-close="!isLoading"
+               :show-close="isClose" :style="isBgColor?'--el-dialog-bg-color:' + isBgColor:''" :title="title"
+               :width="isWidth"
                class="hc-modal-border"
                close-on-press-escape
                draggable
@@ -100,6 +101,10 @@ const props = defineProps({
         type: Boolean,
         default: false
     },
+    isToBody: {
+        type: Boolean,
+        default: false
+    },
 })
 
 //变量

+ 30 - 10
src/global/components/hc-drag-modal/index.vue

@@ -23,12 +23,17 @@
                             <div class="dialog-icon" @click="_fullscreenClick()">
                                 <HcIcon :name="isFullscreen?'fullscreen-exit':'fullscreen'"/>
                             </div>
-                            <div v-if="closeIcon" class="dialog-icon" @click="_closeClick()">
+                            <div v-if="isLoading && closeIcon" class="dialog-icon">
+                                <HcIcon name="close"/>
+                            </div>
+                            <div v-if="!isLoading && closeIcon" class="dialog-icon" @click="_closeClick()">
                                 <HcIcon name="close"/>
                             </div>
                         </div>
                     </div>
-                    <div class="ui-drag-modal-dialog-body" @mousedown.stop="dragModalBodyMouseDown">
+                    <div v-loading="isLoading" :element-loading-text="isLoadingText"
+                         class="ui-drag-modal-dialog-body"
+                         @mousedown.stop="dragModalBodyMouseDown">
                         <slot></slot>
                     </div>
                     <span class="ui-drag-modal-resize" @mousedown="dragModalResizeMouseDown"/>
@@ -56,7 +61,7 @@ const props = defineProps({
     },
     widths: {
         type: [Number, String],
-        default: 340
+        default: 960
     },
     title: {
         type: String,
@@ -80,11 +85,11 @@ const props = defineProps({
     },
     lefts: {
         type: [Number, String],
-        default: 0
+        default: 120
     },
     tops: {
         type: [Number, String],
-        default: 0
+        default: 80
     },
     height: {
         type: [Number, String],
@@ -101,9 +106,17 @@ const props = defineProps({
     },
     //关闭时销毁
     isCloseDestroy: {
+        type: Boolean,
+        default: true
+    },
+    loading: {
         type: Boolean,
         default: false
     },
+    loadingText: {
+        type: [Number, String],
+        default: '加载中...'
+    },
 })
 
 //变量
@@ -115,11 +128,18 @@ const dragModalTop = ref(parseInt(props.tops + ''))
 const widthVal = ref(parseInt(props.widths + ''))
 const heightVal = ref(parseInt(props.height))
 
+const isLoading = ref(props.loading)
+const isLoadingText = ref(props.loadingText)
+
 //监听
 watch(() => [
-    props.isShow
-], ([isShow]) => {
-    isModalShow.value = isShow;
+    props.isShow,
+    props.loading,
+    props.loadingText,
+], ([isShow, loading, loadingText]) => {
+    isModalShow.value = isShow
+    isLoading.value = loading
+    isLoadingText.value = loadingText
 })
 
 //深度监听弹窗排序列表变化
@@ -132,11 +152,11 @@ watch(() => [
 }, {deep: true})
 
 //设置窗口层级
-const zIndex = ref(8000)
+const zIndex = ref(2000)
 const setModalIndex = (sortTopList) => {
     if (props.isSortTop) {
         const index = sortTopList.indexOf(uuid)
-        zIndex.value = 8000 + (index + 1)
+        zIndex.value = 2000 + (index + 1)
     }
 }
 

+ 3 - 3
src/global/components/hc-drag-modal/modal.scss

@@ -14,9 +14,9 @@
         min-height: 380px;
         display: inline-block;
         border-radius: 5px;
-        z-index: 8001;
+        z-index: 2001;
         background: #f5f6f8;
-        box-shadow: 0 16px 24px 0 rgba(26,26,26,0.12), 0 2px 12px 0 rgba(26,26,26,0.10);
+        box-shadow: 0 16px 24px 0 rgba(26, 26, 26, 0.12), 0 2px 12px 0 rgba(26, 26, 26, 0.10);
         .ui-drag-modal-dialog-header {
             position: relative;
             display: flex;
@@ -67,6 +67,6 @@
     }
     &.ui-drag-modal-show {
         opacity: 1;
-        z-index: 8000;
+        z-index: 2000;
     }
 }

+ 4 - 0
src/styles/app/element.scss

@@ -631,6 +631,10 @@
     right: 1px;
 }
 
+.el-loading-mask {
+    z-index: 99999;
+}
+
 //设置表单样式
 .hc-excel-table-form-view {
     position: relative;

+ 119 - 0
src/views/data-fill/collapse-form/formula.vue

@@ -0,0 +1,119 @@
+<template>
+    <div v-if="componentDetail['type']==='radio'">
+        <el-form ref="formIDVFRef" :model="formulaModalinfo" label-width="auto" size="large">
+            <el-form-item :label="componentDetail['info']['label']">
+                <!-- onkeyup="value = value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g,'$1')" -->
+                <el-radio-group v-model="formulaModalinfo.type" size="large">
+                    <el-radio :label="componentDetail['info']['value'][0]['value']">
+                        {{ componentDetail['info']['value'][0]['label'] }}
+                    </el-radio>
+                    <el-radio :label="componentDetail['info']['value'][1]['value']" class="ml-4">
+                        {{ componentDetail['info']['value'][1]['label'] }}
+                    </el-radio>
+                </el-radio-group>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+
+<script setup>
+import {ref, watch, onMounted} from "vue";
+import wbsApi from "~api/data-fill/wbs";
+import {getObjNullValue} from "vue-utils-plus"
+
+const props = defineProps({
+    projectId: [String, Number],
+    contractId: [String, Number],
+    pkeyId: [String, Number],
+    KeyName: [String, Number],
+    formparentId: [String, Number],
+})
+
+
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+const pkey_id = ref(props.pkeyId)
+const key_name = ref(props.KeyName)
+const formparent_id = ref(props.formparentId)
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId,
+    props.pkeyId,
+    props.KeyName,
+    props.formparentId,
+], ([pid, cid, pkeyId, KeyName, formparentId]) => {
+    projectId.value = pid
+    contractId.value = cid
+    pkey_id.value = pkeyId
+    key_name.value = KeyName
+    formparent_id.value = formparentId
+    getPanel()
+})
+
+onMounted(() => {
+    getPanel()
+})
+
+const formIDVFRef = ref(null)
+const componentDetail = ref({
+    type: 'radio',
+    info: {
+        label: '是否引用公式数据',
+        value: [
+            {label: '是', value: 1},
+            {label: '否', value: 0},
+        ]
+    }
+})
+
+const formulaModalinfo = ref({
+    type: ''
+})
+
+//获取公式面板数据
+const getPanel = async () => {
+    const pkeyIds = pkey_id.value
+    if (pkeyIds) {
+        const {error, code, data} = await wbsApi.queryPanel({
+            key: key_name.value,
+            contractId: contractId.value,
+            parentId: formparent_id.value,
+            pkeyId: pkeyIds,
+        }, false)
+        if (!error && code === 200) {
+            componentDetail.value = getObjNullValue(data)
+            formulaModalinfo.value.type = componentDetail?.value.info?.data || ''
+        } else {
+            componentDetail.value = []
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+
+const panelSave = async () => {
+    const {res, error, code} = await wbsApi.savePanel({
+        parentId: formparent_id.value,
+        key: key_name.value,
+        contractId: contractId.value,
+        value: formulaModalinfo?.value?.type,
+        pkeyId: pkey_id.value,
+        scope: componentDetail?.value['scope'],
+        code: componentDetail?.value?.info['code'],
+    })
+    if (!error && code === 200) {
+        window?.$message?.success('操作成功')
+    }
+}
+
+// 暴露出去
+defineExpose({
+    panelSave
+})
+</script>
+
+<style lang="scss">
+
+</style>

+ 121 - 0
src/views/data-fill/collapse-form/index.scss

@@ -0,0 +1,121 @@
+table {
+    width: 100%;
+}
+.data-fill-list-box {
+    position: relative;
+    .hc-collapse-item-header {
+        flex: 1;
+        position: relative;
+        margin-left: 46px;
+        display: flex;
+        align-items: center;
+        .item-title {
+            flex: 1;
+            position: relative;
+            user-select: none;
+            color: #50545E;
+            font-size: 16px;
+            font-weight: 400;
+            cursor: pointer;
+        }
+        .hc-extra-text-box {
+            position: relative;
+            padding-right: 24px;
+        }
+    }
+    .data-fill-list-item-content {
+        position: relative;
+        display: flex;
+        height: calc(100vh - 386px);
+        .data-fill-table-form-box {
+            flex: 1;
+            position: relative;
+            height: 100%;
+            overflow: hidden;
+            border: 8px solid #c4c4c4;
+            &.is-window {
+                border: 0;
+                .hc-window-tip {
+                    position: relative;
+                    height: 100%;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    .table-form-no {
+                        position: relative;
+                        img {
+                            width: 380px;
+                        }
+                        .desc {
+                            text-align: center;
+                            font-size: 20px;
+                            color: #aaa;
+                        }
+                    }
+                }
+            }
+        }
+        .hc-window-switch-box {
+            display: flex;
+            align-items: center;
+            position: absolute;
+            top: 14px;
+            left: 14px;
+            .icon-btn-view {
+                padding: 0 18px;
+                height: 34px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                color: #ffffff;
+                cursor: pointer;
+                border-radius: 80px;
+                box-shadow: 4px 4px 8px 0 rgba(54, 92, 167, 0.15), -3px -2px 8px 0 #ffffff;
+                background: linear-gradient(to right, var(--el-color-primary-light-5), var(--el-color-primary), var(--el-color-primary-dark-2));
+                background-size: 200%;
+                transition: background-position .5s;
+                .icon {
+                    font-size: 16px;
+                }
+                &:hover {
+                    background-position: 100% 0;
+                }
+            }
+        }
+        .data-fill-table-tip-box {
+            width: 240px;
+            position: relative;
+            border-left: 1px solid #E9E9E9;
+            padding: 20px 15px 80px;
+            .tip-title {
+                font-size: 16px;
+                margin-bottom: 10px;
+                display: flex;
+                align-items: center;
+            }
+            .tip-item {
+                margin-bottom: 20px;
+            }
+            .table-tip-foot {
+                position: absolute;
+                bottom: 15px;
+                right: 0;
+                left: 0;
+                display: flex;
+                align-items: center;
+                padding: 0 15px;
+                .tip-left-btn {
+                    flex: 1;
+                    .dow-text {
+                        cursor: pointer;
+                        display: flex;
+                        align-items: center;
+                    }
+                }
+            }
+        }
+    }
+}
+.radio-group-box {
+    text-align: center;
+}

+ 1152 - 0
src/views/data-fill/collapse-form/index.vue

@@ -0,0 +1,1152 @@
+<template>
+    <div class="data-fill-list-box">
+        <el-collapse v-model="ActiveKey" @change="CollapseChange">
+            <el-collapse-item v-for="(item,index) in listDatas" :id="`item-${index}-${item?.pkeyId}`"
+                              :key="item?.pkeyId" :disabled="item['isBussShow'] === 2"
+                              :name="`item-${index}-${item?.pkeyId}`">
+                <template #title>
+                    <div class="hc-collapse-item-header">
+                        <div class="text-lg truncate item-title"> {{ item.nodeName }}</div>
+                        <div class="hc-extra-text-box">
+                            <HcTooltip v-if="item['isCopeTab'] === 2 || item['isCopeTab'] === 3" keys="wbs_del_table">
+                                <el-button :disabled="item['isBussShow'] === 2" :loading="tableFormDelLoading" plain
+                                           type="danger"
+                                           @click.stop="delClick(item)">删除本表
+                                </el-button>
+                            </HcTooltip>
+                            <HcTooltip keys="wbs_copy_table">
+                                <el-button
+                                    v-if="item['isLinkTable'] === 1"
+                                    disabled plain
+                                    type="info">
+                                    复制本表
+                                </el-button>
+                                <el-button v-else :loading="copyClickLoading" plain type="primary"
+                                           @click.stop="copyClick(item,index)">复制本表
+                                </el-button>
+                            </HcTooltip>
+                            <HcTooltip keys="wbs_hide_table">
+                                <el-button :loading="tableFormHideLoading" plain type="primary"
+                                           @click.stop="hideClick(item,index)">
+                                    <template v-if="item['isBussShow'] === 1">隐藏本表</template>
+                                    <template v-else>显示本表</template>
+                                </el-button>
+                            </HcTooltip>
+                            <HcTooltip v-if="isStatus !== 3" keys="wbs_preview_table">
+                                <el-button
+                                    v-if="item['isBussShow'] === 2 || item['isTabPdf'] === 1|| item['pdfUrl'] === ''"
+                                    disabled plain
+                                    type="info">
+                                    预览
+                                </el-button>
+                                <el-button v-else :loading="tableFormPreviewLoading" plain type="primary"
+                                           @click.stop="previewClick(item)">预览
+                                </el-button>
+                            </HcTooltip>
+                            <HcTooltip keys="wbs_upload_table">
+                                <el-button :disabled="item['isBussShow'] === 2"
+                                           :type="item['tabFileType'] === 2?'success':'primary'"
+                                           plain
+                                           @click.stop="uploadClick(item,index)">
+                                    <template v-if="item['tabFileType'] === 2">已上传</template>
+                                    <template v-else>上传</template>
+                                </el-button>
+                            </HcTooltip>
+                        </div>
+                    </div>
+                </template>
+                <div :style="`height: calc(100vh - ${draw_type ? '555px' : '360px'});`"
+                     class="data-fill-list-item-content">
+                    <div v-if="item?.isWindow" class="data-fill-table-form-box is-window">
+                        <div class="hc-window-tip">
+                            <div class="table-form-no">
+                                <img :src="NoDataSvg" alt=""/>
+                                <div class="desc">当前表单处于窗口模式,关闭相关窗口后恢复</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div v-else class="data-fill-table-form-box">
+                        <HcTableForm v-if="item.isTableRender" :ref="(el) => setItemRefs(el, item)" :api="apis"
+                                     :classify="classifys" :kid="item?.pkeyId"
+                                     :tid="treeId" @render="tableFormRender($event, item, index)"
+                                     @rightTap="tableFormRightTap($event, index)"/>
+                    </div>
+                    <div class="hc-window-switch-box">
+                        <el-tooltip :content="item.isWindow?'关闭窗口并恢复':'当前表单窗口化'" placement="top">
+                            <div class="icon-btn-view" @click.stop="windowClick(item, index)">
+                                <template v-if="item.isWindow">
+                                    <HcIcon class="icon" name="picture-in-picture-2"/>
+                                    <span class="ml-1">关闭窗口化</span>
+                                </template>
+                                <template v-else>
+                                    <HcIcon class="icon" name="picture-in-picture-exit"/>
+                                    <span class="ml-1">表单窗口化</span>
+                                </template>
+                            </div>
+                        </el-tooltip>
+                    </div>
+                    <div class="data-fill-table-tip-box">
+                        <div class="text-orange tip-title">
+                            <HcIcon fill name="error" ui="text-2xl"/>
+                            <span class="ml-1">提示</span>
+                        </div>
+                        <div class="text-gray-400 tip-item">
+                            1、灰色框代表可通过系统识别计算,公式自动引用,可通过公式计算少量数据,(表头数据及简单),也可只填写白色框数据
+                        </div>
+                        <div class="text-gray-400 tip-item">2、系统支持键盘中,shift +
+                            tab键向上一个填报框切换,tab向下一个填报框切换。Shift + 上 ( ↑ )、下 ( ↓ )、左 ( ← )、右 ( →
+                            )键,切换填报输入框焦点。
+                        </div>
+                        <div class="table-tip-foot">
+                            <div class="tip-left-btn">
+                                <HcTooltip keys="wbs_import_table">
+                                    <div class="text-gray-400 dow-text">
+                                        <HcIcon name="publish" ui="text-lg"/>
+                                        <span class="ml-1">导入列表数据</span>
+                                    </div>
+                                </HcTooltip>
+                                <HcTooltip keys="wbs_download_table">
+                                    <div class="text-main dow-text">
+                                        <HcIcon name="file_download" ui="text-lg"/>
+                                        <span class="ml-1">下载导入模板</span>
+                                    </div>
+                                </HcTooltip>
+                            </div>
+                            <div class="tip-right-btn">
+                                <HcTooltip keys="wbs_save_table">
+                                    <el-button :disabled="!item?.isTableForm"
+                                               :loading="tableFormSaveLoading" hc-btn
+                                               type="primary"
+                                               @click="tableFormSaveClick(item)">
+                                        <HcIcon name="save"/>
+                                        <span>保存</span>
+                                    </el-button>
+                                </HcTooltip>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </el-collapse-item>
+        </el-collapse>
+    </div>
+
+    <!--右键菜单-->
+    <HcContextMenu ref="contextMenuRef" :datas="tableFormMenu" @item-click="handleMenuSelect"/>
+
+    <!--上传文件-->
+    <HcDialog :footer="false" :show="uploadModal" title="上传文件" widths="38rem" @close="uploadModalClose">
+        <HcUpload :datas="uploadData" :fileList="fileListData" @change='uploadChange'/>
+    </HcDialog>
+
+    <!--插入设计值/频率-->
+    <HcDialog :loading="designModalLoading" :show="designModal" isToBody saveText="确认插入"
+              title="插入设计值/频率" widths="600px"
+              @close="closeDesignModal" @save="designModalSave">
+        <el-alert :closable="false" title="填写完设计值和频率,系统自动计算实测值" type="warning"/>
+        <el-form ref="formDesignRef" :model="formDesignModel" label-width="auto" size="large">
+            <div class="form-item-div text-center mb-3">
+                <el-radio-group v-model="formDesignModel.type" size="large">
+                    <el-radio :label="1">公路工程</el-radio>
+                    <el-radio :label="2" class="ml-4">水利水电</el-radio>
+                </el-radio-group>
+            </div>
+            <el-form-item label="设计值">
+                <!-- onkeyup="value = value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g,'$1')" -->
+                <el-input v-model="formDesignModel.design" placeholder="如果设计值存在两个,则使用 '/' 连接,例如12/23"/>
+            </el-form-item>
+            <el-form-item v-if="formDesignModel.type === 1" label="频率">
+                <!--  onkeyup="value = value.replace(/^(0+)|[^\d]+/g,'')" -->
+                <el-input v-model="formDesignModel.size"
+                          placeholder="如果设计值使用 '/' 连接,则频率也是用 '/' 连接,例如5/10"/>
+            </el-form-item>
+            <el-form-item v-if="formDesignModel.type === 2" label="容量">
+                <!--   onkeyup="value = value.replace(/^(0+)|[^\d]+/g,'')" -->
+                <el-input v-model="formDesignModel.capacity"
+                          placeholder="如果容量存在两个,则使用 '*',或者 '/' 连接例如2*4,5/7"/>
+            </el-form-item>
+            <el-form-item label="合格点数">
+                <el-input v-model="formDesignModel.pass"
+                          placeholder="如果设计值使用 '/' 连接,则合格点数也是用 '/' 连接,例如2/5"/>
+            </el-form-item>
+            <el-form-item label="偏差范围">
+                <el-input v-model="formDesignModel.dev"
+                          placeholder="如果设计值使用 '/' 连接,则偏差范围也是用 '/' 连接,例如±2/±3"/>
+            </el-form-item>
+        </el-form>
+    </HcDialog>
+
+    <!--插入特殊字符-->
+    <HcDialog :loading="specialModalLoading" :show="specialModal" isToBody saveText="确认插入" title="插入特殊字符"
+              widths="600px" @close="specialModalClose" @save="specialNodeClick">
+        <HcSpecialDiaolg v-if="specialModal" ref="specialRef" @change="specialDiaolgChange"/>
+    </HcDialog>
+
+    <!--关联试验数据-->
+    <HcDialog :footer="false" :loading="testModalLoading" :show="testModal" isTable isToBody title="关联试验数据"
+              widths="84%" @close="testModalClose">
+        <HcTestDiaolg v-if="testModal" :contractId="contract_id" :projectId="projectId" :tenantId="tenant_id"
+                      :wbsId="wbsTemp_id"
+                      :wbsType="wbs_type" @change="testTableRowName"/>
+    </HcDialog>
+
+    <!-- 选择要插入的数据 -->
+    <HcDialog :loading="insertDataLoading" :show="insertDataShow" isTable isToBody saveText="保存"
+              title="选择需要插入的数据" widths="84%" @close="cancelinsertData"
+              @save="submitinsertData">
+        <HcTestData v-if="insertDataShow" ref="insertDataRef" :contractId="contract_id" :projectId="projectId"
+                    :tableId="itinsertTableId"
+                    :treeId="itinsertTreeId"/>
+    </HcDialog>
+
+    <!--关联试验文件-->
+    <HcDialog :loading="fileModalLoading" :show="fileModal" isTable isToBody saveText="保存" title="关联试验文件"
+              widths="84%" @close="fileModalClose"
+              @save="savefileModal">
+        <HcTestFile v-if="fileModal" ref="testFileRefs" :contractId="contract_id" :projectId="projectId"
+                    :tenantId="tenant_id"
+                    :treeId="treeId" :wbsId="wbsTemp_id"
+                    :wbsType="wbs_type"/>
+    </HcDialog>
+
+    <!-- 公式参数配置 -->
+    <HcDialog :loading="formulaModalLoading" :show="formulaModal" isToBody saveText="保存" title="公式参数配置"
+              widths="84%" @close="formulaModalClose"
+              @save="formulaSaveClick">
+        <HcFormula v-if="formulaModal" ref="formulaRefs" :KeyName="tableFormItemNode.KeyName" :contractId="contract_id"
+                   :formparentId="formparentId"
+                   :pkeyId="tableFormItemNode.pkeyId" :projectId="projectId"/>
+    </HcDialog>
+
+    <!--查看表单-->
+    <template v-for="(item,index) in DragModalTableForm">
+        <HcDragModal :eid="item.pkeyId" :height="DragModalHeight" :isShow="item.isShow" :loading="item.loading"
+                     :loadingText="item.loadingText" :title="item.title"
+                     isSortTop @close="TableFormClose($event, item, index)">
+            <HcDragNode :more-menu="dragNodeMoreMenu" @menuTap="dragNodeMoreMenuTap($event, item)">
+                <HcTableForm :ref="(el) => setItemRefs(el, item)" :api="apis" :classify="item.classify"
+                             :height="item.height"
+                             :kid="item.pkeyId" :tid="item.treeId" :width="item.width"
+                             @render="tableFormRender($event, item['item'], item['index'])"
+                             @rightTap="tableFormRightTap($event, item['index'])"/>
+            </HcDragNode>
+        </HcDragModal>
+    </template>
+
+</template>
+
+<script setup>
+import {ref, watch, nextTick} from "vue";
+import {useAppStore} from "~src/store";
+import wbsApi from "~api/data-fill/wbs"
+import HcUpload from "../components/HcUpload.vue"
+import HcTableForm from "~com/table-form/index.vue"
+import HcSpecialDiaolg from "./special-diaolg.vue"
+import HcTestDiaolg from "./test-diaolg.vue"
+import HcTestData from "./test-data.vue"
+import HcTestFile from "./test-file.vue"
+import HcFormula from "./formula.vue"
+import HcDragNode from "~com/drag-node/index.vue"
+import NoDataSvg from '~src/assets/view/no-data.svg'
+import {setPosRange, isType, utilsArray, deepClone} from "vue-utils-plus"
+//初始变量
+const useAppState = useAppStore()
+const {isIndex, getIndex} = utilsArray()
+const {isString, getObjNullValue, getArrValue, getObjValue} = isType()
+
+//参数
+const props = defineProps({
+    datas: {
+        type: Array,
+        default: () => ([])
+    },
+    classify: {
+        type: [String, Number],
+        default: ''
+    },
+    status: {
+        type: [String, Number],
+        default: ''
+    },
+    primaryKeyId: {
+        type: [String, Number],
+        default: ''
+    },
+    contractId: {
+        type: [String, Number],
+        default: ''
+    },
+    authBtnTabKey: {
+        type: [String, Number],
+        default: ''
+    },
+    drawType: {
+        type: Boolean,
+        default: false
+    },
+    wbsTempId: {
+        type: [String, Number],
+        default: ''
+    },
+    tenantId: {
+        type: [String, Number],
+        default: ''
+    },
+    wbsType: {
+        type: [String, Number],
+        default: ''
+    },
+})
+
+//全局变量
+const projectId = ref(useAppState.projectId);
+const contract_id = ref(props.contractId)
+const treeId = ref(props.primaryKeyId)
+const classifys = ref(props.classify)
+const wbsTemp_id = ref(props.wbsTempId);
+const tenant_id = ref(props.tenantId);
+const wbs_type = ref(props.wbsType);
+const isStatus = ref(parseInt(props.status))
+const listDatas = ref([])
+const draw_type = ref(props.drawType)
+const auth_Key = ref(props.authBtnTabKey)
+
+//表单变量
+const formDataList = ref([])
+const formKeyIds = ref('')
+const formparentId = ref('')
+
+//处理ref
+const itemRefs = ref([])
+const setItemRefs = (el, {pkeyId}) => {
+    if (el) {
+        if (!isIndex(itemRefs.value, 'pkeyId', pkeyId)) {
+            itemRefs.value.push({
+                pkeyId: pkeyId,
+                ref: el
+            });
+        }
+    }
+}
+
+//处理表单的ref
+const setSpliceItemRefs = async ({pkeyId}) => {
+    const refs = itemRefs.value
+    let index = getIndex(refs, 'pkeyId', pkeyId)
+    if (index !== -1) {
+        refs.splice(index, 1)
+        itemRefs.value = refs
+    }
+}
+
+//事件
+const emit = defineEmits(['renew', 'offsetTop'])
+
+//组件参数变量
+const apis = ref({
+    dataInfo: wbsApi.getBussDataInfo,
+    bussCols: wbsApi.getHtmlBussCols,
+    excelHtml: wbsApi.getExcelHtml
+})
+
+//深度监听数据
+watch(() => [
+    props.datas,
+], ([datas]) => {
+    setFormDataNum(datas)
+}, {deep: true})
+
+
+//监听变量值
+watch(() => [
+    useAppState.projectId,
+    props.contractId,
+    props.wbsTempId,
+    props.tenantId,
+    props.wbsType,
+    props.status,
+    props.classify,
+    props.primaryKeyId,
+], ([pid, cid, temp_id, tid, type, status, class_id, tree_id]) => {
+    projectId.value = pid
+    contract_id.value = cid
+    wbsTemp_id.value = temp_id
+    tenant_id.value = tid
+    wbs_type.value = type
+    isStatus.value = parseInt(status)
+    classifys.value = class_id
+    treeId.value = tree_id
+})
+
+//渲染完成
+nextTick(() => {
+    setFormDataNum(props.datas)
+    setTableFormMenu(useAppState.projectInfo)
+    const {offsetHeight} = document.body
+    DragModalHeight.value = offsetHeight - 200
+})
+
+//展开事件
+const ActiveKey = ref([])
+const CollapseChange = (name) => {
+    ActiveKey.value = name
+    let index = getCollapseItemIndex(name)
+    if (index > -1) {
+        getOffsetTop(name);
+        const item = listDatas.value[index]
+        formKeyIds.value = setToString(item.pkeyId)
+        formparentId.value = setToString(item.parentId)
+        nextTick(() => {
+            if (!item.isTableRender) {
+                item.isTableRender = true
+            }
+        })
+    } else {
+        getOffsetTop()
+        formKeyIds.value = ''
+        formparentId.value = ''
+    }
+}
+
+//初始设置
+const setFormDataNum = (datas) => {
+    itemRefs.value = []
+    ActiveKey.value = []
+    let newArr = [];
+    for (let i = 0; i < datas.length; i++) {
+        newArr.push({isCollapseLoad: false})
+    }
+    formDataList.value = newArr
+    listDatas.value = deepClone(datas)
+}
+
+//渲染完成
+const tableFormRender = (form, item, index) => {
+    formDataList.value[index] = {
+        ...form,
+        isCollapseLoad: form.isRenderForm
+    }
+    item.isTableForm = form.isRenderForm
+}
+
+
+//菜单数据
+const contextMenuRef = ref(null)
+const tableFormMenu = ref([])
+const tableFormItemNode = ref({}) //临时信息
+
+//设置菜单权限数据
+const setTableFormMenu = (info) => {
+    let newArr = [], infos = getObjValue(info)
+    const isOpen = infos['isOpenRandomNumber'] ?? 0
+    if (isOpen === 1) {
+        newArr.push({label: '插入设计值/频率', key: "design"})
+    }
+    newArr.push({label: '插入特殊字符', key: "special"})
+    newArr.push({label: '关联试验数据', key: "test"})
+    newArr.push({label: '关联试验文件', key: "file"})
+    newArr.push({label: '公式参数', key: "formula"})
+    tableFormMenu.value = newArr
+}
+
+//鼠标右键事件
+const tableFormRightTap = ({event, KeyName, startPos, endPos, pkeyId}, index) => {
+    //存储临时信息
+    tableFormItemNode.value = {KeyName, index, startPos, endPos, pkeyId}
+    contextMenuRef.value?.showMenu(event) //展开菜单
+}
+
+//鼠标右键菜单被点击
+const handleMenuSelect = ({key}) => {
+    if (key === 'design') {
+        setInitDesignForm()
+        designModalLoading.value = false
+        designModal.value = true
+    } else if (key === 'special') {
+        specialModalShow()
+    } else if (key === 'test') {
+        testModalLoading.value = false
+        testModal.value = true
+    } else if (key === 'file') {
+        fileModalLoading.value = false
+        fileModal.value = true;
+    } else if (key === 'formula') {
+        formulaModalLoading.value = false
+        formulaModal.value = true
+    }
+}
+
+//插入设计值
+const designModal = ref(false)
+const formDesignRef = ref(null)
+const formDesignModel = ref()
+
+//初始设计值/频率表单
+const setInitDesignForm = () => {
+    formDesignModel.value = {
+        type: 1, design: '', size: '',
+        dev: '', key: '', capacity: '',
+        pass: '', pkId: ''
+    }
+}
+
+//设计值频率计算
+const designModalLoading = ref(false)
+const designModalSave = async () => {
+    const {pkeyId, KeyName} = tableFormItemNode.value
+    if (pkeyId) {
+        designModalLoading.value = true
+        //const {design, size} = formDesignModel.value
+        const {error, code, data} = await wbsApi.queryFormulaRange({
+            ...formDesignModel.value,
+            // dev: (!design && !size) ? '±5': '',
+            key: KeyName,
+            pkId: pkeyId,
+        })
+        //处理数据
+        const res = getObjNullValue(data)
+        if (!error && code === 200 && res) {
+            try {
+                const refs = await getFormRef(pkeyId)
+                const itemFormData = refs?.getFormData()
+                Object.keys(data).forEach(key => {
+                    itemFormData[key] = data[key]
+                })
+                refs?.setFormData(itemFormData)
+            } catch {
+            }
+            designModalLoading.value = false
+            designModal.value = false
+        } else {
+            designModalLoading.value = false
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+//关闭设计值/频率弹窗
+const closeDesignModal = () => {
+    designModal.value = false
+    setInitDesignForm()
+}
+
+
+//插入特殊字符
+const specialModal = ref(false)
+const specialModalLoading = ref(false)
+const specialModalShow = () => {
+    specialFormValue.value = ''
+    specialModalLoading.value = false
+    specialModal.value = true
+}
+
+//监听特殊符号输入框的内容
+const specialFormValue = ref('')
+const specialDiaolgChange = (val) => {
+    specialFormValue.value = val
+}
+
+//确认插入
+const specialRef = ref(null)
+const specialNodeClick = async () => {
+    specialModalLoading.value = true
+    const itemNode = tableFormItemNode.value
+    const {KeyName, pkeyId} = itemNode
+    try {
+        const refs = await getFormRef(pkeyId)
+        const itemFormData = refs?.getFormData()
+        const {code, val, posVal} = await specialRef.value?.getSpecialNode(itemNode, itemFormData[KeyName])
+        if (code === 200 && val) {
+            itemFormData[KeyName] = val
+            refs?.setFormData(itemFormData)
+            specialModalLoading.value = false
+            specialModal.value = false
+            await nextTick(() => {
+                setPosRange(KeyName, posVal)
+            })
+        } else {
+            specialModalLoading.value = false
+        }
+    } catch (e) {
+        specialModalLoading.value = false
+    }
+}
+
+//关闭插入特殊字符
+const specialModalClose = () => {
+    specialModalLoading.value = false
+    specialModal.value = false
+}
+
+//关联试验数据
+const testModal = ref(false)
+const testModalLoading = ref(false)
+
+//关联试验数据被点击
+const itinsertTableId = ref('')
+const itinsertTreeId = ref('')
+const testTableRowName = ({row, treeId}) => {
+    itinsertTableId.value = row.id
+    itinsertTreeId.value = treeId
+    insertDataLoading.value = false
+    insertDataShow.value = true
+}
+
+//关闭弹窗
+const testModalClose = () => {
+    testModal.value = false
+    testModalLoading.value = false
+}
+
+//选择要插入的实验数据
+const insertDataShow = ref(false);
+const insertDataLoading = ref(false);
+
+//确定关联试验数据数据
+const insertDataRef = ref(null)
+const submitinsertData = async () => {
+    insertDataLoading.value = true
+    const itemNode = tableFormItemNode.value
+    const {KeyName, pkeyId} = itemNode
+    try {
+        const refs = await getFormRef(pkeyId)
+        const itemFormData = refs?.getFormData()
+        const {code, val, posVal} = await insertDataRef.value?.submitinsertData(itemNode, itemFormData[KeyName])
+        if (code === 200 && val) {
+            itemFormData[KeyName] = val
+            refs?.setFormData(itemFormData)
+            insertDataLoading.value = false
+            insertDataShow.value = false
+            testModal.value = false
+            await nextTick(() => {
+                setPosRange(KeyName, posVal)
+            })
+        }
+    } catch {
+        insertDataLoading.value = false
+    }
+}
+
+//取消关联数据
+const cancelinsertData = async () => {
+    insertDataShow.value = false
+    insertDataLoading.value = false
+}
+
+
+//关联试验文件
+const fileModal = ref(false)
+const testFileRefs = ref(null)
+//确认关联文件
+const fileModalLoading = ref(false)
+const savefileModal = async () => {
+    fileModalLoading.value = true
+    await testFileRefs.value?.savefileSubmit()
+    fileModalLoading.value = false
+    fileModal.value = false
+}
+
+//关闭弹窗
+const fileModalClose = () => {
+    fileModal.value = false
+    fileModalLoading.value = false
+}
+
+//公式参数配置
+const formulaModal = ref(false)
+const formulaRefs = ref(null)
+
+//保存
+const formulaModalLoading = ref(false)
+const formulaSaveClick = async () => {
+    formulaModalLoading.value = true
+    await formulaRefs.value?.savefileSubmit()
+    formulaModalLoading.value = false
+    formulaModal.value = false
+}
+
+
+//关闭
+const formulaModalClose = () => {
+    formulaModal.value = false
+    formulaModalLoading.value = false
+}
+
+
+//窗口化
+const DragModalTableForm = ref([])
+const DragModalHeight = ref(600)
+const windowClick = async (item, indexs) => {
+    let KeyId = `item-${indexs}-${item?.pkeyId}`
+    if (KeyId === ActiveKey.value) {
+        const list = DragModalTableForm.value
+        let index = getIndex(list, 'pkeyId', item.pkeyId)
+        if (!item.isWindow) {
+            const formSize = getTableFormSize(item?.pkeyId)
+            const newTableForm = {
+                ...setInitDragModalTableForm(item, indexs),
+                ...formSize
+            }
+            item.isWindow = true
+            //处理表单的ref
+            await setSpliceItemRefs(item)
+            //弹窗表单的排序
+            if (index === -1) {
+                list.push(newTableForm)
+            } else if (index !== list.length - 1) {
+                //检查是否在最上层,不在则置顶,可以解决多次点击时,频繁更改全局状态的问题
+                list.splice(index, 1)
+                list.push(newTableForm)
+            }
+            DragModalTableForm.value = list
+        } else {
+            //处理表单的ref
+            await setSpliceItemRefs(item)
+            if (index !== -1) {
+                list.splice(index, 1)
+                DragModalTableForm.value = list
+            }
+            item.isWindow = false
+        }
+    } else {
+        window.$message.warning('请先展开此表单,等待表单渲染完成后再操作')
+    }
+}
+
+//初始拖动表单的内容
+const setInitDragModalTableForm = (item, index) => {
+    return {
+        projectId: projectId.value,
+        contractId: contract_id.value,
+        wbsTempId: wbsTemp_id.value,
+        tenantId: tenant_id.value,
+        wbsType: wbs_type.value,
+        classify: classifys.value,
+        treeId: treeId.value,
+        pkeyId: item.pkeyId,
+        height: '100%',
+        width: '100%',
+        title: item.nodeName,
+        isShow: true,
+        index: index,
+        item: item
+    }
+}
+
+const TableFormClose = async (closeFunc, item, index) => {
+    const list = DragModalTableForm.value
+    list.splice(index, 1)
+    listDatas.value[item.index].isWindow = false
+    //取表单的数据
+    const refs = await getFormRef(item?.pkeyId)
+    const formData = refs?.getFormData()
+    //关闭窗口
+    closeFunc()
+    console.log(formData)
+}
+
+const dragNodeMoreMenu = [
+    {key: 'save', icon: 'save-2', name: '保存'},
+    {key: 'preview', icon: 'eye', name: '预览'},
+]
+
+//菜单被点击
+const dragNodeMoreMenuTap = ({key}, items) => {
+    const {item} = items
+    if (key === 'save') {
+        if (item?.isTableForm) {
+            tableFormSaveClick(item, items)
+        } else {
+            window.$message.warning('此表单暂无数据和文件')
+        }
+    } else if (key === 'preview') {
+        if (item['isBussShow'] === 2 || item['isTabPdf'] === 1 || item['pdfUrl'] === '') {
+            window.$message.warning('此表单暂无可预览文件')
+        } else {
+            previewClick(item, items)
+        }
+    }
+}
+
+//删除本表
+const tableFormDelLoading = ref(false)
+const delClick = async ({pkeyId}) => {
+    if (pkeyId) {
+        if (isStatus.value !== 3) {
+            tableFormDelLoading.value = true
+            const {error, code} = await wbsApi.removeBussTabInfo({
+                pkeyid: pkeyId,
+                classify: classifys.value,
+            })
+            tableFormDelLoading.value = false
+            if (!error && code === 200) {
+                window?.$message?.success('操作成功')
+                //判断是否存在窗口,如果存在,就删除窗口
+                delWindowRefs(pkeyId)
+                renewData()
+            }
+        } else {
+            window?.$message?.warning('已上报的资料,不允许删除')
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+
+//复制本表
+const copyClickLoading = ref(false)
+const copyClick = async (items) => {
+    const {pkeyId, isTableRender, isTableForm} = items
+    if (pkeyId) {
+        if (isStatus.value !== 3) {
+            if (!isTableRender) {
+                await copeBussTab(pkeyId)
+            } else if (!isTableForm) {
+                window?.$message?.warning('暂无表单数据')
+            } else if (isTableRender) {
+                const isSave = await saveExcelBussData(items, null, false)
+                if (isSave) {
+                    await copeBussTab(pkeyId)
+                } else {
+                    window?.$message?.warning('复制本表操作失败')
+                }
+            } else {
+                window?.$message?.warning(`数据异常了, isRenderTableForm: ${isTableRender}, isTableForm: ${isTableForm}, pkeyIds:${pkeyId}`)
+            }
+        } else {
+            window?.$message?.warning('已上报的资料,不允许复制')
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+
+//复制表的请求
+const copeBussTab = async (pkeyId) => {
+    copyClickLoading.value = true
+    const {error, code} = await wbsApi.copeBussTab({
+        pkeyId: pkeyId
+    })
+    copyClickLoading.value = false
+    if (!error && code === 200) {
+        window?.$message?.success('操作成功')
+        renewData()
+    }
+}
+
+//隐藏本表
+const tableFormHideLoading = ref(false)
+const hideClick = async ({pkeyId, isBussShow}) => {
+    if (pkeyId) {
+        if (isStatus.value !== 3) {
+            tableFormHideLoading.value = true
+            const bussShow = isBussShow === 2 ? 1 : 2 //状态(1显示 2隐藏)
+            const {error, code} = await wbsApi.showBussTab({
+                pkeyId: pkeyId,
+                status: bussShow
+            })
+            tableFormHideLoading.value = false
+            if (!error && code === 200) {
+                window?.$message?.success('操作成功')
+                if (bussShow === 2) {
+                    //判断是否存在窗口,如果存在,就删除窗口
+                    delWindowRefs(pkeyId)
+                }
+                renewData()
+            }
+        } else {
+            window?.$message?.warning('已上报的资料,不允许隐藏')
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+
+//预览本表
+const tableFormPreviewLoading = ref(false)
+const previewClick = async (item, dragItem = null) => {
+    tableFormPreviewLoading.value = true
+    await getBussPdfInfo(item, dragItem)
+    tableFormPreviewLoading.value = false
+}
+
+//上传变量
+const uploadModal = ref(false)
+const fileListData = ref([]);
+const uploadData = ref({})
+//上传附件
+const uploadClick = (items, index) => {
+    const {pkeyId, isTableForm, isTableRender} = items
+    const keyName = `item-${index}-${pkeyId}`
+    if (pkeyId) {
+        if (isStatus.value !== 3 && isTableForm) {
+            uploadModal.value = true
+            uploadData.value = {
+                projectId: projectId.value,
+                contractId: contract_id.value,
+                classify: classifys.value,
+                pkeyId: pkeyId,
+                nodeId: treeId.value
+            }
+            //获取文件列表
+            getBussFileList(pkeyId)
+        } else if (!isTableRender) {
+            CollapseChange([keyName])
+            window?.$message?.warning('请再次点击上传')
+        } else if (!isTableForm) {
+            window?.$message?.warning('暂无表单数据')
+        } else {
+            window?.$message?.warning('已上报的资料,不允许上传')
+        }
+    } else {
+        window?.$message?.warning('pkeyId为空')
+    }
+}
+
+//获取文件列表
+const getBussFileList = async (pkeyId) => {
+    const {error, code, data} = await wbsApi.getBussFileList({
+        pkeyid: pkeyId
+    })
+    if (!error && code === 200) {
+        fileListData.value = getArrValue(data)
+    } else {
+        fileListData.value = []
+    }
+}
+
+//上传文件
+const uploadChange = async ({type}) => {
+    if (type === 'success') {
+        uploadModal.value = false
+        renewData()
+    } else if (type === 'del') {
+        renewData()
+    }
+}
+
+//关闭上传附件窗口
+const uploadModalClose = () => {
+    uploadModal.value = false
+}
+
+//单个保存
+const tableFormSaveLoading = ref(false)
+const tableFormSaveClick = async (item, dragItem = null) => {
+    if (isStatus.value !== 3) {
+        tableFormSaveLoading.value = true
+        const isSave = await saveExcelBussData(item, dragItem)
+        if (isSave) {
+            await getBussPdfInfo(item, dragItem)
+            tableFormSaveLoading.value = false
+            renewData()
+        } else {
+            tableFormSaveLoading.value = false
+        }
+    } else {
+        window?.$message?.warning('已上报的资料,不允许保存。')
+    }
+}
+
+//保存表单数据
+const saveExcelBussData = async ({pkeyId}, dragItem = null, showTip = true) => {
+    setDragModalLoading(dragItem, '保存中...', true)
+    const refs = await getFormRef(pkeyId)
+    const isRegExp = refs?.isFormRegExp()
+    if (isRegExp) {
+        const formData = refs?.getFormData()
+        const {error, code} = await wbsApi.saveExcelBussData(formData)
+        setDragModalLoading(dragItem)
+        if (!error && code === 200) {
+            if (showTip) {
+                window?.$message?.success('保存成功')
+            }
+            return true
+        } else {
+            return false
+        }
+    } else {
+        setDragModalLoading(dragItem)
+        return false
+    }
+}
+
+//预览PDF
+const getBussPdfInfo = async ({pkeyId}, dragItem = null, showTip = true) => {
+    setDragModalLoading(dragItem, '获取pdf中...', true)
+    const {error, code, data} = await wbsApi.getBussPdfInfo({
+        pkeyId: pkeyId
+    }, false)
+    setDragModalLoading(dragItem)
+    if (!error && code === 200) {
+        if (data) {
+            window.open(data, '_blank')
+        } else if (showTip) {
+            window?.$message?.warning('PDF错误')
+        }
+    } else {
+        if (showTip) {
+            window?.$message?.warning(data.msg || '获取PDF失败')
+        }
+    }
+}
+
+
+//通知数据更新
+const renewData = () => {
+    emit('renew')
+    ActiveKey.value = []
+}
+
+//设置表单的加载状态
+const setDragModalLoading = (dragItem, text = '保存中...', show = false) => {
+    if (dragItem && show) {
+        dragItem.loading = true
+        dragItem.loadingText = text
+    }
+    if (dragItem && !show) {
+        dragItem.loading = false
+    }
+}
+
+//获取表单的ref
+const getFormRef = async (pkeyId) => {
+    const itemRef = itemRefs.value
+    const index = getIndex(itemRef, 'pkeyId', pkeyId)
+    return itemRef[index].ref
+}
+
+//删除打开的窗口
+const delWindowRefs = (pkeyId) => {
+    //判断是否存在窗口,如果存在,就删除窗口
+    const list = DragModalTableForm.value
+    const index = getIndex(list, 'pkeyId', pkeyId)
+    if (index !== -1) {
+        list.splice(index, 1)
+        DragModalTableForm.value = list
+    }
+}
+
+//计算展开高度和滚动位置
+const getOffsetTop = (key = '') => {
+    if (key) {
+        const dom = document.getElementById(key[key.length - 1])
+        if (!draw_type.value) {
+            if (dom?.offsetTop >= 583 && ActiveKey.value.length > 1) {
+                emit('offsetTop', dom?.offsetTop - 583)
+            } else {
+                emit('offsetTop', dom?.offsetTop)
+            }
+        } else {
+            if (dom.offsetTop >= 424 && ActiveKey.value.length > 1) {
+                emit('offsetTop', dom?.offsetTop - 424)
+            } else {
+                emit('offsetTop', dom?.offsetTop)
+            }
+        }
+    } else {
+        emit('offsetTop', 0)
+    }
+    ActiveKey.value = ActiveKey.value[ActiveKey.value.length - 1]
+}
+
+//获取折叠面板的索引
+const getCollapseItemIndex = (name) => {
+    let newList = [], keys = []
+    if (isString(name)) {
+        newList.push(name)
+    } else {
+        newList = name
+    }
+    if (newList.length > 0) {
+        keys = newList[0] ? newList[newList.length - 1].split('-') : newList[0]
+    }
+    if (keys.length > 0) {
+        return keys[1]
+    } else {
+        return -1
+    }
+}
+
+//获取表单的大小
+const getTableFormSize = (pkeyId) => {
+    let formId = `table-form-${pkeyId}`
+    try {
+        const {clientWidth, clientHeight} = document.getElementById(formId).children[0]
+        return {
+            width: (clientWidth + 40) + 'px',
+            height: (clientHeight + 80) + 'px'
+        }
+    } catch {
+        return {
+            width: '100%',
+            height: '100%'
+        }
+    }
+}
+
+//转字符串
+const setToString = (val) => {
+    return val ? val + '' : ''
+}
+
+//获取已渲染的表单
+const getFilterFormData = async () => {
+    const formArr = formDataList.value;
+    return formArr.filter((item) => {
+        return (item.pkeyId ?? '') !== '' && item.isCollapseLoad;
+    })
+}
+
+//获取表单数据
+const getFormData = async () => {
+    const formArr = await getFilterFormData();
+    //获取表单数据
+    let newArr = [];
+    for (let i = 0; i < formArr.length; i++) {
+        const pkeyId = formArr[i].pkeyId
+        const refs = await getFormRef(pkeyId)
+        const form = refs?.getFormData()
+        newArr.push(form)
+    }
+    return newArr
+}
+
+//获取表单效验数据
+const getFormRegExpJson = async () => {
+    const formArr = await getFilterFormData();
+    //获取表单数据
+    let formRegExpJson = {};
+    for (let i = 0; i < formArr.length; i++) {
+        const pkeyId = formArr[i].pkeyId
+        const refs = await getFormRef(pkeyId)
+        formRegExpJson[pkeyId] = refs?.getRegExpJson()
+    }
+    return formRegExpJson
+}
+
+//获取当前展开项
+const getActiveKey = () => {
+    return ActiveKey.value;
+}
+
+//设置当前展开项
+const setActiveKey = (key) => {
+    return ActiveKey.value = key;
+}
+
+// 暴露出去
+defineExpose({
+    getFormData,
+    getFormRegExpJson,
+    getActiveKey,
+    setActiveKey
+})
+</script>
+
+<style lang="scss" scoped>
+@import "./index.scss";
+</style>
+
+<style lang="scss">
+@import "./style.scss";
+</style>

+ 121 - 0
src/views/data-fill/collapse-form/special-diaolg.vue

@@ -0,0 +1,121 @@
+<template>
+    <el-form ref="specialFormRef" :model="specialFormModel" :rules="specialFormRules" class="mb-6" label-width="0px"
+             size="large">
+        <el-form-item class="special-form-item" prop="val">
+            <el-input id="specialId" ref="specialRef" v-model="specialFormModel.val"
+                      clearable placeholder="请选择特殊字符代码" @blur="specialInputBlur" @input="specialInputInput"/>
+        </el-form-item>
+    </el-form>
+    <el-row :gutter="20" style="margin: -10px;">
+        <el-col v-for="item in specialCharacters" :span="3" style="padding: 10px;">
+            <div class="special-box" @click="specialClick">
+                <span :title="`字符代码(C):${item !== 'K̅'?item.slice(2,7):'K̅'}`" class="font-EUDC" v-html="item"/>
+            </div>
+        </el-col>
+    </el-row>
+</template>
+
+<script setup>
+import {ref, nextTick} from "vue";
+import {formValidate, setPosInsert, setPosRange} from "vue-utils-plus"
+
+const specialRef = ref(null)
+const specialCharacters = [
+    '&#57344;', "&#57345;", "&#57346;", "&#57347;", '&#8804;', '&#8805;', '&#8451;',
+    '&#9312;', '&#9313;', '&#9314;', '&#9315;', '&#9316;', '&#9317;', '&#9318;', '&#9319;', '&#9320;', '&#9321;', '&#9322;', '&#9323;',
+    '&#9324;', '&#9325;', '&#9326;', '&#9327;', '&#9328;', '&#9329;', '&#9330;', '&#9331;',
+    "&#8544;", "&#8545;", "&#8546;", "&#8547;", "&#8548;", "&#8549;", "&#8550;", "&#8551;", "&#8552;", "&#8553;", "&#8554;", "&#8555;", "K̅"
+]
+
+nextTick(() => {
+    specialRef.value?.focus();
+})
+
+//输入框验证
+const specialFormRef = ref(null)
+const specialFormModel = ref({val: ''})
+const specialFormRules = {
+    val: {
+        required: true,
+        trigger: "blur",
+        message: "请选择特殊字符代码"
+    }
+}
+
+//事件
+const emit = defineEmits(['change'])
+
+
+//失去焦点
+const specialPos = ref({start: 0, end: 0})
+const specialInputBlur = (e) => {
+    specialPos.value = {
+        start: e?.target?.selectionStart || 0,
+        end: e?.target?.selectionEnd || 0
+    }
+}
+
+//点击符号
+const specialClick = (event) => {
+    const text = event?.target?.innerText ?? ''
+    const {start, end} = specialPos.value
+    const form = specialFormModel.value.val
+    specialFormModel.value.val = setPosInsert(start, end, form, text)
+    specialRef.value?.focus();
+    let posVal = start + text.length;
+    nextTick(() => {
+        setPosRange('specialId', posVal)
+    })
+}
+
+//输入框内容改变
+const specialInputInput = () => {
+    const val = specialFormModel.value.val ?? ''
+    emit('change', val)
+}
+
+//插入特殊符号的处理
+const getSpecialNode = async ({KeyName, startPos, endPos, pkeyId}, itemForm) => {
+    const res = await formValidate(specialFormRef.value)
+    if (res) {
+        const val = specialFormModel.value.val ?? ''
+        try {
+            const formValue = setPosInsert(startPos, endPos, itemForm, val)
+            let posVal = startPos + val.length;
+            return {code: 200, val: formValue, posVal}
+        } catch (e) {
+            return {code: 300, val: '', posVal: ''}
+        }
+    } else {
+        return {code: 300, val: '', posVal: ''}
+    }
+}
+
+// 暴露出去
+defineExpose({
+    getSpecialNode
+})
+</script>
+
+<style lang="scss">
+.special-box {
+    position: relative;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border: 1px solid #eee;
+    border-radius: 3px;
+    height: 52px;
+    width: 52px;
+    cursor: pointer;
+    user-select: none;
+    transition: color .3s, background-color .3s;
+    &:hover {
+        color: var(--el-color-primary);
+        background-color: var(--el-color-primary-light-8);
+    }
+    .font-EUDC {
+        font-size: 22px;
+    }
+}
+</style>

+ 105 - 0
src/views/data-fill/collapse-form/style.scss

@@ -0,0 +1,105 @@
+//插入特殊字符弹窗的输入框
+.data-fill-list-box .data-fill-table-form-box td,
+.data-fill-list-box .data-fill-table-form-box td .el-input .el-input__wrapper .el-input__inner,
+.el-form-item.special-form-item .el-form-item__content .el-input .el-input__wrapper .el-input__inner {
+    font-family: "EUDC", 宋体, v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+.data-fill-list-box {
+    .el-collapse {
+        --el-collapse-header-height: 60px;
+        border: 0;
+        .el-collapse-item {
+            margin: 0 0 16px;
+            background-color: #f1f5f8;
+            border: 1px solid #E9E9E9;
+            border-radius: 4px;
+        }
+        .el-collapse-item__header {
+            background-color: transparent;
+            font-weight: 400;
+            border-bottom: 0;
+            cursor: default;
+            font-size: 14px;
+            .el-collapse-item__arrow {
+                position: absolute;
+                color: #50545E;
+                cursor: pointer;
+                left: 20px;
+                margin: 0;
+            }
+        }
+        .el-collapse-item.is-active .el-collapse-item__header.is-active {
+            background-color: #E7EEF4;
+        }
+        .el-collapse-item__wrap {
+            background-color: transparent;
+            border-bottom: 0;
+            .el-collapse-item__content {
+                position: relative;
+                padding-bottom: 0;
+                font-size: 14px;
+                color: #50545E;
+                line-height: initial;
+            }
+        }
+    }
+}
+.data-fill-list-box .data-fill-table-form-box .hc-table-form-data-item {
+    overflow: auto;
+    background-color: initial;
+}
+
+//复制本表弹窗
+.copy-node-many-box {
+    position: relative;
+    height: 53vh;
+    display: flex;
+    margin-top: 24px;
+    margin-bottom: -30px;
+    border-top: 1px solid #efeff5;
+    .copy-node-many-tree {
+        position: relative;
+        flex: 1;
+        height: 100%;
+        padding: 20px 20px 20px 0;
+        border-right: 1px solid #efeff5;
+    }
+    .copy-node-many-table {
+        position: relative;
+        flex: 1;
+        height: 100%;
+        padding: 20px 0 20px 20px;
+    }
+}
+
+.copy-node-many-table {
+    position: relative;
+    flex: 1;
+    height: 100%;
+    padding: 20px 0 20px 20px;
+}
+.dialog-table-box {
+    position: relative;
+    flex: 1;
+    height: 100%;
+    padding: 18px;
+    .dialog-search {
+        position: relative;
+        display: flex;
+    }
+    .dialog-table {
+        position: relative;
+        height: calc(100% - 68px);
+        padding: 18px 0;
+    }
+    .dialog-pages {
+        position: relative;
+    }
+}
+.text-blue {
+    color: blue;
+}
+.text-green {
+    color: green
+}

+ 172 - 0
src/views/data-fill/collapse-form/test-data.vue

@@ -0,0 +1,172 @@
+<template>
+    <div>
+        <div class="flex-1" style="padding-left:20px">
+            <HcNewSwitch :datas="tabTypeTab" :keys="tabTypeKey" :round="false" size="default"
+                         @change="tabTypeChange"/>
+            <el-select v-model="testReportId" :loading="insertDataSelectoading" :placeholder="placeholderType"
+                       class="ml-2 w-80"
+                       clearable @change="testReportIdchange">
+                <el-option v-for="item in testReportData" :key="item.pKeyId" :label="item['nodeName']"
+                           :value="item['pKeyId']"/>
+            </el-select>
+        </div>
+        <div class="dialog-table-box">
+            <div class="copy-node-many-table">
+                <el-scrollbar v-loading="insertDataTableLoading">
+                    <el-table :data="insertDataTable" border stripe @selection-change="insertDataTableCheck">
+                        <el-table-column type="selection" width="55"/>
+                        <el-table-column label="字段名称" prop="key"/>
+                        <el-table-column label="数值" prop="value"/>
+                    </el-table>
+                </el-scrollbar>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import {ref, watch, onMounted} from "vue";
+import {getArrValue, getObjValue, setPosInsert} from "vue-utils-plus"
+import dataApi from "~api/tentative/detect/test";
+
+const props = defineProps({
+    projectId: [String, Number],
+    contractId: [String, Number],
+    tableId: [String, Number],
+    treeId: [String, Number],
+})
+
+//参数变量
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+const table_id = ref(props.tableId)
+const tree_id = ref(props.treeId)
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId,
+    props.tableId,
+    props.treeId,
+], ([pid, cid, tableId, treeId]) => {
+    projectId.value = pid
+    contractId.value = cid
+    table_id.value = tableId
+    tree_id.value = treeId
+})
+
+//渲染完成
+onMounted(() => {
+    getSearchNodeTables()
+})
+
+//事件
+const emit = defineEmits(['change'])
+
+//类型tab数据和相关处理
+const tabTypeKey = ref('2')
+const tabTypeTab = ref([
+    {key: '1', name: '试验记录表'},
+    {key: '2', name: '试验报告单'},
+]);
+const placeholderType = ref('试验报告单')
+const tabTypeChange = ({key}) => {
+    tabTypeKey.value = key
+    if (tabTypeKey.value === '1') {
+        placeholderType.value = '试验记录表'
+    } else {
+        placeholderType.value = '试验报告单'
+    }
+    getSearchNodeTables()
+}
+
+const insertDataSelectoading = ref(false)
+const testReportData = ref([])
+
+const getSearchNodeTables = async () => {
+    insertDataSelectoading.value = true
+    const {error, code, data} = await dataApi.searchNodeTables({
+        id: table_id.value,
+        projectId: projectId.value,
+        contractId: contractId.value,
+        primaryKeyId: tree_id.value,
+        // type: authBtnTabKey.value,
+        tableType: tabTypeKey.value
+    })
+    //处理数据
+    insertDataSelectoading.value = false
+    if (!error && code === 200) {
+        testReportData.value = getArrValue(data)
+        if (testReportData.value.length > 0) {
+            testReportId.value = testReportData.value[0].pKeyId
+            getBussddataInfotrialData()
+        }
+    } else {
+        testReportData.value = []
+    }
+}
+
+const testReportId = ref('');
+const checPkd = ref('')
+const testReportIdchange = (key) => {
+    testReportData.value.forEach((item) => {
+        if (item.id == key) {
+            checPkd.value = item.pKeyId
+        }
+    })
+    getBussddataInfotrialData()
+}
+
+const insertDataTableLoading = ref(false)
+const insertDataTable = ref([])
+const multipleSelection = ref([])
+const insertDataTableCheck = (val) => {
+    multipleSelection.value = val
+}
+
+//获取试验表中的data数据接口:
+const getBussddataInfotrialData = async () => {
+    insertDataTableLoading.value = true;
+    const {error, code, data} = await dataApi.getBussddataInfotrialList({
+        // id:testReportId.value,
+        id: table_id.value,
+        pkeyId: testReportId.value,
+    })
+    insertDataTableLoading.value = false
+    if (!error && code === 200) {
+        let arrobj = getObjValue(data)
+        let arr = []
+        for (let key of Object.keys(arrobj)) {
+            arr.push({
+                key: key,
+                value: arrobj[key]
+            })
+        }
+        insertDataTable.value = arr
+    } else {
+        insertDataTable.value = []
+    }
+}
+
+//确定关联试验数据数据
+const submitinsertData = ({KeyName, startPos, endPos, pkeyId}, itemForm) => {
+    if (multipleSelection.value.length > 0) {
+        const val = []
+        multipleSelection.value.forEach((item) => {
+            val.push(item['value'])
+        })
+        const newval = val.join('、')
+        const formValue = setPosInsert(startPos, endPos, itemForm, newval)
+        let posVal = startPos + newval.length;
+        return {code: 200, val: formValue, posVal}
+    } else {
+        window?.$message?.warning('请选择你要关联的数据')
+        return {code: 300, val: '', posVal: ''}
+    }
+}
+
+// 暴露出去
+defineExpose({
+    submitinsertData
+})
+</script>

+ 191 - 0
src/views/data-fill/collapse-form/test-diaolg.vue

@@ -0,0 +1,191 @@
+<template>
+    <div class="adding-form-dialog-box">
+        <div class="dialog-tree-box">
+            <el-scrollbar>
+                <TestTree :autoExpandKey="testTreeExpandKeys"
+                          :contractId="contractId"
+                          :projectId="projectId"
+                          :tenantId="tenant_id"
+                          :wbsId="wbsTempId"
+                          :wbsType="wbs_type"
+                          @nodeTap="testElTreeClick"/>
+            </el-scrollbar>
+        </div>
+        <div class="dialog-table-box">
+            <div class="dialog-search">
+                <div class="w-64 ml-2">
+                    <HcDatePicker :dates="testBetweenTime" clearable @change="testBetweenTimeUpdate"/>
+                </div>
+                <div class="ml-2">
+                    <el-button type="primary" @click="testSearchClick">
+                        <HcIcon name="search-2"/>
+                        <span>搜索</span>
+                    </el-button>
+                </div>
+            </div>
+            <div class="dialog-table">
+                <HcTable :column="testDialogTableColumn" :datas="testDialogTableData" :loading="testDialogTableLoading">
+                    <template #reportNo="{row}">
+                        <span class="text-link" @click="testTableRowName(row)">{{ row?.reportNo }}</span>
+                    </template>
+                </HcTable>
+            </div>
+            <div class="dialog-pages">
+                <HcPages :pages="testSearchFormPage" @change="testSearchFormPageChange"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import {ref, watch, onMounted} from "vue";
+import TestTree from "./testTree.vue"
+import {getArrValue} from "vue-utils-plus"
+import {getStoreData, setStoreData} from "~uti/storage";
+import samplingApi from "~api/tentative/material/sampling";
+
+const props = defineProps({
+    projectId: [String, Number],
+    contractId: [String, Number],
+    wbsId: [String, Number],
+    tenantId: [String, Number],
+    wbsType: [String, Number],
+})
+
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+
+const wbsTempId = ref(props.wbsId)
+const tenant_id = ref(props.tenantId)
+const wbs_type = ref(props.wbsType)
+
+const testTreeExpandKeys = ref([])
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId,
+    props.wbsId,
+    props.tenantId,
+    props.wbsType,
+], ([pid, cid, wbs_id, tid, type]) => {
+    projectId.value = pid
+    contractId.value = cid
+    wbsTempId.value = wbs_id
+    tenant_id.value = tid
+    wbs_type.value = type
+})
+
+//渲染完成
+onMounted(() => {
+    testTreeExpandKeys.value = getStoreData('testTreeAutoExpandKeys') || []
+})
+
+//事件
+const emit = defineEmits(['change'])
+
+//实验树被点击
+const treeId = ref('')
+const testElTreeClick = ({data, keys}) => {
+    const nodeId = data?.primaryKeyId || ''
+    treeId.value = nodeId
+    testSearchFormPage.value.nodeId = nodeId
+    testSearchClick()
+    //缓存展开的节点
+    testTreeExpandKeys.value = keys.value || []
+    setStoreData('testTreeAutoExpandKeys', keys.value)
+}
+
+//搜索表单
+const testSearchFormPage = ref({
+    startTime: null, endTime: null, nodeId: null, current: 1, size: 20, total: 0
+})
+
+//时间日期
+const testBetweenTime = ref([])
+const testBetweenTimeUpdate = ({arr}) => {
+    testBetweenTime.value = arr
+    testSearchFormPage.value.startTime = arr[0]
+    testSearchFormPage.value.endTime = arr[1]
+}
+
+//搜索
+const testSearchClick = () => {
+    testSearchFormPage.value.current = 1;
+    getDialogTableData()
+}
+
+//分页
+const testSearchFormPageChange = ({current, size}) => {
+    testSearchFormPage.value.current = current
+    testSearchFormPage.value.size = size
+    getDialogTableData()
+}
+
+//表格数据
+const testDialogTableColumn = ref([
+    {key: 'reportNo', name: '报告编号'},
+    {key: 'reportDate', name: '报告日期'},
+    {key: 'projectPositionName', name: '工程用途及部位'},
+    {key: 'detectionResultName', name: '检测结果'},
+])
+const testDialogTableData = ref([])
+
+//获取表格数据
+const testDialogTableLoading = ref(false)
+const getDialogTableData = async () => {
+    testDialogTableLoading.value = true
+    const {error, code, data} = await samplingApi.gettrialPage({
+        ...testSearchFormPage.value,
+        projectId: projectId.value,
+        contractId: contractId.value
+    })
+    //处理数据
+    testDialogTableLoading.value = false
+    if (!error && code === 200) {
+        testDialogTableData.value = getArrValue(data['records'])
+        testSearchFormPage.value.total = data.total || 0
+    } else {
+        testDialogTableData.value = []
+        testSearchFormPage.value.total = 0
+    }
+}
+
+//名称被点击
+const testTableRowName = (row) => {
+    emit('change', {row: row, treeId: treeId.value})
+}
+</script>
+
+<style lang="scss">
+//关联试验数据
+.adding-form-dialog-box {
+    position: relative;
+    height: 100%;
+    display: flex;
+    .dialog-tree-box {
+        position: relative;
+        border-right: 1px solid #EEEEEE;
+        width: 500px;
+        height: 100%
+    }
+    .dialog-table-box {
+        position: relative;
+        flex: 1;
+        height: 100%;
+        padding: 18px;
+        .dialog-search {
+            position: relative;
+            display: flex;
+        }
+        .dialog-table {
+            position: relative;
+            height: calc(100% - 68px);
+            padding: 18px 0;
+        }
+        .dialog-pages {
+            position: relative;
+        }
+    }
+}
+</style>

+ 409 - 0
src/views/data-fill/collapse-form/test-file.vue

@@ -0,0 +1,409 @@
+<template>
+    <div class="radio-box">
+        <el-radio-group v-model="fileModalradio" @change="radioChange">
+            <el-radio :label="1">试验检测</el-radio>
+            <el-radio :label="2">第三方检测</el-radio>
+            <el-radio :label="3">外委检测</el-radio>
+        </el-radio-group>
+    </div>
+    <div class="adding-form-dialog-box">
+        <div class="dialog-tree-box">
+            <el-scrollbar>
+                <!-- 试验检测树 -->
+                <ElTree v-if="fileModalradio==1"
+                        :indent="0"
+                        :load="ElTreeLoadNode"
+                        :props="ElTreeProps"
+                        accordion
+                        class="hc-tree-node tree-line"
+                        highlight-current
+                        lazy
+                        node-key="primaryKeyId"
+                        @node-click="fileModalElTreeClick"
+                >
+                </ElTree>
+                <!-- 第三方树 -->
+                <ElTree
+                    v-else
+                    ref="ElTreeRef2" :data="thirdtreeDatas" :indent="0" :props="thirdElTreeProps"
+                    accordion class="hc-tree-node" highlight-current
+                    node-key="id" @node-click="thirdtreeDatasElTreeClick">
+                </ElTree>
+            </el-scrollbar>
+        </div>
+        <div class="dialog-table-box">
+            <div class="dialog-search">
+                <div class="w-64 ml-2">
+                    <HcDatePicker :dates="filebetweenTime" clearable @change="filebetweenTimeUpdate"/>
+                </div>
+                <div class="ml-2">
+                    <el-button type="primary" @click="filesearchClick">
+                        <HcIcon name="search-2"/>
+                        <span>搜索</span>
+                    </el-button>
+                </div>
+            </div>
+            <div class="dialog-table">
+                <HcTable v-if="fileModalradio==1" ref="dialogTableRef1" :column="filedialogTableColumn"
+                         :datas="filedialogTableData" :loading="filedialogTableLoading" isCheck
+                         @selection-change="filedialogTableSelection1">
+                    <template #reportNo="{row}">
+                        <span :class="[row?.isSelectedStatus==1?'text-green':'']">{{ row?.reportNo }}</span>
+                    </template>
+                </HcTable>
+                <HcTable v-if="fileModalradio==2" ref="dialogTableRef2" :column="filedialogTableColumn1"
+                         :datas="filedialogTableData" :loading="filedialogTableLoading" isCheck
+                         @selection-change="filedialogTableSelection">
+                    <template #reportNo="{row}">
+                        <span :class="[row?.isSelectedStatus===1?'text-green':'']">{{ row?.reportNo }}</span>
+                    </template>
+                </HcTable>
+                <HcTable v-if="fileModalradio==3" ref="dialogTableRef" :column="filedialogTableColumn1"
+                         :datas="filedialogTableData" :loading="filedialogTableLoading" isCheck
+                         @selection-change="filedialogTableSelection">
+                    <template #reportNo="{row}">
+                        <span :class="[row?.isSelectedStatus===1?'text-green':'']">{{ row?.reportNo }}</span>
+                    </template>
+                </HcTable>
+
+            </div>
+            <div class="dialog-pages">
+                <HcPages :pages="filesearchFormPage" @change="filesearchFormPageChange"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import {ref, watch, onMounted, nextTick} from "vue";
+import {getArrValue, isItem} from "vue-utils-plus"
+import {getTreeAll} from "~api/tentative/detect";
+import thirdApi from "~api/tentative/detect/third";
+import samplingApi from "~api/tentative/material/sampling";
+
+const props = defineProps({
+    projectId: [String, Number],
+    contractId: [String, Number],
+    wbsId: [String, Number],
+    tenantId: [String, Number],
+    wbsType: [String, Number],
+    treeId: [String, Number],
+})
+
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+
+const wbsTempId = ref(props.wbsId)
+const tenant_id = ref(props.tenantId)
+const wbs_type = ref(props.wbsType)
+const isPrimaryKeyId = ref(props.treeId)
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId,
+    props.wbsId,
+    props.tenantId,
+    props.wbsType,
+    props.treeId
+], ([pid, cid, wbs_id, tid, type, treeId]) => {
+    projectId.value = pid
+    contractId.value = cid
+    wbsTempId.value = wbs_id
+    tenant_id.value = tid
+    wbs_type.value = type
+    isPrimaryKeyId.value = treeId
+})
+
+const CTDTreeAutoExpandKeys = ref([])
+const defaultExpandedCids = ref([])
+const ElTreeProps = ref({
+    label: 'title',
+    children: 'children',
+    isLeaf: 'hasChildren'
+})
+const ElTreeLoadNode = async (node, resolve) => {
+    let parentId = '0';
+    if (node.level !== 0) {
+        parentId = node?.data?.id
+    }
+    //获取数据
+    const {error, code, data} = await samplingApi.queryLazyTree({
+        wbsId: wbsTempId.value,
+        tenantId: tenant_id.value,
+        projectId: projectId.value,
+        parentId,
+        wbsType: wbs_type.value
+    })
+    //处理数据
+    if (!error && code === 200) {
+        let clickKey = '', defaultExpandedArr = [];
+        const keys = CTDTreeAutoExpandKeys.value || []
+        const resData = getArrValue(data)
+        for (let i = 0; i < resData.length; i++) {
+            resData[i].hasChildren = !resData[i].hasChildren
+        }
+        if (keys.length > 0) {
+            let lastKey = keys[keys.length - 1];
+            for (const item of resData) {
+                //自动展开
+                if (isItem(keys, item?.primaryKeyId)) {
+                    defaultExpandedArr.push(item?.primaryKeyId)
+                }
+                //最后一个,选中点击
+                if (item?.primaryKeyId === lastKey) {
+                    clickKey = item?.primaryKeyId
+                }
+            }
+        } else if (node.level === 0) {
+            defaultExpandedArr.push(resData[0]?.primaryKeyId)
+        }
+        //自动展开
+        defaultExpandedCids.value = defaultExpandedArr
+        if (node.level === 0) {
+            if (resData.length <= 0) {
+                window?.$message?.warning('该项目未使用试验系统,无法获取数据')
+            }
+        }
+        resolve(resData)
+    } else {
+        resolve([])
+    }
+}
+
+//关联试验文件
+const filedialogTableData = ref([])
+const fileModalradio = ref(1)
+const radioChange = (val) => {
+    if (val !== 1) {
+        getthirdTreeDatas(val)
+    }
+}
+//获取试验文件树树数据
+const fileModaltreeDatas = ref([])
+//节点被点击
+//节点被点击
+const filenodeItemInfo = ref({})
+const filenodeDataInfo = ref({})
+const fileTableData1 = ref([])
+const fileTableColumn = ref([
+    {key: 'recordNo', name: '报告编号'},
+    {key: 'reportDate', name: '报告日期'},
+    {key: 'projectPositionName', name: '工程用途及部位'},
+    {key: 'detectionResultName', name: '检测结果'},
+])
+
+const fileModalElTreeClick = async (data, node) => {
+    filenodeItemInfo.value = node
+    filenodeDataInfo.value = data
+    getfileNodeData()
+}
+
+//获取第三方树试验文件
+
+//获取树数据
+const thirdtreeDatas = ref([])
+const thirdElTreeProps = ref({
+    label: 'nodeName',
+    children: 'children',
+    isLeaf: 'hasChildren'
+})
+const getthirdTreeDatas = async (type) => {
+    const {error, code, data} = await getTreeAll({
+        projectId: projectId.value,
+        contractId: contractId.value
+    })
+    if (!error && code === 200) {
+        if (type == 2) {
+            thirdtreeDatas.value = getArrValue(data['rightTree'])
+        } else {
+            thirdtreeDatas.value = getArrValue(data['leftTree'])
+        }
+
+
+    } else {
+        thirdtreeDatas.value = []
+    }
+}
+const thirdfilenodeItemInfo = ref({})
+const thirdfilenodeDataInfo = ref({})
+//获取第三方树节点下的数据
+const getthirdTreetavleDatas = async () => {
+    filedialogTableLoading.value = true
+    const {error, code, data} = await thirdApi.queryPage({
+        projectId: projectId.value,
+        contractId: contractId.value,
+        nodeId: thirdfilenodeDataInfo.value.id,
+        current: filesearchFormPage.value.current,
+        size: filesearchFormPage.value.size,
+        startTime: filesearchFormPage.value.startTime,
+        endTime: filesearchFormPage.value.lastTime,
+        qualityTestPKeyId: isPrimaryKeyId.value
+
+
+    })
+    //处理数据
+    if (!error && code === 200) {
+        filedialogTableData.value = getArrValue(data['records'])
+        filesearchFormPage.value.total = data.total || 0
+        filedialogTableLoading.value = false
+        if (filedialogTableData.value.length > 0) {
+            let defaultarr = []
+            filedialogTableData.value.forEach((item) => {
+                if (item.isSelectedStatus === 1) {
+                    defaultarr.push(item)
+
+                }
+                if (fileModalradio === 2) {
+                    outtabtoggleSelection(defaultarr)
+                } else {
+                    thirdtabtoggleSelection(defaultarr)
+                }
+
+            })
+        }
+    } else {
+        filedialogTableData.value = []
+        filesearchFormPage.value.total = 0
+        filesearchFormPage.value = false
+    }
+    filedialogTableLoading.value = false
+}
+const thirdtreeDatasElTreeClick = (data, node) => {
+    thirdfilenodeItemInfo.value = node
+    thirdfilenodeDataInfo.value = data
+    getthirdTreetavleDatas()
+}
+
+const filebetweenTime = ref([])
+const filesearchFormPage = ref({
+    startTime: null, lastTime: null, wbsId: null, current: 1, size: 20, total: 0
+})
+const filesearchFormPageChange = ({current, size}) => {
+    filesearchFormPage.value.current = current
+    filesearchFormPage.value.size = size
+    // getDialogTableData()
+}
+const filebetweenTimeUpdate = ({arr}) => {
+    filebetweenTime.value = arr
+    filesearchFormPage.value.startTime = arr[0]
+    filesearchFormPage.value.lastTime = arr[1]
+}
+
+const filesearchClick = () => {
+    filesearchFormPage.value.current = 1;
+    getfileNodeData()
+}
+const filedialogTableLoading = ref(false)
+
+const filedialogTableColumn = ref([
+    // reportNo
+    {key: 'reportNo', name: '报告编号'},
+    {key: 'reportDate', name: '报告日期'},
+    {key: 'projectPositionName', name: '工程用途及部位'},
+    {key: 'detectionResultName', name: '检测结果'},
+])
+const filedialogTableColumn1 = ref([
+    // reportNo
+    {key: 'reportNo', name: '报告编号'},
+    {key: 'reportDate', name: '报告日期'},
+    {key: 'projectPosition', name: '工程用途及部位'},
+    {key: 'detectionResultName', name: '检测结果'},
+])
+
+//多选
+const filetableCheckedKeys = ref([]);
+const filedialogTableSelection1 = (rows) => {
+    filetableCheckedKeys.value = rows.filter((item) => {
+        return (item ?? '') !== '';
+    })
+}
+const filedialogTableSelection = (rows) => {
+    filetableCheckedKeys.value = rows.filter((item) => {
+        return (item ?? '') !== '';
+    })
+}
+
+//试验文件节点下的数据
+const getfileNodeData = async () => {
+    //获取数据
+    filedialogTableLoading.value = true
+    const {error, code, data} = await samplingApi.gettrialPage({
+        contractId: contractId.value,
+        nodeId: filenodeDataInfo.value.primaryKeyId,
+        current: filesearchFormPage.value.current,
+        size: filesearchFormPage.value.size,
+        startTime: filesearchFormPage.value.startTime,
+        endTime: filesearchFormPage.value.lastTime,
+        qualityTestPKeyId: isPrimaryKeyId.value
+    })
+    //处理数据
+    if (!error && code === 200) {
+        filedialogTableData.value = getArrValue(data['records'])
+        filesearchFormPage.value.total = data.total || 0
+        filedialogTableLoading.value = false
+        let defaultarr = []
+        filedialogTableData.value.forEach((item) => {
+            if (item.isSelectedStatus === 1) {
+                defaultarr.push(item)
+            }
+            tabtoggleSelection(defaultarr)
+        })
+
+    } else {
+        filedialogTableData.value = []
+        filesearchFormPage.value.total = 0
+        filesearchFormPage.value = false
+    }
+}
+
+const dialogTableRef = ref(null)
+const dialogTableRef1 = ref(null)
+const dialogTableRef2 = ref(null)
+
+const tabtoggleSelection = (rows) => {
+    if (rows) {
+        rows.forEach(row => {
+            nextTick(() => {
+                dialogTableRef1.value?.toggleRowSelection(row, true);
+            })
+        });
+    } else {
+        dialogTableRef1.value?.clearSelection();
+
+    }
+}
+
+//确认关联试验文件
+const savefileSubmit = async () => {
+    const idarr = []
+    filetableCheckedKeys.value.forEach((item) => {
+        idarr.push(item.id)
+    })
+    const idval = idarr.join(',')
+    await savesubmitRelationFile(idval)
+}
+
+//关联试验文件
+const savesubmitRelationFile = async (ids) => {
+    const {error, code, data} = await samplingApi.submitRelationFile({
+        projectId: projectId.value,
+        contractId: contractId.value,
+        nodeId: isPrimaryKeyId.value,
+        type: fileModalradio.value,
+        ids
+    })
+    if (!error && code === 200) {
+        window?.$message?.success('操作成功')
+    }
+}
+
+// 暴露出去
+defineExpose({
+    savefileSubmit
+})
+</script>
+
+<style lang="scss">
+
+</style>

+ 158 - 0
src/views/data-fill/collapse-form/testTree.vue

@@ -0,0 +1,158 @@
+<template>
+    <ElTree key="primaryKeyId" ref="ElTreeRef" :autoExpandKeys="autoExpandKeys"
+            :default-expanded-keys="defaultExpandedCids"
+            :indent="0"
+            :load="ElTreeLoadNode" :props="ElTreeProps"
+            accordion class="hc-tree-node tree-line"
+            highlight-current lazy
+            node-key="primaryKeyId" @node-click="ElTreeClick">
+        <template #default="{ node, data }">
+            <div :id="`test-tree-${data['primaryKeyId']}`" class="data-custom-tree-node">
+                <!--树组件,节点名称-->
+                <div :class="node.level === 1?'level-name':''" class="label">
+                    <span>{{ node.label }}</span>
+                </div>
+            </div>
+        </template>
+    </ElTree>
+</template>
+
+<script setup>
+import {ref, nextTick, watch} from "vue";
+import {isItem, getArrValue} from "vue-utils-plus"
+import samplingApi from "~api/tentative/material/sampling";
+
+//参数
+const props = defineProps({
+    projectId: [String, Number],
+    contractId: [String, Number],
+    wbsId: [String, Number],
+    tenantId: [String, Number],
+    wbsType: [String, Number],
+    autoExpandKey: {
+        type: Array,
+        default: () => ([])
+    },
+})
+
+//变量
+const ElTreeRef = ref(null)
+const ElTreeProps = ref({
+    label: 'title',
+    children: 'children',
+    isLeaf: 'hasChildren'
+})
+
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+
+const wbsTempId = ref(props.wbsId)
+const tenant_id = ref(props.tenantId)
+const wbs_type = ref(props.wbsType)
+
+const autoExpandKeys = ref(props.autoExpandKey)
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId,
+    props.wbsId,
+    props.tenantId,
+    props.wbsType,
+    props.autoExpandKey,
+], ([pid, cid, wbs_id, tid, type, keys]) => {
+    projectId.value = pid
+    contractId.value = cid
+    wbsTempId.value = wbs_id
+    tenant_id.value = tid
+    wbs_type.value = type
+    autoExpandKeys.value = keys
+})
+
+//事件
+const emit = defineEmits(['nodeTap', 'nodeLoading'])
+
+//树形结构异步加载数据
+const defaultExpandedCids = ref([])
+const ElTreeLoadNode = async (node, resolve) => {
+    let parentId = '0';
+    if (node.level !== 0) {
+        parentId = node?.data?.id
+    }
+    //获取数据
+    const {error, code, data} = await samplingApi.queryLazyTree({
+        wbsId: wbsTempId.value,
+        tenantId: tenant_id.value,
+        projectId: projectId.value,
+        parentId,
+        wbsType: wbs_type.value
+    })
+    //处理数据
+    if (!error && code === 200) {
+        let clickKey = '', defaultExpandedArr = [];
+        const keys = autoExpandKeys.value || []
+        const resData = getArrValue(data)
+        for (let i = 0; i < resData.length; i++) {
+            resData[i].hasChildren = !resData[i].hasChildren
+        }
+        if (keys.length > 0) {
+            let lastKey = keys[keys.length - 1];
+            for (const item of resData) {
+                //自动展开
+                if (isItem(keys, item?.primaryKeyId)) {
+                    defaultExpandedArr.push(item?.primaryKeyId)
+                }
+                //最后一个,选中点击
+                if (item?.primaryKeyId === lastKey) {
+                    clickKey = item?.primaryKeyId
+                }
+            }
+        } else if (node.level === 0) {
+            defaultExpandedArr.push(resData[0]?.primaryKeyId)
+        }
+        //自动展开
+        defaultExpandedCids.value = defaultExpandedArr
+        if (node.level === 0) {
+            emit('nodeLoading')
+            if (resData.length <= 0) {
+                window?.$message?.warning('该项目未使用试验系统,无法获取数据')
+            }
+        }
+        resolve(resData)
+        //最后一个,执行点击
+        if (clickKey) {
+            await nextTick(() => {
+                document.getElementById(`test-tree-${clickKey}`)?.click()
+            })
+        }
+    } else {
+        if (node.level === 0) {
+            emit('nodeLoading')
+        }
+        resolve([])
+    }
+}
+
+//节点被点击
+const ElTreeClick = async (data, node) => {
+    let autoKeysArr = []
+    await getNodeExpandKeys(node, autoKeysArr)
+    const autoKeys = autoKeysArr.reverse()
+    emit('nodeTap', {node, data, keys: autoKeys})
+}
+
+//处理自动展开的节点KEY
+const getNodeExpandKeys = async (node, newKeys) => {
+    const parent = node?.parent ?? []
+    const primaryKeyId = node?.data?.primaryKeyId ?? ''
+    if (primaryKeyId) {
+        newKeys.push(primaryKeyId)
+        await getNodeExpandKeys(parent, newKeys)
+    }
+}
+
+</script>
+
+<style lang="scss" scoped>
+@import "~src/styles/app/tree.scss";
+</style>

+ 19 - 6
src/views/data-fill/wbs.vue

@@ -186,8 +186,21 @@
                     </template>
                     <!--清表列表-->
                     <el-scrollbar v-if="ListItemDatas.length > 0" ref="ListItemScrollRef" v-loading="ListItemLoading">
-                        <ListItem ref="ListItemRef"
-                                  :authBtnTabKey="authBtnTabKey"
+                        <CollapseForm ref="ListItemRef"
+                                      :authBtnTabKey="authBtnTabKey"
+                                      :classify="authBtnTabKey"
+                                      :contractId="contractId"
+                                      :datas="ListItemDatas"
+                                      :drawType="!isDrawType"
+                                      :primaryKeyId="primaryKeyId"
+                                      :status="NodeStatus"
+                                      :tenantId="userInfo?.tenant_id"
+                                      :wbsTempId="projectInfo?.referenceWbsTemplateIdTrial"
+                                      :wbsType="2"
+                                      @offsetTop="ListItemOffsetTop"
+                                      @renew="getTableDataAll"/>
+
+                        <!--ListItem ref="ListItemRef"
                                   :classify="authBtnTabKey"
                                   :contractId="contractId"
                                   :datas="ListItemDatas"
@@ -199,7 +212,7 @@
                                   :wbsTempId="projectInfo?.referenceWbsTemplateIdTrial"
                                   :wbsType="2"
                                   @offsetTop="ListItemOffsetTop"
-                                  @renew="getTableDataAll"/>
+                                  @renew="getTableDataAll"/-->
                     </el-scrollbar>
 
                     <HcStatus v-else text="暂无表单"/>
@@ -642,6 +655,7 @@ import {useRouter, useRoute} from 'vue-router'
 import {useAppStore} from "~src/store";
 import {HcIsButton} from "~src/plugins/IsButtons";
 import ListItem from "./components/ListItem.vue"
+import CollapseForm from "./collapse-form/index.vue"
 import NodeTree from "./components/nodeTree/index.vue"
 import HcTreeNode from "./components/HcTreeNode.vue"
 import HcTreeData from "./components/HcTreeData.vue"
@@ -1549,7 +1563,6 @@ const searchNodeAllTable = async () => {
 const NodeStatus = ref('1')
 const queryNodeStatus = async () => {
     const info = nodeDataInfo.value;
-    console.log(info, 'info')
     if (contractInfo.value?.contractType == 1) {
         const {error, code, data} = await wbsApi.queryNodeStatus({
             primaryKeyId: info['primaryKeyId'],
@@ -1658,12 +1671,12 @@ const tableFormSaveClick = async () => {
         tableFormSaveLoading.value = false
         if (!error && code === 200) {
             window?.$message?.success('保存成功')
-            bussPdfsClick()
+            await bussPdfsClick()
             getTableDataAll()
         }
     } else {
         //http://testcandao.hcxxy.com/bug-view-819.html
-        bussPdfsClick()
+        await bussPdfsClick()
     }
 }
 

Some files were not shown because too many files changed in this diff