刘洋 il y a 2 ans
Parent
commit
5798c5b742

+ 85 - 80
src/assets/styles/element/custom.scss

@@ -117,9 +117,9 @@ button.el-button {
       display: none;
       display: none;
     }
     }
   }
   }
-  &[canoverflow]{
-    &>.el-dialog__body{
-      overflow:visible;
+  &[canoverflow] {
+    & > .el-dialog__body {
+      overflow: visible;
     }
     }
   }
   }
   &[less] {
   &[less] {
@@ -170,7 +170,7 @@ button.el-button {
     line-height: 40px;
     line-height: 40px;
   }
   }
 }
 }
-.preview-clear-mask{
+.preview-clear-mask {
   // width:0;
   // width:0;
   // height:0;
   // height:0;
 }
 }
@@ -194,53 +194,61 @@ button.el-button {
   display: none;
   display: none;
 }
 }
 
 
-
-
-.el-table__border-left-patch{
-  background-color:#ddd !important;
+.el-table__border-left-patch {
+  background-color: #ddd !important;
 }
 }
-.el-table--border:after, .el-table--border:before, .el-table--border .el-table__inner-wrapper:after, .el-table__inner-wrapper:before{
+.el-table--border:after,
+.el-table--border:before,
+.el-table--border .el-table__inner-wrapper:after,
+.el-table__inner-wrapper:before {
   background-color: #ddd !important;
   background-color: #ddd !important;
 }
 }
-.el-table th.el-table__cell.is-leaf, .el-table td.el-table__cell{
-  border-bottom:1px solid #ddd !important;
-  border-right:1px solid #ddd !important;
+.el-table th.el-table__cell.is-leaf,
+.el-table td.el-table__cell {
+  border-bottom: 1px solid #ddd !important;
+  border-right: 1px solid #ddd !important;
 }
 }
-.el-table--group .el-table__inner-wrapper tr:first-child td:first-child, .el-table--group .el-table__inner-wrapper tr:first-child th:first-child, .el-table--group .el-table__footer-wrapper tr:first-child td:first-child, .el-table--group .el-table__footer-wrapper tr:first-child th:first-child, .el-table--border .el-table__inner-wrapper tr:first-child td:first-child, .el-table--border .el-table__inner-wrapper tr:first-child th:first-child, .el-table--border .el-table__footer-wrapper tr:first-child td:first-child, .el-table--border .el-table__footer-wrapper tr:first-child th:first-child{
-  border-left:1px solid #ddd !important;
+.el-table--group .el-table__inner-wrapper tr:first-child td:first-child,
+.el-table--group .el-table__inner-wrapper tr:first-child th:first-child,
+.el-table--group .el-table__footer-wrapper tr:first-child td:first-child,
+.el-table--group .el-table__footer-wrapper tr:first-child th:first-child,
+.el-table--border .el-table__inner-wrapper tr:first-child td:first-child,
+.el-table--border .el-table__inner-wrapper tr:first-child th:first-child,
+.el-table--border .el-table__footer-wrapper tr:first-child td:first-child,
+.el-table--border .el-table__footer-wrapper tr:first-child th:first-child {
+  border-left: 1px solid #ddd !important;
 }
 }
-.el-input__wrapper{
+.el-input__wrapper {
   box-shadow: 0 0 0 1px #ddd inset !important;
   box-shadow: 0 0 0 1px #ddd inset !important;
 }
 }
 
 
-.el-table.el-table--small .el-table__body{
-  font-size:13px;
+.el-table.el-table--small .el-table__body {
+  font-size: 13px;
 }
 }
 
 
-
 #app {
 #app {
-  .el-table__body{
-    .el-table__row.current-row .el-table__cell{
-      background-color:#A1D6FF;
+  .el-table__body {
+    .el-table__row.current-row .el-table__cell {
+      background-color: #a1d6ff;
     }
     }
-    .el-table__row:not(.current-row,.el-table__row--striped,.top-three-row,.last-three-row) .el-table__cell{
+    .el-table__row:not(.current-row, .el-table__row--striped, .top-three-row, .last-three-row) .el-table__cell {
       background-color: #fff;
       background-color: #fff;
     }
     }
   }
   }
 }
 }
-body{
-  .el-message{
-    z-index:10001 !important;
-    padding:30px 35px;
-    *{
-      font-size:22px !important;
+body {
+  .el-message {
+    z-index: 10001 !important;
+    padding: 30px 35px;
+    * {
+      font-size: 22px !important;
     }
     }
   }
   }
-  .el-loading-mask{
-    z-index:1200 !important;
+  .el-loading-mask {
+    z-index: 1200 !important;
   }
   }
-  .el-loading-parent--relative{
-    pointer-events:	auto !important;
+  .el-loading-parent--relative {
+    pointer-events: auto !important;
   }
   }
 }
 }
 
 
@@ -253,77 +261,74 @@ body{
 //   background-color: #A1D6FF;
 //   background-color: #A1D6FF;
 // }
 // }
 
 
-
-
 .keybord-dialog {
 .keybord-dialog {
   .el-dialog__header {
   .el-dialog__header {
     padding: 0 10px !important;
     padding: 0 10px !important;
   }
   }
-  .el-dialog__body{
-    padding:10px !important;
-    height:calc(100% - 104px);
+  .el-dialog__body {
+    padding: 10px !important;
+    height: calc(100% - 104px);
   }
   }
-  .el-dialog__footer{
-    padding:10px !important;
-
+  .el-dialog__footer {
+    padding: 10px !important;
   }
   }
 }
 }
 
 
 .el-table-v2 {
 .el-table-v2 {
-  .el-table-v2__header-row{
+  .el-table-v2__header-row {
     // border-bottom:1px solid #ddd !important;
     // border-bottom:1px solid #ddd !important;
     // border-top:1px solid #ddd !important;
     // border-top:1px solid #ddd !important;
-    border-bottom:none;
-    .el-table-v2__header-cell{
-      border-left:1px solid #ddd;
-      border-top:1px solid #ddd;
-      border-bottom:1px solid #ddd;
-      &:last-child{
-        position:relative;
-        border-right:1px solid #ddd;
+    border-bottom: none;
+    .el-table-v2__header-cell {
+      border-left: 1px solid #ddd;
+      border-top: 1px solid #ddd;
+      border-bottom: 1px solid #ddd;
+      &:last-child {
+        position: relative;
+        border-right: 1px solid #ddd;
       }
       }
     }
     }
   }
   }
-  .el-table-v2__row{
-    border-bottom:1px solid #ddd !important;
-    .el-table-v2__row-cell{
-      border-left:1px solid #ddd;
-      &:last-child{
-        border-right:1px solid #ddd;
+  .el-table-v2__row {
+    border-bottom: 1px solid #ddd !important;
+    .el-table-v2__row-cell {
+      border-left: 1px solid #ddd;
+      &:last-child {
+        border-right: 1px solid #ddd;
       }
       }
     }
     }
   }
   }
   // .el-empty{
   // .el-empty{
   //   display:none;
   //   display:none;
   // }
   // }
-  .el-table-v2__empty{
-    height:60px;
-    .el-empty{
-      display:none;
+  .el-table-v2__empty {
+    height: 60px;
+    .el-empty {
+      display: none;
     }
     }
-    border-left:1px solid #ddd;
-    &:before{
-      content:'';
-      width:1px;
-      height:100%;
-      background:#ddd;
-      position:absolute;
-      right:1px;
+    border-left: 1px solid #ddd;
+    &:before {
+      content: '';
+      width: 1px;
+      height: 100%;
+      background: #ddd;
+      position: absolute;
+      right: 1px;
     }
     }
-    &:after{
-      content:'';
-      height:1px;
-      width:calc(100% - 1px);
-      background:#ddd;
-      position:absolute;
-      bottom:0;
+    &:after {
+      content: '';
+      height: 1px;
+      width: calc(100% - 1px);
+      background: #ddd;
+      position: absolute;
+      bottom: 0;
     }
     }
   }
   }
-  .el-table-v2__header-row{
-    font-size:12px;
+  .el-table-v2__header-row {
+    font-size: 12px;
   }
   }
-  .el-table-v2__row-cell{
-    color:#666;
-    font-size:13px;
+  .el-table-v2__row-cell {
+    color: #666;
+    font-size: 13px;
   }
   }
-}
+}

+ 72 - 0
src/components/common/customDialog.vue

@@ -0,0 +1,72 @@
+<template>
+  <div v-if="visible" v-dragDialog="resizeKey" class="custom-dialog" :class="[resizeKey ? resizeKey : '']">
+    <div class="d-head">
+      <span>{{ $props.title }}</span>
+      <div class="head-btn-box flex justify-center items-center" @click="closeDialog">
+        <el-icon><close /></el-icon>
+      </div>
+    </div>
+    <div class="d-body">
+      <slot></slot>
+    </div>
+    <!-- <div class="preview-foot"></div> -->
+  </div>
+</template>
+
+<script setup lang="ts" name="CustomDialog">
+// import BaseDialog from '../element/BaseDialog.vue'
+import useVModel from '@/hooks/useVModel'
+import { ref, watch } from 'vue'
+import { Close } from '@element-plus/icons-vue'
+import { ElIcon } from 'element-plus'
+
+const showDialog = ref(true)
+
+const props = defineProps<{
+  title?: string
+  modelValue: boolean
+  resizeKey?: string
+}>()
+const visible = useVModel(props)
+const closeDialog = () => {
+  visible.value = false
+}
+</script>
+
+<style scoped lang="scss">
+.custom-dialog {
+  display: flex;
+  flex-direction: column;
+  position: fixed;
+  z-index: 2666;
+  border-radius: 6px;
+  box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.04), 0px 8px 20px rgba(0, 0, 0, 0.08);
+  .d-head {
+    background-color: #f8f8f8;
+    border-radius: 6px 6px 0 0;
+    color: #333;
+    font-size: 14px;
+    height: 44px;
+    line-height: 44px;
+    padding: 0 10px;
+    position: relative;
+    .head-btn-box {
+      position: absolute;
+      right: 0;
+      top: 0;
+      width: 44px;
+      height: 44px;
+      z-index: 1;
+      cursor: pointer;
+      &:hover {
+        :deep(i) {
+          color: $color--primary;
+        }
+      }
+    }
+  }
+  .d-body {
+    height: calc(100% - 44px);
+  }
+}
+</style>

+ 1 - 1
src/components/shared/ScoringPanelItem.vue

@@ -30,7 +30,7 @@
       <toggle-dialog-render>
       <toggle-dialog-render>
         <div class="flex items-center">
         <div class="flex items-center">
           <div class="flex items-center score-result" :class="{ 'score-valid-fail': scoreValidFail }">
           <div class="flex items-center score-result" :class="{ 'score-valid-fail': scoreValidFail }">
-            <div class="flex-1 text-center">给分</div>
+            <div class="flex-1 text-center" style="font-size: 12px">给分</div>
             <input
             <input
               ref="refInput1"
               ref="refInput1"
               class="flex-1 text-center score-input score-num"
               class="flex-1 text-center score-input score-num"

+ 2 - 2
src/components/shared/message/MessageHeadList.vue

@@ -10,7 +10,7 @@
       >
       >
         <div class="flex items-center m-b-base message-title">
         <div class="flex items-center m-b-base message-title">
           <div class="message-send-user">{{ message.sendUserName }}</div>
           <div class="message-send-user">{{ message.sendUserName }}</div>
-          <div class="m-l-auto message-send-time">{{ dayjs(message.sendTime).format('HH:mm') }}</div>
+          <div class="m-l-auto message-send-time">{{ dayjs(message.sendTime).format('MM-DD HH:mm') }}</div>
         </div>
         </div>
 
 
         <pre class="message-content">{{ transHtmlContent(message.content) }}</pre>
         <pre class="message-content">{{ transHtmlContent(message.content) }}</pre>
@@ -82,7 +82,7 @@ watch(
 <style scoped lang="scss">
 <style scoped lang="scss">
 .message-list {
 .message-list {
   width: 260px;
   width: 260px;
-  height: 446px;
+  height: 100%;
   background: #fafafa;
   background: #fafafa;
   box-shadow: 0px 6px 6px 0px rgba(0, 0, 0, 0.1);
   box-shadow: 0px 6px 6px 0px rgba(0, 0, 0, 0.1);
   .none-msg-box {
   .none-msg-box {

+ 12 - 8
src/components/shared/message/MessageList.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <div class="flex radius-base overflow-hidden message-list-modal">
+  <div class="flex overflow-hidden message-list-modal">
     <slot></slot>
     <slot></slot>
     <!-- <div class="message-list p-base scroll-y-auto">
     <!-- <div class="message-list p-base scroll-y-auto">
       <template v-if="!!(messageList || []).length">
       <template v-if="!!(messageList || []).length">
@@ -25,16 +25,16 @@
         </div>
         </div>
       </div>
       </div>
     </div> -->
     </div> -->
-    <div class="radius-base message-info-container">
+    <div class="message-info-container">
       <div class="flex direction-column message-info">
       <div class="flex direction-column message-info">
         <div class="flex items-center p-base message-info-header">
         <div class="flex items-center p-base message-info-header">
           <div class="flex items-center send-user">
           <div class="flex items-center send-user">
             <!-- <span class="m-r-mini">发件人</span> -->
             <!-- <span class="m-r-mini">发件人</span> -->
             <span class="radius-base user-name">{{ currentMessage?.sendUserName }}</span>
             <span class="radius-base user-name">{{ currentMessage?.sendUserName }}</span>
           </div>
           </div>
-          <div class="grid pointer m-l-auto close-icon" @click="$emit('close')">
+          <!-- <div class="grid pointer m-l-auto close-icon" @click="$emit('close')">
             <el-icon><close /></el-icon>
             <el-icon><close /></el-icon>
-          </div>
+          </div> -->
         </div>
         </div>
         <div class="flex-1 p-base">
         <div class="flex-1 p-base">
           <!-- <pre
           <!-- <pre
@@ -144,7 +144,7 @@ watch(currentMessage, (newVal: any, oldVal: any) => {
           getMessageHistory({ sendUserId: currentMessage.value.sendUserId }).then(() => {
           getMessageHistory({ sendUserId: currentMessage.value.sendUserId }).then(() => {
             let newV = unref(newVal)
             let newV = unref(newVal)
             let oldV = unref(oldVal)
             let oldV = unref(oldVal)
-            if (newV.id != oldV.id) {
+            if (newV.id != oldV?.id) {
               setTimeout(scrollToBottom, 1)
               setTimeout(scrollToBottom, 1)
             }
             }
           })
           })
@@ -236,16 +236,19 @@ onUnmounted(() => {
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
 .message-list-modal {
 .message-list-modal {
-  width: 750px;
+  width: 100%;
+  height: 100%;
   background-color: transparent;
   background-color: transparent;
   border-top-left-radius: 0;
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
   border-bottom-left-radius: 0;
+  border-bottom-right-radius: 6px;
   .message-info-container {
   .message-info-container {
     // width: 600px;
     // width: 600px;
     width: calc(100% - 260px);
     width: calc(100% - 260px);
+    height: 100%;
     position: relative;
     position: relative;
     .history-box {
     .history-box {
-      height: 200px;
+      height: 100%;
       border: 1px solid #eee;
       border: 1px solid #eee;
       border-radius: 4px;
       border-radius: 4px;
       padding: 15px;
       padding: 15px;
@@ -267,7 +270,8 @@ onUnmounted(() => {
       }
       }
     }
     }
     .message-info {
     .message-info {
-      height: 446px;
+      // height: 446px;
+      height: 100%;
       background-color: $color--white;
       background-color: $color--white;
       .message-info-header {
       .message-info-header {
         border-bottom: $OnePixelLine;
         border-bottom: $OnePixelLine;

+ 10 - 6
src/components/shared/message/MessageSend.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <div class="flex radius-base overflow-hidden message-list-modal">
+  <div class="flex overflow-hidden message-list-modal">
     <template v-if="!!replyUserId">
     <template v-if="!!replyUserId">
       <slot></slot>
       <slot></slot>
     </template>
     </template>
@@ -32,9 +32,9 @@
               </el-button> -->
               </el-button> -->
             </span>
             </span>
           </div>
           </div>
-          <div class="grid pointer m-l-auto close-icon" @click="$emit('close')">
+          <!-- <div class="grid pointer m-l-auto close-icon" @click="$emit('close')">
             <el-icon><close /></el-icon>
             <el-icon><close /></el-icon>
-          </div>
+          </div> -->
         </div>
         </div>
         <div class="flex-1 p-base overflow-hidden">
         <div class="flex-1 p-base overflow-hidden">
           <div class="full-h radius-base p-base message-info-content">
           <div class="full-h radius-base p-base message-info-content">
@@ -304,12 +304,16 @@ const toggleHistory = () => {
 <style scoped lang="scss">
 <style scoped lang="scss">
 .message-list-modal {
 .message-list-modal {
   background-color: transparent;
   background-color: transparent;
-  width: 750px;
+  width: 100%;
+  height: 100%;
   border-top-left-radius: 0;
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
   border-bottom-left-radius: 0;
+  border-bottom-right-radius: 6px;
+
   .tree-box {
   .tree-box {
     width: 260px;
     width: 260px;
-    height: 446px;
+    // height: 446px;
+    height: 100%;
     ::v-deep(.el-tree) {
     ::v-deep(.el-tree) {
       min-height: 100%;
       min-height: 100%;
       .is-online,
       .is-online,
@@ -354,7 +358,7 @@ const toggleHistory = () => {
       width: 750px;
       width: 750px;
     }
     }
     .message-info {
     .message-info {
-      height: 446px;
+      height: 100%;
       background-color: $color--white;
       background-color: $color--white;
       .message-info-header {
       .message-info-header {
         border-bottom: $OnePixelLine;
         border-bottom: $OnePixelLine;

+ 54 - 39
src/components/shared/message/MessageWindow.vue

@@ -1,36 +1,38 @@
 <template>
 <template>
-  <base-dialog v-model="visible" unless canoverflow :footer="false" class="message-dialog" destroy-on-close>
-    <div class="left-tabs-box">
-      <!-- <div class="left-tab-item" :class="{ active: modalType === 'view' }" @click="modalType = 'view'">消息</div>
-      <div class="left-tab-item" :class="{ active: modalType === 'send' }" @click="modalType = 'send'">联系人</div> -->
-      <div class="left-tab-item" :class="{ active: modalType === 'view' }" @click="modalType = 'view'">
-        <img v-if="modalType === 'view'" src="../../../assets/images/tab_xx_active.png" />
-        <img v-else src="../../../assets/images/tab_xx.png" />
-        <p>消息</p>
+  <custom-dialog v-model="visible" title="我的消息" resize-key="can-message-drag">
+    <div class="msg-wrap">
+      <div class="left-tabs-box">
+        <div class="left-tab-item" :class="{ active: modalType === 'view' }" @click="modalType = 'view'">
+          <img v-if="modalType === 'view'" src="../../../assets/images/tab_xx_active.png" />
+          <img v-else src="../../../assets/images/tab_xx.png" />
+          <p>消息</p>
+        </div>
+        <div class="left-tab-item" :class="{ active: modalType === 'send' }" @click="modalType = 'send'">
+          <img v-if="modalType === 'send'" src="../../../assets/images/tab_lxr_active.png" />
+          <img v-else src="../../../assets/images/tab_lxr.png" />
+          <p>联系人</p>
+        </div>
       </div>
       </div>
-      <div class="left-tab-item" :class="{ active: modalType === 'send' }" @click="modalType = 'send'">
-        <img v-if="modalType === 'send'" src="../../../assets/images/tab_lxr_active.png" />
-        <img v-else src="../../../assets/images/tab_lxr.png" />
-        <p>联系人</p>
+      <div class="right-main-box">
+        <component
+          :is="MessageWindowContent"
+          :reply-user-id="replyUserId"
+          :paper-path="props.paperPath"
+          :reply-user-name="replyUserName"
+          @close="onClose"
+          @change-type="onChangeType"
+          @reply="onReply"
+        >
+          <message-head-list
+            :mode="props.type"
+            :cur-msg="currentMessage"
+            :reply-user-id="replyUserId"
+            @left-msg-click="leftMsgClick"
+          ></message-head-list>
+        </component>
       </div>
       </div>
     </div>
     </div>
-    <component
-      :is="MessageWindowContent"
-      :reply-user-id="replyUserId"
-      :paper-path="props.paperPath"
-      :reply-user-name="replyUserName"
-      @close="onClose"
-      @change-type="onChangeType"
-      @reply="onReply"
-    >
-      <message-head-list
-        :mode="props.type"
-        :cur-msg="currentMessage"
-        :reply-user-id="replyUserId"
-        @left-msg-click="leftMsgClick"
-      ></message-head-list>
-    </component>
-  </base-dialog>
+  </custom-dialog>
 </template>
 </template>
 
 
 <script setup lang="ts" name="MessageWindow">
 <script setup lang="ts" name="MessageWindow">
@@ -42,6 +44,7 @@ import MessageHistory from '@/components/shared/message/MessageList.vue'
 import MessageSend from '@/components/shared/message/MessageSend.vue'
 import MessageSend from '@/components/shared/message/MessageSend.vue'
 import MessageHeadList from './MessageHeadList.vue'
 import MessageHeadList from './MessageHeadList.vue'
 import bus from '@/utils/bus'
 import bus from '@/utils/bus'
+import CustomDialog from '@/components/common/customDialog.vue'
 
 
 type ModalType = 'view' | 'send'
 type ModalType = 'view' | 'send'
 const replyUserName = ref<string | null>('')
 const replyUserName = ref<string | null>('')
@@ -94,20 +97,32 @@ const leftMsgClick = (msg: any) => {
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
-.message-dialog {
-  background-color: transparent;
-  box-shadow: none;
+.msg-wrap {
+  width: 100%;
+  height: 100%;
   position: relative;
   position: relative;
+  .el-dialog__header {
+    border-radius: 0 6px 0 0 !important;
+  }
+  .right-main-box {
+    margin-left: 60px;
+    height: 100%;
+  }
   .left-tabs-box {
   .left-tabs-box {
-    position: absolute;
+    // position: absolute;
+    // width: 60px;
+    // height: 100%;
+    // left: -60px;
+    // top: 0;
+    // background-color: #fff;
+    // padding: 10px 5px;
+    // border-top-left-radius: 6px;
+    // border-bottom-left-radius: 6px;
     width: 60px;
     width: 60px;
     height: 100%;
     height: 100%;
-    left: -60px;
-    top: 0;
-    background-color: #fff;
     padding: 10px 5px;
     padding: 10px 5px;
-    border-top-left-radius: 6px;
-    border-bottom-left-radius: 6px;
+    background-color: #fff;
+    float: left;
     .left-tab-item {
     .left-tab-item {
       width: 50px;
       width: 50px;
       height: 50px;
       height: 50px;
@@ -116,7 +131,7 @@ const leftMsgClick = (msg: any) => {
       cursor: pointer;
       cursor: pointer;
       margin-top: 10px;
       margin-top: 10px;
       color: #666;
       color: #666;
-      font-size: 8px;
+      font-size: 12px;
       padding-top: 8px;
       padding-top: 8px;
       &:hover {
       &:hover {
         background-color: #f3f3f3;
         background-color: #f3f3f3;

+ 201 - 0
src/directives/dragDialog.ts

@@ -0,0 +1,201 @@
+function resetPosition(key: any) {
+  const keyboardResize = localStorage.getItem(key)
+  if (keyboardResize) {
+    const obj = JSON.parse(keyboardResize)
+    if (parseInt(obj.left) < 0) {
+      obj.left = 0
+    }
+    if (parseInt(obj.top) < 0) {
+      obj.top = 0
+    }
+    localStorage.setItem(key, JSON.stringify(obj))
+  }
+}
+export const dragDialog = {
+  mounted(el: any, binding: any, vnode: any) {
+    const bValue = binding.value
+    const localKeyMap: any = {
+      positions: {
+        'can-message-drag': 'cet-message-positions',
+      },
+      resize: {
+        'can-message-drag': 'cet-message-resize',
+      },
+    }
+    const resizeEvent = new CustomEvent('drag-resize', {
+      detail: '尺寸变化',
+      bubbles: false,
+    })
+    resetPosition(localKeyMap.positions[bValue])
+
+    const keyboardPositions = localStorage.getItem(localKeyMap.positions[bValue])
+    const keyboardResize = localStorage.getItem(localKeyMap.resize[bValue])
+    // 初始化不最大化
+    el.fullscreen = false
+    // 弹框可拉伸最小宽高
+    const minWidth = 810
+    const minHeight = 490
+    const maxHeight = window.innerHeight
+    const winHeight = window.innerHeight
+    const winWidth = window.innerWidth
+
+    // 弹窗
+    const dragDom: any = document.querySelector('.' + binding.value)
+    const dialogHeaderEl = dragDom.querySelector('.d-head')
+    const resizeEl = document.createElement('div')
+
+    el.style.overflow = 'initial'
+    dragDom.className += ' el-drag-dialog'
+    // 给弹窗加上overflow auto;不然缩小时框内的标签可能超出dialog;
+    // dragDom.style.overflow = 'auto'
+    dragDom.style.background = '#fff'
+
+    if (keyboardPositions) {
+      dragDom.style.left = JSON.parse(keyboardPositions).left || 0
+      dragDom.style.top = JSON.parse(keyboardPositions).top || 0
+    } else {
+      dragDom.style.left = winWidth / 2 - minWidth / 2
+      dragDom.style.top = winHeight / 2 - minHeight / 2
+    }
+    if (keyboardResize) {
+      const localW = JSON.parse(keyboardResize).width
+      const localH = JSON.parse(keyboardResize).height
+      localW && (dragDom.style.width = localW)
+      localH && (dragDom.style.height = localH)
+    } else {
+      dragDom.style.width = minWidth + 'px'
+      dragDom.style.height = minHeight + 'px'
+    }
+
+    // 清除选择头部文字效果
+    // eslint-disable-next-line no-new-func
+    dialogHeaderEl.onselectstart = new Function('return false')
+    // 头部加上可拖动cursor
+    dialogHeaderEl.style.cursor = 'move'
+
+    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
+
+    // 头部插入最大化最小化元素
+    const maxMin = document.createElement('button')
+    maxMin.className += ' el-dialog__headerbtn el-dialog__minmax'
+    maxMin.style.right = '40px'
+    maxMin.style.color = '#ffffff'
+    maxMin.title = el.fullscreen ? '还原' : '最大化'
+    maxMin.innerHTML =
+      '<i class=' +
+      (el.fullscreen ? '"el-icon-crop"' : '"el-icon-full-screen"') +
+      ' onMouseOver="this.style.color=\'#409EFF\'" onMouseOut="this.style.color=\'inherit\'"></i>'
+    // dialogHeaderEl.insertBefore(maxMin, dialogHeaderEl.childNodes[1])
+    const moveDown = (e: any) => {
+      // 鼠标按下,计算当前元素距离可视区的距离
+      const disX = e.clientX - dialogHeaderEl.offsetLeft
+      const disY = e.clientY - dialogHeaderEl.offsetTop
+
+      // 获取到的值带px 正则匹配替换
+      let styL: any, styT: any
+
+      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+      if (sty.left.includes('%')) {
+        styL = +document.body.clientWidth * (+sty.left.replace(/\\%/g, '') / 100)
+        styT = +document.body.clientHeight * (+sty.top.replace(/\\%/g, '') / 100)
+      } else {
+        styL = +sty.left.replace(/\px/g, '')
+        styT = +sty.top.replace(/\px/g, '')
+      }
+
+      document.onmousemove = function (e) {
+        // 通过事件委托,计算移动的距离
+        const l = e.clientX - disX
+        const t = e.clientY - disY
+
+        // 移动当前元素
+        dragDom.style.left = `${
+          l + styL < 0 ? 0 : l + styL > winWidth - dragDom.clientWidth ? winWidth - dragDom.clientWidth : l + styL
+        }px`
+        dragDom.style.top = `${
+          t + styT < 0 ? 0 : t + styT > winHeight - dragDom.clientHeight ? winHeight - dragDom.clientHeight : t + styT
+        }px`
+
+        // 将此时的位置传出去
+        // binding.value({x:e.pageX,y:e.pageY})
+      }
+
+      document.onmouseup = function (e) {
+        document.onmousemove = null
+        document.onmouseup = null
+        console.log('dragDom.style.left:', dragDom.style.left, dragDom.style.top)
+        localStorage.setItem(
+          localKeyMap.positions[bValue],
+          JSON.stringify({
+            left: dragDom.style.left || 0,
+            top: dragDom.style.top || 0,
+          })
+        )
+      }
+    }
+    dialogHeaderEl.onmousedown = moveDown
+
+    // 拉伸
+    dragDom.appendChild(resizeEl)
+    // 在弹窗右下角加上一个10-10px的控制块
+    resizeEl.style.cursor = 'se-resize'
+    resizeEl.style.position = 'absolute'
+    resizeEl.style.height = '10px'
+    resizeEl.style.width = '10px'
+    resizeEl.style.border = '3px solid transparent'
+    resizeEl.style.borderRightColor = '#333'
+    resizeEl.style.borderBottomColor = '#333'
+    resizeEl.style.right = '0px'
+    resizeEl.style.bottom = '0px'
+    resizeEl.style.zIndex = '99'
+    // 鼠标拉伸弹窗
+
+    resizeEl.onmousedown = (e) => {
+      // 记录初始x位置
+      const clientX = e.clientX
+      // 鼠标按下,计算当前元素距离可视区的距离
+      const disX = e.clientX - resizeEl.offsetLeft
+      const disY = e.clientY - resizeEl.offsetTop
+
+      // let maxWW = 0
+      document.onmousemove = function (e) {
+        e.preventDefault() // 移动时禁用默认事件
+        // 通过事件委托,计算移动的距离
+        // const x = e.clientX - disX + (e.clientX - clientX) // 这里 由于elementUI的dialog控制居中的,所以水平拉伸效果是双倍
+        const x = e.clientX - disX + (e.clientX - clientX) / 5
+        const y = e.clientY - disY
+
+        if (dragDom.clientWidth + parseFloat(dragDom.style.left) >= winWidth - 20 && e.clientX > winWidth - 20) {
+        } else {
+          dragDom.style.width = x > minWidth ? `${x}px` : minWidth + 'px'
+        }
+
+        if (dragDom.clientHeight + parseFloat(dragDom.style.top) >= winHeight - 20 && e.clientY > winHeight - 20) {
+        } else {
+          dragDom.style.height = y > minHeight ? `${y}px` : minHeight + 'px'
+        }
+      }
+      // 拉伸结束
+      document.onmouseup = function (e) {
+        document.onmousemove = null
+        document.onmouseup = null
+        if (parseFloat(dragDom.style.width) > winWidth - 20) {
+          dragDom.style.width = winWidth - 20 + 'px'
+        }
+        if (parseFloat(dragDom.style.height) > winHeight - 20) {
+          dragDom.style.height = winHeight - 20 + 'px'
+        }
+        localStorage.setItem(
+          localKeyMap.resize[bValue],
+          JSON.stringify({
+            width: dragDom.style.width || 'auto',
+            height: dragDom.style.height || 'auto',
+          })
+        )
+
+        el.dispatchEvent(resizeEvent)
+      }
+    }
+  },
+}

+ 2 - 0
src/directives/index.ts

@@ -2,6 +2,7 @@ import { dialogResize } from './dialogResize'
 import { dialogResizeImg } from './dialogResizeImg'
 import { dialogResizeImg } from './dialogResizeImg'
 import { dialogResizeStandard } from './dialogResizeStandard'
 import { dialogResizeStandard } from './dialogResizeStandard'
 import { customDialogResizeImg } from './customDialogResizeImg'
 import { customDialogResizeImg } from './customDialogResizeImg'
+import { dragDialog } from './dragDialog'
 import { permBtn } from './permBtn'
 import { permBtn } from './permBtn'
 
 
 export function setupDirectives(app: any) {
 export function setupDirectives(app: any) {
@@ -9,5 +10,6 @@ export function setupDirectives(app: any) {
   app.directive('dialogResizeImg', dialogResizeImg)
   app.directive('dialogResizeImg', dialogResizeImg)
   app.directive('customDialogResizeImg', customDialogResizeImg)
   app.directive('customDialogResizeImg', customDialogResizeImg)
   app.directive('dialogResizeStandard', dialogResizeStandard)
   app.directive('dialogResizeStandard', dialogResizeStandard)
+  app.directive('dragDialog', dragDialog)
   app.directive('permBtn', permBtn)
   app.directive('permBtn', permBtn)
 }
 }

+ 3 - 3
src/modules/admin-exam/edit-exam/index.vue

@@ -43,7 +43,7 @@ const initModel: ExtractApiParams<'saveExamInfo'> = {
   userNameCollect: true,
   userNameCollect: true,
   enable: true,
   enable: true,
   rejectReason: true,
   rejectReason: true,
-  scoreEffective: 'FINAL_MARK',
+  // scoreEffective: 'FINAL_MARK',
 }
 }
 
 
 const model = reactive<ExtractApiParams<'saveExamInfo'>>(initModel)
 const model = reactive<ExtractApiParams<'saveExamInfo'>>(initModel)
@@ -69,7 +69,7 @@ const rules: EpFormRules = {
     { type: 'number', min: 1, max: 1440, message: '任务回收时间限制1-1440分钟' },
     { type: 'number', min: 1, max: 1440, message: '任务回收时间限制1-1440分钟' },
   ],
   ],
   userNameCollect: [{ required: true, message: '是否收集用户姓名' }],
   userNameCollect: [{ required: true, message: '是否收集用户姓名' }],
-  scoreEffective: [{ required: true, message: '分析计算取值方式' }],
+  // scoreEffective: [{ required: true, message: '分析计算取值方式' }],
   enable: [{ required: true, message: '是否启用考试' }],
   enable: [{ required: true, message: '是否启用考试' }],
 }
 }
 
 
@@ -99,7 +99,7 @@ const items: EpFormItem[] = [
     slot: { stepStrictly: true, step: 1 },
     slot: { stepStrictly: true, step: 1 },
   }),
   }),
   span8({ label: '用户姓名收集', slotType: 'select', prop: 'userNameCollect', slot: { options: TrueOrFalse } }),
   span8({ label: '用户姓名收集', slotType: 'select', prop: 'userNameCollect', slot: { options: TrueOrFalse } }),
-  span8({ label: '分析计算取值', slotType: 'select', prop: 'scoreEffective', slot: { options: scoreEffectiveOption } }),
+  // span8({ label: '分析计算取值', slotType: 'select', prop: 'scoreEffective', slot: { options: scoreEffectiveOption } }),
   span8({ label: '状态', slotType: 'select', prop: 'enable', slot: { options: StatusMap } }),
   span8({ label: '状态', slotType: 'select', prop: 'enable', slot: { options: StatusMap } }),
 ]
 ]
 
 

+ 14 - 4
src/modules/admin-role/setting/index.vue

@@ -14,6 +14,7 @@
           node-key="id"
           node-key="id"
           :data="myTreeData || []"
           :data="myTreeData || []"
           :props="treeProps"
           :props="treeProps"
+          :filter-node-method="filterTree"
           :render-content="renderContent"
           :render-content="renderContent"
           @check-change="onCheckChange"
           @check-change="onCheckChange"
         >
         >
@@ -97,9 +98,17 @@ watch(
   },
   },
   { immediate: true }
   { immediate: true }
 )
 )
+const filterTree = (value: string, data: any) => {
+  return !data.originId
+}
 const myTreeData = ref<any[]>([])
 const myTreeData = ref<any[]>([])
-const treeProps = ref({
+const treeProps = {
   label: 'name',
   label: 'name',
+}
+watch(myTreeData, () => {
+  nextTick(() => {
+    treeRef?.value?.filter('')
+  })
 })
 })
 const renderContent = (h: any, { node, data, store }: any) => {
 const renderContent = (h: any, { node, data, store }: any) => {
   if (data.originId) {
   if (data.originId) {
@@ -123,6 +132,7 @@ const renderContent = (h: any, { node, data, store }: any) => {
   }
   }
   return data.name
   return data.name
 }
 }
+
 const getPIds = (treeData: any, pIds: any = []) => {
 const getPIds = (treeData: any, pIds: any = []) => {
   for (let i = 0; i < treeData.length; i++) {
   for (let i = 0; i < treeData.length; i++) {
     let item: any = treeData[i]
     let item: any = treeData[i]
@@ -146,12 +156,12 @@ watch(privilege, () => {
     let node = data[i]
     let node = data[i]
     if (ROLE_BUTTON_CODES.includes(node.code)) {
     if (ROLE_BUTTON_CODES.includes(node.code)) {
       let pNode: any = data.find((v: any) => v.id == node.parentId) || {}
       let pNode: any = data.find((v: any) => v.id == node.parentId) || {}
-      if (!hasPermBtnPageNodes.find((v: any) => v.code == node.code + '_in_page')) {
+      if (!hasPermBtnPageNodes.find((v: any) => v.code == pNode.code + '_in_page')) {
         let id = createNum(-100000, -10000)
         let id = createNum(-100000, -10000)
         hasPermBtnPageNodes.push(
         hasPermBtnPageNodes.push(
           Object.assign({}, node, {
           Object.assign({}, node, {
-            code: node.code + '_in_page',
-            nodeCode: node.nodeCode + '_in_page',
+            code: pNode.code + '_in_page',
+            nodeCode: pNode.nodeCode + '_in_page',
             name: '页面',
             name: '页面',
             hasPrivilege: !!pNode.hasPrivilege,
             hasPrivilege: !!pNode.hasPrivilege,
             id,
             id,

+ 8 - 3
src/modules/admin-user/manage/index.vue

@@ -69,7 +69,7 @@
           >
           >
             <el-button size="small" custom-1 class="m-l-base" :loading="importUsersLoading">导入姓名</el-button>
             <el-button size="small" custom-1 class="m-l-base" :loading="importUsersLoading">导入姓名</el-button>
           </el-upload>
           </el-upload>
-          <el-link class="m-l-base" type="primary" @click="downTpl">导入姓名模板下载</el-link>
+          <!-- <el-link class="m-l-base" type="primary" @click="downTpl">导入姓名模板下载</el-link> -->
         </div>
         </div>
         <base-table
         <base-table
           ref="tableRef"
           ref="tableRef"
@@ -171,8 +171,13 @@ watch(
   fileList,
   fileList,
   () => {
   () => {
     importUsersModel.file = fileList.value?.[0]?.raw
     importUsersModel.file = fileList.value?.[0]?.raw
-    importUsersFile(importUsersModel).then(() => {
-      ElMessage.success('导入用户成功')
+    importUsersFile(importUsersModel).then((res: any) => {
+      // ElMessage.success('导入用户成功')
+      if (!res.hasError) {
+        ElMessage.success('导入用户成功')
+      } else {
+        ElMessage.error('导入失败,详见“任务信息报告”')
+      }
     })
     })
   },
   },
   { deep: true }
   { deep: true }

+ 1 - 1
src/modules/analysis/marking-progress/components/GroupProgress.vue

@@ -4,7 +4,7 @@
       <template #form-item-operation>
       <template #form-item-operation>
         <el-button class="m-l-base" type="primary" @click="onSearch">查询</el-button>
         <el-button class="m-l-base" type="primary" @click="onSearch">查询</el-button>
         <el-button
         <el-button
-          v-permBtn="'analysis-marking_progress_export2'"
+          v-permBtn="'analysis-marking_progress_export'"
           :loading="exportLoading"
           :loading="exportLoading"
           type="primary"
           type="primary"
           custom-1
           custom-1

+ 7 - 3
src/modules/analysis/personnel-statistics/components/StatisticsPersonnel.vue

@@ -69,7 +69,7 @@
 
 
 <script setup lang="tsx" name="StatisticsPersonnel">
 <script setup lang="tsx" name="StatisticsPersonnel">
 /** 人员数据统计-按人员展开 */
 /** 人员数据统计-按人员展开 */
-import { ref, inject, computed, watch, nextTick } from 'vue'
+import { ref, inject, computed, watch, nextTick, unref } from 'vue'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { ElButton, ElPopover, ElMenu, ElMenuItem } from 'element-plus'
 import { ElButton, ElPopover, ElMenu, ElMenuItem } from 'element-plus'
 import VueECharts from 'vue-echarts'
 import VueECharts from 'vue-echarts'
@@ -126,7 +126,7 @@ const columns = computed(() => {
       prop: 'xyRelate',
       prop: 'xyRelate',
       minWidth: 90,
       minWidth: 90,
       formatter(row: any) {
       formatter(row: any) {
-        let r = props.result || []
+        let r = unref(props.result) || []
         const { markingGroupNumber } = row
         const { markingGroupNumber } = row
         let compareTarget = r.find((item: any) => item.markingGroupNumber == markingGroupNumber)
         let compareTarget = r.find((item: any) => item.markingGroupNumber == markingGroupNumber)
         if (compareTarget && markingGroupNumber) {
         if (compareTarget && markingGroupNumber) {
@@ -169,9 +169,13 @@ const columns = computed(() => {
       prop: 'online',
       prop: 'online',
       minWidth: 72,
       minWidth: 72,
       formatter(row: any) {
       formatter(row: any) {
+        console.log(11, row, unref(props.result))
         if (row.markingGroupNumber === 0) {
         if (row.markingGroupNumber === 0) {
-          let total = (props.result || [])
+          let total = (unref(props.result) || [])
             .filter((item: any) => item.markingGroupNumber !== 0)
             .filter((item: any) => item.markingGroupNumber !== 0)
+            .reduce((ar: any, item: any) => {
+              return [...ar, ...(item.markerDetails || [])]
+            }, [])
             .reduce((num: number, item: any) => {
             .reduce((num: number, item: any) => {
               return num + (item.online ? 1 : 0)
               return num + (item.online ? 1 : 0)
             }, 0)
             }, 0)

+ 4 - 2
src/modules/analysis/personnel-statistics/index.vue

@@ -67,6 +67,7 @@ import StatisticsPersonnel from './components/StatisticsPersonnel.vue'
 import { Setting } from '@element-plus/icons-vue'
 import { Setting } from '@element-plus/icons-vue'
 import BaseDialog from '@/components/element/BaseDialog.vue'
 import BaseDialog from '@/components/element/BaseDialog.vue'
 import ConfirmButton from '@/components/common/ConfirmButton.vue'
 import ConfirmButton from '@/components/common/ConfirmButton.vue'
+import { cloneDeep } from 'lodash-es'
 
 
 import type { ExtractApiResponse } from '@/api/api'
 import type { ExtractApiResponse } from '@/api/api'
 
 
@@ -128,8 +129,9 @@ onMounted(() => {
 const { fetch, result, loading } = useFetch('getStatisticsByGroup')
 const { fetch, result, loading } = useFetch('getStatisticsByGroup')
 
 
 const data = computed<ExtractApiResponse<'getStatisticsByGroup'>>(() => {
 const data = computed<ExtractApiResponse<'getStatisticsByGroup'>>(() => {
-  let groupList = result?.value?.slice(0) || []
-  groupList = groupList
+  let arr = cloneDeep(result?.value || [])
+  // let groupList = result?.value?.slice(0) || []
+  let groupList = arr
     .map((groupItem: any) => {
     .map((groupItem: any) => {
       if (groupItem.markingGroupNumber === 0) {
       if (groupItem.markingGroupNumber === 0) {
         return groupItem
         return groupItem

+ 1 - 1
src/modules/marking/inquiry/index.vue

@@ -235,7 +235,7 @@ const items = computed<EpFormItem[]>(() => [
     },
     },
   }),
   }),
   SixRow({
   SixRow({
-    label: '分',
+    label: '大题分',
     slotType: 'inputNumber',
     slotType: 'inputNumber',
     prop: 'scoreStart',
     prop: 'scoreStart',
     slot: {
     slot: {

+ 2 - 2
src/plugins/request/index.ts

@@ -47,12 +47,12 @@ request.interceptors.request.use(
     return Promise.reject(error)
     return Promise.reject(error)
   }
   }
 )
 )
-
+const whiteList: any = ['/api/user/name/import']
 request.interceptors.response.use(
 request.interceptors.response.use(
   (response) => {
   (response) => {
     useMainStore().serverTime = new Date(response.headers?.['date']).getTime()
     useMainStore().serverTime = new Date(response.headers?.['date']).getTime()
     useMainStore().diffTime = useMainStore().serverTime - Date.now()
     useMainStore().diffTime = useMainStore().serverTime - Date.now()
-    if (response.data.hasError) {
+    if (response.data.hasError && whiteList.indexOf(response.config.url) == -1) {
       const errMsg = response.data?.failRecords.map((v: any) => [v.msg, v.lineNum].join('--'))?.join('\n')
       const errMsg = response.data?.failRecords.map((v: any) => [v.msg, v.lineNum].join('--'))?.join('\n')
       toastError(errMsg)
       toastError(errMsg)
       return Promise.reject(response.data)
       return Promise.reject(response.data)