Browse Source

工作面、优化模型导入、主风机

hongrunxia 1 year ago
parent
commit
a17081ac48
100 changed files with 2172 additions and 214 deletions
  1. 1 0
      index.html
  2. 0 0
      public/model/glft/cf/dscf_2023-06-02.glb
  3. 0 0
      public/model/glft/cf/dsgd_2023-06-02.glb
  4. 0 0
      public/model/glft/cf/dsmove_2023-06-02.glb
  5. 0 0
      public/model/glft/cf/lmcfSide_2023-06-02.glb
  6. 0 0
      public/model/glft/cf/lmcf_2023-06-02.glb
  7. 0 0
      public/model/glft/cf/zdcf_2023-06-02.glb
  8. 0 0
      public/model/glft/fc/ddFc_2023-06-02.glb
  9. BIN
      public/model/glft/fc/fc.glb
  10. 0 0
      public/model/glft/fc/sdFc_2023-06-02.glb
  11. 0 0
      public/model/glft/fire/chamber_2023-06-02.glb
  12. 0 0
      public/model/glft/fire/grout_2023-06-02.glb
  13. 0 0
      public/model/glft/fire/laneway_2023-06-02.glb
  14. 0 0
      public/model/glft/fire/nitrogen_2023-06-02.glb
  15. 0 0
      public/model/glft/fire/workFace_2023-06-02.glb
  16. 0 0
      public/model/glft/fm/Fm-door_2023-06-02.glb
  17. 0 0
      public/model/glft/fm/Fm-wall_2023-06-02.glb
  18. 0 0
      public/model/glft/fm/Fm-wire_2023-06-02.glb
  19. 0 0
      public/model/glft/fm/fm_2023-06-02.glb
  20. 0 0
      public/model/glft/jbfj/jbfj-fc_2023-06-02.glb
  21. 0 0
      public/model/glft/jbfj/jbfj-fm_2023-06-02.glb
  22. 0 0
      public/model/glft/jbfj/jbfj-hd_2023-06-06.glb
  23. BIN
      public/model/glft/jbfj/jbfj.glb
  24. 0 0
      public/model/glft/yafeng/compressor_2023-06-02.glb
  25. BIN
      public/model/glft/ztfj/bg1_2023-06-02.glb
  26. 0 0
      public/model/glft/ztfj/bg_2023-06-02.glb
  27. 0 0
      public/model/glft/ztfj/dj1_2023-06-02.glb
  28. 0 0
      public/model/glft/ztfj/dj2_2023-06-02.glb
  29. BIN
      public/model/glft/ztfj/dzp_2023-06-02.glb
  30. BIN
      public/model/glft/ztfj/fbm_2023-06-02.glb
  31. BIN
      public/model/glft/ztfj/main.glb
  32. BIN
      public/model/glft/ztfj/ztfj-fc_2023-06-02.glb
  33. BIN
      public/model/glft/ztfj/ztfj.glb
  34. BIN
      public/model/glft/ztfj/ztfj_2023-06-02.glb
  35. 6 0
      src/App.vue
  36. BIN
      src/assets/font/ysbtFont.ttf
  37. 6 0
      src/assets/icons/CO-aveg.svg
  38. 6 0
      src/assets/icons/CO-low.svg
  39. 8 0
      src/assets/icons/CO-top.svg
  40. BIN
      src/assets/images/vent/alarm/1.png
  41. BIN
      src/assets/images/vent/alarm/2.png
  42. BIN
      src/assets/images/vent/alarm/3.png
  43. BIN
      src/assets/images/vent/alarm/4.png
  44. BIN
      src/assets/images/vent/bottom-btn-active.png
  45. BIN
      src/assets/images/vent/bottom-btn.png
  46. BIN
      src/assets/images/vent/home/tab11.png
  47. BIN
      src/assets/images/vent/home/tab21.png
  48. BIN
      src/assets/images/vent/param-bg.png
  49. BIN
      src/assets/images/vent/vent-header1.png
  50. BIN
      src/assets/images/vent/vent-header2.png
  51. BIN
      src/assets/images/vent/wokeFaca-nav.png
  52. BIN
      src/assets/images/vent/workFace-param-bg.png
  53. 11 0
      src/components/Form/src/components/ApiTreeSelect.vue
  54. 89 2
      src/design/vent/comment.less
  55. 1 0
      src/design/vent/index.less
  56. 4 41
      src/design/vent/modal.less
  57. 1 1
      src/hooks/core/useThree copy.ts
  58. 16 18
      src/layouts/default/header/index.less
  59. 8 2
      src/layouts/default/header/index.vue
  60. 3 2
      src/store/modules/threejs.ts
  61. 16 6
      src/utils/threejs/loadGltf.worker.js
  62. 57 36
      src/utils/threejs/main.worker.ts
  63. 93 5
      src/utils/threejs/useThree.ts
  64. 1 4
      src/views/monitor/mynews/index.vue
  65. 1 1
      src/views/monitor/quartz/index.vue
  66. 11 11
      src/views/monitor/quartz/quartz.api.ts
  67. 0 2
      src/views/system/depart/index.vue
  68. 122 0
      src/views/system/menuModal/DataRuleList.vue
  69. 54 0
      src/views/system/menuModal/DataRuleModal.vue
  70. 104 0
      src/views/system/menuModal/MenuDrawer.vue
  71. 205 0
      src/views/system/menuModal/index.vue
  72. 122 0
      src/views/system/menuModal/menu.api.ts
  73. 415 0
      src/views/system/menuModal/menu.data.ts
  74. 1 1
      src/views/system/message/manage/index.vue
  75. 1 1
      src/views/system/message/template/index.vue
  76. 56 8
      src/views/vent/comment/EditRowTable.vue
  77. 22 20
      src/views/vent/comment/components/bottomMenu.vue
  78. 12 5
      src/views/vent/comment/components/customHeader.vue
  79. 2 1
      src/views/vent/comment/threejs/Smoke.ts
  80. 10 4
      src/views/vent/deviceManager/comment/DeviceModal.vue
  81. 9 1
      src/views/vent/deviceManager/comment/NormalTable.vue
  82. 4 5
      src/views/vent/deviceManager/comment/pointTabel/DeviceModalTable.vue
  83. 110 13
      src/views/vent/deviceManager/comment/pointTabel/WorkFacePointTable.vue
  84. 20 5
      src/views/vent/deviceManager/comment/pointTabel/point.api.ts
  85. 78 0
      src/views/vent/deviceManager/comment/warningTabel/DevicePointTable.vue
  86. 175 0
      src/views/vent/deviceManager/comment/warningTabel/index1.vue
  87. 198 0
      src/views/vent/deviceManager/comment/warningTabel/index2.vue
  88. 27 0
      src/views/vent/deviceManager/comment/warningTabel/warning.api.ts
  89. 40 1
      src/views/vent/deviceManager/comment/warningTabel/warning.data.ts
  90. 3 3
      src/views/vent/deviceManager/pointTabel/point.api.ts
  91. 2 4
      src/views/vent/deviceManager/pointTabel/point.data.ts
  92. 2 3
      src/views/vent/deviceManager/tableColumns/tableColumns.api.ts
  93. 11 1
      src/views/vent/deviceManager/tableColumns/tableColumns.data.ts
  94. 1 1
      src/views/vent/monitorManager/chamberMonitor/chamber.api.ts
  95. 1 1
      src/views/vent/monitorManager/chamberMonitor/chamber.data.ts
  96. 11 1
      src/views/vent/monitorManager/chamberMonitor/chamber.threejs.base.ts
  97. 2 1
      src/views/vent/monitorManager/chamberMonitor/chamber.threejs.ts
  98. 1 1
      src/views/vent/monitorManager/chamberMonitor/components/chamberHome.vue
  99. 3 2
      src/views/vent/monitorManager/chamberMonitor/index.vue
  100. 9 0
      src/views/vent/monitorManager/compressor/components/nitrogenHome.vue

+ 1 - 0
index.html

@@ -315,6 +315,7 @@
         </div>
       </div>
     </div>
+    
     <script type="module" src="/src/main.ts"></script>
     <script type="module" src="/src/utils/threejs/main.worker.ts"></script>
   </body>

+ 0 - 0
public/model/glft/cf/dscf.glb → public/model/glft/cf/dscf_2023-06-02.glb


+ 0 - 0
public/model/glft/cf/dsgd.glb → public/model/glft/cf/dsgd_2023-06-02.glb


+ 0 - 0
public/model/glft/cf/dsmove.glb → public/model/glft/cf/dsmove_2023-06-02.glb


+ 0 - 0
public/model/glft/cf/lmcfSide.glb → public/model/glft/cf/lmcfSide_2023-06-02.glb


+ 0 - 0
public/model/glft/cf/lmcf.glb → public/model/glft/cf/lmcf_2023-06-02.glb


+ 0 - 0
public/model/glft/cf/zdcf.glb → public/model/glft/cf/zdcf_2023-06-02.glb


+ 0 - 0
public/model/glft/fc/ddFc.glb → public/model/glft/fc/ddFc_2023-06-02.glb


BIN
public/model/glft/fc/fc.glb


+ 0 - 0
public/model/glft/fc/sdFc.glb → public/model/glft/fc/sdFc_2023-06-02.glb


+ 0 - 0
public/model/glft/fire/chamber.glb → public/model/glft/fire/chamber_2023-06-02.glb


+ 0 - 0
public/model/glft/fire/grout.glb → public/model/glft/fire/grout_2023-06-02.glb


+ 0 - 0
public/model/glft/fire/laneway.glb → public/model/glft/fire/laneway_2023-06-02.glb


+ 0 - 0
public/model/glft/fire/nitrogen.glb → public/model/glft/fire/nitrogen_2023-06-02.glb


+ 0 - 0
public/model/glft/fire/workFace.glb → public/model/glft/fire/workFace_2023-06-02.glb


+ 0 - 0
public/model/glft/fm/Fm-door.glb → public/model/glft/fm/Fm-door_2023-06-02.glb


+ 0 - 0
public/model/glft/fm/Fm-wall.glb → public/model/glft/fm/Fm-wall_2023-06-02.glb


+ 0 - 0
public/model/glft/fm/Fm-wire.glb → public/model/glft/fm/Fm-wire_2023-06-02.glb


+ 0 - 0
public/model/glft/fm/fm.glb → public/model/glft/fm/fm_2023-06-02.glb


+ 0 - 0
public/model/glft/jbfj/jbfj_fc.glb → public/model/glft/jbfj/jbfj-fc_2023-06-02.glb


+ 0 - 0
public/model/glft/jbfj/jbfj_fm.glb → public/model/glft/jbfj/jbfj-fm_2023-06-02.glb


+ 0 - 0
public/model/glft/jbfj/jbfj_hd.glb → public/model/glft/jbfj/jbfj-hd_2023-06-06.glb


BIN
public/model/glft/jbfj/jbfj.glb


+ 0 - 0
public/model/glft/yafeng/compressor.glb → public/model/glft/yafeng/compressor_2023-06-02.glb


BIN
public/model/glft/ztfj/bg1_2023-06-02.glb


+ 0 - 0
public/model/glft/ztfj/bg.glb → public/model/glft/ztfj/bg_2023-06-02.glb


+ 0 - 0
public/model/glft/ztfj/dj1.glb → public/model/glft/ztfj/dj1_2023-06-02.glb


+ 0 - 0
public/model/glft/ztfj/dj2.glb → public/model/glft/ztfj/dj2_2023-06-02.glb


BIN
public/model/glft/ztfj/dzp_2023-06-02.glb


BIN
public/model/glft/ztfj/fbm_2023-06-02.glb


BIN
public/model/glft/ztfj/main.glb


BIN
public/model/glft/ztfj/ztfj-fc_2023-06-02.glb


BIN
public/model/glft/ztfj/ztfj.glb


BIN
public/model/glft/ztfj/ztfj_2023-06-02.glb


+ 6 - 0
src/App.vue

@@ -63,4 +63,10 @@
     font-weight: normal;
     font-style: normal;
   }
+  @font-face {
+    font-family: 'ysbtFont';
+    src: url('/@/assets/font/ysbtFont.ttf');
+    font-weight: normal;
+    font-style: normal;
+  }
 </style>

BIN
src/assets/font/ysbtFont.ttf


+ 6 - 0
src/assets/icons/CO-aveg.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="73.001" height="57" viewBox="0 0 73.001 57">
+  <g id="组_13623" data-name="组 13623" transform="translate(-1671 -361)">
+    <path id="路径_55489" data-name="路径 55489" d="M64,120.4v3.189H79.763V120.4Zm0,19.507H79.763v-3.189H64Zm0-8.157H79.763V128.56H64Z" transform="translate(1664.238 240.6)" fill="#3df6ff"/>
+    <text id="CO" transform="translate(1671 409)" fill="#fff" font-size="45" font-family="Kingsoft_Cloud_Font"><tspan x="0" y="0">CO</tspan></text>
+  </g>
+</svg>

+ 6 - 0
src/assets/icons/CO-low.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="73" height="58" viewBox="0 0 73 58">
+  <g id="组_13624" data-name="组 13624" transform="translate(-1671 -429.778)">
+    <text id="CO" transform="translate(1671 478.778)" fill="#fff" font-size="45" font-family="Kingsoft_Cloud_Font"><tspan x="0" y="0">CO</tspan></text>
+    <path id="路径_55490" data-name="路径 55490" d="M283.9,162.338v-13h2.627v13l2.759-2.759,1.839,1.97-5.911,5.911L279.3,161.55l1.839-1.839Zm-6.568,6.7H293.1v2.627H277.333Z" transform="translate(1450.904 280.445)" fill="#3df6ff"/>
+  </g>
+</svg>

+ 8 - 0
src/assets/icons/CO-top.svg

@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="73" height="58" viewBox="0 0 73 58">
+  <g id="组_13622" data-name="组 13622" transform="translate(-1652 -407)">
+    <g id="组_13620" data-name="组 13620" transform="translate(-19 -22.778)">
+      <path id="路径_55370" data-name="路径 55370" d="M283.9,158.66v13h2.627v-13l2.759,2.759,1.839-1.97-5.911-5.911-5.911,5.911,1.839,1.839Zm-6.568-6.7H293.1v-2.627H277.333Z" transform="translate(1450.904 280.445)" fill="#3df6ff"/>
+      <text id="CO" transform="translate(1671 478.778)" fill="#fff" font-size="45" font-family="Kingsoft_Cloud_Font"><tspan x="0" y="0">CO</tspan></text>
+    </g>
+  </g>
+</svg>

BIN
src/assets/images/vent/alarm/1.png


BIN
src/assets/images/vent/alarm/2.png


BIN
src/assets/images/vent/alarm/3.png


BIN
src/assets/images/vent/alarm/4.png


BIN
src/assets/images/vent/bottom-btn-active.png


BIN
src/assets/images/vent/bottom-btn.png


BIN
src/assets/images/vent/home/tab11.png


BIN
src/assets/images/vent/home/tab21.png


BIN
src/assets/images/vent/param-bg.png


BIN
src/assets/images/vent/vent-header1.png


BIN
src/assets/images/vent/vent-header2.png


BIN
src/assets/images/vent/wokeFaca-nav.png


BIN
src/assets/images/vent/workFace-param-bg.png


+ 11 - 0
src/components/Form/src/components/ApiTreeSelect.vue

@@ -25,8 +25,11 @@
       params: { type: Object },
       immediate: { type: Boolean, default: true },
       resultField: propTypes.string.def(''),
+      isClickParentNode: {type: Boolean, default: true}
     },
+
     emits: ['options-change', 'change'],
+
     setup(props, { attrs, emit }) {
       const treeData = ref<Recordable[]>([]);
       const isFirstLoaded = ref<Boolean>(false);
@@ -78,6 +81,14 @@
           result = get(result, props.resultField);
         }
         treeData.value = (result as Recordable[]) || [];
+        // disable
+        if(!props.isClickParentNode){
+          treeData.value.filter(item => {
+            if(item.children && item.children.length){
+              item['disabled'] = true
+            }
+          })
+        }
         isFirstLoaded.value = true;
         emit('options-change', treeData.value);
       }

+ 89 - 2
src/design/vent/comment.less

@@ -23,18 +23,105 @@
   justify-content: center;
   align-items: center;
 }
+.vent-right{
+  text-align: right;
+}
 
 .vent-margin-b-20{
   margin-bottom: 20px;
 }
+.vent-margin-b-10 {
+  margin-bottom: 10px;
+}
+.vent-margin-b-5 {
+  margin-bottom: 5px;
+}
 .vent-margin-t-20 {
-  margin-top: 20px;
+  margin-top: 20px !important;
+}
+.vent-margin-t-15 {
+  margin-top: 15px !important;
 }
 .vent-margin-t-10 {
   margin-top: 10px;
 }
+.vent-margin-l-8 {
+  margin-left: 8px;
+}
+.vent-margin-l-10 {
+  margin-left: 10px;
+}
+.vent-margin-l-15 {
+  margin-left: 15px;
+}
+.vent-margin-l-20 {
+  margin-left: 20px;
+}
+.vent-margin-r-8 {
+  margin-right: 8px;
+}
+
+.vent-padding-lr-5{
+  padding: 0 5px
+}
 
 .table-action-link {
   color: #00e7ff;
   padding: 0 5px;
-}
+}
+
+.signal-round {
+    display: inline-block;
+    width: 8px;
+    height: 8px;
+    border-radius: 50%;
+    // margin: 0 10px;
+    position: relative;
+
+    &::after {
+      display: block;
+      content: '';
+      position: absolute;
+      width: 12px;
+      height: 12px;
+      top: -2px;
+      left: -2px;
+      border-radius: 50%;
+    }
+  }
+
+  .signal-round-gry {
+    background-color: #858585;
+
+    &::after {
+      background-color: #85858544;
+      box-shadow: 0 0 1px 1px #85858599;
+    }
+  }
+
+  .signal-round-run {
+    background-color: #67fc00;
+
+    &::after {
+      background-color: #67fc0044;
+      box-shadow: 0 0 1px 1px #c6ff77;
+    }
+  }
+
+  .signal-round-blue {
+    background-color: #00ffe1;
+
+    &::after {
+      background-color: #00b2de66;
+      box-shadow: 0 0 1px 1px rgba(105, 238, 255, 0.3);
+    }
+  }
+
+  .signal-round-warning {
+    background-color: #e9170b;
+
+    &::after {
+      background-color: #e9170b44;
+      box-shadow: 0 0 1px 1px #e9170b;
+    }
+  }

+ 1 - 0
src/design/vent/index.less

@@ -376,6 +376,7 @@
   // backdrop-filter: blur(8px);
   box-shadow: 0 0 20px #44b4ff33 inset;
   background-color: #ffffff11;
+  overflow-y: auto;
   // background-color: #00b3ff12;
   .@{ventSpace}-table-thead > tr > th {
     color: #fff !important;

+ 4 - 41
src/design/vent/modal.less

@@ -303,7 +303,7 @@
         height: 10px;
         content: '';
         position: absolute;
-        top: 35px;
+        top: 28px;
         right: 0;
         left: -16px;
         bottom: 0;
@@ -407,10 +407,11 @@
       bottom: 0px;
       height: calc(100% - 0px);
       pointer-events: auto;
+      // background: #02263aaa;
       // background: linear-gradient(#00daff33, #2081ff11);
       background: linear-gradient(#0091aa33, #2081ff11);
       // background: radial-gradient(circle at 50% 80%, #3df6ff33, #0038b433);
-      backdrop-filter: blur(5px);
+      backdrop-filter: blur(8px);
       overflow-y: hidden;
       border-radius: 8px;
       .tabs-box {
@@ -503,45 +504,7 @@
   display: flex;
   justify-content: space-between;
   align-items: center;
-  .signal-round {
-    display: inline-block;
-    width: 8px;
-    height: 8px;
-    border-radius: 50%;
-    margin: 0 10px;
-    position: relative;
-    &::after {
-      display: block;
-      content: '';
-      position: absolute;
-      width: 12px;
-      height: 12px;
-      top: -2px;
-      left: -2px;
-      border-radius: 50%;
-    }
-  }
-  .signal-round-gry {
-    background-color: #858585;
-    &::after {
-      background-color: #85858544;
-      box-shadow: 0 0 1px 1px #85858599;
-    }
-  }
-  .signal-round-run {
-    background-color: #67fc00;
-    &::after {
-      background-color: #67fc0044;
-      box-shadow: 0 0 1px 1px #c6ff77;
-    }
-  }
-  .signal-round-warning {
-    background-color: #e9170b;
-    &::after {
-      background-color: #e9170b44;
-      box-shadow: 0 0 1px 1px #e9170b;
-    }
-  }
+  
 }
 /* 模态框样式 */
 :deep(.modal-container) {

+ 1 - 1
src/hooks/core/useThree copy.ts

@@ -90,7 +90,7 @@ class UseThree {
     this.scene?.add(mesh);
   }
 
-  setModel(modalName) {
+  setGLTFModel(modalName) {
     const a = new Date().getTime() / 1000
     return new Promise(async (resolve, reject) => {
       try {

+ 16 - 18
src/layouts/default/header/index.less

@@ -115,24 +115,21 @@
     display: flex;
     justify-content: center;
     align-items: center;
-    //font-family: Geneva, sans-serif;
-    font-size: 22px; //#ffa500 ffbb63
-    //color: #ff9900;
-    color: #ffa500;
-    letter-spacing: 0.15em;
+    font-size: 26px; //#ffa500 ffbb63
+    font-weight: 600;
     //text-shadow: -1px -1px 1px #fff8ef;
-    text-shadow: -0px -1px 0px #ffbb63, -1px -1px 1px #fff8ef, 0px 2px 0 #74520022, 0px 3px 0 #74520022, 0px 4px 0 #74520022, 0px 5px 0 #74520022,
-      0px 6px 0 #74520022, 0px 7px 0 #28282822,
-      //0px 8px 0 #28282822,
-      //0px 9px 0 #28282822,
-      //0px 10px 0 #28282822,
-      //0px 11px 0 #28282822,
-      // 0px 12px 0 #181818,
-      // 0px 13px 0 #161616,
-      // 0px 14px 0 #141414,
-      // 0px 15px 0 #121212,
-      2px 15px 5px rgba(0, 0, 0, 0.3),
-      5px 18px 5px rgba(0, 0, 0, 0.1), 8px 24px 8px rgba(0, 0, 0, 0.2), 8px 28px 35px rgba(0, 0, 0, 0.9);
+    // text-shadow: -0px -1px 0px #ffbb63, -1px -1px 1px #fff8ef, 0px 2px 0 #74520022, 0px 3px 0 #74520022, 0px 4px 0 #74520022, 0px 5px 0 #74520022,
+    //   0px 6px 0 #74520022, 0px 7px 0 #28282822,
+    //   //0px 8px 0 #28282822,
+    //   //0px 9px 0 #28282822,
+    //   //0px 10px 0 #28282822,
+    //   //0px 11px 0 #28282822,
+    //   // 0px 12px 0 #181818,
+    //   // 0px 13px 0 #161616,
+    //   // 0px 14px 0 #141414,
+    //   // 0px 15px 0 #121212,
+    //   2px 15px 5px rgba(0, 0, 0, 0.3),
+    //   5px 18px 5px rgba(0, 0, 0, 0.1), 8px 24px 8px rgba(0, 0, 0, 0.2), 8px 28px 35px rgba(0, 0, 0, 0.9);
     &:before {
       content: attr(text);
       position: absolute;
@@ -264,7 +261,8 @@
 
 .vent-header {
   height: 120px !important;
-  background: url('/src/assets/images/vent/header-bg.png') no-repeat !important;
+  // background: url('/src/assets/images/vent/header-bg.png') no-repeat !important;
+  background: url('/src/assets/images/vent/vent-header2.png') no-repeat !important;
   border: none !important;
   padding-bottom: 0px !important;
   box-shadow: none !important;

+ 8 - 2
src/layouts/default/header/index.vue

@@ -24,7 +24,7 @@
     <!-- menu start -->
     <div v-if="currentRoute.path.startsWith('/monitorChannel/monitor-')" :class="`${prefixCls}-vent`">
       <!-- <div>主通风机监测控制</div> -->
-      <div>{{ currentRoute.meta.title }} </div>
+      <div class="header-nav-title">{{ currentRoute.meta.title }} </div>
     </div>
     <div :class="`${prefixCls}-menu`" v-else-if="getShowTopMenu && !getIsMobile">
       <LayoutMenu :isHorizontal="true" :theme="getHeaderTheme" :splitType="getSplitType" :menuMode="getMenuMode" />
@@ -38,7 +38,7 @@
       </div>
     </div>
   </Header>
-  <div v-else-if="currentRoute.path !== '/micro-vent-3dModal/dashboard/analysis' && !currentRoute.path.startsWith('micro-need-air')" :class="`${prefixCls}-action`"  style="position: fixed; top: 30px; right: 20px; z-index: 999;">
+  <div v-if="currentRoute.path.endsWith('home') || currentRoute.path.startsWith('micro-need-air')" :class="`${prefixCls}-action`"  style="position: fixed; top: 30px; right: 20px; z-index: 999;">
     <div class="right-position">
       <UserDropDown :theme="getHeaderTheme" />
     </div>
@@ -281,4 +281,10 @@
     height: 0px !important;
     display: none !important;
   }
+  .header-nav-title{
+    background-image: linear-gradient(#ffffff 50%, #60f4ff);
+    -webkit-background-clip: text;
+    color: transparent;
+    font-weight: 600;
+  }
 </style>

+ 3 - 2
src/store/modules/threejs.ts

@@ -2,7 +2,8 @@ import { defineStore } from 'pinia';
 
 type model = {
   modelName: string;
-  modelVal: any;
+  modalVal: any;
+  version: string;
 };
 
 export const useModelStore = defineStore({
@@ -14,7 +15,7 @@ export const useModelStore = defineStore({
   },
   actions: {
     setModel(model: model) {
-      this.modelArr.set(model.modelName, model.modelVal);
+      this.modelArr.set(model.modelName, { modalVal: model.modalVal, version: model.version });
     },
   },
 });

+ 16 - 6
src/utils/threejs/loadGltf.worker.js

@@ -3,7 +3,7 @@ import Dexie from 'dexie';
 
 const db = new Dexie('DB');
 db.version(1).stores({
-  modal: '++id, modalName, modalVal',
+  modal: '&modalName, modalVal, versionStr',
 });
 self['modelLen'] = -1;
 
@@ -35,7 +35,7 @@ self.addEventListener(
     const { data, message } = e.data;
     if (message == 'load') {
       if (data) {
-        const model = await loadGltf(data);
+        const model = await loadModal(data);
         self.postMessage({ message: 'end', data: model });
       }
     }
@@ -43,19 +43,29 @@ self.addEventListener(
   false
 );
 
-function loadGltf(url) {
+function loadModal(url) {
   return new Promise((resolve, reject) => {
+    const suffix = url.match(/[^.]+$/)[0];
+
     const loader = new THREE.FileLoader();
-    loader.setPath('/model/glft/');
+    if (suffix == 'glb' || suffix == 'gltf'){
+      loader.setPath('/model/glft/');
+    } else if (suffix == 'fbx' || suffix == 'FBX') {
+      loader.setPath('/model/fbx/');
+    }
+    
     loader.setResponseType('arraybuffer');
-    loader.setRequestHeader({});
+    loader.setRequestHeader({})
     loader.setWithCredentials(false);
     try {
       loader.load(url, async (data) => {
-        const modalName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
+        const fileName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
+        const modalName = fileName.slice(0, -11);
+        const version = fileName.slice(-11);
         const model = {
           modalName: modalName,
           modalVal: data,
+          versionStr: version,
         };
         await db.modal.add(model);
         self.postMessage({ message: 'save', data: model });

+ 57 - 36
src/utils/threejs/main.worker.ts

@@ -5,34 +5,41 @@ import { useModelStore } from '/@/store/modules/threejs';
 export function initModalWorker() {
   type model = {
     modelName: string;
-    modelVal: any;
+    modalVal: any;
+    version: string;
   };
   const modalUrlArr = [
-    'fm/fm.glb',
-    'fm/Fm-door.glb',
-    'fm/Fm-wire.glb',
-    'fm/Fm-wall.glb',
-    'fc/sdFc.glb',
-    'fc/ddFc.glb',
-    'cf/lmcf.glb',
-    'cf/lmcfSide.glb',
-    'cf/zdcf.glb',
-    'cf/dscf.glb',
-    'cf/dsgd.glb',
-    'cf/dsmove.glb',
-    'jbfj/jbfj_hd.glb',
-    'jbfj/jbfj_fm.glb',
-    'jbfj/jbfj_fc.glb',
-    'ztfj/main.glb',
-    'ztfj/dj1.glb',
-    'ztfj/dj2.glb',
-    'ztfj/bg.glb',
-    'fire/laneway.glb',
-    'fire/chamber.glb',
-    'fire/workFace.glb',
-    'yafeng/compressor.glb',
-    'fire/nitrogen.glb',
-    'fire/grout.glb',
+    'fm/fm_2023-06-02.glb',
+    'fm/Fm-door_2023-06-02.glb',
+    'fm/Fm-wire_2023-06-02.glb',
+    'fm/Fm-wall_2023-06-02.glb',
+    'fc/sdFc_2023-06-02.glb',
+    'fc/ddFc_2023-06-02.glb',
+    'cf/lmcf_2023-06-02.glb',
+    'cf/lmcfSide_2023-06-02.glb',
+    'cf/zdcf_2023-06-02.glb',
+    'cf/dscf_2023-06-02.glb',
+    'cf/dsgd_2023-06-02.glb',
+    'cf/dsmove_2023-06-02.glb',
+
+    'jbfj/jbfj-hd_2023-06-06.glb',
+
+    'jbfj/jbfj-fm_2023-06-02.glb',
+    'jbfj/jbfj-fc_2023-06-02.glb',
+    'ztfj/dj1_2023-06-02.glb',
+    'ztfj/dj2_2023-06-02.glb',
+    'ztfj/bg_2023-06-02.glb',
+    'ztfj/bg1_2023-06-02.glb',
+    'ztfj/dzp_2023-06-02.glb',
+    'ztfj/fbm_2023-06-02.glb',
+    'ztfj/ztfj-fc_2023-06-02.glb',
+    'ztfj/ztfj_2023-06-02.glb',
+    'fire/laneway_2023-06-02.glb',
+    'fire/chamber_2023-06-02.glb',
+    'fire/workFace_2023-06-02.glb',
+    'fire/nitrogen_2023-06-02.glb',
+    'fire/grout_2023-06-02.glb',
+    'yafeng/compressor_2023-06-02.glb',
   ];
 
   const db: any = new Dexie('DB');
@@ -40,26 +47,40 @@ export function initModalWorker() {
 
   const modelStore = useModelStore();
   db.version(1).stores({
-    modal: '++id, modalName, modalVal',
+    modal: '&modalName, modalVal, versionStr',
   });
+  db.open();
 
   new Promise(async (resolve) => {
     const validateModelUrlArr: string[] = [];
+
     for (let i = 0; i < modalUrlArr.length; i++) {
       const url = modalUrlArr[i];
-      const modalName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
-      const aa = new Date().getTime();
+      const fileName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
+      const modalName = fileName.slice(0, -11);
+      const version = fileName.slice(-11);
       const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
-      console.log('模型下载时间------------》', new Date().getTime() - aa);
       if (modalArr.length < 1) {
         validateModelUrlArr.push(url);
       } else {
-        if (!modelStore.modelArr.get(modalName)) {
-          const model: model = {
-            modelName: modalName,
-            modelVal: modalArr[0].modalVal,
-          };
-          modelStore.setModel(model);
+        // 判断该版本是否一致,不一致删除
+        const data = modelStore.modelArr.get(modalName);
+        const versionDB = modalArr[0].versionStr;
+
+        if (versionDB == version) {
+          // 版本一致
+          if (!data) {
+            const model: model = {
+              modelName: modalName,
+              modalVal: modalArr[0].modalVal,
+              version: modalArr[0].versionStr,
+            };
+            modelStore.setModel(model);
+          }
+        } else {
+          // 版本不一致,删除模型,重新下载
+          await db.modal.delete(modalName);
+          validateModelUrlArr.push(url);
         }
       }
     }

+ 93 - 5
src/utils/threejs/useThree.ts

@@ -7,11 +7,11 @@ import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
 // import gsap from 'gsap';
 import ResourceTracker from '/@/utils/threejs/ResourceTracker.js';
 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
 import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
 import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
 import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
 import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
-import { setModalCenter } from '/@/utils/threejs/util';
 import Stats from 'three/examples/jsm/libs/stats.module.js';
 import { useModelStore } from '/@/store/modules/threejs';
 import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';
@@ -178,7 +178,7 @@ class UseThree {
     // this.orbitControls.enableDamping = true;
   }
 
-  setModel(modalNames, group = null) {
+  setGLTFModel(modalNames, group = null) {
     window['startTime'] = new Date().getTime();
     const modelStore = useModelStore();
     return new Promise(async (resolve, reject) => {
@@ -202,11 +202,15 @@ class UseThree {
             try {
               // 解析模型
               const modalNameStr = modalNameArr[i];
-              let modalValue = modelStore.modelArr.get(modalNameStr) || null;
-              if (!modalValue) {
+              let data = modelStore.modelArr.get(modalNameStr) || null;
+              let modalValue;
+              if (!data) {
                 const modalArr = await db.modal.where('modalName').equals(modalNameStr).toArray();
                 if (modalArr.length > 0) modalValue = modalArr[0].modalVal;
+              } else {
+                modalValue = data.modalVal;
               }
+
               if (modalValue) {
                 gltfLoader.parse(
                   modalValue,
@@ -219,6 +223,7 @@ class UseThree {
                         obj.material.emissiveIntensity = 1;
                         obj.material.emissiveMap = obj.material.map;
                         obj.material.blending = THREE.CustomBlending;
+
                         if (obj.material.opacity < 1) {
                           obj.material.transparent = true;
                         }
@@ -259,7 +264,6 @@ class UseThree {
             }
           });
         }
-
         Promise.all(resolvePromise).then((objects) => {
           dracoLoader.dispose();
           resolve(objects);
@@ -270,6 +274,90 @@ class UseThree {
     });
   }
 
+  setFBXModel(modalNames, group = null) {
+    window['startTime'] = new Date().getTime();
+    const modelStore = useModelStore();
+    return new Promise(async (resolve, reject) => {
+      try {
+        const fbxLoader = new FBXLoader();
+
+        fbxLoader.setPath('/model/fbx/');
+
+        const db = window['CustomDB'];
+        const resolvePromise: Promise<any>[] = [];
+
+        let modalNameArr = Object.prototype.toString.call(modalNames) === '[object Array]' ? modalNames : [modalNames];
+        let len = modalNameArr.length;
+
+        for (let i = 0; i < len; i++) {
+          resolvePromise[i] = new Promise(async (childResolve, reject) => {
+            try {
+              // 解析模型
+              let modalValue;
+              const modalNameStr = modalNameArr[i];
+              let data = modelStore.modelArr.get(modalNameStr) || null;
+              if (!data) {
+                const modalArr = await db.modal.where('modalName').equals(modalNameStr).toArray();
+                if (modalArr.length > 0) modalValue = modalArr[0].modalVal;
+              } else {
+                modalValue = data.modalVal;
+              }
+              if (modalValue) {
+                const object = fbxLoader.parse(modalValue, '/model/fbx/');
+                // const object = fbx.scene;
+                // setModalCenter(object);
+                if (object) {
+                  object.traverse((obj) => {
+                    if (obj instanceof THREE.Mesh) {
+                      obj.material.emissiveIntensity = 1;
+                      obj.material.emissiveMap = obj.material.map;
+                      obj.material.blending = THREE.CustomBlending;
+                      if (obj.material.opacity < 1) {
+                        obj.material.transparent = true;
+                      }
+                      if (obj.material.map) {
+                        obj.material.map.encoding = THREE.sRGBEncoding;
+                        obj.material.map.flipY = false;
+                        obj.material.map.anisotropy = 1;
+                      }
+                      if (obj.material.emissiveMap) {
+                        obj.material.emissiveMap.encoding = THREE.sRGBEncoding;
+                        obj.material.emissiveMap.flipY = false;
+                      }
+
+                      if (obj.material.map || obj.material.emissiveMap) {
+                        obj.material.needsUpdate = true;
+                      }
+
+                      // if (envMap) {
+                      //   obj.material.envMap = envMap;
+                      //   obj.material.envMapIntensity = 1;
+                      // }
+                      // obj.renderOrder = 1;
+                    }
+                  });
+                  object.animations = object.animations;
+                  object.name = modalNameStr;
+                  group?.add(object);
+                  childResolve(object);
+                }
+              }
+            } catch (error) {
+              console.log(error);
+              reject();
+            }
+          });
+        }
+
+        Promise.all(resolvePromise).then((objects) => {
+          resolve(objects);
+        });
+      } catch (error) {
+        reject('加载模型出错');
+      }
+    });
+  }
+
   setTestPlane() {
     const plane = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshPhongMaterial({ color: 0x999999, specular: 0x101010 }));
     plane.rotation.x = -Math.PI / 2;

+ 1 - 4
src/views/monitor/mynews/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="device-manager-box">
     <BasicTable @register="registerTable" :searchInfo="searchInfo">
       <template #tableTitle>
         <a-button type="primary" @click="handlerReadAllMsg">全部标注已读</a-button>
@@ -24,12 +24,9 @@
   const glob = useGlobSetting();
   const { createMessage } = useMessage();
   const checkedKeys = ref<Array<string | number>>([]);
-  const content = ref({});
   const searchInfo = { logType: '1' };
   const [register, { openModal: openDetail }] = useModal();
   import { useListPage } from '/@/hooks/system/useListPage';
-  import { getLogList } from '/@/views/monitor/log/log.api';
-  import { useRouter } from 'vue-router';
   import { useAppStore } from '/@/store/modules/app';
   import { useMessageHref } from '/@/views/system/message/components/useSysMessage';
   const appStore = useAppStore();

+ 1 - 1
src/views/monitor/quartz/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="device-manager-box">
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <template #tableTitle>
         <a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd" style="margin-right: 5px">新增</a-button>

+ 11 - 11
src/views/monitor/quartz/quartz.api.ts

@@ -2,17 +2,17 @@ import { defHttp } from '/@/utils/http/axios';
 import { Modal } from 'ant-design-vue';
 
 enum Api {
-  list = '/sys/quartzJob/list',
-  save = '/sys/quartzJob/add',
-  edit = '/sys/quartzJob/edit',
-  get = '/sys/quartzJob/queryById',
-  pause = '/sys/quartzJob/pause',
-  resume = '/sys/quartzJob/resume',
-  delete = '/sys/quartzJob/delete',
-  exportXlsUrl = '/sys/quartzJob/exportXls',
-  importExcelUrl = '/sys/quartzJob/importExcel',
-  execute = '/sys/quartzJob/execute',
-  deleteBatch = '/sys/quartzJob/deleteBatch',
+  list = '/ventanaly/quartzJob/list',
+  save = '/ventanaly/quartzJob/add',
+  edit = '/ventanaly/quartzJob/edit',
+  get = '/ventanaly/quartzJob/queryById',
+  pause = '/ventanaly/quartzJob/pause',
+  resume = '/ventanaly/quartzJob/resume',
+  delete = '/ventanaly/quartzJob/delete',
+  exportXlsUrl = '/ventanaly/quartzJob/exportXls',
+  importExcelUrl = '/ventanaly/quartzJob/importExcel',
+  execute = '/ventanaly/quartzJob/execute',
+  deleteBatch = '/ventanaly/quartzJob/deleteBatch',
 }
 
 /**

+ 0 - 2
src/views/system/depart/index.vue

@@ -66,8 +66,6 @@
   .@{ventSpace}-tabs {
     border: 1px solid #44d3ff70 !important;
     border-radius: 2px !important;
-    -webkit-backdrop-filter: blur(8px) !important;
-    backdrop-filter: blur(8px) !important;
     box-shadow: 0 0 20px #44b4ff33 inset;
     background-color: #ffffff11 !important;
   }

+ 122 - 0
src/views/system/menuModal/DataRuleList.vue

@@ -0,0 +1,122 @@
+<template>
+  <BasicDrawer v-bind="$attrs" @register="registerDrawer" title="数据权限规则" :width="adaptiveWidth">
+    <BasicTable @register="registerTable">
+      <template #tableTitle>
+        <a-button type="primary" @click="handleCreate"> 新增</a-button>
+      </template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" />
+      </template>
+    </BasicTable>
+  </BasicDrawer>
+  <DataRuleModal @register="registerModal" @success="reload" :permissionId="permissionId" />
+</template>
+<script lang="ts" setup>
+  import { ref, unref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import DataRuleModal from './DataRuleModal.vue';
+  import { dataRuleColumns, dataRuleSearchFormSchema } from './menu.data';
+  import { dataRuleList, deleteRule } from './menu.api';
+  import { ColEx } from '/@/components/Form/src/types';
+  import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
+  const permissionId = ref('');
+  const { adaptiveWidth } = useDrawerAdaptiveWidth();
+  //权限规则model
+  const [registerModal, { openModal }] = useModal();
+  const [registerDrawer] = useDrawerInner(async (data) => {
+    permissionId.value = data.id;
+    setProps({ searchInfo: { permissionId: unref(permissionId) } });
+    reload();
+  });
+  // 自适应列配置
+  const adaptiveColProps: Partial<ColEx> = {
+    xs: 24, // <576px
+    sm: 24, // ≥576px
+    md: 24, // ≥768px
+    lg: 12, // ≥992px
+    xl: 8, // ≥1200px
+    xxl: 8, // ≥1600px
+  };
+  const [registerTable, { reload, setProps }] = useTable({
+    api: dataRuleList,
+    columns: dataRuleColumns,
+    size: 'small',
+    formConfig: {
+      baseColProps: adaptiveColProps,
+      labelAlign: 'right',
+      labelCol: {
+        offset: 1,
+        xs: 24,
+        sm: 24,
+        md: 24,
+        lg: 8,
+        xl: 8,
+        xxl: 8,
+      },
+      wrapperCol: {},
+      schemas: dataRuleSearchFormSchema,
+      autoSubmitOnEnter: true,
+    },
+    striped: true,
+    useSearchForm: true,
+    bordered: true,
+    showIndexColumn: false,
+    showTableSetting: false,
+    canResize: false,
+    immediate: false,
+    actionColumn: {
+      width: 100,
+      title: '操作',
+      dataIndex: 'action',
+      slots: { customRender: 'action' },
+      fixed: undefined,
+    },
+  });
+
+  /**
+   * 新增
+   */
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+    });
+  }
+
+  /**
+   * 编辑
+   */
+  function handleEdit(record) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+    });
+  }
+
+  /**
+   * 删除
+   */
+  async function handleDelete(record) {
+    await deleteRule({ id: record.id }, reload);
+  }
+
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+        },
+      },
+    ];
+  }
+</script>

+ 54 - 0
src/views/system/menuModal/DataRuleModal.vue

@@ -0,0 +1,54 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" width="700px">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { defineProps, ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { dataRuleFormSchema } from './menu.data';
+  import { saveOrUpdateRule } from './menu.api';
+  // 声明Emits
+  const emit = defineEmits(['success', 'register']);
+  const props = defineProps({ permissionId: String });
+  const isUpdate = ref(true);
+  //表单配置
+  const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
+    schemas: dataRuleFormSchema,
+    showActionButtonGroup: false,
+  });
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.record,
+      });
+    }
+  });
+
+  //设置标题
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增规则' : '编辑规则'));
+
+  //表单提交事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      values.permissionId = props.permissionId;
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdateRule(values, isUpdate.value);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 104 - 0
src/views/system/menuModal/MenuDrawer.vue

@@ -0,0 +1,104 @@
+<template>
+  <BasicDrawer v-bind="$attrs" @register="registerDrawer" showFooter :width="adaptiveWidth" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" class="menuForm" />
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref, useAttrs } from 'vue';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema, ComponentTypes } from './menu.data';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { list, saveOrUpdateMenu } from './menu.api';
+  import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
+  // 声明Emits
+  const emit = defineEmits(['success', 'register']);
+  const { adaptiveWidth } = useDrawerAdaptiveWidth();
+  const attrs = useAttrs();
+  const isUpdate = ref(true);
+  const menuType = ref(0);
+  const isButton = (type) => type === 2;
+  const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate }] = useForm({
+    labelCol: {
+      md: { span: 4 },
+      sm: { span: 6 },
+    },
+    wrapperCol: {
+      md: { span: 20 },
+      sm: { span: 18 },
+    },
+    schemas: formSchema,
+    showActionButtonGroup: false,
+  });
+
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+    await resetFields();
+    setDrawerProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    menuType.value = data?.record?.menuType;
+
+    //获取下拉树信息
+    const treeData = await list();
+    updateSchema([
+      {
+        field: 'parentId',
+        componentProps: { treeData },
+      },
+      {
+        field: 'name',
+        label: isButton(unref(menuType)) ? '按钮/权限' : '菜单名称',
+      },
+      {
+        field: 'url',
+        required: !isButton(unref(menuType)),
+        componentProps: {
+          onChange: (e) => onUrlChange(e.target.value),
+        },
+      },
+    ]);
+
+    // 无论新增还是编辑,都可以设置表单值
+    if (typeof data.record === 'object') {
+      let values = { ...data.record };
+      setFieldsValue(values);
+      onUrlChange(values.url);
+    }
+    //禁用表单
+    setProps({ disabled: !attrs.showFooter });
+  });
+  //获取弹窗标题
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
+  //提交事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      // iframe兼容
+      if (ComponentTypes.IFrame === values.component) {
+        values.component = values.frameSrc;
+      }
+      setDrawerProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdateMenu(values, unref(isUpdate));
+      closeDrawer();
+      emit('success');
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+
+  /** url 变化时,动态设置组件名称placeholder */
+  function onUrlChange(url) {
+    let placeholder = '';
+    if (url != null && url != '') {
+      if (url.startsWith('/')) {
+        url = url.substring(1);
+      }
+      url = url.replaceAll('/', '-');
+      // 特殊标记
+      url = url.replaceAll(':', '@');
+      placeholder = `${url}`;
+    } else {
+      placeholder = '请输入组件名称';
+    }
+    updateSchema([{ field: 'componentName', componentProps: { placeholder } }]);
+  }
+</script>

+ 205 - 0
src/views/system/menuModal/index.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="p-4 device-manager-box">
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <template #tableTitle>
+        <a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增菜单</a-button>
+        <a-button type="primary" preIcon="ic:round-expand" @click="expandAll">展开全部</a-button>
+        <a-button type="primary" preIcon="ic:round-compress" @click="collapseAll">折叠全部</a-button>
+
+        <a-dropdown v-if="checkedKeys.length > 0">
+          <template #overlay>
+            <a-menu>
+              <a-menu-item key="1" @click="batchHandleDelete">
+                <Icon icon="ant-design:delete-outlined" />
+                删除
+              </a-menu-item>
+            </a-menu>
+          </template>
+          <a-button
+            >批量操作
+            <Icon icon="ant-design:down-outlined" />
+          </a-button>
+        </a-dropdown>
+      </template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+    </BasicTable>
+    <MenuDrawer @register="registerDrawer" @success="handleSuccess" :showFooter="showFooter" />
+    <DataRuleList @register="registerDrawer1" />
+  </div>
+</template>
+<script lang="ts" name="system-menu" setup>
+  import { nextTick, ref } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { useDrawer } from '/@/components/Drawer';
+  import MenuDrawer from './MenuDrawer.vue';
+  import DataRuleList from './DataRuleList.vue';
+  import { columns, searchFormSchema } from './menu.data';
+  import { list, deleteMenu, batchDeleteMenu } from './menu.api';
+
+  const checkedKeys = ref<Array<string | number>>([]);
+  const showFooter = ref(true);
+  const [registerDrawer, { openDrawer }] = useDrawer();
+  const [registerDrawer1, { openDrawer: openDataRule }] = useDrawer();
+  // 列表页面公共参数、方法
+  const { prefixCls, tableContext } = useListPage({
+    tableProps: {
+      title: '菜单列表',
+      api: list.bind(null, {kind: 2}),
+      columns: columns,
+      size: 'small',
+      pagination: false,
+      isTreeTable: true,
+      striped: true,
+      useSearchForm: true,
+      showTableSetting: true,
+      bordered: true,
+      showIndexColumn: false,
+      tableSetting: { fullScreen: true },
+      formConfig: {
+        schemas: searchFormSchema,
+        autoAdvancedCol: 4,
+        baseColProps: { xs: 24, sm: 12, md: 6, lg: 6, xl: 6, xxl: 6 },
+        actionColOptions: { xs: 24, sm: 12, md: 6, lg: 6, xl: 6, xxl: 6 },
+      },
+      actionColumn: {
+        width: 120,
+      },
+    },
+  });
+  //注册table数据
+  const [registerTable, { reload, expandAll, collapseAll }] = tableContext;
+
+  /**
+   * 选择列配置
+   */
+  const rowSelection = {
+    type: 'checkbox',
+    columnWidth: 30,
+    selectedRowKeys: checkedKeys,
+    onChange: onSelectChange,
+  };
+
+  /**
+   * 选择事件
+   */
+  function onSelectChange(selectedRowKeys: (string | number)[]) {
+    checkedKeys.value = selectedRowKeys;
+  }
+
+  /**
+   * 新增
+   */
+  function handleCreate() {
+    showFooter.value = true;
+    openDrawer(true, {
+      isUpdate: false,
+      kind: 2
+    });
+  }
+
+  /**
+   * 编辑
+   */
+  function handleEdit(record) {
+    showFooter.value = true;
+    openDrawer(true, {
+      record,
+      isUpdate: true,
+      kind: 2
+    });
+  }
+  /**
+   * 详情
+   */
+  function handleDetail(record) {
+    showFooter.value = false;
+    openDrawer(true, {
+      record,
+      isUpdate: true,
+      kind: 2
+    });
+  }
+  /**
+   * 添加下级
+   */
+  function handleAddSub(record) {
+    openDrawer(true, {
+      record: { parentId: record.id, menuType: 1 },
+      isUpdate: false,
+      kind: 2
+    });
+  }
+  /**
+   * 数据权限弹窗
+   */
+  function handleDataRule(record) {
+    openDataRule(true, { id: record.id, kind: 2 });
+  }
+
+  /**
+   * 删除
+   */
+  async function handleDelete(record) {
+    await deleteMenu({ id: record.id, kind: 2 }, reload);
+  }
+  /**
+   * 批量删除事件
+   */
+  async function batchHandleDelete() {
+    await batchDeleteMenu({ ids: checkedKeys.value, kind: 2 }, reload);
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    reload();
+  }
+
+  function onFetchSuccess() {
+    // 演示默认展开所有表项
+    nextTick(expandAll);
+  }
+
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
+    ];
+  }
+
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      // {
+      //   label: '详情',
+      //   onClick: handleDetail.bind(null, record),
+      // },
+      {
+        label: '添加下级',
+        onClick: handleAddSub.bind(null, record),
+      },
+      {
+        label: '数据规则',
+        onClick: handleDataRule.bind(null, record),
+      },
+      {
+        label: '删除',
+        color: 'error',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+        },
+      },
+    ];
+  }
+</script>

+ 122 - 0
src/views/system/menuModal/menu.api.ts

@@ -0,0 +1,122 @@
+import { defHttp } from '/@/utils/http/axios';
+import { Modal } from 'ant-design-vue';
+
+enum Api {
+  list = '/sys/permissionNew/list',
+  save = '/sys/permissionNew/add',
+  edit = '/sys/permissionNew/edit',
+  delete = '/sys/permissionNew/delete',
+  deleteBatch = '/sys/permissionNew/deleteBatch',
+  ruleList = '/sys/permissionNew/queryPermissionRule',
+  ruleSave = '/sys/permissionNew/addPermissionRule',
+  ruleEdit = '/sys/permissionNew/editPermissionRule',
+  ruleDelete = '/sys/permissionNew/deletePermissionRule',
+  checkPermDuplication = '/sys/permissionNew/checkPermDuplication',
+}
+
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => {
+  return defHttp.get({ url: Api.list, params });
+};
+
+/**
+ * 删除菜单
+ */
+export const deleteMenu = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.delete, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+/**
+ * 批量删除菜单
+ * @param params
+ */
+export const batchDeleteMenu = (params, handleSuccess) => {
+  Modal.confirm({
+    title: '确认删除',
+    content: '是否删除选中数据',
+    okText: '确认',
+    cancelText: '取消',
+    onOk: () => {
+      return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
+        handleSuccess();
+      });
+    },
+  });
+};
+/**
+ * 保存或者更新菜单
+ * @param params
+ */
+export const saveOrUpdateMenu = (params, isUpdate) => {
+  const url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params });
+};
+/**
+ * 菜单数据权限列表接口
+ * @param params
+ */
+export const dataRuleList = (params) => defHttp.get({ url: Api.ruleList, params });
+/**
+ * 保存或者更新数据规则
+ * @param params
+ */
+export const saveOrUpdateRule = (params, isUpdate) => {
+  const url = isUpdate ? Api.ruleEdit : Api.ruleSave;
+  return defHttp.post({ url: url, params });
+};
+
+/**
+ * 删除数据权限
+ */
+export const deleteRule = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.ruleDelete, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+/**
+ * 根据code获取字典数值
+ * @param params
+ */
+export const ajaxGetDictItems = (params) => defHttp.get({ url: `/sys/dict/getDictItems/${params.code}` });
+
+/**
+ * 唯一校验
+ * @param params
+ */
+export const getCheckPermDuplication = (params) => defHttp.get({ url: Api.checkPermDuplication, params }, { isTransformResponse: false });
+
+/**
+ * 校验菜单是否存在
+ * @param model
+ * @param schema
+ * @param required
+ */
+export const checkPermDuplication = (model, schema, required?) => {
+  return [
+    {
+      validator: (_, value) => {
+        if (!value && required) {
+          return Promise.reject(`请输入${schema.label}`);
+        }
+        return new Promise<void>((resolve, reject) => {
+          // getCheckPermDuplication({
+          //   id: model.id,
+          //   url: model.url,
+          //   alwaysShow: model.alwaysShow,
+          // })
+          //   .then((res) => {
+          //     res.success ? resolve() : reject(res.message || '校验失败');
+          //   })
+          //   .catch((err) => {
+          //     reject(err.message || '验证失败');
+          //   });
+          resolve()
+        });
+      },
+    },
+  ];
+};

+ 415 - 0
src/views/system/menuModal/menu.data.ts

@@ -0,0 +1,415 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { h } from 'vue';
+import { Icon } from '/@/components/Icon';
+import { duplicateCheck } from '../user/user.api';
+import { ajaxGetDictItems ,checkPermDuplication } from './menu.api';
+import { render } from '/@/utils/common/renderUtils';
+
+const isDir = (type) => type === 0;
+const isMenu = (type) => type === 1;
+const isButton = (type) => type === 2;
+
+// 定义可选择的组件类型
+export enum ComponentTypes {
+  Default = 'layouts/default/index',
+  IFrame = 'sys/iframe/FrameBlank',
+}
+
+export const columns: BasicColumn[] = [
+  {
+    title: '菜单名称',
+    dataIndex: 'name',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '菜单类型',
+    dataIndex: 'menuType',
+    width: 150,
+    customRender: ({ text }) => {
+      return render.renderDict(text, 'menu_type');
+    },
+  },
+  {
+    title: '图标',
+    dataIndex: 'icon',
+    width: 50,
+    customRender: ({ record }) => {
+      return h(Icon, { icon: record.icon });
+    },
+  },
+  {
+    title: '组件',
+    dataIndex: 'component',
+    align: 'left',
+    width: 150,
+  },
+  {
+    title: '路径',
+    dataIndex: 'url',
+    align: 'left',
+    width: 150,
+  },
+  {
+    title: '排序',
+    dataIndex: 'sortNo',
+    width: 50,
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '菜单名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    label: 'id',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'menuType',
+    label: '菜单类型',
+    component: 'RadioButtonGroup',
+    defaultValue: 0,
+    componentProps: ({ formActionType, formModel }) => {
+      return {
+        options: [
+          { label: '一级菜单', value: 0 },
+          { label: '子菜单', value: 1 },
+          { label: '按钮/权限', value: 2 },
+        ],
+        onChange: (e) => {
+          const { updateSchema, clearValidate } = formActionType;
+          const label = isButton(e) ? '按钮/权限' : '菜单名称';
+          //清除校验
+          clearValidate();
+          updateSchema([
+            {
+              field: 'name',
+              label: label,
+            },
+            {
+              field: 'url',
+              required: !isButton(e),
+            },
+          ]);
+          //update-begin---author:wangshuai ---date:20220729  for:[VUEN-1834]只有一级菜单,才默认值,子菜单的时候,清空------------
+          if (isMenu(e) && !formModel.id && formModel.component == 'layouts/RouteView') {
+            formModel.component = '';
+          }
+          //update-end---author:wangshuai ---date:20220729  for:[VUEN-1834]只有一级菜单,才默认值,子菜单的时候,清空------------
+        },
+      };
+    },
+  },
+  {
+    field: 'name',
+    label: '菜单名称',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'parentId',
+    label: '上级菜单',
+    component: 'TreeSelect',
+    required: true,
+    componentProps: {
+      replaceFields: {
+        title: 'name',
+        key: 'id',
+        value: 'id',
+      },
+      dropdownStyle: {
+        maxHeight: '50vh',
+      },
+      getPopupContainer: (node) => node.parentNode,
+    },
+    ifShow: ({ values }) => !isDir(values.menuType),
+  },
+  {
+    field: 'url',
+    label: '访问路径',
+    component: 'Input',
+    required: true,
+    ifShow: ({ values }) => !(values.component === ComponentTypes.IFrame && values.internalOrExternal) && values.menuType !== 2,
+    //update-begin-author:zyf date:2022-11-02 for: 聚合路由允许路径重复
+     dynamicRules: ({ model, schema }) => {
+       return checkPermDuplication(model, schema, true);
+    },
+    //update-end-author:zyf date:2022-11-02 for: 聚合路由允许路径重复
+  },
+  {
+    field: 'component',
+    label: '前端组件',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入前端组件',
+    },
+    defaultValue: 'layouts/RouteView',
+    required: true,
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'componentName',
+    label: '组件名称',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入组件名称',
+    },
+    helpMessage: [
+      '此处名称应和vue组件的name属性保持一致。',
+      '组件名称不能重复,主要用于路由缓存功能。',
+      '如果组件名称和vue组件的name属性不一致,则会导致路由缓存失效。',
+      '非必填,留空则会根据访问路径自动生成。',
+    ],
+    defaultValue: '',
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'frameSrc',
+    label: 'Iframe地址',
+    component: 'Input',
+    rules: [
+      { required: true, message: '请输入Iframe地址' },
+      { type: 'url', message: '请输入正确的url地址' },
+    ],
+    ifShow: ({ values }) => !isButton(values.menuType) && values.component === ComponentTypes.IFrame,
+  },
+  {
+    field: 'redirect',
+    label: '默认跳转地址',
+    component: 'Input',
+    ifShow: ({ values }) => isDir(values.menuType),
+  },
+  {
+    field: 'perms',
+    label: '授权标识',
+    component: 'Input',
+    ifShow: ({ values }) => isButton(values.menuType),
+    dynamicRules: ({ model }) => {
+      return [
+        {
+          required: false,
+          validator: (_, value) => {
+            return new Promise((resolve, reject) => {
+              const params = {
+                tableName: 'sys_permission',
+                fieldName: 'perms',
+                fieldVal: value,
+                dataId: model.id,
+              };
+              duplicateCheck(params)
+                .then((res) => {
+                  res.success ? resolve() : reject(res.message || '校验失败');
+                })
+                .catch((err) => {
+                  reject(err.message || '校验失败');
+                });
+            });
+          },
+        },
+      ];
+    },
+  },
+  {
+    field: 'permsType',
+    label: '授权策略',
+    component: 'RadioGroup',
+    defaultValue: '1',
+    helpMessage: ['可见/可访问(授权后可见/可访问)', '可编辑(未授权时禁用)'],
+    componentProps: {
+      options: [
+        { label: '可见/可访问', value: '1' },
+        { label: '可编辑', value: '2' },
+      ],
+    },
+    ifShow: ({ values }) => isButton(values.menuType),
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioGroup',
+    defaultValue: '1',
+    componentProps: {
+      options: [
+        { label: '有效', value: '1' },
+        { label: '无效', value: '0' },
+      ],
+    },
+    ifShow: ({ values }) => isButton(values.menuType),
+  },
+  {
+    field: 'icon',
+    label: '菜单图标',
+    component: 'IconPicker',
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'sortNo',
+    label: '排序',
+    component: 'InputNumber',
+    defaultValue: 1,
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'route',
+    label: '是否路由菜单',
+    component: 'Switch',
+    defaultValue: true,
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'hidden',
+    label: '隐藏路由',
+    component: 'Switch',
+    defaultValue: 0,
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'hideTab',
+    label: '隐藏Tab',
+    component: 'Switch',
+    defaultValue: 0,
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'keepAlive',
+    label: '是否缓存路由',
+    component: 'Switch',
+    defaultValue: false,
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'alwaysShow',
+    label: '聚合路由',
+    component: 'Switch',
+    defaultValue: false,
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+  {
+    field: 'internalOrExternal',
+    label: '打开方式',
+    component: 'Switch',
+    defaultValue: false,
+    componentProps: {
+      checkedChildren: '外部',
+      unCheckedChildren: '内部',
+    },
+    ifShow: ({ values }) => !isButton(values.menuType),
+  },
+];
+
+export const dataRuleColumns: BasicColumn[] = [
+  {
+    title: '规则名称',
+    dataIndex: 'ruleName',
+    width: 150,
+  },
+  {
+    title: '规则字段',
+    dataIndex: 'ruleColumn',
+    width: 100,
+  },
+  {
+    title: '规则值',
+    dataIndex: 'ruleValue',
+    width: 100,
+  },
+];
+
+export const dataRuleSearchFormSchema: FormSchema[] = [
+  {
+    field: 'ruleName',
+    label: '规则名称',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+  {
+    field: 'ruleValue',
+    label: '规则值',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+];
+
+export const dataRuleFormSchema: FormSchema[] = [
+  {
+    label: 'id',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'ruleName',
+    label: '规则名称',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'ruleColumn',
+    label: '规则字段',
+    component: 'Input',
+    ifShow: ({ values }) => {
+      return values.ruleConditions !== 'USE_SQL_RULES';
+    },
+  },
+  {
+    field: 'ruleConditions',
+    label: '条件规则',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: ajaxGetDictItems,
+      params: { code: 'rule_conditions' },
+      labelField: 'text',
+      valueField: 'value',
+      getPopupContainer: (node) => document.body,
+    },
+  },
+  {
+    field: 'ruleValue',
+    label: '规则值',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: '1',
+    componentProps: {
+      options: [
+        { label: '无效', value: '0' },
+        { label: '有效', value: '1' },
+      ],
+    },
+  },
+];

+ 1 - 1
src/views/system/message/manage/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div :class="prefixCls">
+  <div :class="prefixCls" class="device-manager-box">
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <!--插槽:table标题-->
       <template #tableTitle>

+ 1 - 1
src/views/system/message/template/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div :class="prefixCls">
+  <div :class="prefixCls" class="device-manager-box">
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <!--插槽:table标题-->
       <template #tableTitle>

+ 56 - 8
src/views/vent/comment/EditRowTable.vue

@@ -3,7 +3,10 @@
     <a-button v-if="isAdd" type="primary" @click="addRow"> 新增 </a-button>
     <BasicTable @register="registerTable" @edit-change="onEditChange">
       <template #action="{ record, column }">
-        <TableAction :actions="createActions(record, column)" />
+        <div class="vent-flex-row">
+          <TableAction :actions="createActions(record, column)" />
+        </div>
+        
       </template>
       <template #bodyCell="{ column, record }">
         <slot name="filterCell" v-bind="{ column, record }"> </slot>
@@ -12,7 +15,7 @@
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent, ref, nextTick } from 'vue';
+  import { defineComponent, ref, nextTick, watch} from 'vue';
   import { BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow } from '/@/components/Table';
   import { useMessage } from '/@/hooks/web/useMessage';
   // import { nextTick } from 'process';
@@ -24,9 +27,15 @@
         type: Array,
         requried: true,
       },
+      dataSource: {
+        type: Array,
+      },
+      searchFormSchema: {
+        type: Array,
+        default: () => []
+      },
       list: {
         type: Function,
-        requried: true,
       },
       isAdd: {
         type: Boolean,
@@ -36,13 +45,14 @@
       },
     },
     emits: ['saveOrUpdate', 'deleteById', 'rowChange'],
-    setup(props, { emit }) {
+    setup(props, { emit, expose }) {
       const { createMessage: msg } = useMessage();
       const currentEditKeyRef = ref('');
+      const dataList = ref<any[]>([])
 
-      const [registerTable, { insertTableDataRecord, reload, getSelectRows, getDataSource, setTableData }] = useTable({
+      const [registerTable, { insertTableDataRecord, reload, getSelectRows, getDataSource, setTableData }] = !props.list ?  useTable({
         title: '',
-        api: props.list,
+        dataSource: props.dataSource,
         rowKey: 'id',
         clickToRowSelect: false,
         columns: props.columns as BasicColumn[],
@@ -50,12 +60,33 @@
         showTableSetting: false,
         rowSelection: !props.isRadio ? undefined : { type: 'radio', onChange: rowChange },
         tableSetting: { fullScreen: true },
+        formConfig: {
+          labelWidth: 120,
+          schemas: props.searchFormSchema,
+          autoSubmitOnEnter: true,
+        },
         actionColumn: {
           width: 160,
           title: '操作',
           dataIndex: 'action',
           slots: { customRender: 'action' },
         },
+        }) : useTable({
+          title: '',
+          api: props.list,
+          rowKey: 'id',
+          clickToRowSelect: false,
+          columns: props.columns as BasicColumn[],
+          showIndexColumn: false,
+          showTableSetting: false,
+          rowSelection: !props.isRadio ? undefined : { type: 'radio', onChange: rowChange },
+          tableSetting: { fullScreen: true },
+          actionColumn: {
+            width: 160,
+            title: '操作',
+            dataIndex: 'action',
+            slots: { customRender: 'action' },
+          },
       });
 
       function rowChange(e) {
@@ -69,6 +100,7 @@
           handleEdit(record);
         });
       }
+
       function handleEdit(record: EditRecordRow) {
         currentEditKeyRef.value = record.key;
         record.onEdit?.(true);
@@ -137,6 +169,13 @@
                 disabled: currentEditKeyRef.value ? currentEditKeyRef.value !== record.key : false,
                 onClick: handleEdit.bind(null, record),
               },
+              {
+                label: '删除',
+                popConfirm: {
+                  title: '是否删除',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
             ];
           }
         }
@@ -145,14 +184,14 @@
             label: '保存',
             popConfirm: {
               title: '是否保存',
-              confirm: handleSave.bind(null, record, column),
+              confirm: handleSave.bind(null, record),
             },
           },
           {
             label: '取消',
             popConfirm: {
               title: '是否取消编辑',
-              confirm: handleCancel.bind(null, record, column),
+              confirm: handleCancel.bind(null, record),
             },
           },
         ];
@@ -166,6 +205,14 @@
         // console.log(column, value, record);
       }
 
+      watch(() => props.dataSource, (newVal:any[]) => {
+        setTableData(newVal)
+      })
+
+      expose({
+        reload
+      });
+
       return {
         registerTable,
         handleEdit,
@@ -174,6 +221,7 @@
         addRow,
         reload,
         getDataSource,
+        dataList
       };
     },
   });

+ 22 - 20
src/views/vent/comment/components/bottomMenu.vue

@@ -1,22 +1,16 @@
 <template>
   <div class="bottom-btn-group">
-    <div v-for="item in navList" :key="item.pathName" class="vent-row-center btn-item" @click="setBtn('click', item)">
-      <dv-decoration11
-        :color="isBtnActive === item.pathName ? activeColors : item.isHover ? activeColors : noActiveColors"
-        style="width:200px;height:60px;">
-        {{ item.title }}
-      </dv-decoration11>
+    <div v-for="item in navList" :key="item.pathName" class="vent-row-center btn-item" :class="{'btn-item-active': isBtnActive === item.pathName || item.isHover }" @click="setBtn('click', item)">
+      {{ item.title }}
     </div>
   </div>
 </template>
 <script lang="ts">
 import { ref, defineComponent } from 'vue'
-import { Decoration11 as DvDecoration11} from '@kjgl77/datav-vue3'
 type navListType = { title: string; pathName: string; isHover: Boolean };
 
 export default defineComponent({
   name: 'BottomMenu',
-  components: { DvDecoration11 },
   props: {
     navList: {
       type: Array<navListType>,
@@ -24,7 +18,7 @@ export default defineComponent({
         {
           title: '监控界面',
           pathName: 'monitor',
-          isHover: false
+          isHover: true
         },
         {
           title: '历史监测记录',
@@ -47,21 +41,21 @@ export default defineComponent({
   emits: ['change'],
   setup(props, {emit}) {
     const isBtnActive = ref(props.navList[0].pathName)
-    const activeColors = ['#009BFF', '#28DBE4']
-    const noActiveColors = ['#aaa', '#aaa']
 
     function setBtn(type, activeObj) {
-      if (type === 'over') {
-        activeObj.isHover = true
-      } else if (type === 'leave') {
-        activeObj.isHover = false
-      } else if (type === 'click') {
+      if (type === 'click') {
         isBtnActive.value = activeObj.pathName
+        activeObj.isHover = true
       }
+      props.navList.forEach(item => {
+        if(item.title !== activeObj.title){
+          activeObj.isHover = false
+        }
+      })
       emit('change', isBtnActive.value)
     }
     return {
-      activeColors, noActiveColors, setBtn, isBtnActive
+      setBtn, isBtnActive
     }
   },
 })
@@ -74,17 +68,25 @@ export default defineComponent({
     position: fixed;
     width: 100%;
     height: 100px;
-    bottom: 20px;
+    bottom: 10px;
     align-items: center;
     justify-content: center;
 
     .btn-item {
-      width: 200px;
-      height: 60px;
+      width: 182px;
+      height: 53px;
       margin: 10px;
       color: #fff;
       cursor: pointer;
+      padding-bottom: 2px;
       pointer-events: auto;
+      background: url('/@/assets/images/vent/bottom-btn.png');
+      font-family: 'ysbtFont';
+      font-size: 18px;
+    }
+    .btn-item-active {
+      background: url('/@/assets/images/vent/bottom-btn-active.png') !important;
+      color: #ffffff !important;
     }
   }
 </style>

+ 12 - 5
src/views/vent/comment/components/customHeader.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="vent-custom-header">
     <div class="vent-home-header">
-      <Decoration5 class="header-icon" :dur="2" :color="['#21437F', '#2CF7FE']" style="width:500px;height:40px;" />
+      <!-- <Decoration5 class="header-icon" :dur="2" :color="['#21437F', '#2CF7FE']" style="width:500px;height:40px;" /> -->
       <div class="header-text"><slot></slot></div>
     </div>
     <div class = "container-title" v-if="fieldNames">
@@ -79,10 +79,13 @@ export default defineComponent({
     width: 100%;
     .vent-home-header {
       width: 100%;
-      height: 100px;
+      
       position: fixed;
       top: 0;
-      background: url('/@/assets/images/vent/new-home/header-bg.png') no-repeat;
+      // background: url('/@/assets/images/vent/new-home/header-bg.png') no-repeat;
+      // height: 100px;
+      background: url('/@/assets/images/vent/vent-header1.png') no-repeat;
+      height: 89px;
       background-size: contain;
       display: flex;
       justify-content: center;
@@ -91,9 +94,13 @@ export default defineComponent({
       }
       .header-text{
         position: fixed;
-        top: 18px;
+        top: 5px;
         color: #fff;
-        font-size: 26px;
+        font-size: 32px;
+        font-family: 'ysbtFont';
+        background-image: linear-gradient(#ffffff 50%, #60f4ff);
+        -webkit-background-clip: text;
+        color: transparent;
       }
     }
     .container-title {

+ 2 - 1
src/views/vent/comment/threejs/Smoke.ts

@@ -88,6 +88,7 @@ export default class Smoke {
 
     // 创建点,并添加进场景
     this.points = new THREE.Points(this.geometry, material);
+    
   }
 
   startSmoke(duration = 15) {
@@ -162,7 +163,7 @@ export default class Smoke {
     const self = this;
     return new Promise((resolve) => {
       gsap.to(this, {
-        opacityFactor: -0.1,
+        opacityFactor: 0,
         life: 300,
         duration: duration,
         ease: 'easeInCubic',

+ 10 - 4
src/views/vent/deviceManager/comment/DeviceModal.vue

@@ -3,7 +3,7 @@
     v-bind="$attrs"
     @register="registerModal"
     :title="title"
-    width="900px"
+    width="1000px"
     :showCancelBtn="false"
     :showOkBtn="false"
     :footer="null"
@@ -26,8 +26,12 @@
       <a-tab-pane key="3" tab="设备关联" v-if="deviceType == 'managesys'">
         <WorkFacePointTable :columns="pointColumns" :deviceId="deviceData.id" @save="savePointData" @delete="deletePointById" />
       </a-tab-pane>
-      <a-tab-pane key="4" tab="报警字段配置">
-        <WarningTabel :deviceId="deviceData.id" :pointType="record.strtype" />
+      <a-tab-pane key="4" :tab="deviceType !== 'managesys' ? '报警配置' : '关联设备报警配置'">
+        <WarningTable v-if="deviceType !== 'managesys'" :deviceId="deviceData.id" :pointType="record.strtype" />
+        <ManagerWarningTable v-else :deviceId="deviceData.id" :pointType="record.strtype"/>
+      </a-tab-pane>
+      <a-tab-pane v-if="deviceType == 'managesys'" key = "6" tab="报警控制设备配置">
+        <ManagerWarningDeviceTable :deviceId="deviceData.id" :pointType="record.strtype"/>
       </a-tab-pane>
       <a-tab-pane key="5" tab="摄像头配置"
         ><EditRowTable
@@ -46,7 +50,9 @@
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import EditRowTable from '../../comment/EditRowTable.vue';
   import PointTable from './pointTabel/PointTable.vue';
-  import WarningTabel from './warningTabel/index.vue';
+  import WarningTable from './warningTabel/index.vue';
+  import ManagerWarningTable from './warningTabel/index1.vue';
+  import ManagerWarningDeviceTable from './warningTabel/index2.vue';
   import WorkFacePointTable from './pointTabel/WorkFacePointTable.vue';
   import FormModal from './FormModal.vue';
   import { cloneDeep } from 'lodash-es';

+ 9 - 1
src/views/vent/deviceManager/comment/NormalTable.vue

@@ -148,8 +148,15 @@
         width: 180,
       },
       beforeFetch: (params) => {
+        if(params['devicetype']) params['devicetype'] = params['devicetype']+'*'
         return Object.assign({ column: 'createTime', order: 'desc' }, params);
       },
+      exportConfig: {
+        url: props.getExportUrl,
+      },
+      importConfig: {
+        url: props.getImportUrl,
+      },
     },
     exportConfig: {
       name: props.title,
@@ -258,8 +265,9 @@
   }
 
   defineExpose({
-    doRequest,
+    doRequest, onExportXls, onImportXls,
   });
+
 </script>
 
 <style scoped lang="less">

+ 4 - 5
src/views/vent/deviceManager/comment/pointTabel/DeviceModalTable.vue

@@ -4,7 +4,7 @@
     v-bind="$attrs"
     @register="registerModal"
     title="选择关联设备"
-    width="900px"
+    width="1000px"
     :showCancelBtn="false"
     :showOkBtn="false"
     :footer="null"
@@ -18,7 +18,7 @@
         v-model:value="deviceKind"
         style="width: 200px"
       /> -->
-      <a-button type="primary" @click="saveData"> 保存 </a-button>
+      <a-button type="primary" @click="saveData"> 确定 </a-button>
     </div>
     <a-table
       :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
@@ -44,7 +44,6 @@
   });
   const emit = defineEmits(['reload', 'register']);
 
-  const deviceKind = ref('');
   const dataSource = ref<any[]>([]);
   const columns = ref<any[]>([]);
   const selectedRowKeys = ref([]);
@@ -57,7 +56,7 @@
 
   const getDataSource = async () => {
     columns.value = deviceColumns;
-    dataSource.value = await getDeviceId({ devicetype: deviceKind.value || props.deviceType, deviceID: props.deviceID, devicePos: props.devicePos });
+    dataSource.value = await getDeviceId({ devicetype: props.deviceType, deviceID: props.deviceID, devicePos: props.devicePos });
   };
 
   /**
@@ -71,7 +70,7 @@
   }
 
   const saveData = async () => {
-    await workDeviceEdit({ deviceKind: deviceKind.value || props.deviceType, ids: selectedRowKeys.value, sysid: props.deviceID });
+    await workDeviceEdit({ deviceKind: props.deviceType, ids: selectedRowKeys.value, sysid: props.deviceID });
     closeModal();
     emit('reload');
   };

+ 110 - 13
src/views/vent/deviceManager/comment/pointTabel/WorkFacePointTable.vue

@@ -12,8 +12,11 @@
         />
         <a-button type="primary" @click="addRow"> 新增 </a-button>
       </div>
-      <div>
-        <a-button type="primary" @click="handleSave()"> 保存 </a-button>
+      <a-button class="vent-margin-l-10" type="primary" @click="handleSave()"> 批量保存 </a-button>
+    </div>
+    <div class="monitor-nav">
+      <div class="device-button-group" v-if="relevanceDeviceData.length > 0">
+        <div class="device-button" :class="{ 'device-active': deviceActive == device.type }" v-for="(device, index) in relevanceDeviceData" :key="index" @click="deviceChange(index)">{{ device.typeName }}</div>
       </div>
     </div>
     <BasicTable ref="editTable" @register="registerTable" :dataSource="dataSource" :columns="workFaceColumns" @edit-change="onEditChange">
@@ -36,8 +39,13 @@
   import { useModal } from '/@/components/Modal';
   import { ApiTreeSelect } from '/@/components/Form';
   import DeviceModalTable from './DeviceModalTable.vue';
-  import { deviceId, workDeviceList, deviceList, list, edit } from './point.api';
+  import { deviceId, workDeviceList, deviceList, list, edit, workDeviceEdit, workRelevanceDeviceTypes, workRelevanceDeviceDelete } from './point.api';
   import { workFaceColumns, deviceColumns } from './point.data';
+  type RelevanceDeviceType = {
+    datalist: [],
+    type: string,
+    typeName: string
+  }
 
   export default defineComponent({
     components: { BasicTable, TableAction, DeviceModalTable, ApiTreeSelect },
@@ -71,6 +79,10 @@
       const dataSource = ref<any>([]);
       const selectionRowKeys = ref<any[]>([]);
 
+      const relevanceDeviceData = ref<RelevanceDeviceType[]>([])
+      const deviceNavData = ref<any[]>([]) // 关联设备
+      const deviceActive = ref('') // 激活状态下的设备
+
       const [registerModal, { openModal }] = useModal();
 
       const [registerTable, { insertTableDataRecord, reload }] = useTable({
@@ -86,6 +98,11 @@
         },
       });
 
+      async function getDeviceNavData() {
+        relevanceDeviceData.value = await workRelevanceDeviceTypes({ id: props.deviceId })
+        deviceChange(0)
+      }
+
       function addRow() {
         openModal();
       }
@@ -106,20 +123,31 @@
       async function getOptions() {
         const res = await deviceList({ devicetype: '' });
         options.value = res;
-        deviceKind.value = options.value[0]['itemValue'];
-        getWorkFaceList();
       }
 
       function getWorkFaceList() {
-        workDeviceList({ deviceKind: deviceKind.value, sysId: props.deviceId }).then((res) => {
-          dataSource.value = res['records'];
-          selectionRowKeys.value = dataSource.value.map((item) => item.deviceId) || [];
-        });
+        deviceActive.value = deviceKind.value
+        handleSuccess()
+      }
+      
+      function deviceChange(index) {
+        deviceActive.value = relevanceDeviceData.value[index].type
+        dataSource.value = relevanceDeviceData.value[index].datalist
       }
 
-      function handleChange(id) {
-        deviceKind.value = id;
-        getWorkFaceList();
+      function handleChange(value, label, extra) {
+        console.log('树节点选中------>',value, label, extra)
+        deviceKind.value = value;
+        const activeData = relevanceDeviceData.value.find(item => {
+          return item.type === value
+        })
+        if(activeData){
+          deviceActive.value = activeData.type
+          dataSource.value = activeData.datalist
+        }else{
+          deviceActive.value = ''
+          dataSource.value = []
+        }
       }
 
       async function handleSave(record?: EditRecordRow) {
@@ -134,18 +162,27 @@
           await edit(dataSource.value);
           record.editable = false;
         } else {
+          //workDeviceEdit
           if (editTable.value && editTable?.value.getDataSource) {
             const arr: any[] = [];
             const dataList = editTable?.value.getDataSource();
+            const ids = <any[]>[];
             dataList.forEach((element) => {
               arr.push(Object.assign(element, element.editValueRefs));
+              ids.push(element.deviceId)
             });
             dataSource.value = dataList;
+            // await workDeviceEdit({ deviceKind : deviceKind.value, ids: ids, sysid: props.deviceId }, handleSuccess)
             await edit(dataSource.value);
           }
         }
       }
 
+      async function handleSuccess() {
+        relevanceDeviceData.value = await workRelevanceDeviceTypes({ id: props.deviceId })
+        handleChange(deviceActive.value)
+      }
+
       function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
         if (!record.editable) {
           if (props.isAdd) {
@@ -168,6 +205,10 @@
                 disabled: currentEditKeyRef.value ? currentEditKeyRef.value !== record.key : false,
                 onClick: handleEdit.bind(null, record),
               },
+              {
+                label: '删除',
+                onClick: workRelevanceDeviceDelete.bind(null, { deviceid: record.deviceID, sysid: props.deviceId }, handleSuccess),
+              },
             ];
           }
         }
@@ -196,7 +237,8 @@
 
       onMounted(async () => {
         await getOptions();
-        getWorkFaceList();
+        await getDeviceNavData()
+        // getWorkFaceList();
       });
 
       return {
@@ -219,6 +261,11 @@
         handleSave,
         selectionRowKeys,
         getWorkFaceList,
+        relevanceDeviceData,
+        deviceNavData,
+        deviceActive,
+        deviceChange,
+        
         // handleChangeDeviceType,
         // handleChangeLinkCode,
         // monitorParamsOptions,
@@ -235,4 +282,54 @@
   .btm-group {
     padding-bottom: 2px;
   }
+  .device-button-group{
+    // margin: 0 20px;
+    display: flex;
+    flex-wrap: wrap;
+    pointer-events: auto;
+    position: relative;
+    margin-top: 10px;
+    margin-bottom: 5px;
+    &::after{
+      position:absolute;
+      content: '';
+      width: calc(100% + 10px);
+      height: 2px;
+      top: 30px;
+      left: -10px;
+      border-bottom: 1px solid #0efcff;
+    }
+    .device-button{
+      padding: 4px 10px;
+      position: relative;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 14px;
+      
+      color: #fff;
+      cursor: pointer;
+      margin: 0 1px;
+
+      &::before{
+        content: '';
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        border: 1px solid #6176AF;
+        // transform: skewX(-38deg);
+        background-color: rgba(0, 77, 103,85%);
+        z-index: -1;
+      }
+    }
+    .device-active{
+      // color: #0efcff;
+      &::before{
+        border-color: #0efcff;
+        box-shadow: 1px 1px 3px 1px #0efcff inset;
+      }
+    }
+  }
 </style>

+ 20 - 5
src/views/vent/deviceManager/comment/pointTabel/point.api.ts

@@ -24,6 +24,9 @@ enum Api {
 
   workDeviceList = '/safety/managesysDevice/list',
   workDeviceEdit = '/safety/managesysDevice/editlink',
+
+  workRelevanceDeviceTypes = '/safety/managesysDevice/typelist',
+  workRelevanceDeviceDelete = '/safety/managesysDevice/delete',
 }
 /**
  * 导出api
@@ -72,15 +75,15 @@ export const pointEdit = (params) => defHttp.put({ url: Api.pointEdit, params })
  */
 export const workDeviceList = (params) => defHttp.get({ url: Api.workDeviceList, params });
 
-export const workDeviceEdit = (params) => defHttp.post({ url: Api.workDeviceEdit, params });
+export const workDeviceEdit = (params) =>
+  defHttp.post({ url: Api.workDeviceEdit, params })
 
 export const warningList = (params) => defHttp.get({ url: Api.warningList, params });
 export const warningEdit = (params) => defHttp.put({ url: Api.warningEdit, params });
-export const warningDeleteById = (params, handleSuccess) => {
-  return defHttp.delete({ url: Api.deleteById, params }, { joinParamsToUrl: true }).then(() => {
-    handleSuccess();
-  });
+export const warningDeleteById = (params) => {
+  return defHttp.delete({ url: Api.deleteById, params }, { joinParamsToUrl: true })
 };
+
 export const warningDeleteBatch = (params, handleSuccess) => {
   Modal.confirm({
     title: '确认删除',
@@ -130,3 +133,15 @@ export const saveOrUpdate = (params, isUpdate) => {
   const url = isUpdate ? Api.edit : Api.save;
   return defHttp.put({ url: url, params });
 };
+
+/**
+ * 查询工作面关联设备类型列表
+ * @param params
+ */
+export const workRelevanceDeviceTypes = (params) => defHttp.get({ url: Api.workRelevanceDeviceTypes, params });
+
+export const workRelevanceDeviceDelete = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.workRelevanceDeviceDelete, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};

+ 78 - 0
src/views/vent/deviceManager/comment/warningTabel/DevicePointTable.vue

@@ -0,0 +1,78 @@
+<template>
+  <BasicModal
+    ref="DeviceModalTable"
+    v-bind="$attrs"
+    @register="registerModal"
+    title="选择关联设备预警点位"
+    width="1000px"
+    :showCancelBtn="false"
+    :showOkBtn="false"
+    :footer="null"
+    destroyOnClose
+  >
+    <div>
+      <a-button type="primary" @click="saveData"> 确定 </a-button>
+    </div>
+    <a-table
+      :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
+      :columns="columns"
+      :dataSource="dataSource"
+      :rowKey="(record) => record.id"
+      size="small"
+    />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, defineEmits, onUpdated, defineExpose } from 'vue';
+  import { workFacePointList, workFaceWarningBatchAdd } from './warning.api';
+  import { columns } from '../../pointTabel/point.data';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+
+  const DeviceModalTable = ref();
+  const props = defineProps({
+    deviceType: { type: String, default: '' },
+    deviceId: { type: String, default: '' },
+    sysId: { type: String, default: '' },
+    selectionRowKeys: { type: Array, default: () => [] },
+  });
+  const emit = defineEmits(['reload', 'register']);
+
+  const dataSource = ref<any[]>([]);
+  const selectedRowKeys = ref([]);
+  const selectionRows = ref([]);
+
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    setModalProps({ confirmLoading: false });
+  });
+
+  const getDataSource = async () => {
+    dataSource.value = await workFacePointList({ deviceType: props.deviceType, valueType: 2 });
+  };
+
+  /**
+   * 复选框选中事件
+   * @param rowKeys
+   * @param rows
+   */
+  function onSelectChange(rowKeys, rows) {
+    selectedRowKeys.value = rowKeys;
+    selectionRows.value = rows;
+  }
+
+  const saveData = async () => {
+    const monitorList = <any[]>[]
+    selectionRows.value.forEach(item => {
+      monitorList.push({ deviceId: props.deviceId,  monitorId: item['id'], monitorName: item['valuename'], sysId: props.sysId })
+    })
+    await workFaceWarningBatchAdd(monitorList)
+    closeModal();
+    emit('reload');
+  };
+
+  onMounted(async () => {
+    await getDataSource();
+  });
+
+</script>
+<style scoped lang="less"></style>

+ 175 - 0
src/views/vent/deviceManager/comment/warningTabel/index1.vue

@@ -0,0 +1,175 @@
+<template>
+  <div class="vent-flex-row">
+    <span style = "color: #fff;">关联设备:</span>
+    <a-select
+      v-model:value="sysDeviceId"
+      style="width: 200px"
+      placeholder="关联设备"
+      :options="sysDeviceOptions"
+      @change="handleChange"
+    />
+    <a-button class="vent-margin-l-8" type="primary" @click="addPoint"> 新增 </a-button>
+  </div>
+
+  <EditRowTableVue 
+    v-if="refresh"
+    ref="RefComponent"
+    :columns="manageWarningPointColumns"
+    :data-source="dataSource"
+    @save-or-update="saveOrUpdate"
+    @delete-by-id="handleDelete"
+    :isAdd="false"
+    style="margin-top: 10px"
+  >
+
+  </EditRowTableVue>
+
+  <DevicePointTable @register="registerModal" :device-type="sysDeviceType" :selection-row-keys="selectionRowKeys" :device-id="sysDeviceId" :sys-id="deviceId" @reload="reload"/>
+</template>
+
+<script lang="ts" setup>  
+  import { defineProps, ref, onMounted } from 'vue';
+  import { manageWarningPointColumns } from './warning.data';
+  import EditRowTableVue from '../../../comment/EditRowTable.vue';
+  import { workFaceWarningList,  workFaceWarningEdit, workFaceWarningDelete, workFaceDeviceList } from './warning.api';
+  import DevicePointTable from './DevicePointTable.vue';
+  import { useModal } from '/@/components/Modal';
+  
+  const props = defineProps({
+    deviceId: { type: String },
+    pointType: {
+      type: String,
+      requried: true,
+    },
+  });
+
+  const sysDeviceId = ref('') // 选中的关联设备的id
+  const sysDeviceType = ref('')
+  const selectionRowKeys = ref<any[]>([])
+  const sysDeviceOptions = ref<any[]>([])
+  let sysDeviceList = <any[]>[]
+  const RefComponent = ref()
+  const dataSource = ref<any[]>([])
+
+  const refresh = ref(true)
+
+  const [registerModal, { openModal }] = useModal();
+
+  const handleChange = async (value) => {
+    sysDeviceId.value = value
+    const obj = sysDeviceList.find((element) => {
+      return element.id == value
+    })
+    sysDeviceType.value = obj['strtype']
+    await getDevicePointList()
+  }
+
+  async function getDevicePointList() {
+
+    const result = await workFaceWarningList({ deviceId: sysDeviceId.value})
+
+    if(result && result.records.length > 0){
+      dataSource.value = result.records
+      const rowKeys = <any[]>[]
+      result.records.forEach((item) => {
+        rowKeys.push(item['id'])
+      })
+      selectionRowKeys.value = rowKeys
+    }else{
+      dataSource.value = []
+      selectionRowKeys.value = []
+    }
+  }
+  
+  async function getDeviceOptions() {
+    const result = await workFaceDeviceList({id: props.deviceId})
+    const options = <any[]>[]
+    if(result.length > 0){
+      result.forEach(element => {
+        options.push({ value: element.id, label: element.strname })
+      });
+      sysDeviceOptions.value = options
+      sysDeviceList = result
+    }
+    
+  }
+
+  async function saveOrUpdate(record) {
+    try {
+      if (record.id) {
+        await workFaceWarningEdit({ ...record });
+      }
+      RefComponent.value.reload()
+    } catch (error) { }
+  }
+
+  function handleDelete(id) {
+    workFaceWarningDelete({id}, getDevicePointList)
+  }
+
+  function reload() {
+    getDevicePointList()
+  }
+
+
+  function addPoint() {
+    openModal();
+  }
+
+  onMounted(async () => {
+    await getDeviceOptions()
+  });
+</script>
+
+<style lang="less" scoped>
+  .device-button-group{
+    // margin: 0 20px;
+    display: flex;
+    flex-wrap: wrap;
+    pointer-events: auto;
+    position: relative;
+    margin-top: 10px;
+    margin-bottom: 5px;
+    &::after{
+      position:absolute;
+      content: '';
+      width: calc(100% + 10px);
+      height: 2px;
+      top: 30px;
+      left: -10px;
+      border-bottom: 1px solid #0efcff;
+    }
+    .device-button{
+      padding: 4px 10px;
+      position: relative;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 14px;
+      
+      color: #fff;
+      cursor: pointer;
+      margin: 0 1px;
+
+      &::before{
+        content: '';
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        border: 1px solid #6176AF;
+        // transform: skewX(-38deg);
+        background-color: rgba(0, 77, 103,85%);
+        z-index: -1;
+      }
+    }
+    .device-active{
+      // color: #0efcff;
+      &::before{
+        border-color: #0efcff;
+        box-shadow: 1px 1px 3px 1px #0efcff inset;
+      }
+    }
+  }
+</style>

+ 198 - 0
src/views/vent/deviceManager/comment/warningTabel/index2.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="vent-flex-row">
+    <div class="vent-flex-row ">
+      <span style = "color: #fff;">预警监测设备:</span>
+      <a-select
+        v-model:value="sysDeviceId"
+        style="width: 180px"
+        placeholder="预警监测设备"
+        :options="sysDeviceOptions"
+        @change="handleChange"
+      />
+    </div>
+    <div class="vent-flex-row vent-padding-lr-5">
+      <span style = "color: #fff;">预警条目:</span>
+      <a-select
+        v-model:value="sysDeviceId"
+        style="width: 180px"
+        placeholder="监测设备预警条目"
+        :options="warningOptions"
+        @change="handleChange"
+      />
+    </div>
+    <div class="vent-flex-row">
+      <span style = "color: #fff;">预警控制设备:</span>
+      <a-select
+        v-model:value="sysDeviceId"
+        style="width: 180px"
+        placeholder="预警控制设备"
+        :options="warningOptions"
+        @change="handleChange"
+      />
+    </div>
+    <a-button class="vent-margin-l-8" type="primary" @click="addPoint"> 新增 </a-button>
+  </div>
+
+  <EditRowTableVue 
+    v-if="refresh"
+    ref="RefComponent"
+    :columns="manageWarningPointColumns"
+    :data-source="dataSource"
+    @save-or-update="saveOrUpdate"
+    @delete-by-id="handleDelete"
+    :isAdd="false"
+    style="margin-top: 10px"
+  >
+
+  </EditRowTableVue>
+
+  <DevicePointTable @register="registerModal" :device-type="sysDeviceType" :selection-row-keys="selectionRowKeys" :device-id="sysDeviceId" :sys-id="deviceId" @reload="reload"/>
+</template>
+
+<script lang="ts" setup>  
+  import { defineProps, ref, onMounted } from 'vue';
+  import { manageWarningPointColumns } from './warning.data';
+  import EditRowTableVue from '../../../comment/EditRowTable.vue';
+  import { workFaceWarningList, workFaceWarningEdit, workFaceWarningDelete, workFaceDeviceList, workFacePointList } from './warning.api';
+  import DevicePointTable from './DevicePointTable.vue';
+  import { useModal } from '/@/components/Modal';
+  
+  const props = defineProps({
+    deviceId: { type: String },
+    pointType: {
+      type: String,
+      requried: true,
+    },
+  });
+
+  const sysDeviceId = ref('') // 选中的关联设备的id
+  const sysDeviceType = ref('')
+  const selectionRowKeys = ref<any[]>([])
+  const sysDeviceOptions = ref<any[]>([])
+  const warningOptions = ref<any[]>([]) //设备预警条目列表
+  let sysDeviceList = <any[]>[]
+  const RefComponent = ref()
+  const dataSource = ref<any[]>([])
+
+  const refresh = ref(true)
+
+  const [registerModal, { openModal }] = useModal();
+
+  const handleChange = async (value) => {
+    sysDeviceId.value = value
+    const obj = sysDeviceList.find((element) => {
+      return element.id == value
+    })
+    sysDeviceType.value = obj['strtype']
+    await getDevicePointList()
+  }
+
+  async function getDevicePointList() {
+    
+    const result = await workFaceWarningList({ deviceId: sysDeviceId.value})
+
+    if(result && result.records.length > 0){
+      dataSource.value = result.records
+      const rowKeys = <any[]>[]
+      result.records.forEach((item) => {
+        rowKeys.push(item['id'])
+      })
+      selectionRowKeys.value = rowKeys
+    }else{
+      dataSource.value = []
+      selectionRowKeys.value = []
+    }
+  }
+  
+  async function getDeviceOptions() {
+    const result = await workFaceDeviceList({id: props.deviceId})
+    const options = <any[]>[]
+    if(result.length > 0){
+      result.forEach(element => {
+        options.push({ value: element.id, label: element.strname })
+      });
+      sysDeviceOptions.value = options
+      sysDeviceList = result
+    }
+    
+  }
+
+  async function saveOrUpdate(record) {
+    try {
+      if (record.id) {
+        await workFaceWarningEdit({ ...record });
+      }
+      RefComponent.value.reload()
+    } catch (error) { }
+  }
+
+  function handleDelete(id) {
+    workFaceWarningDelete({id}, getDevicePointList)
+  }
+
+  function reload() {
+    getDevicePointList()
+  }
+
+
+  function addPoint() {
+    openModal();
+  }
+
+  onMounted(async () => {
+    await getDeviceOptions()
+  });
+</script>
+
+<style lang="less" scoped>
+  .device-button-group{
+    // margin: 0 20px;
+    display: flex;
+    flex-wrap: wrap;
+    pointer-events: auto;
+    position: relative;
+    margin-top: 10px;
+    margin-bottom: 5px;
+    &::after{
+      position:absolute;
+      content: '';
+      width: calc(100% + 10px);
+      height: 2px;
+      top: 30px;
+      left: -10px;
+      border-bottom: 1px solid #0efcff;
+    }
+    .device-button{
+      padding: 4px 10px;
+      position: relative;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 14px;
+      
+      color: #fff;
+      cursor: pointer;
+      margin: 0 1px;
+
+      &::before{
+        content: '';
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        border: 1px solid #6176AF;
+        // transform: skewX(-38deg);
+        background-color: rgba(0, 77, 103,85%);
+        z-index: -1;
+      }
+    }
+    .device-active{
+      // color: #0efcff;
+      &::before{
+        border-color: #0efcff;
+        box-shadow: 1px 1px 3px 1px #0efcff inset;
+      }
+    }
+  }
+</style>

+ 27 - 0
src/views/vent/deviceManager/comment/warningTabel/warning.api.ts

@@ -14,6 +14,14 @@ enum Api {
 
   importExcel = '/sys/user/importExcel',
   exportXls = '/sys/user/exportXls',
+
+  workFaceDeviceList = '/safety/managesysDevice/getManagesDeviceInfo',
+  workFaceWarningList = '/safety/managesysAlarm/list',
+  workFaceWarningAdd = '/safety/managesysAlarm/add',
+  workFaceWarningBatchAdd = '/safety/managesysAlarm/addBatch',
+  workFaceWarningEdit = '/safety/managesysAlarm/edit',
+  workFaceWarningDelete = '/safety/managesysAlarm/delete',
+  workFacePointList = '/safety/ventanalyMonitorParams/getListByType', // 参数1 :设置点位 2:读取点位
 }
 /**
  * 导出api
@@ -63,3 +71,22 @@ export const saveOrUpdate = (params, isUpdate) => {
 };
 
 export const limitList = (params) => defHttp.get({ url: Api.limitList, params });
+
+// 下面是工作面预警点表管理
+export const workFaceDeviceList = (params) => defHttp.get({ url: Api.workFaceDeviceList, params });
+
+export const workFaceWarningList = (params) => defHttp.get({ url: Api.workFaceWarningList, params });
+
+export const workFaceWarningAdd = (params) => defHttp.post({ url: Api.workFaceWarningAdd, params });
+
+export const workFaceWarningBatchAdd = (params) => defHttp.post({ url: Api.workFaceWarningBatchAdd, params });
+
+export const workFaceWarningEdit = (params) => defHttp.put({ url: Api.workFaceWarningEdit, params });
+
+export const workFaceWarningDelete = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.workFaceWarningDelete, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+
+export const workFacePointList = (params) => defHttp.get({ url: Api.workFacePointList, params });

+ 40 - 1
src/views/vent/deviceManager/comment/warningTabel/warning.data.ts

@@ -1,5 +1,7 @@
-import { BasicColumn } from '/@/components/Table';
+import { BasicColumn, FormSchema } from '/@/components/Table';
 import { initDictOptions } from '/@/utils/dict';
+import { defHttp } from '/@/utils/http/axios';
+const list = '/safety/ventanalyManageSystem/list';
 
 export const levelColumns: BasicColumn[] = [
   {
@@ -186,3 +188,40 @@ export const workFaceColumns: BasicColumn[] = [
     },
   },
 ];
+
+export const manageWarningPointColumns: BasicColumn[] = [
+  {
+    title: '报警条目',
+    dataIndex: 'monitorName',
+    align: 'center',
+  },
+  {
+    title: '报警下限',
+    dataIndex: 'fmin',
+    align: 'center',
+    edit: true,
+    editComponent: 'InputNumber',
+  },
+  {
+    title: '报警上限',
+    dataIndex: 'fmax',
+    align: 'center',
+    edit: true,
+    editComponent: 'InputNumber',
+  },
+  {
+    title: '报警持续时间(s)',
+    dataIndex: 'cxTime',
+    align: 'center',
+    edit: true,
+    editComponent: 'InputNumber',
+  },
+  {
+    title: '更新人',
+    dataIndex: 'updateBy',
+    align: 'center',
+    width: 100,
+  },
+];
+
+

+ 3 - 3
src/views/vent/deviceManager/pointTabel/point.api.ts

@@ -7,9 +7,9 @@ enum Api {
   edit = '/safety/ventanalyMonitorParams/edit',
   selectDevice = '/jeecg-system/sys/dict/DeviceKind/query',
   deleteById = '/safety/ventanalyMonitorParams/delete',
-  deleteBatch = '/sys/user/deleteBatch',
-  importExcel = '/sys/user/importExcel',
-  exportXls = '/sys/user/exportXls',
+  importExcel = '/safety/ventanalyMonitorParams/importExcel',
+  deleteBatch = '/safety/ventanalyMonitorParams/deleteBatch',
+  exportXls = '/safety/ventanalyMonitorParams/exportXls',
   queryDevice = '/sys/dict/DeviceKind/query',
 }
 /**

+ 2 - 4
src/views/vent/deviceManager/pointTabel/point.data.ts

@@ -9,7 +9,7 @@ export const columns: BasicColumn[] = [
     dataIndex: 'devicekind_dictText',
     width: 120,
   },
- 
+
   {
     title: '值名称',
     dataIndex: 'valuename',
@@ -60,7 +60,7 @@ export const columns: BasicColumn[] = [
 export const searchFormSchema: FormSchema[] = [
   {
     label: '设备类型',
-    field: 'devicekind',
+    field: 'devicetype',
     component: 'MTreeSelect',
     colProps: { span: 6 },
   },
@@ -125,9 +125,7 @@ export const formSchema: FormSchema[] = [
     field: 'devicetype',
     component: 'ApiSelect',
     componentProps: ({ formModel, formActionType }) => {
-      // debugger;
       return {
-        // options: provincesOptions,
         componentProps: {},
         api: (params) => defHttp.get({ url: `/sys/dict/getDictItems/${formModel.devicekind + 'kind'}` }, params),
         resultField: 'result',

+ 2 - 3
src/views/vent/deviceManager/tableColumns/tableColumns.api.ts

@@ -7,9 +7,8 @@ enum Api {
   edit = '/safety/ventanalyShowColum/edit',
   selectDevice = '/jeecg-system/sys/dict/DeviceKind/query',
   deleteById = '/safety/ventanalyShowColum/delete',
-  deleteBatch = '/sys/user/deleteBatch',
-  importExcel = '/sys/user/importExcel',
-  exportXls = '/sys/user/exportXls',
+  importExcel = '/safety/ventanalyShowColum/importExcel',
+  exportXls = '/safety/ventanalyShowColum/exportXls',
 }
 /**
  * 导出api

+ 11 - 1
src/views/vent/deviceManager/tableColumns/tableColumns.data.ts

@@ -38,11 +38,21 @@ export const columns: BasicColumn[] = [
 export const searchFormSchema: FormSchema[] = [
   {
     label: '设备类型',
-    field: 'devicekind',
+    field: 'devicetype',
     component: 'MTreeSelect',
     colProps: { span: 6 },
   },
   {
+    label: '页面类型',
+    field: 'pagetype',
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'pagetype',
+      placeholder: '请选择状态',
+    },
+    colProps: { span: 6 },
+  },
+  {
     label: '值名称',
     field: 'valuename',
     component: 'Input',

+ 1 - 1
src/views/vent/monitorManager/chamberMonitor/chamber.api.ts

@@ -2,7 +2,7 @@ import { defHttp } from '/@/utils/http/axios';
 import { Modal } from 'ant-design-vue';
 
 enum Api {
-  list = '/monitor/device',
+  list = '/ventanaly-device/monitor/device',
   baseList = '/safety/ventanalyDeviceInfo/list',
 }
 /**

+ 1 - 1
src/views/vent/monitorManager/chamberMonitor/chamber.data.ts

@@ -55,7 +55,7 @@ export const dustColumns: BasicColumn[] = [
     align: 'center',
   },
   {
-    title: '操作',
+    title: '启停',
     dataIndex: 'action',
     width: 100,
     align: 'center',

+ 11 - 1
src/views/vent/monitorManager/chamberMonitor/chamber.threejs.base.ts

@@ -1,4 +1,5 @@
 import * as THREE from 'three';
+import { setModalCenter } from '/@/utils/threejs/util';
 // import * as dat from 'dat.gui';
 // const gui = new dat.GUI();
 // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
@@ -86,7 +87,16 @@ class ChamberBase {
 
   mountedThree() {
     return new Promise((resolve) => {
-      this.model.setModel([this.modelName]).then((gltf) => {
+      // this.model.setFBXModel(['chamber1']).then((gltf) => {
+      //   this.group = gltf[0];
+      //   if (this.group) {
+      //     this.group?.scale.set(0.1, 0.1, 0.1);
+      //     this.group.position.y += 40;
+      //     resolve(null);
+      //     this.addLight();
+      //   }
+      // });
+      this.model.setGLTFModel([this.modelName]).then((gltf) => {
         this.group = gltf[0];
         if (this.group) {
           this.group?.scale.set(0.1, 0.1, 0.1);

+ 2 - 1
src/views/vent/monitorManager/chamberMonitor/chamber.threejs.ts

@@ -1,7 +1,7 @@
 import * as THREE from 'three';
 import UseThree from '../../../../utils/threejs/useThree';
 import ChamberBase from './chamber.threejs.base';
-import { animateCamera } from '/@/utils/threejs/util';
+import { animateCamera, setModalCenter } from '/@/utils/threejs/util';
 import { useAppStore } from '/@/store/modules/app';
 
 // 模型对象、 文字对象
@@ -68,6 +68,7 @@ export const setModelType = (type) => {
       group = chamberBaseObj.group;
       const oldCameraPosition = { x: 124.736, y: 63.486, z: 103.337 };
       model.scene.add(chamberBaseObj.group);
+      setModalCenter(model.scene);
       model.camera.position.set(0, 0, 300);
       setTimeout(async () => {
         // const position = { x: 0, y: 3.8, z: 10.5 };

+ 1 - 1
src/views/vent/monitorManager/chamberMonitor/components/chamberHome.vue

@@ -136,7 +136,7 @@
           </ventBox1>
           <ventBox1 class="vent-margin-t-10">
             <template #title>
-              <div>{{ groupNum }}喷粉监控</div>
+              <div>{{  }}喷粉监控</div>
             </template>
             <template #container>
               <a-table :columns="dustColumns" :data-source="dustDataSource" :pagination="false" size="small" maxWidth="340"

+ 3 - 2
src/views/vent/monitorManager/chamberMonitor/index.vue

@@ -157,12 +157,13 @@ onUnmounted(() => {
     padding: 0 20px;
     .history-container{
       position: relative;
-      background: #6176AF11;
+      background: #6195af1a;
       width: calc(100% + 10px);
       top: 0px;
       left: -10px;
-      border: 1px solid #ffffff22;
+      border: 1px solid #00fffd22;
       padding: 10px 0;
+      box-shadow: 0 0 20px #44b4ff33 inset;
     }
   }
   .device-button-group{

+ 9 - 0
src/views/vent/monitorManager/compressor/components/nitrogenHome.vue

@@ -202,6 +202,15 @@ const deviceParameterData = [
 
 const monitorData = ref(new Array(3).fill({
   strName: '空压机',
+  cumulativeFlow: '-',
+  centerTemperature: '-',
+  outletTemperature: '-',
+  Ia: '-',
+  Ib: '-',
+  Ic: '-',
+  Vab: '-',
+  Vac: '-',
+  Vbc: '-',
   compressGroupName: '',
   compressExhaustPressF1: '-',
   compressSeparatePressF1: '-',

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