iZaiZaiA 3 jaren geleden
bovenliggende
commit
c1ec78d7fd

+ 4 - 4
package.json

@@ -16,11 +16,11 @@
         "js-md5": "^0.7.3",
         "js-web-screen-shot": "^1.7.1",
         "moment": "^2.29.4",
-        "naive-ui": "^2.32.0",
-        "pinia": "^2.0.18",
+        "naive-ui": "^2.32.2",
+        "pinia": "^2.0.19",
         "vue": "^3.2.37",
         "vue-router": "^4.1.3",
-        "vue-utils-plus": "^1.0.1",
+        "vue-utils-plus": "^1.0.2",
         "vuedraggable": "^4.1.0"
     },
     "devDependencies": {
@@ -31,7 +31,7 @@
         "postcss": "^8.4.16",
         "sass": "^1.54.4",
         "tailwindcss": "^3.1.8",
-        "unplugin-auto-import": "^0.11.1",
+        "unplugin-auto-import": "^0.11.2",
         "unplugin-vue-components": "^0.22.4",
         "vfonts": "^0.0.3",
         "vite": "^3.0.8",

+ 8 - 0
src/api/modules/data-fill/wbs.js

@@ -185,5 +185,13 @@ export default {
             data: form
         });
     },
+    //输入框查询合同段树
+    async searchContractTree(form) {
+        return httpApi({
+            url: '/api/blade-business/informationWriteQuery/searchContractTree',
+            method: 'get',
+            params: form
+        })
+    },
 }
 

+ 12 - 2
src/global/components/hc-card/index.vue

@@ -22,7 +22,7 @@
         <div class="hc-card-search-bar" v-if="isSlotSearchBar">
             <slot name='search'/>
         </div>
-        <div class="hc-card-main-box" :class="isSlotAction?'is-action':''">
+        <div class="hc-card-main-box" :id="idRef" :class="isSlotAction?'is-action':''">
             <template v-if="scrollbar">
                 <el-scrollbar>
                     <slot></slot>
@@ -39,7 +39,7 @@
 </template>
 
 <script setup>
-import {ref,useSlots,watch} from "vue";
+import {ref,useSlots} from "vue";
 const props = defineProps({
     ui: {
         type: String,
@@ -61,6 +61,10 @@ const props = defineProps({
         type: [String,Number],
         default: 'df'
     },
+    idRef: {
+        type: [String,Number],
+        default: ''
+    },
 })
 
 const slots = useSlots()
@@ -139,6 +143,12 @@ const isSlotSearchBar = ref(!!slots.search);
             height: calc(100% - 124px);
         }
     }
+    &.is-search-bar.is-action-lg .el-card__body .hc-card-main-box {
+        height: calc(100% - 40px);
+        &.is-action {
+            height: calc(100% - 124px);
+        }
+    }
 }
 .hc-card-box.el-card:not(.is-header) {
     .el-card__body {

+ 107 - 0
src/global/components/hc-drawer/index.vue

@@ -0,0 +1,107 @@
+<template>
+    <Suspense v-if="isBody">
+        <Teleport :to="`#${toId}`">
+            <el-drawer :custom-class="`hc-drawer-box ${ui}`" v-model="isShow" :with-header="false" :direction="direction" :size="size">
+                <HcCard :title="title" :extraText="extraText" :actionSize="actionSize" :scrollbar="scrollbar">
+                    <template #header v-if="isSlotHeader">
+                        <slot name='header'/>
+                    </template>
+                    <template #extra v-if="isSlotExtra">
+                        <slot name='extra'/>
+                    </template>
+                    <template #search v-if="isSlotSearchBar">
+                        <slot name='search'/>
+                    </template>
+                    <template #action v-if="isSlotAction">
+                        <slot name='action'/>
+                    </template>
+                    <slot></slot>
+                </HcCard>
+            </el-drawer>
+        </Teleport>
+    </Suspense>
+</template>
+
+<script setup>
+import {ref, nextTick, watch, useSlots} from "vue";
+const props = defineProps({
+    ui: {
+        type: String,
+        default: ''
+    },
+    show: {
+        type: Boolean,
+        default: false
+    },
+    toId: {
+        type: [String,Number],
+        default: ''
+    },
+    title: {
+        type: [String,Number],
+        default: ''
+    },
+    //rtl / ltr / ttb / btt
+    direction: {
+        type: String,
+        default: 'ttb'
+    },
+    scrollbar: {
+        type: Boolean,
+        default: true
+    },
+    extraText: {
+        type: [String,Number],
+        default: ''
+    },
+    actionSize: {
+        type: [String,Number],
+        default: 'df'
+    },
+    size: {
+        type: [String,Number],
+        default: '100%'
+    },
+})
+
+//变量
+const isShow = ref(props.show)
+const isBody = ref(false)
+
+//监听
+watch(() => [
+    props.show
+], ([show]) => {
+    isShow.value = show
+})
+
+//渲染完成
+nextTick(()=> {
+    //页面渲染完成后,再让 vue3 的 Teleport,挂载到指定节点
+    isBody.value = true
+})
+
+//判断<slot>是否有传值
+const slots = useSlots()
+const isSlotHeader = ref(!!slots.header);
+const isSlotExtra = ref(!!slots.extra);
+const isSlotAction = ref(!!slots.action);
+const isSlotSearchBar = ref(!!slots.search);
+</script>
+
+<style lang="scss">
+.hc-card-box.el-card .el-card__body .hc-card-main-box .el-overlay {
+    position: absolute;
+    .hc-drawer-box.el-drawer {
+        background-color: transparent;
+        box-shadow: initial;
+        .el-drawer__body {
+            padding: 24px;
+            overflow: hidden;
+            .data-fill-list-box .el-collapse .el-collapse-item__wrap .el-collapse-item__content .data-fill-list-item-content {
+                height: calc(100vh - 545px);
+            }
+        }
+    }
+}
+</style>

+ 25 - 18
src/global/components/hc-report-modal/index.vue

@@ -13,14 +13,16 @@
                 </el-select>
             </el-form-item>
             <el-form-item label="任务人" path="user" v-if="false">
-                <!--TasksUser class="w-full"/-->
+                <HcTasksUser ui="w-full"/>
+            </el-form-item>
+            <el-form-item label="任务人">
+                <div class="form-item-div">测试的{{linkUserJoinString}}</div>
             </el-form-item>
-            <el-form-item label="任务人">{{linkUserJoinString}}</el-form-item>
             <el-form-item label="上报批次" path="batch">
                 <HcCounter v-model:value="formModel.batch" @update:modelValue="batchUpdateValue"/>
             </el-form-item>
             <el-form-item label="限定审批时间" path="restrictDay">
-                <HcCounter v-model:value="formModel.restrictDay" @update:modelValue="restrictDayUpdateValue"/>
+                <HcCounter v-model:value="formModel.restrictDay" text="(天)" @update:modelValue="restrictDayUpdateValue"/>
             </el-form-item>
         </el-form>
         <template #footer>
@@ -34,8 +36,7 @@
 
 <script setup>
 import {ref,watch,onMounted} from "vue";
-//import TasksUser from "~com/tasksUser/index.vue"
-import {getArrValue,getIndex} from "vue-utils-plus"
+import {getArrValue,getIndex,formValidate} from "vue-utils-plus"
 import tasksFlowApi from '~api/tasks/flow';
 import {ApprovalApi} from '~api/other';
 
@@ -68,6 +69,10 @@ const props = defineProps({
         type: [String,Number],
         default: ''
     },
+    addition: {
+        type: Object,
+        default: () => ({})
+    },
 })
 
 //变量
@@ -81,22 +86,23 @@ const formRef = ref(null)
 const processData = ref([])
 const formModel = ref({
     projectId: projectId.value, contractId: contractId.value, ids: props.ids,
-    taskName: props.taskName, taskContent: '', fixedFlowId: '', batch: 1, restrictDay: 1
+    taskName: props.taskName, taskContent: '', fixedFlowId: '', batch: 1, restrictDay: 1,
+    ...props.addition
 })
 const formRules = ref({
     taskContent: {
         required: true,
-        trigger: ["blur", "input"],
+        trigger: "blur",
         message: "请输入任务描述"
     },
     fixedFlowId: {
         required: true,
-        trigger: ["blur", "change"],
+        trigger: "blur",
         message: "请选择任务流程"
     },
     user: {
         required: true,
-        trigger: ["blur", "change"],
+        trigger: "blur",
         message: "请选择任务人"
     }
 })
@@ -109,16 +115,18 @@ watch(() => [
     props.taskName,
     props.ids,
     props.url,
-], ([val,pid,cid,name,ids,url]) => {
+    props.addition
+], ([val,pid,cid,name,ids,url,addition]) => {
     isShow.value = val
     projectId.value = pid
     contractId.value = cid
     ApiUrl.value = url
     //更新到表单数据
-    formModel.value.ids = ids
-    formModel.value.taskName = name
-    formModel.value.projectId = pid
-    formModel.value.contractId = cid
+    formModel.value = {
+        projectId: pid, contractId: cid, ids: ids, taskName: name,
+        taskContent: '', fixedFlowId: '', batch: 1, restrictDay: 1,
+        ...addition
+    }
 })
 
 //渲染完成
@@ -165,10 +173,9 @@ const cancelReportClick = () => {
     emit('hide', false)
 }
 //上报
-const formReportClick = () => {
-    formRef.value?.validate((errors) => {
-        if (!errors) batchApprovalApi()
-    });
+const formReportClick = async () => {
+    const res = await formValidate(formRef.value)
+    if (res) await batchApprovalApi()
 }
 
 //上报请求

+ 309 - 0
src/global/components/hc-tasks-user/index.vue

@@ -0,0 +1,309 @@
+<template>
+    <div :class="ui" class="hc-tasks-user">
+        <div class="tasks-user-box">
+            <div class="tag-user-list" @click="showModalClick">
+                <template v-for="(item,index) in UserDataList" :key="index">
+                    <el-tag>{{setCheckboxUserName(item)}}</el-tag>
+                    <HcIcon name="navigate_next" ui="arrow-icon-tag" v-if="(UserDataList.length - 1) > index"/>
+                </template>
+                <div class="tasks-placeholder" v-if="UserDataList.length <= 0"> 点击这里选择任务人 </div>
+            </div>
+        </div>
+
+        <!--选择任务人-->
+        <el-dialog v-model="showModal" title="选择任务人" width="62rem" custom-class="hc-modal-border hc-modal-nop">
+            <div class="hc-tasks-user-modal-content-box">
+                <div class="tree-box">
+                    <el-scrollbar>
+                        <ElTree class="hc-tree-node-box" :props="ElTreeProps" :data="ElTreeData" node-key="roleId" highlight-current accordion :default-expanded-keys="[0]" @node-click="ElTreeNodeClick"/>
+                    </el-scrollbar>
+                </div>
+                <div class="user-box">
+                    <div class="y-user-list-box">
+                        <div class="title-box">
+                            <div class="title">可选择</div>
+                        </div>
+                        <div class="user-list">
+                            <el-scrollbar>
+                                <el-checkbox-group v-model="checkboxUserList">
+                                    <template v-for="item in signUserList">
+                                        <div class="user-item checkbox-li">
+                                            <el-checkbox :label="`${item['certificateUserName']}-${item['certificateUserId']}`">
+                                                <div class="item-user-name">{{item['certificateUserName']}}</div>
+                                            </el-checkbox>
+                                        </div>
+                                    </template>
+                                </el-checkbox-group>
+                            </el-scrollbar>
+                        </div>
+                    </div>
+                    <div class="s-user-list-box">
+                        <div class="title-box">
+                            <div class="title">已选择({{checkboxUserList.length}})</div>
+                            <el-button plain size="small" @click="sequenceModal = true">调整顺序</el-button>
+                        </div>
+                        <div class="user-list">
+                            <el-scrollbar>
+                                <template v-for="(item,index) in checkboxUserList" :key="index">
+                                    <el-tag closable @close="delCheckboxUser(index)">{{setCheckboxUserName(item)}}</el-tag>
+                                </template>
+                            </el-scrollbar>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button size="large" @click="showModal = false">取消</el-button>
+                    <el-button type="primary" hc-btn @click="sureSignUserClick">确定</el-button>
+                </div>
+            </template>
+        </el-dialog>
+
+        <!--调整顺序-->
+        <el-dialog v-model="sequenceModal" title="调整顺序" width="38rem" custom-class="hc-modal-border">
+            <el-alert title="可拖动排序,也可在后面点击图标,切换排序" type="warning" :closable="false"/>
+            <div class="sort-node-body-box list-group header">
+                <div class="list-group-item">
+                    <div class="index-box">序号</div>
+                    <div class="title-box">任务人</div>
+                    <div class="icon-box">排序</div>
+                </div>
+            </div>
+            <Draggable class="sort-node-body-box list-group" ghost-class="ghost" :list="checkboxUserList" item-key="id" @start="sortNodeDrag = true" @end="sortNodeDrag = false">
+                <template #item="{element, index}">
+                    <div class="list-group-item">
+                        <div class="index-box">{{index + 1}}</div>
+                        <div class="title-box">{{setCheckboxUserName(element)}}</div>
+                        <div class="icon-box">
+                            <span class="icon" @click="downSortClick(index)">
+                                <HcIcon name="south" ui="text-lg"/>
+                            </span>
+                            <span class="icon" @click="upSortClick(index)">
+                                <HcIcon name="north" ui="text-lg"/>
+                            </span>
+                        </div>
+                    </div>
+                </template>
+            </Draggable>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button size="large" @click="sequenceModal = false">取消</el-button>
+                    <el-button type="primary" hc-btn @click="sequenceModal = false">确认</el-button>
+                </div>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup>
+import {ref, watch, onMounted} from "vue";
+import {useAppStore} from "~src/store/index";
+import tasksFlowApi from '~api/tasks/flow';
+import {getArrValue} from "vue-utils-plus"
+import Draggable from "vuedraggable";
+
+//初始变量
+const userStore = useAppStore()
+const contractId = ref(userStore.getContractId);
+
+//参数
+const props = defineProps({
+    ui: {
+        type: String,
+        default: ''
+    },
+    //选中的用户数组
+    users: {
+        type: String,
+        default: ''
+    }
+})
+
+//变量
+const showModal = ref(false)
+const sequenceModal = ref(false)
+const checkboxUserList = ref([])
+const UserDataList = ref([])
+
+//树数据
+const ElTreeProps = {children: 'childRoleList', label: 'roleName'}
+const ElTreeData = ref([{
+    roleName: '全部人员',
+    roleId: 0,
+    childRoleList: [],
+    signPfxFileList: []
+}])
+
+//监听
+watch(() => [
+    userStore.getContractId,
+    props.users
+], ([UserContractId,users]) => {
+    contractId.value = UserContractId
+    setUserDataList(users)
+})
+
+//渲染完成
+onMounted(() => {
+    if (props.users) {
+        setUserDataList(props.users)
+    }
+    queryAllRoleList()
+})
+
+//处理用户数据
+const setUserDataList = (users) => {
+    if (users) {
+        const usersArr = users.split(',')
+        UserDataList.value = usersArr
+        checkboxUserList.value = usersArr
+    } else {
+        UserDataList.value = []
+        checkboxUserList.value = []
+    }
+}
+
+//展开弹窗
+const showModalClick = () => {
+    showModal.value = true
+}
+
+//获取系统所有角色划分
+const signUserList = ref([])
+const queryAllRoleList = async () => {
+    const { error, code, data } = await tasksFlowApi.queryAllRoleList({
+        contractId: contractId.value
+    })
+    //处理数据
+    if (!error && code === 200) {
+        let signList = [], dataArr = getArrValue(data)
+        ElTreeData.value[0].childRoleList = dataArr
+        if (dataArr.length > 0) {
+            dataArr.forEach(item => {
+                signList = signList.concat(item.signPfxFileList)
+            })
+        }
+        ElTreeData.value[0].signPfxFileList = signList
+        signUserList.value = signList
+    } else {
+        signUserList.value = []
+        ElTreeData.value[0].childRoleList = []
+        ElTreeData.value[0].signPfxFileList = []
+    }
+}
+
+//树被点击
+const ElTreeNodeClick = (data) => {
+    signUserList.value = getArrValue(data?.signPfxFileList)
+}
+
+//处理已选择的用户问题
+const setCheckboxUserName = (item) => {
+    if (item) {
+        const itemArr = item.split('-')
+        if (itemArr.length > 0 && itemArr[0]) {
+            return itemArr[0]
+        } else {
+            return ''
+        }
+    } else {
+        return ''
+    }
+}
+
+//删除已选择的用户
+const delCheckboxUser = (index) => {
+    checkboxUserList.value.splice(index,1);
+}
+
+//排序
+const sortNodeDrag = ref(false)
+//向下
+const downSortClick = (index) => {
+    const indexs = index + 1
+    const data = checkboxUserList.value
+    if(indexs !== data.length) {
+        const tmp = data.splice(indexs,1);
+        checkboxUserList.value.splice(index,0,tmp[0]);
+    } else {
+        window?.$message?.warning('已经处于置底,无法下移')
+    }
+}
+//向上
+const upSortClick = (index) => {
+    const data = checkboxUserList.value || []
+    if(index !== 0) {
+        const tmp = data.splice(index - 1,1);
+        checkboxUserList.value.splice(index,0,tmp[0]);
+    } else {
+        window?.$message?.warning('已经处于置顶,无法上移')
+    }
+}
+
+
+//事件
+const emit = defineEmits(['change'])
+
+//确认选择
+const sureSignUserClick = () => {
+    const dataList = JSON.parse(JSON.stringify(checkboxUserList.value))
+    UserDataList.value = dataList
+    showModal.value = false
+    //处理数据格式
+    let users = ''
+    if (dataList.length > 0) {
+        dataList.forEach(item => {
+            if (users) {
+                users += ',' + item
+            } else {
+                users = item
+            }
+        })
+    }
+    emit('change', users)
+}
+</script>
+
+<style lang="scss" scoped>
+@import './style.scss';
+</style>
+
+<style lang="scss">
+.hc-tasks-user .tasks-user-box .tag-user-list {
+    .el-tag {
+        --el-icon-size: 14px;
+        padding: 0 10px;
+        height: 26px;
+    }
+}
+.hc-tasks-user-modal-content-box {
+    .checkbox-li .el-checkbox {
+        width: 100%;
+        .el-checkbox__input {
+            position: absolute;
+            right: 0;
+            .el-checkbox__inner {
+                width: 18px;
+                height: 18px;
+                &:after {
+                    height: 9px;
+                    left: 6px;
+                    top: 2px;
+                }
+            }
+        }
+        .el-checkbox__label {
+            flex: 1;
+            padding-left: 0;
+            padding-right: 20px;
+        }
+    }
+    .user-list {
+        .el-tag {
+            margin-right: 10px;
+            margin-top: 12px;
+        }
+    }
+}
+</style>

+ 147 - 0
src/global/components/hc-tasks-user/style.scss

@@ -0,0 +1,147 @@
+.hc-tasks-user {
+    position: relative;
+    .tasks-user-box {
+        position: relative;
+        border: 1px solid #e0e0e6;
+        border-radius: 4px;
+        padding: 0 12px;
+        cursor: pointer;
+        height: 40px;
+        .tag-user-list {
+            position: relative;
+            display: flex;
+            align-items: center;
+            flex-flow: row wrap;
+            height: inherit;
+            .tasks-placeholder {
+                color: #a9abb2;
+                font-size: 14px;
+            }
+            .arrow-icon-tag {
+                position: relative;
+                color: #a9abb2;
+                font-size: 18px;
+                margin: 0 8px;
+            }
+        }
+    }
+}
+
+.hc-tasks-user-modal-content-box {
+    position: relative;
+    display: flex;
+    height: 460px;
+    .tree-box {
+        flex: 1;
+        user-select: none;
+        position: relative;
+        padding: 20px;
+        overflow: hidden;
+        border-right: 1px solid #EEEEEE;
+    }
+    .user-box {
+        flex: 2;
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        .y-user-list-box, .s-user-list-box {
+            position: relative;
+            overflow: hidden;
+            display: flex;
+            flex-direction: column;
+            .title-box {
+                position: relative;
+                padding: 2px 24px;
+                display: flex;
+                align-items: center;
+                border-bottom: 1px solid #EEEEEE;
+                background-color: #F8F8F8;
+                color: #838791;
+                .title {
+                    flex: auto;
+                }
+            }
+            .user-list {
+                position: relative;
+                overflow: hidden;
+                padding: 0 24px;
+                .user-item {
+                    position: relative;
+                    padding: 4px 0;
+                }
+                .user-item + .user-item {
+                    border-top: 1px dashed #EEEEEE;
+                }
+            }
+        }
+        .y-user-list-box {
+            flex: 1;
+            .user-list {
+                flex: 1;
+            }
+        }
+        .s-user-list-box {
+            position: relative;
+            border-top: 1px solid #EEEEEE;
+            .user-list {
+                height: 6rem;
+            }
+        }
+    }
+}
+
+.sort-node-body-box.list-group {
+    position: relative;
+    min-height: 20px;
+    border: 1px solid #EEEEEE;
+    .list-group-item {
+        position: relative;
+        display: flex;
+        align-items: center;
+        padding: 6px 15px;
+        cursor: move;
+        transition: background 0.2s;
+        .index-box {
+            position: relative;
+            width: 50px;
+        }
+        .title-box {
+            position: relative;
+            padding-right: 24px;
+            flex: 1;
+        }
+        .icon-box {
+            position: relative;
+            font-size: 18px;
+            display: flex;
+            align-items: center;
+            .icon {
+                cursor: pointer;
+                display: flex;
+                align-items: center;
+            }
+        }
+        &:first-child .icon-box i:last-child,
+        &:last-child .icon-box i:first-child {
+            cursor: default;
+            color: #aaaaaa;
+        }
+        &:hover {
+            background: var(--el-color-primary-light-9);
+        }
+    }
+    .list-group-item + .list-group-item {
+        border-top: 1px solid #EEEEEE;
+    }
+    &.header {
+        border-bottom: 0;
+        .list-group-item {
+            cursor: default;
+            padding: 8px 15px;
+            background-color: #F8F8F8;
+            .index-box, .title-box, .icon-box {
+                font-size: 14px;
+            }
+        }
+    }
+}

+ 4 - 0
src/global/components/index.js

@@ -2,6 +2,7 @@ import HcImg from './hc-img/index.vue'
 import HcIcon from './hc-icon/index.vue'
 import HcCard from './hc-card/index.vue'
 import HcPages from './hc-page/index.vue'
+import HcDrawer from './hc-drawer/index.vue'
 import HcCounter from './hc-counter/index.vue'
 import HcTooltip from './hc-tooltip/index.vue'
 import HcMenuSimple from './hc-menu-simple/index.vue'
@@ -9,6 +10,7 @@ import HcDatePicker from './hc-date-picker/index.vue'
 import HcNewSwitch from './hc-new-switch/index.vue'
 import HcDragModal from './hc-drag-modal/index.vue'
 import HcReportModal from './hc-report-modal/index.vue'
+import HcTasksUser from './hc-tasks-user/index.vue'
 
 //注册全局组件
 export const setupComponents = (App) => {
@@ -16,6 +18,7 @@ export const setupComponents = (App) => {
     App.component('HcIcon', HcIcon)
     App.component('HcCard', HcCard)
     App.component('HcPages', HcPages)
+    App.component('HcDrawer', HcDrawer)
     App.component('HcCounter', HcCounter)
     App.component('HcTooltip', HcTooltip)
     App.component('HcMenuSimple', HcMenuSimple)
@@ -23,4 +26,5 @@ export const setupComponents = (App) => {
     App.component('HcNewSwitch', HcNewSwitch)
     App.component('HcDragModal', HcDragModal)
     App.component('HcReportModal', HcReportModal)
+    App.component('HcTasksUser', HcTasksUser)
 }

+ 1 - 0
src/layout/index.vue

@@ -127,6 +127,7 @@ watch(() => [
 const setIsCollapse = (key) => {
     if (key === 'data-fill-wbs') {
         isCollapse.value = true
+        useAppState.setCollapse(true)
     }
 }
 

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

@@ -265,6 +265,11 @@
             padding-top: var(--el-dialog-padding-primary);
         }
     }
+    .el-dialog.hc-modal-nop {
+        .el-dialog__body {
+            padding: 0;
+        }
+    }
 }
 
 //上传
@@ -361,3 +366,17 @@
         }
     }
 }
+
+//伪表单
+.el-form-item--large .el-form-item__content .form-item-div {
+    position: relative;
+    color: #606266;
+    font-size: 14px;
+    padding: 0 12px;
+    height: 40px;
+    width: 100%;
+    border: 1px solid #e0e0e6;
+    border-radius: 4px;
+    display: flex;
+    align-items: center;
+}

+ 63 - 48
src/styles/data-fill/query.scss

@@ -1,14 +1,13 @@
 .hc-layout-box {
     display: flex;
     position: relative;
-    height: calc(100% - 60px);
+    height: 100%;
     .hc-layout-left-box {
-        position: relative;
-        background: white;
-        overflow: auto;
-        border-top: 1px solid #EEEEEE;
-        border-right: 1px solid #EEEEEE;
         width: 382px;
+        position: relative;
+        background: #f1f5f8;
+        border-radius: 10px;
+        box-shadow: -2px 0 10px 0 rgba(32,37,50,0.03), 0 10px 21px 20px rgba(32,37,50,0.03);
         .horizontal-drag-line {
             position: absolute;
             right: 0;
@@ -17,39 +16,47 @@
             height: 100%;
             user-select: none;
             cursor: col-resize;
-            background-color: #e4e4e4;
-            transition: background-color 0.2s;
-            &:hover {
-                background-color: rgba(119, 119, 119, .5);
-            }
+            background-color: #00000000;
         }
         .hc-project-box {
             position: relative;
             padding: 15px 24px;
-            border-bottom: 1px solid #EEEEEE;
-            .project-alias-box {
-                position: relative;
-                color: var(--hc-primary);
+            display: flex;
+            align-items: flex-start;
+            border-bottom: 1px solid #E9E9E9;
+            .hc-project-icon-box {
+                font-size: 30px;
+                color: var(--el-color-primary);
             }
-            .project-name {
+            .project-name-box {
+                flex: auto;
                 position: relative;
-                color: #999999;
-                margin-top: 10px;
+                overflow: hidden;
+                .project-alias {
+                    color: var(--el-color-primary);
+                }
+                .project-name {
+                    margin-top: 6px;
+                    color: #838791;
+                }
             }
         }
-        .hc-el-tree-box {
+        .hc-tree-box {
             position: relative;
             padding: 15px 20px;
-            height: calc(100% - 191px);
-            overflow: auto;
-            .search-tree-val {
+            height: calc(100% - 187px);
+            .hc-search-tree-val {
+                position: relative;
+                margin-bottom: 24px;
+            }
+            .hc-tree-scrollbar {
                 position: relative;
-                margin-bottom: 15px;
+                height: calc(100% - 68px);
             }
         }
         .hc-tree-foot-tip-box {
             position: absolute;
-            border-top: 1px solid #EEEEEE;
+            border-top: 1px solid #E9E9E9;
             padding: 15px 24px;
             width: 100%;
             bottom: 0;
@@ -70,7 +77,7 @@
                     border-radius: 25px;
                 }
                 &.green:before {
-                    background-color: #3eb93b;
+                    background-color: #1ECC95;
                 }
                 &.black:before {
                     background-color: #111111;
@@ -86,39 +93,47 @@
     }
     .hc-layout-content-box {
         flex: 1;
-        overflow: auto;
         position: relative;
-        padding: 0 24px 15px 20px;
+        margin-left: 24px;
+        .hc-card-max-h-box {
+            position: relative;
+            height: calc(100% - 56px);
+            overflow-y: auto;
+            scroll-behavior: smooth;
+            &.node-tree {
+                height: 100%;
+                .hc-tree-foot-tip-box {
+                    padding: 15px 0;
+                    text-align: center;
+                    border: 0;
+                    z-index: 11;
+                    .dot-view {
+                        width: auto;
+                        margin-top: 0;
+                    }
+                    .dot-view + .dot-view {
+                        margin-left: 80px;
+                    }
+                }
+            }
+            &::-webkit-scrollbar {
+                width: 0;
+            }
+        }
         .data-fill-foot-box {
             position: absolute;
             bottom: 0;
             right: 24px;
             padding: 24px;
             left: 20px;
-            text-align: center;
             box-shadow: 0px -3px 6px rgb(0 0 0 / 6%);
+            text-align: center;
         }
     }
 }
-.hc-card-header {
-    position: relative;
-    font-size: initial;
-    font-weight: initial;
+.flip-list-move {
+    transition: transform 0.5s;
 }
-
-html.theme-dark {
-    .hc-layout-box .hc-layout-left-box {
-        background: var(--hc-bg-color);
-        border-top: 1px solid #303030;
-        border-right: 1px solid #303030;
-    }
-    .hc-layout-box .hc-layout-left-box .hc-project-box {
-        border-bottom: 1px solid #303030;
-    }
-    .hc-layout-box .hc-tree-foot-tip-box {
-        border-top: 1px solid #303030;
-    }
-    .hc-layout-box .hc-layout-left-box .horizontal-drag-line {
-        background-color: #303030;
-    }
+.no-move {
+    transition: transform 0s;
 }

+ 0 - 21
src/styles/data-fill/wbs.scss

@@ -267,24 +267,3 @@
 .no-move {
     transition: transform 0s;
 }
-.ghost {
-    background: var(--hc-primary-light-3);
-}
-
-
-html.theme-dark {
-    .hc-layout-box .hc-layout-left-box {
-        background: var(--hc-bg-color);
-        border-top: 1px solid #303030;
-        border-right: 1px solid #303030;
-    }
-    .hc-layout-box .hc-layout-left-box .hc-project-box {
-        border-bottom: 1px solid #303030;
-    }
-    .hc-layout-box .hc-tree-foot-tip-box {
-        border-top: 1px solid #303030;
-    }
-    .hc-layout-box .hc-layout-left-box .horizontal-drag-line {
-        background-color: #303030;
-    }
-}

+ 250 - 0
src/views/data-fill/components/HcTreeData.vue

@@ -0,0 +1,250 @@
+<template>
+    <ElTree class="hc-tree-node" ref="ElTreeRef" :props="ElTreeProps" :data="datas" highlight-current accordion node-key="primaryKeyId"
+            :default-expanded-keys="TreeExpandKey" @node-click="ElTreeClick" @node-contextmenu="ElTreeLabelContextMenu">
+        <template #default="{ node, data }">
+            <div class="data-custom-tree-node" :id="`${idPrefix}${data['primaryKeyId']}`">
+                <!--树组件,节点名称-->
+                <div class="label" :class="node.level === 1?'level-name':''">
+                    <span :class="data?.colorStatus === 2?'text-blue':data?.colorStatus === 3?'text-orange':data?.colorStatus === 4?'text-green':''" v-if="isColor">{{ node.label }}</span>
+                    <span v-else>{{ node.label }}</span>
+                </div>
+                <!--树组件,操作菜单-->
+                <div class="menu-icon" :class="node.showTreeMenu?'show':''" v-if="node.level !== 1 && menusData.length > 0" @click.stop>
+                    <n-dropdown placement="bottom-end" trigger="click" size="huge" :options="menusData" @select="ElTreeMenuClick($event,node,data)" @update:show="ElTreeMenuShow($event,node)">
+                        <div class="cu-tree-node-popover-menu-icon">
+                            <HcIcon name="menu" ui="text-2xl"/>
+                        </div>
+                    </n-dropdown>
+                </div>
+                <!--树组件,操作菜单 END-->
+            </div>
+        </template>
+    </ElTree>
+    <n-dropdown placement="bottom" trigger="manual" :x="menusX" :y="menusY" size="huge" :options="menusData" :show="showDropdown" @clickoutside="onClickoutside" @select="handleMenuSelect" v-if="menusData.length > 0"/>
+</template>
+
+<script setup>
+import {ref,nextTick,watch} from "vue";
+import {hIconJs} from "~src/plugins/renderele";
+import {NDropdown} from 'naive-ui';
+
+//参数
+const props = defineProps({
+    menus: {
+        type: Array,
+        default: () => ([])
+    },
+    datas: {
+        type: Array,
+        default: () => ([])
+    },
+    autoExpandKeys: {
+        type: Array,
+        default: () => ([])
+    },
+    isMark: {
+        type: Boolean,
+        default: false
+    },
+    idPrefix: {
+        type: String,
+        default: 'tree-data-'
+    },
+    isAutoKeys: {
+        type: Boolean,
+        default: true
+    },
+    isAutoClick: {
+        type: Boolean,
+        default: true
+    },
+    isColor: {
+        type: Boolean,
+        default: false
+    },
+})
+
+//变量
+const ElTreeRef = ref(null)
+const showDropdown = ref(false)
+const treeRefNode = ref(null)
+const treeRefData = ref(null)
+const ElTreeProps = ref({
+    label: 'title',
+    children: 'children',
+    isLeaf: 'notExsitChild'
+})
+const menusData = ref(props.menus)
+const menusX = ref(0);
+const menusY = ref(0);
+
+const menuMark = ref(props.isMark)
+const isAutoKeys = ref(props.isAutoKeys)
+const TreeExpandKey = ref(props.autoExpandKeys)
+const idPrefix = ref(props.idPrefix);
+
+//监听
+watch(() => [
+    props.menus,
+    props.isMark,
+    props.isAutoKeys,
+    props.autoExpandKeys,
+    props.idPrefix,
+], ([menus, isMark, AutoKeys, expandKeys, UserIdPrefix]) => {
+    menusData.value = menus
+    menuMark.value = isMark
+    isAutoKeys.value = AutoKeys
+    TreeExpandKey.value = expandKeys
+    idPrefix.value = UserIdPrefix
+})
+
+//事件
+const emit = defineEmits(['menuTap','nodeTap'])
+
+//节点被点击
+const ElTreeClick = async (data,node) => {
+    if (isAutoKeys.value) {
+        let autoKeysArr = []
+        await getNodeExpandKeys(node, autoKeysArr)
+        const autoKeys = autoKeysArr.reverse()
+        emit('nodeTap', {node, data, keys: autoKeys})
+    } else {
+        emit('nodeTap', {node, data, keys: []})
+    }
+}
+
+//处理自动展开的节点KEY
+const getNodeExpandKeys = async (node, newKeys) => {
+    const parent = node?.parent ?? []
+    const primaryKeyId = node?.data?.primaryKeyId ?? ''
+    if (primaryKeyId) {
+        newKeys.push(primaryKeyId)
+        await getNodeExpandKeys(parent, newKeys)
+    }
+}
+
+//鼠标右键事件
+const ElTreeLabelContextMenu = (e,data,node) => {
+    const rows = menusData.value || [];
+    if (node.level !== 1 && rows.length > 0) {
+        e.preventDefault();
+        treeRefNode.value = node;
+        treeRefData.value = data;
+        if (menuMark.value) {
+            setMenuMarkVal(rows,data)
+        }
+        nextTick(() => {
+            menusX.value = e.clientX;
+            menusY.value = e.clientY;
+            showDropdown.value = true;
+        });
+    }
+}
+
+//设置菜单标记状态
+const setMenuMarkVal = (rows,item) => {
+    for (let i = 0; i < rows.length; i++) {
+        if (rows[i].key === 'mark' || rows[i].key === 'cancel_mark') {
+            if (item.isFirst) {
+                menusData.value[i].label = '取消标记为首件';
+                menusData.value[i].key = 'cancel_mark';
+                menusData.value[i].icon = hIconJs({
+                    name: 'grade', fill: true
+                });
+            } else {
+                menusData.value[i].label = '标记为首件';
+                menusData.value[i].key = 'mark';
+                menusData.value[i].icon = hIconJs({name: 'grade'});
+            }
+            break;
+        }
+    }
+}
+
+//鼠标右键菜单被点击
+const handleMenuSelect = (key) => {
+    const node = treeRefNode.value;
+    const data = treeRefData.value;
+    showDropdown.value = false;
+    emit('menuTap', {key,node,data})
+}
+const onClickoutside = () => {
+    treeRefNode.value = null;
+    treeRefData.value = null;
+    showDropdown.value = false;
+}
+
+//菜单被点击
+const ElTreeMenuClick = (key,node,data) => {
+    emit('menuTap', {key,node,data})
+}
+//菜单是否显示
+const ElTreeMenuShow = (key,node) => {
+    node.showTreeMenu = key
+}
+
+//设置树菜单的标记数据
+const setElTreeMenuMark = (keys,isFirst) => {
+    keys.forEach(item => {
+        //根据 data 或者 key 拿到 Tree 组件中的 node
+        let node = ElTreeRef.value.getNode(item)
+        if (!!node) node.data.isFirst = isFirst;
+    })
+}
+
+//设置树菜单的标记数据
+const removeElTreeNode = (key) => {
+    //根据 data 或者 key 拿到 Tree 组件中的 node
+    let node = ElTreeRef.value.getNode(key)
+    //删除 Tree 中的一个节点,使用此方法必须设置 node-key 属性
+    ElTreeRef.value.remove(node)
+}
+
+// 暴露出去
+defineExpose({
+    setElTreeMenuMark,
+    removeElTreeNode
+})
+</script>
+
+<style lang="scss" scoped>
+.data-custom-tree-node {
+    position: relative;
+    display: flex;
+    align-items: center;
+    width: 100%;
+    color: var(--ui-TC);
+    .label {
+        flex: auto;
+        font-size: 16px;
+    }
+    .label.level-name {
+        font-size: 18px;
+        font-weight: bold;
+    }
+    .menu-icon {
+        position: relative;
+        font-size: 20px;
+        opacity: 0;
+        pointer-events: none;
+        transition: opacity 0.2s;
+        .cu-tree-node-popover-menu-icon {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+    }
+    &:hover {
+        .menu-icon {
+            opacity: 1;
+            pointer-events: all;
+            cursor: context-menu;
+        }
+    }
+    .menu-icon.show {
+        opacity: 1;
+        pointer-events: all;
+        cursor: context-menu;
+    }
+}
+</style>

+ 1 - 1
src/views/data-fill/components/WbsTree.vue

@@ -54,7 +54,7 @@ const props = defineProps({
     },
     idPrefix: {
         type: String,
-        default: '`wbs-tree-'
+        default: 'wbs-tree-'
     },
     isAutoKeys: {
         type: Boolean,

+ 300 - 281
src/views/data-fill/query.vue

@@ -1,22 +1,35 @@
 <template>
-    <n-divider dashed title-placement="left">资料查询</n-divider>
     <div class="hc-layout-box">
-        <div class="hc-layout-left-box" :style="'width:' + leftWidth + 'px;'">
+        <div class="hc-layout-left-box" id="wbs-left-tree" :style="'width:' + leftWidth + 'px;'">
             <div class="hc-project-box">
-                <div class="text-xl text-cut project-alias-box">
-                    <i class="hcicon-xiangmu"/>
-                    <span class="ml-2">{{projectInfo['projectAlias']}}</span>
+                <div class="hc-project-icon-box">
+                    <HcIcon name="layers"/>
+                </div>
+                <div class="ml-2 project-name-box">
+                    <span class="text-xl text-cut project-alias">{{projectInfo['projectAlias']}}</span>
+                    <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
                 </div>
-                <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
             </div>
-            <div class="hc-el-tree-box">
-                <div class="search-tree-val">
-                    <n-input-group>
-                        <n-input v-model:value="searchTreeVal" type="text" placeholder="请输入名称关键词检索" clearable/>
-                        <n-button type="primary" @click="searchTreeClick">搜索</n-button>
-                    </n-input-group>
+            <div class="hc-tree-box">
+                <div class="hc-search-tree-val">
+                    <el-input v-model="searchTreeVal" block size="large" placeholder="请输入名称关键词检索" clearable @keyup="searchTreeKeyUp">
+                        <template #suffix>
+                            <HcIcon name="search" ui="text-2xl"/>
+                        </template>
+                    </el-input>
+                </div>
+                <div class="hc-tree-scrollbar" v-loading="treeLoading" element-loading-text="获取数据中...">
+                    <el-scrollbar>
+                        <KeepAlive>
+                            <template v-if="isSearchTree">
+                                <HcTreeData :datas="searchTreeData" :autoExpandKeys="treeAutoExpandKeys" isColor @nodeTap="wbsElTreeClick"/>
+                            </template>
+                            <template v-else>
+                                <WbsTree :autoExpandKeys="treeAutoExpandKeys" :projectId="projectId" :contractId="contractId" isColor @nodeTap="wbsElTreeClick"/>
+                            </template>
+                        </KeepAlive>
+                    </el-scrollbar>
                 </div>
-                <HcTree type="data-fill-query" :params="treeParams" :props="wbsElTreeProps" lazy isColor @node-click="wbsElTreeClick"/>
             </div>
             <div class="hc-tree-foot-tip-box">
                 <div class="dot-view green">已审批</div>
@@ -28,378 +41,383 @@
             <div class="horizontal-drag-line" @mousedown="onmousedown"/>
         </div>
         <div class="hc-layout-content-box">
-            <n-card class="hc-card-overflow-box" :segmented="{content: true}">
+            <HcCard :scrollbar="false" actionSize="lg">
                 <template #header>
-                    <div class="hc-card-header flex items-center">
+                    <HcTooltip keys="query_report">
+                        <el-button type="primary" hc-btn :disabled="tableCheckedKeys.length <= 0" @click="reportModalClick">
+                            <HcIcon name="drive_folder_upload"/>
+                            <span>上报</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="query_download">
+                        <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" :loading="downloadLoading" @click="batchDownload">
+                            <HcIcon name="download"/>
+                            <span>下载</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="query_print">
+                        <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" :loading="printLoading" @click="batchPrint">
+                            <HcIcon name="print"/>
+                            <span>打印</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="query_abolish">
+                        <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" @click="batchAbolishClick">
+                            <HcIcon name="delete"/>
+                            <span>废除</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="query_local_attestation">
+                        <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" :loading="localLoading" @click="batchLocal">
+                            <HcIcon name="folder"/>
+                            <span>本地验签</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="query_online_attestation">
+                        <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" :loading="onlineLoading" @click="batchOnline">
+                            <HcIcon name="browse_activity"/>
+                            <span>在线验签</span>
+                        </el-button>
+                    </HcTooltip>
+                </template>
+                <template #search>
+                    <div class="flex items-center">
                         <div class="w-40">
-                            <n-select v-model:value="searchForm.taskStatus" :options="processStatusData" placeholder="流程状态" clearable/>
+                            <el-select v-model="searchForm.taskStatus" placeholder="流程状态" clearable>
+                                <el-option v-for="item in processStatusData" :key="item.value" :label="item['dictValue']" :value="item['dictKey']"/>
+                            </el-select>
                         </div>
-                        <div class="w-40 ml-5">
-                            <n-select v-model:value="searchForm.fileUserIdAndName" :options="reportingPersonData" placeholder="填报人" clearable/>
+                        <div class="w-40 ml-2">
+                            <el-select v-model="searchForm.fileUserIdAndName" placeholder="填报人" clearable>
+                                <el-option v-for="item in reportingPersonData" :key="item.value" :label="item['label']" :value="item['value']"/>
+                            </el-select>
                         </div>
-                        <div class="w-40 ml-5">
-                            <n-select v-model:value="searchForm.sourceType" :options="fileTypeData" placeholder="文件类型" clearable/>
+                        <div class="w-40 ml-2">
+                            <el-select v-model="searchForm.sourceType" placeholder="文件类型" clearable>
+                                <el-option v-for="item in fileTypeData" :key="item.value" :label="item['dictValue']" :value="item['dictKey']"/>
+                            </el-select>
                         </div>
-                        <div class="w-40 ml-5">
-                            <n-select v-model:value="searchForm.reportNumber" :options="reportBatchData" placeholder="上报批次" clearable/>
+                        <div class="w-32 ml-2">
+                            <el-select v-model="searchForm.reportNumber" placeholder="上报批次" clearable>
+                                <el-option v-for="item in reportBatchData" :key="item.value" :label="item['label']" :value="item['value']"/>
+                            </el-select>
                         </div>
-                        <div class="w-96 ml-5">
-                            <n-date-picker v-model:formatted-value="betweenTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" clearable @update:value="betweenTimeUpdate"/>
+                        <div class="w-64 ml-2">
+                            <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate"/>
                         </div>
-                        <div class="w-60 ml-5">
-                            <n-input v-model:value="searchForm.queryValue" type="text" placeholder="请输入名称关键词检索" clearable @keyup="keyUpEvent">
-                                <template #suffix>
-                                    <span class="_icon-search hc-input-search-icon"/>
-                                </template>
-                            </n-input>
+                        <div class="w-60 ml-2">
+                            <el-input v-model="searchForm.queryValue" placeholder="请输入名称关键词检索" clearable @keyup="keyUpEvent"/>
+                        </div>
+                        <div class="ml-2">
+                            <el-button type="primary" @click="searchClick">搜索</el-button>
                         </div>
-                        <n-button type="primary" class="ml-2" @click="searchChange">搜索</n-button>
                     </div>
                 </template>
-                <div class="hc-sticky-box p-6">
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
-                        <template #trigger>
-                            <n-button type="primary" class="px-8" @click="reportModalClick">上报</n-button>
-                        </template>
-                        <span>{{btn_report?.textInfo}}</span>
-                    </n-popover>
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_download?.textInfo" v-if="btn_download">
-                        <template #trigger>
-                            <n-button type="primary" strong secondary class="px-8 ml-6">下载</n-button>
-                        </template>
-                        <span>{{btn_download?.textInfo}}</span>
-                    </n-popover>
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_print?.textInfo" v-if="btn_print">
-                        <template #trigger>
-                            <n-button type="primary" strong secondary class="px-8 ml-6">打印</n-button>
-                        </template>
-                        <span>{{btn_print?.textInfo}}</span>
-                    </n-popover>
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_abolish?.textInfo" v-if="btn_abolish">
-                        <template #trigger>
-                            <n-button type="primary" strong secondary class="px-8 ml-6" @click="batchAbolishClick">废除</n-button>
-                        </template>
-                        <span>{{btn_abolish?.textInfo}}</span>
-                    </n-popover>
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_online?.textInfo" v-if="btn_online">
-                        <template #trigger>
-                            <n-button type="primary" strong secondary class="px-8 ml-6">在线验签</n-button>
-                        </template>
-                        <span>{{btn_online?.textInfo}}</span>
-                    </n-popover>
-                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_local?.textInfo" v-if="btn_local">
-                        <template #trigger>
-                            <n-button type="primary" strong secondary class="px-8 ml-6">本地验签</n-button>
-                        </template>
-                        <span>{{btn_local?.textInfo}}</span>
-                    </n-popover>
-                </div>
-                <div class="p-6 pt-0">
-                    <n-data-table :columns="tableColumns" :data="tableData" :row-key="row => row.name" @update:checked-row-keys="tableHandleCheck" :single-line="false" striped/>
-                </div>
+                <el-scrollbar>
+                    <div class="hc-table-ref-box">
+                        <el-table ref="recycleTableRef" hc :data="tableListData" :loading="tableLoading" stripe @selection-change="tableSelectionChange">
+                            <el-table-column type="selection" width="50" />
+                            <el-table-column prop="num" label="序号" width="80">
+                                <template #default="scope">
+                                    {{scope.$index + 1}}
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="name" label="文件名称" />
+                            <el-table-column prop="startTime" label="开始时间"/>
+                            <el-table-column prop="taskStatusStr" label="流程状态"/>
+                            <el-table-column prop="reportNumber" label="上报批次"/>
+                            <el-table-column prop="fileUserIdAndName" label="填报人"/>
+                            <el-table-column prop="tesk" label="任务人"/>
+                        </el-table>
+                    </div>
+                </el-scrollbar>
                 <template #action>
-                    <HcPage :pages="searchForm" @change="pageChange"/>
+                    <HcPages :pages="searchForm" @change="pageChange"/>
                 </template>
-            </n-card>
+            </HcCard>
         </div>
+
+        <!--批量上报审批-->
+        <HcReportModal  title="批量上报审批" url="informationWriteQuery/batchTask" :show="showReportModal" :projectId="projectId" :contractId="contractId"
+                        :taskName="reportTaskName" :ids="reportIds" :addition="reportAddition" @hide="showReportModal = false" @finish="showReportFinish"/>
     </div>
-    <!--批量上报审批-->
-    <HcReportModal  title="批量上报审批" url="informationWriteQuery/batchTask" :show="showReportModal" :projectId="projectId" :contractId="contractId"
-                    :taskName="reportTaskName" :ids="reportIds" @hide="showReportModal = false" @finish="showReportFinish"/>
 </template>
 
 <script setup>
 import {ref,watch,onMounted} from "vue";
 import {useAppStore} from "~src/store/index";
-import HcTree from "~com/plugins/element/HcTree.vue"
-import HcPage from "~com/plugins/naive/HcPage.vue"
-import dataFillQuery from '~api/data-fill/query';
-import HcReportModal from "~com/reportModal/index.vue"
-import {deepClone} from "~src/utils/lib/util";
+import WbsTree from "./components/WbsTree.vue"
+import HcTreeData from "./components/HcTreeData.vue"
+import {getStoreData, setStoreData} from '~src/utils/storage'
+import {isType, deepClone, formValidate} from "vue-utils-plus"
+import queryApi from '~api/data-fill/query';
 
 //变量
 const useAppState = useAppStore()
+const {getObjNullValue, getObjValue, getArrValue} = isType()
 const projectId = ref(useAppState.getProjectId);
 const contractId = ref(useAppState.getContractId);
 const projectInfo = ref(useAppState.getProjectInfo);
+const isCollapse = ref(useAppState.getCollapse)
 
-//按钮气泡开关
-const bubbleVal = ref(useAppState.getBubble);
 //监听
 watch(() => [
-    useAppState.getProjectId,
-    useAppState.getContractId,
-    useAppState.getProjectInfo,
-    useAppState.getBubble,
-], ([UserProjectId, UserContractId,UserProjectInfo,Bubble]) => {
-    //项目合同数据
-    projectId.value = UserProjectId
-    contractId.value = UserContractId
-    projectInfo.value = UserProjectInfo
-    //按钮气泡开关
-    bubbleVal.value = Bubble
+    useAppState.getCollapse
+], ([Collapse]) => {
+    isCollapse.value = Collapse
 })
 
-//获取气泡数据
-const getButtonsVal = (value) => {
-    return useAppState.getButtonsVal(value)
-}
-
-//气泡数据
-const btn_report = ref(getButtonsVal('query_report'))
-const btn_download = ref(getButtonsVal('query_download'))
-const btn_print = ref(getButtonsVal('query_print'))
-const btn_local = ref(getButtonsVal('query_local_attestation'))
-const btn_abolish = ref(getButtonsVal('query_abolish'))
-const btn_online = ref(getButtonsVal('query_online_attestation'))
+//自动展开缓存
+const treeAutoExpandKeys = ref(getStoreData('wbsTreeExpandKeys') || [])
 
-const processStatusData = ref([])   //流程状态
-const reportingPersonData = ref([]) //填报人
-const fileTypeData = ref([])        //文件类型
-const reportBatchData = ref([])     //上报批次
-
-//搜索表单
-const betweenTime = ref(null)
-const searchForm = ref({
-    taskStatus: null, fileUserIdAndName: null, sourceType: null, reportNumber: null,
-    queryValue: null, betweenTime: null, wbsId: null, current: 1, size: 20, total: 0
+//渲染完成
+onMounted(() => {
+    getFileUser()
+    getReportNumber()
+    getDictBizClassify('flowTaskStatus')
+    getDictBizClassify('fileType')
 })
 
-//树数据
-const wbsElTreeProps = {label: 'title', children: 'children', isLeaf: 'exsitChild'}
-const treeParams = ref({contractId: contractId.value})
-//树被点击
-const wbsElTreeClick = ({data}) => {
-    if (data.leaf === true) {
-        searchForm.value.wbsId = data['primaryKeyId']
-        searchForm.value.current = 1;
-        getTableData()
+//树搜索
+const isSearchTree = ref(false)
+const searchTreeVal = ref('')
+const searchTreeData = ref([])
+
+//回车
+const treeLoading = ref(false)
+const searchTreeKeyUp = (e) => {
+    if (e.key === "Enter") {
+        searchTreeClick()
+    }
+}
+const searchTreeClick = async () => {
+    if (searchTreeVal.value) {
+        isSearchTree.value = true
+        treeLoading.value = true
+        const {error, code, data} = await queryApi.searchContractTree({
+            contractId: contractId.value,
+            queryValue: searchTreeVal.value
+        })
+        //判断状态
+        if (!error && code === 200) {
+            searchTreeData.value = getArrValue(data)
+            treeLoading.value = false
+        } else {
+            treeLoading.value = false
+            searchTreeData.value = []
+        }
     } else {
-        tableData.value = []
-        searchForm.value.total = 0
+        treeLoading.value = false
+        isSearchTree.value = false
     }
 }
 
+//树相关的变量
+const primaryKeyId = ref('')
+const nodeItemInfo = ref({})
+const nodeDataInfo = ref({})
 
-//树搜索
-const searchTreeVal = ref('')
-const searchTreeClick = () => {
-
+//树被点击
+const wbsElTreeClick = ({node, data, keys}) => {
+    nodeItemInfo.value = node
+    nodeDataInfo.value = data
+    primaryKeyId.value = data['primaryKeyId'] || ''
+    //缓存自动展开
+    treeAutoExpandKeys.value = keys
+    setStoreData('wbsTreeExpandKeys',keys)
+    //改变搜索表单数据
+    searchForm.value.wbsId = data['contractIdRelation'] ? data['id'] : data['primaryKeyId']
+    searchForm.value.contractIdRelation = data['contractIdRelation']
+    searchForm.value.current = 1;
+    getTableData()
 }
 
-
-//渲染完成
-onMounted(() => {
-    getFileUser()
-    getReportNumber()
-    getDictBizClassify('flowTaskStatus')
-    getDictBizClassify('fileType')
-})
+//搜索条件
+const processStatusData = ref([])   //流程状态
+const reportingPersonData = ref([]) //填报人
+const fileTypeData = ref([])        //文件类型
+const reportBatchData = ref([])     //上报批次
 
 //获取所有填报人
-const getFileUser = () => {
-    dataFillQuery.getFileUser({
+const getFileUser = async () => {
+    const {error, code, data} = await queryApi.getFileUser({
         contractId: contractId.value
-    }).then(({data}) => {
-        let res = data?.data || [], userArr = [];
+    })
+    //判断状态
+    if (!error && code === 200) {
+        let res = getArrValue(data), userArr = [];
         res.forEach(item => {
-            userArr.push({
-                label: item?.userName,
-                value: `${item?.userId}-${item?.userName}`
-            })
+            userArr.push({label: item['userName'], value: `${item['userId']}-${item['userName']}`})
         })
         reportingPersonData.value = userArr
-    })
+    } else {
+        reportingPersonData.value = []
+    }
 }
-
-//获取所有填报人
-const getReportNumber = () => {
-    dataFillQuery.getReportNumber({
+//获取上报批次
+const getReportNumber = async () => {
+    const {error, code, data} = await queryApi.getReportNumber({
         contractId: contractId.value
-    }).then(({data}) => {
-        let res = data?.data || [];
     })
+    //判断状态
+    if (!error && code === 200) {
+        console.log(data)
+        //let res = getArrValue(data);
+        reportBatchData.value = []
+    } else {
+        reportBatchData.value = []
+    }
 }
-
 //获取流程状态分类和文件类型分类
-const getDictBizClassify = (code) => {
-    dataFillQuery.getDictBizClassify({
+const getDictBizClassify = async (type) => {
+    const {error, code, data} = await queryApi.getDictBizClassify({
         contractId: contractId.value,
-        code: code
-    }).then(({data}) => {
-        let res = data?.data || [], typeArr = [];
-        res.forEach((key) => {
-            typeArr.push({
-                label: key?.dictValue,
-                value: key?.dictKey
-            })
-        })
+        code: type
+    })
+    //判断状态
+    if (!error && code === 200) {
         if (code === 'flowTaskStatus') {
-            processStatusData.value = typeArr
+            processStatusData.value = getArrValue(data)
         } else if (code === 'fileType') {
-            fileTypeData.value = typeArr
+            fileTypeData.value = getArrValue(data)
         }
-    })
+    } else {
+        if (code === 'flowTaskStatus') {
+            processStatusData.value = []
+        } else if (code === 'fileType') {
+            fileTypeData.value = []
+        }
+    }
 }
 
-//重新搜索数据
-const searchChange = () => {
-    searchForm.value.current = 1
-    getTableData()
+//搜索表单
+const searchForm = ref({
+    taskStatus: null, fileUserIdAndName: null, sourceType: null, reportNumber: null, betweenTime: null,
+    queryValue: null, contractIdRelation: null, wbsId: null, current: 1, size: 20, total: 0
+})
+
+//日期时间被选择
+const betweenTime = ref(null)
+const betweenTimeUpdate = ({val,arr}) => {
+    betweenTime.value = arr
+    searchForm.value.betweenTime = `${val['start']}~${val['end']}`
 }
 
 //回车搜索
 const keyUpEvent = (e) => {
     if (e.key === "Enter") {
         searchForm.value.current = 1;
-        getTableData()
+        getLogTableData()
     }
 }
 
-//日期时间被选择
-const betweenTimeUpdate = (_,res) => {
-    res = res || []
-    if (res.length > 0) {
-        searchForm.value.betweenTime = res[0] + '~' + res[1]
-    } else {
-        searchForm.value.betweenTime = null
-    }
+//搜索
+const searchClick = () => {
     searchForm.value.current = 1;
     getTableData()
 }
 
 //分页被点击
-const pageChange = (res) => {
-    searchForm.value.current = res.current;
-    searchForm.value.size = res.size;
+const pageChange = ({current, size}) => {
+    searchForm.value.current = current
+    searchForm.value.size = size
     getTableData()
 }
 
 //获取数据
-const getTableData = () => {
+const tableLoading = ref(false)
+const tableListData = ref([])
+const getTableData = async () => {
     if (!!searchForm.value.wbsId) {
-        dataFillQuery.getPageData({
+        tableLoading.value = true
+        const { error, code, data } = await queryApi.getPageData({
             projectId: projectId.value,
             contractId: contractId.value,
             ...searchForm.value
-        }).then(({data}) => {
-            let res = data['data'] || {}
-            tableData.value = res['records'] || []
-            searchForm.value.total = res.total || 0
         })
+        //处理数据
+        tableLoading.value = false
+        if (!error && code === 200) {
+            tableListData.value = getArrValue(data['records'])
+            searchForm.value.total = data.total || 0
+        } else {
+            tableListData.value = []
+            searchForm.value.total = 0
+        }
     } else {
         window?.$message?.warning('请先选择一个树节点')
     }
 }
 
-//表格表头
-const tableColumns = [
-    {type: 'selection'},
-    {title: '序号', key: 'key', width: 80, align: 'center',
-        render(_, index) {
-            return index + 1
-        }
-    },
-    {title: '文件名称', key: 'name'},
-    {title: '开始时间', key: 'startTime'},
-    {title: '流程状态', key: 'taskStatusStr'},
-    {title: '上报批次', key: 'reportNumber', width: 120, align: 'center'},
-    {title: '填报人', key: 'fileUserIdAndName'},
-    {title: '任务人', key: 'tesk'}
-]
-//表格数据
-const tableData = ref([]);
-//表格勾选
-const checkedRowsRef = ref([])
-const tableHandleCheck = (_,rows) => {
-    checkedRowsRef.value = rows.filter((item) => {
+//多选
+const tableCheckedKeys = ref([]);
+const tableSelectionChange = (rows) => {
+    tableCheckedKeys.value = rows.filter((item) => {
         return (item??'') !== '';
     })
+    console.log(rows)
 }
 
-//批量上报
+//上报
 const reportIds = ref('')
 const reportTaskName = ref('')
+const reportAddition = ref({})
 const showReportModal = ref(false)
 const reportModalClick = () => {
-    const rows = checkedRowsRef.value;
-    if (rows.length > 0) {
-        let ids = '', report = true;
-        for (let i = 0; i < rows.length; i++) {
-            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
-            if (rows[i].status !== 0 && rows[i].status !== 3) {
-                report = false
-                break;
-            }
-        }
-        //判断状态
-        if (report) {
-            reportIds.value = ids
-            reportTaskName.value = rows.length > 1?`${rows[0].name}等${rows.length}个文件`:rows[0].name
-            showReportModal.value = true
-        } else {
-            window.$message?.warning('已上报的文件不能进行再次上报,若要重新上报,要先撤回之前的上报,再重新上报')
-        }
+    const rows = tableCheckedKeys.value;
+    //判断是否满足条件
+    const result = rows.every(({status})=> {
+        return status === 0 || status === 3
+    })
+    //判断状态
+    if (result) {
+        //const info = nodeDataInfo.value;
+        const row = getObjValue(rows[0])
+        //拼接ID
+        reportIds.value = rows.map((obj) => {
+            return obj.id;
+        }).join(",")
+        //设置任务名称
+        reportTaskName.value = rows.length > 1 ? `${row.name}等${rows.length}个文件` : row.name
+        //reportAddition.value = {contractIdRelation: info['contractIdRelation']}
+        showReportModal.value = true
     } else {
-        window.$message?.warning('请先勾选要上报的数据')
+        window.$message?.warning('已上报的文件不能进行再次上报,若要重新上报,要先撤回之前的上报,再重新上报')
     }
 }
-
 //上报完成
 const showReportFinish = () => {
     showReportModal.value = false
-    checkedRowsRef.value = []
     getTableData()
 }
 
-//批量废除
+//下载
+const downloadLoading = ref(false)
+const batchDownload = () => {
+
+}
+
+//打印
+const printLoading = ref(false)
+const batchPrint = () => {
+
+}
+
+//废除
 const batchAbolishClick = () => {
-    const rows = deepClone(checkedRowsRef.value)
-    if (rows.length > 0) {
-        let report = true, ids = '';
-        for (let i = 0; i < rows.length; i++) {
-            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
-            if (rows[i].status === 0 || rows[i].status === 3) {
-                report = false
-                break;
-            }
-        }
-        //判断状态
-        if (report) {
-            window?.$dialog?.warning({
-                title: "确认操作",
-                content: "是否废除勾选的已上报文件?",
-                positiveText: "确定废除",
-                negativeText: "取消",
-                onPositiveClick: () => {
-                    batchAbolishSave(ids)
-                }
-            });
-        } else {
-            window.$message?.warning('未上报的文件不能废除')
-        }
-    } else {
-        window.$message?.warning('请先勾选要废除的数据')
-    }
+
 }
 
-//废除勾选的已上报文件
-const batchAbolishSave = (ids) => {
-    if (ids) {
-        dataFillQuery.batchAbolish({
-            ids: ids
-        }).then(({data}) => {
-            if (data.code === 200) {
-                window.$message?.success('批量废除成功')
-                checkedRowsRef.value = []
-                getTableData()
-            } else {
-                window.$message?.error(data.msg || '批量废除失败')
-            }
-        })
-    } else {
-        window.$message?.warning('请先勾选要废除的数据')
-    }
+//本地验签
+const localLoading = ref(false)
+const batchLocal = () => {
+
+}
+
+//在线验签
+const onlineLoading = ref(false)
+const batchOnline = () => {
+
 }
 
 
@@ -407,8 +425,9 @@ const batchAbolishSave = (ids) => {
 //左右拖动,改变树形结构宽度
 const leftWidth = ref(382);
 const onmousedown = () => {
+    const leftNum = isCollapse.value ? 142 : 272
     document.onmousemove = (ve) => {
-        let diffVal = ve.clientX + 2;
+        let diffVal = ve.clientX - leftNum;
         if(diffVal >= 310 && diffVal <= 900) {
             leftWidth.value = diffVal;
         }

+ 545 - 0
src/views/data-fill/query_bak.vue

@@ -0,0 +1,545 @@
+<template>
+    <n-divider dashed title-placement="left">资料查询</n-divider>
+    <div class="hc-layout-box">
+        <div class="hc-layout-left-box" :style="'width:' + leftWidth + 'px;'">
+            <div class="hc-project-box">
+                <div class="text-xl text-cut project-alias-box">
+                    <i class="hcicon-xiangmu"/>
+                    <span class="ml-2">{{projectInfo['projectAlias']}}</span>
+                </div>
+                <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
+            </div>
+            <div class="hc-el-tree-box">
+                <div class="search-tree-val">
+                    <n-input-group>
+                        <n-input v-model:value="searchTreeVal" type="text" placeholder="请输入名称关键词检索" clearable  @keyup="searchTreeKeyUp"/>
+                        <n-button type="primary" @click="searchTreeClick">搜索</n-button>
+                    </n-input-group>
+                </div>
+                <template v-if="isSearchTree">
+                    <HcTreeData :datas="searchTreeData" :params="treeParams" :props="wbsElTreeProps" isColor @node-click="wbsElTreeClick"/>
+                </template>
+                <template v-else>
+                    <HcTree type="data-fill-query" :params="treeParams" :props="wbsElTreeProps" lazy isColor @node-click="wbsElTreeClick"/>
+                </template>
+            </div>
+            <div class="hc-tree-foot-tip-box">
+                <div class="dot-view green">已审批</div>
+                <div class="dot-view black">未填报</div>
+                <div class="dot-view orange">已填报-待审批</div>
+                <div class="dot-view blue">已填报-未上报</div>
+            </div>
+            <!--左右拖动-->
+            <div class="horizontal-drag-line" @mousedown="onmousedown"/>
+        </div>
+        <div class="hc-layout-content-box">
+            <n-card class="hc-card-overflow-box" :segmented="{content: true}">
+                <template #header>
+                    <div class="hc-card-header flex items-center">
+                        <div class="w-40">
+                            <n-select v-model:value="searchForm.taskStatus" :options="processStatusData" placeholder="流程状态" clearable/>
+                        </div>
+                        <div class="w-40 ml-5">
+                            <n-select v-model:value="searchForm.fileUserIdAndName" :options="reportingPersonData" placeholder="填报人" clearable/>
+                        </div>
+                        <div class="w-40 ml-5">
+                            <n-select v-model:value="searchForm.sourceType" :options="fileTypeData" placeholder="文件类型" clearable/>
+                        </div>
+                        <div class="w-40 ml-5">
+                            <n-select v-model:value="searchForm.reportNumber" :options="reportBatchData" placeholder="上报批次" clearable/>
+                        </div>
+                        <div class="w-96 ml-5">
+                            <n-date-picker v-model:formatted-value="betweenTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" clearable @update:value="betweenTimeUpdate"/>
+                        </div>
+                        <div class="w-60 ml-5">
+                            <n-input v-model:value="searchForm.queryValue" type="text" placeholder="请输入名称关键词检索" clearable @keyup="keyUpEvent">
+                                <template #suffix>
+                                    <span class="_icon-search hc-input-search-icon"/>
+                                </template>
+                            </n-input>
+                        </div>
+                        <n-button type="primary" class="ml-2" @click="searchChange">搜索</n-button>
+                    </div>
+                </template>
+                <div class="hc-sticky-box p-6">
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
+                        <template #trigger>
+                            <n-button type="primary" class="px-8" @click="reportModalClick">上报</n-button>
+                        </template>
+                        <span>{{btn_report?.textInfo}}</span>
+                    </n-popover>
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_download?.textInfo" v-if="btn_download">
+                        <template #trigger>
+                            <n-button type="primary" strong secondary class="px-8 ml-6" :loading="batchDownloadLoading" @click="batchDownload">下载</n-button>
+                        </template>
+                        <span>{{btn_download?.textInfo}}</span>
+                    </n-popover>
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_print?.textInfo" v-if="btn_print">
+                        <template #trigger>
+                            <n-button type="primary" strong secondary class="px-8 ml-6" :loading="batchPrintLoading" @click="batchPrint">打印</n-button>
+                        </template>
+                        <span>{{btn_print?.textInfo}}</span>
+                    </n-popover>
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_abolish?.textInfo" v-if="btn_abolish">
+                        <template #trigger>
+                            <n-button type="primary" strong secondary class="px-8 ml-6" @click="batchAbolishClick">废除</n-button>
+                        </template>
+                        <span>{{btn_abolish?.textInfo}}</span>
+                    </n-popover>
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_online?.textInfo" v-if="btn_online">
+                        <template #trigger>
+                            <n-button type="primary" strong secondary class="px-8 ml-6" :loading="batchOnlineLoading" @click="batchOnline">在线验签</n-button>
+                        </template>
+                        <span>{{btn_online?.textInfo}}</span>
+                    </n-popover>
+                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_local?.textInfo" v-if="btn_local">
+                        <template #trigger>
+                            <n-button type="primary" strong secondary class="px-8 ml-6" :loading="batchLocalLoading" @click="batchLocal">本地验签</n-button>
+                        </template>
+                        <span>{{btn_local?.textInfo}}</span>
+                    </n-popover>
+                </div>
+                <div class="p-6 pt-0">
+                    <n-data-table :columns="tableColumns" :data="tableData" :row-key="row => row.id" @update:checked-row-keys="tableHandleCheck" :single-line="false" striped/>
+                </div>
+                <template #action>
+                    <HcPage :pages="searchForm" @change="pageChange"/>
+                </template>
+            </n-card>
+        </div>
+    </div>
+    <!--批量上报审批-->
+    <HcReportModal  title="批量上报审批" url="informationWriteQuery/batchTask" :show="showReportModal" :projectId="projectId" :contractId="contractId"
+                    :taskName="reportTaskName" :ids="reportIds" @hide="showReportModal = false" @finish="showReportFinish"/>
+</template>
+
+<script setup>
+import {ref,watch,onMounted} from "vue";
+import {useAppStore} from "~src/store/index";
+import HcTree from "~com/plugins/element/HcTree.vue"
+import HcPage from "~com/plugins/naive/HcPage.vue"
+import HcTreeData from "./components/HcTreeData.vue"
+import dataFillQuery from '~api/data-fill/query';
+import HcReportModal from "~com/reportModal/index.vue"
+import {deepClone} from "~src/utils/lib/util";
+import {download} from '~src/utils/lib/tools';
+
+//变量
+const useAppState = useAppStore()
+const projectId = ref(useAppState.getProjectId);
+const contractId = ref(useAppState.getContractId);
+const projectInfo = ref(useAppState.getProjectInfo);
+
+//按钮气泡开关
+const bubbleVal = ref(useAppState.getBubble);
+//监听
+watch(() => [
+    useAppState.getProjectId,
+    useAppState.getContractId,
+    useAppState.getProjectInfo,
+    useAppState.getBubble,
+], ([UserProjectId, UserContractId,UserProjectInfo,Bubble]) => {
+    //项目合同数据
+    projectId.value = UserProjectId
+    contractId.value = UserContractId
+    projectInfo.value = UserProjectInfo
+    //按钮气泡开关
+    bubbleVal.value = Bubble
+})
+
+//获取气泡数据
+const getButtonsVal = (value) => {
+    return useAppState.getButtonsVal(value)
+}
+
+//气泡数据
+const btn_report = ref(getButtonsVal('query_report'))
+const btn_download = ref(getButtonsVal('query_download'))
+const btn_print = ref(getButtonsVal('query_print'))
+const btn_local = ref(getButtonsVal('query_local_attestation'))
+const btn_abolish = ref(getButtonsVal('query_abolish'))
+const btn_online = ref(getButtonsVal('query_online_attestation'))
+
+const processStatusData = ref([])   //流程状态
+const reportingPersonData = ref([]) //填报人
+const fileTypeData = ref([])        //文件类型
+const reportBatchData = ref([])     //上报批次
+
+//搜索表单
+const betweenTime = ref(null)
+const searchForm = ref({
+    taskStatus: null, fileUserIdAndName: null, sourceType: null, reportNumber: null,
+    queryValue: null, betweenTime: null, wbsId: null, current: 1, size: 20, total: 0
+})
+
+//树数据
+const wbsElTreeProps = {label: 'title', children: 'children', isLeaf: 'notExsitChild'}
+const treeParams = ref({contractId: contractId.value})
+//树被点击
+const wbsElTreeClick = ({data}) => {
+    searchForm.value.wbsId = data['contractIdRelation'] ? data['id'] : data['primaryKeyId']
+    searchForm.value.contractIdRelation = data['contractIdRelation']
+    searchForm.value.current = 1;
+    getTableData()
+}
+
+//树搜索
+const isSearchTree = ref(false)
+const searchTreeVal = ref('')
+const searchTreeData = ref([])
+console.log(isSearchTree.value)
+//回车
+const searchTreeKeyUp = (e) => {
+    if (e.key === "Enter") {
+        searchTreeClick()
+    }
+}
+const searchTreeClick = () => {
+    if (searchTreeVal.value) {
+        isSearchTree.value = true
+        dataFillQuery.searchContractTree({
+            contractId: contractId.value,
+            queryValue: searchTreeVal.value
+        }).then(({data}) => {
+            searchTreeData.value = data?.data || []
+        }).catch(() => {
+            searchTreeData.value = []
+        })
+    } else {
+        isSearchTree.value = false
+    }
+}
+
+//渲染完成
+onMounted(() => {
+    getFileUser()
+    getReportNumber()
+    getDictBizClassify('flowTaskStatus')
+    getDictBizClassify('fileType')
+})
+
+//获取所有填报人
+const getFileUser = () => {
+    dataFillQuery.getFileUser({
+        contractId: contractId.value
+    }).then(({data}) => {
+        let res = data?.data || [], userArr = [];
+        res.forEach(item => {
+            userArr.push({
+                label: item?.userName,
+                value: `${item?.userId}-${item?.userName}`
+            })
+        })
+        reportingPersonData.value = userArr
+    })
+}
+
+//获取所有填报人
+const getReportNumber = () => {
+    dataFillQuery.getReportNumber({
+        contractId: contractId.value
+    }).then(({data}) => {
+        let res = data?.data || [];
+    })
+}
+
+//获取流程状态分类和文件类型分类
+const getDictBizClassify = (code) => {
+    dataFillQuery.getDictBizClassify({
+        contractId: contractId.value,
+        code: code
+    }).then(({data}) => {
+        let res = data?.data || [], typeArr = [];
+        res.forEach((key) => {
+            typeArr.push({
+                label: key?.dictValue,
+                value: key?.dictKey
+            })
+        })
+        if (code === 'flowTaskStatus') {
+            processStatusData.value = typeArr
+        } else if (code === 'fileType') {
+            fileTypeData.value = typeArr
+        }
+    })
+}
+
+//重新搜索数据
+const searchChange = () => {
+    searchForm.value.current = 1
+    getTableData()
+}
+
+//回车搜索
+const keyUpEvent = (e) => {
+    if (e.key === "Enter") {
+        searchForm.value.current = 1;
+        getTableData()
+    }
+}
+
+//日期时间被选择
+const betweenTimeUpdate = (_,res) => {
+    res = res || []
+    if (res.length > 0) {
+        searchForm.value.betweenTime = res[0] + '~' + res[1]
+    } else {
+        searchForm.value.betweenTime = null
+    }
+    searchForm.value.current = 1;
+    getTableData()
+}
+
+//分页被点击
+const pageChange = (res) => {
+    searchForm.value.current = res.current;
+    searchForm.value.size = res.size;
+    getTableData()
+}
+
+//获取数据
+const getTableData = () => {
+    if (!!searchForm.value.wbsId) {
+        dataFillQuery.getPageData({
+            projectId: projectId.value,
+            contractId: contractId.value,
+            ...searchForm.value
+        }).then(({data}) => {
+            let res = data['data'] || {}
+            tableData.value = res['records'] || []
+            searchForm.value.total = res.total || 0
+        })
+    } else {
+        window?.$message?.warning('请先选择一个树节点')
+    }
+}
+
+//表格表头
+const tableColumns = [
+    {type: 'selection'},
+    {title: '序号', key: 'key', width: 80, align: 'center',
+        render(_, index) {
+            return index + 1
+        }
+    },
+    {title: '文件名称', key: 'name'},
+    {title: '开始时间', key: 'startTime'},
+    {title: '流程状态', key: 'taskStatusStr'},
+    {title: '上报批次', key: 'reportNumber', width: 120, align: 'center'},
+    {title: '填报人', key: 'fileUserIdAndName'},
+    {title: '任务人', key: 'tesk'}
+]
+//表格数据
+const tableData = ref([]);
+//表格勾选
+const checkedRowsRef = ref([])
+const tableHandleCheck = (_,rows) => {
+    checkedRowsRef.value = rows.filter((item) => {
+        return (item??'') !== '';
+    })
+}
+
+//批量上报
+const reportIds = ref('')
+const reportTaskName = ref('')
+const showReportModal = ref(false)
+const reportModalClick = () => {
+    const rows = checkedRowsRef.value;
+    if (rows.length > 0) {
+        let ids = '', report = true;
+        for (let i = 0; i < rows.length; i++) {
+            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
+            if (rows[i].status !== 0 && rows[i].status !== 3) {
+                report = false
+                break;
+            }
+        }
+        //判断状态
+        if (report) {
+            reportIds.value = ids
+            reportTaskName.value = rows.length > 1?`${rows[0].name}等${rows.length}个文件`:rows[0].name
+            showReportModal.value = true
+        } else {
+            window.$message?.warning('已上报的文件不能进行再次上报,若要重新上报,要先撤回之前的上报,再重新上报')
+        }
+    } else {
+        window.$message?.warning('请先勾选要上报的数据')
+    }
+}
+
+//上报完成
+const showReportFinish = () => {
+    showReportModal.value = false
+    checkedRowsRef.value = []
+    getTableData()
+}
+
+//批量废除
+const batchAbolishClick = () => {
+    const rows = deepClone(checkedRowsRef.value)
+    if (rows.length > 0) {
+        let report = true, ids = '';
+        for (let i = 0; i < rows.length; i++) {
+            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
+            if (rows[i].status === 0 || rows[i].status === 3) {
+                report = false
+                break;
+            }
+        }
+        //判断状态
+        if (report) {
+            window?.$dialog?.warning({
+                title: "确认操作",
+                content: "是否废除勾选的已上报文件?",
+                positiveText: "确定废除",
+                negativeText: "取消",
+                onPositiveClick: () => {
+                    batchAbolishSave(ids)
+                }
+            });
+        } else {
+            window.$message?.warning('未上报的文件不能废除')
+        }
+    } else {
+        window.$message?.warning('请先勾选要废除的数据')
+    }
+}
+
+//废除勾选的已上报文件
+const batchAbolishSave = (ids) => {
+    if (ids) {
+        dataFillQuery.batchAbolish({
+            ids: ids
+        }).then(({data}) => {
+            if (data.code === 200) {
+                window.$message?.success('批量废除成功')
+                checkedRowsRef.value = []
+                getTableData()
+            } else {
+                window.$message?.error(data.msg || '批量废除失败')
+            }
+        })
+    } else {
+        window.$message?.warning('请先勾选要废除的数据')
+    }
+}
+
+//批量下载
+const batchDownloadLoading = ref(false)
+const batchDownload = () => {
+    const rows = checkedRowsRef.value;
+    if (rows.length > 0) {
+        let ids = '';
+        for (let i = 0; i < rows.length; i++) {
+            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
+        }
+        //批量下载
+        batchDownloadLoading.value = true
+        dataFillQuery.batchDownloadFileToZip({
+            ids: ids
+        }).then((res) => {
+            batchDownloadLoading.value = false
+            download(res)   //下载文件
+        })
+    } else {
+        window.$message?.warning('请先勾选要下载的数据')
+    }
+}
+
+//批量打印
+const batchPrintLoading = ref(false)
+const batchPrint = () => {
+    const rows = checkedRowsRef.value;
+    if (rows.length > 0) {
+        let ids = '';
+        for (let i = 0; i < rows.length; i++) {
+            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
+        }
+        //发起
+        batchPrintLoading.value = true
+        dataFillQuery.batchPrint({
+            ids: ids
+        }).then(({data}) => {
+            console.log(data)
+            batchPrintLoading.value = false
+        })
+    } else {
+        window.$message?.warning('请先勾选要下载的数据')
+    }
+}
+
+//本地验签
+const batchLocalLoading = ref(false)
+const batchLocal = () => {
+    const rows = checkedRowsRef.value;
+    if (rows.length <= 0) {
+        window.$message?.warning('请先勾选要下载的数据')
+    } else {
+        let report = true, ids = '';
+        for (let i = 0; i < rows.length; i++) {
+            ids += `${ids?`,${rows[i]['id']}`:rows[i]['id']}`
+            if (rows[i].status !== 2) {
+                report = false
+                break;
+            }
+        }
+        //判断状态
+        if (report) {
+            batchLocalLoading.value = true
+            dataFillQuery.localVerify({
+                ids: ids
+            }).then(({data}) => {
+                console.log(data)
+                batchLocalLoading.value = false
+            })
+        } else {
+            window.$message?.warning('存在未审批或未上报数据')
+        }
+    }
+}
+
+//在线验签
+const batchOnlineLoading = ref(false)
+const batchOnline = () => {
+    const rows = checkedRowsRef.value;
+    if (rows.length <= 0) {
+        window.$message?.warning('请先勾选要下载的数据')
+        return;
+    }
+    if (rows.length > 1) {
+        window.$message?.warning('在线验签只能勾选一条数据进行验签')
+        return;
+    }
+    if (rows[0].status !== 2) {
+        window.$message?.warning('存在未审批或未上报数据')
+        return;
+    }
+    //发起
+    batchOnlineLoading.value = true
+    dataFillQuery.onlineVerify({
+        ids: rows[0]['id']
+    }).then(({data}) => {
+        console.log(data)
+        batchOnlineLoading.value = false
+    })
+}
+
+//左右拖动,改变树形结构宽度
+const leftWidth = ref(382);
+const onmousedown = () => {
+    document.onmousemove = (ve) => {
+        let diffVal = ve.clientX + 2;
+        if(diffVal >= 310 && diffVal <= 900) {
+            leftWidth.value = diffVal;
+        }
+    }
+    document.onmouseup = () => {
+        document.onmousemove = null;
+        document.onmouseup = null;
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../../styles/data-fill/query.scss";
+</style>

+ 118 - 20
src/views/data-fill/wbs.vue

@@ -1,6 +1,22 @@
 <template>
     <div class="h-full">
-        <HcCard :scrollbar="false" v-if="wbsTypeTabKey === 'map'">
+        <HcCard :scrollbar="false" id-ref="wbs-node-tree-card-target" v-if="wbsTypeTabKey === 'map'">
+            <template #header>
+                <HcTooltip keys="wbs_views_drawings" v-if="NodeStatus !== '3' && isDrawer">
+                    <el-button hc-btn :disabled="!nodeDataInfo?.fileUrl" @click="viewsDrawings">
+                        <HcIcon name="wallpaper"/>
+                        <span>查看图纸</span>
+                    </el-button>
+                </HcTooltip>
+                <el-button :type="authBtnTabKey === '1'?'primary':''" hc-btn @click="authBtnTabClick('1')" v-if="isDrawer">
+                    <HcIcon name="e911_emergency"/>
+                    <span>施工质检</span>
+                </el-button>
+                <el-button :type="authBtnTabKey === '2'?'primary':''" hc-btn @click="authBtnTabClick('2')" v-if="isDrawer">
+                    <HcIcon name="engineering"/>
+                    <span>监理质检</span>
+                </el-button>
+            </template>
             <template #extra>
                 <HcNewSwitch :datas="wbsTypeTab" :keys="wbsTypeTabKey" @change="wbsTypeTabChange"/>
             </template>
@@ -14,6 +30,43 @@
                     <div class="dot-view blue">已填报-未上报</div>
                 </div>
             </template>
+            <HcDrawer :show="isDrawer" :scrollbar="false" actionSize="lg" to-id="wbs-node-tree-card-target">
+                <!--清表列表-->
+                <el-scrollbar ref="ListItemScrollRef">
+                    <ListItem ref="ListItemRef" :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop" @renew="getTableDataAll"/>
+                </el-scrollbar>
+                <!--底部按钮区域-->
+                <template #action>
+                    <HcTooltip keys="wbs_save" v-if="NodeStatus !== '3'">
+                        <el-button type="primary" hc-btn :disabled="NodeStatus === '3' || ListItemDatas.length <= 0" :loading="tableFormSaveLoading" @click="tableFormSaveClick">
+                            <HcIcon name="save"/>
+                            <span>保存</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="wbs_report" v-if="NodeStatus !== '3'">
+                        <el-button hc-btn :disabled="NodeStatus === '3' || NodeStatus === '1'" @click="reportModalClick">
+                            <HcIcon name="drive_folder_upload"/>
+                            <span>上报</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="wbs_preview">
+                        <el-button hc-btn :disabled="NodeStatus === '1'" :loading="bussPdfsLoading" @click="bussPdfsClick">
+                            <HcIcon name="visibility"/>
+                            <span>预览</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="wbs_abolish" v-if="NodeStatus === '3'">
+                        <el-button hc-btn @click="abolishOneClick">
+                            <HcIcon name="undo"/>
+                            <span>撤回上报流程</span>
+                        </el-button>
+                    </HcTooltip>
+                    <el-button hc-btn @click="drawerClose">
+                        <HcIcon name="close"/>
+                        <span>关闭填报页面</span>
+                    </el-button>
+                </template>
+            </HcDrawer>
         </HcCard>
         <div class="hc-layout-box" v-if="wbsTypeTabKey === 'tree'">
             <div class="hc-layout-left-box" id="wbs-left-tree" :style="'width:' + leftWidth + 'px;'">
@@ -28,15 +81,22 @@
                 </div>
                 <div class="hc-tree-box">
                     <div class="hc-search-tree-val">
-                        <el-input v-model="searchTreeVal" block size="large" placeholder="请输入名称关键词检索" clearable>
+                        <el-input v-model="searchTreeVal" block size="large" placeholder="请输入名称关键词检索" clearable @keyup="searchTreeKeyUp">
                             <template #suffix>
                                 <HcIcon name="search" ui="text-2xl"/>
                             </template>
                         </el-input>
                     </div>
-                    <div class="hc-tree-scrollbar">
+                    <div class="hc-tree-scrollbar" v-loading="treeLoading" element-loading-text="获取数据中...">
                         <el-scrollbar>
-                            <WbsTree ref="HcTreeRef" :menus="ElTreeMenu" :isMark="TreeMark" :autoExpandKeys="TreeAutoExpandKeys" :projectId="projectId" :contractId="contractId" isColor @nodeTap="wbsElTreeClick" @menuTap="ElTreeMenuClick"/>
+                            <KeepAlive>
+                                <template v-if="isSearchTree">
+                                    <HcTreeData :datas="searchTreeData" :menus="ElTreeMenu" :isMark="TreeMark" :autoExpandKeys="TreeAutoExpandKeys" isColor @nodeTap="wbsElTreeClick" @menuTap="ElTreeMenuClick"/>
+                                </template>
+                                <template v-else>
+                                    <WbsTree :menus="ElTreeMenu" :isMark="TreeMark" :autoExpandKeys="TreeAutoExpandKeys" :projectId="projectId" :contractId="contractId" isColor @nodeTap="wbsElTreeClick" @menuTap="ElTreeMenuClick"/>
+                                </template>
+                            </KeepAlive>
                         </el-scrollbar>
                     </div>
                 </div>
@@ -81,7 +141,7 @@
                     <!--底部按钮区域-->
                     <template #action>
                         <HcTooltip keys="wbs_save" v-if="NodeStatus !== '3'">
-                            <el-button type="primary" hc-btn :disabled="NodeStatus === '3'" :loading="tableFormSaveLoading" @click="tableFormSaveClick">
+                            <el-button type="primary" hc-btn :disabled="NodeStatus === '3' || ListItemDatas.length <= 0" :loading="tableFormSaveLoading" @click="tableFormSaveClick">
                                 <HcIcon name="save"/>
                                 <span>保存</span>
                             </el-button>
@@ -249,8 +309,8 @@
                 </div>
             </template>
         </el-dialog>
-        <!--批量上报审批 showReportModal-->
-        <HcReportModal  title="批量上报审批" url="informationWriteQuery/taskOne" :show="true" :projectId="projectId" :contractId="contractId"
+        <!--批量上报审批-->
+        <HcReportModal  title="批量上报审批" url="informationWriteQuery/taskOne" :show="showReportModal" :projectId="projectId" :contractId="contractId"
                         :taskName="reportTaskName" :ids="reportIds" :addition="reportAddition" @hide="showReportModal = false" @finish="showReportFinish"/>
     </div>
 </template>
@@ -264,8 +324,8 @@ import {HcIsButton} from "~src/plugins/IsButtons";
 import ListItem from "./components/ListItem.vue"
 import NodeTree from "./components/nodeTree/index.vue"
 import ImgPreview from "~com/imgPreview/index.vue"
-//import HcReportModal from "~com/reportModal/index.vue"
 import HcTreeNode from "./components/HcTreeNode.vue"
+import HcTreeData from "./components/HcTreeData.vue"
 import WbsTree from "./components/WbsTree.vue"
 import {getTokenHeader} from '~src/api/request/header';
 import {getStoreData, setStoreData} from '~src/utils/storage'
@@ -308,7 +368,9 @@ onMounted(()=> {
 //身份按钮切换数据
 const authBtnTabKey = ref('1')
 const authBtnTabClick = (val) => {
-    if (val !== authBtnTabKey.value) {
+    if (!primaryKeyId.value) {
+        window?.$message?.warning('请先在左侧项目树选择一个节点')
+    } else if (val !== authBtnTabKey.value) {
         authBtnTabKey.value = val
         getTableDataAll()
     }
@@ -330,10 +392,15 @@ const getTableDataAll = () => {
 
 //结构类型tab数据和相关处理
 const wbsTypeTabKey = ref(typeName)
-const wbsTypeTab = ref([{key:'map',  name: '导图结构填报'}, {key:'tree', name: '树形结构填报'}]);
+const wbsTypeTab = ref([
+    {key:'map',  name: '导图结构填报'},
+    {key:'tree', name: '树形结构填报'}
+]);
 const wbsTypeTabChange = async (item) => {
     wbsTypeTabKey.value = item?.key;
+    ListItemDatas.value = []
     isDrawer.value = false;
+    //路由跳转
     router.push({
         path: useRoutes.path,
         query: {
@@ -387,18 +454,49 @@ const setElTreeMenu = (contractType) => {
     ElTreeMenu.value = newArr
 }
 
-//树的配置
-const HcTreeRef = ref(null)
-const searchTreeVal = ref('')
-
 //树相关变量
+const primaryKeyId = ref('')
 const nodeItemInfo = ref({})
 const nodeDataInfo = ref({})
 
+//树搜索
+const isSearchTree = ref(false)
+const searchTreeVal = ref('')
+const searchTreeData = ref([])
+//回车
+const searchTreeKeyUp = (e) => {
+    if (e.key === "Enter") {
+        searchTreeClick()
+    }
+}
+const treeLoading = ref(false)
+const searchTreeClick = async () => {
+    if (searchTreeVal.value) {
+        isSearchTree.value = true
+        treeLoading.value = true
+        const {error, code, data} = await wbsApi.searchContractTree({
+            contractId: contractId.value,
+            queryValue: searchTreeVal.value
+        })
+        //判断状态
+        if (!error && code === 200) {
+            searchTreeData.value = getArrValue(data)
+            treeLoading.value = false
+        } else {
+            treeLoading.value = false
+            searchTreeData.value = []
+        }
+    } else {
+        treeLoading.value = false
+        isSearchTree.value = false
+    }
+}
+
 //树被点击
 const wbsElTreeClick = ({node, data, keys}) => {
     nodeItemInfo.value = node
     nodeDataInfo.value = data
+    primaryKeyId.value = data?.primaryKeyId || ''
     setStoreData('wbsTreeExpandKeys',keys)
     TreeAutoExpandKeys.value = keys || []
     searchNodeAllTable()
@@ -424,9 +522,14 @@ const isDrawer = ref(false)
 const NodeTreeDblClick = ({node,data}) => {
     nodeItemInfo.value = node
     nodeDataInfo.value = data
+    primaryKeyId.value = data?.primaryKeyId || ''
     isDrawer.value = true;
     searchNodeAllTable()
 }
+const drawerClose = () => {
+    isDrawer.value = false;
+    ListItemDatas.value = []
+}
 
 //导图树菜单被点击
 const NodeTreeMenuClick = async ({key,node,data}) => {
@@ -867,13 +970,8 @@ const drawingsClose = (res) => {
 const ListItemScrollRef = ref(null)
 const ListItemOffsetTop = (offsetTop) => {
     if (offsetTop > 0) {
-        const type = wbsTypeTabKey.value || 'map'
         setTimeout(() => {
-            if (type === 'map') {
-                //document.getElementById('hc-drawer-list-item').scrollTop = offsetTop
-            } else if (type === 'tree') {
-                ListItemScrollRef.value?.setScrollTop(offsetTop)
-            }
+            ListItemScrollRef.value?.setScrollTop(offsetTop)
         }, 200)
     }
 }

+ 0 - 1448
src/views/data-fill/wbs_new.vue

@@ -1,1448 +0,0 @@
-<template>
-    <n-divider dashed title-placement="left">{{wbsTypeTabKey === 'tree'?'树形结构填报':'导图结构填报'}}</n-divider>
-    <div class="hc-layout-box">
-        <div class="hc-layout-left-box" :style="'width:' + leftWidth + 'px;'" v-if="wbsTypeTabKey === 'tree'">
-            <div class="hc-project-box">
-                <div class="text-xl text-cut project-alias-box">
-                    <i class="hcicon-xiangmu"/>
-                    <span class="ml-2">{{projectInfo['projectAlias']}}</span>
-                </div>
-                <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
-            </div>
-            <div class="hc-el-tree-box">
-                <HcTree ref="HcTreeRef" type="data-fill-wbs" :params="treeParams" :props="wbsElTreeProps" :menus="ElTreeMenu" :menuMark="TreeMark" lazy :autoExpandKeys="TreeAutoExpandKeys" isColor @node-click="wbsElTreeClick" @menuTap="ElTreeMenuClick"/>
-            </div>
-            <div class="hc-tree-foot-tip-box">
-                <div class="dot-view green">已审批</div>
-                <div class="dot-view black">未填报</div>
-                <div class="dot-view orange">已填报-待审批</div>
-                <div class="dot-view blue">已填报-未上报</div>
-            </div>
-            <!--左右拖动-->
-            <div class="horizontal-drag-line" @mousedown="onmousedown"/>
-        </div>
-        <div class="hc-layout-content-box">
-            <n-card class="hc-card-overflow-box" :segmented="{content: true}">
-                <template #header>
-                    <div class="hc-card-header flex items-center" v-show="wbsTypeTabKey === 'tree' || isDrawer">
-                        <n-popover trigger="hover" :disabled="!bubbleVal || !btn_drawings?.textInfo" v-if="btn_drawings">
-                            <template #trigger>
-                                <n-button type="primary" strong secondary class="px-3" @click="viewsDrawings" v-if="nodeDataInfo.fileUrl">
-                                    <i class="hcicon-Frame"/>
-                                    <span class="ml-2">查看图纸</span>
-                                </n-button>
-                                <n-button color="#EEEEEE" text-color="#999999" class="px-3" @click="viewsDrawingsTip" v-else>
-                                    <i class="hcicon-Frame"/>
-                                    <span class="ml-2">查看图纸</span>
-                                </n-button>
-                            </template>
-                            <span>{{btn_drawings?.textInfo}}</span>
-                        </n-popover>
-                        <BtnTab class="ml-8" :datas="authBtnTabData" :cur="authBtnTabKey" :disabled="!nodeDataInfo?.primaryKeyId" @tabClick="authBtnTabClick"/>
-                    </div>
-                </template>
-                <template #header-extra>
-                    <HcTabs :datas="wbsTypeTab" :keys="wbsTypeTabKey" @change="wbsTypeTabChange"/>
-                </template>
-                <div class="data-fill-wbs-content" id="data-fill-wbs-content-target">
-                    <div class="hc-card-max-h-box" id="hc-card-list-item" v-if="wbsTypeTabKey === 'tree'">
-                        <ListItem ref="ListItemRef" :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop" @uploadData="getTableDataAll"/>
-                    </div>
-                    <div class="hc-card-max-h-box node-tree" v-if="wbsTypeTabKey === 'map'">
-                        <NodeTree ref="NodeTreeRef" :data="NodeTreeData" :format="NodeTreeFormat" :autoExpandKeys="TreeAutoExpandKeys" :menus="ElTreeMenu" :isMark="TreeMark" isColor
-                                  :accordion='NodeTreeAccordion' @nodeClick="NodeTreeClick" @expand="expandClick" @nodeDblClick="NodeTreeDblClick" @menuClick="NodeTreeMenuClick"/>
-                        <div class="hc-tree-foot-tip-box bg-svg-xml">
-                            <div class="dot-view green">已审批</div>
-                            <div class="dot-view black">未填报</div>
-                            <div class="dot-view orange">已填报-待审批</div>
-                            <div class="dot-view blue">已填报-未上报</div>
-                        </div>
-                    </div>
-                </div>
-                <!--填报弹窗-->
-                <n-drawer v-model:show="isDrawer" :width="200" :height="200" placement="top" :trap-focus="false" :block-scroll="false" to="#data-fill-wbs-content-target">
-                    <div class="drawer-data-fill-content-box">
-                        <n-card :segmented="{content: true}">
-                            <div class="data-fill-content" id="hc-drawer-list-item">
-                                <ListItem ref="ListItemsRef" :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop" @uploadData="getTableDataAll"/>
-                            </div>
-                            <template #action>
-                                <div class="data-fill-foot">
-                                    <template v-if="ListItemDatas.length > 0">
-                                        <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save && NodeStatus !== '3'">
-                                            <template #trigger>
-                                                <n-button type="primary" size="large" class="px-8" :disabled="NodeStatus === '3'" :loading="tableFormSaveLoading" @click="tableFormSaveClick">保存</n-button>
-                                            </template>
-                                            <span>{{btn_save?.textInfo}}</span>
-                                        </n-popover>
-                                        <n-popover trigger="hover" :disabled="!bubbleVal || !btn_preview?.textInfo" v-if="btn_preview">
-                                            <template #trigger>
-                                                <n-button type="primary" strong secondary class="px-8 ml-6" size="large" :disabled="NodeStatus === '1'" :loading="bussPdfsLoading" @click="bussPdfsClick">预览</n-button>
-                                            </template>
-                                            <span>{{btn_preview?.textInfo}}</span>
-                                        </n-popover>
-                                        <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report && NodeStatus !== '3'">
-                                            <template #trigger>
-                                                <n-button type="primary" strong secondary class="px-8 ml-6" size="large" :disabled="NodeStatus === '3' || NodeStatus === '1'" @click="reportModalClick">上报</n-button>
-                                            </template>
-                                            <span>{{btn_report?.textInfo}}</span>
-                                        </n-popover>
-                                        <n-popover trigger="hover" :disabled="!bubbleVal || !btn_abolish?.textInfo" v-if="btn_abolish && NodeStatus === '3'">
-                                            <template #trigger>
-                                                <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="abolishOneClick">撤回上报流程</n-button>
-                                            </template>
-                                            <span>{{btn_abolish?.textInfo}}</span>
-                                        </n-popover>
-                                    </template>
-                                    <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="isDrawer = false">关闭填报页面</n-button>
-                                </div>
-                            </template>
-                        </n-card>
-                    </div>
-                </n-drawer>
-            </n-card>
-            <div class="bg-white data-fill-foot-box" v-if="wbsTypeTabKey === 'tree' && ListItemDatas.length > 0">
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save && NodeStatus !== '3'">
-                    <template #trigger>
-                        <n-button type="primary" size="large" class="px-8" :disabled="NodeStatus === '3'" :loading="tableFormSaveLoading" @click="tableFormSaveClick">保存</n-button>
-                    </template>
-                    <span>{{btn_save?.textInfo}}</span>
-                </n-popover>
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_preview?.textInfo" v-if="btn_preview">
-                    <template #trigger>
-                        <n-button type="primary" strong secondary class="px-8 ml-6" size="large" :disabled="NodeStatus === '1'" :loading="bussPdfsLoading" @click="bussPdfsClick">预览</n-button>
-                    </template>
-                    <span>{{btn_preview?.textInfo}}</span>
-                </n-popover>
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report && NodeStatus !== '3'">
-                    <template #trigger>
-                        <n-button type="primary" strong secondary class="px-8 ml-6" size="large" :disabled="NodeStatus === '3' || NodeStatus === '1'" @click="reportModalClick">上报</n-button>
-                    </template>
-                    <span>{{btn_report?.textInfo}}</span>
-                </n-popover>
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_abolish?.textInfo" v-if="btn_abolish && NodeStatus === '3'">
-                    <template #trigger>
-                        <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="abolishOneClick">撤回上报流程</n-button>
-                    </template>
-                    <span>{{btn_abolish?.textInfo}}</span>
-                </n-popover>
-            </div>
-        </div>
-    </div>
-    <DragModal title="查看图纸" :isShow="drawingsShow" closeIcon tops="125" widths="380px" bg="bg-dark-3" @close="drawingsClose">
-        <div class="img-preview-box">
-            <ImgPreview :src="nodeDataInfo.fileUrl" isDom toolsSm/>
-        </div>
-    </DragModal>
-
-    <!--标记首件-->
-    <n-modal v-model:show="firstItemModal" class="hc-custom-card" title="标记首件制" preset="card" style="width: 600px;" size="huge" :bordered="false" :segmented="{content: 'soft', footer: 'soft'}">
-        <div class="text-center text-base font-bold">
-            <span>请确认将</span>
-            <span class="text-main">【{{nodeDataInfo.title}}】</span>
-            <span>{{nodeDataInfo.isFirst?'取消':''}}标记为首件</span>
-        </div>
-        <template #footer>
-            <div class="text-center">
-                <n-button class="px-4" @click="firstItemModal = false">取消</n-button>
-                <n-button type="primary" class="px-4 ml-6" :loading="firstItemLoading" @click="firstItemClick">确认</n-button>
-            </div>
-        </template>
-    </n-modal>
-
-    <!--编辑节点-->
-    <n-modal v-model:show="editNodeModal" class="hc-custom-card" title="编辑节点" preset="card" style="width: 600px;" :segmented="{content: 'soft', footer: 'soft'}">
-        <n-form ref="formEditNodeRef" :model="formEditNodeModel" :rules="formEditNodeRules" label-placement="left" label-width="auto" size="large">
-            <n-form-item label="节点名称" path="title">
-                <n-input v-model:value="formEditNodeModel.title" placeholder="请输入节点名称"/>
-            </n-form-item>
-            <n-form-item label="上级节点">
-                <n-input v-model:value="formEditNodeModel.parent.title" disabled/>
-            </n-form-item>
-            <n-form-item label="节点类型">
-                <n-select v-model:value="formEditNodeModel.type" :options="nodeTypeData" disabled/>
-            </n-form-item>
-            <n-form-item label="划分编号" path="partitionCode">
-                <n-input v-model:value="formEditNodeModel.partitionCode" placeholder="请输入划分编号"/>
-            </n-form-item>
-        </n-form>
-        <template #footer>
-            <div class="text-center">
-                <n-button class="px-4" @click="editNodeModal = false">取消</n-button>
-                <n-button type="primary" class="px-4 ml-6" :loading="editNodeLoading" @click="editNodeClick">确认</n-button>
-            </div>
-        </template>
-    </n-modal>
-
-    <!--复制节点-->
-    <n-modal v-model:show="copyNodeModal" title="复制节点" preset="card" class="hc-custom-card copy" :class="copyNodeTabKey==='1'?'one':'many'" :segmented="{content: 'soft', footer: 'soft'}">
-        <template #header-extra>
-            <HcTabs :datas="copyNodeTab" :keys="copyNodeTabKey" @change="copyNodeTabChange"/>
-        </template>
-        <n-form ref="formCopyNodeModelRef" :model="formCopyNodeModel" :rules="formCopyNodeModelRules" label-placement="left" label-width="auto" size="large" v-if="copyNodeTabKey !== '3'">
-            <n-form-item label="节点名称" path="title">
-                <n-input v-model:value="formCopyNodeModel.title" placeholder="请输入节点名称"/>
-            </n-form-item>
-        </n-form>
-        <template #footer v-if="copyNodeTabKey === '1'">
-            <div class="text-center">
-                <n-button class="px-4" @click="copyNodeModal = false">取消</n-button>
-                <n-button type="primary" class="px-4 ml-6" :loading="copyNodeLoading" @click="copyNodeClick">确认</n-button>
-            </div>
-        </template>
-        <div class="copy-node-many-box" v-if="copyNodeTabKey === '2'">
-            <div class="copy-node-many-tree">
-                <HcTree type="data-fill-query" :params="treeParams" :props="wbsElTreeProps" lazy idPrefix="tree-node-copy-" :isAutoRes="false" :autoExpandKeys="treeExpandKeys" @node-click="copyNodeElTreeClick"/>
-            </div>
-            <div class="copy-node-many-table">
-                <div class="copy-node-many-table-data">
-                    <el-table :data="copyNodeTable" border stripe>
-                        <el-table-column prop="title" label="复制到的位置"/>
-                        <el-table-column prop="nodeName" label="节点名称">
-                            <template #default="{row}">
-                                <n-form ref="copyNodeTableRef" :model="row" :rules="copyNodeTableRules">
-                                    <n-form-item path="nodeName">
-                                        <n-input v-model:value="row.nodeName"/>
-                                    </n-form-item>
-                                </n-form>
-                            </template>
-                        </el-table-column>
-                        <el-table-column prop="action" label="操作" width="120" align="center">
-                            <template #default="{_,$index}">
-                                <n-button type="primary" color="#e54d42" size="small" secondary @click="copyNodeTableDel($index)">删除</n-button>
-                            </template>
-                        </el-table-column>
-                    </el-table>
-                </div>
-                <div class="copy-node-many-foot-center">
-                    <n-button class="px-4" @click="copyNodeModal = false">取消</n-button>
-                    <n-button type="primary" class="px-4 ml-6" :loading="copyNodeLoading" @click="copyNodeClick">确认</n-button>
-                </div>
-            </div>
-        </div>
-        <div class="copy-node-many-box" v-if="copyNodeTabKey === '3'">
-            <div class="copy-node-many-tree">
-                <HcTree type="data-fill-query" :params="treeParams" :props="wbsElTreeProps" lazy idPrefix="tree-node-copy-data-" :isAutoRes="false" :autoExpandKeys="treeExpandKeys" @node-click="copyNodeDataElTreeClick"/>
-            </div>
-            <div class="copy-node-many-table">
-                <div class="copy-node-many-table-data">
-                    <el-table :data="copyNodeDataTable" border stripe>
-                        <el-table-column prop="nodeName" label="复制到的位置"/>
-                        <el-table-column prop="action" label="操作" width="120" align="center">
-                            <template #default="{_,$index}">
-                                <n-button type="primary" color="#e54d42" size="small" secondary @click="copyNodeDataTableDel($index)">删除</n-button>
-                            </template>
-                        </el-table-column>
-                    </el-table>
-                </div>
-                <div class="copy-node-many-foot-center">
-                    <n-button class="px-4" @click="copyNodeModal = false">取消</n-button>
-                    <n-button type="primary" class="px-4 ml-6" :loading="copyNodeDataLoading" @click="copyNodeDataClick">确认</n-button>
-                </div>
-            </div>
-        </div>
-    </n-modal>
-
-    <!--新增子节点-->
-    <n-modal v-model:show="addNodeModal" class="hc-custom-card" title="新增子节点" preset="card" style="width: 720px;" :segmented="{content: 'soft', footer: 'soft'}">
-        <HcTreeNode :projectId="projectId" :nodeId="addTreeNodeId" :oldId="addTreeNodeOldId" :strictly="addTreeNodeType == 2" @check-change="addTreeNodeCheckChange"/>
-        <template #footer>
-            <div class="hc-add-node-modal-foot-box">
-                <div class="left-box">
-                    <div class="font-bold mr-4">选中方式:</div>
-                    <el-radio-group v-model="addTreeNodeType">
-                        <el-radio label="1" border>当前及子节点</el-radio>
-                        <el-radio label="2" border>仅当前节点</el-radio>
-                    </el-radio-group>
-                </div>
-                <div class="right-box">
-                    <n-button class="px-4" @click="addNodeModal = false">取消</n-button>
-                    <n-button type="primary" class="px-4 ml-6" :loading="addNodeLoading" @click="addNodeClick">确认</n-button>
-                </div>
-            </div>
-        </template>
-    </n-modal>
-
-    <!--批量上报审批-->
-    <HcReportModal  title="批量上报审批" url="informationWriteQuery/taskOne" :show="showReportModal" :projectId="projectId" :contractId="contractId"
-                    :taskName="reportTaskName" :ids="reportIds" :addition="reportAddition" @hide="showReportModal= false" @finish="showReportFinish"/>
-
-    <!--上传图纸-->
-    <div class="upload-drawing" id="upload-drawing">
-        <n-upload :action="action" :headers="getTokenHeader()" :accept="accept" with-credentials @before-upload="beforeUpload" @finish="uploadFinish"/>
-    </div>
-
-    <!--调整排序-->
-    <n-modal v-model:show="sortNodeModal" class="hc-custom-card" title="调整排序" preset="card" style="width: 720px;" :segmented="{content: 'soft', footer: 'soft'}">
-        <Draggable class="sort-node-body-box list-group" ghost-class="ghost" :list="sortNodeData" item-key="id" @start="sortNodeDrag = true" @end="sortNodeDrag = false">
-            <template #item="{element, index}">
-                <div class="list-group-item">
-                    <div class="index-box">{{index + 1}}</div>
-                    <div class="title-box">{{element.title}}</div>
-                    <div class="icon-box">
-                        <i class="hcicon-wind-descending" @click="downSortClick(index)"/>
-                        <i class="hcicon-wind-ascending" @click="upSortClick(index)"/>
-                    </div>
-                </div>
-            </template>
-        </Draggable>
-        <template #footer>
-            <div class="text-center">
-                <n-button class="px-4" @click="sortNodeModal = false">取消</n-button>
-                <n-button type="primary" class="px-4 ml-6" :loading="sortNodeLoading" @click="sortNodeClick">确认</n-button>
-            </div>
-        </template>
-    </n-modal>
-
-</template>
-
-<script setup>
-import {ref,watch,nextTick,onMounted} from "vue";
-import {useRoute} from 'vue-router'
-import router from '~src/router/index';
-import {useAppStore} from "~src/store/index";
-import {HcIcon} from "~src/plugins/renderele";
-import HcTabs from "~com/plugins/naive/HcTabs.vue"
-import HcTree from "~com/plugins/element/HcTree.vue"
-import BtnTab from "~com/btnTab/index.vue"
-import ListItem from "~com/data-fill/ListItem.vue"
-import NodeTree from "~com/data-fill/nodeTree/index.vue"
-import DragModal from "~com/dragModal/index.vue"
-import ImgPreview from "~com/imgPreview/index.vue"
-import HcReportModal from "~com/reportModal/index.vue"
-import HcTreeNode from "./components/HcTreeNode.vue"
-import wbsApi from "~api/data-fill/wbs"
-import {deepClone} from "~src/utils/lib/util";
-import {getTokenHeader} from '~src/api/request/header';
-import {isArray,isObjNull} from "~src/utils/lib/isApp";
-import {setStore,getStore} from "~src/utils/lib/storage";
-import Draggable from "vuedraggable";
-
-//变量
-const useRoutes = useRoute()
-const useAppState = useAppStore()
-const projectId = ref(useAppState.getProjectId);
-const contractId = ref(useAppState.getContractId);
-const projectInfo = ref(useAppState.getProjectInfo);
-const contractInfo = ref(useAppState.getContractInfo);
-
-//路由参数
-const routerQuery = useRoutes?.query;
-const MenuBarKey = routerQuery?.MenuBarKey || '';
-const typeName = routerQuery?.type || 'map'
-
-const TreeAutoExpandKeys = ref(getStore({name: 'wbsTreeExpandKeys'}) || [])
-
-//按钮气泡开关
-const bubbleVal = ref(useAppState.getBubble);
-
-//监听
-watch(() => [
-    useAppState.getProjectId,
-    useAppState.getContractId,
-    useAppState.getProjectInfo,
-    useAppState.getContractInfo,
-    useAppState.getBubble,
-], ([UserProjectId, UserContractId, UserProjectInfo,UserContractInfo,Bubble]) => {
-    //项目合同数据
-    projectId.value = UserProjectId
-    contractId.value = UserContractId
-    projectInfo.value = UserProjectInfo
-    contractInfo.value = UserContractInfo
-    //按钮气泡开关
-    bubbleVal.value = Bubble
-    setContractType(UserContractInfo?.contractType)
-})
-//获取气泡数据
-const getButtonsVal = (value) => {
-    return useAppState.getButtonsVal(value)
-}
-//气泡数据
-const btn_drawings = ref(getButtonsVal('wbs_views_drawings'))   //查看图纸
-const btn_save = ref(getButtonsVal('wbs_save'))                 //保存
-const btn_preview = ref(getButtonsVal('wbs_preview'))           //预览
-const btn_report = ref(getButtonsVal('wbs_report'))             //上报
-const btn_abolish = ref(getButtonsVal('wbs_abolish'))           //撤回上报流程
-
-//树菜单
-const tree_menu_edit = ref(getButtonsVal('wbs_tree_edit'))  //编辑节点
-const tree_menu_mark = ref(getButtonsVal('wbs_tree_mark'))  //标记为首件
-const tree_menu_copy = ref(getButtonsVal('wbs_tree_copy'))  //复制节点
-const tree_menu_add = ref(getButtonsVal('wbs_tree_add'))    //新增节点
-const tree_menu_up = ref(getButtonsVal('wbs_tree_upload'))  //上传图纸
-const tree_menu_del = ref(getButtonsVal('wbs_tree_del'))    //删除节点
-const tree_menu_sort = ref(getButtonsVal('wbs_tree_sort'))  //调整排序
-
-
-//上传文件的
-const action = ref("/api/blade-resource/oss/endpoint/put-file")
-const accept = ref("image/png,image/jpg,image/jpeg")
-
-//身份按钮切换数据
-const authBtnTabKey = ref('1')
-const authBtnTabData = [
-    {key: "1", label: "施工质检", icon: 'hcicon-a-Frame1'},
-    {key: "2", label: "监理抽检", icon: 'hcicon-a-Frame2'}
-]
-const authBtnTabClick = (val) => {
-    authBtnTabKey.value = val
-    searchNodeAllTable()
-    queryNodeStatus()
-}
-
-const getTableDataAll = () => {
-    searchNodeAllTable()
-    queryNodeStatus()
-}
-
-//结构类型tab数据和相关处理
-const wbsTypeTabKey = ref(typeName)
-const wbsTypeTab = ref([
-    {key:'map',  name: '导图结构填报'},
-    {key:'tree', name: '树形结构填报'}
-]);
-
-//渲染完成
-onMounted(()=> {
-    const contractType = contractInfo.value?.contractType
-    setContractType(contractType)
-    setElTreeMenu()
-    if (wbsTypeTabKey.value === 'map') {
-        queryMappingStructureTree()
-    }
-})
-
-const setContractType = (contractType) => {
-    //contractType,  1施工,2监理
-    if (contractType <= 0) {
-        authBtnTabKey.value = '1'
-    } else {
-        authBtnTabKey.value = contractType + ''
-    }
-}
-
-const wbsTypeTabChange = async (value) => {
-    wbsTypeTabKey.value = value;
-    isDrawer.value = false;
-    const keys = await NodeExpandKeys(true)
-    router.push({
-        path: useRoutes.path,
-        query: {
-            MenuBarKey: MenuBarKey,
-            type: value
-        }
-    })
-    nextTick(() => {
-        TreeAutoExpandKeys.value = keys
-        if (value === 'map') {
-            queryMappingStructureTree()
-        }
-    })
-}
-
-const NodeTreeAccordion = ref(true)
-
-//设置树菜单数据
-const ElTreeMenu = ref([])
-const TreeMark = ref(false)
-const setElTreeMenu = () => {
-    let newArr = [], contractType = contractInfo.value?.contractType;
-    if (contractType === 1) {
-        if (tree_menu_edit.value) {
-            newArr.push({icon: HcIcon('hcicon-bianji'), label: '编辑节点', key: "edit"})
-        }
-        if (tree_menu_mark.value) {
-            newArr.push({icon: HcIcon('cicon-star-o'), label: '标记为首件', key: "mark"})
-            TreeMark.value = true
-        }
-        if (tree_menu_copy.value) {
-            newArr.push({icon: HcIcon('cicon-file-copy-o'), label: '复制节点', key: "copy"})
-        }
-        if (tree_menu_add.value) {
-            newArr.push({icon: HcIcon('cicon-add-round-o'), label: '新增节点', key: "add"})
-        }
-        if (tree_menu_up.value) {
-            newArr.push({icon: HcIcon('hcicon-shangchuan'), label: '上传图纸', key: "upload"})
-        }
-        if (tree_menu_del.value) {
-            newArr.push({icon: HcIcon('cicon-delete-line-o'), label: '删除节点', key: "del"})
-        }
-        if (tree_menu_sort.value) {
-            newArr.push({icon: HcIcon('cicon-sort-order'), label: '调整排序', key: "sort"})
-        }
-    } else if (contractType === 2) {
-        if (tree_menu_copy.value) {
-            newArr.push({icon: HcIcon('cicon-file-copy-o'), label: '复制节点', key: "copy"})
-        }
-        if (tree_menu_add.value) {
-            newArr.push({icon: HcIcon('cicon-add-round-o'), label: '新增节点', key: "add"})
-        }
-    }
-    ElTreeMenu.value = newArr
-}
-
-//树的配置
-const HcTreeRef = ref(null)
-const wbsElTreeProps = {label: 'title', children: 'children', isLeaf: 'notExsitChild'}
-const treeParams = ref({contractId: contractId.value})
-
-//树被点击
-const wbsElTreeClick = ({node,data}) => {
-    nodeItemInfo.value = node
-    nodeDataInfo.value = data
-    searchNodeAllTable()
-    queryNodeStatus()
-}
-
-//导图结构数据
-const NodeTreeRef = ref(null)
-const NodeTreeFormat = ref({key: "primaryKeyId", label: "title", children: "children"})
-const NodeTreeData = ref({})
-
-//导图结构树节点查询
-const queryMappingStructureTree = () => {
-    wbsApi.queryMappingStructureTree({
-        contractId: contractId.value,
-        contractIdRelation: '',
-        parentId: '0',
-        wbsType: 1
-    }).then(({data}) => {
-        NodeTreeData.value = data?.data[0] || {}
-    })
-}
-
-//导图结构展开和收缩被点击
-const expandClick = ({node,data}) => {
-    if (!node.children) {
-        node.expanded = false;
-    }
-    node.isExpand = !node.isExpand;
-}
-
-//鼠标左键单击事件
-const NodeTreeClick = ({node,data}) => {
-    let ifChildren = !!(node.childNodes && node.childNodes.length > 0);
-    if (!ifChildren) {
-        node.loading = true
-        const cid = data?.contractIdRelation
-        const key = data?.primaryKeyId
-        const id = data?.id
-        wbsApi.queryMappingStructureTree({
-            contractId: contractId.value,
-            contractIdRelation: cid,
-            parentId: cid?key:id,
-            wbsType: 1
-        }).then((res) => {
-            const resData = res?.data?.data || []
-            node.childNodes = resData
-            nextTick(() => {
-                if (resData.length > 0) {
-                    node.expanded = true;
-                    node.isExpand = true;
-                } else {
-                    node.expanded = false;
-                    node.expanded = false;
-                }
-                node.loading = false
-            })
-        })
-    } else {
-        node.expanded = true;
-        node.isExpand = true;
-        node.loading = false
-    }
-}
-
-//双击事件
-const isDrawer = ref(false)
-const NodeTreeDblClick = ({node,data}) => {
-    nodeItemInfo.value = node
-    nodeDataInfo.value = data
-    isDrawer.value = true;
-    searchNodeAllTable()
-    queryNodeStatus()
-}
-
-//菜单被点击
-const firstItemModal = ref(false)
-const editNodeModal = ref(false)
-const nodeItemInfo = ref({})
-const nodeDataInfo = ref({})
-const treeExpandKeys = ref([])
-const ElTreeMenuClick = async ({key,node,data}) => {
-    nodeItemInfo.value = node
-    nodeDataInfo.value = data
-    if (key === 'mark' || key === 'cancel_mark') {
-        firstItemModal.value = true
-    } else if (key === 'edit') {
-        const parent = deepClone(node?.parent?.data || {})
-        formEditNodeModel.value = {...deepClone(data), parent: parent}
-        editNodeModal.value = true
-    } else if (key === 'copy') {
-        const parent = deepClone(node?.parent?.data || {})
-        formCopyNodeModel.value = {...deepClone(data), parent: parent}
-        copyNodeTable.value = []
-        copyNodeLoading.value = false
-        treeExpandKeys.value = await NodeExpandKeys()
-        copyNodeModal.value = true
-    } else if (key === 'add') {
-        addTreeNodeId.value = data?.id
-        addTreeNodeOldId.value = data?.oldId
-        addNodeLoading.value = false
-        addNodeModal.value = true
-    } else if (key === 'upload') {
-        const uploadDom = document.getElementById('upload-drawing');
-        let inputDom = uploadDom?.getElementsByTagName('input')[0];
-        inputDom.click()
-    } else if (key === 'del') {
-        delModalClick()
-    } else if (key === 'sort') {
-        let nodes = []
-        const childNodes = node?.parent?.childNodes || []
-        for (let i = 0; i < childNodes.length; i++) {
-            const res = childNodes[i]?.data
-            nodes.push({
-                id: res?.primaryKeyId,
-                title: res?.title
-            })
-        }
-        sortNodeData.value = nodes
-        sortNodeModal.value = true
-    }
-}
-
-const NodeExpandKeys = async (wbsTab = false) => {
-    let newKeysArr = [], node = nodeItemInfo.value || {}
-    await getNodeExpandKeys(node,newKeysArr,wbsTab)
-    const treeKeys = newKeysArr.reverse()
-    await setStore({name: 'wbsTreeExpandKeys', content: treeKeys})
-    return treeKeys
-}
-
-//处理自动展开的节点KEY
-const getNodeExpandKeys = async (node, newKeys, wbsTab = false) => {
-    const tabType = wbsTypeTabKey.value || ''
-    const primaryKeyId = node?.data?.primaryKeyId || ''
-    if (primaryKeyId) {
-        const nodes = node?.parentNodes || []
-        const parent = node?.parent || []
-        newKeys.push(primaryKeyId)
-        if (tabType === 'map' && !wbsTab) {
-            await getNodeExpandKeys(nodes, newKeys,wbsTab)
-        } else if (tabType === 'tree' && !wbsTab) {
-            await getNodeExpandKeys(parent, newKeys,wbsTab)
-        } else if (tabType === 'map' && wbsTab) {
-            await getNodeExpandKeys(parent, newKeys,wbsTab)
-        } else if (tabType === 'tree' && wbsTab) {
-            await getNodeExpandKeys(nodes, newKeys,wbsTab)
-        }
-    }
-}
-
-const NodeTreeMenuClick = async ({key,node,data}) => {
-    nodeItemInfo.value = node
-    nodeDataInfo.value = data
-    if (key === 'mark' || key === 'cancel_mark') {
-        firstItemModal.value = true
-    } else if (key === 'edit') {
-        const parent = deepClone(node?.parentNodes?.data || {})
-        formEditNodeModel.value = {...deepClone(data), parent: parent}
-        editNodeModal.value = true
-    } else if (key === 'copy') {
-        const parent = deepClone(node?.parentNodes?.data || {})
-        formCopyNodeModel.value = {...deepClone(data), parent: parent}
-        copyNodeTable.value = []
-        copyNodeLoading.value = false
-        treeExpandKeys.value = await NodeExpandKeys()
-        copyNodeModal.value = true
-    } else if (key === 'add') {
-        addTreeNodeId.value = data?.id
-        addTreeNodeOldId.value = data?.oldId
-        addNodeLoading.value = false
-        addNodeModal.value = true
-    } else if (key === 'upload') {
-        const uploadDom = document.getElementById('upload-drawing');
-        let inputDom = uploadDom?.getElementsByTagName('input')[0];
-        inputDom.click()
-    } else if (key === 'del') {
-        delModalClick()
-    } else if (key === 'sort') {
-        let nodes = []
-        const childNodes = node?.parentNodes?.childrenNodes || []
-        for (let i = 0; i < childNodes.length; i++) {
-            const res = childNodes[i]?.data
-            nodes.push({
-                id: res?.primaryKeyId,
-                title: res?.title
-            })
-        }
-        sortNodeData.value = nodes
-        sortNodeModal.value = true
-    }
-}
-
-//上传前
-const loadingReactive = ref(null)
-const beforeUpload = () => {
-    loadingReactive.value = window?.$message?.loading('上传中...', {
-        duration: 0
-    })
-    return true
-}
-
-//上传完成
-const uploadFinish = ({event}) => {
-    const res = JSON.parse(event?.target?.response);
-    const info = nodeDataInfo.value;
-    let link = res?.data?.link;
-    wbsApi.saveContractTreeDrawings({
-        fileUrl: link,
-        id: info['drawingsId'],
-        primaryKeyId: info['primaryKeyId']
-    }).then(({data}) => {
-        loadingReactive.value.destroy()
-        loadingReactive.value = null
-        if (data.code === 200) {
-            nodeDataInfo.value['drawingsId'] = data.data
-            nodeDataInfo.value['fileUrl'] = link
-            window?.$message?.success('上传成功')
-        } else {
-            window?.$message?.error(data.msg || '上传失败')
-        }
-    }).catch(erro => {
-        loadingReactive.value.destroy()
-        loadingReactive.value = null
-        window?.$message?.error('请求异常')
-    })
-}
-
-//确认标记为首件
-const firstItemLoading = ref(false)
-const firstItemClick = () => {
-    const info = nodeDataInfo.value;
-    const TabKey = wbsTypeTabKey.value;
-    wbsApi.wbsTreeFirstSave({
-        primaryKeyId: info['primaryKeyId'],
-        saveOrDeleted: !!info['isFirst']?1:0,
-    }).then(({data}) => {
-        if (data.code === 200) {
-            const isFirst = !info['isFirst'];
-            firstItemModal.value = false
-            nodeDataInfo.value['isFirst'] = isFirst
-            //处理菜单状态
-            if (TabKey === 'tree') {
-                HcTreeRef.value.setElTreeMenuMark(data?.data || [], isFirst)
-            } else {
-                setNodeTreeMenuMark(info['primaryKeyId'],data?.data || [], isFirst)
-            }
-            window?.$message?.success('操作成功')
-        } else {
-            window?.$message?.error(data.msg || '操作失败')
-        }
-    })
-}
-
-//设置树菜单的标记数据
-const setNodeTreeMenuMark = (KeyId,keys,isFirst) => {
-    const info = nodeItemInfo.value;
-    setNodeTreeParentNodes(info['parentNodes'],keys,isFirst)
-    setNodeTreeChildNodes(info['childrenNodes'],keys,isFirst)
-}
-
-//设置父级
-const setNodeTreeParentNodes = (parent,keys,isFirst) => {
-    if (keys.indexOf(parent['key']) !== -1) {
-        parent['data']['isFirst'] = isFirst
-    }
-    if (!isObjNull(parent['parentNodes'])) {
-        setNodeTreeParentNodes(parent['parentNodes'],keys,isFirst)
-    }
-}
-
-//设置子级
-const setNodeTreeChildNodes = (child,keys,isFirst) => {
-    if (child.length > 0) {
-        child.forEach(item => {
-            if (keys.indexOf(item['key']) !== -1) {
-                item['data']['isFirst'] = isFirst
-            }
-            if (item['childrenNodes'].length > 0) {
-                setNodeTreeChildNodes(item['childrenNodes'],keys,isFirst)
-            }
-        })
-    }
-}
-
-//编辑节点
-const nodeTypeData = ref([{label: "分项工程", value: 1}, {label: "分部工程", value: 2}])
-const editNodeLoading = ref(false)
-const formEditNodeRef = ref(null)
-const formEditNodeModel = ref({})
-const formEditNodeRules = {
-    title: {
-        required: true,
-        trigger: ["blur", "input"],
-        message: "请输入节点名称"
-    },
-}
-const editNodeClick = () => {
-    editNodeLoading.value = true
-    const {primaryKeyId,title,partitionCode} = formEditNodeModel.value
-    wbsApi.wbsTreeUpdateNode({
-        deptName: title,
-        pKeyId: primaryKeyId,
-        partitionCode: partitionCode||''
-    }).then(({data}) => {
-        editNodeLoading.value = false
-        if (data.code === 200) {
-            window?.$message?.success('修改成功')
-            nodeDataInfo.value['title'] = title || ''
-            nodeDataInfo.value['partitionCode'] = partitionCode || ''
-            editNodeModal.value = false
-        } else {
-            window?.$message?.error(data.msg || '修改失败')
-        }
-    }).catch(erro => {
-        editNodeLoading.value = false
-        window?.$message?.error('请求异常')
-    })
-}
-
-//复制节点
-const copyNodeModal = ref(false)
-
-//复制节点类型tab数据和相关处理
-const copyNodeTabKey = ref('2')
-const copyNodeTab = ref([
-    {key:'1',  name: '单份复制'},
-    {key:'2', name: '多份复制'},
-    {key:'3', name: '复制数据'},
-]);
-const copyNodeTabChange = (value) => {
-    copyNodeTabKey.value = value;
-    copyNodeLoading.value = false
-}
-const copyNodeLoading = ref(false)
-const formCopyNodeModel = ref({})
-const copyNodeTable = ref([])
-
-//树被点击
-const copyNodeElTreeClick = ({data}) => {
-    const {title} = formCopyNodeModel.value;
-    if (!data?.notExsitChild) {
-        copyNodeTable.value.push({
-            title: data?.title || '',
-            nodeName: title || '',
-            primaryKeyId: data?.primaryKeyId || ''
-        })
-    }
-}
-//树被点击
-const copyNodeDataTable = ref([])
-const copyNodeDataElTreeClick = ({data}) => {
-    if (data?.notExsitChild) {
-        copyNodeDataTable.value.push({
-            nodeName: data?.title || '',
-            primaryKeyId: data?.primaryKeyId || ''
-        })
-    }
-}
-
-const formCopyNodeModelRef = ref(null)
-const formCopyNodeModelRules = {
-    title: {
-        required: true,
-        trigger: ["blur", "input"],
-        message: "请输入节点名称"
-    }
-}
-
-//删除选中的节点
-const copyNodeTableDel = (index) => {
-    copyNodeTable.value.splice(index,1)
-}
-const copyNodeDataTableDel = (index) => {
-    copyNodeDataTable.value.splice(index,1)
-}
-
-const copyNodeTableRef = ref(null)
-const copyNodeTableRules = {
-    nodeName: {
-        required: true,
-        trigger: ["blur", "input"],
-        message: "请输入节点名称"
-    }
-}
-
-const copyNodeClick = async () => {
-    const type = copyNodeTabKey.value
-    const form = formCopyNodeModel.value
-    const table = copyNodeTable.value
-    //效验数据
-    let validate = false
-    if (type === '1') {
-        const errors = await formCopyNodeModelRef.value?.validate()
-        validate = !errors
-    } else if (type === '2') {
-        if (table.length > 0) {
-            const errors = await copyNodeTableRef.value?.validate()
-            validate = !errors
-        } else {
-            validate = false
-            window?.$message?.warning('请先在左侧选择要复制到的节点')
-        }
-    }
-    //发起请求
-    if (validate) {
-        copyNodeLoading.value = true
-        const {data} = await wbsApi.copyContractTreeNode({
-            copyType: type,
-            needCopyNodeName: form?.title || '',
-            needCopyPrimaryKeyId: form?.primaryKeyId || '',
-            parentPrimaryKeyId: form?.parent?.primaryKeyId || '',
-            copyBatchToPaths: table
-        })
-        //判断状态
-        if (data.code === 200) {
-            window?.$message?.success('复制成功')
-            copyNodeLoading.value = false
-            copyNodeModal.value = false
-            await NodeExpandKeys()
-            window?.location?.reload()  //刷新页面
-        } else {
-            copyNodeLoading.value = false
-            window?.$message?.error(data.msg || '复制失败')
-        }
-    } else {
-        window?.$message?.warning('请先检查相关表单是否正确填写或节点是否选择')
-    }
-}
-
-//复制数据
-const copyNodeDataLoading = ref(false)
-const copyNodeDataClick = async () => {
-    const form = formCopyNodeModel.value
-    const table = copyNodeDataTable.value
-    if (table.length > 0) {
-        copyNodeDataLoading.value = true
-        const {data} = await wbsApi.copyContractNodeSubmitBusinessData({
-            needCopyPrimaryKeyId: form?.primaryKeyId || '',
-            copyBatchToPaths: table
-        })
-        //判断状态
-        if (data && data.code === 200) {
-            window?.$message?.success('复制成功')
-            copyNodeDataLoading.value = false
-            copyNodeModal.value = false
-            await NodeExpandKeys()
-            window?.location?.reload()  //刷新页面
-        } else {
-            copyNodeDataLoading.value = false
-            window?.$message?.error(data.msg || '复制失败')
-        }
-    } else {
-        window?.$message?.warning('请先选择节点')
-    }
-}
-
-
-//新增节点
-const addNodeModal = ref(false)
-const addTreeNodeId = ref('')
-const addTreeNodeOldId = ref('')
-const addTreeNodeType = ref('1')
-
-//选中的节点
-const allSelectedList = ref([])
-const halfSelectedList = ref([])
-const addTreeNodeCheckChange = (nodes) => {
-    console.log(nodes)
-    let NodesArr = [], halfArr = []
-    //全选数据
-    const keys = nodes.checkedNodes || []
-    for (let i = 0; i < keys.length; i++) {
-        NodesArr.push({
-            nodeName: keys[i].title,
-            primaryKeyId: keys[i].primaryKeyId
-        })
-    }
-    allSelectedList.value = NodesArr
-    //半选数据
-    const halfNodes = nodes.halfCheckedNodes || []
-    for (let i = 0; i < halfNodes.length; i++) {
-        halfArr.push({
-            nodeName: halfNodes[i].title,
-            primaryKeyId: halfNodes[i].primaryKeyId
-        })
-    }
-    halfSelectedList.value = halfArr
-}
-
-//新增节点
-const addNodeLoading = ref(false)
-const addNodeClick = async () => {
-    const keys = allSelectedList.value || []
-    if (keys.length <= 0) {
-        window?.$message?.warning('请先选择节点')
-    } else {
-        //发起请求
-        addNodeLoading.value = true
-        const {data} = await wbsApi.saveContractTreeNode({
-            projectId: projectId.value,
-            contractId: contractId.value,
-            saveType: addTreeNodeType.value,
-            allSelectedList: allSelectedList.value,
-            halfSelectedList: halfSelectedList.value,
-            currentNodePrimaryKeyId: nodeDataInfo.value?.primaryKeyId
-        })
-        //判断状态
-        if (data.code === 200) {
-            window?.$message?.success('新增成功')
-            await NodeExpandKeys()
-            addNodeLoading.value = false
-            addNodeModal.value = false
-            window?.location?.reload()  //刷新页面
-        } else {
-            addNodeLoading.value = false
-            window?.$message?.error(data.msg || '新增失败')
-        }
-    }
-}
-
-//删除确认弹窗
-const delModalClick  = () => {
-    window?.$dialog?.error({
-        title: "确认此操作?",
-        content: "请谨慎考虑后,确认是否需要删除",
-        positiveText: "确认删除",
-        negativeText: "取消",
-        onPositiveClick: () => {
-            const info = nodeDataInfo.value;
-            const TabKey = wbsTypeTabKey.value;
-            wbsApi.removeContractTreeNode({
-                ids: info['primaryKeyId']
-            }).then(({data}) => {
-                if (data.code === 200) {
-                    window?.$message?.success('删除成功')
-                    //处理菜单状态
-                    if (TabKey === 'tree') {
-                        HcTreeRef.value.removeElTreeNode(info['primaryKeyId'])
-                    } else {
-                        removeNodeTreeNode()
-                    }
-                } else {
-                    window?.$message?.error(data.msg || '删除失败')
-                }
-            })
-        }
-    });
-}
-const removeNodeTreeNode = () => {
-    const info = nodeItemInfo.value;
-    const parent = info['parentNodes']
-    const childNodes = parent['childNodes'] || []
-    const childrenNodes = parent['childrenNodes'] || []
-    //删除
-    for (let i = 0; i < childNodes.length; i++) {
-        if (info['key'] === childNodes[i]['primaryKeyId']) {
-            childNodes.splice(i, 1);
-        }
-    }
-    //删除
-    for (let i = 0; i < childrenNodes.length; i++) {
-        if (info['key'] === childrenNodes[i]['key']) {
-            childrenNodes.splice(i, 1);
-        }
-    }
-}
-
-//调整排序
-const sortNodeModal = ref(false)
-const sortNodeLoading = ref(false)
-const sortNodeData = ref([])
-const sortNodeDrag = ref(false)
-//向下
-const downSortClick = (index) => {
-    const indexs = index + 1
-    const data = sortNodeData.value || []
-    if(indexs !== data.length) {
-        const tmp = data.splice(indexs,1);
-        sortNodeData.value.splice(index,0,tmp[0]);
-    } else {
-        window?.$message?.warning('已经处于置底,无法下移')
-    }
-}
-//向上
-const upSortClick = (index) => {
-    const data = sortNodeData.value || []
-    if(index !== 0) {
-        const tmp = data.splice(index - 1,1);
-        sortNodeData.value.splice(index,0,tmp[0]);
-    } else {
-        window?.$message?.warning('已经处于置顶,无法上移')
-    }
-}
-//确认排序
-const sortNodeClick = async () => {
-    const sortList = [];
-    const nodes = sortNodeData.value || []
-    nodes.forEach(item => {
-        sortList.push(item?.id)
-    })
-    //发起请求
-    sortNodeLoading.value = true
-    const { data } = await wbsApi.diySortTreeNode({sortList})
-    //判断状态
-    if (data.code === 200) {
-        window?.$message?.success('保存成功')
-        await NodeExpandKeys()
-        sortNodeLoading.value = false
-        sortNodeModal.value = false
-        window?.location?.reload()  //刷新页面
-    } else {
-        sortNodeLoading.value = false
-        window?.$message?.error(data.msg || '保存失败')
-    }
-}
-
-
-//查看图纸
-const drawingsShow = ref(false);
-const viewsDrawings = () => {
-    drawingsShow.value = true
-}
-const drawingsClose = (res) => {
-    drawingsShow.value = res
-}
-const viewsDrawingsTip = () => {
-    const {primaryKeyId,fileUrl} = nodeDataInfo.value
-    if (!primaryKeyId) {
-        window?.$message?.warning('请先选择树节点')
-    } else if (!fileUrl) {
-        window?.$message?.warning('该节点暂未上传图纸')
-    }
-}
-
-//数据列表
-const ListItemDatas = ref([]);
-
-//获取数据列表
-const searchNodeAllTable = () => {
-    const info = nodeDataInfo.value;
-    const cid = info?.contractIdRelation || ''
-    const key = info?.primaryKeyId || ''
-    const id = info?.id || ''
-    wbsApi.searchNodeAllTable({
-        primaryKeyId: cid?id:key,
-        type: authBtnTabKey.value,
-        projectId: projectId.value,
-        contractId: contractId.value
-    }).then(({data}) => {
-        if (data.code === 200) {
-            let tableData = isArray(data?.data)?data['data']:[]
-            if (tableData.length > 0) {
-                tableData.forEach(item => {
-                    item.name = item.deptName
-                })
-            }
-            ListItemDatas.value = tableData
-        } else {
-            ListItemDatas.value = []
-        }
-    }).catch(() => {
-        ListItemDatas.value = []
-    })
-}
-
-//批量上报
-const reportIds = ref('')
-const reportTaskName = ref('')
-const reportAddition = ref({})
-const showReportModal = ref(false)
-const reportModalClick = () => {
-    const info = nodeDataInfo.value;
-    const rows = ListItemDatas.value;
-    reportIds.value = info['contractIdRelation'] ? info['id'] : info['primaryKeyId']
-    if (rows.length > 0) {
-        reportTaskName.value = rows.length > 1?`${rows[0].name}等${rows.length}个文件`:rows[0].name
-        reportAddition.value = {
-            classify: authBtnTabKey.value,
-            contractIdRelation: info['contractIdRelation'],
-        }
-        showReportModal.value = true
-    } else {
-        window.$message?.warning('暂无相关数据')
-    }
-}
-
-//上报完成
-const showReportFinish = () => {
-    showReportModal.value = false
-    searchNodeAllTable()
-    queryNodeStatus()
-}
-
-//被展开
-const ListItemOffsetTop = (offsetTop) => {
-    if (offsetTop > 0) {
-        const type = wbsTypeTabKey.value || 'map'
-        setTimeout(() => {
-            if (type === 'map') {
-                document.getElementById('hc-drawer-list-item').scrollTop = offsetTop
-            } else if (type === 'tree') {
-                document.getElementById('hc-card-list-item').scrollTop = offsetTop
-            }
-        }, 200)
-    }
-}
-
-//查询状态
-const NodeStatus = ref('1')
-const queryNodeStatus = () => {
-    const info = nodeDataInfo.value;
-    wbsApi.queryNodeStatus({
-        primaryKeyId: info?.contractIdRelation ? info?.id : info?.primaryKeyId,
-        classify: authBtnTabKey.value
-    }).then(({data}) => {
-        //1 未填报,2待上报,3已上报
-        if (data.code === 200) {
-            NodeStatus.value = data?.data || '1'
-        }
-    })
-}
-
-//撤回上报流程
-const abolishOneClick = () => {
-    window?.$dialog?.warning({
-        title: "确认操作",
-        content: "请谨慎操作",
-        positiveText: "确定撤回",
-        negativeText: "取消",
-        onPositiveClick: () => {
-            abolishOneSave()
-        }
-    });
-}
-
-//撤回请求
-const abolishOneSave = () => {
-    const info = nodeDataInfo.value;
-    wbsApi.abolishOne({
-        primaryKeyId:  info?.primaryKeyId || '',
-        classify: authBtnTabKey.value
-    }).then(({data}) => {
-        if (data.code === 200) {
-            window.$message?.success('撤回成功')
-            searchNodeAllTable()
-            queryNodeStatus()
-        } else {
-            window.$message?.error(data.msg || '撤回失败')
-            searchNodeAllTable()
-            queryNodeStatus()
-        }
-    })
-}
-
-//表单变量
-const ListItemRef = ref(null)
-const ListItemsRef = ref(null)
-
-//保存
-const tableFormSaveLoading = ref(false)
-const tableFormSaveClick = () => {
-    let FormData = [];
-    if (isDrawer.value) {
-        FormData = ListItemsRef.value.getFormData()
-    } else {
-        FormData = ListItemRef.value.getFormData()
-    }
-    console.log(FormData)
-    tableFormSaveLoading.value = true
-    wbsApi.saveExcelBussData({
-        dataInfo: {orderList: FormData}
-    }).then(({data}) => {
-        if(data.code === 200) {
-            window?.$message?.success('保存成功')
-            bussPdfsClick()
-            searchNodeAllTable()
-            queryNodeStatus()
-        } else {
-            window?.$message?.warning(data.msg || '保存失败')
-        }
-    }).catch(() => {
-        tableFormSaveLoading.value = false
-    })
-}
-
-//多表预览
-const bussPdfsLoading = ref(false)
-const bussPdfsClick = () => {
-    const info = nodeDataInfo.value;
-    bussPdfsLoading.value = true
-    wbsApi.getBussPdfs({
-        nodeId:  info?.primaryKeyId || '',
-        classify: authBtnTabKey.value,
-        projectId: projectId.value,
-        contractId: contractId.value
-    }).then(({data}) => {
-        tableFormSaveLoading.value = false
-        bussPdfsLoading.value = false
-        if(data.code === 200 && data?.data) {
-            window.open(data?.data,'_blank')
-        } else {
-            window.$message?.warning('暂无PDF')
-        }
-    }).catch(() => {
-        tableFormSaveLoading.value = false
-        bussPdfsLoading.value = false
-    })
-}
-
-
-//左右拖动,改变树形结构宽度
-const leftWidth = ref(382);
-const onmousedown = () => {
-    document.onmousemove = (ve) => {
-        let diffVal = ve.clientX + 2;
-        if(diffVal >= 310 && diffVal <= 900) {
-            leftWidth.value = diffVal;
-        }
-    }
-    document.onmouseup = () => {
-        document.onmousemove = null;
-        document.onmouseup = null;
-    }
-}
-</script>
-
-<style lang="scss" scoped>
-@import "../../styles/data-fill/wbs.scss";
-.hc-add-node-modal-foot-box {
-    position: relative;
-    display: flex;
-    align-items: center;
-    .left-box {
-        position: relative;
-        flex: 1;
-        display: flex;
-        align-items: center;
-    }
-    .right-box {
-        position: relative;
-    }
-}
-
-html.theme-dark {
-    .bg-svg-xml {
-        background-color: initial;
-        background-image: initial;
-    }
-    .hc-layout-box .hc-layout-content-box .hc-card-max-h-box.node-tree .hc-tree-foot-tip-box {
-        border-top: 1px solid #303030;
-    }
-}
-</style>
-
-<style lang="scss">
-.data-fill-wbs-content {
-    position: relative;
-    height: 100%;
-    .n-drawer-container {
-        margin: -20px -24px;
-    }
-    .n-drawer.n-drawer--top-placement {
-        height: auto !important;
-        background-color: initial;
-        pointer-events: none;
-        bottom: 0;
-    }
-    .drawer-data-fill-content-box {
-        position: relative;
-        height: 100%;
-        padding: 24px;
-        .n-card {
-            pointer-events: auto;
-            height: 100%;
-            overflow: auto;
-        }
-        .data-fill-content {
-            position: relative;
-            height: 100%;
-            overflow-y: auto;
-            scroll-behavior: smooth;
-            .data-fill-list-box .data-fill-list-item-content {
-                height: calc(100vh - 470px);
-                .data-fill-table-form-box {
-                    height: 100%;
-                }
-            }
-        }
-        .data-fill-foot {
-            position: relative;
-            text-align: center;
-        }
-    }
-}
-.n-card.hc-card-overflow-box .n-card__content {
-    padding: 24px;
-}
-.n-card.hc-custom-card > .n-card-header {
-    padding: 15px 24px;
-}
-.n-card.hc-custom-card.copy {
-    width: 1200px;
-    max-height: 90vh;
-    overflow: auto;
-    .n-card-header .n-card-header__close {
-        display: none;
-    }
-    &.one {
-        width: 600px;
-    }
-    &.many {
-        width: 1200px;
-        .copy-node-many-box {
-            position: relative;
-            height: 60vh;
-            display: flex;
-            .copy-node-many-tree {
-                position: relative;
-                flex: 1;
-                height: 100%;
-                overflow: auto;
-                padding-right: 20px;
-                border-right: 1px solid #efeff5;
-            }
-            .copy-node-many-table {
-                position: relative;
-                flex: 1;
-                height: 100%;
-                margin-left: 20px;
-                .copy-node-many-table-data {
-                    position: relative;
-                    height: calc(100% - 74px);
-                    margin-bottom: 20px;
-                    overflow: auto;
-                }
-                .copy-node-many-foot-center {
-                    position: relative;
-                    text-align: center;
-                    padding-top: 20px;
-                    border-top: 1px solid #efeff5;
-                }
-            }
-        }
-    }
-}
-.img-preview-box {
-    position: relative;
-    height: 100%;
-    width: 100%;
-    .cu-img-preview.cu-img-preview-dom .cu-img-preview-box {
-        height: calc(100% - 60px);
-    }
-    .cu-img-preview.cu-img-preview-dom .cu-img-preview-tools-box{
-        position: absolute;
-    }
-}
-</style>

+ 45 - 35
yarn.lock

@@ -642,11 +642,16 @@ estree-walker@^2.0.1, estree-walker@^2.0.2:
   resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
   integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
 
-evtd@^0.2.2, evtd@^0.2.3:
+evtd@^0.2.2:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/evtd/-/evtd-0.2.3.tgz#185158d533b4440ee831a0fa0cffde16e8bda504"
   integrity sha512-tmiT1YUVqFjTY+BSBOAskL83xNx41iUfpvKP6Gcd/xMHjg3mnER98jXGXJyKnxCG19uPc6EhZiUC+MUyvoqCtw==
 
+evtd@^0.2.4:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/evtd/-/evtd-0.2.4.tgz#0aac39ba44d6926e6668948ac27618e0795b9d07"
+  integrity sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==
+
 fast-glob@^3.2.11:
   version "3.2.11"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
@@ -906,10 +911,10 @@ ms@2.1.2:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
-naive-ui@^2.32.0:
-  version "2.32.1"
-  resolved "https://registry.yarnpkg.com/naive-ui/-/naive-ui-2.32.1.tgz#4559b0a4d31500f817b45a8465d14fd9932e8119"
-  integrity sha512-4zRHAn9d273qKWHs2ZQ+9xZmGTfxhL/CI1tj8bJkE313zjwyGRaiPbCeh3hQgrNdZWiRXdqmA442EVKHizGWeg==
+naive-ui@^2.32.2:
+  version "2.32.2"
+  resolved "https://registry.yarnpkg.com/naive-ui/-/naive-ui-2.32.2.tgz#40f593df0b86b36f6af5635d9299417d45d6d96a"
+  integrity sha512-vTNuZ8LBlfo/cdiv4S8o6Cg5g7p9V9cR5rK+Fag2cplOnng5twTILD3sBaCqw3k/BV1331Xdk26ml8Me8QJ7iA==
   dependencies:
     "@css-render/plugin-bem" "^0.15.10"
     "@css-render/vue3-ssr" "^0.15.10"
@@ -919,11 +924,11 @@ naive-ui@^2.32.0:
     css-render "^0.15.10"
     date-fns "^2.28.0"
     date-fns-tz "^1.3.3"
-    evtd "^0.2.3"
+    evtd "^0.2.4"
     highlight.js "^11.5.0"
     lodash "^4.17.21"
     lodash-es "^4.17.21"
-    seemly "^0.3.4"
+    seemly "^0.3.6"
     treemate "^0.3.11"
     vdirs "^0.1.8"
     vooks "^0.2.12"
@@ -994,10 +999,10 @@ pify@^2.3.0:
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
   integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
 
-pinia@^2.0.18:
-  version "2.0.18"
-  resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.18.tgz#b29ed34bcb4c032b7da7c24c10db18b88d9254d8"
-  integrity sha512-I5MW05UVX6a5Djka136oH3VzYFiZUgeOApBwFjMx6pL91eHtGVlE3adjNUKLgtwGnrxiBRuJ8+4R3LKJKwnyZg==
+pinia@^2.0.19:
+  version "2.0.19"
+  resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.19.tgz#1ee8b521ab0fdac16b727151f7761674a04875e1"
+  integrity sha512-Q/UQrmFLDMdlCkLfM5rGw1Ug0A7dy0G7NtBusMQSK+TNjf3CV/pO0RqblNIfuurWl42byTjM6HIemCWOfo8KXA==
   dependencies:
     "@vue/devtools-api" "^6.2.1"
     vue-demi "*"
@@ -1139,11 +1144,16 @@ scule@^0.3.2:
   resolved "https://registry.yarnpkg.com/scule/-/scule-0.3.2.tgz#472445cecd8357165a94a067f78cee40e700b596"
   integrity sha512-zIvPdjOH8fv8CgrPT5eqtxHQXmPNnV/vHJYffZhE43KZkvULvpCTvOt1HPlFaCZx287INL9qaqrZg34e8NgI4g==
 
-seemly@^0.3.1, seemly@^0.3.4:
+seemly@^0.3.1:
   version "0.3.5"
   resolved "https://registry.yarnpkg.com/seemly/-/seemly-0.3.5.tgz#7f680d1b8f9ecdfbd6358d62c72b9af10a485b3d"
   integrity sha512-Z0QAytAEpkAeWbQZBQ+zb+9YYn86+AFBdWA4y7FwFtJ+ZcIO55QEVdT0sJ0DxuX8FWD4UTuwaftz/UX+MmHlhw==
 
+seemly@^0.3.6:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/seemly/-/seemly-0.3.6.tgz#7ef97e8083dea00804965e2662f572a5df9cb18e"
+  integrity sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==
+
 sortablejs@1.14.0:
   version "1.14.0"
   resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
@@ -1228,10 +1238,10 @@ tslib@2.3.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
   integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
 
-unimport@^0.6.5:
-  version "0.6.5"
-  resolved "https://registry.yarnpkg.com/unimport/-/unimport-0.6.5.tgz#f50fcfcba6ee4228d649670b4bed2f63cb03ef96"
-  integrity sha512-B8x6+GiYUzDphN6Iaoshu99mUo8n7QCq13QTY2Z8saj1QBmGKXf+vJJlptfL3MdOmzxpve2Ikx91UZ4Qoz4dVQ==
+unimport@^0.6.7:
+  version "0.6.7"
+  resolved "https://registry.yarnpkg.com/unimport/-/unimport-0.6.7.tgz#7b2e2063b88ee8ea363d2d751f7c82a6960beac3"
+  integrity sha512-EMoVqDjswHkU+nD098QYHXH7Mkw7KwGDQAyeRF2lgairJnuO+wpkhIcmCqrD1OPJmsjkTbJ2tW6Ap8St0PuWZA==
   dependencies:
     "@rollup/pluginutils" "^4.2.1"
     escape-string-regexp "^5.0.0"
@@ -1242,19 +1252,19 @@ unimport@^0.6.5:
     pathe "^0.3.3"
     scule "^0.3.2"
     strip-literal "^0.4.0"
-    unplugin "^0.8.1"
+    unplugin "^0.9.0"
 
-unplugin-auto-import@^0.11.1:
-  version "0.11.1"
-  resolved "https://registry.yarnpkg.com/unplugin-auto-import/-/unplugin-auto-import-0.11.1.tgz#c77b0e29890ea5efd013a6f893d436765462e9f0"
-  integrity sha512-3KMbjc3Sv2h1osgQ16uKmD2XHb8MLK3Lj5Twzo3x2vDexYdNKjVljLZUX+eT5cvh2f1VzJR77dkzDhdEi9+1uw==
+unplugin-auto-import@^0.11.2:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/unplugin-auto-import/-/unplugin-auto-import-0.11.2.tgz#9b541f65e2800f298b13d73bca8e4eb333f94de7"
+  integrity sha512-1+VwBfn9dtiYv9SQLKP1AvZolUbK9xTVeAT+iOcEk4EHSFUlmIqBVLEKI76cifSQTLOJ3rZyPrEgptf3SZNLlQ==
   dependencies:
     "@antfu/utils" "^0.5.2"
     "@rollup/pluginutils" "^4.2.1"
     local-pkg "^0.4.2"
     magic-string "^0.26.2"
-    unimport "^0.6.5"
-    unplugin "^0.9.0"
+    unimport "^0.6.7"
+    unplugin "^0.9.3"
 
 unplugin-vue-components@^0.22.4:
   version "0.22.4"
@@ -1272,20 +1282,20 @@ unplugin-vue-components@^0.22.4:
     resolve "^1.22.1"
     unplugin "^0.9.0"
 
-unplugin@^0.8.1:
-  version "0.8.1"
-  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.8.1.tgz#4517b6a8ec3d944e838f9c346921d9777cd159e1"
-  integrity sha512-o7rUZoPLG1fH4LKinWgb77gDtTE6mw/iry0Pq0Z5UPvZ9+HZ1/4+7fic7t58s8/CGkPrDpGq+RltO+DmswcR4g==
+unplugin@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.9.0.tgz#ebad287d61aa1b1f16de60feea74e8dd12224819"
+  integrity sha512-6o7q8Y9yxdPi5yCPmRuFfeNnVzGumRNZSK6hIkvZ6hd0cfigVdm0qBx/GgQ/NEjs54eUV1qTjvMYKRs9yh3rzw==
   dependencies:
     acorn "^8.8.0"
     chokidar "^3.5.3"
     webpack-sources "^3.2.3"
     webpack-virtual-modules "^0.4.4"
 
-unplugin@^0.9.0:
-  version "0.9.0"
-  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.9.0.tgz#ebad287d61aa1b1f16de60feea74e8dd12224819"
-  integrity sha512-6o7q8Y9yxdPi5yCPmRuFfeNnVzGumRNZSK6hIkvZ6hd0cfigVdm0qBx/GgQ/NEjs54eUV1qTjvMYKRs9yh3rzw==
+unplugin@^0.9.3:
+  version "0.9.3"
+  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.9.3.tgz#baeec1e3d70d18f693597ef3fcc441779e164e7f"
+  integrity sha512-GWXxizZG+tobNs8fuGTCeilerkkfZTZax2iivuE4pxLaF9wTnPJHOq8tbLKDb5ohVb+2BXNjrU9xx59yWTUnuw==
   dependencies:
     acorn "^8.8.0"
     chokidar "^3.5.3"
@@ -1355,10 +1365,10 @@ vue-router@^4.1.3:
   dependencies:
     "@vue/devtools-api" "^6.1.4"
 
-vue-utils-plus@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/vue-utils-plus/-/vue-utils-plus-1.0.1.tgz#bc43c72232145d258e53a7096623775a97a9efc9"
-  integrity sha512-BKTSZhUFfefSB9sEN/HtuBW5/wDbVRigV55ZBMBXXN6m8iVZQjNX4mcDX/4ykalSDE3O3A3Lq/cdfhUl9t6HeA==
+vue-utils-plus@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/vue-utils-plus/-/vue-utils-plus-1.0.2.tgz#8069c00c30a0e5cb83d7277c6975843814d8d452"
+  integrity sha512-Fg/2U0ysqpE2flCZ1Vmg5pRSnkmlmakmkEJ+w7ULDalme7SvoPp2sbe9UukZRL5eAm6ZqpKprrwLaj81nEyiRQ==
 
 vue@^3.2.37:
   version "3.2.37"