浏览代码

初始化2.0.0

tangning 1 周之前
父节点
当前提交
13c63f922f
共有 52 个文件被更改,包括 11748 次插入662 次删除
  1. 1755 0
      package-lock.json
  2. 5 0
      src/App.vue
  3. 5 2
      src/app/criminal/routeConfig.ts
  4. 1238 111
      src/assets/icon/fuse/demo_index.html
  5. 200 4
      src/assets/icon/fuse/iconfont.css
  6. 1 1
      src/assets/icon/fuse/iconfont.js
  7. 345 2
      src/assets/icon/fuse/iconfont.json
  8. 二进制
      src/assets/icon/fuse/iconfont.ttf
  9. 二进制
      src/assets/icon/fuse/iconfont.woff
  10. 二进制
      src/assets/icon/fuse/iconfont.woff2
  11. 二进制
      src/assets/icon/fuse/oldIcon.zip
  12. 4072 0
      src/assets/style/animate.css
  13. 30 5
      src/assets/style/public.scss
  14. 13 4
      src/components/viewImg/index.vue
  15. 10 4
      src/request/urls.ts
  16. 36 18
      src/router/config.ts
  17. 2 1
      src/router/index.ts
  18. 3 0
      src/router/routeName.ts
  19. 3 0
      src/store/camera.ts
  20. 23 2
      src/store/case.ts
  21. 2 0
      src/util/index.ts
  22. 206 0
      src/util/platform.ts
  23. 329 153
      src/view/abstract/index.vue
  24. 1 1
      src/view/case/draw/index.vue
  25. 440 135
      src/view/case/draw/leaflet.ChineseTmsProviders.js
  26. 572 0
      src/view/case/draw/leaflet.mapCorrection.js
  27. 0 0
      src/view/case/draw/loginDialog.vue
  28. 2 1
      src/view/case/draw/selectMapImage.vue
  29. 6 1
      src/view/case/draw/selectMapleaftImages.vue
  30. 2 2
      src/view/case/draw/share.vue
  31. 3 3
      src/view/case/quisk.ts
  32. 51 61
      src/view/dossier/index.vue
  33. 69 74
      src/view/layout/index.vue
  34. 272 0
      src/view/layout/loginDialog.vue
  35. 33 5
      src/view/layout/slide/index.vue
  36. 11 3
      src/view/layout/top/index.vue
  37. 8 8
      src/view/layout/top/style.scss
  38. 184 0
      src/view/material/AddScenesImg.vue
  39. 218 0
      src/view/material/addCaseFile.vue
  40. 189 0
      src/view/material/addLibrary.vue
  41. 148 0
      src/view/material/addScenes.vue
  42. 67 0
      src/view/material/list.vue
  43. 54 0
      src/view/material/pagging.ts
  44. 145 0
      src/view/material/photos.vue
  45. 39 0
      src/view/material/quisk.ts
  46. 140 0
      src/view/material/rollMaking.vue
  47. 148 0
      src/view/material/rollMakingList.vue
  48. 407 0
      src/view/material/sceneImg.vue
  49. 139 0
      src/view/material/setType.vue
  50. 79 42
      src/view/originalPhoto/index.vue
  51. 14 12
      src/view/vrmodel/index.vue
  52. 29 7
      vite.config.ts

文件差异内容过多而无法显示
+ 1755 - 0
package-lock.json


+ 5 - 0
src/App.vue

@@ -10,10 +10,15 @@
 <script setup lang="ts">
 import Locale from "@/config/locale.vue";
 import { setToken } from "@/store/user";
+import { sceneType } from "@/store/case";
 import { strToParams } from "@/util";
 const params = strToParams(window.location.search);
+console.log("params", params);
 if (params.token) {
   setToken(params.token);
 }
+if(params.power){
+  sceneType.value = params.power;
+}
 console.log("params", params);
 </script>

+ 5 - 2
src/app/criminal/routeConfig.ts

@@ -8,10 +8,13 @@ export const CriminalRouteName = {
 
 export const menuRouteNames = [
   // CriminalRouteName.statistics,
-  CriminalRouteName.vrmodel,
   CriminalRouteName.abstract,
+  CriminalRouteName.vrmodel,
   CriminalRouteName.originalPhoto,
-  CriminalRouteName.material,
+  // CriminalRouteName.material,
+  CriminalRouteName.photos,
+  CriminalRouteName.rollMaking,
+  CriminalRouteName.sceneImg,
   CriminalRouteName.other,
   CriminalRouteName.aiList,
   CriminalRouteName.dossier,

文件差异内容过多而无法显示
+ 1238 - 111
src/assets/icon/fuse/demo_index.html


+ 200 - 4
src/assets/icon/fuse/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
-  font-family: "iconfont"; /* Project id 3549513 */
-  src: url('iconfont.woff2?t=1706348932330') format('woff2'),
-       url('iconfont.woff?t=1706348932330') format('woff'),
-       url('iconfont.ttf?t=1706348932330') format('truetype');
+  font-family: "iconfont"; /* Project id 4647199 */
+  src: url('iconfont.woff2?t=1753069447778') format('woff2'),
+       url('iconfont.woff?t=1753069447778') format('woff'),
+       url('iconfont.ttf?t=1753069447778') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,202 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-window_n:before {
+  content: "\e7b4";
+}
+
+.icon-window_m:before {
+  content: "\e7b5";
+}
+
+.icon-screen_s:before {
+  content: "\e7b2";
+}
+
+.icon-a-3D_scence:before {
+  content: "\e7b3";
+}
+
+.icon-media:before {
+  content: "\e7af";
+}
+
+.icon-rename:before {
+  content: "\e7b0";
+}
+
+.icon-display:before {
+  content: "\e7b1";
+}
+
+.icon-line_d:before {
+  content: "\e79e";
+}
+
+.icon-a-label_s:before {
+  content: "\e79d";
+}
+
+.icon-guide_p:before {
+  content: "\e79c";
+}
+
+.icon-view:before {
+  content: "\e79b";
+}
+
+.icon-ratio:before {
+  content: "\e799";
+}
+
+.icon-a-1b1:before {
+  content: "\e79a";
+}
+
+.icon-reset:before {
+  content: "\e65a";
+}
+
+.icon-menu:before {
+  content: "\e798";
+}
+
+.icon-keys_a:before {
+  content: "\e797";
+}
+
+.icon-add_a:before {
+  content: "\e796";
+}
+
+.icon-rectification:before {
+  content: "\e78a";
+}
+
+.icon-a-zoom:before {
+  content: "\e78b";
+}
+
+.icon-a-play:before {
+  content: "\e78c";
+}
+
+.icon-a-animation_s:before {
+  content: "\e78d";
+}
+
+.icon-keys:before {
+  content: "\e78e";
+}
+
+.icon-a-anchor:before {
+  content: "\e78f";
+}
+
+.icon-a-monitoring_s:before {
+  content: "\e790";
+}
+
+.icon-a-rotate:before {
+  content: "\e791";
+}
+
+.icon-a-move:before {
+  content: "\e792";
+}
+
+.icon-a-path_s:before {
+  content: "\e793";
+}
+
+.icon-a-pause:before {
+  content: "\e794";
+}
+
+.icon-a-guide_s:before {
+  content: "\e795";
+}
+
+.icon-magnify:before {
+  content: "\e788";
+}
+
+.icon-reduce:before {
+  content: "\e789";
+}
+
+.icon-pic_path:before {
+  content: "\e778";
+}
+
+.icon-list_s:before {
+  content: "\e776";
+}
+
+.icon-message_c:before {
+  content: "\e777";
+}
+
+.icon-pen:before {
+  content: "\e750";
+}
+
+.icon-show_dot_s:before {
+  content: "\e64f";
+}
+
+.icon-show_dot_n:before {
+  content: "\e657";
+}
+
+.icon-show_pic_n:before {
+  content: "\e650";
+}
+
+.icon-show_pic_s:before {
+  content: "\e658";
+}
+
+.icon-show_3d_s:before {
+  content: "\e72b";
+}
+
+.icon-show_3d_n:before {
+  content: "\e72e";
+}
+
+.icon-show_roaming_s:before {
+  content: "\e730";
+}
+
+.icon-show_roaming_n:before {
+  content: "\e731";
+}
+
+.icon-index:before {
+  content: "\e76d";
+}
+
+.icon-symbol:before {
+  content: "\e76e";
+}
+
+.icon-arrows1:before {
+  content: "\e76f";
+}
+
+.icon-text1:before {
+  content: "\e770";
+}
+
+.icon-fire_firmware:before {
+  content: "\e767";
+}
+
+.icon-fire_app:before {
+  content: "\e768";
+}
+
 .icon-fire_statistics:before {
   content: "\e74d";
 }

文件差异内容过多而无法显示
+ 1 - 1
src/assets/icon/fuse/iconfont.js


+ 345 - 2
src/assets/icon/fuse/iconfont.json

@@ -1,11 +1,354 @@
 {
-  "id": "3549513",
-  "name": "融合平台",
+  "id": "4647199",
+  "name": "多元数据融合系统",
   "font_family": "iconfont",
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
     {
+      "icon_id": "44890707",
+      "name": "window_n",
+      "font_class": "window_n",
+      "unicode": "e7b4",
+      "unicode_decimal": 59316
+    },
+    {
+      "icon_id": "44890706",
+      "name": "window_m",
+      "font_class": "window_m",
+      "unicode": "e7b5",
+      "unicode_decimal": 59317
+    },
+    {
+      "icon_id": "44889955",
+      "name": "screen_s",
+      "font_class": "screen_s",
+      "unicode": "e7b2",
+      "unicode_decimal": 59314
+    },
+    {
+      "icon_id": "44889954",
+      "name": "3D_scence",
+      "font_class": "a-3D_scence",
+      "unicode": "e7b3",
+      "unicode_decimal": 59315
+    },
+    {
+      "icon_id": "44887769",
+      "name": "media",
+      "font_class": "media",
+      "unicode": "e7af",
+      "unicode_decimal": 59311
+    },
+    {
+      "icon_id": "44887770",
+      "name": "rename",
+      "font_class": "rename",
+      "unicode": "e7b0",
+      "unicode_decimal": 59312
+    },
+    {
+      "icon_id": "44887771",
+      "name": "display",
+      "font_class": "display",
+      "unicode": "e7b1",
+      "unicode_decimal": 59313
+    },
+    {
+      "icon_id": "43973886",
+      "name": "line_d",
+      "font_class": "line_d",
+      "unicode": "e79e",
+      "unicode_decimal": 59294
+    },
+    {
+      "icon_id": "43913586",
+      "name": "a-label_s",
+      "font_class": "a-label_s",
+      "unicode": "e79d",
+      "unicode_decimal": 59293
+    },
+    {
+      "icon_id": "43886962",
+      "name": "guide_p",
+      "font_class": "guide_p",
+      "unicode": "e79c",
+      "unicode_decimal": 59292
+    },
+    {
+      "icon_id": "43623992",
+      "name": "view",
+      "font_class": "view",
+      "unicode": "e79b",
+      "unicode_decimal": 59291
+    },
+    {
+      "icon_id": "43616883",
+      "name": "ratio",
+      "font_class": "ratio",
+      "unicode": "e799",
+      "unicode_decimal": 59289
+    },
+    {
+      "icon_id": "43616882",
+      "name": "1b1",
+      "font_class": "a-1b1",
+      "unicode": "e79a",
+      "unicode_decimal": 59290
+    },
+    {
+      "icon_id": "25654903",
+      "name": "reset",
+      "font_class": "reset",
+      "unicode": "e65a",
+      "unicode_decimal": 58970
+    },
+    {
+      "icon_id": "43615153",
+      "name": "menu",
+      "font_class": "menu",
+      "unicode": "e798",
+      "unicode_decimal": 59288
+    },
+    {
+      "icon_id": "43559284",
+      "name": "keys_a",
+      "font_class": "keys_a",
+      "unicode": "e797",
+      "unicode_decimal": 59287
+    },
+    {
+      "icon_id": "43559283",
+      "name": "add_a",
+      "font_class": "add_a",
+      "unicode": "e796",
+      "unicode_decimal": 59286
+    },
+    {
+      "icon_id": "43549167",
+      "name": "rectification",
+      "font_class": "rectification",
+      "unicode": "e78a",
+      "unicode_decimal": 59274
+    },
+    {
+      "icon_id": "43549165",
+      "name": "a-zoom",
+      "font_class": "a-zoom",
+      "unicode": "e78b",
+      "unicode_decimal": 59275
+    },
+    {
+      "icon_id": "43549159",
+      "name": "a-play",
+      "font_class": "a-play",
+      "unicode": "e78c",
+      "unicode_decimal": 59276
+    },
+    {
+      "icon_id": "43549156",
+      "name": "a-animation_s",
+      "font_class": "a-animation_s",
+      "unicode": "e78d",
+      "unicode_decimal": 59277
+    },
+    {
+      "icon_id": "43549164",
+      "name": "keys",
+      "font_class": "keys",
+      "unicode": "e78e",
+      "unicode_decimal": 59278
+    },
+    {
+      "icon_id": "43549163",
+      "name": "a-anchor",
+      "font_class": "a-anchor",
+      "unicode": "e78f",
+      "unicode_decimal": 59279
+    },
+    {
+      "icon_id": "43549160",
+      "name": "a-monitoring_s",
+      "font_class": "a-monitoring_s",
+      "unicode": "e790",
+      "unicode_decimal": 59280
+    },
+    {
+      "icon_id": "43549162",
+      "name": "a-rotate",
+      "font_class": "a-rotate",
+      "unicode": "e791",
+      "unicode_decimal": 59281
+    },
+    {
+      "icon_id": "43549161",
+      "name": "a-move",
+      "font_class": "a-move",
+      "unicode": "e792",
+      "unicode_decimal": 59282
+    },
+    {
+      "icon_id": "43549158",
+      "name": "a-path_s",
+      "font_class": "a-path_s",
+      "unicode": "e793",
+      "unicode_decimal": 59283
+    },
+    {
+      "icon_id": "43549157",
+      "name": "a-pause",
+      "font_class": "a-pause",
+      "unicode": "e794",
+      "unicode_decimal": 59284
+    },
+    {
+      "icon_id": "43549155",
+      "name": "a-guide_s",
+      "font_class": "a-guide_s",
+      "unicode": "e795",
+      "unicode_decimal": 59285
+    },
+    {
+      "icon_id": "43549168",
+      "name": "magnify",
+      "font_class": "magnify",
+      "unicode": "e788",
+      "unicode_decimal": 59272
+    },
+    {
+      "icon_id": "43549166",
+      "name": "reduce",
+      "font_class": "reduce",
+      "unicode": "e789",
+      "unicode_decimal": 59273
+    },
+    {
+      "icon_id": "42880568",
+      "name": "pic_path",
+      "font_class": "pic_path",
+      "unicode": "e778",
+      "unicode_decimal": 59256
+    },
+    {
+      "icon_id": "42851849",
+      "name": "list_s",
+      "font_class": "list_s",
+      "unicode": "e776",
+      "unicode_decimal": 59254
+    },
+    {
+      "icon_id": "42851848",
+      "name": "message_c",
+      "font_class": "message_c",
+      "unicode": "e777",
+      "unicode_decimal": 59255
+    },
+    {
+      "icon_id": "39993146",
+      "name": "pen",
+      "font_class": "pen",
+      "unicode": "e750",
+      "unicode_decimal": 59216
+    },
+    {
+      "icon_id": "25633715",
+      "name": "show_dot_s",
+      "font_class": "show_dot_s",
+      "unicode": "e64f",
+      "unicode_decimal": 58959
+    },
+    {
+      "icon_id": "25633717",
+      "name": "show_dot_n",
+      "font_class": "show_dot_n",
+      "unicode": "e657",
+      "unicode_decimal": 58967
+    },
+    {
+      "icon_id": "25633765",
+      "name": "show_pic_n",
+      "font_class": "show_pic_n",
+      "unicode": "e650",
+      "unicode_decimal": 58960
+    },
+    {
+      "icon_id": "25633777",
+      "name": "show_pic_s",
+      "font_class": "show_pic_s",
+      "unicode": "e658",
+      "unicode_decimal": 58968
+    },
+    {
+      "icon_id": "35129313",
+      "name": "show_3d_s",
+      "font_class": "show_3d_s",
+      "unicode": "e72b",
+      "unicode_decimal": 59179
+    },
+    {
+      "icon_id": "35129316",
+      "name": "show_3d_n",
+      "font_class": "show_3d_n",
+      "unicode": "e72e",
+      "unicode_decimal": 59182
+    },
+    {
+      "icon_id": "35129318",
+      "name": "show_roaming_s",
+      "font_class": "show_roaming_s",
+      "unicode": "e730",
+      "unicode_decimal": 59184
+    },
+    {
+      "icon_id": "35129319",
+      "name": "show_roaming_n",
+      "font_class": "show_roaming_n",
+      "unicode": "e731",
+      "unicode_decimal": 59185
+    },
+    {
+      "icon_id": "41299621",
+      "name": "index",
+      "font_class": "index",
+      "unicode": "e76d",
+      "unicode_decimal": 59245
+    },
+    {
+      "icon_id": "41299620",
+      "name": "symbol",
+      "font_class": "symbol",
+      "unicode": "e76e",
+      "unicode_decimal": 59246
+    },
+    {
+      "icon_id": "41299619",
+      "name": "arrows",
+      "font_class": "arrows1",
+      "unicode": "e76f",
+      "unicode_decimal": 59247
+    },
+    {
+      "icon_id": "41299618",
+      "name": "text",
+      "font_class": "text1",
+      "unicode": "e770",
+      "unicode_decimal": 59248
+    },
+    {
+      "icon_id": "41207891",
+      "name": "fire_firmware",
+      "font_class": "fire_firmware",
+      "unicode": "e767",
+      "unicode_decimal": 59239
+    },
+    {
+      "icon_id": "41207890",
+      "name": "fire_app",
+      "font_class": "fire_app",
+      "unicode": "e768",
+      "unicode_decimal": 59240
+    },
+    {
       "icon_id": "39135106",
       "name": "fire_statistics",
       "font_class": "fire_statistics",

二进制
src/assets/icon/fuse/iconfont.ttf


二进制
src/assets/icon/fuse/iconfont.woff


二进制
src/assets/icon/fuse/iconfont.woff2


二进制
src/assets/icon/fuse/oldIcon.zip


文件差异内容过多而无法显示
+ 4072 - 0
src/assets/style/animate.css


+ 30 - 5
src/assets/style/public.scss

@@ -1,5 +1,6 @@
 @import "../icon/fire/iconfont.css";
 @import "../icon/fuse/iconfont.css";
+@import "./animate.css";
 
 * {
   margin : 0;
@@ -41,6 +42,10 @@ body {
 .fill.el-button {
   width: 100%;
 }
+.newbut {
+  height: 32px !important;
+  border: none !important;
+}
 
 .slide {
   .el-menu {
@@ -175,9 +180,10 @@ body {
 }
 
 .el-tabs__item {
-  font-size  : 16px;
-  line-height: 56px;
-  height     : 56px;
+  font-size  : 14px;
+  line-height: 46px;
+  height     : 46px;
+  padding:  0 12px !important;
 }
 
 .el-tabs__nav-wrap::after {
@@ -545,8 +551,8 @@ body {
   display         : flex;
   align-items     : center;
   justify-content : space-between;
-  padding         : 15px 40px;
-  height          : 5.33rem;
+  padding         : 64px 48px 24px 48px;
+  height          : 7.5rem;
   box-sizing      : border-box;
 }
 
@@ -577,4 +583,23 @@ html .el-input-group__append button.el-button {
   color: rgba(0,0,0,0.85);
   line-height: 22px;
   // margin-bottom: 16px;
+}
+.el-tabs__nav-wrap::after{
+  height: 0;
+}
+.abstract {
+  .el-tabs__active-bar {
+    // display: none;
+  }
+  .el-tabs__header {
+    margin: 0;
+  }
+  .el-tabs__item {
+    line-height: 20px;
+    height: 20px;
+  }
+}
+.el-select__wrapper{
+  min-height: 38px;
+  padding: 0 12px;
 }

+ 13 - 4
src/components/viewImg/index.vue

@@ -14,9 +14,10 @@
         fit="cover"
       />
     </div>
-    <div class="imgList flex justify-start flex-wrap">
+    <div class="imgList my-2 grid gap-4 grid-cols-4" :class="'grid-cols-'+ itenNumber">
+      <!-- flex justify-start flex-wrap -->
       <div
-        class="maskContainer previewBox my-2 mr-4"
+        class="maskContainer previewBox "
         :style="{ width: width, height: height }"
         v-for="(item, index) in list"
         :key="index"
@@ -72,6 +73,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
 import fileImg from "@/assets/svg/file.svg";
 import { delCaseFile, } from "@/store/caseFile";
 import { caseOverviewDel, } from "@/store/case";
+import { number } from "echarts";
 const caseId = computed(() => {
   const caseId = router.currentRoute.value.params.caseId;
   if (caseId) {
@@ -110,6 +112,10 @@ const props = defineProps({
     default: false,
     required: false,
   },
+  itenNumber: {
+    type: number,
+    default: 4,
+  },
   list: {
     type: Array,
     default() {
@@ -220,8 +226,9 @@ const del = async (file) => {
   line-height: 0;
   width: 66px;
   height: 66px;
-  padding: 7px;
-  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+  padding: 0px;
+  // border: 1px solid #d9d9d9;
   &:hover {
     .mask {
       opacity: 1;
@@ -236,6 +243,7 @@ const del = async (file) => {
     transition: all 0.5s;
     opacity: 0;
     position: absolute;
+    border-radius: 4px;
     top: 0;
     right: 0;
     bottom: 0;
@@ -271,6 +279,7 @@ const del = async (file) => {
   .originalImg {
     height: 100%;
     width: 100%;
+    border-radius: 4px;
   }
 }
 </style>

+ 10 - 4
src/request/urls.ts

@@ -264,9 +264,15 @@ export const caseaddOrUpdate = '/service/manage/case/addOrUpdate';
 export const getByImage = '/fusion/ai/getByImage';
 export const getFloor = "/fusion/ai/getFloor/";
 //地图相关
-// export const getTips = "http://map.jms.gd//s/api/gettips";
-// export const getTipsName = "http://map.jms.gd/s/api/gettips_name";
-export const getTips = "/s/api/gettips";
-export const getTipsName = "/s/api/gettips_name";
+const VITE_VIBE = import.meta.env.VITE_VIBE;
+let getTipss = "/s/api/gettips";
+let getTipsNames = "/s/api/gettips_name";
+
+if(VITE_VIBE == 'jiangmeng'){
+  getTipss = "http://map.jms.gd//s/api/gettips";
+  getTipsNames = "http://map.jms.gd/s/api/gettips_name";
+}
+export const getTips = getTipss;
+export const getTipsName = getTipsNames;
 
 

+ 36 - 18
src/router/config.ts

@@ -61,24 +61,42 @@ export const routes: Routes = [
         component: () => import("@/view/originalPhoto/index.vue"),
         meta: { title: "原始照片", icon: "icon-zhaopian" },
       },
-      {
-        name: RouteName.material,
-        path: "material/:caseId",
-        component: () => import("@/view/material/index.vue"),
-        meta: { title: "三录材料", icon: "icon-tidangan" },
-      },
-      {
-        name: RouteName.other,
-        path: "other/:caseId",
-        component: () => import("@/view/other/index.vue"),
-        meta: { title: "其他材料", icon: "icon-fuzhi" },
-      },
-      {
-        name: RouteName.aiList,
-        path: "aiList/:caseId",
-        component: () => import("@/view/aiList/index.vue"),
-        meta: { title: "AI 勘查", icon: "icon-chaxun" },
-      },
+      // {
+      //   name: RouteName.material,
+      //   path: "material/:caseId",
+      //   component: () => import("@/view/material/index.vue"),
+      //   meta: { title: "三录材料", icon: "icon-tidangan" },
+      // },
+      {
+        name: RouteName.photos,
+        path: "photos/:caseId",
+        component: () => import("@/view/material/photos.vue"),
+        meta: { title: "现场照片", icon: "icon-tidangan" },
+      },
+      {
+        name: RouteName.rollMaking,
+        path: "rollMaking/:caseId",
+        component: () => import("@/view/material/rollMaking.vue"),
+        meta: { title: "平面图", icon: "icon-tidangan" },
+      },
+      {
+        name: RouteName.sceneImg,
+        path: "sceneImg/:caseId",
+        component: () => import("@/view/material/sceneImg.vue"),
+        meta: { title: "勘验笔录", icon: "icon-tidangan" },
+      },
+      // {
+      //   name: RouteName.other,
+      //   path: "other/:caseId",
+      //   component: () => import("@/view/other/index.vue"),
+      //   meta: { title: "其他材料", icon: "icon-fuzhi" },
+      // },
+      // {
+      //   name: RouteName.aiList,
+      //   path: "aiList/:caseId",
+      //   component: () => import("@/view/aiList/index.vue"),
+      //   meta: { title: "AI 勘查", icon: "icon-chaxun" },
+      // },
       {
         name: RouteName.dossier,
         path: "dossier/:caseId",

+ 2 - 1
src/router/index.ts

@@ -10,8 +10,9 @@ export const router = createRouter({
 });
 
 router.beforeEach((to, from, next) => {
+  console.log(to.name, 'to.name1', to, RouteName.viewLayout);
   if (!to.name || to.name === RouteName.viewLayout) {
-    router.replace({ name: RouteName.scene, params: { caseId: 360 } });
+    router.replace({ name: RouteName.scene, params: { caseId: 52 } });
     return;
   }
   next();

+ 3 - 0
src/router/routeName.ts

@@ -23,6 +23,9 @@ export const RouteName = {
   records: "records",
   aiList: "aiList",
   dossier: "dossier",
+  photos: "photos",
+  rollMaking: "rollmaking",
+  sceneImg: "sceneimg",
 } as const;
 
 type RouteNamesType = typeof RouteName;

+ 3 - 0
src/store/camera.ts

@@ -1,3 +1,4 @@
+import { ref } from "vue";
 import {
   getListByUser,
   axios,
@@ -53,3 +54,5 @@ export const addCamera = (camera: Camera) =>
 
 export const editCamera = (camera: Camera) =>
   axios.post(updateCamera, { ...camera, cameraSn: camera.snCode });
+
+export const sceneURL = ref('');

+ 23 - 2
src/store/case.ts

@@ -47,6 +47,7 @@ import {
 } from "@/request";
 import { router } from "@/router";
 import { ModelScene, QuoteScene, Scene, SceneType } from "./scene";
+import { user } from "@/store/user";
 import { CaseFile } from "./caseFile";
 import { ref } from "vue";
 
@@ -60,10 +61,12 @@ export type Case = {
 };
 let isloadList = false;
 export const treeList = ref([]);
+export const Extract = ref(false);
 export const sceneList = ref([]);
 export const caseInfoData = ref({
   caseTitle: '',
 });
+export const sceneType = ref('edit');
 
 export const setCaseSharePWD = (params: { caseId: number; randCode: string }) =>
   axios.post(setCasePsw, params);
@@ -134,7 +137,10 @@ export const updateCaseInfo = async (caseFile: CaseFile) =>
     filesTitle: caseFile.filesTitle,
   });
 
-export const getCaseSceneList = async (caseId: number): Promise<Scene[]> => {
+export const getCaseSceneList = async (caseId: number, refresh = false): Promise<Scene[]> => {
+  if (!refresh && sceneList.value.length) {
+      return sceneList.value;
+  }
   let mylist = (await axios.get(caseSceneList, { params: { caseId } })).data;
   sceneList.value = mylist.filter(item => item.type != 3);
   return sceneList.value;
@@ -323,6 +329,7 @@ export const getSceneListTree = (list = sceneList.value) => {
 }
 
 export const getUrlSrc = (item, caseId) => {
+  let token = user.value.token
   let SceneType = {
     0: `/spg.html?m=${item.num}`,
     1: `/spg.html?m=${item.num}`,
@@ -336,5 +343,19 @@ export const getUrlSrc = (item, caseId) => {
     7: `/spg.html?m=${item.num}`,
     99: `/code/index.html?caseId=${caseId}&single#/show`,
   };
-  return SceneType[item.type];
+  let SceneEditType = {
+    0: `/epg.html?m=${item.num}&token=${token}`,
+    1: `/epg.html?m=${item.num}&token=${token}`,
+    2: `/mega/index.html?m=${item.num}`,
+    3: `/swss/index.html?m=${item.num}`,
+    4: `/epg.html?m=${item.num}&token=${token}`,
+    // 5: `/spg.html?m=${item.num}`,
+    // 5: `index.html?caseId=${caseId}&modelId=${item.num}#sign-model`,
+    5: `/mega/index.html?m=${item.num}`,
+    6: `/epg.html?m=${item.num}&token=${token}`,
+    7: `/epg.html?m=${item.num}&token=${token}`,
+    99: `/code/index.html?caseId=${caseId}&single#/show`,
+  };
+  console.log(sceneType.value, 'sceneType.value')
+  return sceneType.value == 'view' ? SceneEditType[item.type]: SceneEditType[item.type];
 }

+ 2 - 0
src/util/index.ts

@@ -2,6 +2,8 @@ import { Base64 } from "js-base64";
 import { positionTransform } from "./mt4";
 import { alert } from "@/helper/message";
 
+export const asyncTimeout = (mis: number = 0) => new Promise(resolve => setTimeout(resolve, mis))
+
 export const dateFormat = (date: Date, fmt: string) => {
   var o: any = {
     "M+": date.getMonth() + 1, //月份

+ 206 - 0
src/util/platform.ts

@@ -0,0 +1,206 @@
+import { watchEffect, nextTick } from "vue";
+// import {
+//   fuseModelsLoaded,
+//   scenes,
+//   setting,
+//   caseProject,
+// } from "@/store";
+// import {
+//   initialSDK,
+//   initialed as fuseInitialed,
+//   sdk as fuseSDK,
+//   analysisPose,
+//   analysisPoseInfo,
+//   setPose,
+// } from "@/sdk";
+import { asyncTimeout } from "./index";
+// import { aMapToWgs84 } from "@/utils/coord";
+import { SceneType } from "@/store/scene";
+
+// import { offlinePrev } from "@/api/offline";
+
+// export async function modelSDKFactory(
+//   type,
+//   dom: HTMLDivElement | HTMLIFrameElement
+// ): Promise<ModelExpose> {
+//   let center: number[] | undefined = undefined;
+//   const lonlatStr = caseProject.value?.latAndLong || "22.364093,113.600356";
+//     // const lonlatStr = caseProject.value?.latAndLong || "22.364093,113.600356";
+//   const lonlat = lonlatStr.split(",").map(Number);
+//   const gcenter = aMapToWgs84({
+//     x: lonlat[1],
+//     y: lonlat[0],
+//   });
+//   center = [gcenter.x, gcenter.y];
+//   if (type === fuseModel) {
+//     if (!fuseInitialed) {
+//       await initialSDK({
+//         laserRoot: offline ? offlinePrev : import.meta.env.VITE_LASER_HOST,
+//         panoOSSRoot: offline ? offlinePrev : import.meta.env.VITE_PANO_OSS,
+//         ossRoot: offline ? offlinePrev : import.meta.env.VITE_OSS,
+//         laserOSSRoot: offline ? offlinePrev : import.meta.env.VITE_LASER_OSS,
+//         layout: dom,
+//         scenes: scenes.value,
+//         lonlat: center,
+//       });
+//     }
+//     return exposeFactory(fuseModel);
+//   } else {
+//     const iframe = dom as HTMLIFrameElement;
+//     const win = await new Promise<Window | null>((resolve, reject) => {
+//       const loadedHandler = () => {
+//         resolve(iframe.contentWindow);
+//         cleanup();
+//       };
+//       const errorHandler = (err: any) => {
+//         reject(err);
+//         cleanup();
+//       };
+//       const cleanup = () => {
+//         iframe.removeEventListener("load", loadedHandler);
+//         iframe.removeEventListener("error", errorHandler);
+//       };
+//       iframe.addEventListener("load", loadedHandler);
+//       iframe.addEventListener("error", errorHandler);
+//     });
+
+//     if (!win) {
+//       throw new Error("场景加载失败");
+//     }
+//     return await exposeFactory(type, win);
+//   }
+// }
+
+const findObjectAttr = <T extends {}, K extends keyof T>(
+  data: T,
+  key: K
+): Promise<T[K]> => {
+  return new Promise<T[K]>((resolve) => {
+    const query = () => {
+      if (key in data) {
+        resolve(data[key]);
+      } else {
+        setTimeout(query, 6);
+      }
+    };
+    query();
+  });
+};
+
+const fuseLoaded = new Promise<void>((resolve) => {
+  const stop = watchEffect(() => {
+    // if (fuseModelsLoaded.value) {
+      resolve();
+      nextTick(() => stop());
+    // }
+  });
+});
+
+export interface ModelExpose {
+  getView: () => Promise<{ image: Blob; flyData: string }>;
+  setView: (flyData: string) => void;
+}
+
+export async function exposeFactory(
+  type,
+  win?: any
+): Promise<ModelExpose> {
+  const sceneType = type === 'fuseModel' ? 99 : type.type;
+  const platforms: {
+    [key in any]: { getSDK: () => Promise<any>; expose: ModelExpose };
+  } = {
+    [99]: {
+      getSDK: async () => {
+        return await findObjectAttr(win, "__sdk");;
+      },
+      expose: {
+        async getView() {
+          const dataURL = await sdk.screenshot(260, 160);
+          const res = await fetch(dataURL);
+          const image = await res.blob();
+          const pose = sdk.getPose();
+          return {
+            image,
+            flyData: JSON.stringify(pose),
+          };
+        },
+        async setView(flyData: string) {
+          console.error(JSON.parse(flyData));
+          // setPose(JSON.parse(flyData), sdk);
+          const pose = JSON.parse(flyData);
+          sdk.Camera.setPose({ dur: 300, ...pose });
+        },
+      },
+    },
+    [SceneType.SWKK]: {
+      getSDK: async () => {
+        const sdk = await findObjectAttr(win, "__sdk");
+        if (!sdk.Scene.loaded) {
+          await new Promise((reoslve) => sdk.Scene.on("loaded", reoslve));
+        }
+        return sdk;
+      },
+      expose: {
+        async getView() {
+          const pose = sdk.Camera.getPose();
+          const images = await sdk.Camera.screenshot(
+            [{ width: 260, height: 160, name: "2k" }],
+            true
+          );
+          return {
+            image: images[0].data,
+            flyData: JSON.stringify(pose),
+          };
+        },
+        async setView(flyData: string) {
+          const pose = JSON.parse(flyData);
+          sdk.Camera.setPose({ dur: 300, ...pose });
+        },
+      },
+    },
+    [SceneType.SWSS]: {
+      getSDK: async () => {
+        await findObjectAttr(win, "laserLoaded");
+        return await findObjectAttr(win, "__sdk");
+      },
+      expose: {
+        async getView() {
+          const dataURL = await sdk.scene.screenshot(260, 160);
+          const res = await fetch(dataURL.dataUrl);
+          const image = await res.blob();
+          const pose = await sdk.scene.getPose();
+          const mode = sdk.customMap.mode;
+
+          return {
+            image,
+            flyData: JSON.stringify({ pose, mode }),
+          };
+        },
+        async setView(flyData: string) {
+          const { pose, mode } = JSON.parse(flyData);
+          sdk.customMap.mode = mode;
+          sdk.scene.setPose(pose, 300);
+        },
+      },
+    },
+  };
+  platforms[SceneType.SWYDSS] = platforms[SceneType.SWSS];
+  platforms[SceneType.DSFXJ] = platforms[SceneType.SWYDMX] =
+    platforms[SceneType.SWSSMX] =
+    platforms[SceneType.SWKJ] =
+      platforms[SceneType.SWKK];
+  platforms[SceneType.SWMX] = {
+    getSDK: async () => findObjectAttr(win, "__sdk"),
+    expose: ()=>{},
+  };
+
+  if (!(sceneType in platforms)) {
+    throw new Error("不支持该类型场景!");
+  }
+
+  const sdk: any = await Promise.race([
+    asyncTimeout(100000).then(() => Promise.reject(new Error("加载超时"))),
+    platforms[sceneType].getSDK(),
+  ]);
+  return platforms[sceneType].expose;
+}

+ 329 - 153
src/view/abstract/index.vue

@@ -1,38 +1,48 @@
 <template>
   <div class="abstract">
-    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
-      <el-tab-pane label="案件信息" name="1">
-        <el-form
-          ref="exampleRef"
-          :model="bindExample"
-          label-position="top"
-          :rules="rules"
-          label-width="auto"
-          class="demo-ruleForm"
-          size="default"
-          status-icon
-        >
-          <div class="form-content">
+    <!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+      <el-tab-pane label="案件信息" name="1"> -->
+    <el-form
+      ref="exampleRef"
+      :model="bindExample"
+      label-position="top"
+      :rules="rules"
+      label-width="auto"
+      class="demo-ruleForm"
+      size="default"
+      status-icon
+    >
+      <div class="form-content">
+        <div class="subtitle">案件信息</div>
+        <el-row :gutter="20">
+          <el-col :span="8">
             <el-form-item label="案件名称" required prop="caseTitle">
               <el-input
                 v-model="bindExample.caseTitle"
                 placeholder="请输入"
                 show-word-limit
+                @blur="submit"
                 maxlength="100"
               />
             </el-form-item>
+          </el-col>
+          <el-col :span="8">
             <el-form-item label="立案编号" prop="caseNum">
               <el-input
                 v-model="bindExample.caseNum"
                 placeholder="请输入"
                 show-word-limit
+                @blur="submit"
                 maxlength="100"
               />
             </el-form-item>
+          </el-col>
+          <el-col :span="8">
             <el-form-item label="案件类别" required prop="caseCategory">
               <el-select
                 v-model="bindExample.caseCategory"
                 placeholder="请选择案件类别"
+                @change="submit"
               >
                 <el-option
                   v-for="item in criminalType"
@@ -41,37 +51,39 @@
                 />
               </el-select>
             </el-form-item>
+          </el-col>
+          <el-col :span="8">
             <el-form-item label="案发时间" required prop="crimeTime">
               <el-date-picker
                 v-model="bindExample.crimeTime"
                 type="datetime"
+                @change="submit"
                 aria-label="请选择案发时间"
                 placeholder="请选择案发时间"
                 style="width: 100%"
                 value-format="YYYY-MM-DD HH:mm:ss"
               />
             </el-form-item>
-
+          </el-col>
+          <el-col :span="8">
             <el-form-item label="案发区域" prop="name">
-              <!-- <el-input
-                v-model="bindExample.caseRegion"
-                placeholder="请输入案发区域"
-                show-word-limit
-                maxlength="100"
-              /> -->
               <el-cascader
-        size="large"
-        style="width: 100%"
-        :props="selectSetting"
-        :options="(geoData as any as CascaderOption[])"
-        v-model="bindExample.caseRegion"
-      ></el-cascader>
+                size="large"
+                @change="submit"
+                style="width: 100%"
+                :props="selectSetting"
+                :options="(geoData as any as CascaderOption[])"
+                v-model="bindExample.caseRegion"
+              ></el-cascader>
             </el-form-item>
+          </el-col>
+          <el-col :span="8">
             <el-form-item label="案件地点">
               <el-input
                 v-model="bindExample.caseAddress"
                 placeholder="输入名称搜索"
                 clearable
+                @blur="submit"
                 maxlength="100"
                 disabled
               >
@@ -80,41 +92,156 @@
                 </template>
               </el-input>
             </el-form-item>
-            <!-- <el-form-item label="案发地点" prop="name">
-          <el-input
-            v-model="bindExample.caseAddress"
-            placeholder="请输入"
-            show-word-limit
-            maxlength="100"
-          />
-        </el-form-item> -->
+          </el-col>
+
+          <el-col :span="4">
             <el-form-item label="是否命案" prop="region">
               <el-select
                 v-model="bindExample.homicideCase"
+                @change="submit"
                 placeholder="请选择"
               >
                 <el-option label="是" :value="1" />
                 <el-option label="否" :value="0" />
               </el-select>
             </el-form-item>
+          </el-col>
+          <el-col :span="4">
             <el-form-item label="是否刑案" prop="region">
               <el-select
                 v-model="bindExample.criminalCase"
                 placeholder="请选择"
+                @change="submit"
               >
                 <el-option label="是" :value="1" />
                 <el-option label="否" :value="0" />
               </el-select>
             </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
             <el-form-item label="经纬度" prop="name">
               <el-input
                 v-model="bindExample.latAndLongs"
+                @blur="submit"
                 placeholder="请输入"
                 show-word-limit
                 maxlength="100"
               />
             </el-form-item>
-            <el-form-item>
+          </el-col>
+        </el-row>
+        <!-- <el-form-item label="案件名称" required prop="caseTitle">
+              <el-input
+                v-model="bindExample.caseTitle"
+                placeholder="请输入"
+                show-word-limit
+                @blur="submit"
+                maxlength="100"
+              />
+            </el-form-item>
+            <el-form-item label="立案编号" prop="caseNum">
+              <el-input
+                v-model="bindExample.caseNum"
+                placeholder="请输入"
+                show-word-limit
+                @blur="submit"
+                maxlength="100"
+              />
+            </el-form-item>
+            <el-form-item label="案件类别" required prop="caseCategory">
+              <el-select
+                v-model="bindExample.caseCategory"
+                placeholder="请选择案件类别"
+                @change="submit"
+              >
+                <el-option
+                  v-for="item in criminalType"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item> -->
+        <!-- <el-form-item label="案发时间" required prop="crimeTime">
+              <el-date-picker
+                v-model="bindExample.crimeTime"
+                type="datetime"
+                @change="submit"
+                aria-label="请选择案发时间"
+                placeholder="请选择案发时间"
+                style="width: 100%"
+                value-format="YYYY-MM-DD HH:mm:ss"
+              />
+            </el-form-item> -->
+
+        <!-- <el-form-item label="案发区域" prop="name">
+              <el-cascader
+                size="large"
+                @change="submit"
+                style="width: 100%"
+                :props="selectSetting"
+                :options="(geoData as any as CascaderOption[])"
+                v-model="bindExample.caseRegion"
+              ></el-cascader>
+            </el-form-item> -->
+        <!-- <el-form-item label="案件地点">
+              <el-input
+                v-model="bindExample.caseAddress"
+                placeholder="输入名称搜索"
+                clearable
+                @blur="submit"
+                maxlength="100"
+                disabled
+              >
+                <template #append>
+                  <el-button :icon="Search" @click="searchAMapAddress" />
+                </template>
+              </el-input>
+            </el-form-item> -->
+        <!-- <el-form-item label="案发地点" prop="name">
+          <el-input
+            v-model="bindExample.caseAddress"
+            placeholder="请输入"
+            show-word-limit
+            maxlength="100"
+          />
+        </el-form-item> -->
+        <!-- <el-row :gutter="10">
+              <el-col :span="12">
+                <el-form-item label="是否命案" prop="region">
+                  <el-select
+                    v-model="bindExample.homicideCase"
+                    @change="submit"
+                    placeholder="请选择"
+                  >
+                    <el-option label="是" :value="1" />
+                    <el-option label="否" :value="0" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="是否刑案" prop="region">
+                  <el-select
+                    v-model="bindExample.criminalCase"
+                    placeholder="请选择"
+                    @change="submit"
+                  >
+                    <el-option label="是" :value="1" />
+                    <el-option label="否" :value="0" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row> -->
+        <!-- <el-form-item label="经纬度" prop="name">
+              <el-input
+                v-model="bindExample.latAndLongs"
+                @blur="submit"
+                placeholder="请输入"
+                show-word-limit
+                maxlength="100"
+              />
+            </el-form-item> -->
+        <!-- <el-form-item>
               <el-button
                 class="w-full"
                 type="primary"
@@ -122,106 +249,127 @@
               >
                 保存
               </el-button>
-            </el-form-item>
-          </div>
-        </el-form>
-      </el-tab-pane>
-      <el-tab-pane label="勘验信息" name="2">
-        <el-form
-          ref="ruleFormRef"
-          :model="ruleForm"
-          label-position="top"
-          :rules="rules"
-          label-width="auto"
-          class="demo-ruleForm"
-          size="default"
-          status-icon
-        >
-            <div class="form-content">
-              <el-form-item label="指挥中心电话时间">
-            <el-date-picker
-              v-model="ruleForm.commandTime"
-              type="datetime"
-              placeholder="请选择"
-              aria-label="请选择"
-              value-format="YYYY-MM-DD HH:mm:ss"
-              style="width: 100%"
-            />
-          </el-form-item>
-          <el-form-item label="接警时间">
-            <el-date-picker
-              v-model="ruleForm.alarmTime"
-              type="datetime"
-              placeholder="请选择"
-              aria-label="请选择"
-              style="width: 100%"
-              value-format="YYYY-MM-DD HH:mm:ss"
-            />
-          </el-form-item>
-          <el-form-item label="报警人">
-            <el-input
-              v-model="ruleForm.alarmName"
-              placeholder="请输入"
-              show-word-limit
-              maxlength="100"
-            />
-          </el-form-item>
-          <el-form-item label="现场勘验单位">
-            <el-input
-              v-model="ruleForm.inquestDept"
-              placeholder="请输入"
-              show-word-limit
-              maxlength="100"
-            />
-          </el-form-item>
-          <el-form-item label="指派/报告单位">
-            <el-input
-              v-model="ruleForm.assignDept"
-              placeholder="请输入"
-              show-word-limit
-              maxlength="100"
-            />
-          </el-form-item>
-          <el-form-item label="指派方式">
-            <el-input
-              v-model="ruleForm.assignType"
-              placeholder="请输入"
-              show-word-limit
-              maxlength="100"
-            />
-          </el-form-item>
-          <el-form-item label="勘验时间">
-            <el-date-picker
-              v-model="ruleForm.times"
-              value-format="YYYY-MM-DD HH:mm:ss"
-              type="datetimerange"
-              start-placeholder="开始时间"
-              end-placeholder="结束时间"
-              format="YYYY-MM-DD HH:mm:ss"
-              date-format="YYYY/MM/DD ddd"
-            />
-          </el-form-item>
-          <el-form-item label="勘验地点">
-            <el-input
-              v-model="ruleForm.inquestAddress"
-              placeholder="请输入"
-              show-word-limit
-              maxlength="100"
-            />
-          </el-form-item>
-          <el-form-item>
-            <el-button
-              class="w-full"
-              type="primary"
-              @click="submitForm(ruleFormRef)"
-            >
-              保存
-            </el-button>
-          </el-form-item>
-          </div>
-        </el-form>
-      </el-tab-pane>
-    </el-tabs>
+            </el-form-item> -->
+      </div>
+    </el-form>
+    <el-form
+      ref="ruleFormRef"
+      :model="ruleForm"
+      label-position="top"
+      :rules="rules"
+      label-width="auto"
+      class="demo-ruleForm"
+      size="default"
+      status-icon
+    >
+      <div class="form-content">
+        <div class="subtitle">勘验信息</div>
+        <el-row :gutter="20">
+          <el-col :span="8"
+            ><el-form-item label="指挥中心电话时间">
+              <el-date-picker
+                v-model="ruleForm.commandTime"
+                type="datetime"
+                placeholder="请选择"
+                @change="submit"
+                aria-label="请选择"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                style="width: 100%"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="接警时间">
+              <el-date-picker
+                v-model="ruleForm.alarmTime"
+                type="datetime"
+                @change="submit"
+                placeholder="请选择"
+                aria-label="请选择"
+                style="width: 100%"
+                value-format="YYYY-MM-DD HH:mm:ss"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="报警人">
+              <el-input
+                v-model="ruleForm.alarmName"
+                placeholder="请输入"
+                @blur="submit"
+                show-word-limit
+                maxlength="100"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="现场勘验单位">
+              <el-input
+                v-model="ruleForm.inquestDept"
+                @blur="submit"
+                placeholder="请输入"
+                show-word-limit
+                maxlength="100"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="指派/报告单位">
+              <el-input
+                v-model="ruleForm.assignDept"
+                @blur="submit"
+                placeholder="请输入"
+                show-word-limit
+                maxlength="100"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="指派方式">
+              <el-input
+                v-model="ruleForm.assignType"
+                @blur="submit"
+                placeholder="请输入"
+                show-word-limit
+                maxlength="100"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="勘验时间">
+              <el-date-picker
+                v-model="ruleForm.times"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                @change="submit"
+                type="datetimerange"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                format="YYYY-MM-DD HH:mm:ss"
+                date-format="YYYY/MM/DD ddd"
+              /> </el-form-item
+          ></el-col>
+          <el-col :span="8"
+            ><el-form-item label="勘验地点">
+              <el-input
+                v-model="ruleForm.inquestAddress"
+                @blur="submit"
+                placeholder="请输入"
+                show-word-limit
+                maxlength="100"
+              /> </el-form-item
+          ></el-col>
+        </el-row>
+
+        <!-- <el-form-item>
+              <el-button
+                class="w-full"
+                type="primary"
+                @click="submitForm(ruleFormRef)"
+              >
+                保存
+              </el-button>
+            </el-form-item> -->
+      </div>
+    </el-form>
+    <!-- </el-tab-pane> -->
+    <!-- <el-tab-pane label="勘验信息" name="2"> -->
+
+    <!-- </el-tab-pane> -->
+    <!-- </el-tabs> -->
   </div>
 </template>
 
@@ -229,10 +377,20 @@
 import { computed, ref, reactive, onMounted } from "vue";
 import { Search } from "@element-plus/icons-vue";
 import { ElMessage, CascaderOption, CascaderProps } from "element-plus";
-import { Example, setExample, getExamplePagging } from "@/app/criminal/store/example";
+import {
+  Example,
+  setExample,
+  getExamplePagging,
+} from "@/app/criminal/store/example";
 import { selectMapImage } from "@/view/case/quisk";
 import { geoData } from "./getGeo";
-import { getSceneListData, getCaseInquestInfo, casesaveOrUpDate, getcaseInDate, getCaseInfo  } from "@/store/case";
+import {
+  getSceneListData,
+  getCaseInquestInfo,
+  casesaveOrUpDate,
+  getcaseInDate,
+  getCaseInfo,
+} from "@/store/case";
 const selectSetting = ref<CascaderProps>({
   value: "name",
   label: "name",
@@ -293,14 +451,17 @@ const criminalType = [
 ];
 onMounted(async () => {
   console.log("caseInfo", caseId.value);
-  const caseInfo = await getCaseInfo(caseId.value)
-  const caseData = await getCaseInquestInfo(caseId.value)
+  const caseInfo = await getCaseInfo(caseId.value);
+  const caseData = await getCaseInquestInfo(caseId.value);
   console.log("caseInfo", caseInfo, caseData);
   if (caseInfo) {
     ruleForm.value = caseData || {};
     bindExample.value = JSON.parse(JSON.stringify(caseInfo));
-    bindExample.value.latAndLongs = bindExample.value.latAndLong.split(',').reverse().join(',')
- }
+    bindExample.value.latAndLongs = bindExample.value.latAndLong && bindExample.value.latAndLong
+      .split(",")
+      .reverse()
+      .join(",");
+  }
 });
 const options = Array.from({ length: 10000 }).map((_, idx) => ({
   value: `${idx + 1}`,
@@ -328,22 +489,27 @@ const rules = reactive({
       trigger: "change",
     },
   ],
-  caseCategory: [
-    { required: true, message: "请选择", trigger: "change" },
-  ],
+  caseCategory: [{ required: true, message: "请选择", trigger: "change" }],
 });
 const showModal = ref(false);
 const submit = async () => {
-  if(activeName.value == "1") {
+  if (activeName.value == "1") {
     if (!bindExample.value.caseTitle || !bindExample.value.caseTitle.trim()) {
       ElMessage.error("案件名称不能为空");
       throw "案件名称不能为空";
     }
-    let latAndLong = bindExample.value.latAndLongs?.split(',').reverse().join(',')
-    await setExample({...bindExample.value, latAndLong, caseId :caseId.value })
-    await getCaseInfo(caseId.value)
-  }else{
-    await casesaveOrUpDate({...ruleForm.value, caseId :caseId.value })
+    let latAndLong = bindExample.value.latAndLongs
+      ?.split(",")
+      .reverse()
+      .join(",");
+    await setExample({
+      ...bindExample.value,
+      latAndLong,
+      caseId: caseId.value,
+    });
+    await getCaseInfo(caseId.value);
+  } else {
+    await casesaveOrUpDate({ ...ruleForm.value, caseId: caseId.value });
   }
   ElMessage.success("保存成功");
 };
@@ -369,7 +535,10 @@ async function handleAdd() {
   console.log("handleAdd");
 }
 const searchAMapAddress = async () => {
-  const data = await selectMapImage({text: true, mapId: bindExample.value.mapId});
+  const data = await selectMapImage({
+    text: true,
+    mapId: bindExample.value.mapId,
+  });
   console.log("searchAMapAddress", data);
   if (!data?.search) return;
   bindExample.value.mapId = data.mapId;
@@ -386,8 +555,8 @@ const searchAMapAddress = async () => {
     margin-bottom: 14px;
   }
   .form-content {
-    height: calc(100vh - 175px);
-    overflow-y: scroll;
+    // height: calc(100vh - 175px);
+    // overflow-y: scroll;
   }
   .demo-tabs {
     .el-tabs__item {
@@ -395,5 +564,12 @@ const searchAMapAddress = async () => {
       line-height: 32px;
     }
   }
+  .subtitle {
+    font-weight: bold;
+    font-size: 16px;
+    color: rgba(0, 0, 0, 0.85);
+    line-height: 22px;
+    margin: 36px 0 16px 0;
+  }
 }
 </style>

+ 1 - 1
src/view/case/draw/index.vue

@@ -42,7 +42,7 @@
 import Header from "./header.vue";
 import Slider from "./slider.vue";
 import Eshape from "./eshape.vue";
-import login from "./login.vue";
+import login from "./loginDialog.vue";
 import { computed, nextTick, ref, watch, onMounted } from "vue";
 import { RouteName, router } from "@/router";
 import { useBoard, title } from "./board/useBoard";

+ 440 - 135
src/view/case/draw/leaflet.ChineseTmsProviders.js

@@ -1,161 +1,466 @@
 if (L.Proj) {
-  L.CRS.Baidu = new L.Proj.CRS('EPSG:900913', '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs', {
-      resolutions: function () {
-          var level = 19
-          var res = [];
-          res[0] = Math.pow(2, 18);
-          for (var i = 1; i < level; i++) {
-              res[i] = Math.pow(2, (18 - i))
-          }
-          return res;
-      }(),
-      origin: [0, 0],
-      bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
-  });
+    L.CRS.Baidu = new L.Proj.CRS('EPSG:900913', '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs', {
+        resolutions: function () {
+            var level = 19
+            var res = [];
+            res[0] = Math.pow(2, 18);
+            for (var i = 1; i < level; i++) {
+                res[i] = Math.pow(2, (18 - i))
+            }
+            return res;
+        }(),
+        origin: [0, 0],
+        bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
+    });
 }
 
 L.TileLayer.ChinaProvider = L.TileLayer.extend({
 
-  initialize: function(type, options) { // (type, Object)
-      var providers = L.TileLayer.ChinaProvider.providers;
+    initialize: function (type, options) { // (type, Object)
+        var providers = L.TileLayer.ChinaProvider.providers;
 
-      options = options || {}
+        options = options || {}
 
-      var parts = type.split('.');
+        var parts = type.split('.');
 
-      var providerName = parts[0];
-      var mapName = parts[1];
-      var mapType = parts[2];
+        var providerName = parts[0];
+        var mapName = parts[1];
+        var mapType = parts[2];
 
-      var url = providers[providerName][mapName][mapType];
-      options.subdomains = providers[providerName].Subdomains;
-      options.key = options.key || providers[providerName].key;
+        var url = providers[providerName][mapName][mapType];
+        options.subdomains = providers[providerName].Subdomains;
+        options.key = options.key || providers[providerName].key;
 
-      if ('tms' in providers[providerName]) {
-          options.tms = providers[providerName]['tms']
-      }
+        if ('tms' in providers[providerName]) {
+            options.tms = providers[providerName]['tms']
+        }
 
-      L.TileLayer.prototype.initialize.call(this, url, options);
-  },
+        L.TileLayer.prototype.initialize.call(this, url, options);
+    },
 
-  getTileUrl: function (coords) {
-  var data = {
-    s: this._getSubdomain(coords),
-    x: coords.x,
-    y: coords.y,
-    z: this._getZoomForUrl(),
-  };
-  if (this._map && !this._map.options.crs.infinite) {
-    var invertedY = this._globalTileRange.max.y - coords.y;
-    if (this.options.tms) {
-      data['y'] = invertedY;
-    }
-    data['-y'] = invertedY;
-  }
+    getTileUrl: function (coords) {
+        var data = {
+            s: this._getSubdomain(coords),
+            x: coords.x,
+            y: coords.y,
+            z: this._getZoomForUrl(),
+        };
+        if (this._map && !this._map.options.crs.infinite) {
+            var invertedY = this._globalTileRange.max.y - coords.y;
+            if (this.options.tms) {
+                data['y'] = invertedY;
+            }
+            data['-y'] = invertedY;
+        }
 
-      data.sx = data.x >> 4
-      data.sy = (( 1 << data.z) - data.y) >> 4
+        data.sx = data.x >> 4
+        data.sy = ((1 << data.z) - data.y) >> 4
 
-  return L.Util.template(this._url, L.Util.extend(data, this.options));
-},
+        return L.Util.template(this._url, L.Util.extend(data, this.options));
+    },
 });
+const VITE_VIBE = import.meta.env.VITE_VIBE;
+if (VITE_VIBE == 'jiangmeng') {
+    L.TileLayer.ChinaProvider.providers = {
+        TianDiTu: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/tiandi/{z}/{x}/{y}.png?apikey={key}",
+                Annotion: "http://{s}.map.jms.gd/tile/tiandi_cva/{z}/{x}/{y}.png?apikey={key}",
+                Ibo: "http://{s}.map.jms.gd/tile/tiandi_ibo/{z}/{x}/{y}.png?apikey={key}",
+                Mycva: "http://{s}.map.jms.gd/tile/tdt/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/tiandi_wx/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Terrain: {
+                Map: "http://a.map.jms.gd/tile/tiandi_ter/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        GaoDe: {
+            Normal: {
+                Map: 'http://{s}.map.jms.gd/tile/shiliang/{z}/{x}/{y}.png?apikey={key}',
+                XiangTu: 'http://{s}.map.jms.gd/tile/gd_xiangtu/{z}/{x}/{y}.png?apikey={key}',
+                XiangTuGray: 'http://{s}.map.jms.gd/tile/gd_xiangtu_gray/{z}/{x}/{y}.png?apikey={key}',
+                WuWenZi: 'http://{s}.map.jms.gd/tile/gd_wuwenzi/{z}/{x}/{y}.png?apikey={key}',
+                LuWang: 'http://{s}.map.jms.gd/tile/luwang/{z}/{x}/{y}.png?apikey={key}',
+                DiBiao: 'http://{s}.map.jms.gd/tile/gd_dibiao/{z}/{x}/{y}.png?apikey={key}',
+                Test: "http://{s}.map.jms.gd/tile/test/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Satellite: {
+                Map: 'http://{s}.map.jms.gd/tile/weixing/{z}/{x}/{y}.png?apikey={key}'
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Pengjiang: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/pengjiang/{z}/{x}/{y}.png?apikey={key}"
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+        Taishan: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/taishan/{z}/{x}/{y}.png?apikey={key}"
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Test: {
+            Normal: {
+                Test: "http://f.map.jms.gd/tile/test/{z}/{x}/{y}.png?apikey={key}",
+                Test1: "http://f.map.jms.gd/tile/test1/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Guangzhou: {
+            Normal: {
+                Hdpi: "http://{s}.map.jms.gd/tile/gz_hdpi/{z}/{x}/{y}.png?apikey={key}"
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Google: {
+            Normal: {
+                Map: "",
+            },
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/google_s/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        GoogleCn: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/google/{z}/{x}/{y}.png?apikey={key}",
+                Terrain: "http://{s}.map.jms.gd/tile/google_dx/{z}/{x}/{y}.png?apikey={key}",
+                Annotion: "http://{s}.map.jms.gd/tile/google_h/{z}/{x}/{y}.png?apikey={key}",
+                Ter: "http://{s}.map.jms.gd/tile/google_t/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/google_s/{z}/{x}/{y}.png?apikey={key}",
+                Annotion: "http://{s}.map.jms.gd/tile/google_wx/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Tengxun: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/tengxun/{z}/{x}/{y}.png?apikey={key}",
+                Map9: "http://{s}.map.jms.gd/tile/tengxun_9/{z}/{x}/{y}.png?apikey={key}",
+                Map8: "http://{s}.map.jms.gd/tile/tengxun_8/{z}/{x}/{y}.png?apikey={key}",
+                Map4: "http://f.map.jms.gd/tile/tengxun_4/{z}/{x}/{y}.png?apikey={key}",
+                Map3: "http://{s}.map.jms.gd/tile/tengxun_3/{z}/{x}/{y}.png?apikey={key}",
+                Map2: "http://{s}.map.jms.gd/tile/tengxun_2/{z}/{x}/{y}.png?apikey={key}",
+                Map1: "http://{s}.map.jms.gd/tile/tengxun_1/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/tengxun_wx/{z}/{x}/{y}.png?apikey={key}"
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Carto: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/carto_label/{z}/{x}/{y}.png?apikey={key}",
+                Light: "http://{s}.map.jms.gd/tile/railmap/{z}/{x}/{y}.png?apikey={key}",
+                Dark: "http://{s}.map.jms.gd/tile/carto_dark/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+
+        Thunderforest: {
+            Normal: {
+                Opencyclemap: "http://{s}.map.jms.gd/tile/opencyclemap/{z}/{x}/{y}.png?apikey={key}",
+                Transport: "http://{s}.map.jms.gd/tile/transport/{z}/{x}/{y}.png?apikey={key}",
+                Landscape: "http://{s}.map.jms.gd/tile/landscape/{z}/{x}/{y}.png?apikey={key}",
+                Mobile: "http://{s}.map.jms.gd/tile/mobile-atlas/{z}/{x}/{y}.png?apikey={key}",
+                Pioneer: "http://{s}.map.jms.gd/tile/pioneer/{z}/{x}/{y}.png?apikey={key}",
+                Neighbourhood: "http://{s}.map.jms.gd/tile/neighbourhood/{z}/{x}/{y}.png?apikey={key}",
+                Outdoors: "http://{s}.map.jms.gd/tile/outdoors/{z}/{x}/{y}.png?apikey={key}",
+                Atlas: "http://{s}.map.jms.gd/tile/atlas/{z}/{x}/{y}.png?apikey={key}",
+                TransportDark: "http://{s}.map.jms.gd/tile/transport-dark/{z}/{x}/{y}.png?apikey={key}",
+                Spinal: "http://{s}.map.jms.gd/tile/spinal-map/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        OSM: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/osm/{z}/{x}/{y}.png?apikey={key}",
+                Topo: "http://{s}.map.jms.gd/tile/osm_topo/{z}/{x}/{y}.png?apikey={key}",
+                Openstreetmap: "http://{s}.map.jms.gd/tile/openstreetmap/{z}/{x}/{y}.png?apikey={key}",
+                winter: "http://{s}.map.jms.gd/tile/osm_winter/{z}/{x}/{y}.png?apikey={key}",
+                summer: "http://{s}.map.jms.gd/tile/osm_summer/{z}/{x}/{y}.png?apikey={key}",
+                cycling: "http://{s}.map.jms.gd/tile/osm_cycling/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Opentopomap: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/opentopomap/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Baidu: {
+            Normal: {
+                Map: 'http://{s}.map.jms.gd/tile/baidu/{z}/{x}/{y}.png?apikey={key}'
+            },
+            Satellite: {
+                Map: 'http://{s}.map.jms.gd/tile/baidu_wx/{z}/{x}/{y}.png?apikey={key}',
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118",
+            tms: true
+        },
+
+        Openrailway: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/openrailway/{z}/{x}/{y}.png?apikey={key}",
+                Maxspeed: "http://{s}.map.jms.gd/tile/openrailway_maxspeed/{z}/{x}/{y}.png?apikey={key}",
+                Elec: "http://{s}.map.jms.gd/tile/openrailway_elec/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Stadia: {
+            Normal: {
+                Alidade_smooth: "http://{s}.map.jms.gd/tile/alidade_smooth/{z}/{x}/{y}.png?apikey={key}",
+                Watercolor: "http://{s}.map.jms.gd/tile/stamen_watercolor/{z}/{x}/{y}.jpg?apikey={key}",
+                Terrain: "http://{s}.map.jms.gd/tile/stamen_terrain/{z}/{x}/{y}.png?apikey={key}",
+                Toner: "http://{s}.map.jms.gd/tile/stamen_toner/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        ArcgisCn: {
+            Normal: {
+                Community: "http://{s}.map.jms.gd/tile/arcgis_comm/{z}/{x}/{y}.png?apikey={key}",
+                Gray: "http://{s}.map.jms.gd/tile/arcgis_gray/{z}/{x}/{y}.jpg?apikey={key}",
+                Blue: "http://{s}.map.jms.gd/tile/arcgis_blue/{z}/{x}/{y}.png?apikey={key}",
+                Warm: "http://{s}.map.jms.gd/tile/arcgis_warm/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Arcgis: {
+            Normal: {
+                Street: "http://{s}.map.jms.gd/tile/arcgis_world_street/{z}/{x}/{y}.png?apikey={key}",
+                Hillshade: "http://{s}.map.jms.gd/tile/arcgis_world_hillshade/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/arcgis_world_imagery/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Jl1: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/jl1_2022/{z}/{x}/{y}.png?apikey={key}",
+                Map2023: "http://{s}.map.jms.gd/tile/jl1_2023/{z}/{x}/{y}.png?apikey={key}",
+                Map2024: "http://{s}.map.jms.gd/tile/jl1_2024/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Geovis: {
+            Normal: {
+                Base: "http://{s}.map.jms.gd/tile/geovis_base/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Stamen: {
+            Terrain: {
+                Lines: "http://{s}.map.jms.gd/tile/stamen_terrain_lines/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Toner: {
+                Lines: "http://{s}.map.jms.gd/tile/stamen_toner_lines/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        BingCn: {
+            Normal: {
+                Map: "http://{s}.map.jms.gd/tile/bing/{z}/{x}/{y}.png?apikey={key}",
+                Dark: "http://{s}.map.jms.gd/tile/bing_dark/{z}/{x}/{y}.png?apikey={key}",
+                Cf: "http://{s}.map.jms.gd/tile/bing_cf/{z}/{x}/{y}.png?apikey={key}", //实时路况
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Bing: {
+            Satellite: {
+                Map: "http://{s}.map.jms.gd/tile/bing_aerial/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Jiangmen: {
+            Normal: {
+                Map: "http://68.196.3.198/map/mapimg/tile/{z}/{x}/{y}?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Maptiler: {
+            Normal: {
+                basic: "http://{s}.map.jms.gd/tile/maptiler_basic/{z}/{x}/{y}.png?apikey={key}",
+                backdrop: "http://{s}.map.jms.gd/tile/maptiler_backdrop/{z}/{x}/{y}.png?apikey={key}",
+                streets: "http://{s}.map.jms.gd/tile/maptiler_streets/{z}/{x}/{y}.png?apikey={key}",
+                winter: "http://{s}.map.jms.gd/tile/maptiler_winter/{z}/{x}/{y}.png?apikey={key}",
+                ocean: "http://{s}.map.jms.gd/tile/maptiler_ocean/{z}/{x}/{y}.png?apikey={key}",
+                toner: "http://{s}.map.jms.gd/tile/maptiler_toner/{z}/{x}/{y}.png?apikey={key}",
+                topo: "http://{s}.map.jms.gd/tile/maptiler_topo/{z}/{x}/{y}.png?apikey={key}",
+                dataviz: "http://{s}.map.jms.gd/tile/maptiler_dataviz/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        mapbox: {
+            Normal: {
+                mapbox01: "http://{s}.map.jms.gd/tile/mapbox01/{z}/{x}/{y}.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+
+        Nothing: {
+            Normal: {
+                Map: "http://map.jms.gd/static/img/512.png?apikey={key}",
+            },
+            Subdomains: ["a", "b", "c", "d"],
+            key: "jm20240118"
+        },
+    };
+} else {
+
+    L.TileLayer.ChinaProvider.providers = {
+        TianDiTu: {
+            Normal: {
+                Map: "//t{s}.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk={key}",
+                Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk={key}"
+            },
+            Satellite: {
+                Map: "//t{s}.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk={key}",
+                Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cia_w&X={x}&Y={y}&L={z}&tk={key}"
+            },
+            Terrain: {
+                Map: "//t{s}.tianditu.gov.cn/DataServer?T=ter_w&X={x}&Y={y}&L={z}&tk={key}",
+                Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cta_w&X={x}&Y={y}&L={z}&tk={key}"
+            },
+            Subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
+            key: "174705aebfe31b79b3587279e211cb9a"
+        },
+
+        GaoDe: {
+            Normal: {
+                Map: '//webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
+            },
+            Satellite: {
+                Map: '//webst0{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
+                Annotion: '//webst0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}'
+            },
+            Subdomains: ["1", "2", "3", "4"]
+        },
+
+        Google: {
+            Normal: {
+                Map: "//www.google.cn/maps/vt?lyrs=m@189&gl=cn&x={x}&y={y}&z={z}"
+            },
+            Satellite: {
+                Map: "//www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}",
+                Annotion: "//www.google.cn/maps/vt?lyrs=y@189&gl=cn&x={x}&y={y}&z={z}"
+            },
+            Subdomains: []
+        },
+
+        Geoq: {
+            Normal: {
+                Map: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}",
+                PurplishBlue: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
+                Gray: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}",
+                Warm: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetWarm/MapServer/tile/{z}/{y}/{x}",
+            },
+            Theme: {
+                Hydro: "//thematic.geoq.cn/arcgis/rest/services/ThematicMaps/WorldHydroMap/MapServer/tile/{z}/{y}/{x}"
+            },
+            Subdomains: []
+        },
+
+        OSM: {
+            Normal: {
+                Map: "//{s}.tile.osm.org/{z}/{x}/{y}.png",
+            },
+            Subdomains: ['a', 'b', 'c']
+        },
+
+        Baidu: {
+            Normal: {
+                Map: '//online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1'
+            },
+            Satellite: {
+                Map: '//shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46',
+                Annotion: '//online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=sl&v=020'
+            },
+            Subdomains: '0123456789',
+            tms: true
+        },
+
+        Tencent: {
+            Normal: {
+                Map: "//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={-y}&type=vector&styleid=3",
+            },
+            Satellite: {
+                Map: "//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{-y}.jpg",
+            },
+            Terrain: {
+                Map: "//p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}_{-y}.jpg"
+            },
+            Subdomains: '0123',
+        }
 
-L.TileLayer.ChinaProvider.providers = {
-  TianDiTu: {
-      Normal: {
-          Map: "//t{s}.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk={key}",
-          Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk={key}"
-      },
-      Satellite: {
-          Map: "//t{s}.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk={key}",
-          Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cia_w&X={x}&Y={y}&L={z}&tk={key}"
-      },
-      Terrain: {
-          Map: "//t{s}.tianditu.gov.cn/DataServer?T=ter_w&X={x}&Y={y}&L={z}&tk={key}",
-          Annotion: "//t{s}.tianditu.gov.cn/DataServer?T=cta_w&X={x}&Y={y}&L={z}&tk={key}"
-      },
-      Subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
-      key: "174705aebfe31b79b3587279e211cb9a"
-  },
-
-  GaoDe: {
-      Normal: {
-          Map: '//webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
-      },
-      Satellite: {
-          Map: '//webst0{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
-          Annotion: '//webst0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}'
-      },
-      Subdomains: ["1", "2", "3", "4"]
-  },
-
-  Google: {
-      Normal: {
-          Map: "//www.google.cn/maps/vt?lyrs=m@189&gl=cn&x={x}&y={y}&z={z}"
-      },
-      Satellite: {
-          Map: "//www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}",
-          Annotion: "//www.google.cn/maps/vt?lyrs=y@189&gl=cn&x={x}&y={y}&z={z}"
-      },
-      Subdomains: []
-  },
-
-  Geoq: {
-      Normal: {
-          Map: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}",
-          PurplishBlue: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
-          Gray: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}",
-          Warm: "//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetWarm/MapServer/tile/{z}/{y}/{x}",
-      },
-      Theme: {
-          Hydro: "//thematic.geoq.cn/arcgis/rest/services/ThematicMaps/WorldHydroMap/MapServer/tile/{z}/{y}/{x}"
-      },
-      Subdomains: []
-  },
-
-  OSM: {
-      Normal: {
-          Map: "//{s}.tile.osm.org/{z}/{x}/{y}.png",
-      },
-      Subdomains: ['a', 'b', 'c']
-  },
-
-  Baidu: {
-      Normal: {
-          Map: '//online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1'
-      },
-      Satellite: {
-          Map: '//shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46',
-          Annotion: '//online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=sl&v=020'
-      },
-      Subdomains: '0123456789',
-      tms: true
-  },
-
-  Tencent: {
-      Normal: {
-          Map: "//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={-y}&type=vector&styleid=3",
-      },
-      Satellite: {
-          Map: "//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{-y}.jpg",
-      },
-      Terrain: {
-          Map: "//p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}_{-y}.jpg"
-      },
-      Subdomains: '0123',
-  }
-
-};
+    };
+}
 
-L.tileLayer.chinaProvider = function(type, options) {
-  return new L.TileLayer.ChinaProvider(type, options);
+L.tileLayer.chinaProvider = function (type, options) {
+    return new L.TileLayer.ChinaProvider(type, options);
 };
 
 //坐标转换
 L.CoordConver = function () {
 
     /**百度转84*/
-    this.bd09_To_gps84 = function(lng, lat) {
+    this.bd09_To_gps84 = function (lng, lat) {
         var gcj02 = this.bd09_To_gcj02(lng, lat);
         var map84 = this.gcj02_To_gps84(gcj02.lng, gcj02.lat);
         return map84;

+ 572 - 0
src/view/case/draw/leaflet.mapCorrection.js

@@ -0,0 +1,572 @@
+// this L.CRS.Baidu from https://github.com/muyao1987/leaflet-tileLayer-baidugaode/blob/master/src/tileLayer.baidu.js
+
+if (L.Proj) {
+    L.CRS.Baidu = new L.Proj.CRS('EPSG:900913', '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs', {
+        resolutions: function () {
+            level = 19
+            var res = [];
+            res[0] = Math.pow(2, 18);
+            for (var i = 1; i < level; i++) {
+                res[i] = Math.pow(2, (18 - i))
+            }
+            return res;
+        }(),
+        origin: [0, 0],
+        bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
+    });
+}
+
+L.TileLayer.ChinaProvider = L.TileLayer.extend({
+
+    initialize: function(type, options) { // (type, Object)
+        var providers = L.TileLayer.ChinaProvider.providers;
+
+        options = options || {}
+
+        var parts = type.split('.');
+
+        var providerName = parts[0];
+        var mapName = parts[1];
+        var mapType = parts[2];
+
+        var url = providers[providerName][mapName][mapType];
+
+        function getcookie(objname){
+            var arrstr = document.cookie.split("; ");
+            for(var i=0;i<arrstr.length;i++){
+              var temp = arrstr[i].split("=");
+              if(temp[0]==objname)return unescape(temp[1]);
+            }
+        }
+
+        var login_apikey = localStorage.apikey;
+        //如果当前用户没有apikey则取配置中的默认key
+        if(typeof(login_apikey) === 'undefined'){
+            login_apikey = providers[providerName].key;
+        }
+        //url += "?apikey=" + login_apikey;
+
+        
+        options.subdomains = providers[providerName].Subdomains;
+        //options.key = options.key || providers[providerName].key;
+        options.key = options.key || login_apikey;
+
+        if ('tms' in providers[providerName]) {
+            options.tms = providers[providerName]['tms']
+        }
+
+        L.TileLayer.prototype.initialize.call(this, url, options);
+    }
+});
+
+
+//
+L.TileLayer.ChinaProvider.providers = {
+    TianDiTu: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/tiandi/{z}/{x}/{y}.png?apikey={key}",
+            Annotion: "http://{s}.map.jms.gd/tile/tiandi_cva/{z}/{x}/{y}.png?apikey={key}",
+            Ibo: "http://{s}.map.jms.gd/tile/tiandi_ibo/{z}/{x}/{y}.png?apikey={key}",
+            Mycva: "http://{s}.map.jms.gd/tile/tdt/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/tiandi_wx/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Terrain: {
+            Map: "http://a.map.jms.gd/tile/tiandi_ter/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    GaoDe: {
+        Normal: {
+            Map: 'http://{s}.map.jms.gd/tile/shiliang/{z}/{x}/{y}.png?apikey={key}',
+            XiangTu: 'http://{s}.map.jms.gd/tile/gd_xiangtu/{z}/{x}/{y}.png?apikey={key}',
+            XiangTuGray: 'http://{s}.map.jms.gd/tile/gd_xiangtu_gray/{z}/{x}/{y}.png?apikey={key}',
+            WuWenZi: 'http://{s}.map.jms.gd/tile/gd_wuwenzi/{z}/{x}/{y}.png?apikey={key}',
+            LuWang: 'http://{s}.map.jms.gd/tile/luwang/{z}/{x}/{y}.png?apikey={key}',
+            DiBiao: 'http://{s}.map.jms.gd/tile/gd_dibiao/{z}/{x}/{y}.png?apikey={key}',
+            Test: "http://{s}.map.jms.gd/tile/test/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Satellite: {
+            Map: 'http://{s}.map.jms.gd/tile/weixing/{z}/{x}/{y}.png?apikey={key}'
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Pengjiang: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/pengjiang/{z}/{x}/{y}.png?apikey={key}"
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+    Taishan: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/taishan/{z}/{x}/{y}.png?apikey={key}"
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Test: {
+        Normal: {
+            Test: "http://f.map.jms.gd/tile/test/{z}/{x}/{y}.png?apikey={key}",
+            Test1: "http://f.map.jms.gd/tile/test1/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Guangzhou: {
+        Normal: {
+            Hdpi: "http://{s}.map.jms.gd/tile/gz_hdpi/{z}/{x}/{y}.png?apikey={key}"
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+    
+    Google: {
+        Normal: {
+            Map: "",
+        },
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/google_s/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    GoogleCn: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/google/{z}/{x}/{y}.png?apikey={key}",
+            Terrain: "http://{s}.map.jms.gd/tile/google_dx/{z}/{x}/{y}.png?apikey={key}",
+            Annotion: "http://{s}.map.jms.gd/tile/google_h/{z}/{x}/{y}.png?apikey={key}",
+            Ter: "http://{s}.map.jms.gd/tile/google_t/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/google_s/{z}/{x}/{y}.png?apikey={key}",
+            Annotion: "http://{s}.map.jms.gd/tile/google_wx/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Tengxun: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/tengxun/{z}/{x}/{y}.png?apikey={key}",
+            Map9: "http://{s}.map.jms.gd/tile/tengxun_9/{z}/{x}/{y}.png?apikey={key}",
+            Map8: "http://{s}.map.jms.gd/tile/tengxun_8/{z}/{x}/{y}.png?apikey={key}",
+            Map4: "http://f.map.jms.gd/tile/tengxun_4/{z}/{x}/{y}.png?apikey={key}",
+            Map3: "http://{s}.map.jms.gd/tile/tengxun_3/{z}/{x}/{y}.png?apikey={key}",
+            Map2: "http://{s}.map.jms.gd/tile/tengxun_2/{z}/{x}/{y}.png?apikey={key}",
+            Map1: "http://{s}.map.jms.gd/tile/tengxun_1/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/tengxun_wx/{z}/{x}/{y}.png?apikey={key}"
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Carto: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/carto_label/{z}/{x}/{y}.png?apikey={key}",
+            Light: "http://{s}.map.jms.gd/tile/railmap/{z}/{x}/{y}.png?apikey={key}",
+            Dark: "http://{s}.map.jms.gd/tile/carto_dark/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+
+    Thunderforest: {
+        Normal: {
+            Opencyclemap: "http://{s}.map.jms.gd/tile/opencyclemap/{z}/{x}/{y}.png?apikey={key}",
+            Transport: "http://{s}.map.jms.gd/tile/transport/{z}/{x}/{y}.png?apikey={key}",
+            Landscape: "http://{s}.map.jms.gd/tile/landscape/{z}/{x}/{y}.png?apikey={key}",
+            Mobile: "http://{s}.map.jms.gd/tile/mobile-atlas/{z}/{x}/{y}.png?apikey={key}",
+            Pioneer: "http://{s}.map.jms.gd/tile/pioneer/{z}/{x}/{y}.png?apikey={key}",
+            Neighbourhood: "http://{s}.map.jms.gd/tile/neighbourhood/{z}/{x}/{y}.png?apikey={key}",
+            Outdoors: "http://{s}.map.jms.gd/tile/outdoors/{z}/{x}/{y}.png?apikey={key}",
+            Atlas: "http://{s}.map.jms.gd/tile/atlas/{z}/{x}/{y}.png?apikey={key}",
+            TransportDark: "http://{s}.map.jms.gd/tile/transport-dark/{z}/{x}/{y}.png?apikey={key}",
+            Spinal: "http://{s}.map.jms.gd/tile/spinal-map/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    OSM: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/osm/{z}/{x}/{y}.png?apikey={key}",
+            Topo: "http://{s}.map.jms.gd/tile/osm_topo/{z}/{x}/{y}.png?apikey={key}",
+            Openstreetmap: "http://{s}.map.jms.gd/tile/openstreetmap/{z}/{x}/{y}.png?apikey={key}",
+            winter: "http://{s}.map.jms.gd/tile/osm_winter/{z}/{x}/{y}.png?apikey={key}",
+            summer: "http://{s}.map.jms.gd/tile/osm_summer/{z}/{x}/{y}.png?apikey={key}",
+            cycling: "http://{s}.map.jms.gd/tile/osm_cycling/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Opentopomap: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/opentopomap/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Baidu: {
+        Normal: {
+            Map: 'http://{s}.map.jms.gd/tile/baidu/{z}/{x}/{y}.png?apikey={key}'
+        },
+        Satellite: {
+            Map: 'http://{s}.map.jms.gd/tile/baidu_wx/{z}/{x}/{y}.png?apikey={key}',
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118",
+        tms: true
+    },
+
+    Openrailway: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/openrailway/{z}/{x}/{y}.png?apikey={key}",
+            Maxspeed: "http://{s}.map.jms.gd/tile/openrailway_maxspeed/{z}/{x}/{y}.png?apikey={key}",
+            Elec: "http://{s}.map.jms.gd/tile/openrailway_elec/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Stadia: {
+        Normal: {
+            Alidade_smooth: "http://{s}.map.jms.gd/tile/alidade_smooth/{z}/{x}/{y}.png?apikey={key}",
+            Watercolor: "http://{s}.map.jms.gd/tile/stamen_watercolor/{z}/{x}/{y}.jpg?apikey={key}",
+            Terrain: "http://{s}.map.jms.gd/tile/stamen_terrain/{z}/{x}/{y}.png?apikey={key}",
+            Toner: "http://{s}.map.jms.gd/tile/stamen_toner/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    ArcgisCn: {
+        Normal: {
+            Community: "http://{s}.map.jms.gd/tile/arcgis_comm/{z}/{x}/{y}.png?apikey={key}",
+            Gray: "http://{s}.map.jms.gd/tile/arcgis_gray/{z}/{x}/{y}.jpg?apikey={key}",
+            Blue: "http://{s}.map.jms.gd/tile/arcgis_blue/{z}/{x}/{y}.png?apikey={key}",
+            Warm: "http://{s}.map.jms.gd/tile/arcgis_warm/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Arcgis: {
+        Normal: {
+            Street: "http://{s}.map.jms.gd/tile/arcgis_world_street/{z}/{x}/{y}.png?apikey={key}",
+            Hillshade: "http://{s}.map.jms.gd/tile/arcgis_world_hillshade/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/arcgis_world_imagery/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Jl1: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/jl1_2022/{z}/{x}/{y}.png?apikey={key}",
+            Map2023: "http://{s}.map.jms.gd/tile/jl1_2023/{z}/{x}/{y}.png?apikey={key}",
+            Map2024: "http://{s}.map.jms.gd/tile/jl1_2024/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Geovis: {
+        Normal: {
+            Base: "http://{s}.map.jms.gd/tile/geovis_base/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Stamen: {
+        Terrain: {
+            Lines: "http://{s}.map.jms.gd/tile/stamen_terrain_lines/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Toner: {
+            Lines: "http://{s}.map.jms.gd/tile/stamen_toner_lines/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    BingCn: {
+        Normal: {
+            Map: "http://{s}.map.jms.gd/tile/bing/{z}/{x}/{y}.png?apikey={key}",
+            Dark: "http://{s}.map.jms.gd/tile/bing_dark/{z}/{x}/{y}.png?apikey={key}",
+            Cf: "http://{s}.map.jms.gd/tile/bing_cf/{z}/{x}/{y}.png?apikey={key}", //实时路况
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Bing: {
+        Satellite: {
+            Map: "http://{s}.map.jms.gd/tile/bing_aerial/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Jiangmen: {
+        Normal: {
+            Map: "http://68.196.3.198/map/mapimg/tile/{z}/{x}/{y}?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Maptiler: {
+        Normal: {
+            basic: "http://{s}.map.jms.gd/tile/maptiler_basic/{z}/{x}/{y}.png?apikey={key}",
+            backdrop: "http://{s}.map.jms.gd/tile/maptiler_backdrop/{z}/{x}/{y}.png?apikey={key}",
+            streets: "http://{s}.map.jms.gd/tile/maptiler_streets/{z}/{x}/{y}.png?apikey={key}",
+            winter: "http://{s}.map.jms.gd/tile/maptiler_winter/{z}/{x}/{y}.png?apikey={key}",
+            ocean: "http://{s}.map.jms.gd/tile/maptiler_ocean/{z}/{x}/{y}.png?apikey={key}",
+            toner: "http://{s}.map.jms.gd/tile/maptiler_toner/{z}/{x}/{y}.png?apikey={key}",
+            topo: "http://{s}.map.jms.gd/tile/maptiler_topo/{z}/{x}/{y}.png?apikey={key}",
+            dataviz: "http://{s}.map.jms.gd/tile/maptiler_dataviz/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    mapbox: {
+        Normal: {
+            mapbox01: "http://{s}.map.jms.gd/tile/mapbox01/{z}/{x}/{y}.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+
+    Nothing: {
+        Normal: {
+            Map: "http://map.jms.gd/static/img/512.png?apikey={key}",
+        },
+        Subdomains: ["a", "b", "c", "d"],
+        key: "jm20240118"
+    },
+};
+
+L.tileLayer.chinaProvider = function(type, options) {
+    return new L.TileLayer.ChinaProvider(type, options);
+};
+
+
+//坐标转换
+L.CoordConver = function () {
+
+    /**百度转84*/
+    this.bd09_To_gps84 = function(lng, lat) {
+        var gcj02 = this.bd09_To_gcj02(lng, lat);
+        var map84 = this.gcj02_To_gps84(gcj02.lng, gcj02.lat);
+        return map84;
+    }
+    /**84转百度*/
+    this.gps84_To_bd09 = function (lng, lat) {
+        var gcj02 = this.gps84_To_gcj02(lng, lat);
+        var bd09 = this.gcj02_To_bd09(gcj02.lng, gcj02.lat);
+        return bd09;
+    }
+    /**84转火星*/
+    this.gps84_To_gcj02 = function (lng, lat) {
+        var dLat = transformLat(lng - 105.0, lat - 35.0);
+        var dLng = transformLng(lng - 105.0, lat - 35.0);
+        var radLat = lat / 180.0 * pi;
+        var magic = Math.sin(radLat);
+        magic = 1 - ee * magic * magic;
+        var sqrtMagic = Math.sqrt(magic);
+        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
+        dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
+        var mgLat = lat + dLat;
+        var mgLng = lng + dLng;
+        var newCoord = {
+            lng: mgLng,
+            lat: mgLat
+        };
+        return newCoord;
+    }
+    /**火星转84*/
+    this.gcj02_To_gps84 = function (lng, lat) {
+        var coord = transform(lng, lat);
+        var lontitude = lng * 2 - coord.lng;
+        var latitude = lat * 2 - coord.lat;
+        var newCoord = {
+            lng: lontitude,
+            lat: latitude
+        };
+        return newCoord;
+    }
+    /**火星转百度*/
+    this.gcj02_To_bd09 = function (x, y) {
+        var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
+        var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
+        var bd_lng = z * Math.cos(theta) + 0.0065;
+        var bd_lat = z * Math.sin(theta) + 0.006;
+        var newCoord = {
+            lng: bd_lng,
+            lat: bd_lat
+        };
+        return newCoord;
+    }
+    /**百度转火星*/
+    this.bd09_To_gcj02 = function (bd_lng, bd_lat) {
+        var x = bd_lng - 0.0065;
+        var y = bd_lat - 0.006;
+        var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
+        var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
+        var gg_lng = z * Math.cos(theta);
+        var gg_lat = z * Math.sin(theta);
+        var newCoord = {
+            lng: gg_lng,
+            lat: gg_lat
+        };
+        return newCoord;
+    }
+
+    var pi = 3.1415926535897932384626;
+    var a = 6378245.0;
+    var ee = 0.00669342162296594323;
+    var x_pi = pi * 3000.0 / 180.0;
+    var R = 6378137;
+
+    function transform(lng, lat) {
+        var dLat = transformLat(lng - 105.0, lat - 35.0);
+        var dLng = transformLng(lng - 105.0, lat - 35.0);
+        var radLat = lat / 180.0 * pi;
+        var magic = Math.sin(radLat);
+        magic = 1 - ee * magic * magic;
+        var sqrtMagic = Math.sqrt(magic);
+        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
+        dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
+        var mgLat = lat + dLat;
+        var mgLng = lng + dLng;
+        var newCoord = {
+            lng: mgLng,
+            lat: mgLat
+        };
+        return newCoord;
+    }
+
+    function transformLat(x, y) {
+        var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
+        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
+        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
+        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
+        return ret;
+    }
+
+    function transformLng(x, y) {
+        var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
+        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
+        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
+        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
+        return ret;
+    }
+}
+
+L.coordConver = function () {
+    return new L.CoordConver()
+}
+
+
+L.tileLayer.chinaProvider = function (type, options) {
+    options = options || {}
+    options.corrdType = getCorrdType(type);
+    return new L.TileLayer.ChinaProvider(type, options);
+
+    //获取坐标类型
+    function getCorrdType(type) {
+        var parts = type.split('.');
+        var providerName = parts[0];
+        var zbName = "wgs84"
+        switch (providerName) {
+            case "Tengxun":
+            case "GaoDe":
+            case "Pengjiang":
+            case "GoogleCn": //google中国地图
+            case "ArcgisCn":
+            case "BingCn":
+            case "Test":
+                zbName = "gcj02";
+                break;
+            case "Baidu":
+                zbName = "bd09";
+                break;
+            case "Carto":
+            case "Thunderforest":
+            case "OSM":
+            case "TianDiTu":
+            case "Jl1":
+            case "Openrailway":
+            case "Bing":
+            case "Jiangmen":
+            case "Google":
+            case "mapbox":
+                zbName = "wgs84";
+                break;
+        }
+        return zbName;
+    }
+};
+
+L.GridLayer.include({
+    _setZoomTransform: function (level, _center, zoom) {
+        var center = _center;
+
+        if (center != undefined && this.options) {
+            if (this.options.corrdType == 'gcj02') {
+                center = L.coordConver().gps84_To_gcj02(_center.lng, _center.lat);
+            } else if (this.options.corrdType == 'bd09') {
+                center = L.coordConver().gps84_To_bd09(_center.lng, _center.lat);
+            }
+        }
+
+
+        var scale = this._map.getZoomScale(zoom, level.zoom),
+            translate = level.origin.multiplyBy(scale)
+                .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
+
+        if (L.Browser.any3d) {
+            L.DomUtil.setTransform(level.el, translate, scale);
+        } else {
+            L.DomUtil.setPosition(level.el, translate);
+        }
+
+    },
+    _getTiledPixelBounds: function (_center) {
+        var center = _center;
+
+        if (center != undefined && this.options) {
+            if (this.options.corrdType == 'gcj02') {
+                center = L.coordConver().gps84_To_gcj02(_center.lng, _center.lat);
+            } else if (this.options.corrdType == 'bd09') {
+                center = L.coordConver().gps84_To_bd09(_center.lng, _center.lat);
+            }
+        }
+
+        var map = this._map,
+            mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
+            scale = map.getZoomScale(mapZoom, this._tileZoom),
+            pixelCenter = map.project(center, this._tileZoom).floor(),
+            halfSize = map.getSize().divideBy(scale * 2);
+
+        return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
+
+    }
+})

src/view/case/draw/login.vue → src/view/case/draw/loginDialog.vue


+ 2 - 1
src/view/case/draw/selectMapImage.vue

@@ -69,6 +69,7 @@ import { QuiskExpose } from "@/helper/mount";
 import { debounce } from "@/util";
 import html2canvas from "html2canvas";
 import L from "leaflet";
+import "leaflet.chinatmsproviders";
 import "leaflet/dist/leaflet.css";
 import { router } from "@/router";
 export type MapImage = {
@@ -396,7 +397,7 @@ defineExpose<QuiskExpose>({
           });
         // }
       } else {
-        resolve({ blob: null, search: null, mapId: mapName.value.mapId, mapUrl: mapName.value.mapUrl });
+        resolve({ blob: null, search: null, mapId: mapName.value.mapId, mapUrl: mapName.value.mapUrl, ...info });
       }
     });
   },

+ 6 - 1
src/view/case/draw/selectMapleaftImages.vue

@@ -40,6 +40,7 @@
 </template>
 
 <script setup lang="ts">
+const VITE_VIBE = import.meta.env.VITE_VIBE;
 import AMapLoader from "@amap/amap-jsapi-loader";
 import { Search } from "@element-plus/icons-vue";
 import { wgs84_to_gcj02 } from "./map";
@@ -49,7 +50,11 @@ import { QuiskExpose } from "@/helper/mount";
 import { debounce } from "@/util";
 import html2canvas from "html2canvas";
 import L from "leaflet";
-import "leaflet.chinatmsproviders";
+import "./leaflet.ChineseTmsProviders.js";
+// import "leaflet.mapCorrection";
+// if(VITE_VIBE == 'jiangmeng'){
+//   import "leaflet.mapCorrection";
+// }
 import "leaflet/dist/leaflet.css";
 import { router } from "@/router";
 export type MapImage = { blob: Blob | null; search: MapInfo | null };

+ 2 - 2
src/view/case/draw/share.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="df-layout">
-    <login />
+    <!-- <login /> -->
     <Header
       class="df-header"
       :type="props.type"
@@ -43,7 +43,7 @@
 import Header from "./header.vue";
 import Slider from "./slider.vue";
 import Eshape from "./eshape.vue";
-import login from "./login.vue";
+// import login from "./loginDialog.vue";
 import { computed, nextTick, ref, watch, onMounted } from "vue";
 import { RouteName, router } from "@/router";
 import { useBoard, title } from "./board/useBoard";

+ 3 - 3
src/view/case/quisk.ts

@@ -8,8 +8,8 @@ import EditEshapeTable, {
 } from "./draw/editEshapeTable.vue";
 import SceneList from "./sceneList.vue";
 import SelectFuseImage, { FuseImage } from "./draw/selectFuseImage.vue";
-import SelectMapImages from "./draw/selectMapImagess.vue";
-// import SelectMapImage, { MapImage } from "./draw/selectMapleaftImages.vue";
+import SelectMapleaftImages from "./draw/selectMapImagess.vue";
+// import SelectMapleaftImages, { MapImage } from "./draw/selectMapleaftImages.vue";
 import SelectMapImage, { MapImage } from "./draw/selectMapImage.vue";
 import { quiskMountFactory } from "@/helper/mount";
 import { nextTick } from "vue";
@@ -58,7 +58,7 @@ export const selectFuseImage = quiskMountFactory(SelectFuseImage, {
   width: 1300,
 })<FuseImage>;
 
-export const selectMapImage = quiskMountFactory(params.testMap?SelectMapImages:SelectMapImage, {
+export const selectMapImage = quiskMountFactory(params.testMap?SelectMapImage:SelectMapleaftImages, {
   title: "选择地址",
   width: 588,
 })<MapImage>;

+ 51 - 61
src/view/dossier/index.vue

@@ -1,62 +1,28 @@
 <template>
   <div class="abstract">
-    <el-button class="w-full" @click="handleAdd">上传</el-button>
-    <!-- <div class="blfrom">
-      <div class="fromTitle">侦查实验</div>
-      <el-upload
-        class="upload-demo"
-        :multiple="false"
-        drag
-        :show-file-list="false"
-        :before-upload="upload"
-        :file-list="fileList"
-        :http-request="uploadNewFile"
-        :on-success="handleSuccess"
-        :on-preview="previewFile"
-        :accept="accept"
-        :before-remove="removeFile"
-      >
-        <div type="primary" :disabled="!!file">
-          <div>点击或拖拽文件上传</div>
-          <div class="">
-            支持≤10MB jpg\png\jpeg格式图片,及≤500MB pdf\doc文件上传
-          </div>
-        </div>
-      </el-upload>
+    <div class="float-right">
+      <el-button class="newbut" :icon="Upload" @click="handleAdd">上传</el-button>
     </div>
-    <div class="blfrom">
-      <div class="fromTitle">其他材料</div>
-      <el-upload
-        class="upload-demo"
-        :multiple="false"
-        drag
-        :before-upload="upload"
-        :file-list="fileList"
-        :on-success="handleSuccess1"
-        :http-request="uploadNewFile"
-        :on-preview="previewFile"
-        :accept="accept"
-        :before-remove="removeFile"
+    <div class="blList">
+      <div
+        class="listItem py-2"
+        v-for="(item, index) in list"
+        :key="index"
+        v-show="item.caseFilesList?.length"
       >
-        <div type="primary" :disabled="!!file">
-          <div>点击或拖拽文件上传</div>
-          <div class="">
-            支持≤10MB jpg\png\jpeg格式图片,及≤500MB pdf\doc文件上传
+        <div class="title1 pb-2">{{ item.filesTypeName }}</div>
+        <div class="itemList">
+          <div
+            style="margin: 12px 0"
+            class="item flex justify-between"
+            v-for="(item, index) in item.caseFilesList"
+          >
+            <span class="name cursor-pointer" @click="handleviewer(item.filesUrl)">{{
+              item.filesTitle
+            }}</span>
+            <el-icon><Delete @click="handleDel(item)" /></el-icon>
           </div>
         </div>
-      </el-upload>
-    </div> -->
-    <div class="blList">
-      <div class="listItem py-2" v-for="(item, index) in list" :key="index" v-show="item.caseFilesList?.length">
-        <div class="title1 pb-2">{{ item.filesTypeName }}</div>
-        <viewImg edit :list="item.caseFilesList || []" @handleItem="handleItem" /> 
-        <!-- <div
-          class="list2"
-          v-for="(item2, index2) in item.childrenList"
-          :key="index2"
-        >
-          <div class="title2">{{ item2.filesTypeName }}</div>
-        </div> -->
       </div>
     </div>
   </div>
@@ -68,8 +34,9 @@ import { addCaseFile } from "../originalPhoto/quisk";
 import { ElMessage, ElMessageBox } from "element-plus";
 import { uploadNewFile, addByMediaLiBrary } from "@/store/case";
 import { updateByTreeFileLists } from "@/store/case";
-import { Delete, Edit } from "@element-plus/icons-vue";
-import viewImg from "@/components/viewImg/index.vue"
+import { Delete, Edit, Upload } from "@element-plus/icons-vue";
+import viewImg from "@/components/viewImg/index.vue";
+import { delCaseFile } from "@/store/caseFile";
 import { router } from "@/router";
 const active = ref(true);
 const srcList = [
@@ -86,7 +53,7 @@ const showModal = ref(false);
 const caseId = computed(() => router.currentRoute.value?.params?.caseId);
 const fileInfo = ref({
   accept: ".jpg, .jpeg, .png, .mp4",
-  formats:  [".jpg", ".png", ".jpeg", ".doc", ".docx", ".pdf"],
+  formats: [".jpg", ".png", ".jpeg", ".doc", ".docx", ".pdf"],
   DrawFormatDesc: "jpg、png、jpeg、doc、pdf上传",
   fileSize: 500 * 1024 * 1024,
 });
@@ -123,13 +90,21 @@ function getList() {
 }
 function handleSuccess(item) {
   let uploadId = item?.data.id;
-  addByMediaLiBrary({ caseId: caseId.value, filesTypeId: ssjId.value, uploadId }).then((res) => {
+  addByMediaLiBrary({
+    caseId: caseId.value,
+    filesTypeId: ssjId.value,
+    uploadId,
+  }).then((res) => {
     getList();
   });
 }
 function handleSuccess1(item) {
   let uploadId = item?.data.id;
-  addByMediaLiBrary({ caseId: caseId.value, filesTypeId: zjjId.value, uploadId }).then((res) => {
+  addByMediaLiBrary({
+    caseId: caseId.value,
+    filesTypeId: zjjId.value,
+    uploadId,
+  }).then((res) => {
     getList();
   });
 }
@@ -162,21 +137,31 @@ function handleItem(type, item) {
   getList();
 }
 async function handleAdd() {
-  await addCaseFile({ caseId: caseId.value, fileInfo: fileInfo.value, filesTypeName: ['案件卷宗'] });
+  await addCaseFile({
+    caseId: caseId.value,
+    fileInfo: fileInfo.value,
+    filesTypeName: ["案件卷宗"],
+  });
   getList();
 }
-function handleDel() {
+function handleDel(file) {
   ElMessageBox.confirm("确定删除?", "提示", {
     confirmButtonText: "确定",
     cancelButtonText: "取消",
     type: "warning",
-  }).then(() => {
+  }).then(async () => {
+    await delCaseFile({ caseId: caseId.value, filesId: file.filesId });
     ElMessage({
       type: "success",
       message: "删除成功",
     });
+    getList();
   });
 }
+const handleviewer = (file) => {
+  showModal.value = true;
+  console.log("file", file);
+};
 </script>
 <style lang="scss" scoped>
 .abstract {
@@ -194,5 +179,10 @@ function handleDel() {
       line-height: 32px;
     }
   }
+  .blList{
+    .name {
+      color: #26559B;
+    }
+  }
 }
 </style>

+ 69 - 74
src/view/layout/index.vue

@@ -1,54 +1,37 @@
 <template>
   <div class="layer">
-    <!-- <login /> -->
+    <login />
     <ly-top class="top" />
+    <ly-slide class="slide" :names="menuRouteNames" v-if="!hiddenSlide" />
     <div class="content">
       <router-view v-slot="{ Component }" v-if="isSystem">
         <component :is="Component" />
       </router-view>
       <template v-else>
-        <ly-slide class="slide" :names="menuRouteNames" v-if="!hiddenSlide" />
         <div class="view" :class="{ full: hiddenSlide }">
-          <div class="app-wrap m-4" v-if="!hiddenSlide">
+          <div class="main p-4" :class="{ fullmain: hiddenSlide }">
+            <router-view v-slot="{ Component }">
+              <component :is="Component" />
+            </router-view>
+          </div>
+          <!-- <div class="app-wrap m-4" v-if="!hiddenSlide">
             <div
               class="app-scene"
               ref="sceneRef"
               :style="{
                 width: '100%',
                 height: 'calc(100% - 24px)',
-                // display: showScene ? 'none' : 'block'
               }"
             >
-            <!-- <div class="poprs sceneList" v-if="sceneList.length">
-              <el-dropdown placement="bottom" style="width: 100%" trigger="click" @command="handleCommand">
-                <el-button >
-                  <div style="width: 200px;" class="truncate" :title="sceneListName">
-                    {{sceneListName}}
-                  </div>
-                  <el-icon class="el-icon--right"><arrow-down /></el-icon>
-                </el-button>
-              <template #dropdown>
-                <el-dropdown-menu>
-                  <el-dropdown-item class="truncate" style="max-width: 400px;" v-for="item,index in sceneList" :key="index" :command="item.id">{{ item.name || '多元融合' }}</el-dropdown-item>
-                </el-dropdown-menu>
-              </template>
-            </el-dropdown>
-            </div> -->
-            <!-- @load="setupSDK($event.target)" -->
               <iframe
                 v-if="caseId"
-              :key="qpisceneList.length"
-                :src="sceneURL"
+                :key="qpisceneList.length"
+                :src="'https://survey.4dkankan.com' + sceneURL"
                 frameborder="0"
                 :style="{ width: '100%', height: '100%' }"
               ></iframe>
             </div>
-          </div>
-          <div class="main p-4" :class="{ fullmain: hiddenSlide }">
-            <router-view v-slot="{ Component }">
-              <component :is="Component" />
-            </router-view>
-          </div>
+          </div> -->
         </div>
       </template>
     </div>
@@ -57,14 +40,21 @@
 
 <script lang="ts" setup>
 import lyTop from "./top/index.vue";
-import login from "./login.vue";
+import login from "./loginDialog.vue";
 import lySlide from "./slide/index.vue";
 import { routeIsSystem, router } from "@/router";
 import { computed, ref, onMounted, watch } from "vue";
 import { menuRouteNames } from "@/app";
-import { updateByTreeFileLists, getCaseSceneListData, getUrlSrc, getcaseLists, getCaseSceneList  } from "@/store/case";
+import {
+  updateByTreeFileLists,
+  getCaseSceneListData,
+  getUrlSrc,
+  getcaseLists,
+  getCaseSceneList,
+  Extract,
+} from "@/store/case";
 import { json } from "stream/consumers";
-console.log(menuRouteNames, 'menuRouteNames', router.currentRoute.value.name);
+console.log(menuRouteNames, "menuRouteNames", router.currentRoute.value.name);
 const isSystem = computed(() => routeIsSystem());
 const caseId = computed(() => {
   const caseId = router.currentRoute.value.params.caseId;
@@ -74,58 +64,60 @@ const caseId = computed(() => {
 });
 // const qpisceneList = ref(getCaseSceneListData() as any[]);
 const sceneList = ref([]);
-const sceneURL = ref(`/code/index.html?caseId=${caseId.value}#/show`)
-const sceneListName = ref('')
-const modeList = ref([])
+const sceneURL = ref(`/code/index.html?caseId=${caseId.value}#/show`);
+const sceneListName = ref("");
+const modeList = ref([]);
 const init = async () => {
-  modeList.value = modeList.value.length == 0 ? await getcaseLists(caseId.value as number) : modeList.value;
+  modeList.value =
+    modeList.value.length == 0
+      ? await getcaseLists(caseId.value as number)
+      : modeList.value;
   let listss = await getCaseSceneList(caseId.value as number);
   sceneList.value = listss;
-  if(listss.length){
-    sceneList.value.unshift({ id: -1, type: 99, name: '多元融合' });
-    let url = getUrlSrc({ id: 0 }, caseId.value)
+  if (listss.length) {
+    // sceneList.value.unshift({ id: -1, type: 99, name: "多元融合" });
+    let url = getUrlSrc({ id: 0, ...listss[0] }, caseId.value);
     sceneURL.value = url;
   }
-  console.log('init', modeList.value, sceneList.value, modeList.value.length);
-  handleCommand()
+  console.log("init", modeList.value, sceneList.value, modeList.value.length);
+  // handleCommand();
   // let url = getUrlSrc({ id: 0 }, caseId.value)
   // sceneURL.value = url;
-}
-// init()
-const qpisceneList = computed(() => getCaseSceneListData(caseId.value as number));
-watch(() => qpisceneList.value, () => {
-  console.log(qpisceneList.value, 'qpisceneList');
-//   if(!qpisceneList.value instanceof Array){
-//     return;
-//   }
-//   let newqpisceneList = qpisceneList.value && JSON.parse(JSON.stringify(qpisceneList.value)) || []
-//   if(newqpisceneList.length){
-//     newqpisceneList.unshift({ id: -1, type: 99, name: '多元融合' });
-//   }
-//   console.log('qpisceneList', qpisceneList.value, newqpisceneList);
-//   sceneList.value = qpisceneList.value && [...newqpisceneList] || []
-//   handleCommand()
-})
+};
+init();
+const qpisceneList = computed(() =>
+  getCaseSceneListData(caseId.value as number)
+);
+watch(
+  () => Extract.value,
+  () => {
+    if (Extract.value) {
+      sceneURL.value = sceneURL.value + "#/repair";
+    } else {
+      sceneURL.value = sceneURL.value.replace("#/repair", "");
+    }
+    console.log(Extract.value, "Extract");
+  }
+);
 onMounted(() => {
   // setupSDK(document.querySelector('iframe') as HTMLIFrameElement)
-})
-updateByTreeFileLists()
+});
+updateByTreeFileLists();
 const hiddenSlide = computed(
   () => !menuRouteNames.includes(router.currentRoute.value.name as string)
 );
 const handleCommand = (command) => {
   let newid = command || sceneList.value[0].id;
-  console.log('handleCommand', command, newid, sceneList.value);
-  let item = sceneList.value.find(item => item.id == newid);
+  console.log("handleCommand", command, newid, sceneList.value);
+  let item = sceneList.value.find((item) => item.id == newid);
   sceneListName.value = item.name;
-  let url = getUrlSrc(item, caseId.value)
+  let url = getUrlSrc(item, caseId.value);
   sceneURL.value = url;
-  console.log('handleCommand', command, item, url);
-}
+  console.log("handleCommand", command, item, url);
+};
 const handleClick = (command: string) => {
-  console.log('handleClick', command);
-}
-
+  console.log("handleClick", command);
+};
 </script>
 
 <style lang="scss" scoped>
@@ -147,13 +139,13 @@ const handleClick = (command: string) => {
     overflow: hidden;
 
     .slide {
-      width: 16.66rem;
+      width: 100%;
       flex: 0 0 auto;
     }
 
     .view {
       flex: 0 0 auto;
-      width: calc(100% - 16.66rem);
+      width: 100%;
       background-color: var(--bgColor);
       // flex-direction: column;
       display: flex;
@@ -161,8 +153,9 @@ const handleClick = (command: string) => {
       &.full {
         width: 100%;
       }
-      .app-wrap, .app-view {
-        width: calc(100% - 320px);
+      .app-wrap,
+      .app-view {
+        width: calc(100% - 640px);
         height: 100%;
       }
       .player {
@@ -177,15 +170,17 @@ const handleClick = (command: string) => {
         overflow: hidden;
         flex: 1;
         background: #fff;
+        padding: 1rem 20px 1rem 32px;
+        border-top: 1px solid #e6e6e6;
       }
-      .fullmain{
+      .fullmain {
         background-color: var(--bgColor);
         overflow-y: scroll;
       }
-      .app-scene{
+      .app-scene {
         position: relative;
       }
-      .sceneList{
+      .sceneList {
         position: absolute;
         width: 200px;
         text-align: center;
@@ -212,7 +207,7 @@ const handleClick = (command: string) => {
 }
 </style>
 <style lang="scss">
-.el-dropdown{
+.el-dropdown {
   width: 200px;
 }
 </style>

+ 272 - 0
src/view/layout/loginDialog.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="login-layer">
+    <el-dialog
+      v-model="loginShow"
+      width="560"
+      :show-close="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :before-close="handleClose"
+    >
+      <div style="text-align: center">
+        <el-form
+          class="panel dialoglogin"
+          ref="formEl"
+          :model="form"
+          :rules="rules"
+          @submit.stop
+        >
+          <h2 class="title1">现场勘查平面图</h2>
+          <div class="title2">支持户型图、平面图绘制</div>
+          <el-form-item class="panel-form-item" prop="account">
+            <p class="err-info">{{ verification.phone }}</p>
+            <el-input
+              :maxlength="11"
+              v-model.trim="form.account"
+              placeholder="账号"
+              @keydown.enter="submitClick"
+            ></el-input>
+          </el-form-item>
+          <el-form-item class="panel-form-item" prop="password">
+            <p class="err-info">{{ verification.psw }}</p>
+            <el-input
+              v-model="form.password"
+              :maxlength="16"
+              placeholder="密码"
+              :type="flag ? 'password' : 'text'"
+              @keydown.enter="submitClick"
+            >
+              <template v-slot:suffix>
+                <img
+                  v-if="flag"
+                  @click="flag = !flag"
+                  style="width: 20px"
+                  src="@/assets/image/pasword.png"
+                  alt=""
+                />
+                <el-icon
+                  :size="20"
+                  @click="flag = !flag"
+                  class="icon-style"
+                  v-else
+                >
+                  <View />
+                </el-icon>
+              </template>
+            </el-input>
+          </el-form-item>
+          <el-form-item class="panel-form-item">
+            <el-checkbox v-model="rememberMe" label="记住密码" size="large" />
+          </el-form-item>
+          <!-- <el-form-item class="panel-form-item code-form-item">
+          <p class="err-info">{{ verification.code }}</p>
+          <el-input
+            v-model="form.code"
+            placeholder="验证码"
+            @keydown.enter="submitClick"
+            class="code-input"
+          >
+            <template v-slot:append>
+              <img :src="codeImg" class="code-img" @click="refer" />
+            </template>
+          </el-input>
+        </el-form-item> -->
+
+          <el-form-item class="panel-form-item">
+            <el-button type="primary" class="fill" @click="submitClick"
+              >登录</el-button
+            >
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <div class="tips">
+            公安部鉴定中心 & 江门市公安局 & 四维时代
+          </div>
+          <div class="tips" >公安部科技强警基础工作计划(2022JC13)</div>
+          <!-- <el-button @click="loginShow = false">Cancel</el-button>
+        <el-button type="primary" @click="loginShow = false">
+          Confirm
+        </el-button> -->
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { reactive, watch, ref, computed, onMounted } from "vue";
+import { openErrorMsg, baseURL, getCode } from "@/request";
+import { PHONE } from "@/constant/REG";
+import { guid, strToParams } from "@/util";
+import { RouteName, router } from "@/router";
+import { login } from "@/store/system";
+import { appConstant } from "@/app";
+import { user } from "@/store/user";
+import { loginShow } from "@/store/system";
+
+// 是否显示明文密码
+const flag = ref(true);
+// 表单
+const jsonrUrl = ref({
+  register: '',
+  setting: '/admin/index.html?key=gabjdzx',
+});
+  // fetch('/admin/register.json')
+  //   .then((response) => response.json())
+  //   .then((data) => {
+  //     jsonrUrl.value.register = data.url;
+  //   });
+const form = reactive({
+  account: localStorage.getItem("userName") || "",
+  password: localStorage.getItem("password") || "",
+  code: "",
+  remember: import.meta.env.DEV || localStorage.getItem("remember") === "1",
+});
+const formEl = ref(null);
+const rememberMe = ref(false);
+const verification = reactive({ phone: "", psw: "", code: "" });
+const rules = reactive<FormRules<RuleForm>>({
+  account: [
+    { required: true, message: "请输入账号", trigger: "blur" },
+    { max: 16, message: "最大长度16", trigger: "blur" },
+  ],
+  password: [
+    {
+      required: true,
+      message: "请输入密码",
+      trigger: "change",
+    },
+  ],
+});
+onMounted(() => {
+  let localePass = localStorage.getItem("password");
+  console.log("localePass", localePass);
+  if (localePass) {
+    // let password = decodeURIComponent(escape(window.atob(localePass)));
+    form.account = localStorage.getItem("username") || "";
+    form.password = localePass;
+    rememberMe.value = true;
+  }
+});
+// 验证
+// watch(
+//   form,
+//   () => {
+//     console.log("form", form);
+//     if (!form.account) {
+//       verification.phone = "请输入手机号";
+//     } else if (form.account == "88888888888") {
+//       verification.phone = "";
+//     } else {
+//       verification.phone = PHONE.REG.test(form.account) ? "" : PHONE.tip;
+//     }
+//     if (!form.password) {
+//       verification.psw = "请输入密码";
+//     } else {
+//       verification.psw = "";
+//     }
+//     if (!form.code.trim()) {
+//       verification.code = "请输入验证码";
+//     } else {
+//       verification.code = "";
+//     }
+//   },
+//   { immediate: true }
+// );
+
+// 图片验证码
+const imgKey = ref(guid());
+const refer = () => (imgKey.value = guid());
+const codeImg = computed(() => baseURL + getCode + "?key=" + imgKey.value);
+
+// 表单提交
+const submitClick = async (value) => {
+  if(value == 1 || value == 2){
+    return window.open(value==1?jsonrUrl.value.register:jsonrUrl.value.setting);
+  }
+  if (!formEl.value) return;
+  await formEl.value.validate(async (valid, fields) => {
+    if (valid) {
+      console.log("submit!");
+      try {
+        await login({
+          username: form.account,
+          password: form.password,
+        });
+
+        if (rememberMe.value) {
+          let password: string = window.btoa(
+            unescape(encodeURIComponent(form.password))
+          );
+          localStorage.setItem("password", password);
+          localStorage.setItem("username", form.account);
+        } else {
+          localStorage.removeItem("password");
+          localStorage.removeItem("username");
+        }
+
+        // const params = strToParams(window.location.search);
+        // if ("redirect" in params) {
+        //   const url = new URL(unescape(params.redirect));
+        //   url.searchParams.delete("token");
+        //   url.searchParams.append("token", user.value.token);
+        //   window.location.replace(url);
+        // } else {
+          location.reload()
+          // router.replace({ name: RouteName.scene, params: { caseId: 360 } });
+        // }
+      } catch (e) {
+        console.error(e);
+        return refer();
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+  // if (verification.phone && verification.phone !== "88888888888") {
+  //   return openErrorMsg(verification.phone);
+  // }
+  // if (verification.psw) return openErrorMsg(verification.psw);
+  // if (verification.code) return openErrorMsg(verification.code);
+};
+</script>
+
+<style lang="scss" scoped>
+.login-layer {
+  text-align: center;
+  .dialoglogin {
+    padding: 0 80px;
+  }
+  .title1 {
+    font-weight: bold;
+    font-size: 40px;
+    color: #000000;
+    line-height: 60px;
+    letter-spacing: 8px;
+    font-style: normal;
+  }
+  .title2 {
+    font-weight: 400;
+    font-size: 14px;
+    color: #666666;
+    line-height: 30px;
+    font-style: normal;
+    margin-bottom: 32px;
+  }
+}
+.tips {
+  font-weight: 400;
+  font-size: 12px;
+  color: #999999;
+  line-height: 30px;
+  letter-spacing: 1px;
+  text-align: center;
+}
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+}
+</style>

+ 33 - 5
src/view/layout/slide/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="slide">
-    <el-menu
+    <!-- <el-menu
       :default-active="(router.currentRoute.value.name as string)"
       @select="(name: string) => router.push({ name })"
     >
@@ -10,23 +10,51 @@
         v-for="route in routes"
         :key="route.name"
       />
-    </el-menu>
+    </el-menu> -->
+    <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
+      <el-tab-pane
+        v-for="item in routes"
+        :label="item.meta.title"
+        :name="item.name"
+        :key="item.name"
+      />
+    </el-tabs>
   </div>
 </template>
 
 <script setup lang="ts">
+import { ref, watch } from "vue";
 import subMenu from "./submenu.vue";
 import { getPermissionRoutes } from "@/store/permission";
 import { router } from "@/router";
 
 const props = defineProps<{ names: string[] }>();
-
+const activeName = ref(router.currentRoute.value.name || "scene");
 const routes = getPermissionRoutes(props.names);
-console.log(routes, 'routes', props.names);
+activeName.value = router.currentRoute.value.name as string;
+const handleClick = (name) => {
+  console.log(name, "tab", router);
+  router.push({ name });
+};
+// watch(() => route.path,(newPath, oldPath) => { console.log(newPath) },{ immediate: true });
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 .slide {
+  padding: 0 38px;
   background: #ffffff;
+  .el-tabs__header {
+    // margin: 0 0 24px;
+    margin: 0;
+  }
+  .el-tabs__nav-next,
+  .el-tabs__nav-prev {
+    line-height: 50px;
+  }
+  .el-tabs__item {
+    font-size: 16px;
+    line-height: 88px;
+    height: 88px;
+  }
 }
 </style>

+ 11 - 3
src/view/layout/top/index.vue

@@ -1,12 +1,20 @@
 <template>
   <div class="header-top">
     <div class="title" >
-      <div class="span" :title="title">{{ title }}</div>
       <div class="edit" style="white-space: nowrap;text-overflow: ellipsis;">编辑</div>
+      <div class="span" :title="title">{{ title }}</div>
     </div>
     <div class="oper-btns">
-      <el-button style="padding: 5px 10px" type="primary" @click="handlemtk">媒体库</el-button>
-      <el-button style="padding: 5px 10px" type="primary" @click="handleSee">预览案件</el-button>
+      <el-button class="newbut" style="padding: 5px 10px" type="primary" @click="handlemtk">
+        <i class='iconfont icon-rename'></i>
+      </el-button>
+      <el-button class="newbut" style="padding: 5px 10px" type="primary" @click="handlemtk">
+        <i class='iconfont icon-view'></i>
+      </el-button>
+      <el-button class="newbut" style="padding: 5px 10px" type="primary" @click="handleSee">
+        <i class='iconfont icon-display'></i>
+      </el-button>
+      <el-button class="newbut" style="padding: 5px 10px" type="primary" @click="handleSee">预览</el-button>
     </div>
     <!-- <div class="oper-btns" v-if="user.info">
       <div class="user-menu">

+ 8 - 8
src/view/layout/top/style.scss

@@ -1,5 +1,5 @@
 .header-top {
-  background-color: #001529;
+  background-color: #fff;
   &.system {
     background-color: #fff;
 
@@ -8,7 +8,7 @@
   .title {
     display    : flex;
     align-items: center;
-    color: #FFFFFF;
+    color: #000;
     font-weight: bold;
     font-size: 24px;
     margin-bottom: 0;
@@ -21,13 +21,13 @@
       font-family: Roboto, Roboto;
       font-weight: bold;
       font-size: 24px;
-      margin: 0 12px;
-      padding: 0 12px;
-      border-left: 1px solid rgba(255, 255, 255, 0.30);
+      margin: 0 12px 0 0px;
+      padding: 0 12px 0 0px;
+      border-right: 1px solid #ECECEC;
     }
     h2 {
       font-size  : 1.14rem;
-      color      : #fff;
+      color      : #000;
       margin-left: 0.66rem;
 
       span {
@@ -52,14 +52,14 @@
       span {
         margin-left: 5px;
         outline    : none;
-        color      : #fff;
+        color      : #000;
       }
 
       .oper-down {
         cursor: pointer;
 
         +i {
-          color: #fff;
+          color: #000;
         }
       }
     }

+ 184 - 0
src/view/material/AddScenesImg.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="AddScenesImg flex">
+    <div class="overContent" v-if="sdk">
+    <div class="secnetitle flex items-center" @click="handleShow">
+      <div class="title">{{scenes.item && scenes.item.sceneName || '场景选择'}}</div>
+      <el-icon><ArrowDownBold /></el-icon>
+    </div>
+    <div class="scale flex items-center justify-center">
+        <div class="scaleItem level">
+          <el-icon><Rank /></el-icon>
+        </div>
+        <div class="scaleItem proportion">
+          1:1
+        </div>
+        <div class="scaleItem slider">
+          <el-slider v-model="scenes.scale" />
+        </div>
+    </div>
+    <transition name="animate__backInLeft"  enter-active-class="animated animate__slideInLeft" leave-active-class="animated animate__slideOutLeft">
+      <div class="viewLeft" v-if="show.left">
+        <div class="scenesList">
+          <el-icon class="clear" @click="handleShow"><Close /></el-icon>
+          <div
+            class="item"
+            :class="{ active: item.num == scenes.num }"
+            v-for="(item, index) in scenes.list"
+            :key="index"
+            @click="handleItem(item)"
+          >
+            {{ item.name }}
+          </div>
+        </div>
+      </div>
+    </transition>
+    </div>
+    <div class="view">
+      <iframe
+        ref="scene"
+        src="/swkk/spg.html?m=YZL-jm-Mmsq4vpgtQh"
+        frameborder="0"
+      ></iframe>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import { router } from "@/router";
+import { QuiskExpose } from "@/helper/mount";
+import {
+  getCaseSceneList,
+  getUrlSrc,
+  getSceneListTree,
+  setCaseaddOrUpdate,
+} from "@/store/case";
+import { exposeFactory } from "@/util/platform";
+import { computed, ref, onMounted } from "vue";
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+const scenes = ref({
+  url: "",
+  num: "",
+  scale: 1,
+  item: {},
+  list: [],
+});
+const sdk = ref(null);
+const show = ref({
+  left: true
+})
+const scene = ref(null);
+async function geiList() {
+  scenes.value.list = await getCaseSceneList(caseId.value);
+  if(scenes.value.list.length>0){
+    let item = scenes.value.list[0]
+    scenes.value.item = item
+    scenes.value.num = item.num
+  }
+  console.log("res", scenes.value.list);
+}
+const findObjectAttr = (
+  data,
+  key
+) => {
+  return new Promise((resolve) => {
+    const query = () => {
+      if (key in data) {
+        resolve(data[key]);
+      } else {
+        setTimeout(query, 6);
+      }
+    };
+    query();
+  });
+};
+
+const handleItem = (item: any) => {
+  scenes.value.num = item.num;
+  scenes.value.item = item;
+};
+const handleShow = () => {
+  show.value.left = !show.value.left
+}
+geiList();
+onMounted(async () => {
+  let win = scene.value.contentWindow;
+  sdk.value = await findObjectAttr(win, "__sdk")
+  console.log("sdk", sdk);
+});
+defineExpose<QuiskExpose>({
+  async submit() {
+    // 打印 caseId 的值
+    console.log(caseId.value);
+
+    // 调用 exposeFactory 函数,传入 scenes.value.num 作为参数,并将结果赋值给变量 img
+    let sceneDom = await exposeFactory(
+      scenes.value.item,
+      scene.value.contentWindow
+    );
+    let img = await sceneDom?.getView();
+    console.log("exposeFactory", img);
+  },
+});
+</script>
+<style lang="scss" scoped>
+.AddScenesImg {
+  position: relative;
+  height: 500px;
+  overflow: hidden;
+  .scale{
+    position: absolute;
+    width: 100%;
+    text-align: center;
+    .scaleItem {
+      display: inline-block;
+      margin: 0 10px;
+      background: rgb(0, 0, 0, 0.8);
+      padding: 10px;
+      color: #fff;
+    }
+    .slider{
+      width: 200px;
+    }
+  }
+  .secnetitle{
+    position: absolute;
+    left: 20px;
+    top: 15px;
+    font-size: 16px;
+    font-weight: 700;
+    text-shadow: 0 0 2px rgba(0, 0, 0, .7);
+    color: #fff;
+  }
+  .view {
+    width: 100%;
+    height: 100%;
+    iframe {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .viewLeft {
+    padding: 3px 0;
+    height: 100%;
+    color: #fff;
+    position: absolute;
+    background: rgb(0, 0, 0, 0.8);
+    line-height: 30px;
+    text-align: center;
+    width: 230px;
+    .active {
+      color: aqua;
+    }
+    .scenesList {
+      padding: 32px 0;
+      position: relative;
+      .clear {
+        position: absolute;
+        top: 20px;
+        color: #fff;
+        right: 20px;
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 218 - 0
src/view/material/addCaseFile.vue

@@ -0,0 +1,218 @@
+<template>
+  <el-form
+    ref="form"
+    :model="caseFile"
+    label-width="90px"
+    class="camera-from dispatch-file-from jm-file-upload"
+  >
+    <el-form-item label="分类:" class="mandatory">
+      <el-cascader
+        v-model="caseFile.filesTypeId"
+        :how-all-levels="false"
+        style="width: 300px;"
+        :options="fileOptions"
+        :props="{
+          checkStrictly: false,
+        }"
+        clearable
+      />
+    </el-form-item>
+    <el-form-item label="上传类别:" class="mandatory">
+      <el-select
+        style="width: 300px;"
+        v-model="caseFile.filesType"
+        placeholder="请选择上传类别"
+      >
+        <el-option label="本地上传" :value="1" />
+        <el-option label="媒体库上传" :value="0" />
+      </el-select>
+      <!-- <el-cascader
+        v-model="caseFile.filesTypeId"
+        :how-all-levels="false"
+        style="width: 300px;"
+        :options="fileOptions"
+        :props="{
+          checkStrictly: false,
+        }"
+        clearable
+      /> -->
+    </el-form-item>
+    <el-form-item label="文件:" v-if="caseFile.filesType == 1" class="mandatory uploadFile">
+      <el-upload
+        class="upload-demo"
+        :multiple="false"
+        drag
+        :limit="1"
+        :before-upload="upload"
+        :file-list="fileList"
+        :http-request="uploadNewFile"
+        :on-success="handleSuccess"
+        :on-preview="previewFile"
+        :on-exceed="handleExceed"
+        :accept="accept"
+        :before-remove="removeFile"
+      >
+        <div type="primary" :disabled="!!file">
+          <div>点击或拖拽文件上传</div>
+          <div class="">{{ size }}以内的{{ formatDesc }}</div>
+        </div>
+        <template v-slot:file="{ file }: { file: UploadFile }">
+          <div class="file" @click.stop="previewFile()">
+            <div>
+              <el-icon><Document /></el-icon>
+              <span class="name">{{ file.name }}</span>
+            </div>
+            <el-icon @click.stop="removeFile()"><Close /></el-icon>
+          </div>
+        </template>
+      </el-upload>
+    </el-form-item>
+    
+    <el-form-item label="文件:" v-else class="mandatory uploadFile">
+      <el-button  type="primary" class="mtk" @click="handleAdd"
+      >从媒体库上传</el-button>
+    </el-form-item>
+    <div style="padding-left: 90px" v-if="caseFile.filesType == 0">
+        <viewImg :list="mtkList" edit delete @handleItem="handleItem"/>
+      </div>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import { uploadNewFile, addByMediaLiBrary } from "@/store/case";
+import viewImg from "@/components/viewImg/index.vue"
+import {
+  DrawFormatDesc,
+  DrawFormats,
+  FileDrawType,
+  OtherFormatDesc,
+  OtherFormats,
+} from "@/constant/caseFile";
+import { maxFileSize } from "@/constant/caseFile";
+import { useUpload } from "@/hook/upload";
+import { CaseFile, addCaseFile } from "@/store/caseFile";
+import { ElMessage, UploadFile } from "element-plus";
+import { computed, ref, watchEffect, onMounted } from "vue";
+import { addCaseScenes } from "./quisk";
+import { QuiskExpose } from "@/helper/mount";
+import { updateSelectByTreeFileLists } from "@/store/case";
+
+const props = defineProps<{
+  caseId: number;
+  fileType: number;
+  filesTypeName: [string];
+  fileInfo?: Object;
+}>();
+const fileOptions = ref([])
+const mtkList = ref([]);
+onMounted(async () => {
+  let newfileOptions = await updateSelectByTreeFileLists();
+  if(props.filesTypeName){
+    let newChildren = newfileOptions
+    props.filesTypeName.map(ele => {
+      newChildren = newChildren.find((item) => item.label == ele).children || []
+    })
+    fileOptions.value = newChildren
+    // fileOptions.value = newfileOptions.find((item) => item.label == props.filesTypeName).children || [];
+  } else {
+    fileOptions.value = newfileOptions;
+  }
+  console.log('fileOptions', fileOptions.value, newfileOptions);
+});
+
+const caseFile = ref({
+  caseId: props.caseId,
+  filesTypeId: props.fileType,
+  filesTitle: "",
+  dictId: '',
+  uploadId: '',
+  filesType: 1,
+});
+console.log('caseFile', props.fileInfo);
+const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: props.fileInfo?.fileSize || 50 * 1024 * 1024,
+  formats: props.fileInfo?.formats || [".jpg", ".jpeg", ".png"],
+});
+
+const formatDesc = computed(() => {
+  return props.fileInfo?.DrawFormatDesc||'jpg、png、jpeg上传'
+});
+
+// 上传请求
+const handleSuccess = (option) => {
+  console.log('handleSuccess', option);
+}
+const handleExceed = (option) => {
+  ElMessage.error("只能上传1个文件!");
+}
+
+const handleAdd = async () => {
+  let fileId =  await addCaseScenes({formats: props.fileInfo?.formats || [".jpg", ".jpeg", ".png"]});
+  mtkList.value = fileId.map(ele => {
+    return {
+      filesUrl: ele.fileUrl,
+      ...ele,
+     }
+  }) || [];
+  console.log("handleItem", mtkList.value);
+};
+const handleItem = (type, item) => {
+  console.log("handleItem", type, item);
+  if(type == 'delete'){
+    mtkList.value = mtkList.value.filter(ele => ele.id != item.id)
+  }
+};
+watchEffect(() => {
+  console.log('file', file.value);
+  if (file.value?.name) {
+    caseFile.value.uploadId = file.value?.response?.data.id
+  }
+});
+
+defineExpose<QuiskExpose>({
+  async submit() {
+    let filesTypeId = caseFile.value.filesTypeId && Array.isArray(caseFile.value.filesTypeId)?caseFile.value.filesTypeId.slice(-1):caseFile.value.filesTypeId
+    if (!file.value && caseFile.value.filesType == 1 || caseFile.value.filesType == 0 && mtkList.value.length == 0) {
+      ElMessage.error("请上传文件");
+      throw "请上传文件";
+    } else if (!filesTypeId) {
+      ElMessage.error("文件分类不能为空!");
+      throw "文件分类不能为空!";
+    }
+    let uploadId = file.value?.response?.data.id,
+    uploadIds = mtkList.value.map(ele => ele.uploadId) || [];
+    let params = { ...caseFile.value, file: file.value, filesTypeId: filesTypeId[0] };
+    if(caseFile.value.filesType == 1) params.uploadId = uploadId;
+    if(caseFile.value.filesType == 0) params.uploadIds = uploadIds;
+    await addByMediaLiBrary(params);
+    return caseFile.value;
+  },
+});
+</script>
+
+<style scoped lang="scss">
+.upload-demo {
+  overflow: hidden;
+}
+
+.file {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  > div {
+    display: flex;
+    align-items: center;
+  }
+
+  .name {
+    margin-left: 10px;
+  }
+}
+.jm-file-upload {
+  // .mtk {
+  //   position: absolute;
+  //   right: 0;
+  //   top: 0;
+  // }
+}
+</style>

+ 189 - 0
src/view/material/addLibrary.vue

@@ -0,0 +1,189 @@
+<template>
+  <el-form
+    ref="form"
+    :model="caseFile"
+    label-width="90px"
+    class="camera-from dispatch-file-from jm-file-upload"
+  >
+    <el-form-item label="分组:">
+      <el-select
+      v-model="caseFile.dictId"
+      placeholder="请选择分组"
+      size="large"
+      @change="change"
+      :fit-input-width="true"
+      style="width: 240px"
+    >
+      <el-option
+        v-for="item in options"
+        :key="item.id"
+        :label="item.dictName"
+        :value="item.id"
+      />
+    </el-select>
+      <!-- <el-cascader
+        v-model="caseFile.type"
+        :options="fileOptions"
+        :props="{
+          checkStrictly: true,
+        }"
+        clearable
+      /> -->
+    </el-form-item>
+    <el-form-item label="文件:" class="mandatory uploadFile">
+      <el-upload
+        class="upload-demo"
+        :multiple="false"
+        drag
+        :limit="1"
+        :disabled="!caseFile.dictId || (!!file)"
+        :before-upload="isAnimation?upload5:upload"
+        :file-list="isAnimation?fileList5:fileList"
+        :http-request="()=>{}"
+        :on-success="handleSuccess"
+        :on-preview="isAnimation?previewFile5:previewFile"
+        :accept="isAnimation?accept5:accept"
+        :before-remove="isAnimation?removeFile5:removeFile"
+      >
+        <div type="primary" :disabled="!!file">
+          <div>点击或拖拽文件上传 {{ isAnimation?'5MB':'2GB' }}</div>
+          <div class="">以内的{{ formatDesc }}</div>
+        </div>
+        <template v-slot:file="{ file }: { file: UploadFile }">
+          <div class="file" @click.stop="previewFile()">
+            <div>
+              <el-icon><Document /></el-icon>
+              <span class="name">{{ file.name }}</span>
+            </div>
+            <el-icon @click.stop="removeFileAll"><Close /></el-icon>
+          </div>
+        </template>
+      </el-upload>
+    </el-form-item>
+    <div v-if="isAnimation" style="margin-left: 35px">注意:obj 文件需要压缩成zip格式(含obj、jpg、mtl)上传。</div>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import {
+  DrawFormatDesc,
+  DrawFormats,
+  FileDrawType,
+  OtherFormatDesc,
+  OtherFormats,
+  fileOptions
+} from "@/constant/caseFile";
+import { maxFileSize } from "@/constant/caseFile";
+import { useUpload } from "@/hook/upload";
+import { CaseFile, addCaseFile } from "@/store/caseFile";
+import { getDictFileLists, uploadNewFile } from "@/store/case";
+import { ElMessage, UploadFile } from "element-plus";
+import { computed, ref, watchEffect, onMounted } from "vue";
+import { addCaseScenes } from "./quisk";
+import { QuiskExpose } from "@/helper/mount";
+
+const props = defineProps<{
+  caseId: number;
+  fileType: number;
+}>();
+
+const isAnimation = ref(false)
+const caseFile = ref({
+  caseId: props.caseId,
+  filesTypeId: props.fileType,
+  filesTitle: "",
+  type: '',
+  dictId: ''
+});
+const options = ref([])
+onMounted(async () => {
+  const res = await getDictFileLists(1);
+  console.log('onMounted', res);
+  options.value = res;
+});
+
+const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: 2000 * 1024 * 1024,
+  formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3", ".shp"],
+});
+
+const { size:size5, fileList:fileList5, upload:upload5, removeFile:removeFile5, previewFile:previewFile5, file:file5, accept:accept5 } = useUpload({
+  maxSize: 5 * 1024 * 1024,
+  formats: [".zip",".glb"],
+});
+const formatDesc = computed(() =>
+{
+  return isAnimation.value?'zip、glb上传':'jpg、png、jpeg、mp4、wav、mp3、shp上传'
+}
+);
+
+// 上传请求
+const handleSuccess = (option) => {
+  console.log(option);
+}
+const change = (vale) => {
+  removeFile()
+  removeFile5()
+  let item = options.value.find(ele => ele.id == vale) || {}
+  isAnimation.value = item.useType == 'animation'
+}
+const removeFileAll = async () => {
+  removeFile()
+  removeFile5()
+  console.log('removeFileAll')
+};
+
+const handleAdd = async () => {
+  await addCaseScenes();
+};
+watchEffect(() => {
+  if (file.value?.name) {
+    caseFile.value.filesTitle = file.value?.name.substring(0, 50);
+  }
+  if (file5.value?.name) {
+    caseFile.value.filesTitle = file5.value?.name.substring(0, 50);
+  }
+});
+
+defineExpose<QuiskExpose>({
+  async submit() {
+    if ((!file.value && !isAnimation.value) || (!file5.value && isAnimation.value)) {
+      ElMessage.error("请上传附件");
+      throw "请上传附件";
+    } else if (!caseFile.value.filesTitle.trim()) {
+      ElMessage.error("附件标题不能为空!");
+      throw "附件标题不能为空!";
+    }
+
+    await uploadNewFile({ dictId: caseFile.value.dictId, file: isAnimation.value?file5.value:file.value });
+    return 
+  },
+});
+</script>
+
+<style scoped lang="scss">
+.upload-demo {
+  overflow: hidden;
+}
+
+.file {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  > div {
+    display: flex;
+    align-items: center;
+  }
+
+  .name {
+    margin-left: 10px;
+  }
+}
+.jm-file-upload {
+  .mtk {
+    position: absolute;
+    right: 0;
+    top: 0;
+  }
+}
+</style>

+ 148 - 0
src/view/material/addScenes.vue

@@ -0,0 +1,148 @@
+<template>
+  <VRModelList :params="params">
+    <template v-slot:header>
+      <el-form-item label="名称:" style="grid-area: 1 / 1 / 2 / 4">
+        <el-input v-model="params.pagging.state.query.name" placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="分组:" style="grid-area: 1 / 1 / 2 / 4">
+        <el-select
+          v-model="params.pagging.state.query.dictId"
+          placeholder="请选择"
+          style="width: 200px"
+          clearable
+
+        >
+        <el-option
+          v-for="item in options"
+          :key="item.id"
+          :label="item.dictName"
+          :value="item.id"
+        />
+        </el-select>
+        <!-- <el-input  placeholder="请输入" /> -->
+      </el-form-item>
+    </template>
+    <template v-slot:content>
+      <el-table
+        :data="params.pagging.state.table.rows"
+        tooltip-effect="dark"
+        ref="tableRef"
+        style="width: 100%"
+        size="large"
+        @selection-change="changeSelection"
+      >
+        <el-table-column type="selection" width="50" :selectable="selectable"/>
+        <el-table-column width="400" label="名称" v-slot:default="{ row }">
+          <span class="truncate" :title="row.name">{{ row.name }}</span>
+        </el-table-column>
+        <el-table-column label="格式" prop="fileFormat"></el-table-column>
+        <el-table-column width="140" label="大小" prop="fileSize" v-slot:default="{ row }">
+          {{ formatSizeUnits(row.fileSize) }}
+        </el-table-column>
+        <el-table-column class="truncate" width="140" label="分组" prop="dictName" v-slot:default="{ row }">
+          <span :title="row.dictName" class="truncate">{{ row.dictName }}</span>
+        </el-table-column>
+        <el-table-column
+        label="操作"
+        v-slot:default="{ row }"
+        :width="240"
+      >
+        <span
+          class="oper-span"
+          v-if="row.useType != 'animation'"
+          @click="del(row)"
+          style="color: var(--primaryColor)"
+        >
+          删除
+        </span>
+      </el-table-column>
+      </el-table>
+    </template>
+  </VRModelList>
+</template>
+
+<script setup lang="ts">
+import VRModelList from "./list.vue";
+import { Scene } from "@/store/scene";
+import { CaseScenes, getDictFileLists, delDictFileLists } from "@/store/case";
+import { useScenePaggingParams } from "./pagging";
+import { onMounted, ref, watch, watchEffect } from "vue";
+import {
+  getCaseSceneList,
+  getCaseScenes,
+  getSceneKey,
+  replaceCaseScenes,
+} from "@/store/case";
+import { QuiskExpose } from "@/helper/mount";
+import { ElTable } from "element-plus";
+const options = ref([]);
+const props = defineProps<{ caseId: number, formats: Array<string> }>();
+const params = useScenePaggingParams();
+console.log("formats", params);
+const caseScenes = ref([]);
+const tableRef = ref<InstanceType<typeof ElTable>>();
+// const 
+watch(
+  () => params.pagging.state.query,
+  () => {
+    params.pagging.state.query.status = 2;
+    params.pagging.state.query.caseId = props.caseId;
+  },
+  { immediate: true, deep: true }
+);
+// 复选框同步
+watchEffect(() => {
+  if (!tableRef.value) return;
+  const selectKeys = caseScenes.value.map((item) => item.id);
+  console.log('caseScenes.value', caseScenes.value);
+  // caseScenes.value.find(
+  //   (item) => item.type === params.pagging.state.query.type
+  // )!.numList;
+  params.pagging.state.table.rows.forEach((scene) => {
+    tableRef.value!.toggleRowSelection(scene, selectKeys.includes(scene.id));
+  });
+});
+
+const selectable = (row) => {
+  console.log("row", row);
+  const fileType = row.fileUrl?.substring(row.fileUrl.lastIndexOf("."))
+      .toLowerCase();
+  return props.formats.includes(fileType);
+} 
+
+const del = async (row) => {
+  if (await confirm("确定要删除吗?")) {
+    const res = await delDictFileLists(row.id);
+    params.pagging.queryReset()
+  }
+}
+const changeSelection = (selectScenes) => {
+  if (params.pagging.loading) return;
+  const pagScenes = params.pagging.state.table.rows;
+  const type = params.pagging.state.query.type;
+  caseScenes.value = selectScenes;
+};
+function formatSizeUnits(bytes){
+  if      (bytes >= 1073741824) { bytes = (bytes / 1073741824).toFixed(2) + " GB"; }
+  else if (bytes >= 1048576)    { bytes = (bytes / 1048576).toFixed(2) + " MB"; }
+  else if (bytes >= 1024)       { bytes = (bytes / 1024).toFixed(2) + " KB"; }
+  else if (bytes > 1)           { bytes = bytes + " bytes"; }
+  else if (bytes == 1)          { bytes = bytes + " byte"; }
+  else                          { bytes = "0 bytes"; }
+  return bytes;
+}
+// 初始化数据
+onMounted(async () => {
+  const res = await getDictFileLists(1);
+  console.log('onMounted', res);
+  options.value = res;
+});
+const submit = () => {
+  console.log(caseScenes.value);
+  return caseScenes.value;
+  // () => replaceCaseScenes(props.caseId, caseScenes.value)
+};
+defineExpose<QuiskExpose>({
+  submit,
+});
+</script>

+ 67 - 0
src/view/material/list.vue

@@ -0,0 +1,67 @@
+<template>
+    <el-form label-width="84px" inline>
+      <slot name="header" />
+      <el-form-item class="searh-btns" style="grid-area: 1 / 4 / 2 / 4">
+        <el-button type="primary" @click="params.pagging.refresh">查询</el-button>
+        <el-button type="primary" plain @click="params.pagging.queryReset"
+          >重置</el-button
+        >
+        <!-- :disabled="!!file" -->
+        <!-- <el-upload
+        class="upload-demo"
+        :multiple="false"
+        :limit="1"
+        :before-upload="upload"
+        :show-file-list="false"
+        :http-request="() => {}"
+        :accept="accept"
+      > -->
+        <el-button style="margin-left: 12px" type="primary" @click="handleAdd">上传文件</el-button>
+      <!-- </el-upload> -->
+      </el-form-item>
+    </el-form>
+
+  <div class="body-layer">
+    <slot name="content" />
+    <com-pagination
+      @size-change="params.pagging.changPageSize"
+      @current-change="params.pagging.changPageCurrent"
+      :current-page="params.pagging.state.pag.currentPage"
+      :page-size="params.pagging.state.pag.size"
+      :total="params.pagging.state.pag.total"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import comHead from "@/components/head/index.vue";
+import comPagination from "@/components/pagination/index.vue";
+import { SceneType } from "@/store/scene";
+import { useFirePagging } from "./pagging";
+import { SceneTypeDesc } from "@/constant/scene";
+import { useScenePaggingParams } from "./pagging";
+import { useUpload } from "@/hook/upload";
+import { addLibraryFile } from "./quisk";
+
+// const { pagging } = useScenePaggingParams();
+const props = defineProps<{ params: ReturnType<typeof useScenePaggingParams> }>();
+
+const { size, fileList, upload, removeFile, previewFile, file, accept } =
+  useUpload({
+    maxSize: 2000 * 1024 * 1024,
+    formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3", ".shp"],
+  });
+const headOptions = [
+  { value: SceneType.SWKK, name: SceneTypeDesc[SceneType.SWKK] },
+  { value: SceneType.SWKJ, name: SceneTypeDesc[SceneType.SWKJ] },
+  { value: SceneType.SWSS, name: SceneTypeDesc[SceneType.SWSS] },
+  { value: SceneType.SWSSMX, name: SceneTypeDesc[SceneType.SWSSMX] },
+  { value: SceneType.SWYDSS, name: SceneTypeDesc[SceneType.SWYDSS] },
+  { value: SceneType.SWYDMX, name: SceneTypeDesc[SceneType.SWYDMX] },
+  { value: SceneType.SWMX, name: SceneTypeDesc[SceneType.SWMX] },
+];
+const handleAdd = async () => {
+  await addLibraryFile();
+  props.params.pagging.refresh();
+};
+</script>

+ 54 - 0
src/view/material/pagging.ts

@@ -0,0 +1,54 @@
+import { usePagging } from "@/hook/pagging";
+import { SceneType, getScenePagging } from "@/store/scene";
+import { getFilepageList } from "@/store/case";
+
+import { computed, reactive, watch, watchEffect } from "vue";
+
+export const useScenePaggingParams = () => {
+  const pagging = usePagging({
+    get: getFilepageList,
+    paramsTemlate: {
+      sceneName: "",
+      modelTitle: "",
+      name: "",
+      dictId: "",
+      snCode: "",
+      type: '1',
+    },
+  });
+
+  const isSwmx = computed(() => pagging.state.query.type === SceneType.SWMX);
+  const keyword = computed({
+    get: () => pagging.state.query.name,
+    set: (val: string) => {
+      pagging.state.query.name = val;
+    },
+  });
+
+  let oldSnCode = pagging.state.query.snCode;
+  watchEffect(() => {
+    if (isSwmx.value) {
+      oldSnCode = pagging.state.query.snCode;
+      pagging.state.query.snCode = "";
+    } else {
+      pagging.state.query.snCode = oldSnCode;
+    }
+  });
+
+  watch(
+    () => pagging.state.query.type,
+    () => {
+      pagging.state.pag.currentPage = 1;
+    }
+  );
+
+  const queryResetRaw = pagging.queryReset;
+  pagging.queryReset = () => {
+    const type = pagging.state.query.type;
+    queryResetRaw();
+    pagging.state.query.type = type;
+  };
+
+  return reactive({ pagging, keyword, isSwmx });
+};
+export type ScenePagging = ReturnType<typeof useScenePaggingParams>["pagging"];

+ 145 - 0
src/view/material/photos.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="scene abstract">
+    <!-- <el-button class="w-full my-4"  style="margin-left: 0; margin-right: 0">添加场景</el-button> -->
+    <div class="scene-list">
+      <div class="list">
+        <div class="tabs1 flex justify-between content-center items-center">
+          <el-tabs v-model="active1" class="demo-tabs" @tab-click="handleClick">
+            <el-tab-pane
+              v-for="(item, index) in list"
+              :key="index"
+              :label="item.filesTypeName"
+              :name="index"
+            >
+            </el-tab-pane>
+          </el-tabs>
+
+          <div class="float-right">
+            <el-button class="newbut" :icon="Plus" @click="handleAdd">照片制卷</el-button>
+          </div>
+        </div>
+        
+        <div class="tabs1 flex justify-between content-center items-center">
+        <el-tabs v-model="active2" class="demo-tabs" @tab-click="handleClick2">
+          <el-tab-pane
+            v-for="(childrenitem, indexs) in childrenList.list"
+            :key="indexs"
+            :label="childrenitem.filesTypeName"
+            :name="indexs"
+          >
+          </el-tab-pane>
+        </el-tabs>
+
+          <div class="float-right">
+            <el-button class="newbut" :icon="FullScreen" @click="handleSceneImg">视图截取</el-button>
+            <el-button class="newbut" :icon="Upload" @click="handleAdd">上传</el-button>
+          </div>
+        </div>
+
+        <viewImg
+          :list="childrenList.caseFilesList || []"
+          width="128px"
+          height="128px"
+          @handleItem="handleItem"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import viewImg from "@/components/viewImg/index.vue";
+import { computed, ref } from "vue";
+import { updateByTreeFileLists, getByTreeFileLists, Extract } from "@/store/case";
+import { Plus, Upload, FullScreen } from "@element-plus/icons-vue";
+import { router } from "@/router";
+import { addCaseFile, setTypeFile, addSceneImg } from "./quisk";
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+const active1 = ref(0);
+const active2 = ref(0);
+const active = ref(true);
+const settype = ref(false);
+const childrenList = ref({
+  list: [],
+  value: "",
+  caseFilesList: [],
+});
+const showModal = ref(false);
+const srcList = [
+  "https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg",
+  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
+];
+const list = ref([]);
+const activeNames = ref(["1"]);
+const handleChange = (val: string[]) => {
+  console.log(val);
+};
+getList();
+function handleActive(params) {
+  console.log("handleActive", params);
+}
+function getList() {
+  updateByTreeFileLists(caseId.value).then((res) => {
+    let newlist =
+      res.find((ele) => ele.filesTypeName == "三录材料")?.childrenList || [];
+    list.value =
+      newlist.find((ele) => ele.filesTypeName == "现场照片")?.childrenList ||
+      [];
+    childrenList.value.list = list.value[0]?.childrenList || [];
+    childrenList.value.caseFilesList =
+      childrenList.value.list[0]?.childrenList || [];
+  });
+}
+async function handleAdd() {
+  await addCaseFile({ caseId: caseId.value, filesTypeName: ["原始照片"] });
+  getList();
+}
+async function handleSceneImg() {
+  Extract.value = true;
+  await addSceneImg({ caseId: caseId.value, filesTypeName: ["原始照片"] });
+  getList();
+}
+const handleClick = (val) => {
+  console.log("handleClick", list.value[val.index], val);
+  childrenList.value.list = list.value[val.index]
+    ? list.value[val.index].childrenList
+    : [];
+  childrenList.value.value = active.value;
+  active2.value = 0;
+};
+const handleClick2 = (val) => {
+  childrenList.value.caseFilesList =
+    childrenList.value.list[val.index]?.caseFilesList || [];
+  console.log("handleClick2", active2.value, val);
+};
+async function handleItem(type, item) {
+  console.log("handleItem", type, item);
+  if (type == "edit") {
+    await setTypeFile({
+      ...item,
+      caseId: caseId.value,
+      fileOptions: list.value,
+    });
+  }
+  getList();
+}
+</script>
+<style scoped lang="scss">
+.scene {
+  height: 100%;
+  overflow-x: scroll;
+  .scene-list {
+    // height: 100%;
+  }
+  .title0 {
+    font-family: Microsoft YaHei, Microsoft YaHei;
+    font-weight: bold;
+    font-size: 16px;
+    color: rgba(0, 0, 0, 0.85);
+    line-height: 22px;
+  }
+  .demo-tabs {
+    padding: 12px 0 ;
+  }
+}
+</style>

+ 39 - 0
src/view/material/quisk.ts

@@ -0,0 +1,39 @@
+import AddCaseFile from "./addCaseFile.vue";
+import AddLibrary from "./addLibrary.vue";
+import AddScenes from "./addScenes.vue";
+import rollMakingList from "./rollMakingList.vue";
+import AddScenesImg from "./AddScenesImg.vue";
+import setType from "./setType.vue";
+import { quiskMountFactory } from "@/helper/mount";
+import { nextTick } from "vue";
+
+export const addCaseFile = quiskMountFactory(AddCaseFile, {
+  title: "上传附件",
+  width: 570,
+});
+
+export const addSceneImg = quiskMountFactory(AddScenesImg, {
+  title: "视图截取",
+  width: 955,
+});
+
+export const addCaseScenes = quiskMountFactory(AddScenes, {
+  title: "媒体库",
+  width: 1000,
+});
+
+export const addrollMaking = quiskMountFactory(rollMakingList, {
+  title: "平面图",
+  width: 1000,
+});
+
+
+export const addLibraryFile = quiskMountFactory(AddLibrary, {
+  title: "上传文件",
+  width: 550,
+});
+
+export const setTypeFile = quiskMountFactory(setType, {
+  title: "修改分类",
+  width: 550,
+});

+ 140 - 0
src/view/material/rollMaking.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="scene abstract">
+    <!-- <el-button class="w-full my-4"  style="margin-left: 0; margin-right: 0">添加场景</el-button> -->
+    <div class="scene-list">
+      <div class="list">
+        <div class="tabs1 flex justify-between content-center items-center">
+          <el-tabs v-model="active1" class="demo-tabs" @tab-click="handleClick">
+            <el-tab-pane
+              v-for="(item, index) in list"
+              :key="index"
+              :label="item.filesTypeName"
+              :name="index"
+            >
+            </el-tab-pane>
+          </el-tabs>
+
+          <div class="float-right">
+            <el-button class="newbut" :icon="Plus" @click="handleAdd">新增</el-button>
+          </div>
+        </div>
+        
+        <div class="tabs1 flex justify-between content-center items-center">
+        <el-tabs v-model="active2" class="demo-tabs" @tab-click="handleClick2">
+          <el-tab-pane
+            v-for="(childrenitem, indexs) in childrenList.list"
+            :key="indexs"
+            :label="childrenitem.filesTypeName"
+            :name="indexs"
+          >
+          </el-tab-pane>
+        </el-tabs>
+
+          <!-- <div class="float-right">
+            <el-button class="newbut" @click="handleAdd">视图截取</el-button>
+            <el-button class="newbut" @click="handleAdd">上传</el-button>
+          </div> -->
+        </div>
+
+        <viewImg
+          :list="childrenList.caseFilesList || []"
+          width="128px"
+          height="128px"
+          @handleItem="handleItem"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import viewImg from "@/components/viewImg/index.vue";
+import { computed, ref } from "vue";
+import { updateByTreeFileLists, getByTreeFileLists } from "@/store/case";
+import { router } from "@/router";
+import { addCaseFile, setTypeFile, addrollMaking } from "./quisk";
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+import { Plus, Upload } from "@element-plus/icons-vue";
+const active1 = ref(0);
+const active2 = ref(0);
+const active = ref(true);
+const settype = ref(false);
+const childrenList = ref({
+  list: [],
+  value: "",
+  caseFilesList: [],
+});
+const showModal = ref(false);
+const srcList = [
+  "https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg",
+  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
+];
+const list = ref([]);
+const activeNames = ref(["1"]);
+const handleChange = (val: string[]) => {
+  console.log(val);
+};
+getList();
+function handleActive(params) {
+  console.log("handleActive", params);
+}
+function getList() {
+  updateByTreeFileLists(caseId.value).then((res) => {
+    let newlist =
+      res.find((ele) => ele.filesTypeName == "三录材料")?.childrenList || [];
+    list.value =
+      newlist.find((ele) => ele.filesTypeName == "现场图")?.childrenList ||
+      [];
+    childrenList.value.list = list.value[0]?.childrenList || [];
+    childrenList.value.caseFilesList =
+      childrenList.value.list[0]?.childrenList || [];
+  });
+}
+async function handleAdd() {
+  let resImg = await addrollMaking({ caseId: caseId.value, filesTypeName: ["原始照片"] });
+  getList();
+}
+const handleClick = (val) => {
+  console.log("handleClick", list.value[val.index], val);
+  childrenList.value.list = list.value[val.index]
+    ? list.value[val.index].childrenList
+    : [];
+  childrenList.value.value = active.value;
+  active2.value = 0;
+};
+const handleClick2 = (val) => {
+  childrenList.value.caseFilesList =
+    childrenList.value.list[val.index]?.caseFilesList || [];
+  console.log("handleClick2", active2.value, val);
+};
+async function handleItem(type, item) {
+  console.log("handleItem", type, item);
+  if (type == "edit") {
+    await setTypeFile({
+      ...item,
+      caseId: caseId.value,
+      fileOptions: list.value,
+    });
+  }
+  getList();
+}
+</script>
+<style scoped lang="scss">
+.scene {
+  height: 100%;
+  overflow-x: scroll;
+  .scene-list {
+    // height: 100%;
+  }
+  .title0 {
+    font-family: Microsoft YaHei, Microsoft YaHei;
+    font-weight: bold;
+    font-size: 16px;
+    color: rgba(0, 0, 0, 0.85);
+    line-height: 22px;
+  }
+  .demo-tabs {
+    padding: 12px 0 ;
+  }
+}
+</style>

+ 148 - 0
src/view/material/rollMakingList.vue

@@ -0,0 +1,148 @@
+<template>
+  <VRModelList :params="params">
+    <template v-slot:header>
+      <el-form-item label="名称:" style="grid-area: 1 / 1 / 2 / 4">
+        <el-input v-model="params.pagging.state.query.name" placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="分组:" style="grid-area: 1 / 1 / 2 / 4">
+        <el-select
+          v-model="params.pagging.state.query.dictId"
+          placeholder="请选择"
+          style="width: 200px"
+          clearable
+
+        >
+        <el-option
+          v-for="item in options"
+          :key="item.id"
+          :label="item.dictName"
+          :value="item.id"
+        />
+        </el-select>
+        <!-- <el-input  placeholder="请输入" /> -->
+      </el-form-item>
+    </template>
+    <template v-slot:content>
+      <el-table
+        :data="params.pagging.state.table.rows"
+        tooltip-effect="dark"
+        ref="tableRef"
+        style="width: 100%"
+        size="large"
+        @selection-change="changeSelection"
+      >
+        <el-table-column type="selection" width="50" :selectable="selectable"/>
+        <el-table-column width="400" label="名称" v-slot:default="{ row }">
+          <span class="truncate" :title="row.name">{{ row.name }}</span>
+        </el-table-column>
+        <el-table-column label="格式" prop="fileFormat"></el-table-column>
+        <el-table-column width="140" label="大小" prop="fileSize" v-slot:default="{ row }">
+          {{ formatSizeUnits(row.fileSize) }}
+        </el-table-column>
+        <el-table-column class="truncate" width="140" label="分组" prop="dictName" v-slot:default="{ row }">
+          <span :title="row.dictName" class="truncate">{{ row.dictName }}</span>
+        </el-table-column>
+        <el-table-column
+        label="操作"
+        v-slot:default="{ row }"
+        :width="240"
+      >
+        <span
+          class="oper-span"
+          v-if="row.useType != 'animation'"
+          @click="del(row)"
+          style="color: var(--primaryColor)"
+        >
+          删除
+        </span>
+      </el-table-column>
+      </el-table>
+    </template>
+  </VRModelList>
+</template>
+
+<script setup lang="ts">
+import VRModelList from "./list.vue";
+import { Scene } from "@/store/scene";
+import { CaseScenes, getDictFileLists, delDictFileLists } from "@/store/case";
+import { useScenePaggingParams } from "./pagging";
+import { onMounted, ref, watch, watchEffect } from "vue";
+import {
+  getCaseSceneList,
+  getCaseScenes,
+  getSceneKey,
+  replaceCaseScenes,
+} from "@/store/case";
+import { QuiskExpose } from "@/helper/mount";
+import { ElTable } from "element-plus";
+const options = ref([]);
+const props = defineProps<{ caseId: number, formats: Array<string> }>();
+const params = useScenePaggingParams();
+console.log("formats", params);
+const caseScenes = ref([]);
+const tableRef = ref<InstanceType<typeof ElTable>>();
+// const 
+watch(
+  () => params.pagging.state.query,
+  () => {
+    params.pagging.state.query.status = 2;
+    params.pagging.state.query.caseId = props.caseId;
+  },
+  { immediate: true, deep: true }
+);
+// 复选框同步
+watchEffect(() => {
+  if (!tableRef.value) return;
+  const selectKeys = caseScenes.value.map((item) => item.id);
+  console.log('caseScenes.value', caseScenes.value);
+  // caseScenes.value.find(
+  //   (item) => item.type === params.pagging.state.query.type
+  // )!.numList;
+  params.pagging.state.table.rows.forEach((scene) => {
+    tableRef.value!.toggleRowSelection(scene, selectKeys.includes(scene.id));
+  });
+});
+
+const selectable = (row) => {
+  console.log("row", row);
+  const fileType = row.fileUrl?.substring(row.fileUrl.lastIndexOf("."))
+      .toLowerCase();
+  return props.formats.includes(fileType);
+} 
+
+const del = async (row) => {
+  if (await confirm("确定要删除吗?")) {
+    const res = await delDictFileLists(row.id);
+    params.pagging.queryReset()
+  }
+}
+const changeSelection = (selectScenes) => {
+  if (params.pagging.loading) return;
+  const pagScenes = params.pagging.state.table.rows;
+  const type = params.pagging.state.query.type;
+  caseScenes.value = selectScenes;
+};
+function formatSizeUnits(bytes){
+  if      (bytes >= 1073741824) { bytes = (bytes / 1073741824).toFixed(2) + " GB"; }
+  else if (bytes >= 1048576)    { bytes = (bytes / 1048576).toFixed(2) + " MB"; }
+  else if (bytes >= 1024)       { bytes = (bytes / 1024).toFixed(2) + " KB"; }
+  else if (bytes > 1)           { bytes = bytes + " bytes"; }
+  else if (bytes == 1)          { bytes = bytes + " byte"; }
+  else                          { bytes = "0 bytes"; }
+  return bytes;
+}
+// 初始化数据
+onMounted(async () => {
+  const res = await getDictFileLists(1);
+  console.log('onMounted', res);
+  options.value = res;
+});
+const submit = () => {
+  console.log(caseScenes.value);
+  return caseScenes.value;
+  // () => replaceCaseScenes(props.caseId, caseScenes.value)
+};
+defineExpose<QuiskExpose>({
+  submit,
+});
+</script>

+ 407 - 0
src/view/material/sceneImg.vue

@@ -0,0 +1,407 @@
+<template>
+  <div class="abstract">
+    <div class="blfrom">
+      <el-button @click="handleRecords(-1)" :icon="Plus" class="newbut">新增</el-button>
+
+      <el-upload
+        class="upload-demo"
+        :multiple="false"
+        :show-file-list="false"
+        :limit="1"
+        :before-upload="upload"
+        :file-list="fileLists"
+        :http-request="uploadNewFile"
+        :on-success="handleSuccess"
+        :on-preview="previewFile"
+        :accept="accept"
+        :before-remove="removeFile"
+      >
+        <el-button :icon="Upload" :disabled="!!file" class="newbut">上传</el-button>
+      </el-upload>
+    </div>
+    <div class="blList">
+      <div
+        class="bllistItem flex justify-between items-center mt-4"
+        v-for="(item, index) in list.klbj"
+        :key="index"
+      >
+        <div class="truncate" :title="item.filesTitle">
+          {{ item.filesTitle }}
+        </div>
+        <el-dropdown
+          style="width: 20px"
+          @command="
+            (command) => {
+              handleCommand(command, item);
+            }
+          "
+        >
+          <el-icon>
+            <MoreFilled />
+          </el-icon>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item
+                v-if="item.createType == 'online'"
+                command="edit"
+                >编辑</el-dropdown-item
+              >
+              <el-dropdown-item command="delete">删除</el-dropdown-item>
+              <el-dropdown-item command="down">下载</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+    </div>
+    <el-dialog
+      v-model="dialogVisible"
+      title="请选择场景"
+      width="500"
+      :before-close="handleClose"
+    >
+      <div>
+        <el-radio-group v-model="radio">
+          <el-radio
+            style="width: 100%"
+            v-for="(item, index) in radioList"
+            :key="index"
+            :value="item.num"
+            >{{ item.title }}</el-radio
+          >
+        </el-radio-group>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleRadioItem"> 确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  uploadNewFile,
+  addByMediaLiBrary,
+  getSceneListHasAi,
+} from "@/store/case";
+import { computed, ref, reactive } from "vue";
+import { addCaseFile } from "../originalPhoto/quisk";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { useUpload } from "@/hook/upload";
+import { selectMapImage } from "@/view/case/quisk";
+import { RouteName, router } from "@/router";
+import { BoardType } from "@/store/caseFile";
+import {
+  updateByTreeFileLists,
+  caseOverview,
+  overviewAdd,
+  getcaseByCaseId,
+} from "@/store/case";
+import { Delete, Edit, Plus, Upload } from "@element-plus/icons-vue";
+import { setTypeFile } from "../originalPhoto/quisk";
+import viewImg from "@/components/viewImg/index.vue";
+import { delCaseFile } from "@/store/caseFile";
+import { uploadOldFile } from "@/store/system";
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+const active = ref(true);
+const radio = ref(null);
+const radioList = ref([]);
+const dialogVisible = ref(false);
+const list = ref({
+  xct: [],
+  xczp: [],
+  klbj: [],
+  pmt: [],
+  fwt: [],
+});
+getList();
+const srcList = [
+  "https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg",
+  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
+];
+const settype = ref(false);
+const ruleFormRef = ref(null);
+const klblId = ref(0);
+const fmtId = ref(0);
+const pmtId = ref(0);
+const activeName = ref("1");
+const fileLists = ref([]);
+const showModal = ref(false);
+const { size, fileList, upload, removeFile, previewFile, file, accept } =
+  useUpload({
+    maxSize: 10 * 1024 * 1024,
+    formats: [".doc", ".docx", ".pdf"],
+  });
+const handleClick = (tab) => {
+  console.log(tab);
+};
+const getcaseByCaseIdList = async () => {
+  // let pmt = await caseOverview(caseId.value)
+  // let fwt = await getcaseByCaseId(caseId.value)
+  //   list.value.pmt = pmt.map(ele =>{
+  //     let mapUrl = ele.mapUrl || ele.listCover
+  //     return {
+  //       ...ele,
+  //       mapUrl: mapUrl,
+  //     }
+  //   })
+  //   list.value.fwt = fwt.map(ele =>{
+  //     let mapUrl = ele.mapUrl || ele.listCover
+  //     return {
+  //       ...ele,
+  //       mapUrl: mapUrl,
+  //     }
+  //   })
+  //   console.log('getcaseByCaseId', res)
+  // })
+};
+const gotoDraw = async (type: BoardType, id: number) => {
+  if (BoardType.scene == type) {
+    window.open(`/draw/#/overview?caseId=${caseId.value}`);
+  } else {
+    const { blob, width, height } = await selectMapImage({});
+    console.log("blob", blob);
+    if (!blob) {
+      return;
+    }
+    const file = new File([blob], `方位图_.jpg`);
+    const mapUrl = await uploadOldFile(file);
+    const res = await overviewAdd({
+      caseId: caseId.value,
+      title: "方位图",
+      listCover: mapUrl,
+      high: height,
+      width,
+      mapUrl,
+    });
+    getcaseByCaseIdList();
+    getList();
+    window.open(
+      `/draw/#/tabulation?caseId=${caseId.value}&tabulationId=${res.id}`
+    );
+  }
+  // router.push({
+  //   name: RouteName.drawCaseFile,
+  //   params: { caseId: caseId.value!, type, id },
+  // });
+};
+function getList() {
+  updateByTreeFileLists(caseId.value).then((res) => {
+    let newlist =
+      res.find((ele) => ele.filesTypeName == "三录材料")?.childrenList || [];
+    list.value.xct =
+      newlist.find((ele) => ele.filesTypeName == "现场图")?.childrenList || [];
+    list.value.xczp =
+      newlist.find((ele) => ele.filesTypeName == "现场照片")?.childrenList ||
+      [];
+    list.value.klbj =
+      newlist.find((ele) => ele.filesTypeName == "勘验笔录")?.caseFilesList ||
+      [];
+    klblId.value = newlist.find(
+      (ele) => ele.filesTypeName == "勘验笔录"
+    ).filesTypeId;
+    fmtId.value = list.value.xct.find(
+      (ele) => ele.filesTypeName == "平面图"
+    ).filesTypeId;
+    pmtId.value = list.value.xct.find(
+      (ele) => ele.filesTypeName == "方位图"
+    ).filesTypeId;
+    console.log("list.value", list.value);
+    getcaseByCaseIdList();
+  });
+}
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      console.log("submit!");
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+const resetForm = (formEl) => {
+  if (!formEl) return;
+  formEl.resetFields();
+};
+function handleActive(params) {
+  console.log("handleActive", params);
+}
+function handleoverviewItem(type, item) {
+  console.log("handleoverviewItem", type, item);
+  if ("edit" == type) {
+    let url =
+      item.filesTitle == "平面图"
+        ? `/draw/#/overview?caseId=${caseId.value}&overviewId=${item.overviewId}`
+        : `/draw/#/tabulation?caseId=${caseId.value}&tabulationId=${item.tabulationId}`;
+    window.open(url);
+    // gotoDraw(item.filesTypeId != pmtId.value? BoardType.scene : BoardType.map, item.filesId)
+  } else {
+    getList();
+  }
+}
+// function handleoverviewItem(type, item) {
+//   console.log("handleoverviewItem", type, item);
+//   if('edit' == type) {
+//     window.open(`/draw/#/overview?caseId=${caseId.value}&overviewId=${item.id}`);
+//     // gotoDraw(item.filesTypeId != pmtId.value? BoardType.scene : BoardType.map, item.filesId)
+//   }else{
+//     getList()
+//   }
+// }
+function handleItem(type, item) {
+  console.log("handleItem", type, item);
+  if ("edit" == type) {
+    gotoDraw(
+      item.filesTypeId != pmtId.value ? BoardType.scene : BoardType.map,
+      item.filesId
+    );
+  }
+  getList();
+}
+async function handleItems(type, item) {
+  console.log("handleItem", type, item);
+  if ("edit" == type) {
+    await setTypeFile({
+      ...item,
+      caseId: caseId.value,
+      fileOptions: list.value.xczp,
+    });
+  }
+  getList();
+}
+function handleSuccess(item) {
+  let uploadId = item?.data.id;
+  addByMediaLiBrary({
+    caseId: caseId.value,
+    filesTypeId: klblId.value,
+    uploadId,
+  }).then((res) => {
+    fileLists.value = [];
+    getList();
+  });
+}
+function handleRecords(fileId = -1) {
+  router.push({
+    name: RouteName.records,
+    params: { caseId: caseId.value, fileId },
+  });
+}
+async function handledrawCasePhotos() {
+  router.push({
+    name: RouteName.drawCasePhotos,
+    params: { caseId: caseId.value! },
+  });
+  console.log("handleAdd");
+}
+async function handleAdd(filesTypeName) {
+  await addCaseFile({ caseId: caseId.value, filesTypeName });
+  getList();
+  console.log("handleAdd");
+}
+const handleCommand = (command: string | number | object, item) => {
+  switch (command) {
+    case "edit":
+      handleRecords(item.filesId);
+      break;
+    case "delete":
+      handleDel(item);
+      break;
+    default:
+      // window.open(item.filesUrl)
+      console.log("other", item);
+      downloadFile(item.filesUrl, item.filesTitle);
+  }
+  // ElMessage(`click on item ${command}`)
+};
+function getExtension(name) {
+  return name.substring(name.lastIndexOf("."));
+}
+// 下载文件
+function downloadFile(sourceUrl, fileName) {
+  const link = document.createElement("a");
+  link.style.display = "none";
+  // 设置下载地址
+  link.setAttribute("href", sourceUrl);
+  // 设置文件名
+  link.setAttribute("download", fileName + getExtension(sourceUrl));
+  document.body.appendChild(link);
+  link.click();
+  document.body.removeChild(link);
+}
+
+function handleClose(e) {
+  console.log("handleClose", e);
+  dialogVisible.value = false;
+}
+function handleRadioItem() {
+  console.log("handleRadioItem", radio.value);
+  if (radio.value == null) {
+    ElMessage.error("请选择对应的场景!");
+    return;
+  }
+  dialogVisible.value = false;
+  window.open(`/floorplan/index.html?m=${radio.value}`);
+}
+function handleAiItem() {
+  getSceneListHasAi(caseId.value).then((res) => {
+    console.log("getSceneListHasAi", res);
+    let newradioList = [];
+    res.data.map((ele) => {
+      newradioList.push(ele);
+    });
+    radioList.value = newradioList;
+    if (newradioList.length) {
+      dialogVisible.value = true;
+    } else {
+      ElMessage.error("暂无支持AI 平面图的场景");
+    }
+  });
+}
+function handleDel(item) {
+  ElMessageBox.confirm("确定删除?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await delCaseFile({ caseId: caseId.value, filesId: item.filesId });
+    ElMessage({
+      type: "success",
+      message: "删除成功",
+    });
+    getList();
+  });
+}
+</script>
+<style lang="scss" scoped>
+.abstract {
+  height: 100%;
+  .blfrom {
+    display: flex;
+    justify-content: flex-end;
+    .newbut{
+      margin-left: 20px;
+    }
+  }
+  .el-tab-pane {
+    height: 100%;
+    overflow-y: scroll;
+    position: relative;
+  }
+  .el-form-item--label-top {
+    margin-bottom: 14px;
+  }
+  .form-content {
+    height: calc(100vh - 225px);
+    overflow-y: scroll;
+  }
+  .demo-tabs {
+    .el-tabs__item {
+      height: 32px;
+      line-height: 32px;
+    }
+  }
+}
+</style>

+ 139 - 0
src/view/material/setType.vue

@@ -0,0 +1,139 @@
+<template>
+  <el-form
+    ref="form"
+    :model="caseFile"
+    label-width="90px"
+    class="camera-from dispatch-file-from jm-file-upload"
+  >
+    <el-form-item label="分类:" class="mandatory">
+      <el-cascader
+      style="width: 300px;"
+        v-model="caseFile.filesTypeId"
+        :options="fileOptions"
+        :props="{
+          checkStrictly: false,
+        }"
+        clearable
+      />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import {
+  DrawFormatDesc,
+  DrawFormats,
+  FileDrawType,
+  OtherFormatDesc,
+  OtherFormats,
+} from "@/constant/caseFile";
+import { maxFileSize } from "@/constant/caseFile";
+import { useUpload } from "@/hook/upload";
+import { CaseFile, addCaseFile } from "@/store/caseFile";
+import { ElMessage, UploadFile } from "element-plus";
+import { computed, ref, watchEffect, onMounted } from "vue";
+import { addCaseScenes } from "./quisk";
+import { QuiskExpose } from "@/helper/mount";
+import { updateSelectByTreeFileLists, getupdateFileType } from "@/store/case";
+
+const props = defineProps<{
+  caseId: number;
+  filesTypeId: number;
+  filesId: number;
+  fileOptions: Array<any>;
+}>();
+const fileOptions = ref([])
+const caseFile = ref({
+  caseId: props.caseId,
+  filesTypeId: props.filesTypeId,
+  filesTitle: "",
+  filesId: props.filesId,
+});
+onMounted(async () => {
+  if(props.fileOptions?.length){
+    fileOptions.value = getTreeList(props.fileOptions);
+  }else{
+    fileOptions.value = await updateSelectByTreeFileLists();
+  }
+  console.log('caseFile', caseFile)
+  console.log(fileOptions.value);
+});
+console.log(caseFile.value, '===caseFile');
+const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: 10 * 1024 * 1024,
+  formats: [".jpg", ".jpeg", ".png", '.mp4'],
+});
+const getTreeList = (list) =>  {
+    return list.map(item => {
+      if (item.childrenList) {
+        return {
+          value: item.filesTypeId,
+          label: item.filesTypeName,
+          children: getTreeList(item.childrenList)
+        };
+      }
+      return {
+        value: item.filesTypeId,
+        label: item.filesTypeName,
+      }
+    })
+  }
+const formatDesc = computed(() =>
+  'jpg、png、jpeg、mp4上传'
+);
+
+const handleAdd = async () => {
+  await addCaseScenes();
+};
+watchEffect(() => {
+  if (file.value?.name) {
+    caseFile.value.filesTitle = file.value?.name.substring(0, 50);
+  }
+});
+
+defineExpose<QuiskExpose>({
+  async submit() {
+    console.log(caseFile.value, "文件列表");
+    if (!caseFile.value.filesTypeId) {
+      ElMessage.error("分类不能为空");
+      throw "分类不能为空";
+    } 
+    let filesTypeId
+    if(caseFile.value.filesTypeId instanceof Array){
+      filesTypeId = caseFile.value.filesTypeId?.slice(-1)[0]
+    }else {
+      filesTypeId = caseFile.value.filesTypeId
+    }
+    // let filesTypeId = caseFile.value.filesTypeId?.slice(-1)
+    await getupdateFileType({...caseFile.value , filesTypeId});
+    return caseFile.value;
+  },
+});
+</script>
+
+<style scoped lang="scss">
+.upload-demo {
+  overflow: hidden;
+}
+
+.file {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  > div {
+    display: flex;
+    align-items: center;
+  }
+
+  .name {
+    margin-left: 10px;
+  }
+}
+.jm-file-upload {
+  .mtk {
+    position: absolute;
+    right: 0;
+    top: 0;
+  }
+}
+</style>

+ 79 - 42
src/view/originalPhoto/index.vue

@@ -1,90 +1,127 @@
 <template>
-  <div class="scene">
-    <el-button class="w-full" @click="handleAdd">上传</el-button>
+  <div class="scene abstract">
     <!-- <el-button class="w-full my-4"  style="margin-left: 0; margin-right: 0">添加场景</el-button> -->
     <div class="scene-list">
-      <div class="scene-title flex justify-between content-center pt-4" style="line-height: 14px">
-        <span class="title0 mb-0">全部照片</span>
-        <!-- <span  @click="settype = !settype">{{settype?'完成':'修改分类'}}</span> -->
-      </div>
       <div class="list">
-        <div
-          class="listItem py-2"
-          v-for="item,index in list"
-          :key="index"
-        >
-          <div class="title1 pb-2">{{item.filesTypeName}}</div>
-          <viewImg :list="item.caseFilesList || []" width="calc(50% - 18px)" height="100px" @handleItem="handleItem" />
-          <div class="list2" v-for="item2,index2 in item.childrenList" :key="index2" v-show="item2.caseFilesList?.length">
-          <div class="title2">{{item2.filesTypeName}}</div>
-          <viewImg :list="item2.caseFilesList || []" width="calc(50% - 18px)" height="100px" @handleItem="handleItem" />
-          <!-- <div class="item2 inline-block relative" v-for="(item2, index) in srcList" :key="index">
-            <el-icon :size="20" class="absolute z-1 cursor-pointer" style="right: -0px; top: -8px">
-              <CircleCloseFilled v-if="!settype" />
-              <EditPen v-else />
-            </el-icon>
-            <el-image class="z-0 relative" preview-teleported :preview-src-list="srcList" :initial-index="index" style="width: 80px; height: 80px;margin-right: 10px" :src="item2" :fit="fit" />
-            </div> -->
+        <el-tabs v-model="active1" class="demo-tabs" @tab-click="handleClick">
+          <el-tab-pane
+            v-for="(item, index) in list"
+            :key="index"
+            :label="item.filesTypeName"
+            :name="index"
+          >
+          </el-tab-pane>
+        </el-tabs>
+        <div class="tabs1 flex justify-between content-center items-center">
+        <el-tabs v-model="active2" class="demo-tabs" @tab-click="handleClick2">
+          <el-tab-pane
+            v-for="(childrenitem, indexs) in childrenList.list"
+            :key="indexs"
+            :label="childrenitem.filesTypeName"
+            :name="indexs"
+          >
+          </el-tab-pane>
+        </el-tabs>
+
+          <div class="float-right">
+            <el-button class="newbut" :icon="Upload" @click="handleAdd">上传</el-button>
           </div>
         </div>
+
+        <viewImg
+          :list="childrenList.caseFilesList || []"
+          width="128px"
+          height="128px"
+          @handleItem="handleItem"
+        />
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import viewImg from "@/components/viewImg/index.vue"
+import viewImg from "@/components/viewImg/index.vue";
 import { computed, ref } from "vue";
 import { updateByTreeFileLists, getByTreeFileLists } from "@/store/case";
+import { Upload } from "@element-plus/icons-vue";
 import { router } from "@/router";
 import { addCaseFile, setTypeFile } from "./quisk";
-const caseId = computed(() => (router.currentRoute.value?.params?.caseId));
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+const active1 = ref(0);
+const active2 = ref(0);
 const active = ref(true);
 const settype = ref(false);
+const childrenList = ref({
+  list: [],
+  value: "",
+  caseFilesList: [],
+});
 const showModal = ref(false);
-const srcList = ["https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg","https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg"]
+const srcList = [
+  "https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg",
+  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
+];
 const list = ref([]);
-const activeNames = ref(['1'])
+const activeNames = ref(["1"]);
 const handleChange = (val: string[]) => {
-  console.log(val)
-}
-getList()
+  console.log(val);
+};
+getList();
 function handleActive(params) {
   console.log("handleActive", params);
 }
 function getList() {
-  updateByTreeFileLists(caseId.value).then(res => {
-    list.value = res.find(ele => ele.filesTypeName == '原始照片')?.childrenList || [];
-  })
+  updateByTreeFileLists(caseId.value).then((res) => {
+    list.value =
+      res.find((ele) => ele.filesTypeName == "原始照片")?.childrenList || [];
+      childrenList.value.list = list.value[0]?.childrenList || [];
+      childrenList.value.caseFilesList = childrenList.value.list[0]?.caseFilesList || [];
+  });
 }
 async function handleAdd() {
-  await addCaseFile({ caseId: caseId.value, filesTypeName: ['原始照片'] });
+  await addCaseFile({ caseId: caseId.value, filesTypeName: ["原始照片"] });
   getList();
 }
+const handleClick = (val) => {
+  console.log("handleClick", list.value[val.index], val);
+  childrenList.value.list = list.value[val.index]
+    ? list.value[val.index].childrenList
+    : [];
+  childrenList.value.value = active.value;
+  active2.value = 0;
+};
+const handleClick2 = (val) => {
+  childrenList.value.caseFilesList = childrenList.value.list[val.index]?.caseFilesList || [];
+  console.log("handleClick2", active2.value, val);
+};
 async function handleItem(type, item) {
   console.log("handleItem", type, item);
-  if(type == 'edit'){
+  if (type == "edit") {
     await setTypeFile({
-      ...item,caseId:caseId.value,
-      fileOptions: list.value
-    })
+      ...item,
+      caseId: caseId.value,
+      fileOptions: list.value,
+    });
   }
   getList();
 }
 </script>
 <style scoped lang="scss">
-.scene{
+.scene {
   height: 100%;
   overflow-x: scroll;
-  .scene-list{
+  .scene-list {
     // height: 100%;
   }
-  .title0{
+  .title0 {
     font-family: Microsoft YaHei, Microsoft YaHei;
     font-weight: bold;
     font-size: 16px;
-    color: rgba(0,0,0,0.85);
+    color: rgba(0, 0, 0, 0.85);
     line-height: 22px;
   }
+  .demo-tabs{
+    margin: 12px 0;
+  }
 }
 </style>

+ 14 - 12
src/view/vrmodel/index.vue

@@ -1,21 +1,22 @@
 <template>
   <div class="scene">
-    <span class="title1" style="line-height: 32px">多元融合</span>
-    <el-button class="w-full my-4" @click="handleAdddyrh">编辑</el-button>
+    <!-- <span class="title1" style="line-height: 32px">多元融合</span> -->
+    <!-- <el-button class="w-full my-4" @click="handleAdddyrh">编辑</el-button> -->
     <div class="scene-list">
-      <div class="scene-title flex justify-between content-center">
-        <span class="title1" style="line-height: 32px">场景列表</span>
+      <div class="scene-title flex justify-end content-center">
+        <!-- <span class="title1" style="line-height: 32px">场景列表</span> -->
         <!-- <el-switch v-model="active" @change="handleActive" /> -->
+        <el-button class="newbut" :icon="Plus" @click="handleAdd">新增</el-button>
       </div>
-      <el-button
+      <!-- <el-button
         class="w-full my-4"
         @click="handleAdd"
         style="margin-left: 0; margin-right: 0"
         >新增</el-button
-      >
-      <div class="list" v-if="active" style="min-height: 630px">
+      > -->
+      <div class="list" v-if="active" style="min-height: 630px;margin-top: 8px">
         <div
-          class="listItem flex justify-between py-2"
+          class="listItem flex justify-between py-4"
           v-for="(item, index) in list"
           :key="index"
         >
@@ -56,6 +57,7 @@ import { router } from "@/router";
 import comSelect from "@/components/company-select/index.vue";
 import List from "./list.vue";
 import SceneContent from "./sceneContent.vue";
+import { Plus } from '@element-plus/icons-vue'
 import { ElMessage } from "element-plus";
 import ModelContent from "./modelContent.vue";
 import { useScenePaggingParams } from "./pagging";
@@ -71,8 +73,8 @@ onMounted(() => {
   geiList();
   console.log("router.currentRoute.value", caseId);
 });
-async function geiList() {
-  list.value = await getCaseSceneList(caseId.value);
+async function geiList(refresh) {
+  list.value = await getCaseSceneList(caseId.value, refresh);
   console.log("res", list.value);
 }
 function handlegotoEdit(record) {
@@ -109,14 +111,14 @@ async function submitForm() {
     sceneNumParam: sublist,
   };
   setCaseaddOrUpdate(apiData).then((res) => {
-    geiList();
+    geiList(true);
     ElMessage({
       message: "操作成功",
       type: "success",
     });
   }).catch((errr) => {
     console.log("setCaseaddOrUpdateerrr", errr);
-    return geiList();
+    return geiList(true);
   })
 };
   // window.location.reload()

+ 29 - 7
vite.config.ts

@@ -48,11 +48,13 @@ export default defineConfig({
       "/api": {
         target: dev ? "http://survey.4dkankan.com" : "mix3d.4dkankan.com",
         changeOrigin: true,
+        secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/api`), ""),
       },
       "/fusion": {
         target: dev ? "http://survey.4dkankan.com" : "mix3d.4dkankan.com",
         changeOrigin: true,
+        secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/api`), "/fusion"),
       },
       "/dev-code": {
@@ -64,11 +66,11 @@ export default defineConfig({
         secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/dev-code`), ""),
       },
-      "/swkk": {
-        target: dev ? "https://test.4dkankan.com" : "https://www.4dkankan.com",
-        changeOrigin: true,
-        rewrite: (path) => path.replace(new RegExp(`^/swkk`), ""),
-      },
+      // "/swkk": {
+      //   target: dev ? "https://test.4dkankan.com" : "https://www.4dkankan.com",
+      //   changeOrigin: true,
+      //   rewrite: (path) => path.replace(new RegExp(`^/swkk`), ""),
+      // },
       "/service": {
         target: dev ? "http://survey.4dkankan.com" : "https://www.4dkankan.com",
         changeOrigin: true,
@@ -80,12 +82,32 @@ export default defineConfig({
         changeOrigin: true,
         rewrite: (path) => path.replace(new RegExp(`^/swss`), ""),
       },
+      "/swkk": {
+        target: dev
+          ? "https://survey.4dkankan.com/swkk"
+          : "https://laser.4dkankan.com",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(new RegExp(`^/swkk`), ""),
+      },
+      "/oss": {
+        target: dev
+          ? "https://survey.4dkankan.com/oss"
+          : "https://laser.4dkankan.com",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(new RegExp(`^/oss`), ""),
+      },
       "/laser": {
         target: dev
-          ? "https://uat-laser.4dkankan.com"
+          ? "https://survey.4dkankan.com/laser"
+          : "https://laser.4dkankan.com",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(new RegExp(`^/laser`), ""),
+      },
+      "/code": {
+        target: dev
+          ? "https://survey.4dkankan.com"
           : "https://laser.4dkankan.com",
         changeOrigin: true,
-        rewrite: (path) => path.replace(new RegExp(`^/laser`), "/laser"),
       },
       "/fdkk": {
         target: dev