iZaiZaiA 3 lat temu
rodzic
commit
92ad40869d

+ 5 - 5
package.json

@@ -27,11 +27,11 @@
         "@vue/compiler-sfc": "^3.2.37",
         "autoprefixer": "^10.4.7",
         "nprogress": "^0.2.0",
-        "postcss": "^8.4.14",
-        "sass": "^1.54.1",
-        "tailwindcss": "^3.1.6",
-        "unplugin-auto-import": "^0.10.2",
-        "unplugin-vue-components": "^0.21.2",
+        "postcss": "^8.4.16",
+        "sass": "^1.54.3",
+        "tailwindcss": "^3.1.8",
+        "unplugin-auto-import": "^0.11.1",
+        "unplugin-vue-components": "^0.22.3",
         "vfonts": "^0.0.3",
         "vite": "^3.0.3",
         "vooks": "^0.2.12",

+ 16 - 0
src/api/modules/userInfo/index.js

@@ -73,4 +73,20 @@ export default {
             data: form
         });
     },
+    //回收站分页
+    async queryRecycleBinList(form) {
+        return request({
+            url: '/api/blade-business/recycleBin/list',
+            method: 'get',
+            params: form
+        });
+    },
+    //回收站恢复
+    async recycleBinRegain(form) {
+        return request({
+            url: '/api/blade-business/recycleBin/regain',
+            method: 'post',
+            data: form
+        });
+    },
 }

+ 0 - 2
src/components/data-fill/ListItem.vue

@@ -478,8 +478,6 @@ const CTDModal = ref(false)
 </script>
 
 <style lang="scss" scoped>
-@import "../../styles/EUDC/index";
-
 .data-fill-list-box {
     position: relative;
     .item-title {

+ 7 - 7
src/components/data-fill/nodeTree/children.vue

@@ -5,13 +5,13 @@
                 <div class="cu-tree-node-label-text">
                     <div class="cu-tree-node-label-name" :id="`node-tree-${item.key}`" @click="nodeLabelClick(item)" @dblclick="nodeLabelDblClick(item)" @contextmenu="nodeLabelContextMenu($event,item)">
                         <template v-if="TreeIsColor">
-                            <n-button color="#0081ff" text-color="#ffffff" size="large" :loading="item.loading" v-if="item?.colorStatus === 2"> {{item.label}} </n-button>
-                            <n-button color="#f37b1d" text-color="#ffffff" size="large" :loading="item.loading" v-else-if="item?.colorStatus === 3"> {{item.label}} </n-button>
-                            <n-button color="#32B16C" text-color="#ffffff" size="large" :loading="item.loading" v-else-if="item?.colorStatus === 4"> {{item.label}} </n-button>
-                            <n-button color="#cccccc" text-color="#111111" size="large" :loading="item.loading" v-else>{{item.label}}</n-button>
+                            <el-button hc-btn color="#0081ff" :loading="item.loading" v-if="item?.colorStatus === 2">{{item.label}}</el-button>
+                            <el-button hc-btn color="#f37b1d" :loading="item.loading" v-else-if="item?.colorStatus === 3">{{item.label}}</el-button>
+                            <el-button type="primary" hc-btn :loading="item.loading" v-else-if="item?.colorStatus === 4">{{item.label}}</el-button>
+                            <el-button type="info" hc-btn :loading="item.loading" v-else>{{item.label}}</el-button>
                         </template>
-                        <template v-else>
-                            <n-button color="#cccccc" text-color="#111111" size="large" :loading="item.loading">{{item.label}}</n-button>
+                        <template v-if="!TreeIsColor">
+                            <el-button type="info" hc-btn :loading="item.loading">{{item.label}}</el-button>
                         </template>
                     </div>
                     <span class="cu-tree-node-label-btn" :class="[ifExpanded(item)?'expanded':'']" v-if="isExpanded(item)" @click="expandedClick(item)"/>
@@ -22,7 +22,7 @@
         </div>
     </div>
     <!--菜单-->
-    <n-dropdown placement="bottom" trigger="manual" :x="x" :y="y" size="huge" :options="menusData" :show="showDropdown" @clickoutside="onClickoutside" @select="handleMenuSelect"/>
+    <!--n-dropdown placement="bottom" trigger="manual" :x="x" :y="y" size="huge" :options="menusData" :show="showDropdown" @clickoutside="onClickoutside" @select="handleMenuSelect"/-->
 </template>
 
 <script setup>

+ 2 - 4
src/components/data-fill/nodeTree/index.vue

@@ -5,9 +5,7 @@
                 <div class="cu-tree-node-label">
                     <div class="cu-tree-node-label-text">
                         <div class="cu-tree-node-label-name" :id="`node-tree-${nodes.key}`">
-                            <n-button color="#cccccc" text-color="#111111" size="large" :loading="nodes.loading">
-                                {{nodes.label}}
-                            </n-button>
+                            <el-button type="primary" hc-btn :loading="nodes.loading">{{nodes.label}}</el-button>
                         </div>
                     </div>
                 </div>
@@ -217,6 +215,6 @@ const poverMenuClick = (item) => {
 }
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 @import "style";
 </style>

+ 4 - 10
src/components/data-fill/nodeTree/style.scss

@@ -1,9 +1,7 @@
 .cu-tree-node-container {
     position: relative;
     width: 100%;
-    height: calc(100% - 52px);
-    background-color: #EEEEEE;
-    background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+);
+    height: 100%;
     overflow: auto;
     .cu-tree-node-box {
         display: table;
@@ -79,6 +77,9 @@
                             border-radius: inherit;
                             transform: scale(1);
                         }
+                        .el-button[hc-btn] {
+                            box-shadow: 4px 4px 8px 0 rgb(54 92 167 / 15%);
+                        }
                     }
                 }
             }
@@ -207,10 +208,3 @@
         }
     }
 }
-
-html.theme-dark {
-    .cu-tree-node-container {
-        background-color: initial;
-        background-image: initial;
-    }
-}

+ 7 - 5
src/global/components/hc-card/index.vue

@@ -83,6 +83,7 @@ const isSlotSearchBar = ref(!!slots.search);
     box-shadow: -2px 0 10px 0 rgba(32,37,50,0.03), 0 10px 21px 20px rgba(32,37,50,0.03);
     border: 0;
     .el-card__header {
+        height: 61px;
         padding: 14px 24px;
         border-bottom: 1px solid #e9e9e9;
     }
@@ -100,6 +101,7 @@ const isSlotSearchBar = ref(!!slots.search);
     }
     .el-card__body {
         position: relative;
+        height: 100%;
         .hc-card-search-bar {
             position: relative;
             margin-bottom: 20px;
@@ -107,9 +109,6 @@ const isSlotSearchBar = ref(!!slots.search);
         .hc-card-main-box {
             position: relative;
             height: 100%;
-            &.is-action {
-                height: calc(100% - 57px);
-            }
         }
         .hc-card-action-box {
             position: absolute;
@@ -123,10 +122,13 @@ const isSlotSearchBar = ref(!!slots.search);
         }
     }
     &.is-header .el-card__body {
-        height: calc(100% - 57px);
+        height: calc(100% - 61px);
+    }
+    &.is-header .el-card__body .hc-card-main-box:not(.is-action) {
+        height: 100%;
     }
     &.is-action-df .el-card__body .hc-card-main-box.is-action {
-        height: calc(100% - 124px);
+        height: calc(100% - 63.5px);
     }
     &.is-action-lg .el-card__body .hc-card-main-box.is-action {
         height: calc(100% - 80px);

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

@@ -217,3 +217,51 @@
         }
     }
 }
+
+//滚动条
+.el-scrollbar {
+    scroll-behavior: smooth;
+    .el-scrollbar__wrap--hidden-default {
+        scroll-behavior: smooth;
+    }
+}
+
+//弹窗
+.el-overlay-dialog {
+    .el-dialog.hc-modal-border {
+        .el-dialog__header {
+            padding-bottom: var(--el-dialog-padding-primary);
+            border-bottom: 1px solid #EEEEEE;
+            margin-right: 0;
+        }
+        .el-dialog__footer {
+            border-top: 1px solid #EEEEEE;
+            padding-top: var(--el-dialog-padding-primary);
+        }
+    }
+}
+
+//上传
+.el-upload-list.el-upload-list--picture-card {
+    --el-upload-list-picture-card-size: 88px;
+    .el-upload--picture-card {
+        --el-upload-picture-card-size: 88px;
+        .hc-upload-icon {
+            font-size: 39px;
+        }
+    }
+}
+
+//头像
+.el-avatar {
+    color: initial !important;
+    background: initial !important;
+}
+
+//表格
+.hc-table-ref-box {
+    position: relative;
+    display: inline-grid;
+    width: 100%;
+}
+

+ 0 - 21
src/styles/app/main.scss

@@ -199,27 +199,6 @@ html, body, #app {
     }
 }
 
-.page-top-btn {
-    position: fixed;
-    z-index: 10;
-    bottom: 30px;
-    left: 24px;
-    width: 44px;
-    height: 44px;
-    font-size: 20px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    border-radius: 50%;
-    background-color: white;
-    color: #333639;
-    box-shadow: rgba(0, 0, 0, .12) 0 2px 8px 0;
-    cursor: pointer;
-    i {
-
-    }
-}
-
 .pover-menu-list {
     position: relative;
     .list-item {

+ 39 - 4
src/styles/data-fill/wbs.scss

@@ -121,6 +121,45 @@
         }
     }
 }
+
+.hc-tree-mp-tip-box {
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .dot-view {
+        position: relative;
+        display: flex;
+        align-items: center;
+        &:before {
+            position: relative;
+            left: 0;
+            content: "";
+            width: 15px;
+            height: 15px;
+            background-color: inherit;
+            border-radius: 25px;
+            margin-right: 6px;
+        }
+        &.green:before {
+            background-color: #1ECC95;
+        }
+        &.black:before {
+            background-color: #111111;
+        }
+        &.orange:before {
+            background-color: #f37b1d;
+        }
+        &.blue:before {
+            background-color: #0081ff;
+        }
+        & + .dot-view {
+            margin-left: 80px;
+        }
+    }
+}
+
+
 .hc-card-header {
     position: relative;
     font-size: initial;
@@ -129,10 +168,6 @@
 .upload-drawing {
     display: none;
 }
-.bg-svg-xml {
-    background-color: #EEEEEE;
-    background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+);
-}
 
 .sort-node-body-box.list-group {
     position: relative;

+ 54 - 132
src/styles/other/order-service.scss

@@ -56,77 +56,30 @@
                         display: flex;
                         align-items: center;
                         margin-top: 24px;
-                        .left-box {
+                        .icon-box {
                             position: relative;
-                            flex: 1;
-                            .input-box {
-                                display: flex;
-                                position: relative;
-                                align-items: center;
-                                background: #F4F4F4;
-                                border-radius: 40px;
-                                padding: 4px 15px 4px 4px;
-                                transition: width .3s cubic-bezier(0.4, 0, 0.2, 1);
-                                .input-icon-box {
-                                    position: relative;
-                                    width: 30px;
-                                    height: 30px;
-                                    font-size: 18px;
-                                    display: flex;
-                                    justify-content: center;
-                                    align-items: center;
-                                    background: var(--hc-primary);
-                                    color: white;
-                                    border-radius: 50px;
-                                    flex: none;
-                                }
-                                .text-input-box {
-                                    position: relative;
-                                    flex: auto;
-                                }
-                                &.hidden {
-                                    width: 38px;
-                                    .text-input-box {
-                                        display: none;
-                                    }
-                                }
-                            }
-                        }
-                        .right-box {
-                            flex: 2;
                             height: 38px;
-                            position: relative;
                             display: flex;
+                            justify-content: center;
                             align-items: center;
-                            justify-content: flex-end;
-                            .icon-box {
-                                position: relative;
-                                height: 38px;
-                                display: flex;
-                                justify-content: center;
-                                align-items: center;
-                                i {
-                                    font-size: 20px;
-                                    color: #5e5e5e;
-                                    cursor: pointer;
-                                    transition: color 0.3s;
-                                    &:hover {
-                                        color: #282828;
-                                    }
-                                }
-                                .badge {
-                                    font-size: 11px;
-                                    align-items: flex-start;
-                                    display: flex;
-                                    height: 38px;
-                                    margin-left: 5px;
-                                    color: #888888;
-                                }
+                            font-size: 20px;
+                            cursor: pointer;
+                            color: #838791;
+                            transition: color 0.3s;
+                            .badge {
+                                margin-left: 5px;
+                                font-size: 13px;
+                            }
+                            &.active {
+                                color: var(--el-color-primary);
                             }
-                            .icon-box + .icon-box {
-                                margin-left: 30px;
+                            &:hover {
+                                color: #282828;
                             }
                         }
+                        .icon-box + .icon-box {
+                            margin-left: 30px;
+                        }
                     }
                 }
                 .code-status-box {
@@ -142,41 +95,62 @@
                 }
                 .collapse-comment-box {
                     position: relative;
-                    background-color: #fafafc;
-                    padding: 20px 24px;
-                    border-bottom-left-radius: 3px;
-                    border-bottom-right-radius: 3px;
-                    .comment-btn-box {
+                    margin-top: 20px;
+                    .comment-reply-content-box {
                         position: relative;
                         display: flex;
-                        align-items: center;
-                        margin-top: 15px;
-                        padding-bottom: 24px;
+                        align-items: flex-end;
+                        width: 600px;
+                        margin-bottom: 20px;
                     }
                     .user-comment-info-box {
                         position: relative;
                         display: flex;
-                        border-top: 1px solid #e6e9f1;
-                        padding: 24px 20px;
+                        padding: 16px 0;
                         .user-comment-box {
                             position: relative;
                             margin-left: 15px;
                             flex: auto;
                             .user-info-box {
                                 position: relative;
+                                .user-name {
+                                    font-size: 16px;
+                                    margin-right: 10px;
+                                    color: var(--el-color-primary);
+                                }
+                                .create-time {
+                                    color: #838791;
+                                    font-size: 12px;
+                                }
                             }
                             .user-comment-content-box {
                                 position: relative;
-                                margin: 12px 0;
+                                margin-top: 5px;
+                                color: #50545E;
+                                font-size: 14px;
                             }
                         }
-                        &:last-child {
-                            padding-bottom: 0;
-                        }
                     }
                 }
             }
         }
+        .page-top-btn {
+            position: absolute;
+            z-index: 10;
+            bottom: 8px;
+            right: -20px;
+            width: 40px;
+            height: 40px;
+            font-size: 22px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            border-radius: 50%;
+            background-color: white;
+            color: #333639;
+            cursor: pointer;
+            box-shadow: rgba(0, 0, 0, .12) 0 2px 8px 0;
+        }
     }
     .order-service-data {
         position: relative;
@@ -263,62 +237,10 @@
     font-size: 126px;
     text-align: center;
     position: relative;
+    color: var(--el-color-primary);
 }
 .tip-modal-text-box {
     position: relative;
     font-size: 28px;
-}
-.tip-modal-btn-box {
-    margin-top: 3rem;
-    position: relative;
-    display: flex;
-    align-items: center;
-    .btn-box {
-        position: relative;
-        flex: 1;
-    }
-    .btn-box + .btn-box {
-        text-align: right;
-    }
-}
-
-html.theme-dark {
-    .hc-order-service {
-        background-color: inherit;
-    }
-    .hc-order-service .order-service-data {
-        background-color: var(--hc-bg-color);
-        border-left: 1px solid #303030;
-        border-top: 1px solid #303030;
-    }
-    .hc-order-service .order-service-data .horizontal-drag-line {
-        background-color: #303030;
-    }
-    .hc-order-service .order-service-content {
-        &::-webkit-scrollbar-track-piece {
-            background-color: #000000;
-        }
-        &::-webkit-scrollbar-thumb {
-            background-color: #444444;
-        }
-    }
-    .hc-order-service .order-service-content .content-box .comment-card-box {
-        background: var(--hc-bg-color);
-        border: 1px solid #303030;
-    }
-    .hc-order-service .order-service-content .content-box .comment-card-box .card-content-box .foot-tools-box .left-box .input-box {
-        background: #262629;
-    }
-    .hc-order-service .order-service-content .content-box .comment-card-box .collapse-comment-box {
-        background-color: #1c1c1f;
-    }
-    .hc-order-service .order-service-content .content-box .comment-card-box .collapse-comment-box .user-comment-info-box {
-        border-top: 1px solid #303030;
-    }
-    .order-service-content .content-box .image_desc .el-image.el-image-box {
-        border: 1px solid #303030;
-    }
-    .foot-btn-box {
-        border-top: 1px solid #303030;
-    }
+    color: #1A1A1A;
 }

+ 9 - 252
src/views/data-fill/wbs.vue

@@ -1,262 +1,19 @@
 <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">
+    <HcCard title="您好" :scrollbar="false" v-if="wbsTypeTabKey === 'map'">
+        <template #extra>
+            <HcNewSwitch :datas="wbsTypeTab" :keys="wbsTypeTabKey" @change="wbsTypeTabChange"/>
+        </template>
+        <NodeTree ref="NodeTreeRef" :data="NodeTreeData" :format="NodeTreeFormat" :autoExpandKeys="TreeAutoExpandKeys" :menus="ElTreeMenu" :isMark="TreeMark" isColor
+                  :accordion='NodeTreeAccordion' @nodeClick="NodeTreeClick" @expand="expandClick" @nodeDblClick="NodeTreeDblClick" @menuClick="NodeTreeMenuClick"/>
+        <template #action>
+            <div class="hc-tree-mp-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 :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop"/>
-                    </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 :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop"/>
-                            </div>
-                            <template #action>
-                                <div class="data-fill-foot">
-                                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save">
-                                        <template #trigger>
-                                            <n-button type="primary" size="large" class="px-8">保存</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">预览</n-button>
-                                        </template>
-                                        <span>{{btn_preview?.textInfo}}</span>
-                                    </n-popover>
-                                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
-                                        <template #trigger>
-                                            <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="reportModalClick">上报</n-button>
-                                        </template>
-                                        <span>{{btn_report?.textInfo}}</span>
-                                    </n-popover>
-                                    <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'">
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save">
-                    <template #trigger>
-                        <n-button type="primary" size="large" class="px-8">保存</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">预览</n-button>
-                    </template>
-                    <span>{{btn_preview?.textInfo}}</span>
-                </n-popover>
-                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
-                    <template #trigger>
-                        <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="reportModalClick">上报</n-button>
-                    </template>
-                    <span>{{btn_report?.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">
-            <n-form-item label="节点名称" path="title">
-                <n-input v-model:value="formCopyNodeModel.title" placeholder="请输入节点名称"/>
-            </n-form-item>
-        </n-form>
-        <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>
-        <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>
-    </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" @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 :show="showReportModal" title="上报审批" @hide="showReportModal= false"/>
-
-    <!--上传图纸-->
-    <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>
+    </HcCard>
 
 </template>
 

+ 1225 - 0
src/views/data-fill/wbs_bak.vue

@@ -0,0 +1,1225 @@
+<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 :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop"/>
+                    </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 :datas="ListItemDatas" :projectId="projectId" :contractId="contractId" :classify="authBtnTabKey" @offsetTop="ListItemOffsetTop"/>
+                            </div>
+                            <template #action>
+                                <div class="data-fill-foot">
+                                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save">
+                                        <template #trigger>
+                                            <n-button type="primary" size="large" class="px-8">保存</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">预览</n-button>
+                                        </template>
+                                        <span>{{btn_preview?.textInfo}}</span>
+                                    </n-popover>
+                                    <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
+                                        <template #trigger>
+                                            <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="reportModalClick">上报</n-button>
+                                        </template>
+                                        <span>{{btn_report?.textInfo}}</span>
+                                    </n-popover>
+                                    <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'">
+                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_save?.textInfo" v-if="btn_save">
+                    <template #trigger>
+                        <n-button type="primary" size="large" class="px-8">保存</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">预览</n-button>
+                    </template>
+                    <span>{{btn_preview?.textInfo}}</span>
+                </n-popover>
+                <n-popover trigger="hover" :disabled="!bubbleVal || !btn_report?.textInfo" v-if="btn_report">
+                    <template #trigger>
+                        <n-button type="primary" strong secondary class="px-8 ml-6" size="large" @click="reportModalClick">上报</n-button>
+                    </template>
+                    <span>{{btn_report?.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">
+            <n-form-item label="节点名称" path="title">
+                <n-input v-model:value="formCopyNodeModel.title" placeholder="请输入节点名称"/>
+            </n-form-item>
+        </n-form>
+        <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>
+        <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>
+    </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" @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 :show="showReportModal" title="上报审批" @hide="showReportModal= false"/>
+
+    <!--上传图纸-->
+    <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 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()
+}
+
+//结构类型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 = [];
+    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"})
+    }
+    ElTreeMenu.value = newArr
+}
+
+//树的配置
+const HcTreeRef = ref(null)
+const wbsElTreeProps = {label: 'title', children: 'children', isLeaf: 'exsitChild'}
+const treeParams = ref({contractId: contractId.value})
+
+//树被点击
+const wbsElTreeClick = ({node,data}) => {
+    nodeItemInfo.value = node
+    nodeDataInfo.value = data
+    searchNodeAllTable()
+}
+
+//导图结构数据
+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()
+}
+
+//菜单被点击
+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: '多份复制'}
+]);
+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?.exsitChild) {
+        copyNodeTable.value.push({
+            title: data?.title || '',
+            nodeName: 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 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 addNodeModal = ref(false)
+const addTreeNodeId = ref('')
+const addTreeNodeOldId = ref('')
+const addTreeNodeType = ref('1')
+
+//选中的节点
+const allSelectedList = ref([])
+const halfSelectedList = ref([])
+const addTreeNodeCheckChange = (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 || '删除失败')
+                }
+            }).catch(erro => {
+                window?.$message?.error('请求异常')
+            })
+        }
+    });
+}
+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
+    }).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(erro => {
+        ListItemDatas.value = []
+    })
+}
+
+//批量上报
+const showReportModal = ref(false)
+const reportModalClick = () => {
+    showReportModal.value = true
+}
+
+//被展开
+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 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>

+ 220 - 237
src/views/other/order-service.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="hc-order-service">
         <div class="order-service-content">
-            <el-scrollbar>
+            <el-scrollbar ref="scrollbarRef">
                 <div class="content-box">
                     <div class="comment-card-box" v-for="(item,index) in orderDataList" :key="item.id">
                         <div class="user-avatar-box">
@@ -19,55 +19,45 @@
                                 </div>
                             </div>
                             <div class="foot-tools-box">
-                                <div class="left-box">
-                                    <div class="input-box" :class="item['expandedName']?'hidden':''">
-                                        <div class="input-icon-box">
-                                            <i class="hcicon-bianjimian"/>
-                                        </div>
-                                        <!--div class="text-input-box">
-                                            <n-input type="text" v-model:value="item['replyContent']" size="small" placeholder="我也说一句" @keyup="commentKeyUp($event,item)"/>
-                                        </div-->
-                                    </div>
+                                <div class="icon-box" :class="item['commentsNumber'] >= 1 ? 'active' : ''" @click="commentExpanded(item)">
+                                    <HcIcon name="chat" class="icon"/>
+                                    <span class="badge" v-if="item['commentsNumber'] >= 1">{{item['commentsNumber']}}</span>
                                 </div>
-                                <div class="right-box">
-                                    <div class="icon-box" @click="commentExpanded(item)">
-                                        <HcIcon name="chat"/>
-                                        <div class="badge" v-if="item['commentsNumber'] >= 1">{{item['commentsNumber']}}</div>
-                                    </div>
-                                    <div class="icon-box" :data-index="item['expandedName']" @click="likeClick(item)">
-                                        <HcIcon name="thumb_up"/>
-                                        <div class="badge" v-if="item['goodNumber'] >= 1">{{item['goodNumber']}}</div>
-                                    </div>
+                                <div class="icon-box" :class="item['currentUserGood'] ? 'active' : ''" :data-index="item['expandedName']" @click="likeClick(item)">
+                                    <HcIcon name="thumb_up" class="icon"/>
+                                    <span class="badge" v-if="item['goodNumber'] >= 1">{{item['goodNumber']}}</span>
                                 </div>
                             </div>
+                            <el-collapse class="hc-collapse-box" v-model="item['expandedName']" accordion>
+                                <el-collapse-item title="" :name="`commentList-${item['id']}`">
+                                    <div class="collapse-comment-box">
+                                        <div class="comment-reply-content-box">
+                                            <el-input autosize type="textarea" v-model="item['replyContent']" placeholder="我也说一句"/>
+                                            <el-button type="primary" hc-btn @click="saveCommentClick(item)">评论</el-button>
+                                        </div>
+                                        <div class="user-comment-info-box" v-for="items in item['expandedCommentList']" :key="items.id">
+                                            <el-avatar :size="50" :src="items.avatar || avatarPng" />
+                                            <div class="user-comment-box">
+                                                <div class="user-info-box">
+                                                    <span class="user-name">{{items['userName']||'用户名异常'}}</span>
+                                                    <span class="create-time">{{items['createTime']}}</span>
+                                                </div>
+                                                <div class="user-comment-content-box" v-html="items['replyContent']"></div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </el-collapse-item>
+                            </el-collapse>
                         </div>
                         <div class="code-status-box" v-if="parseInt(item['isSolve']) === 1">
                             <img :src="Web515Png" class="widget" alt=""/>
                         </div>
-                        <!--n-collapse :expanded-names="item['expandedName']" accordion>
-                            <n-collapse-item title="评论列表" :name="'commentList-'+item['id']">
-                                <div class="collapse-comment-box">
-                                    <n-input type="textarea" v-model:value="item['replyContent']" placeholder="我也说一句"/>
-                                    <div class="comment-btn-box">
-                                        <div class="flex-auto"></div>
-                                        <n-button type="primary" class="w-20" @click="saveCommentClick(item)">评论</n-button>
-                                    </div>
-                                    <div class="user-comment-info-box" v-for="items in item['expandedCommentList']" :key="items.id">
-                                        <n-avatar size="large" :src="items.avatar || avatarPng" color="white"/>
-                                        <div class="user-comment-box">
-                                            <div class="user-info-box">
-                                                <span class="text-base mr-3">{{items['userName']||'用户名异常'}}</span>
-                                                <span class="text-gray text-sm">{{items['createTime']}}</span>
-                                            </div>
-                                            <div class="user-comment-content-box" v-html="items['replyContent']"></div>
-                                        </div>
-                                    </div>
-                                </div>
-                            </n-collapse-item>
-                        </n-collapse-->
                     </div>
                 </div>
             </el-scrollbar>
+            <div class="page-top-btn" @click="scrollToTop">
+                <HcIcon name="vertical_align_top" class="icon"/>
+            </div>
         </div>
         <!--我的工单服务-->
         <div class="order-service-data" :style="'width:' + leftWidth + 'px;'">
@@ -79,14 +69,14 @@
                 </template>
                 <template #extra>
                     <el-tooltip effect="dark" content="发起新工单服务" placement="top">
-                        <el-button type="primary" hc-btn class="hc-add-icon">
+                        <el-button type="primary" hc-btn class="hc-add-icon" @click="newOrderServiceClick">
                             <HcIcon name="add"/>
                         </el-button>
                     </el-tooltip>
                 </template>
                 <div class="mb-5">
                     <el-select v-model="nameSelectKey" block placeholder="工单名称" size="large" @change="nameSelectUpdate">
-                        <el-option v-for="item in nameSelectData" :key="item.value" :label="item?.label" :value="item?.value"/>
+                        <el-option v-for="item in nameSelectData" :key="item.id" :label="item?.title" :value="item?.id"/>
                     </el-select>
                 </div>
                 <div class="time-line-box" :class="isCurrentBol?'time-height':''">
@@ -126,6 +116,55 @@
             <!--左右拖动-->
             <div class="horizontal-drag-line" @mousedown="onmousedown"/>
         </div>
+        <!--提交工单-->
+        <el-dialog v-model="showModal" title="发起新工单服务" width="720px" custom-class="hc-modal-border" :before-close="handleModalClose">
+            <div class="title">请选择您需要反馈的问题类型</div>
+            <div class="hc-type-tabs my-5">
+                <el-radio-group v-model="typeTabKey" size="large" @change="typeTabChange">
+                    <el-radio-button v-for="item in typeTab" :label="item?.dictValue">{{item?.dictValue}}</el-radio-button>
+                </el-radio-group>
+            </div>
+            <div class="modal-checkbox-box">
+                <el-checkbox-group v-model="typeCheckBox[typeTabIndex]">
+                    <div class="checkbox-item" v-for="item in typeTab[typeTabIndex]?.children" :key="item.id">
+                        <el-checkbox :label="item['dictValue']">{{item['dictValue']}}</el-checkbox>
+                    </div>
+                </el-checkbox-group>
+            </div>
+            <div class="mt-5">
+                <el-input v-model="opinionContent" :rows="3" type="textarea" placeholder="请输入你宝贵的建议,我们将会跟踪解决"/>
+            </div>
+            <div class="mt-3 upload-img" v-loading="spinShow">
+                <el-upload v-model:file-list="uploadFileList" :action="uploadAction" :headers="getTokenHeader()" :limit="3" :accept="uploadAccept" list-type="picture-card" multiple
+                           :before-upload="beforeUpload" :on-change="uploadChange" :on-exceed="uploadExceed" :on-preview="handlePreview" :on-remove="removeUpload">
+                    <HcIcon name="add" class="hc-upload-icon"/>
+                </el-upload>
+                <el-image-viewer v-if="showViewer" :initial-index="initialIndex" :url-list="previewFileList" @close="showViewer = false"/>
+            </div>
+            <div class="mt-3">
+                <el-alert title="请上传JPG、PNG格式的图片文件,最多上传 3 张图片,文件大小不超过30M" type="error" :closable="false"/>
+            </div>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button size="large" @click="handleModalClose">取消</el-button>
+                    <el-button type="primary" hc-btn @click="saveClick">提交</el-button>
+                </div>
+            </template>
+        </el-dialog>
+
+        <!--提示框-->
+        <el-dialog v-model="showTipModal" title="感谢" width="600px" custom-class="hc-modal-border" :before-close="handleTipModalClose">
+            <div class="tip-modal-icon-box">
+                <HcIcon name="sentiment_very_satisfied"/>
+            </div>
+            <div class="tip-modal-text-box">感谢您的仗义直言,大恩不言谢,有事联系我们,我们随时都在</div>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button size="large" @click="tipModalClick">下次不用感谢了</el-button>
+                    <el-button type="primary" hc-btn @click="handleTipModalClose">不客气</el-button>
+                </div>
+            </template>
+        </el-dialog>
     </div>
 </template>
 
@@ -133,24 +172,27 @@
 import {nextTick, onMounted, ref, watch} from "vue";
 import {useAppStore} from "~src/store/index";
 import orderServe from '~api/other/orderServe';
-import { getTokenHeader } from '~src/api/request/header';
-import HcImage from "~com/plugins/element/HcImage.vue"
+import {getTokenHeader} from '~src/api/request/header';
 import avatarPng from '~src/assets/images/avatar.png';
 import Web515Png from '~src/assets/images/Web515.png';
 import {userConfigSave} from "~api/other";
-import {isObjNull} from "~src/utils/lib/isApp";
+import {isType, utilsFile, utilsArray} from "vue-utils-plus"
 import oss from "~api/oss";
 
+//初始变量
+const { isArray } = isType()
+const { isSize } = utilsFile()
+const { getIndex } = utilsArray()
 const useAppState = useAppStore()
 const projectId = ref(useAppState.getProjectId);
 const contractId = ref(useAppState.getContractId);
 const isScreenShort = ref(useAppState.getScreenShort)
-const opinionView = ref(useAppState.getOrderServiceTipModal) //是否弹出工单感谢, 0不弹出,1弹出
+//是否弹出工单感谢, 0不弹出,1弹出
+const opinionView = ref(useAppState.getOrderServiceTipModal)
 
 //搜索和分页数据
 const searchForm = ref({current: 1, size: 20})
 const orderDataList = ref([])
-const spinShow = ref(true)
 
 // 工单名称
 const nameSelectKey = ref(null)
@@ -174,7 +216,6 @@ watch(() => [
 })
 
 nextTick(() => {
-    spinShow.value = false
     //截图数据
     if(isScreenShort.value) {
         let base64 = window.sessionStorage.getItem('screenShort-base64') || '';
@@ -200,15 +241,11 @@ const queryUserOpinionPage = () => {
 const queryCurrentUserOpinionList = () => {
     orderServe.queryCurrentUserOpinionList({
         projectId: projectId.value
-    }).then(res => {
-        const data = isObjNull(res?.data?.data)?[]:res?.data?.data;
-        data.forEach(item => {
-            item.label = item.title
-            item.value = item.id
-        })
-        nameSelectData.value = data
-        if (data.length > 0) {
-            nameSelectKey.value = data[0].value
+    }).then(({data}) => {
+        const res = isArray(data?.data) ? data?.data : [];
+        nameSelectData.value = res
+        if (res.length > 0) {
+            nameSelectKey.value = res[0].id
             //获取当前工单的最新流程
             queryUserFlowOpinion().then()
         } else {
@@ -276,13 +313,6 @@ const saveCommentClick = (item) => {
     }
 }
 
-//回车提交评论
-const commentKeyUp = (e,item) => {
-    if (e.key === "Enter") {
-        saveCommentClick(item)
-    }
-}
-
 //点赞
 const likeClick = (item) => {
     if (item['currentUserGood']) {
@@ -307,7 +337,6 @@ const likeClick = (item) => {
     }
 }
 
-
 //弹框
 const showModal = ref(false)
 
@@ -318,81 +347,90 @@ const typeTab = ref([]);
 const typeTabIndex = ref(-1);
 const typeCheckBox = ref([]);
 
-const typeTabChange = (value) => {
-    typeTabKey.value = value;
-    typeTabIndex.value = typeTab.value.findIndex(item => item.key === value);
+const typeTabChange = (val) => {
+    typeTabKey.value = val;
+    typeTabIndex.value = typeTab.value.findIndex(item => item.dictValue === val);
 }
 
-nextTick(() => {
-    //获取字典信息
-    orderServe.queryDictBizList().then(res => {
-        const records = res?.data?.data || [];
-        records.forEach(item => {
-            item.key = item['dictValue']
-            item.name = item['dictValue']
-            typeCheckBox.value.push([])
-        })
+//发起新工单服务
+const newOrderServiceClick = () => {
+    queryDictBizList()
+    showModal.value = true
+}
+
+//关闭
+const handleModalClose = () => {
+    showModal.value = false
+}
+
+//获取字典信息
+const queryDictBizList = () => {
+    orderServe.queryDictBizList().then(({data}) => {
+        const records = isArray(data?.data) ? data?.data : [];
         typeTab.value = records
         if (records.length > 0) {
             typeTabIndex.value = 0
-            typeTabKey.value = records[0].key
+            typeTabKey.value = records[0]?.dictValue
         }
     })
-})
+}
 
 //建议内容
 const opinionContent = ref('')
 
 //上传
-const previewFileList = ref([])
 const uploadFileList = ref([])
 const uploadAction = "/api/blade-resource/oss/endpoint/put-file"
 const uploadAccept = "image/png,image/jpg,image/jpeg"
 
 //上传前
-const beforeUpload = async ({file}) => {
-    let maxSize = 30 * 1024 * 1024;
-    let fileSize = file?.file?.size
-    if (fileSize > maxSize) {
-        window?.$message?.warning('文件大小,不能过30M!');
-        return false;
-    } else {
+const beforeUpload = (res) => {
+    if (isSize(res?.size,30)) {
         return true;
+    } else {
+        window?.$ElMessage?.warning('文件大小,不能过30M!');
+        return false;
     }
 }
 
-//上传完成
-const uploadFinish = ({file,event}) => {
-    let res = JSON.parse(event?.target?.response);
-    let data = res?.data;
-    if (data?.link) {
-        file.url = data?.link;
-        file.name = data?.name;
-        file.thumbnailUrl = data?.link;
-    }
+//状态改变
+const uploadChange = (_, uploadFiles) => {
+    console.log(uploadFiles)
+    //暂时不知道怎么搞。。。
 }
-//文件列表改变
-const fileListUpdate = (fileList) => {
-    let fileArr = []
-    fileList.forEach(item => {
-        fileArr.push(item.url)
-    })
-    uploadFileList.value = fileList
-    previewFileList.value = fileArr
+
+//超出限制时
+const uploadExceed = () => {
+    window?.$ElMessage?.warning('请上传JPG、PNG格式的图片文件,最多上传 3 张图片,文件大小不超过30M');
 }
+
 //预览
-const previewImageModal = ref(false);
-const previewImageUrl = ref("");
+const showViewer = ref(false)
+const previewFileList = ref([])
+const initialIndex = ref(0)
 const handlePreview = (file) => {
-    const { url } = file;
-    previewImageUrl.value = url;
-    previewImageModal.value = true;
+    let fileArr = getUploadFileUrl()
+    const fileList = uploadFileList.value ?? [];
+    const index = getIndex(fileList, 'uid', file?.uid)
+    previewFileList.value = fileArr
+    initialIndex.value = index
+    showViewer.value = true
+}
+
+//获取文件URL
+const getUploadFileUrl = () => {
+    let fileArr = [], fileList = uploadFileList.value ?? [];
+    fileList.forEach(item => {
+        fileArr.push(item?.response?.data?.link)
+    })
+    return fileArr
 }
 
 //删除文件
-const removeUpload = ({file}) => {
+const removeUpload = (file) => {
+    const fileName = file?.response?.data?.name
     oss.removeFile({
-        fileName: file.name
+        fileName: fileName
     }).then(({data}) => {
         if (data.code === 200) {
             return true
@@ -412,70 +450,71 @@ const base64toFile = (base64) => {
     for (let i = 0; i < binary.length; i++) {
         array.push(binary.charCodeAt(i));
     }
-    return new Blob([new Uint8Array(array)], { type: "image/jpeg" });
+    let blob = new Blob([new Uint8Array(array)], { type: "image/jpeg" });
+    return new File([blob], new Date() + ".jpg");
 }
 
 //上传截图文件
+const spinShow = ref(false)
 const uploadImgFile = (base64) => {
-    let blob = base64toFile(base64);
-    let fileOfBlob = new File([blob], new Date() + ".jpg");
+    let fileOfBlob = base64toFile(base64);
     let formData = new FormData();
     formData.append("file", fileOfBlob);
     //上传文件
     spinShow.value = true
-    oss.putFile(formData).then(res=> {
-        let data = res?.data?.data;
-        if (data?.link) {
-            data.id = data?.attachId
-            data.status = "finished"
-            data.url = data?.link
-            data.thumbnailUrl = data?.link
+    newOrderServiceClick()
+    oss.putFile(formData).then(({data}) => {
+        let res = data?.data ?? {}
+        if (res?.link) {
+            uploadFileList.value.push({
+                url: res?.link,
+                name: res?.name,
+                response: {
+                    data: res
+                }
+            })
         }
-        uploadFileList.value.push(data)
-        previewFileList.value.push(data?.link)
         window.sessionStorage.removeItem('screenShort-base64');
+        window.$ElMessage?.success('文件上传成功');
         spinShow.value = false
-        window.$message?.success('文件上传成功');
-        showModal.value = true
     }).catch(()=> {
         spinShow.value = false
         window.sessionStorage.removeItem('screenShort-base64');
-        window.$message?.warning('文件上传失败');
+        window.$ElMessage?.warning('文件上传失败');
     })
 }
 
 //提交工单反馈
 const saveClick = () => {
     //拼接问题类型
-    let problemType = typeTabKey.value, problemVal = '';
-    const checkBoxVal = typeCheckBox.value[typeTabIndex.value] || [];
-    checkBoxVal.forEach(item => {
-        problemVal += `-${item}`
-    })
+    let problemType = typeTabKey.value, index = typeTabIndex.value, problemVal = '';
+    const checkBoxVal = typeCheckBox.value[index] || [];
+    checkBoxVal.forEach(item => {problemVal += `-${item}`})
+    let filesUrl = getUploadFileUrl()
     //判断数据
     if (!problemVal) {
-        window.$message?.warning('请先选择问题类型');
+        window.$ElMessage?.warning('请先选择问题类型');
     } else {
-        //组装数据
-        let formData = {
+        //请求接口
+        orderServe.saveUserOpinion({
             projectId: projectId.value,
             contractId: contractId.value,
             problemType: problemType + problemVal,
             opinionContent: opinionContent.value,
-            returnFiles: previewFileList.value
-        }
-        //请求接口
-        orderServe.saveUserOpinion(formData).then(res => {
-            window.$message?.success('提交成功');
-            showModal.value = false;
-            //重置表单
-            typeCheckBox.value[typeTabIndex.value] = []
-            opinionContent.value = ''
-            uploadFileList.value = []
-            previewFileList.value = []
-            //更新数据
-            queryUserOpinionPage()
-            queryCurrentUserOpinionList()
+            returnFiles: filesUrl
+        }).then(({data}) => {
+            if (data.code === 200) {
+                window.$ElMessage?.success('提交成功');
+                showModal.value = false;
+                //重置表单
+                typeCheckBox.value[index] = []
+                opinionContent.value = ''
+                uploadFileList.value = []
+                previewFileList.value = []
+                //更新数据
+                queryUserOpinionPage()
+                queryCurrentUserOpinionList()
+            }
         })
     }
 }
@@ -483,15 +522,20 @@ const saveClick = () => {
 //评价
 const showTipModal = ref(false)
 const evaluationKey = ref('1')
-const evaluationData = [{value: "1", label: "满意"}, {value: "2", label: "不满意并再次提交解决"}, {value: "3", label: "不满意且投诉"}]
+const evaluationData = [
+    {value: "1", label: "满意"},
+    {value: "2", label: "不满意并再次提交解决"},
+    {value: "3", label: "不满意且投诉"}
+]
 const disposeUserFeedback = () => {
     let oldEndFlow = orderFlowList.value[3]?.id || ''
-    let type = evaluationKey.value || ''
-    let userOpinionId = nameSelectKey.value || ''
-    let formData = {oldEndFlow: oldEndFlow, type: type, userOpinionId: userOpinionId}
     //请求接口
-    orderServe.disposeUserFeedback(formData).then(res => {
-        window.$message?.success('提交成功');
+    orderServe.disposeUserFeedback({
+        oldEndFlow: oldEndFlow,
+        type: evaluationKey.value || '',
+        userOpinionId: nameSelectKey.value || ''
+    }).then(() => {
+        window.$ElMessage?.success('提交成功');
         showTipModal.value = parseInt(opinionView.value) === 1
         queryCurrentUserOpinionList()
     })
@@ -508,12 +552,14 @@ const tipModalClick = () => {
     })
 }
 
+const handleTipModalClose = () => {
+    showTipModal.value = false
+}
+
 //滚动到顶部
+const scrollbarRef = ref(null)
 const scrollToTop = () => {
-    let sTop = document.getElementById('order-service-content').scrollTop;
-    if (sTop > 0) {
-        document.getElementById('order-service-content').scrollTop = 0
-    }
+    scrollbarRef.value?.setScrollTop(0)
 }
 
 //左右拖动,改变树形结构宽度
@@ -541,90 +587,27 @@ const onmousedown = () => {
 .item-badge .el-badge__content.is-fixed {
     top: 4px;
 }
-
-.order-service-spin {
-    height: 100%;
-    .n-spin-content {
-        height: 100%;
-        overflow: hidden;
-    }
-}
-.hc-order-service .order-service-data .time-line-box {
-    .n-timeline .n-timeline-item {
-        padding-bottom: 10px;
-    }
-    .n-timeline .n-timeline-item .n-timeline-item-timeline {
-        width: calc(var(--n-icon-size) + 12px + 10px);
-        .n-timeline-item-timeline__line {
-            top: 33px;
-            left: 15px;
-            width: 1px;
-            bottom: 4px;
-            background-color: #b1b1b1;
-        }
-        .n-timeline-item-timeline__icon {
-            color: var(--n-meta-text-color);
-            border: 1px solid;
-            border-radius: 50px;
-            padding: 14px;
-            span {
-                font-size: 16px;
-            }
-        }
-    }
-    .n-timeline.n-timeline--left-placement .n-timeline-item .n-timeline-item-content {
-        margin-left: calc(var(--n-icon-size) + 12px + 10px);
-        .n-timeline-item-content__title {
-            color: var(--n-meta-text-color);
+.comment-card-box .card-content-box .hc-collapse-box.el-collapse {
+    border: 0;
+    .el-collapse-item {
+        &:last-child {
+            margin-bottom: 0;
         }
-        .n-timeline-item-content__content {
-            color: var(--n-meta-text-color);
-        }
-    }
-    .n-timeline .n-timeline-item.n-timeline-item--success-type {
-        .n-timeline-item-timeline .n-timeline-item-timeline__icon {
-            color: #ffffff;
-            background: #18a058;
-            border: 1px solid #18a058;
+        .el-collapse-item__header {
+            display: none;
         }
-    }
-    .n-timeline .n-timeline-item.n-timeline-item--info-type {
-        .n-timeline-item-timeline .n-timeline-item-timeline__icon {
-            color: #ffffff;
-            background: #0081ff;
-            border: 1px solid #0081ff;
+        .el-collapse-item__wrap {
+            background-color: initial;
+            border-bottom: 0;
         }
     }
 }
-.order-service-content .content-box {
-    .n-collapse .n-collapse-item .n-collapse-item__header {
-        display: none;
-    }
-    .n-collapse .n-collapse-item .n-collapse-item__content-wrapper .n-collapse-item__content-inner {
-        padding-top: 0;
-    }
-    .image_desc {
-        .el-image.el-image-box  {
-            width: 100%;
-            height: 160px;
-            border: 1px solid #eee;
-            border-radius: 5px;
-            display: block;
-        }
-    }
-    .foot-tools-box .input-box .text-input-box .n-input {
-        border: 0;
-        flex: auto;
-        width: auto;
-        background: inherit;
-        .n-input__border,
-        .n-input__state-border {
-            display: none;
-        }
-    }
-    .user-comment-info-box {
-        .n-avatar {
-            top: 5px;
+.comment-reply-content-box {
+    .el-textarea {
+        min-height: 40px;
+        margin-right: 10px;
+        .el-textarea__inner {
+            min-height: 40px !important;
         }
     }
 }

+ 0 - 169
src/views/other/scratch_6.vue

@@ -1,169 +0,0 @@
-<n-spin :show="spinShow" class="order-service-spin">
-<div class="hc-order-service h-full flex">
-    <div class="flex-auto h-full order-service-content" id="order-service-content">
-        <div class="content-box">
-            <div class="comment-card-box" v-for="(item,index) in orderDataList" :key="item.id">
-                <div class="card-content-box">
-                    <div class="user-info-box">
-                        <n-avatar round :size="50" :src="item.avatar || avatarPng"/>
-                        <div class="name-box">
-                            <div class="text-lg">{{item['createUserName']||'用户名异常'}}</div>
-                            <div class="text-gray">{{item['createTime']}}</div>
-                        </div>
-                        <div class="code-status-box" v-if="parseInt(item['isSolve']) === 1">
-                            <div class="widget bg-green-thin">已解决</div>
-                        </div>
-                    </div>
-                    <div class="desc_para" v-html="item['opinionContent']"></div>
-                    <div class="image_desc" v-if="item['returnFiles']?.length > 0">
-                        <n-grid x-gap="12" :cols="3">
-                            <n-gi v-for="(items,indexs) in item['returnFiles']">
-                                <HcImage class="el-image-box" ui="el-image-box a" :src="items" :srcs="item['returnFiles']" :index="indexs"/>
-                            </n-gi>
-                        </n-grid>
-                    </div>
-                    <div class="foot-tools-box">
-                        <div class="left-box">
-                            <div class="input-box" :class="item['expandedName']?'hidden':''">
-                                <div class="input-icon-box">
-                                    <i class="hcicon-bianjimian"/>
-                                </div>
-                                <div class="text-input-box">
-                                    <n-input type="text" v-model:value="item['replyContent']" size="small" placeholder="我也说一句" @keyup="commentKeyUp($event,item)"/>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="right-box">
-                            <div class="icon-box" @click="commentExpanded(item)">
-                                <i class="HIcon-pinglunliebiao"/>
-                                <div class="badge" v-if="item['commentsNumber'] >= 1">{{item['commentsNumber']}}</div>
-                            </div>
-                            <div class="icon-box" :data-index="item['expandedName']" @click="likeClick(item)">
-                                <i class="HIcon-dianji-dianzan" v-if="item['currentUserGood']"/>
-                                <i class="HIcon-weidianji-dianzan" v-else/>
-                                <div class="badge" v-if="item['goodNumber'] >= 1">{{item['goodNumber']}}</div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-                <n-collapse :expanded-names="item['expandedName']" accordion>
-                    <n-collapse-item title="评论列表" :name="'commentList-'+item['id']">
-                        <div class="collapse-comment-box">
-                            <n-input type="textarea" v-model:value="item['replyContent']" placeholder="我也说一句"/>
-                            <div class="comment-btn-box">
-                                <div class="flex-auto"></div>
-                                <n-button type="primary" class="w-20" @click="saveCommentClick(item)">评论</n-button>
-                            </div>
-                            <div class="user-comment-info-box" v-for="items in item['expandedCommentList']" :key="items.id">
-                                <n-avatar size="large" :src="items.avatar || avatarPng" color="white"/>
-                                <div class="user-comment-box">
-                                    <div class="user-info-box">
-                                        <span class="text-base mr-3">{{items['userName']||'用户名异常'}}</span>
-                                        <span class="text-gray text-sm">{{items['createTime']}}</span>
-                                    </div>
-                                    <div class="user-comment-content-box" v-html="items['replyContent']"></div>
-                                </div>
-                            </div>
-                        </div>
-                    </n-collapse-item>
-                </n-collapse>
-            </div>
-        </div>
-        <div class="page-top-btn" @click="scrollToTop">
-            <i class="hcicon-fanhuidingbu"/>
-        </div>
-    </div>
-    <!--我的工单服务-->
-    <div class="h-full order-service-data" :style="'width:' + leftWidth + 'px;'">
-        <div class="flex items-center mb-5">
-            <span class="text-lg font-bold mr-4">我的工单服务进度</span>
-            <n-button type="primary" size="small" @click="showModal = true">发起新工单服务</n-button>
-        </div>
-        <div class="mb-5">
-            <n-select v-model:value="nameSelectKey" :options="nameSelectData" placeholder="工单名称" @update:value="nameSelectUpdate"/>
-        </div>
-        <div class="time-line-box" :class="isCurrentBol?'time-height':''">
-            <n-timeline :icon-size="20">
-                <template v-for="(item,index) in orderFlowList">
-                    <n-timeline-item :type="item['currentBol']?'success':item['current']?'info':''" :title="item['replyName']">
-                        <template #icon>
-                            <i class="_icon-check" v-if="index === 0 || index === 3"/>
-                            <span v-else>{{index}}</span>
-                        </template>
-                        <div class="text-xs" v-html="item['replyContent']"></div>
-                    </n-timeline-item>
-                </template>
-            </n-timeline>
-        </div>
-        <div class="evaluation-box" :class="isCurrentBol?'show':''">
-            <div class="text-lg font-bold">评价</div>
-            <div class="tip-box">请对工单处理评价,若是未解决问题,可进行投诉,平台核实情况,将对相关客服人员绩效考核,并且从新为您自动发起工单解决问题</div>
-            <div class="radio-group-box">
-                <n-radio-group v-model:value="evaluationKey" name="radiogroup">
-                    <div class="radio-item" v-for="item in evaluationData" :key="item.value">
-                        <n-radio :value="item.value">{{ item.label }}</n-radio>
-                    </div>
-                </n-radio-group>
-            </div>
-            <div class="btn-box">
-                <n-button type="success" class="w-20" @click="disposeUserFeedback">提交</n-button>
-            </div>
-        </div>
-        <!--左右拖动-->
-        <div class="horizontal-drag-line" @mousedown="onmousedown"/>
-    </div>
-    <!--提交工单-->
-    <n-modal v-model:show="showModal">
-        <n-card class="w-606">
-            <div class="title">请选择您需要反馈的问题类型</div>
-            <div class="hc-type-tabs my-5">
-                <n-tabs type="segment" :default-value="typeTabKey" @update:value="typeTabChange">
-                    <n-tab v-for="item in typeTab" :name="item.key" :key="item.id">{{item.name}}</n-tab>
-                </n-tabs>
-            </div>
-            <div class="modal-checkbox-box">
-                <n-checkbox-group v-model:value="typeCheckBox[typeTabIndex]">
-                    <div class="checkbox-item" v-for="item in typeTab[typeTabIndex]?.children" :key="item.id">
-                        <n-checkbox :value="item['dictValue']" :label="item['dictValue']" />
-                    </div>
-                </n-checkbox-group>
-            </div>
-            <div class="mt-5">
-                <n-input type="textarea" v-model:value="opinionContent" placeholder="请输入你宝贵的建议,我们将会跟踪解决"/>
-            </div>
-            <div class="mt-3 upload-img">
-                <n-upload :action="uploadAction" :headers="getTokenHeader()" list-type="image-card" :file-list="uploadFileList" :with-credentials="true" :max="3" :accept="uploadAccept"
-                          multiple @before-upload="beforeUpload" @finish="uploadFinish" @remove="removeUpload" @update:file-list="fileListUpdate" @preview="handlePreview"/>
-                <div class="text-red mt-3">请上传JPG、PNG格式的图片文件,文件大小不超过30M</div>
-                <n-modal v-model:show="previewImageModal" preset="card" class="w-606">
-                    <img :src="previewImageUrl" style="width: 100%" alt="">
-                </n-modal>
-            </div>
-            <div class="foot-btn-box">
-                <n-button type="primary" strong secondary class="w-20 mr-4" @click="showModal = false">取消</n-button>
-                <n-button type="primary" class="w-20" @click="saveClick">提交</n-button>
-            </div>
-        </n-card>
-    </n-modal>
-    <!--提示框-->
-    <n-modal v-model:show="showTipModal" :mask-closable="false">
-        <n-card class="w-606">
-            <div class="tip-modal-icon-box">
-                <i class="HIcon-xiaolian"/>
-            </div>
-            <div class="tip-modal-text-box">感谢您的仗义直言,大恩不言谢,有事联系我们,我们随时都在</div>
-            <div class="tip-modal-btn-box">
-                <div class="btn-box">
-                    <n-button strong secondary type="primary" @click="tipModalClick">下次不用感谢了</n-button>
-                </div>
-                <div class="btn-box">
-                    <n-button type="primary" @click="showTipModal = false">不客气</n-button>
-                </div>
-            </div>
-        </n-card>
-    </n-modal>
-</div>
-<template #description>
-    正在请求中...
-</template>
-</n-spin>

+ 87 - 37
src/views/user/index.vue

@@ -138,18 +138,20 @@
                         </div>
                     </div>
                 </template>
-                <el-table hc :data="logTableData" :loading="logTableLoading" stripe>
-                    <el-table-column prop="num" label="序号" width="80">
-                        <template #default="scope">
-                            {{scope.$index + 1}}
-                        </template>
-                    </el-table-column>
-                    <el-table-column prop="operationModule" label="业务模块" width="180"/>
-                    <el-table-column prop="operationTypeValue" label="操作类型"/>
-                    <el-table-column prop="operationMedium" label="设备" width="80"/>
-                    <el-table-column prop="operationContent" label="操作内容" />
-                    <el-table-column prop="createTime" label="操作时间" width="180"/>
-                </el-table>
+                <div class="hc-table-ref-box">
+                    <el-table hc :data="logTableData" :loading="logTableLoading" stripe>
+                        <el-table-column prop="num" label="序号" width="80">
+                            <template #default="scope">
+                                {{scope.$index + 1}}
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="operationModule" label="业务模块" width="180"/>
+                        <el-table-column prop="operationTypeValue" label="操作类型"/>
+                        <el-table-column prop="operationMedium" label="设备" width="80"/>
+                        <el-table-column prop="operationContent" label="操作内容" />
+                        <el-table-column prop="createTime" label="操作时间" width="180"/>
+                    </el-table>
+                </div>
                 <template #action>
                     <HcPages :pages="searchLogForm" @change="pageLogChange"/>
                 </template>
@@ -158,20 +160,22 @@
                 <template #extra>
                     <HcNewSwitch :datas="tabTypeTab" :keys="tabTypeKey" @change="tabTypeChange"/>
                 </template>
-                <el-table ref="recycleTableRef" hc :data="recycleTableData" stripe @selection-change="recycleTableSelectionChange">
-                    <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="title" label="删除内容"/>
-                    <el-table-column prop="name" label="父节点名称"/>
-                    <el-table-column prop="date" label="删除时间"/>
-                </el-table>
+                <div class="hc-table-ref-box">
+                    <el-table ref="recycleTableRef" hc :data="recycleTableData" stripe @selection-change="recycleTableSelectionChange">
+                        <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="fileName" label="删除内容" />
+                        <el-table-column prop="position" label="父节点名称"/>
+                        <el-table-column prop="operationTime" label="删除时间"/>
+                    </el-table>
+                </div>
                 <template #action>
                     <div class="foot-recycle">
-                        <el-button type="primary" hc-btn>
+                        <el-button type="primary" hc-btn :loading="recycleBtnLoading" @click="recycleBtnClick">
                             <HcIcon name="reply"/>
                             <span>恢复</span>
                         </el-button>
@@ -187,7 +191,6 @@
 import {ref, watch, onMounted} from "vue";
 import {useRouter, useRoute} from 'vue-router'
 import {useAppStore} from "~src/store/index";
-import {deepClone} from "~src/utils/lib/util";
 import avatarPng from '~src/assets/images/avatar.png';
 import {getTokenHeader} from '~src/api/request/header';
 import userApi from "~api/userInfo/index"
@@ -318,6 +321,8 @@ const getPageTypeData = (key) => {
         queryOperationView()
         operationTypeStatus()
         getLogTableData()
+    } else if (key === 'recycle') {
+        getRecycleTableData()
     }
 }
 
@@ -326,7 +331,7 @@ const basicFormEdit = ref(false)
 const basicHight = ref(false)
 //基础信息表单
 const formUserRef = ref(null)
-const formUserModel = ref(deepClone(userInfo.value))
+const formUserModel = ref(userInfo.value)
 const formUserRules = {
     real_name: {
         required: true,
@@ -620,27 +625,28 @@ const getLogTableData = () => {
 }
 
 //结构类型tab数据和相关处理
-const tabTypeKey = ref('file')
+const tabTypeKey = ref('1')
 const tabTypeTab = ref([
-    {key:'file',  name: '文件资料'},
-    {key:'project', name: '工程划分'}
+    {key: '1',  name: '文件资料'},
+    {key: '2', name: '工程划分'}
 ]);
 const tabTypeChange = (item) => {
     tabTypeKey.value = item?.key;
+    tabTypeKey.value = item?.key;
+    searchRecycleForm.value.current = 1
+    searchRecycleForm.value.delType = item?.key
+    getRecycleTableData()
 }
 
 //搜索和分页数据
 const searchRecycleForm = ref({
-    contractId: contractId.value, current: 1, size: 20, total: 0
+    projectId: projectId.value, contractId: contractId.value,
+    delType: tabTypeKey.value, current: 1, size: 20, total: 0
 })
+
 //表格数据
 const recycleTableRef = ref(null)
-const recycleTableData = ref([
-    { title: '-', name: 'test1', date: "-" },
-    { title: '-', name: 'test2', date: "-" },
-    { title: '-', name: 'test3', date: "-" },
-    { title: '-', name: 'test4', date: "-" }
-]);
+const recycleTableData = ref(null);
 
 //分页被点击
 const pageRecycleChange = ({current, size}) => {
@@ -648,14 +654,58 @@ const pageRecycleChange = ({current, size}) => {
     searchRecycleForm.value.size = size
     getRecycleTableData()
 }
+
 //获取数据
 const getRecycleTableData = () => {
-
+    userApi.queryRecycleBinList({
+        projectId: projectId.value,
+        contractId: contractId.value,
+        delType: tabTypeKey.value,
+        ...searchRecycleForm.value
+    }).then(({data}) => {
+        if (data.code === 200) {
+            let res = data['data'] || {}
+            recycleTableData.value = res['records'] || []
+            searchRecycleForm.value.total = res.total || 0
+        } else {
+            recycleTableData.value = []
+            searchRecycleForm.value.total = 0
+        }
+    })
 }
+
 //多选
+const RecycleCheckedKeys = ref([]);
 const recycleTableSelectionChange = (val) => {
     console.log(val)
 }
+
+//恢复
+const recycleBtnLoading = ref(false)
+const recycleBtnClick = () => {
+    const rows = RecycleCheckedKeys.value
+    if (rows.length > 0) {
+        //请求数据
+        recycleBtnLoading.value = true
+        userApi.recycleBinRegain({
+            projectId: projectId.value,
+            contractId: contractId.value,
+            delType: tabTypeKey.value,
+            regainIds: rows
+        }).then(({data}) => {
+            recycleBtnLoading.value = false
+            if (data.code === 200) {
+                window?.$message?.success('操作成功')
+                searchRecycleForm.value.current = 1
+                getRecycleTableData()
+            }
+        }).catch(() => {
+            recycleBtnLoading.value = false
+        })
+    } else {
+        window.$message?.warning('请先勾选要恢复的数据')
+    }
+}
 </script>
 
 <style lang="scss" scoped>

+ 59 - 36
yarn.lock

@@ -879,7 +879,7 @@ minimist@^1.2.6:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
   integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
 
-mlly@^0.5.3, mlly@^0.5.5:
+mlly@^0.5.3:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.5.5.tgz#80643b3a96671a0d6f41411e3daf7e4e188544d5"
   integrity sha512-2R4JT/SxRDPexomw4rmHYY/gWAGmL9Kkq1OR76Ua6w+P340a1aBDTWzKo2kAlxzrG82OdXs5VB9Lmcmyit0Obg==
@@ -887,6 +887,15 @@ mlly@^0.5.3, mlly@^0.5.5:
     pathe "^0.3.2"
     pkg-types "^0.3.3"
 
+mlly@^0.5.7:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.5.7.tgz#3b058c36268314a1670f89767d40eead66099b93"
+  integrity sha512-rz+n2i9862ymLH+UDlHpsuTVyCIAs+9WejS2De2VUlAKdpq8OJ9x/C2M7nNUMLEW1H+D6n0uZlpz8+tMGxCmyQ==
+  dependencies:
+    acorn "^8.8.0"
+    pathe "^0.3.3"
+    pkg-types "^0.3.3"
+
 moment@^2.29.4:
   version "2.29.4"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
@@ -965,6 +974,11 @@ pathe@^0.3.0, pathe@^0.3.2:
   resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.3.2.tgz#016345ed643027404d7a9ed8d1454ad997a1483a"
   integrity sha512-qhnmX0TOqlCvdWWTkoM83wh5J8fZ2yhbDEc9MlsnAEtEc+JCwxUKEwmd6pkY9hRe6JR1Uecbc14VcAKX2yFSTA==
 
+pathe@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.3.3.tgz#8d6d70a25d4db6024ed4d59e59c1bf80fcf18753"
+  integrity sha512-x3nrPvG0HDSDzUiJ0WqtzhN4MD+h5B+dFJ3/qyxVuARlr4Y3aJv8gri2cZzp9Z8sGs2a+aG9gNbKngh3gme57A==
+
 picocolors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@@ -1050,6 +1064,15 @@ postcss@^8.1.10, postcss@^8.4.14:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
+postcss@^8.4.16:
+  version "8.4.16"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
+  integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
+  dependencies:
+    nanoid "^3.3.4"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
 queue-microtask@^1.2.2:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -1102,10 +1125,10 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
-sass@^1.54.1:
-  version "1.54.1"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.1.tgz#4f72ef57ce2a6c3251f4e2c75eee9a0c19e09eb5"
-  integrity sha512-GHJJr31Me32RjjUBagyzx8tzjKBUcDwo5239XANIRBq0adDu5iIG0aFO0i/TBb/4I9oyxkEv44nq/kL1DxdDhA==
+sass@^1.54.3:
+  version "1.54.3"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.3.tgz#37baa2652f7f1fdadb73240ee9a2b9b81fabb5c4"
+  integrity sha512-fLodey5Qd41Pxp/Tk7Al97sViYwF/TazRc5t6E65O7JOk4XF8pzwIW7CvCxYVOfJFFI/1x5+elDyBIixrp+zrw==
   dependencies:
     chokidar ">=3.0.0 <4.0.0"
     immutable "^4.0.0"
@@ -1153,10 +1176,10 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
-tailwindcss@^3.1.6:
-  version "3.1.7"
-  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.7.tgz#ce99425f30a74e01457a2e6a724463b0df3159ac"
-  integrity sha512-r7mgumZ3k0InfVPpGWcX8X/Ut4xBfv+1O/+C73ar/m01LxGVzWvPxF/w6xIUPEztrCoz7axfx0SMdh8FH8ZvRQ==
+tailwindcss@^3.1.8:
+  version "3.1.8"
+  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.8.tgz#4f8520550d67a835d32f2f4021580f9fddb7b741"
+  integrity sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==
   dependencies:
     arg "^5.0.2"
     chokidar "^3.5.3"
@@ -1205,38 +1228,38 @@ tslib@2.3.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
   integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
 
-unimport@^0.6.4:
-  version "0.6.4"
-  resolved "https://registry.yarnpkg.com/unimport/-/unimport-0.6.4.tgz#65ab496e41ac37952dad0c49394c64c62be6238a"
-  integrity sha512-1cbSeTsC2EwzWeWAyQleajM354y+EYsymxE9p1wDbHKVTEe9XL6+e7vlEv2pj1Zk8YYFf+CBf9QG8My7aDKhag==
+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==
   dependencies:
     "@rollup/pluginutils" "^4.2.1"
     escape-string-regexp "^5.0.0"
     fast-glob "^3.2.11"
     local-pkg "^0.4.2"
     magic-string "^0.26.2"
-    mlly "^0.5.5"
-    pathe "^0.3.2"
+    mlly "^0.5.7"
+    pathe "^0.3.3"
     scule "^0.3.2"
     strip-literal "^0.4.0"
-    unplugin "^0.8.0"
+    unplugin "^0.8.1"
 
-unplugin-auto-import@^0.10.2:
-  version "0.10.3"
-  resolved "https://registry.yarnpkg.com/unplugin-auto-import/-/unplugin-auto-import-0.10.3.tgz#79e42247977618008d87ae20a919c99dc97672cb"
-  integrity sha512-tODQr7ZBnsBZ9lKaz2mqszKVi/4ALuLtS4gc1xwpcsBav5TCAl0HFSMuai1qL4AkYEwD2HPqK04LocCyK+D0KQ==
+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==
   dependencies:
     "@antfu/utils" "^0.5.2"
     "@rollup/pluginutils" "^4.2.1"
     local-pkg "^0.4.2"
     magic-string "^0.26.2"
-    unimport "^0.6.4"
-    unplugin "^0.8.0"
+    unimport "^0.6.5"
+    unplugin "^0.9.0"
 
-unplugin-vue-components@^0.21.2:
-  version "0.21.2"
-  resolved "https://registry.yarnpkg.com/unplugin-vue-components/-/unplugin-vue-components-0.21.2.tgz#d5b04b05e0521aa71fdfdba0b4ca392e3caa964d"
-  integrity sha512-HBU+EuesDj/HRs7EtYH7gBACljVhqLylltrCLModRmCToIIrrNvMh54aylUt4AD4qiwylgOx4Vgb9sBlrIcRDw==
+unplugin-vue-components@^0.22.3:
+  version "0.22.3"
+  resolved "https://registry.yarnpkg.com/unplugin-vue-components/-/unplugin-vue-components-0.22.3.tgz#e799e49486476b5413d3b0983874bc8a92429ad4"
+  integrity sha512-f31VCJF0K9oXCzKizJqNpmQz2XYTA0gjq+E5zM3hB8JxZ6cy5sXxO91fK2pI1TFGeM3JCe6yC9BJDymkMbXnNg==
   dependencies:
     "@antfu/utils" "^0.5.2"
     "@rollup/pluginutils" "^4.2.1"
@@ -1247,22 +1270,22 @@ unplugin-vue-components@^0.21.2:
     magic-string "^0.26.2"
     minimatch "^5.1.0"
     resolve "^1.22.1"
-    unplugin "^0.7.2"
+    unplugin "^0.9.0"
 
-unplugin@^0.7.2:
-  version "0.7.2"
-  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.7.2.tgz#4127012fdc2c84ea4ce03ce75e3d4f54ea47bba1"
-  integrity sha512-m7thX4jP8l5sETpLdUASoDOGOcHaOVtgNyrYlToyQUvILUtEzEnngRBrHnAX3IKqooJVmXpoa/CwQ/QqzvGaHQ==
+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==
   dependencies:
-    acorn "^8.7.1"
+    acorn "^8.8.0"
     chokidar "^3.5.3"
     webpack-sources "^3.2.3"
     webpack-virtual-modules "^0.4.4"
 
-unplugin@^0.8.0:
-  version "0.8.0"
-  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.8.0.tgz#aeabac40e89fb69e5c9366b4821c3836d1b6ffce"
-  integrity sha512-OzOkJ9XOPlD1Cph6qy/p4i/KSUbs76GToXjH/STHpfo6D7y+EqpfAL6G6HaoOw5QLkt9+KWwcxYUmPFkDf1upQ==
+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"