Forráskód Böngészése

江门公安v2.0部署版本提交

tangning 2 hete
szülő
commit
d1dd94157a
95 módosított fájl, 6767 hozzáadás és 2100 törlés
  1. 271 0
      public/browser.html
  2. BIN
      public/images/browser_360.png
  3. BIN
      public/images/browser_Chrome.png
  4. BIN
      public/images/browser_Chromium.png
  5. BIN
      public/images/browser_Edge.png
  6. BIN
      public/images/browser_Firefox.png
  7. BIN
      public/images/browser_Qianxin.png
  8. BIN
      public/images/browser_Safari.png
  9. BIN
      public/images/down.png
  10. BIN
      public/images/pic_update.png
  11. 19 2
      src/App.vue
  12. 1 0
      src/app/criminal/routeConfig.ts
  13. 65 53
      src/app/criminal/view/login/index.vue
  14. 0 1
      src/app/xmfire/view/login/index.vue
  15. 440 3
      src/assets/font/demo_index.html
  16. 79 3
      src/assets/font/iconfont.css
  17. 1 1
      src/assets/font/iconfont.js
  18. 133 0
      src/assets/font/iconfont.json
  19. BIN
      src/assets/font/iconfont.ttf
  20. BIN
      src/assets/font/iconfont.woff
  21. BIN
      src/assets/font/iconfont.woff2
  22. BIN
      src/assets/image/criminalBanner.png
  23. BIN
      src/assets/image/logo.png
  24. 229 152
      src/assets/style/public.scss
  25. 2 1
      src/components/dialog/index.vue
  26. 2 1
      src/components/dialog/style.scss
  27. 2 0
      src/components/dialog/type.ts
  28. 1 1
      src/components/head/index.vue
  29. 405 0
      src/components/viewImg/list.vue
  30. 1 1
      src/helper/mount.ts
  31. 9 6
      src/hook/upload.ts
  32. 2 2
      src/request/config.ts
  33. 60 28
      src/request/index.ts
  34. 11 1
      src/request/urls.ts
  35. 30 23
      src/router/config.ts
  36. 1 1
      src/router/index.ts
  37. 1 0
      src/router/routeName.ts
  38. 71 11
      src/store/case.ts
  39. 4 1
      src/store/system.ts
  40. 97 0
      src/util/browser.ts
  41. 110 0
      src/util/index.ts
  42. 404 369
      src/view/abstract/index.vue
  43. 3 1
      src/view/case/draw/selectMapImagess.vue
  44. 2 1
      src/view/case/quisk.ts
  45. 566 505
      src/view/case/records/index.vue
  46. 1 1
      src/view/case/records/manifest.vue
  47. 121 0
      src/view/diversity/downloadLog.vue
  48. 31 0
      src/view/diversity/editModel.vue
  49. 280 0
      src/view/diversity/index.vue
  50. 44 0
      src/view/diversity/list.vue
  51. 179 0
      src/view/diversity/modelContent.vue
  52. 63 0
      src/view/diversity/pagging.ts
  53. 32 0
      src/view/diversity/quisk.ts
  54. 148 0
      src/view/diversity/sceneContent.vue
  55. 133 0
      src/view/diversity/sceneDownload.vue
  56. 67 0
      src/view/diversity/tableModel.vue
  57. 246 43
      src/view/dossier/index.vue
  58. 11 4
      src/view/layout/index.vue
  59. 0 4
      src/view/layout/loginDialog.vue
  60. 33 5
      src/view/layout/slide/index.vue
  61. 145 52
      src/view/layout/top/index.vue
  62. 2 4
      src/view/layout/top/style.scss
  63. 111 169
      src/view/material/AddScenesImg.vue
  64. 6 6
      src/view/material/addLibrary.vue
  65. 21 15
      src/view/material/addScenes.vue
  66. 68 0
      src/view/material/formData.ts
  67. 0 1
      src/view/material/index.vue
  68. 3 3
      src/view/material/list.vue
  69. 67 0
      src/view/material/newlist.vue
  70. 6 6
      src/view/material/pagging.ts
  71. 196 68
      src/view/material/photos.vue
  72. 7 3
      src/view/material/quisk.ts
  73. 7 3
      src/view/material/rollMakingList.vue
  74. 1043 335
      src/view/material/sceneImg.vue
  75. 3 0
      src/view/material/setType.vue
  76. 123 0
      src/view/material/svg.vue
  77. 11 2
      src/view/organization/index.vue
  78. 3 0
      src/view/originalPhoto/addCaseFile.vue
  79. 2 2
      src/view/originalPhoto/addLibrary.vue
  80. 19 10
      src/view/originalPhoto/addScenes.vue
  81. 204 50
      src/view/originalPhoto/index.vue
  82. 4 4
      src/view/originalPhoto/list.vue
  83. 1 0
      src/view/originalPhoto/pagging.ts
  84. 6 0
      src/view/originalPhoto/quisk.ts
  85. 3 1
      src/view/originalPhoto/setType.vue
  86. 12 3
      src/view/role/index.vue
  87. 0 1
      src/view/system/style.scss
  88. 17 7
      src/view/user/index.vue
  89. 187 78
      src/view/vrmodel/index.vue
  90. 2 2
      src/view/vrmodel/list.vue
  91. 9 2
      src/view/vrmodel/pagging.ts
  92. 1 1
      src/view/vrmodel/quisk.ts
  93. 22 10
      src/view/vrmodel/sceneContent.vue
  94. 30 24
      src/view/vrmodel/tableModel.vue
  95. 15 13
      vite.config.ts

+ 271 - 0
public/browser.html

@@ -0,0 +1,271 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>页面兼容性提示</title>
+  <style>
+    /* 基础重置与布局 */
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
+      background-color: #fff;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      min-height: 100vh;
+      color: #999;
+    }
+
+    /* 容器与文本样式 */
+    .container {
+      text-align: center;
+      padding-bottom: 100px;
+    }
+
+    .tipsImg {
+      width: 240px;
+      height: 240px;
+      margin-bottom: 20px;
+    }
+
+    .tips {
+      padding: 10px 0;
+      line-height: 1.6;
+    }
+
+    /* 浏览器图标区域布局 */
+    .browser-icons {
+      display: flex;
+      justify-content: center;
+      gap: 40px;
+      margin-top: 20px;
+      flex-wrap: wrap;
+      /* 适配小屏幕换行 */
+    }
+
+    .browser-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      cursor: pointer;
+      transition: transform 0.2s ease;
+      width: 130px;
+    }
+
+    .browser-item:hover {
+      transform: scale(1.05);
+    }
+
+    .browser-item img {
+      width: 80px;
+      height: 80px;
+      margin-bottom: 18px;
+    }
+
+    .browser-item span {
+      font-size: 14px;
+    }
+
+    /* 国产系统专属提示(可选样式) */
+    .domestic-os-tip {
+      color: #ff6600;
+      font-weight: bold;
+      margin-top: 20px;
+    }
+
+    .icon-area {
+      display: flex;
+      justify-content: center;
+      gap: 40px;
+      margin-top: 20px;
+    }
+
+    /* 单个图标及文字的样式 */
+    .browser-icon {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      cursor: pointer;
+    }
+
+    .browser-icon img {
+      width: 80px;
+      height: 80px;
+      margin-bottom: 18px;
+    }
+
+    .browser-icon span {
+      font-size: 14px;
+    }
+
+    /* 顶部提示文字样式 */
+    .tips {
+    }
+    .downImg{
+      width: 16px !important;
+      height: 16px !important;
+      margin-bottom: 0 !important;
+      margin-right: 10px;
+    }
+    .browser-bottom{
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+  </style>
+</head>
+
+<body>
+  <div class="container">
+    <!-- 通用提示文案 -->
+    <img class="tipsImg" src="./images/pic_update.png" alt="">
+    <p class="tips" style="margin-top: 0;">无法打开页面,请升级或更换浏览器后重新打开</p>
+    <p class="tips">建议使用以下浏览器</p>
+
+    <!-- 浏览器图标区 -->
+    <div class="browser-icons" id="Domestic">
+    </div>
+  </div>
+
+  <script>
+    // 识别国产操作系统逻辑(示例覆盖常见标识,可根据需要补充)
+    function isDomesticOS() {
+      const userAgent = navigator.userAgent.toLowerCase();
+      const platform = navigator.platform.toLowerCase();
+      console.log(userAgent, platform, '浏览器标识')
+      // 统信 UOS、银河麒麟、深度 Deepin、中标麒麟等标识
+      return /uos|kylin|deepin|neokylin/.test(userAgent)
+        || /linux/.test(platform) || /国产|中国/.test(userAgent);
+    }
+    // 非国产浏览器
+    const browsers = [
+      {
+        name: 'Firefox',
+        icon: 'fa-firefox',
+        color: 'bg-orange-500',
+        src: './images/browser_Firefox.png',
+        downloadUrl: './images/browser_Firefox.png'
+      },
+      {
+        name: 'Microsoft Edge',
+        icon: 'fa-edge',
+        color: 'bg-blue-500',
+        src: './images/browser_Edge.png',
+        downloadUrl: './images/browser_Edge.png'
+      },
+      {
+        name: 'Chrome',
+        icon: 'fa-chrome',
+        color: 'bg-red-500',
+        src: './images/browser_Chrome.png',
+        downloadUrl: './images/browser_Chrome.png'
+      }
+    ];
+    // 国产浏览器
+    const browsersList = [
+      {
+        name: 'Chromiun',
+        icon: 'fa-firefox',
+        color: 'bg-orange-500',
+        src: './images/browser_Chromium.png',
+        downloadUrl: './images/browser_Chromium.png'
+      },
+      {
+        name: '奇安信可信浏览器',
+        icon: 'fa-edge',
+        color: 'bg-blue-500',
+        src: './images/browser_Qianxin.png',
+        downloadUrl: './images/browser_Qianxin.png'
+      },
+      {
+        name: '360安全浏览器',
+        icon: 'fa-chrome',
+        color: 'bg-red-500',
+        src: './images/browser_360.png',
+        downloadUrl: './images/browser_360.png'
+      },
+      {
+        name: '火狐',
+        icon: 'fa-chrome',
+        color: 'bg-red-500',
+        src: './images/browser_Firefox.png',
+        downloadUrl: './images/browser_Firefox.png'
+      }
+    ];
+    // 获取容器元素
+    const container = document.getElementById('Domestic');
+    const isDomestic = isDomesticOS();
+    let list = !isDomestic ? browsers : browsersList;
+    console.log('isDomestic',isDomestic, 'list', list);
+    // 动态遍历数组生成DOM元素
+    list.forEach(browser => {
+      // 创建浏览器卡片容器
+      const browserCard = document.createElement('div');
+      browserCard.className = 'browser-item';
+
+      // 创建链接元素
+      const link = document.createElement('a');
+      // link.href = browser.downloadUrl;
+      // link.target = '_blank';
+      link.href = '#';
+      link.rel = 'noopener noreferrer';
+      link.className = 'flex flex-col items-center';
+      link.style = 'color: #909090';
+
+      // 创建图标容器
+      const iconContainer = document.createElement('div');
+      iconContainer.className = `browser-icon ${browser.color} mb-3 text-white`;
+
+      // 创建图标元素
+      const icon = document.createElement('img');
+      icon.className = `fa ${browser.icon} text-2xl`;
+      icon.src = browser.src;
+
+      // 创建名称元素
+      const name = document.createElement('span');
+      name.className = 'text-sm text-gray-600 group-hover:text-primary transition-colors';
+      name.textContent = browser.name;
+
+      // 创建下载图标
+      // const downloadIcon = document.createElement('img');
+      // downloadIcon.src = './images/down.png';
+      // downloadIcon.className = 'downImg';
+
+      // const textDiv = document.createElement('div');
+      // textDiv.className = `browser-bottom`;
+      // textDiv.appendChild(downloadIcon);
+      // textDiv.appendChild(name);
+      
+      // 组装元素
+      iconContainer.appendChild(icon);
+      iconContainer.appendChild(name);
+      link.appendChild(iconContainer);
+      // link.appendChild(downloadIcon);
+      // link.appendChild(textDiv);
+      browserCard.appendChild(link);
+      container.appendChild(browserCard);
+    });
+    // 执行检测
+    // const domestic = document.getElementById('domestic');
+    // const noDomestic = document.getElementById('noDomestic');
+
+    // if (isDomestic) {
+    //   domestic.style.display = 'flex';
+    //   noDomestic.style.display = 'none';
+    // } else {
+    //   // 非国产系统可隐藏或自定义提示
+    //   domestic.style.display = 'none';
+    //   noDomestic.style.display = 'flex';
+    // }
+  </script>
+</body>
+
+</html>

BIN
public/images/browser_360.png


BIN
public/images/browser_Chrome.png


BIN
public/images/browser_Chromium.png


BIN
public/images/browser_Edge.png


BIN
public/images/browser_Firefox.png


BIN
public/images/browser_Qianxin.png


BIN
public/images/browser_Safari.png


BIN
public/images/down.png


BIN
public/images/pic_update.png


+ 19 - 2
src/App.vue

@@ -10,8 +10,10 @@
 <script setup lang="ts">
 import Locale from "@/config/locale.vue";
 import { setToken } from "@/store/user";
-import { sceneType } from "@/store/case";
+import { sceneType, show } from "@/store/case";
 import { strToParams } from "@/util";
+import { ElMessageBox } from "element-plus";
+import { browser } from "@/util/browser.ts";
 const params = strToParams(window.location.search);
 console.log("params", params);
 if (params.token) {
@@ -20,5 +22,20 @@ if (params.token) {
 if(params.power){
   sceneType.value = params.power;
 }
-console.log("params", params);
+if(params.show){
+  show.value = params.show == 'true' ? true : false;
+}
+let version = browser.isGreaterThan('128.0.0')
+if(!version){
+  window.location.href = './browser.html'
+  //   ElMessageBox.alert('当前浏览器不支持', "提示", {
+  //   confirmButtonText: "确定",
+  //   type: "warning",
+  //   callback: () => {
+  //     window.location.href = './browser.html'
+  //   },
+  // })
+}
+console.log("getBrowserInfo", params, browser.isGreaterThan('128.0.0'));
+
 </script>

+ 1 - 0
src/app/criminal/routeConfig.ts

@@ -10,6 +10,7 @@ export const menuRouteNames = [
   // CriminalRouteName.statistics,
   CriminalRouteName.abstract,
   CriminalRouteName.vrmodel,
+  CriminalRouteName.diversity,
   CriminalRouteName.originalPhoto,
   // CriminalRouteName.material,
   CriminalRouteName.photos,

+ 65 - 53
src/app/criminal/view/login/index.vue

@@ -2,26 +2,26 @@
   <div class="login-layer">
     <div class="content">
       <div class="info">
-        <img src="@/assets/image/criminal.ico" alt="" />
-        <h1>{{ appConstant.title }}</h1>
+        <img src="@/assets/image/logo.png" alt="" />
+        <h1>登录案件</h1>
       </div>
       <el-form class="panel login" :model="form" @submit.stop>
-        <h2>欢 迎 登 录</h2>
+        <!-- <h2>欢 迎 登 录</h2> -->
         <el-form-item class="panel-form-item">
           <p class="err-info">{{ verification.phone }}</p>
           <el-input
             :maxlength="11"
-            v-model.trim="form.phone"
-            placeholder="手机号"
+            v-model.trim="form.account"
+            placeholder="请输入账号"
             @keydown.enter="submitClick"
           ></el-input>
         </el-form-item>
-        <el-form-item class="panel-form-item">
+        <el-form-item class="panel-form-item" style="padding: 0">
           <p class="err-info">{{ verification.psw }}</p>
           <el-input
-            v-model="form.psw"
+            v-model="form.password"
             :maxlength="16"
-            placeholder="密码"
+            placeholder="请输入密码"
             :type="flag ? 'password' : 'text'"
             @keydown.enter="submitClick"
           >
@@ -40,7 +40,7 @@
           </el-input>
         </el-form-item>
 
-        <el-form-item class="panel-form-item code-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"
@@ -52,10 +52,12 @@
               <img :src="codeImg" class="code-img" @click="refer" />
             </template>
           </el-input>
-        </el-form-item>
-
-        <el-form-item class="panel-form-item" style="margin-top: 18px">
-          <el-button type="primary" class="fill" @click="submitClick">登录</el-button>
+        </el-form-item> -->
+          <el-form-item class="panel-form-item" style="padding: 12px 0 24px 0">
+            <el-checkbox v-model="form.remember" label="记住密码" size="large" />
+          </el-form-item>
+        <el-form-item class="panel-form-item" style="margin-top: 0px">
+          <el-button color="#0960BD" type="primary" class="fill" @click="submitClick">登录</el-button>
         </el-form-item>
 
         <!-- <div class="more">
@@ -64,10 +66,10 @@
       </el-form>
     </div>
 
-    <p class="desc">
+    <!-- <p class="desc">
       公安部鉴定中心 & 珠海市四维时代网络科技有限公司 |
       公安部科技强警基础工作计划(2022JC13)
-    </p>
+    </p> -->
   </div>
 </template>
 
@@ -81,12 +83,18 @@ import { login } from "@/store/system";
 import { appConstant } from "@/app";
 import { user } from "@/store/user";
 
+const caseId = computed(() => {
+  const caseId = router.currentRoute.value.params.caseId;
+  if (caseId) {
+    return Number(caseId);
+  }
+});
 // 是否显示明文密码
 const flag = ref(true);
 // 表单
 const form = reactive({
-  phone: localStorage.getItem("userName") || "",
-  psw: localStorage.getItem("password") || "",
+  account: localStorage.getItem("username") || "",
+  password: localStorage.getItem("password") || "",
   code: "",
   remember: import.meta.env.DEV || localStorage.getItem("remember") === "1",
 });
@@ -95,24 +103,16 @@ const verification = reactive({ phone: "", psw: "", code: "" });
 watch(
   form,
   () => {
-    console.log("form", form);
-    if (!form.phone) {
-      verification.phone = "请输入手机号";
-    // } else if (form.phone == "88888888888") {
-    //   verification.phone = "";
-    // } else {
-    //   verification.phone = PHONE.REG.test(form.phone) ? "" : PHONE.tip;
+    if (!form.account) {
+      verification.phone = "请输入账号";
+    } else {
+      verification.phone = "";
     }
-    if (!form.psw) {
+    if (!form.password) {
       verification.psw = "请输入密码";
     } else {
       verification.psw = "";
     }
-    if (!form.code.trim()) {
-      verification.code = "请输入验证码";
-    } else {
-      verification.code = "";
-    }
   },
   { immediate: true }
 );
@@ -124,21 +124,21 @@ const codeImg = computed(() => baseURL + getCode + "?key=" + imgKey.value);
 
 // 表单提交
 const submitClick = async () => {
-  if (verification.phone && verification.phone !== "88888888888") {
+  if (verification.phone) {
     return openErrorMsg(verification.phone);
   }
   if (verification.psw) return openErrorMsg(verification.psw);
   if (verification.code) return openErrorMsg(verification.code);
 
   try {
-    await login({ phoneNum: form.phone, code: form.code, password: form.psw });
+    await login({ username: form.account, code: form.code, password: form.password, caseId: caseId.value });
 
     if (form.remember) {
-      localStorage.setItem("userName", form.phone);
-      localStorage.setItem("password", form.psw);
+      localStorage.setItem("username", form.account);
+      localStorage.setItem("password", form.password);
       localStorage.setItem("remember", "1");
     } else {
-      localStorage.setItem("userName", "");
+      localStorage.setItem("username", "");
       localStorage.setItem("password", "");
       localStorage.setItem("remember", "0");
     }
@@ -150,7 +150,7 @@ const submitClick = async () => {
       url.searchParams.append("token", user.value.token);
       window.location.replace(url);
     } else {
-      router.replace({ name: RouteName.scene, params: { caseId: 360 } });
+      router.replace({ name: RouteName.abstract });
     }
   } catch (e) {
     console.error(e);
@@ -186,28 +186,40 @@ const submitClick = async () => {
   }
 }
 .content {
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-start;
+  width: 400px;
+  height: 430px;
+  margin: 0 auto;
+  text-align: center;
+  position: absolute;
+  top: calc(50% - 215px);
+  left: calc(50% - 200px);
+  // display: flex;
+  // justify-content: space-between;
+  // align-items: flex-start;
   height: 100%;
 }
 .info {
-  color: #fff;
-  margin-right: 143px;
-  padding-top: 40px;
-  padding-left: 44px;
-  flex: none;
-  text-align: left;
-  display: flex;
-  align-items: center;
-
+  // color: #fff;
+  // margin-right: 143px;
+  // padding-top: 40px;
+  // padding-left: 44px;
+  // flex: none;
+  // text-align: left;
+  // display: flex;
+  // align-items: center;
+  // display: inline-flex;
   img {
-    width: 40px;
-    height: 40px;
-    margin-right: 20px;
+    width: 80px;
+    height: 80px;
+    margin: 0px auto 10px auto;
   }
   h1 {
-    font-size: 2.2rem;
+    font-weight: bold;
+    font-size: 32px;
+    color: #000000;
+    line-height: 48px;
+    letter-spacing: 12px;
+    text-align: center;
   }
 }
 
@@ -266,7 +278,7 @@ const submitClick = async () => {
 
 <style>
 .login-layer .el-input {
-  --el-input-bg-color: #f6f8fb;
+  --el-input-bg-color: #fff;
   /* var(--el-fill-color-blank) */
 }
 .login .code-form-item .el-input {

+ 0 - 1
src/app/xmfire/view/login/index.vue

@@ -351,7 +351,6 @@ const submitClick = async () => {
 
 .panel-form-item .el-button {
   line-height: 26px;
-  font-weight: bold;
   font-size: 16px;
 }
 

+ 440 - 3
src/assets/font/demo_index.html

@@ -55,6 +55,120 @@
           <ul class="icon_lists dib-box">
           
             <li class="dib">
+              <span class="icon iconfont">&#xe7c8;</span>
+                <div class="name">Upload</div>
+                <div class="code-name">&amp;#xe7c8;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c9;</span>
+                <div class="name">import</div>
+                <div class="code-name">&amp;#xe7c9;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c6;</span>
+                <div class="name">rename</div>
+                <div class="code-name">&amp;#xe7c6;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c7;</span>
+                <div class="name">media_l</div>
+                <div class="code-name">&amp;#xe7c7;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe63f;</span>
+                <div class="name">PDF图标</div>
+                <div class="code-name">&amp;#xe63f;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe66e;</span>
+                <div class="name">word</div>
+                <div class="code-name">&amp;#xe66e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7ba;</span>
+                <div class="name">virus</div>
+                <div class="code-name">&amp;#xe7ba;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7bc;</span>
+                <div class="name">handprint</div>
+                <div class="code-name">&amp;#xe7bc;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7bd;</span>
+                <div class="name">corpse</div>
+                <div class="code-name">&amp;#xe7bd;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7be;</span>
+                <div class="name">footprint</div>
+                <div class="code-name">&amp;#xe7be;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7bf;</span>
+                <div class="name">check</div>
+                <div class="code-name">&amp;#xe7bf;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c0;</span>
+                <div class="name">video</div>
+                <div class="code-name">&amp;#xe7c0;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c1;</span>
+                <div class="name">electronic</div>
+                <div class="code-name">&amp;#xe7c1;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c2;</span>
+                <div class="name">case_other</div>
+                <div class="code-name">&amp;#xe7c2;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c3;</span>
+                <div class="name">physics_and_chemistry</div>
+                <div class="code-name">&amp;#xe7c3;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c4;</span>
+                <div class="name">cube</div>
+                <div class="code-name">&amp;#xe7c4;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7c5;</span>
+                <div class="name">folder_close</div>
+                <div class="code-name">&amp;#xe7c5;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7bb;</span>
+                <div class="name">wrenchAndScrewdriver</div>
+                <div class="code-name">&amp;#xe7bb;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7b9;</span>
+                <div class="name">poison</div>
+                <div class="code-name">&amp;#xe7b9;</div>
+              </li>
+          
+            <li class="dib">
               <span class="icon iconfont">&#xe603;</span>
                 <div class="name">价格保护</div>
                 <div class="code-name">&amp;#xe603;</div>
@@ -144,9 +258,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1734689634265') format('woff2'),
-       url('iconfont.woff?t=1734689634265') format('woff'),
-       url('iconfont.ttf?t=1734689634265') format('truetype');
+  src: url('iconfont.woff2?t=1756902711273') format('woff2'),
+       url('iconfont.woff?t=1756902711273') format('woff'),
+       url('iconfont.ttf?t=1756902711273') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -173,6 +287,177 @@
         <ul class="icon_lists dib-box">
           
           <li class="dib">
+            <span class="icon iconfont icon-Upload"></span>
+            <div class="name">
+              Upload
+            </div>
+            <div class="code-name">.icon-Upload
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-import"></span>
+            <div class="name">
+              import
+            </div>
+            <div class="code-name">.icon-import
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-rename"></span>
+            <div class="name">
+              rename
+            </div>
+            <div class="code-name">.icon-rename
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-media_l"></span>
+            <div class="name">
+              media_l
+            </div>
+            <div class="code-name">.icon-media_l
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-PDFtubiao"></span>
+            <div class="name">
+              PDF图标
+            </div>
+            <div class="code-name">.icon-PDFtubiao
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-word"></span>
+            <div class="name">
+              word
+            </div>
+            <div class="code-name">.icon-word
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-virus"></span>
+            <div class="name">
+              virus
+            </div>
+            <div class="code-name">.icon-virus
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-handprint"></span>
+            <div class="name">
+              handprint
+            </div>
+            <div class="code-name">.icon-handprint
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-corpse"></span>
+            <div class="name">
+              corpse
+            </div>
+            <div class="code-name">.icon-corpse
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-footprint"></span>
+            <div class="name">
+              footprint
+            </div>
+            <div class="code-name">.icon-footprint
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-check"></span>
+            <div class="name">
+              check
+            </div>
+            <div class="code-name">.icon-check
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-video"></span>
+            <div class="name">
+              video
+            </div>
+            <div class="code-name">.icon-video
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-electronic"></span>
+            <div class="name">
+              electronic
+            </div>
+            <div class="code-name">.icon-electronic
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-case_other"></span>
+            <div class="name">
+              case_other
+            </div>
+            <div class="code-name">.icon-case_other
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-physics_and_chemistry"></span>
+            <div class="name">
+              physics_and_chemistry
+            </div>
+            <div class="code-name">.icon-physics_and_chemistry
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-cube"></span>
+            <div class="name">
+              cube
+            </div>
+            <div class="code-name">.icon-cube
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-folder_close"></span>
+            <div class="name">
+              folder_close
+            </div>
+            <div class="code-name">.icon-folder_close
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-wrenchAndScrewdriver"></span>
+            <div class="name">
+              wrenchAndScrewdriver
+            </div>
+            <div class="code-name">.icon-wrenchAndScrewdriver
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-poison"></span>
+            <div class="name">
+              poison
+            </div>
+            <div class="code-name">.icon-poison
+            </div>
+          </li>
+          
+          <li class="dib">
             <span class="icon iconfont icon-jiagebaohu"></span>
             <div class="name">
               价格保护
@@ -309,6 +594,158 @@
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-Upload"></use>
+                </svg>
+                <div class="name">Upload</div>
+                <div class="code-name">#icon-Upload</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-import"></use>
+                </svg>
+                <div class="name">import</div>
+                <div class="code-name">#icon-import</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-rename"></use>
+                </svg>
+                <div class="name">rename</div>
+                <div class="code-name">#icon-rename</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-media_l"></use>
+                </svg>
+                <div class="name">media_l</div>
+                <div class="code-name">#icon-media_l</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-PDFtubiao"></use>
+                </svg>
+                <div class="name">PDF图标</div>
+                <div class="code-name">#icon-PDFtubiao</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-word"></use>
+                </svg>
+                <div class="name">word</div>
+                <div class="code-name">#icon-word</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-virus"></use>
+                </svg>
+                <div class="name">virus</div>
+                <div class="code-name">#icon-virus</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-handprint"></use>
+                </svg>
+                <div class="name">handprint</div>
+                <div class="code-name">#icon-handprint</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-corpse"></use>
+                </svg>
+                <div class="name">corpse</div>
+                <div class="code-name">#icon-corpse</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-footprint"></use>
+                </svg>
+                <div class="name">footprint</div>
+                <div class="code-name">#icon-footprint</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-check"></use>
+                </svg>
+                <div class="name">check</div>
+                <div class="code-name">#icon-check</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-video"></use>
+                </svg>
+                <div class="name">video</div>
+                <div class="code-name">#icon-video</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-electronic"></use>
+                </svg>
+                <div class="name">electronic</div>
+                <div class="code-name">#icon-electronic</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-case_other"></use>
+                </svg>
+                <div class="name">case_other</div>
+                <div class="code-name">#icon-case_other</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-physics_and_chemistry"></use>
+                </svg>
+                <div class="name">physics_and_chemistry</div>
+                <div class="code-name">#icon-physics_and_chemistry</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-cube"></use>
+                </svg>
+                <div class="name">cube</div>
+                <div class="code-name">#icon-cube</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-folder_close"></use>
+                </svg>
+                <div class="name">folder_close</div>
+                <div class="code-name">#icon-folder_close</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-wrenchAndScrewdriver"></use>
+                </svg>
+                <div class="name">wrenchAndScrewdriver</div>
+                <div class="code-name">#icon-wrenchAndScrewdriver</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-poison"></use>
+                </svg>
+                <div class="name">poison</div>
+                <div class="code-name">#icon-poison</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-jiagebaohu"></use>
                 </svg>
                 <div class="name">价格保护</div>

+ 79 - 3
src/assets/font/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 4789215 */
-  src: url('iconfont.woff2?t=1734689634265') format('woff2'),
-       url('iconfont.woff?t=1734689634265') format('woff'),
-       url('iconfont.ttf?t=1734689634265') format('truetype');
+  src: url('iconfont.woff2?t=1756902711273') format('woff2'),
+       url('iconfont.woff?t=1756902711273') format('woff'),
+       url('iconfont.ttf?t=1756902711273') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,82 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-Upload:before {
+  content: "\e7c8";
+}
+
+.icon-import:before {
+  content: "\e7c9";
+}
+
+.icon-rename:before {
+  content: "\e7c6";
+}
+
+.icon-media_l:before {
+  content: "\e7c7";
+}
+
+.icon-PDFtubiao:before {
+  content: "\e63f";
+}
+
+.icon-word:before {
+  content: "\e66e";
+}
+
+.icon-virus:before {
+  content: "\e7ba";
+}
+
+.icon-handprint:before {
+  content: "\e7bc";
+}
+
+.icon-corpse:before {
+  content: "\e7bd";
+}
+
+.icon-footprint:before {
+  content: "\e7be";
+}
+
+.icon-check:before {
+  content: "\e7bf";
+}
+
+.icon-video:before {
+  content: "\e7c0";
+}
+
+.icon-electronic:before {
+  content: "\e7c1";
+}
+
+.icon-case_other:before {
+  content: "\e7c2";
+}
+
+.icon-physics_and_chemistry:before {
+  content: "\e7c3";
+}
+
+.icon-cube:before {
+  content: "\e7c4";
+}
+
+.icon-folder_close:before {
+  content: "\e7c5";
+}
+
+.icon-wrenchAndScrewdriver:before {
+  content: "\e7bb";
+}
+
+.icon-poison:before {
+  content: "\e7b9";
+}
+
 .icon-jiagebaohu:before {
   content: "\e603";
 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
src/assets/font/iconfont.js


+ 133 - 0
src/assets/font/iconfont.json

@@ -6,6 +6,139 @@
   "description": "",
   "glyphs": [
     {
+      "icon_id": "45434743",
+      "name": "Upload",
+      "font_class": "Upload",
+      "unicode": "e7c8",
+      "unicode_decimal": 59336
+    },
+    {
+      "icon_id": "45434742",
+      "name": "import",
+      "font_class": "import",
+      "unicode": "e7c9",
+      "unicode_decimal": 59337
+    },
+    {
+      "icon_id": "45263630",
+      "name": "rename",
+      "font_class": "rename",
+      "unicode": "e7c6",
+      "unicode_decimal": 59334
+    },
+    {
+      "icon_id": "45263629",
+      "name": "media_l",
+      "font_class": "media_l",
+      "unicode": "e7c7",
+      "unicode_decimal": 59335
+    },
+    {
+      "icon_id": "6233085",
+      "name": "PDF图标",
+      "font_class": "PDFtubiao",
+      "unicode": "e63f",
+      "unicode_decimal": 58943
+    },
+    {
+      "icon_id": "14476528",
+      "name": "word",
+      "font_class": "word",
+      "unicode": "e66e",
+      "unicode_decimal": 58990
+    },
+    {
+      "icon_id": "45263094",
+      "name": "virus",
+      "font_class": "virus",
+      "unicode": "e7ba",
+      "unicode_decimal": 59322
+    },
+    {
+      "icon_id": "45263091",
+      "name": "handprint",
+      "font_class": "handprint",
+      "unicode": "e7bc",
+      "unicode_decimal": 59324
+    },
+    {
+      "icon_id": "45263092",
+      "name": "corpse",
+      "font_class": "corpse",
+      "unicode": "e7bd",
+      "unicode_decimal": 59325
+    },
+    {
+      "icon_id": "45263090",
+      "name": "footprint",
+      "font_class": "footprint",
+      "unicode": "e7be",
+      "unicode_decimal": 59326
+    },
+    {
+      "icon_id": "45263089",
+      "name": "check",
+      "font_class": "check",
+      "unicode": "e7bf",
+      "unicode_decimal": 59327
+    },
+    {
+      "icon_id": "45263086",
+      "name": "video",
+      "font_class": "video",
+      "unicode": "e7c0",
+      "unicode_decimal": 59328
+    },
+    {
+      "icon_id": "45263085",
+      "name": "electronic",
+      "font_class": "electronic",
+      "unicode": "e7c1",
+      "unicode_decimal": 59329
+    },
+    {
+      "icon_id": "45263087",
+      "name": "case_other",
+      "font_class": "case_other",
+      "unicode": "e7c2",
+      "unicode_decimal": 59330
+    },
+    {
+      "icon_id": "45263084",
+      "name": "physics_and_chemistry",
+      "font_class": "physics_and_chemistry",
+      "unicode": "e7c3",
+      "unicode_decimal": 59331
+    },
+    {
+      "icon_id": "45263083",
+      "name": "cube",
+      "font_class": "cube",
+      "unicode": "e7c4",
+      "unicode_decimal": 59332
+    },
+    {
+      "icon_id": "45263082",
+      "name": "folder_close",
+      "font_class": "folder_close",
+      "unicode": "e7c5",
+      "unicode_decimal": 59333
+    },
+    {
+      "icon_id": "45263095",
+      "name": "wrenchAndScrewdriver",
+      "font_class": "wrenchAndScrewdriver",
+      "unicode": "e7bb",
+      "unicode_decimal": 59323
+    },
+    {
+      "icon_id": "45263093",
+      "name": "poison",
+      "font_class": "poison",
+      "unicode": "e7b9",
+      "unicode_decimal": 59321
+    },
+    {
       "icon_id": "90509",
       "name": "价格保护",
       "font_class": "jiagebaohu",

BIN
src/assets/font/iconfont.ttf


BIN
src/assets/font/iconfont.woff


BIN
src/assets/font/iconfont.woff2


BIN
src/assets/image/criminalBanner.png


BIN
src/assets/image/logo.png


+ 229 - 152
src/assets/style/public.scss

@@ -3,7 +3,7 @@
 @import "./animate.css";
 
 * {
-  margin : 0;
+  margin: 0;
   padding: 0;
 }
 
@@ -13,62 +13,76 @@ html {
 
 html,
 body {
-  width : 100%;
+  width: 100%;
   height: 100%;
 }
 
 :root {
   --primaryColor: var(--el-color-primary);
-  --colorColor  : #303133;
-  --bgColor     : #f0f2f5;
+  --colorColor: #303133;
+  --bgColor: #f0f2f5;
+  --leftwidth: 320px;
+  @media (min-width: 1280px) {
+  --leftwidth: 480px;
+  }
 }
 
 body {
 
   font-family: "Microsoft YaHei";
-  color      : var(--colorColor);
-  overflow   : auto;
+  color: var(--colorColor);
+  overflow: auto;
 
 }
 
 #app {
-  position  : relative;
-  min-width : 1280px;
+  position: relative;
+  min-width: 1024px;
   min-height: 760px;
-  height    : 100%;
-  overflow  : hidden;
+  height: 100%;
+  overflow: hidden;
 }
 
 .fill.el-button {
   width: 100%;
 }
+.import{
+  background: #fff !important;
+  border: 1px dashed #D9D9D9;
+  border-radius: 6px;
+  &:hover{
+        border-color: var(--el-color-primary);
+  }
+
+}
 .newbut {
   height: 32px !important;
+  // border-color: #A8A8A8 !important;
   border: none !important;
 }
 
 .slide {
   .el-menu {
     border-right: none;
-    background  : #fff;
+    background: #fff;
 
     i {
-      color       : inherit;
+      color: inherit;
       margin-right: 8px;
     }
   }
 
   .el-menu-item.is-active {
-    position  : relative;
+    position: relative;
     background: var(--el-menu-hover-bg-color);
 
     &::after {
-      content         : "";
-      position        : absolute;
-      top             : 0;
-      bottom          : 0;
-      width           : 2px;
-      right           : 0;
+      content: "";
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      width: 2px;
+      right: 0;
       background-color: var(--primaryColor);
     }
   }
@@ -97,25 +111,25 @@ body {
 }
 
 .body-layer {
-  flex            : 1;
-  overflow-y      : auto;
-  margin-top      : 8px;
+  flex: 1;
+  overflow-y: auto;
+  margin-top: 8px;
   background-color: #fff;
-  border-radius   : 4px;
-  padding         : 0 24px;
-  display         : flex;
-  flex-direction  : column;
+  border-radius: 4px;
+  padding: 0 24px;
+  display: flex;
+  flex-direction: column;
 
   .body-head {
-    display        : flex;
+    display: flex;
     justify-content: space-between;
-    align-items    : center;
+    align-items: center;
 
     h3 {
-      font-size  : 1.14rem;
+      font-size: 1.14rem;
       font-weight: normal;
-      padding    : 17px 0;
-      color      : var(--colorColor);
+      padding: 17px 0;
+      color: var(--colorColor);
     }
   }
 
@@ -124,10 +138,10 @@ body {
   }
 
   >.el-table {
-    flex          : 1;
-    display       : flex;
+    flex: 1;
+    display: flex;
     flex-direction: column;
-    border        : 1px solid #d9d9d9;
+    border: 1px solid #d9d9d9;
 
     .el-table__header-wrapper {
       background-color: #fafafa;
@@ -138,7 +152,7 @@ body {
     }
 
     .el-table__body-wrapper {
-      flex      : 1;
+      flex: 1;
       overflow-y: auto;
     }
   }
@@ -148,14 +162,14 @@ body {
   background: #fafafa;
 
   .cell {
-    font-size  : 0.825rem;
-    color      : var(--colorColor);
+    font-size: 0.825rem;
+    color: var(--colorColor);
     font-weight: normal;
   }
 }
 
 .pag-block {
-  margin-top   : 16px;
+  margin-top: 16px;
   margin-bottom: 16px;
 
   .el-pagination {
@@ -164,13 +178,13 @@ body {
 
   &.no-sizes {
     .el-pagination__total {
-      flex      : 1;
+      flex: 1;
       text-align: left;
     }
   }
 
   .el-pagination__sizes {
-    flex      : 1;
+    flex: 1;
     text-align: left;
   }
 }
@@ -180,10 +194,10 @@ body {
 }
 
 .el-tabs__item {
-  font-size  : 14px;
+  font-size: 14px;
   line-height: 46px;
-  height     : 46px;
-  padding:  0 12px !important;
+  height: 46px;
+  padding: 0 24px !important;
 }
 
 .el-tabs__nav-wrap::after {
@@ -192,15 +206,15 @@ body {
 
 .stop-psw {
   position: absolute;
-  left    : -9999999999px;
-  top     : -999999999px;
+  left: -9999999999px;
+  top: -999999999px;
 }
 
 .oper-span {
-  --color  : #26559b !important;
-  color    : #26559b;
+  --color: #26559b !important;
+  color: #26559b;
   font-size: 0.825rem;
-  outline  : none !important;
+  outline: none !important;
 
   margin: 0 8px;
 
@@ -214,9 +228,9 @@ body {
 }
 
 .el-dropdown-menu {
-  position : initial;
+  position: initial;
   transform: none;
-  margin   : 0;
+  margin: 0;
 }
 
 .el-dropdown-menu__item {
@@ -225,14 +239,14 @@ body {
 
 .head-content {
   .el-form {
-    display              : grid;
-    grid-gap             : 20px;
+    display: grid;
+    grid-gap: 20px;
     grid-template-columns: repeat(3, 1fr) 160px;
   }
 
   .el-form-item {
     display: flex;
-    flex   : 0 0 auto;
+    flex: 0 0 auto;
   }
 
   .el-form-item__label {
@@ -249,7 +263,7 @@ body {
 
   .el-date-editor--daterange.el-input__inner,
   .el-form-item__content {
-    width    : 100%;
+    width: 100%;
     max-width: 300px;
   }
 
@@ -258,8 +272,8 @@ body {
     grid-area: 1 / 4 / 2 / 5;
 
     .el-form-item__content {
-      width      : 100%;
-      max-width  : inherit;
+      width: 100%;
+      max-width: inherit;
       margin-left: 0 !important;
       align-items: flex-start;
     }
@@ -272,7 +286,7 @@ body {
 
     &::before {
       content: "*";
-      color  : var(--primaryColor);
+      color: var(--primaryColor);
     }
   }
 }
@@ -283,7 +297,7 @@ body {
 }
 
 .info-from {
-  width : 380px;
+  width: 380px;
   margin: 0 auto;
 }
 
@@ -297,14 +311,14 @@ body {
 
 /*滚动条整体部分,必须要设置*/
 ::-webkit-scrollbar {
-  width : 5px;
+  width: 5px;
   height: 5px;
 }
 
 /*滚动条的滑块按钮*/
 ::-webkit-scrollbar-thumb {
   border-radius: 10px;
-  background   : var(--bgColor);
+  background: var(--bgColor);
 }
 
 /*滚动条的上下两端的按钮*/
@@ -312,20 +326,20 @@ body {
   display: none;
 }
 
-.el-message-box__message {
-  min-height: 50px;
-  padding   : 0 30px;
-}
+// .el-message-box__message {
+//   min-height: 50px;
+//   padding: 0 30px;
+// }
 
-.el-message-box__status {
-  top: -13px !important;
-}
+// .el-message-box__status {
+//   top: -13px !important;
+// }
 
-.el-message-box__title span::before {
-  content: "";
-  display: inline-block;
-  width  : 30px;
-}
+// .el-message-box__title span::before {
+//   content: "";
+//   display: inline-block;
+//   width: 30px;
+// }
 
 .click-row .el-table__body td:not(:first-child) {
   cursor: pointer;
@@ -333,32 +347,32 @@ body {
 
 .stop-psw {
   position: absolute;
-  left    : -9999999999px;
-  top     : -999999999px;
+  left: -9999999999px;
+  top: -999999999px;
 }
 
 .el-popper.is-dark {
-  position     : absolute;
+  position: absolute;
   border-radius: 4px;
-  padding      : 10px;
-  z-index      : 2000;
-  font-size    : 12px;
-  line-height  : 1.2;
-  min-width    : 10px;
-  word-wrap    : break-word;
-  background   : #303133;
-  color        : #fff;
+  padding: 10px;
+  z-index: 2000;
+  font-size: 12px;
+  line-height: 1.2;
+  min-width: 10px;
+  word-wrap: break-word;
+  background: #303133;
+  color: #fff;
 }
 
 .el-popper:not(.el-cascader__dropdown, .el-picker__popper, .el-dropdown__popper) {
-  max-width       : 500px;
+  max-width: 500px;
   // background: #EFEFEF;
   // box-shadow: 0px 2px 4px 0px #e4e4e4;
   // border-radius: 2px;
   // border: 1px solid #D7D7D7;
-  padding         : 2px 6px;
-  word-break      : break-all;
-  overflow        : hidden;
+  padding: 2px 6px;
+  word-break: break-all;
+  overflow: hidden;
 }
 
 // .is-light.el-popper{
@@ -390,21 +404,21 @@ body {
   }
 
   &::after {
-    content       : "\e6d2";
-    font-size     : 14px;
-    color         : #303133;
-    position      : absolute;
-    left          : 50%;
-    top           : 50%;
-    transform     : translate(-50%, -50%) rotate(90deg);
-    margin-left   : -10px;
-    font-family   : element-icons !important;
-    speak         : none;
-    font-style    : normal;
-    font-weight   : 400;
-    font-variant  : normal;
+    content: "\e6d2";
+    font-size: 14px;
+    color: #303133;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%) rotate(90deg);
+    margin-left: -10px;
+    font-family: element-icons !important;
+    speak: none;
+    font-style: normal;
+    font-weight: 400;
+    font-variant: normal;
     text-transform: none;
-    line-height   : 1;
+    line-height: 1;
     vertical-align: baseline;
   }
 }
@@ -413,15 +427,15 @@ body {
   position: relative;
 
   .input-inner-btn {
-    position   : absolute;
-    right      : 0;
-    top        : 50%;
-    height     : 36px;
+    position: absolute;
+    right: 0;
+    top: 50%;
+    height: 36px;
     line-height: 36px;
-    font-size  : 14px;
-    padding    : 0 15px;
-    text-align : center;
-    transform  : translateY(-50%);
+    font-size: 14px;
+    padding: 0 15px;
+    text-align: center;
+    transform: translateY(-50%);
   }
 }
 
@@ -430,40 +444,52 @@ body {
 }
 
 .el-message-box__header {
-  padding       : 0 34px;
+  padding: 0 !important;
   padding-bottom: 0;
 }
 
 .el-message-box__headerbtn .el-message-box__close {
-  display: none;
+  top: 12px;
+  position: relative;
+  left: -8px;
 }
 
 .el-message-box__title {
-  color    : #000;
+  color: #000;
   font-size: 16px;
+  padding: 0 20px;
+  height: 60px;
+  span{
+  line-height: 60px;
+  }
+}
+.el-message-box__container{
+  justify-content: center;
 }
-
 .el-message-box__content {
-  font-size  : 14px;
-  font-family: PingFangSC-Regular, PingFang SC;
-  font-weight: 400;
-  color      : rgba(0, 0, 0, 0.65);
+  padding: 40px 0;
+  border-top: 1px solid #f5f5f5;
+  border-bottom: 1px solid #f5f5f5;
 }
 
 .el-message-box {
-  padding-bottom: 24px;
+  padding: 0px !important;
 }
 
-.el-message-box__content {
-  padding: 12px 32px 24px 62px;
-}
+// .el-message-box__content {
+//   padding: 12px 32px 24px 62px;
+// }
 
-.el-message-box__message {
-  padding: 0;
-}
+// .el-message-box__message {
+//   padding: 0;
+// }
 
 .el-message-box__btns {
-  padding: 0 16px;
+  padding: 16px 0 !important;
+  justify-content: center !important;
+  button {
+    height: 32px;
+  }
 }
 
 .el-date-editor--date {
@@ -472,13 +498,13 @@ body {
   }
 
   .el-input__prefix {
-    position      : absolute;
-    height        : 100%;
-    right         : 5px;
-    top           : 0;
-    left          : inherit;
-    text-align    : center;
-    transition    : all 0.3s;
+    position: absolute;
+    height: 100%;
+    right: 5px;
+    top: 0;
+    left: inherit;
+    text-align: center;
+    transition: all 0.3s;
     pointer-events: none;
   }
 }
@@ -493,7 +519,7 @@ body {
 
 .el-date-editor--daterange .el-range__icon {
   position: absolute;
-  right   : 5px;
+  right: 5px;
 }
 
 .el-date-editor--daterange .el-range__close-icon {
@@ -505,10 +531,10 @@ body {
 }
 
 .el-upload-list__item-name {
-  font-size   : 14px;
-  font-family : PingFangSC-Regular, PingFang SC;
-  font-weight : 400;
-  line-height : 20px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  line-height: 20px;
   margin-right: 20px;
 }
 
@@ -533,12 +559,12 @@ body {
 }
 
 .disable {
-  opacity       : 0.3 !important;
+  opacity: 0.3 !important;
   pointer-events: none !important;
 }
 
 .body-but {
-  text-align   : right;
+  text-align: right;
   margin-bottom: 15px;
 }
 
@@ -548,19 +574,18 @@ body {
 
 .header-top {
   background-color: var(--primaryColor);
-  display         : flex;
-  align-items     : center;
-  justify-content : space-between;
-  padding         : 64px 48px 24px 48px;
-  height          : 7.5rem;
-  box-sizing      : border-box;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 64px 48px 24px 48px;
+  box-sizing: border-box;
 }
 
 html .el-input-group__append button.el-button {
-  background-color         : var(--el-button-bg-color) !important;
-  border-color             : var(--el-button-border-color) !important;
-  color                    : var(--el-button-text-color) !important;
-  border-top-left-radius   : 0 !important;
+  background-color: var(--el-button-bg-color) !important;
+  border-color: var(--el-button-border-color) !important;
+  color: var(--el-button-text-color) !important;
+  border-top-left-radius: 0 !important;
   border-bottom-left-radius: 0 !important;
 }
 
@@ -580,26 +605,78 @@ html .el-input-group__append button.el-button {
   font-family: Microsoft YaHei, Microsoft YaHei;
   font-weight: bold;
   font-size: 14px;
-  color: rgba(0,0,0,0.85);
+  color: rgba(0, 0, 0, 0.85);
   line-height: 22px;
   // margin-bottom: 16px;
 }
-.el-tabs__nav-wrap::after{
+
+.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{
+
+.el-select__wrapper {
   min-height: 38px;
   padding: 0 12px;
+}
+
+.abstractCentenr {
+  position: relative;
+  width: calc(100% - var(--leftwidth)) !important;
+  overflow-y: scroll;
+  border-right: 48px solid #fff;
+  border-bottom: 48px solid #fff;
+
+  .el-upload {
+    height: 100%;
+    width: 100%;
+  }
+
+  .el-upload-dragger {
+    height: 100%;
+    background: #fff;
+    padding: 0 var(--el-upload-dragger-padding-vertical) !important;
+  }
+
+  .centerBottom {
+    .buttom {
+      .el-button {
+        background-color: #f5f5f5
+      }
+    }
+  }
+}
+
+.noData, .noView {
+  color: #9A9A9A;
+  font-weight: 400;
+  font-size: 14px;
+}
+.textellipsis{
+  overflow:hidden; //超出的文本隐藏
+  text-overflow:ellipsis; //溢出用省略号显示
+  white-space:nowrap; //溢出不换行
+}
+.ItemTitle{
+    cursor: pointer;
+}
+.searh-btns{
+   float: right;
+}
+.el-input__suffix{
+  cursor: pointer;
 }

+ 2 - 1
src/components/dialog/index.vue

@@ -14,7 +14,7 @@
         <div class="floot" v-if="showFloor">
           <el-button type="danger" v-if="showDel" @click="deleteHandle">删 除</el-button>
           <el-button @click="closeHandle" v-if="showClose">取 消</el-button>
-          <el-button type="primary" :disabled="!!disabled" @click="enterHandle">
+          <el-button v-if="showSave" type="primary" :disabled="!!disabled" @click="enterHandle">
             {{ enterText }}
           </el-button>
         </div>
@@ -34,6 +34,7 @@ const props = withDefaults(defineProps<DialogProps>(), {
   show: false,
   hideFloor: false,
   showClose: true,
+  showSave: true,
   notSubmit: false,
   enterText: "保 存",
 });

+ 2 - 1
src/components/dialog/style.scss

@@ -45,7 +45,8 @@
   }
 
   .floot {
-    float: right;
+    // float: right;
     padding: $padding;
+    text-align: center;
   }
 }

+ 2 - 0
src/components/dialog/type.ts

@@ -7,6 +7,7 @@ export type DialogProps = {
   width?: number;
   power?: string;
   showClose?: boolean;
+  showSave?: boolean;
   showDelete?: boolean;
   cornerClose?: boolean;
 };
@@ -19,6 +20,7 @@ export const dialogPropsKeys: (keyof DialogProps)[] = [
   "enterText",
   "width",
   "power",
+  "showSave",
   "showClose",
   "showDelete",
   "cornerClose",

+ 1 - 1
src/components/head/index.vue

@@ -67,7 +67,7 @@ const show = ref(true);
 }
 
 .head-content-layer {
-  padding: 16px 154px 16px 16px;
+  padding: 16px;
   position: relative;
 
   .head-content {

+ 405 - 0
src/components/viewImg/list.vue

@@ -0,0 +1,405 @@
+<template>
+  <div>
+    <div class="demo-image__preview">
+      <el-image-viewer
+        hide-on-click-modal
+        @close="
+          () => {
+            showViewer = false;
+          }
+        "
+        :initial-index="urlindex"
+        show-progress
+        v-if="showViewer"
+        :url-list="initFileList"
+        fit="cover"
+      />
+    </div>
+    <div class="selcet flex justify-between" v-show="!show" v-if="list.length">
+      <div class="name flex content-center items-center">
+        <el-checkbox
+          class="imgCheck"
+          v-model="allChecked"
+          :indeterminate="mylist.length && mylist.length != list.length"
+          @change="changeAllItem"
+          :label="
+            mylist.length && mylist.length && mylist.length == list.length
+              ? '取消选择'
+              : '全选'
+          "
+          size="large"
+        />
+        <span class="ml-4"
+          >已选
+          <span style="color: #409eff"> {{ mylist.length }} </span> 张图片</span
+        >
+      </div>
+      <div class="buttom">
+        <el-button class="newbut" :icon="Edit" @click="handleEdit"
+          >修改</el-button
+        >
+        <el-button class="newbut" :icon="Delete" @click="handleDel"
+          >删除</el-button
+        >
+      </div>
+    </div>
+    <div class="imgList my-2 flex flex-wrap">
+      <div
+        class="maskContainer previewBox"
+        :style="{ width: width, height: height }"
+        v-for="(item, index) in list"
+        :key="index"
+      >
+        <el-checkbox
+          v-show="!show"
+          class="imgCheck"
+          v-model="item.checked"
+          @change="changeItem"
+          size="large"
+        />
+        <div
+          class="demo-image__preview h-full w-full cursor-pointer demoItem" style="overflow:hidden"
+          @click="showImgView(item.filesUrl)"
+        >
+          <el-image
+            :src="urlFilter(item.filesUrl || item.mapUrl)"
+            class="originalImg"
+            fit="cover"
+          >
+            <template #error>
+              <div class="image-slot el-image__error">
+                <el-icon :size="30"><VideoCameraFilled /></el-icon>
+              </div>
+            </template>
+          </el-image>
+        </div>
+      </div>
+      <div class="noData" v-if="list.length == 0">暂无数据</div>
+    </div>
+    <el-dialog
+      v-model="visible.show"
+      title="查看视频"
+      width="500"
+      @close="close"
+    >
+      <video
+        :src="visible.src"
+        v-if="visible.show"
+        ref="videoPlayer"
+        style="width: 100%; height: 300px"
+        controls
+        class="video-js"
+      >
+        <source :src="visible.src" type="video/mp4" />
+      </video>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, defineEmits, watchEffect, computed } from "vue";
+import { router } from "@/router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import fileImg from "@/assets/svg/file.svg";
+import { delCaseFile } from "@/store/caseFile";
+import { caseOverviewDel, show } from "@/store/case";
+import { number } from "echarts";
+import {
+  Upload,
+  DocumentAdd,
+  CircleCheck,
+  Delete,
+  Edit,
+} from "@element-plus/icons-vue";
+const caseId = computed(() => {
+  const caseId = router.currentRoute.value.params.caseId;
+  if (caseId) {
+    return Number(caseId);
+  }
+});
+const emits = defineEmits(["handleItem", "handleEdit", "refresh"]);
+const props = defineProps({
+  width: {
+    type: String,
+    default: "203px",
+    required: false,
+  },
+  height: {
+    type: String,
+    default: "203px",
+    required: false,
+  },
+  edit: {
+    type: Boolean,
+    default: false,
+    required: false,
+  },
+  delete: {
+    type: Boolean,
+    default: false,
+    required: false,
+  },
+  deleteFun: {
+    type: Boolean,
+    default: false,
+    required: false,
+  },
+  deleteShow: {
+    type: Boolean,
+    default: false,
+    required: false,
+  },
+  close: {
+    type: Boolean,
+    default: false,
+    required: false,
+  },
+  customize: {
+    type: Boolean,
+    default: false,
+  },
+  handleDel: {
+    type: Function,
+    default: false,
+  },
+  itenNumber: {
+    type: number,
+    default: 4,
+  },
+  list: {
+    type: Array,
+    default() {
+      return [];
+    },
+  },
+});
+const visible = ref({
+  show: false,
+  src: "",
+});
+const allChecked = ref(false);
+const deleteVal = ref(props.deleteShow);
+const videoPlayer = ref(null);
+const currSize = ref({ width: 0, height: 0 });
+const mylist = ref([]);
+const fileList = ref([".doc", ".docx", ".pdf", ".xls", ".xlsx"]);
+const initFileList = ref([
+  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
+  "https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg",
+]);
+const urlFilter = (url) => {
+  if (!url) return "";
+  const fileType = url.substring(url.lastIndexOf(".")).toLowerCase();
+  return fileList.value.includes(fileType) ? fileImg : url;
+};
+
+const TypeFilter = (url) => {
+  if (!url) return "";
+  const fileType = url.substring(url.lastIndexOf(".")).toLowerCase();
+  return fileType;
+};
+const verifySuffix = (imageName) => {
+  const suffix = `(bmp|jpg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp|jpeg)`;
+  const regular = new RegExp(`.*\.${suffix}`);
+  return regular.test(imageName);
+};
+watchEffect(() => {
+  if (props.list.length) {
+    let newlist = [];
+    props.list.forEach((item) => {
+      item.checked = false;
+      newlist.push(item.filesUrl || item.fileUrl || item.mapUrl);
+    });
+    mylist.value = [];
+    allChecked.value = false;
+    initFileList.value = newlist.filter((item) => {
+      return verifySuffix(item);
+    });
+  }
+});
+
+const urlindex = ref("");
+const showViewer = ref(false);
+const previewList = ref([]);
+
+const showVideoView = (src) => {
+  visible.value.show = true;
+  visible.value.src = src;
+};
+const showImgView = (src) => {
+  console.log(src, "showImgView", initFileList.value);
+  const fileType = src.substring(src.lastIndexOf(".")).toLowerCase();
+  urlindex.value = initFileList.value.findIndex((item) => item === src);
+  console.log(fileType, fileList.value.includes(fileType), "urlFilter");
+  if (fileList.value.includes(fileType)) {
+    window.open(window.location.origin + src);
+    return;
+  }
+  // url.value = src;
+  showViewer.value = true;
+  // previewList.value = [src];
+};
+const handleItem = (type, item) => {
+  if (type == "delete") {
+    del(item);
+    return;
+  }
+  emits("handleItem", type, item);
+};
+const close = () => {
+  visible.value.show = false;
+  videoPlayer.value && videoPlayer.value.pause();
+};
+const handleEdit = () => {
+  if (mylist.value.length === 0) return ElMessage.warning("请至少选择一项");
+  emits("handleEdit", mylist.value);
+  // setTimeout(() => {
+  //   mylist.value = [];
+  //   allChecked.value = false;
+  // }, 200);
+};
+const handleDel = () => {
+  if (mylist.value.length === 0) return ElMessage.warning("请至少选择一项");
+  del(mylist.value.map((item) => item.filesId));
+  // setTimeout(() => {
+  //   mylist.value = [];
+  //   allChecked.value = false;
+  // }, 200);
+};
+
+const changeItem = (checked) => {
+  mylist.value = props.list.filter((item) => item.checked);
+  allChecked.value = mylist.value.length === props.list.length;
+  console.log(mylist.value, "changeAllItem1");
+};
+const changeAllItem = (checked) => {
+  console.log(allChecked, mylist.value, "changeAllItem2", checked, props.list);
+  props.list.forEach((item) => {
+    item.checked = checked;
+  });
+  mylist.value = checked ? props.list : [];
+  // mylist.value = props.list.filter((item) => item.checked);
+};
+
+const del = async (ids) => {
+  ElMessageBox.confirm("确定删除?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    if (props.customize) return emits("handleDel", mylist.value);
+    // if (props.delete) {
+    //   //删除绘图
+    //   await caseOverviewDel(file.id);
+    // } else {
+    await delCaseFile({ caseId: caseId.value, filesIds: ids });
+    mylist.value = [];
+    allChecked.value = false;
+    // }
+    emits("refresh", ids);
+    ElMessage({
+      type: "success",
+      message: "删除成功",
+    });
+  });
+};
+</script>
+
+<style lang="scss" scoped>
+.maskContainer {
+  position: relative;
+  line-height: 0;
+  width: 66px;
+  height: 66px;
+  border-radius: 4px;
+  padding: 0px;
+  margin: 8px;
+  // border: 1px solid #d9d9d9;
+  &:hover {
+    .mask {
+      opacity: 1;
+    }
+
+    img {
+      transform: scale(1.1);
+    }
+    .originalImg{
+      transform: scale(1.2);
+    }
+  }
+  .imgCheck {
+    position: absolute;
+    left: 16px;
+    z-index: 100;
+    // top: 16px;
+  }
+  .mask {
+    transition: all 0.5s;
+    opacity: 0;
+    position: absolute;
+    border-radius: 4px;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    background: rgba($color: #000000, $alpha: 0.3);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    div {
+      margin: 3px;
+    }
+    .close {
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+    .svgBox {
+      height: 40px;
+      width: 40px;
+      background: rgb(#000, 0.3);
+      border-radius: 50%;
+      margin: 10px;
+      padding: 10px;
+      cursor: pointer;
+    }
+
+    svg {
+      // color: #fff;
+      width: 14px;
+      height: 14px;
+    }
+  }
+  .originalImg {
+    height: 100%;
+    width: 100%;
+    border-radius: 4px;
+    background: #d9d9d9;
+    transition-duration: .3s;
+
+  }
+}
+.noData {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%) rotate(0deg);
+}
+</style>
+<style lang="scss">
+.imgCheck {
+  .el-checkbox__inner {
+    height: auto;
+    border-radius: 50% !important;
+  }
+  .el-checkbox.el-checkbox--large {
+    height: auto;
+  }
+}
+</style>

+ 1 - 1
src/helper/mount.ts

@@ -94,7 +94,7 @@ export const quiskMountFactory =
     dRef?: (expose: { quit: () => void; submit: () => void }) => void
   ): Promise<T> => {
     let ref: QuiskExpose;
-
+    console.log('===', dprops);
     return new Promise((resolve) => {
       const api = {
         onQuit: async () => {

+ 9 - 6
src/hook/upload.ts

@@ -52,12 +52,15 @@ export const useUpload = <T>(props: UploadProps<T>) => {
     fileRef.value && window.open(URL.createObjectURL(fileRef.value));
   };
 
-  const upload = async (file: File) => {
-    const fileType = file.name
-      .substring(file.name.lastIndexOf("."))
-      .toUpperCase();
-    console.log('props.formats', props.maxSize, props.formats, format.value);
-    if (!props.formats.some((type) => type.toUpperCase() === fileType)) {
+  const upload = async (file: File, fileLists) => {
+    console.log('uploadts', file, fileLists);
+    if(!file.name) return;
+    const fileType = file.name && file.name.substring(file.name.lastIndexOf(".")).toUpperCase();
+    console.log('props.formats', props.maxSize, props.formats,'fileType',fileType );
+    if (!props.formats.some((type) => {
+      console.log('type', type.toUpperCase(), 'fileType', fileType, type.toUpperCase() === fileType);
+      return type.toUpperCase() === fileType;
+    })) {
       let newformat = format.value.replaceAll('.', "") as string;
       ElMessage.error(`请上传${newformat}`);
       return false;

+ 2 - 2
src/request/config.ts

@@ -70,9 +70,9 @@ export const PostUrls = [
 ];
 
 // 未认证code
-export const unAuthCode = [3004, 4008];
+export const unAuthCode = [3004, 4008, 4010, 7012];
 export const successCode = [0, "000000", 200];
 // baseURL
-export const baseURL = import.meta.env.DEV ? "/api" : "";
+export const baseURL = import.meta.env.DEV ? "" : "";
 
 export const notOpenUrls: string[] = [uploadModel, getDownloadProcess];

+ 60 - 28
src/request/index.ts

@@ -13,6 +13,8 @@ import {
   unAuthCode,
   successCode,
 } from "./config";
+// import { show } from "@/store/case";
+import { strToParams } from "@/util";
 import { router } from "@/router";
 import { RouteName } from "@/router/config";
 // import { loginShow, setLoginShow } from "@/store/system";
@@ -34,6 +36,8 @@ export const setAuthHook = (hook: AuthHook) => (getAuth = hook);
 let getAuth: AuthHook = () => ({ token: "", userId: "0", clear: () => {} });
 
 axios.defaults.baseURL = baseURL;
+let messageBoxFlag = 0 // 默认 MessageBox 未打开
+const source = axios.CancelToken.source();
 
 axios.interceptors.request.use(async (config) => {
   console.log('config.params', config.params);
@@ -54,19 +58,22 @@ axios.interceptors.request.use(async (config) => {
   if (!config.url) {
     return config;
   }
-
+  const PageParams = strToParams(window.location.search);
+  let pageType = PageParams.show == 'true' ? 'view' : 'edit';
   const { token, userId } = getAuth();
   const route = router.currentRoute.value;
   let caseId = router.currentRoute.value?.params?.caseId
   if (!token && !~notLoginUrls.indexOf(config.url)) {
-    // router.replace({ name: RouteName.login });
-    let redirect = encodeURIComponent(`${window.location.href}`);
-    window.location.href = window.location.origin + "/admin/#/login?redirect=" + redirect;
+    console.log('用户未登录', caseId);
+    router.replace({ name: RouteName.login});
+    // let redirect = encodeURIComponent(`${window.location.href}`);
+    // window.location.href = window.location.origin + "/#/login?redirect=" + redirect;
     throw "用户未登录";
   }
   config.headers.token = token;
   config.headers['caseid'] = caseId;
-  config.headers['page-type'] = 'edit';
+  // config.headers['Location'] = 'http://survey.4dkankan.com/';
+  config.headers['page-type'] = pageType;
   config.headers.userid = userId;
 
   if (~GetUrls.indexOf(config.url)) {
@@ -107,27 +114,50 @@ axios.interceptors.request.use(async (config) => {
 });
 
 const responseInterceptor = (res: AxiosResponse<any, any>) => {
-  const route = router.currentRoute.value;
   closeLoading();
-  if (res.data.code === 4010 || res.data.code === 7012 || res.data.code === 4008){
-    const { setLoginShow, loginShow } = getLogin();
-    console.log("4010",loginShow,'loginShow', res);
-    if(route.name == 'drawShareFile'){
-      setLoginShow(true)
+  let caseId = router.currentRoute.value?.params?.caseId
+  let errMsg = res.data.msg || res.data.message;
+  if (res.data.code === 40110) {
+      ElMessageBox.alert(errMsg, "提示", {
+          confirmButtonText: "去查看",
+          type: "none",
+          showClose: false,
+          callback:async () => {
+            window.location.href = window.location.origin + "/mix3d/?show=true#/abstract/"+caseId;
+          }
+        })
       throw res.data.msg;
-    }else{
-      ElMessageBox.alert("您没有访问权限", "提示", {
-        confirmButtonText: "我知道了",
-        type: "warning",
-        showClose: false
-      }).then(async () => {
-        window.open(window.location.origin + "/admin/#/statistics/scene");
-      });
-    }
-    // console.log("4010",loginShow,'loginShow', res);
-    // setLoginShow(true);
-  };
-  if (!successCode.includes(res.data.code) && res.config?.responseType != "blob") {
+
+  }
+  if (res.data.code === 40111) {
+    ElMessageBox.alert(errMsg, "提示", {
+          confirmButtonText: "返回后台",
+          type: "none",
+          showClose: false,
+          callback:async () => {
+            // window.close()
+            window.location.href = window.location.origin + "/admin/index.html";
+          }
+        })
+      throw res.data.msg;
+  }
+  if (res.data.code === 4010 || res.data.code === 7012 ){
+    if(messageBoxFlag  === 0) {
+      messageBoxFlag = 1  // 修改标记
+      ElMessageBox.alert(errMsg||"您没有访问权限", "提示", {
+          confirmButtonText: "我知道了",
+          type: "none",
+          showClose: false,
+          callback:async () => {
+            // window.close()
+            messageBoxFlag = 0  // 修改标记
+            router.replace({ name: RouteName.login});
+          }
+        })
+      }
+      throw res.data.msg;
+      return
+    }else if(!successCode.includes(res.data.code) && res.config?.responseType != "blob") {
     let errMsg = res.data.msg || res.data.message;
     openErrorMsg(errMsg);
 
@@ -135,10 +165,12 @@ const responseInterceptor = (res: AxiosResponse<any, any>) => {
       ~unAuthCode.indexOf(res.data.code) ||
       errMsg === "token已经失效,请重新登录"
     ) {
-      let redirect = encodeURIComponent(`${window.location.href}`);
-      window.location.href = window.location.origin + "/admin/#/login?redirect=" + redirect;
-      router.replace({ name: RouteName.login });
-      getAuth().clear();
+      // let redirect = encodeURIComponent(`${window.location.href}`);
+      // window.location.href = window.location.origin + "/admin/#/login?redirect=" + redirect;
+      // router.replace({ name: RouteName.login });
+      router.replace({ name: RouteName.login});
+
+      // getAuth().clear();
     }
     throw res.data.msg;
   }

+ 11 - 1
src/request/urls.ts

@@ -104,6 +104,9 @@ export const isdyrh = "/fusion/caseFusion/list";
 /**  case接口 */
 // 获取case场景列表
 export const caseSceneList = `/fusion/case/sceneList`;
+export const caseNewList = `/fusion/case/getFusionAndScene`;
+export const caseFusionList = `/service/manage/caseFusion/list`;
+export const caseFusionDel = "/service/manage/caseFusion/del";
 export const repCaseScenes = `/fusion/case/addScene`;
 export const syncInfo = `/fusion/caseLive/getTakeLookRoom`;
 export const sceneListHasAi = '/service/manage/case/sceneListHasAi'
@@ -236,10 +239,15 @@ export const saveApiOrUpdate = "/fusion/caseImg/saveOrUpdate";
 export const uploadImagesAndSave = "/fusion/caseImg/addBatch";
 export const caseApiDel = "/fusion/caseImg/delete";
 export const caseApiUpdateSort = "/fusion/caseImg/updateSort";
-export const caseOverviewAdd = "/fusion/caseTabulation/addOrUpdate";
 export const getByCaseId = "/fusion/caseTabulation/getByCaseId";
 export const getSysSetting = `/fusion/systemSetting/info`;
 export const updateSysSetting = `/fusion/systemSetting/save`;
+//现场图
+export const caseTabulationList = `/service/manage/caseTabulation/list`;
+export const caseOverviewList = `/service/manage/caseOverview/list`;
+export const caseOverviewAdd = "/fusion/caseOverview/addOrUpdate";
+export const caseTabulationAdd = "/fusion/caseTabulation/addOrUpdate";
+export const caseOverviewExport = `/fusion/caseOverview/export`;
 
 // 案件相关接口
 export const sceneList = "/service/manage/case/sceneList"; //{roomId}
@@ -256,11 +264,13 @@ export const criminalInfo = "/fusion/caseInquestCriminal/info";
 export const getMapConfig = "/fusion/notAuth/getMapConfig";
 export const saveOrUpdate = "/fusion/caseInquestCriminal/saveOrUpdate";
 export const downDocx = "/fusion/caseInquestCriminal/downDocx";
+// export const getSceneList = "/service/manage/scene/list";
 export const getSceneList = "/service/manage/scene/list";
 export const getDictFileList = "/service/manage/dict/getByKey/media-library";
 export const delDictFileList = "/service/manage/dictFile/del/media-library";
 export const getListFileList = "/service/manage/dictFile/pageList/media-library";
 export const caseaddOrUpdate = '/service/manage/case/addOrUpdate';
+export const addFusionIds = '/fusion/case/addFusionIds';
 export const getByImage = '/fusion/ai/getByImage';
 export const getFloor = "/fusion/ai/getFloor/";
 //地图相关

+ 30 - 23
src/router/config.ts

@@ -15,23 +15,23 @@ export type Route = {
 export const system: Routes = [
   {
     name: RouteName.login,
-    path: "/login",
+    path: "/login/:caseId",
     component:
-      appConstant.loginComponent || (() => import("@/view/system/index.vue")),
+    appConstant.loginComponent || (() => import("@/view/system/index.vue")),
     meta: { title: "登录" },
   },
-  {
-    name: RouteName.register,
-    path: "/register",
-    component: () => import("@/view/system/index.vue"),
-    meta: { title: "注册" },
-  },
-  {
-    name: RouteName.forget,
-    path: "/forget",
-    component: () => import("@/view/system/index.vue"),
-    meta: { title: "重置密码" },
-  },
+  // {
+  //   name: RouteName.register,
+  //   path: "/register",
+  //   component: () => import("@/view/system/index.vue"),
+  //   meta: { title: "注册" },
+  // },
+  // {
+  //   name: RouteName.forget,
+  //   path: "/forget",
+  //   component: () => import("@/view/system/index.vue"),
+  //   meta: { title: "重置密码" },
+  // },
 ];
 
 export const routes: Routes = [
@@ -47,7 +47,14 @@ export const routes: Routes = [
         path: "home/:caseId",
         component: () => import("@/view/vrmodel/index.vue"),
         
-        meta: { title: "场景管理", icon: "icon-shiyongwendang" },
+        meta: { title: "实景三维", icon: "icon-shiyongwendang" },
+      },  
+      {
+        name: RouteName.diversity,
+        path: "diversity/:caseId",
+        component: () => import("@/view/diversity/index.vue"),
+        
+        meta: { title: "多元融合", icon: "icon-shiyongwendang" },
       },
       {
         name: RouteName.abstract,
@@ -71,19 +78,19 @@ export const routes: Routes = [
         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" },
+        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" },
+        meta: { title: "三录材料", icon: "icon-tidangan" },
       },
       // {
       //   name: RouteName.other,

+ 1 - 1
src/router/index.ts

@@ -12,7 +12,7 @@ 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: 52 } });
+    router.replace({ name: RouteName.scene });
     return;
   }
   next();

+ 1 - 0
src/router/routeName.ts

@@ -26,6 +26,7 @@ export const RouteName = {
   photos: "photos",
   rollMaking: "rollmaking",
   sceneImg: "sceneimg",
+  diversity: "diversity"
 } as const;
 
 type RouteNamesType = typeof RouteName;

+ 71 - 11
src/store/case.ts

@@ -4,9 +4,13 @@ import {
   newupload,
   getMapConfig,
   caseSceneList,
+  caseNewList,
+  caseFusionList,
+  caseFusionDel,
   getCasePsw,
   repCaseScenes,
   setCasePsw,
+  addFusionIds,
   syncInfo,
   updateCaseFile,
   caseInquestInfo,
@@ -41,15 +45,21 @@ import {
   getTipsName,
   getByImage,
   caseOverviewAdd,
+  caseTabulationAdd,
   getByCaseId,
   getFloor,
   addUserOrUpdate,
+  caseTabulationList,
+  caseOverviewList,
+  caseOverviewExport,
 } 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";
+import { ElMessageBox } from "element-plus";
+import { RouteName } from "@/router/config";
 
 export type Case = {
   caseId: number;
@@ -61,6 +71,7 @@ export type Case = {
 };
 let isloadList = false;
 export const treeList = ref([]);
+export const show = ref(false);
 export const Extract = ref(false);
 export const sceneList = ref([]);
 export const caseInfoData = ref({
@@ -73,6 +84,8 @@ export const setCaseSharePWD = (params: { caseId: number; randCode: string }) =>
 
 export const setCaseaddOrUpdate = (params) =>
   axios.post(caseaddOrUpdate, params);
+export const setaddFusionIds = (params) =>
+  axios.post(addFusionIds, params);
 
 export const getAiByImage = async (params) =>
   (await axios.post<string>(getByImage, params)).data;
@@ -110,6 +123,29 @@ export const getSceneListData = async (params) =>
 
 export const getCaseInfo = async (caseId) => {
   caseInfoData.value =  (await axios.get<Case>(caseInfo, { params: { caseId } })).data
+  if(!caseInfoData.value || !caseInfoData.value.viewAuth){
+      return ElMessageBox.alert("您没有访问权限", "提示", {
+        confirmButtonText: "我知道了",
+        type: "warning",
+        showClose: false
+      }).then(async () => {
+        router.replace({ name: RouteName.login});
+        // window.open(window.location.origin + "/admin/#/statistics/scene");
+      });
+    ;
+  }
+  if(!caseInfoData.value.editAuth && !show.value){
+    show.value = true;
+    return ElMessageBox.alert("您没有编辑权限", "提示", {
+      confirmButtonText: "去查看",
+      type: "warning",
+      showClose: false
+    }).then(async () => {
+      window.open(window.location.origin + "/mix3d/?show=true#/abstract/" + caseId)
+      // router.replace({ name: RouteName.login});
+      // window.open(window.location.origin + "/admin/#/statistics/scene");
+    });
+  }
   return caseInfoData.value;
 }
   
@@ -122,13 +158,16 @@ export const casesaveOrUpDate = async (params) =>
 
 export const overviewAdd = async (params) =>
   (await axios.post<string>(caseOverviewAdd, params)).data;
+
+export const TabulationAdd = async (params) =>
+  (await axios.post<string>(caseTabulationAdd, params)).data;
 export const getcaseByCaseId = async (caseId) =>
   (await axios.get(getByCaseId, { params: { caseId } })).data;
 export const caseOverview = async (caseId) =>
   (await axios.get('/fusion/caseOverview/getByCaseId', { params: { caseId } })).data;
 
-export const caseOverviewDel = async (id) =>
-  (await axios.post('/fusion/caseTabulation/del', { id } )).data;
+export const caseOverviewDel = async ({tabulationId, overviewId, type}) =>
+  (await axios.post(type=='方位图'?'/fusion/caseTabulation/del':'/fusion/caseOverview/del', { tabulationId, overviewId } )).data;
 
 
 export const updateCaseInfo = async (caseFile: CaseFile) =>
@@ -141,11 +180,19 @@ export const getCaseSceneList = async (caseId: number, refresh = false): Promise
   if (!refresh && sceneList.value.length) {
       return sceneList.value;
   }
-  let mylist = (await axios.get(caseSceneList, { params: { caseId } })).data;
+  let mylist = (await axios.get(caseSceneList, { params: { caseId, } })).data;
   sceneList.value = mylist.filter(item => item.type != 3);
   return sceneList.value;
 };
-
+export const getCaseList = async (params): Promise<Scene[]> => {
+  return (await axios.get(caseNewList, {params})).data;
+};
+export const getCaseFusionList = async (params): Promise<Scene[]> => {
+  return (await axios.post(caseFusionList, params)).data;
+};
+export const getCaseFusionImgList = async (params): Promise<Scene[]> => {
+  return (await axios.post(params.type?caseTabulationList:caseOverviewList, params)).data;
+};
 export const getFilepageList = async (params): Promise<Scene[]> => {
   return (await axios.post(getListFileList, params)).data;
 };
@@ -202,6 +249,9 @@ export const caseImgList = (caseId: number, orderBy: string | null) =>
 
 export const caseDel = (id: number) => axios.post(caseApiDel, { id });
 
+export const casefusionDel = (fusionId: number) => axios.post(caseFusionDel, { fusionId });
+
+
 export const saveOrUpDate = (params: CaseImg) =>
   axios.post(saveApiOrUpdate, { ...params });
 
@@ -264,6 +314,11 @@ export const uploadNewFile = (data) => axios<undefined>({
     url: newFileupload,
     data: data,
   });
+export const caseExportImg = (data) => axios<undefined>({
+    method: "POST",
+    url: caseOverviewExport,
+    data: data,
+  });
 
 export const uploadFiles = (data) => axios<undefined>({
   method: "POST",
@@ -319,7 +374,7 @@ export const getSceneListTree = (list = sceneList.value) => {
     1: [],//mesh
   };
   list.map((item) => {
-    if(item.type == 2 || item.type == 5 ) {
+    if(item.sceneType == 2 || item.sceneType == 5 || item.type == 2 || item.type == 5 ) {
       myData[0].push(item.num)
     }else{
       myData[1].push(item.num)
@@ -330,6 +385,9 @@ export const getSceneListTree = (list = sceneList.value) => {
 
 export const getUrlSrc = (item, caseId) => {
   let token = user.value.token
+  if(!item.viewAuth){
+    return ''
+  }
   let SceneType = {
     0: `/spg.html?m=${item.num}`,
     1: `/spg.html?m=${item.num}`,
@@ -344,18 +402,20 @@ export const getUrlSrc = (item, caseId) => {
     99: `/code/index.html?caseId=${caseId}&single#/show`,
   };
   let SceneEditType = {
-    0: `/epg.html?m=${item.num}&token=${token}`,
-    1: `/epg.html?m=${item.num}&token=${token}`,
+    0: `/spg.html?m=${item.num}&token=${token}`,
+    1: `/spg.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}`,
+    4: `/spg.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`,
+    6: `/spg.html?m=${item.num}&token=${token}`,
+    7: `/spg.html?m=${item.num}&token=${token}`,
+    99: `/code/index.html?caseId=${item.fusionId || caseId}&single#/show`,
+    100: `/code/index.html?caseId=${item.fusionId || caseId}&pure=1#/show`,
   };
+  
   console.log(sceneType.value, 'sceneType.value')
   return sceneType.value == 'view' ? SceneEditType[item.type]: SceneEditType[item.type];
 }

+ 4 - 1
src/store/system.ts

@@ -7,6 +7,7 @@ import {
   newupload,
   userInfo,
 } from "@/request";
+import { getCaseInfo } from "@/store/case";
 import { encodePwd } from "@/util";
 import { user } from "./user";
 import { refreshRole } from "./role";
@@ -91,6 +92,7 @@ function randomWord(randomFlag, min, max?) {
   }
   return str;
 }
+
 function encodeStr(str, strv = ''): string {
   const NUM = 2;
   const front = randomWord(false, 8);
@@ -108,7 +110,6 @@ function encodeStr(str, strv = ''): string {
 
   return front + str2 + middle + str1 + end;
 }
-
 export type LoginProps = {
   phoneNum?: string;
   userName?: string;
@@ -135,12 +136,14 @@ export const setLoginShow = (show: boolean) => {
 
 
 export const login = async (props) => {
+  console.log("login", props, encodeStr(window.btoa(props.password)));
   const res = await axios.post(userLogin, {
     userName: props.username,
     username: props.username,
     password: encodeStr(window.btoa(props.password))
   });
   user.value.token = res.data.token;
+  await getCaseInfo(props.caseId);
   localStorage.setItem("token", user.value.token);
   loginShow.value = false;
   // refreshUserInfo(res.data);

+ 97 - 0
src/util/browser.ts

@@ -0,0 +1,97 @@
+// 解析版本号为数组,便于比较
+function parseVersion(versionString) {
+  return versionString.split('.').map(Number);
+}
+
+// 比较版本号 (a > b 返回1, a == b返回0, a < b返回-1)
+function compareVersions(versionA, versionB) {
+  const aParts = parseVersion(versionA);
+  const bParts = parseVersion(versionB);
+  const maxLength = Math.max(aParts.length, bParts.length);
+  
+  for (let i = 0; i < maxLength; i++) {
+    const a = aParts[i] || 0;
+    const b = bParts[i] || 0;
+    
+    if (a > b) return 1;
+    if (a < b) return -1;
+  }
+  
+  return 0;
+}
+
+// 获取浏览器信息
+function getBrowserInfo() {
+  const userAgent = navigator.userAgent.toLowerCase();
+  let browserInfo = {
+    name: 'Unknown',
+    version: 'Unknown',
+    versionParts: []
+  };
+
+  // 检测浏览器类型和版本
+  if (/edge\/([\d.]+)/.test(userAgent)) {
+    browserInfo.name = 'Edge';
+    browserInfo.version = userAgent.match(/edge\/([\d.]+)/)[1];
+  } else if (/chrome\/([\d.]+)/.test(userAgent) && !/edge/.test(userAgent)) {
+    browserInfo.name = 'Chrome';
+    browserInfo.version = userAgent.match(/chrome\/([\d.]+)/)[1];
+  } else if (/firefox\/([\d.]+)/.test(userAgent)) {
+    browserInfo.name = 'Firefox';
+    browserInfo.version = userAgent.match(/firefox\/([\d.]+)/)[1];
+  } else if (/safari\/([\d.]+)/.test(userAgent) && !/chrome/.test(userAgent)) {
+    browserInfo.name = 'Safari';
+    // Safari的版本号在version/后面
+    browserInfo.version = userAgent.match(/version\/([\d.]+)/)[1];
+  } else if (/msie ([\d.]+)/.test(userAgent) || /rv:([\d.]+)/.test(userAgent)) {
+    browserInfo.name = 'Internet Explorer';
+    browserInfo.version = userAgent.match(/(msie |rv:)([\d.]+)/)[2];
+  }
+
+  // 解析版本号为数组
+  if (browserInfo.version !== 'Unknown') {
+    browserInfo.versionParts = parseVersion(browserInfo.version);
+  }
+  
+  // 添加版本比较方法
+  browserInfo.isGreaterThan = function(version) {
+    return compareVersions(this.version, version) > 0;
+  };
+  
+  browserInfo.isLessThan = function(version) {
+    return compareVersions(this.version, version) < 0;
+  };
+  
+  browserInfo.isEqual = function(version) {
+    return compareVersions(this.version, version) === 0;
+  };
+  
+  browserInfo.isGreaterOrEqual = function(version) {
+    return compareVersions(this.version, version) >= 0;
+  };
+  
+  browserInfo.isLessOrEqual = function(version) {
+    return compareVersions(this.version, version) <= 0;
+  };
+
+  return browserInfo;
+}
+
+// 使用示例
+export const browser = getBrowserInfo();
+console.log(`浏览器: ${browser.name} ${browser.version}`);
+
+// 版本比较示例
+if (browser.name === 'Chrome') {
+  if (browser.isLessThan('90.0.0')) {
+    console.log('你的Chrome浏览器版本过低,请升级到90.0.0或更高版本');
+  } else {
+    console.log('你的Chrome浏览器版本符合要求');
+  }
+}
+
+if (browser.name === 'Firefox') {
+  if (browser.isGreaterOrEqual('88.0')) {
+    console.log('你的Firefox浏览器支持最新特性');
+  }
+}

+ 110 - 0
src/util/index.ts

@@ -415,3 +415,113 @@ export function copyTextToClipboard(input: string, { target = document.body }: O
   }
   return isSuccess;
 }
+function getExtension (name) {
+  if(!name) return
+  return name.substring(name.lastIndexOf("."))
+}
+// 根据URL下载图片
+export function downloadFile(url, name) {
+  console.log("downloadFile", url, name)
+    let image = new Image();
+    image.setAttribute("crossOrigin", "anonymous");
+    image.src = url;
+    image.onload = () => {
+        let canvas = document.createElement("canvas");
+        canvas.width = image.width;
+        canvas.height = image.height;
+        let ctx = canvas.getContext("2d");
+        ctx.drawImage(image, 0, 0, image.width, image.height);
+        canvas.toBlob(blob => {
+            let url = URL.createObjectURL(blob);
+            let a = document.createElement("a");
+            if(name) {
+              a.download = name;
+            }
+            a.href = url;
+            a.click();
+            a.remove();
+            // 用完释放URL对象
+            URL.revokeObjectURL(url);
+        });
+    };
+}
+function openWindow(
+  url: string,
+  opt?: { target?: string; noopener?: boolean; noreferrer?: boolean },
+) {
+  const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
+  const feature: string[] = [];
+
+  noopener && feature.push('noopener=yes');
+  noreferrer && feature.push('noreferrer=yes');
+
+  window.open(url, target, feature.join(','));
+}
+export function downloadByUrl({
+  url,
+  target = '_blank',
+  fileName,
+}: {
+  url: string;
+  target?: string;
+  fileName?: string;
+}): boolean {
+  const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
+  const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1;
+  console.log("downloadFile", url, fileName)
+
+  if (/(iP)/g.test(window.navigator.userAgent)) {
+    console.error('Your browser does not support download!');
+    return false;
+  }
+  if (isChrome || isSafari) {
+    const link = document.createElement('a');
+    link.href = url;
+    link.target = target;
+    let newfileName = fileName + getExtension(url);
+    if (link.download !== undefined) {
+      link.download = fileName ? newfileName : url.substring(url.lastIndexOf('/') + 1, url.length);
+    }
+    
+    if (document.createEvent) {
+      const e = document.createEvent('MouseEvents');
+      e.initEvent('click', true, true);
+      link.dispatchEvent(e);
+      return true;
+    }
+  }
+  if (url.indexOf('?') === -1) {
+    url += '?download';
+  }
+
+  openWindow(url, { target });
+  return true;
+}
+export const isImage = (url) => {
+  return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url);
+};
+
+export const recursiveSearch = (nodes) => {
+    // 如果没有节点,直接返回
+    if (!nodes || !nodes.length) return;
+    
+    // 遍历当前节点
+    for (const node of nodes) {
+      // 检查是否符合条件
+      if (node.caseFilesList && node.caseFilesList.length > 0 && node.caseFilesList.some(file => file.filesUrl)) {
+        // 如果只需要第一个,直接返回
+            return true; // 终止递归
+      }
+      
+      // 如果有子节点,递归查询
+      if (node.childrenList && node.childrenList.length) {
+        const found = recursiveSearch(node.childrenList);
+        // 如果已经找到第一个,终止递归
+        if(found){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }

+ 404 - 369
src/view/abstract/index.vue

@@ -1,360 +1,358 @@
 <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">
-        <div class="subtitle">案件信息</div>
-        <el-row :gutter="20">
+  <div class="abstractContent">
+    <div v-if="show" class="showAbstract">
+      <div class="abstractTitle">案件信息</div>
+      <div class="list">
+        <el-row :gutter="64">
           <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>
+            <div class="item textellipsis">
+              <span>案件名称:</span>
+              <span :title="bindExample.caseTitle">{{ caseInfoData.caseTitle || bindExample.caseTitle }}</span>
+            </div>
           </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>
+            <div class="item textellipsis">
+              <span>勘验号:</span>
+              <span>{{ caseInfoData.knumber }}</span>
+            </div>
           </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"
-                  :label="item"
-                  :value="item"
-                />
-              </el-select>
-            </el-form-item>
+            <div class="item textellipsis">
+              <span>案件类别:</span>
+              <span>{{ caseInfoData.caseCategory }}</span>
+            </div>
           </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>
+            <div class="item textellipsis">
+              <span>案发时间:</span>
+              <span>{{ caseInfoData.crimeTime }}</span>
+            </div>
           </el-col>
           <el-col :span="8">
-            <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>
+            <div class="item textellipsis">
+              <span>案发区域:</span>
+              <span>{{ caseInfoData.caseRegion && caseInfoData.caseRegion.join('/') }}</span>
+            </div>
           </el-col>
           <el-col :span="8">
-            <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>
+            <div class="item textellipsis">
+              <span>案件地点:</span>
+              <span :title="caseInfoData.caseAddress">{{ caseInfoData.caseAddress }}</span>
+            </div>
           </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 :span="8">
+            <div class="item textellipsis">
+              <span>是否命案:</span>
+              <span>{{ caseInfoData.homicideCase?'是':'否' }}</span>
+            </div>
           </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 :span="8">
+            <div class="item textellipsis">
+              <span>是否刑案:</span>
+              <span>{{ caseInfoData.criminalCase?'是':'否' }}</span>
+            </div>
           </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>
+            <div class="item textellipsis">
+              <span>经纬度:</span>
+              <span>{{ bindExample.latAndLongs }}</span>
+            </div>
           </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"
-                @click="submitForm(exampleRef)"
-              >
-                保存
-              </el-button>
-            </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>
+      <div class="abstractTitle" style="margin-top: 48px">勘验信息</div>
+      <div class="list">
+        <el-row :gutter="64">
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>指挥中心电话时间:</span>
+              <span>{{ ruleForm.commandTime }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>接警时间:</span>
+              <span>{{ ruleForm.alarmTime }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>报警人:</span>
+              <span :title="ruleForm.alarmName">{{ ruleForm.alarmName }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>现场勘验单位:</span>
+              <span :title="ruleForm.inquestDept">{{ ruleForm.inquestDept }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>指派/报告单位:</span>
+              <span :title="ruleForm.assignDept">{{ ruleForm.assignDept }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>指派方式:</span>
+              <span>{{ ruleForm.assignType }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>勘验时间:</span>
+              <span>{{ ruleForm.times && ruleForm.times.join('~') }}</span>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item textellipsis">
+              <span>勘验地点:</span>
+              <span :title="ruleForm.inquestAddress">{{ ruleForm.inquestAddress }}</span>
+            </div>
+          </el-col>
         </el-row>
+      </div>
+    </div>
+    <div v-else class="abstract">
+      <el-form
+        ref="exampleRef"
+        :model="bindExample"
+        label-position="top"
+        :rules="rules"
+        :disabled="show"
+        label-width="auto"
+        class="demo-ruleForm"
+        size="default"
+        status-icon
+      >
+        <div class="form-content">
+          <div class="subtitle">案件信息</div>
+          <el-row :gutter="64">
+            <el-col :span="8">
+              <el-form-item label="案件名称" required prop="caseTitle">
+                <el-input
+                  v-model="caseInfoData.caseTitle"
+                  placeholder="请输入"
+                  disabled
+                  show-word-limit
+                  @blur="submit"
+                  maxlength="100"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="勘验号" prop="knumber">
+                <el-input
+                  v-model="bindExample.knumber"
+                  placeholder="请输入"
+                  show-word-limit
+                  @blur="submit"
+                  maxlength="100"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="案件类别" required>
+                <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-col>
+            <el-col :span="8">
+              <el-form-item label="案发时间" required prop="crimeTime">
+                <el-date-picker
+                  v-model="bindExample.crimeTime"
+                  type="datetime"
+                  :clearable="false"
+                  @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-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
+                >
+                  <template #append>
+                    <el-button :icon="Search" @click="searchAMapAddress" />
+                  </template>
+                </el-input>
+              </el-form-item>
+            </el-col>
 
-        <!-- <el-form-item>
+            <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-col>
+          </el-row>
+        </div>
+      </el-form>
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        label-position="top"
+        :rules="rules"
+        :disabled="show"
+        label-width="auto"
+        class="demo-ruleForm"
+        size="default"
+        status-icon
+      >
+        <div class="form-content">
+          <div class="subtitle">勘验信息</div>
+          <el-row :gutter="64">
+            <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"
@@ -363,13 +361,9 @@
                 保存
               </el-button>
             </el-form-item> -->
-      </div>
-    </el-form>
-    <!-- </el-tab-pane> -->
-    <!-- <el-tab-pane label="勘验信息" name="2"> -->
-
-    <!-- </el-tab-pane> -->
-    <!-- </el-tabs> -->
+        </div>
+      </el-form>
+    </div>
   </div>
 </template>
 
@@ -390,6 +384,8 @@ import {
   casesaveOrUpDate,
   getcaseInDate,
   getCaseInfo,
+  caseInfoData,
+  show,
 } from "@/store/case";
 const selectSetting = ref<CascaderProps>({
   value: "name",
@@ -420,7 +416,7 @@ const ruleForm = ref({
 });
 const bindExample = ref({
   caseTitle: "",
-  caseNum: "",
+  knumber: "",
   mapId: "",
   mapUrl: "",
   caseCategory: "",
@@ -457,10 +453,9 @@ onMounted(async () => {
   if (caseInfo) {
     ruleForm.value = caseData || {};
     bindExample.value = JSON.parse(JSON.stringify(caseInfo));
-    bindExample.value.latAndLongs = bindExample.value.latAndLong && 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) => ({
@@ -475,7 +470,7 @@ const rules = reactive({
       trigger: "blur",
     },
   ],
-  caseNum: [
+  knumber: [
     {
       required: true,
       message: "请输入",
@@ -489,30 +484,46 @@ const rules = reactive({
       trigger: "change",
     },
   ],
-  caseCategory: [{ required: true, message: "请选择", trigger: "change" }],
 });
 const showModal = ref(false);
-const submit = async () => {
-  if (activeName.value == "1") {
-    if (!bindExample.value.caseTitle || !bindExample.value.caseTitle.trim()) {
-      ElMessage.error("案件名称不能为空");
-      throw "案件名称不能为空";
+function debounce (fn, delay = 300){
+    let timer = null
+    return function (...args) {
+        clearTimeout(timer)
+        timer = setTimeout(()=>{
+            fn.call(this, ...args)
+        }, delay);
     }
-    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 });
+}
+const submits = async () => {
+  if (show.value) return;
+
+  // 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,
+    caseTitle: caseInfoData.value.caseTitle,
+    latAndLong,
+    caseId: caseId.value,
+  });
+  ruleForm.value.times ??= [];
+  ruleForm.value.commandTime ??= '';
+  ruleForm.value.alarmTime ??= '';
+  await casesaveOrUpDate({ ...ruleForm.value, caseId: caseId.value });
+  // } else {
+  // }
+  await getCaseInfo(caseId.value);
   ElMessage.success("保存成功");
 };
+const submit = debounce(submits, 300)
+
 const submitForm = async (formEl) => {
   if (!formEl) return;
   await formEl.validate((valid, fields) => {
@@ -543,16 +554,20 @@ const searchAMapAddress = async () => {
   if (!data?.search) return;
   bindExample.value.mapId = data.mapId;
   bindExample.value.mapUrl = data.mapUrl;
-  bindExample.value.caseAddress = data.search.text;
+  bindExample.value.caseAddress = data.search.text || `${data.search.lng},${data.search.lat}`;
   bindExample.value.latAndLong = `${data.search.lat},${data.search.lng}`;
   bindExample.value.latAndLongs = `${data.search.lng},${data.search.lat}`;
+  submit();
 };
 </script>
 <style lang="scss" scoped>
 .abstract {
   height: 100%;
+  padding: 0 48px 0 48px;
+
+  overflow-y: scroll;
   .el-form-item--label-top {
-    margin-bottom: 14px;
+    margin-bottom: 24px;
   }
   .form-content {
     // height: calc(100vh - 175px);
@@ -572,4 +587,24 @@ const searchAMapAddress = async () => {
     margin: 36px 0 16px 0;
   }
 }
+.abstractContent {
+  .showAbstract {
+    padding: 48px;
+    .abstractTitle {
+      font-weight: 400;
+      font-size: 24px;
+      color: rgba(0, 0, 0, 0.85);
+      line-height: 36px;
+    }
+    .list{
+      .item{
+        font-weight: 400;
+        font-size: 14px;
+        line-height: 30px;
+        color: rgba(0,0,0,0.85);
+        margin-top: 24px;
+      }
+    }
+  }
+}
 </style>

+ 3 - 1
src/view/case/draw/selectMapImagess.vue

@@ -240,7 +240,9 @@ defineExpose<QuiskExpose>({
     return new Promise<MapImage>((resolve) => {
         const info = getblc();
         let firstElement = document.querySelector('.leaflet-control-container');
-        firstElement.style.visibility = 'hidden';
+        if(firstElement){
+          firstElement.style.visibility = 'hidden';
+        }
         console.log('searchInfo',info, searchInfo.value, mapEl.value);
         if (mapEl.value) {
         // const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;

+ 2 - 1
src/view/case/quisk.ts

@@ -57,8 +57,9 @@ export const selectFuseImage = quiskMountFactory(SelectFuseImage, {
   title: "选择户型图",
   width: 1300,
 })<FuseImage>;
+console.log('params.testMap', params)
 
-export const selectMapImage = quiskMountFactory(params.testMap?SelectMapImage:SelectMapleaftImages, {
+export const selectMapImage = quiskMountFactory(!params.testMap?SelectMapImage:SelectMapleaftImages, {
   title: "选择地址",
   width: 588,
 })<MapImage>;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 566 - 505
src/view/case/records/index.vue


+ 1 - 1
src/view/case/records/manifest.vue

@@ -321,7 +321,7 @@ onMounted(() => {
 
 <style lang="scss">
 .records {
-  max-width: 1280px;
+  max-width: 1024px;
   margin: 0 auto;
   padding: 20px 0;
 

+ 121 - 0
src/view/diversity/downloadLog.vue

@@ -0,0 +1,121 @@
+<template>
+  <com-head :options="[{ name: '下载记录', value: '2' }]" showCtrl>
+    <el-form label-width="84px">
+      <el-form-item label="所属架构:">
+        <com-company v-model="state.query.deptId" />
+      </el-form-item>
+      <el-form-item label="用户姓名:">
+        <el-input v-model="state.query.nickName" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="用户账号:">
+        <el-input v-model="state.query.userName" placeholder="请输入手机号"></el-input>
+      </el-form-item>
+      <el-form-item label="下载时间:">
+        <el-date-picker
+          type="datetimerange"
+          format="YYYY-MM-DD HH:mm:ss"
+          v-model="createTime"
+          placeholder="请选择"
+          :defaultTime="defaultTime"
+          style="width: 100%"
+        />
+      </el-form-item>
+      <el-form-item label="场景标题:">
+        <el-input v-model="state.query.sceneTitle" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="场景码:">
+        <el-input v-model="state.query.sceneNum" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="SN码:">
+        <el-input v-model="state.query.snCode" placeholder="请输入"></el-input>
+      </el-form-item>
+
+      <el-form-item class="searh-btns" style="grid-area: 1/4/4/5">
+        <el-button type="primary" @click="refresh">查询</el-button>
+        <el-button type="primary" plain @click="queryReset">重置</el-button>
+      </el-form-item>
+    </el-form>
+  </com-head>
+
+  <div class="body-layer" style="padding-top: 8px">
+    <el-table
+      class="user-table"
+      :data="state.table.rows"
+      style="width: 100%; max-height: 480px"
+      size="large"
+    >
+      <el-table-column label="序号" width="70" v-slot:default="{ $index }">
+        <div style="text-align: center">
+          {{ state.pag.size * (state.pag.currentPage - 1) + $index + 1 }}
+        </div>
+      </el-table-column>
+      <el-table-column label="组织名称" prop="deptName"></el-table-column>
+      <el-table-column label="组织类型" prop="deptLevelStr"></el-table-column>
+      <el-table-column label="用户姓名" prop="nickName"></el-table-column>
+      <el-table-column label="用户账号" prop="userName"></el-table-column>
+      <el-table-column label="下载时间" prop="createTime"></el-table-column>
+      <el-table-column label="场景标题" prop="sceneTitle"></el-table-column>
+      <el-table-column label="场景码" prop="sceneNum"></el-table-column>
+      <el-table-column label="SN码" prop="snCode"></el-table-column>
+    </el-table>
+
+    <com-pagination
+      @size-change="changPageSize"
+      @current-change="changPageCurrent"
+      :current-page="state.pag.currentPage"
+      :page-size="state.pag.size"
+      :total="state.pag.total"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { usePagging } from "@/hook/pagging";
+import comHead from "@/components/head/index.vue";
+import comCompany from "@/components/company-select/index.vue";
+import comPagination from "@/components/pagination/index.vue";
+import { getDownloadQuoteScene } from "@/store/scene";
+import { ref, watchEffect } from "vue";
+import { dateFormat } from "@/util";
+
+const {
+  state,
+  queryReset: queryResetRaw,
+  refresh,
+  changPageCurrent,
+  changPageSize,
+} = usePagging({
+  get: getDownloadQuoteScene,
+  paramsTemlate: {
+    nickName: "",
+    deptId: "",
+    userName: "",
+    startCreateTime: "",
+    endCreateTime: "",
+    sceneTitle: "",
+    sceneNum: "",
+    snCode: "",
+  },
+});
+
+const defaultTime: [Date, Date] = [
+  new Date(2000, 1, 1, 0, 0, 0),
+  new Date(2000, 2, 1, 23, 59, 59),
+];
+const createTime = ref<Date[] | null>(null);
+watchEffect(() => {
+  if (createTime.value && createTime.value.length === 2) {
+    state.query.startCreateTime = dateFormat(createTime.value[0], "yyyy-MM-dd hh:mm:ss");
+    state.query.endCreateTime = dateFormat(createTime.value[1], "yyyy-MM-dd hh:mm:ss");
+  } else {
+    state.query.startCreateTime = null as any;
+    state.query.endCreateTime = null as any;
+  }
+});
+const queryReset = () => {
+  queryResetRaw();
+  createTime.value = null;
+};
+</script>
+
+<style scoped lang="scss"></style>

+ 31 - 0
src/view/diversity/editModel.vue

@@ -0,0 +1,31 @@
+<template>
+  <el-form ref="form" label-width="84px">
+    <el-form-item label="模型名称">
+      <el-input
+        v-model="bindModel.modelTitle"
+        maxlength="50"
+        placeholder="请输入模型名称"
+      />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+import { ModelScene, setModelScene } from "@/store/scene";
+import { ElMessage } from "element-plus";
+import { QuiskExpose } from "@/helper/mount";
+
+const props = defineProps<{ model: ModelScene }>();
+const bindModel = ref<ModelScene>({ ...props.model });
+
+defineExpose<QuiskExpose>({
+  async submit() {
+    if (!bindModel.value.modelTitle || !bindModel.value.modelTitle.trim()) {
+      ElMessage.error("模型名称不能为空");
+      throw "模型名称不能为空";
+    }
+    await setModelScene(bindModel.value);
+  },
+});
+</script>

+ 280 - 0
src/view/diversity/index.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="scene flex justify-end content-center">
+    <div class="scene-list" v-if="list.length">
+      <div class="scene-title" v-show="!show">
+        <el-button class="newbut" @click="handleAdd">
+          <i class="iconfont icon-import" style="margin-right: 5px"></i>
+          导入
+        </el-button>
+      </div>
+      <div class="list" v-if="active">
+        <div
+          class="listItem py-4"
+          v-for="(item, index) in list"
+          :key="index"
+          :class="{
+            active: item.fusionId === sceneItem.fusionId,
+            disabled: !item.viewAuth,
+          }"
+          @click="handleItem(item)"
+        >
+          <div class="anmc" :title="item.title || item.caseTitle">
+            {{ item.title || item.caseTitle }}
+          </div>
+          <div
+            class="cursor-pointer"
+            v-if="!show && (item.editAuth || caseInfoData.isAuthor) && item.fusionId === sceneItem.fusionId"
+            quaternary
+            type="primary"
+          >
+            <el-icon v-if="item.editAuth" style="margin-right: 34px">
+              <EditPen @click="handleAdddyrh(item)" />
+            </el-icon>
+            <el-icon>
+              <CircleClose @click="handlegotoelT(item)" />
+            </el-icon>
+          </div>
+          <div class="cursor-pointer" v-show="!show" v-else-if="!item.viewAuth && caseInfoData.isAuthor">
+            <el-icon>
+              <CircleClose @click.stop="handlegotoelT(item)" />
+            </el-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div
+      class="app-scene"
+      ref="sceneRef"
+      :style="{
+        height: '100%',
+        width: list.length == 0?'100%':'',
+      }"
+    >
+      <iframe
+        v-if="list && list.length && sceneURL"
+        :src="sceneURL"
+        frameborder="0"
+        :style="{ width: '100%', height: '100%' }"
+      ></iframe>
+      <div class="import" v-else @click="handleAdd" v-show="!show">
+        <el-icon size="26" color="#26559B">
+          <i style="font-size: 26px; color: #26559B" class="iconfont icon-import"></i></el-icon>
+        <div class="name">导入</div>
+      <div class="noData" >
+        <div class="name" style="margin-top: 25px;">暂无数据</div>
+      </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  getCaseSceneList,
+  getCaseList,
+  getUrlSrc,
+  getSceneListTree,
+  setCaseaddOrUpdate,
+  getCaseFusionList,
+  casefusionDel,
+  show,
+  setaddFusionIds ,
+  caseInfoData,
+} from "@/store/case";
+import { router } from "@/router";
+import comSelect from "@/components/company-select/index.vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import List from "./list.vue";
+import SceneContent from "./sceneContent.vue";
+import { Plus, DocumentAdd, EditPen } from "@element-plus/icons-vue";
+import ModelContent from "./modelContent.vue";
+import { useScenePaggingParams } from "./pagging";
+import { computed, ref, onMounted } from "vue";
+import { tableModelScene } from "./quisk";
+const active = ref(true);
+const list = ref([
+]);
+const isEdit = ref(false);
+const showModal = ref(false);
+const sceneURL = ref("");
+const sceneNum = ref("");
+const sceneItem = ref({
+  fusionId: "",
+});
+const params = useScenePaggingParams();
+const caseId = computed(() => router.currentRoute.value?.params?.caseId);
+onMounted(() => {
+  geiList();
+});
+async function geiList(refresh) {
+  list.value = await getCaseList({
+    caseId: caseId.value,
+    type: 'fusion',
+    pageNum: 1,
+    pageSize: 100000,
+  })||[];
+  // list.value = getList.filter((item) => item.caseId);
+  console.log("getCaseFusionList", list.value);
+  sceneItem.value = (list.value && list.value.find(ele => ele.viewAuth)) || {};
+  sceneURL.value = getUrlSrc({ ...sceneItem.value, type: 100 }, caseId.value);
+  console.log("res", list.value, sceneURL.value);
+}
+function handlegotoEdit(record) {
+  if (record.isLaser) return;
+
+  let url =
+    record.type == 2 || record.type == 5
+      ? `/mega/index.html?m=${record.num}`
+      : `/epg.html?m=${record.num}`;
+  window.open(url.replace("spg", "epg"));
+}
+function handleAdddyrh(record) {
+  let url = `/code/index.html?caseId=${record.fusionId}#/fuseEdit/merge`;
+  window.open(url);
+}
+async function handlegotoelT(record) {
+  isEdit.value = true;
+  ElMessageBox.confirm("确定要移除当前多元融合吗?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    // await casefusionDel(record.fusionId);
+    let newlist = list.value.filter((item) => item.fusionId !== record.fusionId);
+    const apiData = {
+      caseId: caseId.value,
+      fusionIds: newlist.map(item => item.fusionId),
+    };
+    await setaddFusionIds(apiData)
+    list.value = list.value.filter((item) => item.fusionId !== record.fusionId);
+    sceneItem.value = (list.value && list.value.find(ele => ele.viewAuth)) || {};
+    sceneURL.value = getUrlSrc({ ...sceneItem.value, type: 100 }, caseId.value);
+  });
+  // if (record.inFusion || (await confirm("确定要移除当前场景吗?"))) {
+  //   await casefusionDel(record.fusionId);
+  //   list.value = list.value.filter((item) => item.fusionId !== record.fusionId);
+  //   sceneItem.value = (list.value && list.value[0]) || {};
+  //   sceneURL.value = getUrlSrc({ ...sceneItem.value, type: 99 }, caseId.value);
+  //   // submitForm();
+  // }
+}
+async function submitForm() {
+  isEdit.value = false;
+  let sublist = getSceneListTree(list.value);
+  const apiData = {
+    caseId: caseId.value,
+    sceneNumParam: sublist,
+  };
+  setCaseaddOrUpdate(apiData)
+    .then((res) => {
+      geiList(true);
+      ElMessage({
+        message: "操作成功",
+        type: "success",
+      });
+    })
+    .catch((errr) => {
+      console.log("setCaseaddOrUpdateerrr", errr);
+      return geiList(true);
+    });
+}
+async function handleAdd() {
+  let fusionIds = [], noEditList = []
+  list.value.map((item) => {
+    fusionIds.push(item.fusionId);
+    if(!item.editAuth) {
+      noEditList.push(item.fusionId)
+    }
+  });
+  console.log("handleAdd", fusionIds, noEditList);
+  let val = await tableModelScene({ fusionIds, noEditList });
+  geiList();
+  if (val) {
+    console.log("刷新列表");
+  }
+}
+function handleItem(item) {
+  if(!item.viewAuth) return ElMessage.error("您没有查看权限,请联系管理员开通");
+  sceneItem.value = item;
+  sceneURL.value = getUrlSrc({ ...item, type: 100 }, caseId.value);
+}
+</script>
+
+<style scoped lang="scss">
+.scene {
+  height: 100%;
+
+  .scene-list {
+    width: calc(var(--leftwidth));
+    height: 100%;
+    overflow-y: scroll;
+    overflow-x: hidden;
+    &::-webkit-scrollbar {
+      display: none;
+    }
+    .scene-title {
+      height: 64px;
+      padding: 24px 48px 0 48px;
+      .newbut{
+        position: relative;
+        left: -16px;
+      }
+    }
+    .list {
+      .listItem {
+        width: calc(var(--leftwidth) - 0px);
+        padding: 25px 48px;
+      }
+      .active {
+        background: rgba(69, 144, 255, 0.1);
+      }
+      .disabled {
+        color: rgba(0, 0, 0, 0.3);
+        cursor:not-allowed;
+      }
+    }
+  }
+  .app-scene {
+    background: #f5f5f5;
+    width: calc(100% - var(--leftwidth));
+    position: relative;
+  border-right: 48px solid #fff;
+  border-bottom: 48px solid #fff;
+    .import {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      text-align: center;
+      margin-right: 20px;
+      padding-top: 40px;
+      height: 148px;
+      width: 148px;
+      text-align: center;
+      background: #fafafa;
+      cursor: pointer;
+      .noData{
+        position: absolute;
+        top: 140px;
+        width: 100%;
+        // transform: translate(50%, -50%);
+      }
+    }
+  }
+  .anmc {
+    width: 100%;
+    margin-bottom: 16px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    word-break: break-all;
+    -webkit-line-clamp: 2;
+    /*! autoprefixer: off */
+    -webkit-box-orient: vertical;
+  }
+  .cursor-pointer {
+    color: var(--primaryColor);
+    text-align: right;
+  }
+}
+</style>

+ 44 - 0
src/view/diversity/list.vue

@@ -0,0 +1,44 @@
+<template>
+  <com-head :options="[]" v-model="params.pagging.state.query.isObj">
+    <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
+        >
+      </el-form-item>
+    </el-form>
+  </com-head>
+
+  <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 { SceneTypeDesc } from "@/constant/scene";
+import { useScenePaggingParams } from "./pagging";
+
+defineProps<{ params: ReturnType<typeof useScenePaggingParams> }>();
+
+// 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] },
+// ];
+</script>

+ 179 - 0
src/view/diversity/modelContent.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="body-head">
+    <el-tooltip
+      class="item"
+      effect="dark"
+      :content="`请上传${format}(支持obj/ply/las/osgb/b3dm格式的数据),大小在${size}以内 `"
+      placement="bottom-start"
+      ><el-upload
+        class="upload-demo"
+        :multiple="false"
+        :accept="accept"
+        :show-file-list="false"
+        :http-request="() => {}"
+        :file-list="fileList"
+        :disabled="percentage || !operateIsPermissionByPath('sync')"
+        :before-upload="uploadCheck"
+      >
+        <el-button v-pdpath="'sync'" type="primary">
+          <el-icon><Upload /></el-icon>{{ percentage ? "文件上传中" : "上传数据" }}
+        </el-button>
+      </el-upload>
+    </el-tooltip>
+  </div>
+
+  <el-table
+    :data="pagging.state.table.rows"
+    tooltip-effect="dark"
+    style="width: 100%"
+    size="large"
+  >
+    <el-table-column label="序号" width="70" v-slot:default="{ $index }">
+      <div style="text-align: center">
+        {{ pagging.state.pag.size * (pagging.state.pag.currentPage - 1) + $index + 1 }}
+      </div>
+    </el-table-column>
+    <el-table-column label="标题" prop="modelTitle"></el-table-column>
+    <el-table-column label="原始数据格式" prop="modelDateType"></el-table-column>
+    <el-table-column label="大小" prop="modelSize"></el-table-column>
+    <el-table-column label="上传时间" v-slot:default="{ row }: { row: ModelScene }">
+      {{ getStatusText(row) }}
+    </el-table-column>
+    <el-table-column label="所属架构" prop="deptName"></el-table-column>
+    <el-table-column label="操作" v-slot:default="{ row }">
+      <span
+        class="oper-span"
+        v-pdpath="['edit']"
+        @click="editHanlder(row)"
+        v-if="row.createStatus === ModelSceneStatus.SUCCESS"
+      >
+        修改
+      </span>
+      <span
+        class="oper-span"
+        v-pdpath="['view']"
+        @click="openSceneUrl(row, OpenType.query)"
+        v-if="row.createStatus === ModelSceneStatus.SUCCESS"
+      >
+        查看
+      </span>
+      <span
+        class="oper-span delBtn"
+        v-pdscene="row"
+        @click="delOrCancel(row)"
+        v-pdpath="'del'"
+      >
+        {{ row.createStatus !== ModelSceneStatus.RUN ? "删除" : "取消上传" }}
+      </span>
+    </el-table-column>
+  </el-table>
+
+  <el-dialog
+    :model-value="!!percentage"
+    :show-close="false"
+    title="文件上传中"
+    :close-on-click-modal="false"
+  >
+    <el-progress :percentage="percentage" />
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import {
+  ModelSceneStatus,
+  ModelScene,
+  cancelUploadModelScene,
+  uploadModelScene,
+  delModelScene,
+  getModelSceneStatus,
+} from "@/store/scene";
+import {
+  ModelMaxSize,
+  ModelSceneStatusDesc,
+  ModelSupportFormats,
+  SceneTypePaths,
+} from "@/constant/scene";
+import { confirm } from "@/helper/message";
+import { useUpload } from "@/hook/upload";
+import { ScenePagging } from "./pagging";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { watchPolling } from "@/hook/watchPolling";
+import { OpenType, openSceneUrl } from "../case/help";
+import { operateIsPermissionByPath } from "@/directive/permission";
+import { editModelScene } from "./quisk";
+
+const props = defineProps<{ pagging: ScenePagging }>();
+
+const getStatusText = (scene: ModelScene) => {
+  let desc = ModelSceneStatusDesc[scene.createStatus];
+  if (scene.createStatus === ModelSceneStatus.RUN && scene.progress) {
+    desc += ` ${scene.progress}% `;
+  } else if (scene.createStatus === ModelSceneStatus.SUCCESS) {
+    desc = scene.createTime;
+  }
+  return desc;
+};
+
+const delOrCancel = async (scene: ModelScene) => {
+  const isDel = scene.createStatus !== ModelSceneStatus.RUN;
+  const msg = isDel ? "确定要删除此数据?" : "确定要取消上传吗?";
+ElMessageBox.confirm(msg, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    isDel ? await delModelScene(scene) : await cancelUploadModelScene(scene);
+    props.pagging.refresh();
+  });
+  // if (await confirm(msg)) {
+  //   isDel ? await delModelScene(scene) : await cancelUploadModelScene(scene);
+  //   props.pagging.refresh();
+  // }
+};
+
+const editHanlder = async (scene: ModelScene) => {
+  if (await editModelScene({ model: scene })) {
+    props.pagging.refresh();
+  }
+};
+
+const {
+  percentage,
+  upload: uploadCheck,
+  fileList,
+  size,
+  format,
+  removeFile,
+  accept,
+} = useUpload({
+  maxSize: ModelMaxSize,
+  formats: ModelSupportFormats,
+  upload: async (file, onPercentage) => {
+    try {
+      await uploadModelScene(file, onPercentage);
+      props.pagging.refresh();
+    } catch {}
+    removeFile();
+  },
+});
+
+// 处理后台正在处理的模型类
+const refreshStatus = (models: ModelScene[]) => {
+  const refreshStatusAll = models.map(async (scene) => {
+    const { status, progress } = await getModelSceneStatus(scene);
+    scene.createStatus = status;
+    scene.progress = progress;
+    if (status == ModelSceneStatus.SUCCESS) {
+      props.pagging.refresh();
+    }
+  });
+  return Promise.all(refreshStatusAll);
+};
+
+watchPolling(() => {
+  const payload = (props.pagging.state.table.rows as ModelScene[]).filter(
+    (item) => item.createStatus === ModelSceneStatus.RUN
+  );
+  return { start: payload.length > 0, payload };
+}, refreshStatus);
+</script>

+ 63 - 0
src/view/diversity/pagging.ts

@@ -0,0 +1,63 @@
+import { usePagging } from "@/hook/pagging";
+import { SceneType, getScenePagging } from "@/store/scene";
+import { getCaseFusionList } from "@/store/case";
+import { computed, reactive, watch, watchEffect } from "vue";
+
+export const useScenePaggingParams = () => {
+  const pagging = usePagging({
+    get: getCaseFusionList,
+    paramsTemlate: {
+      isShare: 0,
+      sceneName: "",
+      modelTitle: "",
+      fusionTitle: "",
+      deptId: "",
+      snCode: "",
+    },
+  });
+
+  const isSwmx = computed(() => pagging.state.query.type === SceneType.SWMX);
+  const keyword = computed({
+    get: () =>
+      isSwmx.value
+        ? pagging.state.query.modelTitle
+        : pagging.state.query.sceneName,
+    set: (val: string) => {
+      pagging.state.query.fusionTitle = val;
+      pagging.state.query.sceneName = val;
+    },
+  });
+  const isShare = computed({
+    get: () =>pagging.state.query.isShare,
+    set: (val) => {
+      pagging.state.query.isShare = val;
+    },
+  });
+  let oldSnCode = pagging.state.query.snCode;
+  watchEffect(() => {
+    console.log('pagging.state.query', pagging.state.query);
+    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, isShare });
+};
+export type ScenePagging = ReturnType<typeof useScenePaggingParams>["pagging"];

+ 32 - 0
src/view/diversity/quisk.ts

@@ -0,0 +1,32 @@
+import { QuoteScene, SceneType } from "@/store/scene";
+import EditModel from "./editModel.vue";
+import tableModel from "./tableModel.vue";
+import SceneDownload from "./sceneDownload.vue";
+import { quiskMountFactory } from "@/helper/mount";
+import { axios, checkHasDownload } from "@/request";
+
+export const editModelScene = quiskMountFactory(EditModel, {
+  title: "编辑模型",
+  width: 500,
+});
+export const tableModelScene = quiskMountFactory(tableModel, {
+  title: "导入多元融合",
+  width: 1000,
+});
+export type SceneDpwnloadProps = { scene: QuoteScene };
+export const sceneDownload = async(props: SceneDpwnloadProps) => {
+  const params = {
+    num: props.scene.num,
+    isObj: Number(![SceneType.SWSS, SceneType.SWYDSS].includes(props.scene.type)),
+  };
+  const res = await axios.get(checkHasDownload, { params });
+  const hideFloor = Number(res.data.downloadStatus) !== 3
+
+  const sceneDownloadDialog = quiskMountFactory(SceneDownload, {
+    title: "场景离线包下载",
+    width: 500,
+    hideFloor: hideFloor,
+    enterText: '下 载'
+  });
+  return await sceneDownloadDialog(props);
+}

+ 148 - 0
src/view/diversity/sceneContent.vue

@@ -0,0 +1,148 @@
+<template>
+  <el-table
+    class="mybody-head"
+    :data="pagging.state.table.rows"
+    ref="tableRef"
+    tooltip-effect="dark"
+    style="width: 100%;height: 250px;"
+    :height="250"
+    row-key="fusionId"
+    size="large"
+    @select="changeItem"
+    @select-all="changelist"
+    @selection-change="changeSelection"
+  >
+    <!-- -1 计算失败  0 计算中 1 计算成功并可以外网访问,不能编辑 2计算成功只能内网,能编辑 -->
+    <el-table-column type="selection" :selectable="selectable" width="55" />
+    <el-table-column label="名称" show-overflow-tooltip width="640px" prop="fusionTitle"></el-table-column>
+    <!-- <el-table-column label="案件名称" width="320px" prop="caseTitle"></el-table-column> -->
+    <el-table-column label="创建时间" prop="createTime" v-slot:default="{ row }">
+      {{ dayjs(row.createTime).format("YYYY-MM-DD HH:mm") }}
+    </el-table-column>
+
+  </el-table>
+</template>
+
+<script setup lang="ts">
+import {
+  QuoteScene,
+  QuoteSceneStatus,
+  delQuoteScene,
+  SceneType,
+  genMeshScene,
+  LocationEnum,
+} from "@/store/scene";
+import { getSceneListTree } from "@/store/case";
+import dayjs from "dayjs";
+import { QuoteSceneStatusDesc } from "@/constant/scene";
+import { OpenType, openSceneUrl } from "../case/help";
+import { confirm } from "@/helper/message";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { router } from "@/router";
+import { sceneDownload } from "./quisk";
+import { useScenePaggingParams, ScenePagging } from "./pagging";
+import { QuiskExpose } from "@/helper/mount";
+import { onMounted, ref, watch, watchEffect, computed, nextTick } from "vue";
+import {
+  getCaseSceneList,
+  getCaseScenes,
+  getSceneKey,
+  replaceCaseScenes,
+  setCaseaddOrUpdate,
+  setaddFusionIds,
+} from "@/store/case";
+// const params = useScenePaggingParams();
+const props = defineProps<{ pagging: ScenePagging, fusionIds: Array<string>, noEditList:  Array<string> }>();
+const caseId = computed(() => (router.currentRoute.value?.params?.caseId));
+const tableRef = ref(null);
+const fusionIds = ref(JSON.parse(JSON.stringify(props.fusionIds)) || []);
+const selectList = ref({
+  0: [],
+  1: [],
+})
+const pagScenes = props.pagging.state.table.rows;
+console.log('caseScenes', selectList, props.fusionIds);
+const submit = async () => {
+  console.log('fusionIds', fusionIds.value);
+  if(fusionIds.value.length == 0) return confirm('请选择多元融合')
+  const apiData = {
+    caseId: caseId.value,
+    fusionIds: fusionIds.value,
+  };
+  await setaddFusionIds(apiData)
+  // params.pagging.queryReset()
+  }
+defineExpose<QuiskExpose>({
+  submit
+});
+
+
+
+const isObj = ref(props.pagging.state.query?.isObj);
+const delSceneHandler = async (scene: QuoteScene) => {
+ElMessageBox.confirm("确定要删除当前场景吗?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await delQuoteScene(scene);
+    props.pagging.refresh();
+  });
+  // if (await confirm("确定要删除当前场景吗?")) {
+  //   await delQuoteScene(scene);
+  //   props.pagging.refresh();
+  // }
+};
+console.log('propsnumList', props.fusionIds);
+const selectable = (row) => {
+  // let selectlist = selectList.find(item => item.type == params.pagging.state.query?.isObj)?.numList;
+console.log('selectable', props.fusionIds, fusionIds.value, row);
+  if (props.fusionIds.includes(row.fusionId)) return false;
+  return true// && !selectlist.includes(row.num)
+}
+// 复选框同步
+let changIng = false;
+
+watchEffect(() => {
+  if (!tableRef.value) return;
+  const selectKeys = fusionIds.value;
+  const list = props.pagging.state.table.rows
+  // console.log('watchEffect', selectKeys);
+  // console.log('watchEffect', fusionIds.value, selectKeys,'list', list);
+  // changIng = true;
+  props.pagging.state.table.rows.forEach((scene) => {
+    tableRef.value!.toggleRowSelection(scene, selectKeys.includes(scene.fusionId));
+  });
+  // changIng = false;
+  // console.log(tableRef.value!.getSelectionRows())
+  // console.log('watchEffect', props.pagging.state.table.rows);
+}, { flush: 'post' });
+const changeSelection = async (selectScenes, item) => {
+  if (props.pagging.loading) return;
+  const selectKeys = selectScenes.map(ele => ele.fusionId);
+  if(!selectScenes.length){
+    let showList = props.pagging.state.table.rows.map(ele => ele.fusionId)
+    let newFusionIds = fusionIds.value.filter(ele => !showList.includes(ele))
+    fusionIds.value = newFusionIds
+    return
+  }
+  let arrNew= new Set(selectKeys.concat(fusionIds.value)); //通过set集合去重
+  fusionIds.value = [...arrNew]
+};
+const changeItem = (selectScenes, item) => {
+  console.log('changeItem', selectScenes, item);
+  fusionIds.value.includes(item.fusionId) ? fusionIds.value.splice(fusionIds.value.indexOf(item.fusionId), 1) : fusionIds.value.push(item.fusionId)
+}
+const changelist = (selectScenes) => {
+  console.log('changelist', selectScenes);
+
+}
+const sceneDownloadHandler = (scene: QuoteScene) => {
+  sceneDownload({ scene });
+};
+</script>
+<style scoped lang="scss">
+.mybody-head{
+    flex: auto !important;
+}
+</style>

+ 133 - 0
src/view/diversity/sceneDownload.vue

@@ -0,0 +1,133 @@
+<template>
+  <!-- hideFloor: state === State.package -->
+  <div>
+    <div class="title">
+      {{ stateTitle[state] }}
+    </div>
+
+    <div v-if="state === State.package">
+      <div
+        class="text"
+        style="display: flex; justify-content: space-between; margin-top: 15px"
+      >
+        <span>{{ filename }}</span>
+        <span>{{ percent }}%</span>
+      </div>
+      <div style="pointer-events: none">
+        <el-slider v-model="percent" :show-tooltip="false" />
+      </div>
+    </div>
+    <div v-else-if="state === State.readDown">
+      <span>正在下载中……</span>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, onUnmounted, ref } from "vue";
+import saveAs from "@/util/file-serve";
+import { checkHasDownload, getDownloadProcess, downloadScene, axios } from "@/request";
+import { ElLoading, ElMessage } from "element-plus";
+import { QuoteScene, SceneType } from "@/store/scene";
+import { QuiskExpose } from "@/helper/mount";
+
+const props = defineProps<{ scene: QuoteScene }>();
+enum State {
+  uncreate,
+  package,
+  readDown,
+}
+const getState = (type: number) => {
+  const stateTypes = [
+    { codes: [0, 2], state: State.uncreate },
+    { codes: [1], state: State.package },
+    { codes: [3], state: State.readDown },
+  ];
+  return (
+    stateTypes.find((stateType) => stateType.codes.includes(type))?.state ||
+    State.uncreate
+  );
+};
+
+const state = ref<State>(State.uncreate);
+const count = ref<number>(0);
+const filename = ref<string>(props.scene.title + ".zip");
+const downloadURL = ref<string>();
+const percent = ref(0);
+
+const stateTitle = {
+  [State.uncreate]: "下载场景离线数据包,可在本地运行查看。",
+  [State.package]: "正在打包场景离线数据",
+  [State.readDown]: filename.value,
+};
+
+const params = {
+  num: props.scene.num,
+  isObj: Number(![SceneType.SWSS, SceneType.SWYDSS].includes(props.scene.type)),
+};
+// 初始化
+const initial = async () => {
+  const res = await axios.get(checkHasDownload, { params });
+  state.value = getState(res.data.downloadStatus);
+  count.value = res.data.count;
+  downloadURL.value = res.data.downloadUrl;
+
+  if (state.value === State.uncreate) {
+    const downRes = await axios.get(downloadScene, { params });
+    state.value = getState(downRes.data.downloadStatus);
+    // const unCountFlag =
+    //   count.value == 0 ||(await axios.get(downloadScene, { params })).data.downloadStatus !== 1;
+    if (state.value === State.uncreate) {
+      ElMessage.error("下载失败,请联系管理员");
+      throw "暂无剩余下载次数";
+    }
+  }
+
+  if (state.value === State.package) {
+    await new Promise<void>((resolve) => requestUpdateURL(resolve));
+  } else {
+    downloadURL.value = res.data.downloadUrl;
+  }
+};
+
+// 下载
+const download = () => {
+  if (!downloadURL.value) {
+    ElMessage.error("下载链接未生成,请稍等!");
+    throw "下载链接未生成,请稍等!";
+  } else {
+    return saveAs(downloadURL.value, filename.value);
+  }
+};
+
+// 进度请求
+let timer: any;
+const requestUpdateURL = async (callback: () => void) => {
+  const res = await axios.get(getDownloadProcess, { params });
+
+  percent.value = parseInt(res.data.percent);
+  downloadURL.value = res.data.url;
+  if (downloadURL.value) {
+    state.value = State.readDown;
+    callback();
+  } else {
+    timer = setTimeout(() => requestUpdateURL(callback), 1000);
+  }
+};
+
+onUnmounted(() => clearTimeout(timer));
+
+defineExpose<QuiskExpose>({
+  submit: async () => {
+    await initial();
+    const loading = ElLoading.service({
+      lock: true,
+      text: "下载中",
+      background: "rgba(255, 255, 255, 0.4)",
+    });
+    await download();
+    loading.close();
+    ElMessage.success("下载完成");
+  },
+});
+</script>

+ 67 - 0
src/view/diversity/tableModel.vue

@@ -0,0 +1,67 @@
+<template>
+  <List :params="params">
+    <template v-slot:header>
+      <el-form-item label="名称:" style="width: 250px">
+        <el-input v-model="params.keyword" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="" style="width: 140px">
+        <el-select
+          v-model="params.isShare"
+          placeholder="多元融合"
+          style="width: 240px"
+        >
+          <el-option
+            v-for="item in options"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+        <!-- <el-input v-model="params.keyword" placeholder="请输入"></el-input> -->
+      </el-form-item>
+    </template>
+    <template v-slot:content>
+      <component
+        :is="component"
+        :fusionIds="fusionIds"
+        :noEditList="noEditList"
+        :pagging="params.pagging"
+        ref="contentRef"
+      />
+    </template>
+  </List>
+</template>
+
+<script setup lang="ts">
+import comSelect from "@/components/company-select/index.vue";
+import List from "./list.vue";
+import SceneContent from "./sceneContent.vue";
+import ModelContent from "./modelContent.vue";
+import { useScenePaggingParams } from "./pagging";
+import { QuiskExpose } from "@/helper/mount";
+import { computed, ref } from "vue";
+defineProps<{ fusionIds: Array<string>, noEditList: Array<string> }>();
+const options = [
+  {
+    value: 0,
+    label: "多元融合",
+  },
+  {
+    value: 1,
+    label: "共享多元融合",
+  },
+];
+
+const params = useScenePaggingParams();
+console.log("params.isSwmx", params, "ModelContent");
+const component = computed(() => (params.isSwmx ? ModelContent : SceneContent));
+const contentRef = ref(null);
+const submit = async () => {
+  // replaceCaseScenes(props.caseId, caseScenes.value)
+  await contentRef.value?.submit();
+  // console.log("submit", caseScenes.value);
+};
+defineExpose<QuiskExpose>({
+  submit,
+});
+</script>

+ 246 - 43
src/view/dossier/index.vue

@@ -1,26 +1,89 @@
 <template>
   <div class="abstract">
-    <div class="float-right">
-      <el-button class="newbut" :icon="Upload" @click="handleAdd">上传</el-button>
-    </div>
     <div class="blList">
       <div
-        class="listItem py-2"
+        class="listItem"
         v-for="(item, index) in list"
+        @click="handleItem(item)"
+        :class="{ active: active.filesTypeId == item.filesTypeId }"
         :key="index"
-        v-show="item.caseFilesList?.length"
       >
-        <div class="title1 pb-2">{{ item.filesTypeName }}</div>
-        <div class="itemList">
+        <div class="title">{{ item.filesTypeName }}</div>
+      </div>
+    </div>
+    <div class="abstractCentenr">
+      <div class="centerTop" v-show="!show">
+        <el-upload
+          class="upload-demo upload"
+          drag
+          :before-upload="upload"
+          :file-list="fileList"
+          :http-request="uploadNewFile"
+          :on-success="handleSuccess"
+          :on-preview="previewFile"
+          :show-file-list="false"
+          :accept="accept"
+          :before-remove="removeFile"
+          action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
+          multiple
+        >
+          <el-icon class="el-icon--upload" style="margin-top: 20px" size="26"
+            >
+            <i style="font-size: 26px; color: #26559B" class="iconfont icon-Upload"></i>
+            <!-- <upload-filled /> -->
+            </el-icon>
+          <div class="el-upload__text">
+            点击或拖拽文件上传<br />支持 jpg、png、jpeg、doc、pdf ≤ 500MB
+          </div>
+        </el-upload>
+      </div>
+      <div class="centerBottom">
+        <div class="dossierList">
           <div
-            style="margin: 12px 0"
-            class="item flex justify-between"
-            v-for="(item, index) in item.caseFilesList"
+            class="item"
+            v-for="(item, index) in active.caseFilesList"
+            :key="index"
           >
-            <span class="name cursor-pointer" @click="handleviewer(item.filesUrl)">{{
-              item.filesTitle
-            }}</span>
-            <el-icon><Delete @click="handleDel(item)" /></el-icon>
+            <div class="itemLeft">
+              <i
+                :class="'iconfont icon-word'"
+                v-if="getExtension(item.filesUrl, 'word')"
+                style="font-size: 32px"
+              ></i>
+              <i
+                :class="'iconfont icon-PDFtubiao'"
+                v-else-if="getExtension(item.filesUrl, 'pdf')"
+                style="font-size: 32px"
+              ></i>
+              <el-image
+                class="itemLeftImg"
+                v-else
+                style="width: 40px; height: 40px"
+                :src="item.filesUrl"
+                :key="item.filesUrl"
+                :initial-index="srcList.findIndex(ele => ele === item.filesUrl)"
+                :zoom-rate="1.2"
+                :max-scale="7"
+                :min-scale="0.2"
+                :preview-src-list="srcList"
+                show-progress
+                fit="cover"
+              />
+              <!-- <img class="itemLeftImg" @click="handleView"  :src="item.filesUrl" alt="" /> -->
+              <div class="itemText" :title="item.filesTitle">
+                {{ item.filesTitle }}
+              </div>
+            </div>
+            <div class="operation">
+              <el-icon
+                size="16px"
+                @click="downloadByUrl({ fileName: item.filesTitle, url: item.filesUrl })"
+                ><Download
+              /></el-icon>
+              <el-icon size="16px" @click="handleDel(item)" v-show="!show"
+                ><CircleClose
+              /></el-icon>
+            </div>
           </div>
         </div>
       </div>
@@ -32,21 +95,22 @@
 import { computed, ref, reactive } from "vue";
 import { addCaseFile } from "../originalPhoto/quisk";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { uploadNewFile, addByMediaLiBrary } from "@/store/case";
+import { uploadNewFile, addByMediaLiBrary, show } from "@/store/case";
 import { updateByTreeFileLists } from "@/store/case";
-import { Delete, Edit, Upload } from "@element-plus/icons-vue";
 import viewImg from "@/components/viewImg/index.vue";
+import { useUpload } from "@/hook/upload";
+import { downloadFile, downloadByUrl } from "@/util/index.ts";
 import { delCaseFile } from "@/store/caseFile";
 import { router } from "@/router";
-const active = ref(true);
-const srcList = [
-  "https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg",
-  "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
-];
+const srcList = ref([]);
 const settype = ref(false);
 const ruleFormRef = ref(null);
-const activeName = ref("1");
+const active = ref({
+  filesTypeId: 0,
+  caseFilesList: [],
+});
 const list = ref([]);
+const fileList = ref([]);
 const ssjId = ref(0);
 const zjjId = ref(0);
 const showModal = ref(false);
@@ -57,6 +121,10 @@ const fileInfo = ref({
   DrawFormatDesc: "jpg、png、jpeg、doc、pdf上传",
   fileSize: 500 * 1024 * 1024,
 });
+const { size, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: fileInfo.value.fileSize,
+  formats: fileInfo.value.formats,
+});
 getList();
 const handleClick = (tab) => {
   console.log(tab);
@@ -79,35 +147,50 @@ function getList() {
   updateByTreeFileLists(caseId.value).then((res) => {
     let newlist = res.find((ele) => ele.filesTypeName == "案件卷宗") || [];
     list.value = newlist.childrenList || [];
+    if(show.value){//展示也过滤现场照片空数据
+      list.value = list.value?.filter(element => element.caseFilesList) || []
+    }
     ssjId.value = newlist.childrenList.find(
       (ele) => ele.filesTypeName == "诉讼卷"
     ).filesTypeId;
     zjjId.value = newlist.childrenList.find(
       (ele) => ele.filesTypeName == "证据卷"
     ).filesTypeId;
+    let activeIndex =
+      list.value.findIndex(
+        (item) => item.filesTypeId == active.value.filesTypeId
+      ) || 0;
+    if (!active.value.filesTypeId) {
+      activeIndex = 0;
+    }
+    let activeItem = list.value[activeIndex];
+    active.value.filesTypeId = activeItem.filesTypeId;
+    active.value.caseFilesList = activeItem.caseFilesList;
+    let newsrcList = [];
+    activeItem.caseFilesList.map((ele) => {
+      if (isImage(ele.filesUrl)) {
+        newsrcList.push(ele.filesUrl);
+      }
+    });
+    srcList.value = newsrcList;
+
     console.log("list.value", list.value);
   });
 }
+const isImage = (url) => {
+  return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url);
+};
 function handleSuccess(item) {
   let uploadId = item?.data.id;
   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,
+    filesTypeId: active.value.filesTypeId,
     uploadId,
   }).then((res) => {
     getList();
   });
 }
+
 const upload = async (file: File) => {
   const fileType = file.name
     .substring(file.name.lastIndexOf("."))
@@ -115,12 +198,9 @@ const upload = async (file: File) => {
   let imgList = [".jpg", ".png", ".jpeg", ".doc", ".docx", ".pdf"];
   let fileList = [".doc", ".docx", ".pdf"];
   if (!imgList.some((type) => type.toUpperCase() === fileType)) {
-    ElMessage.error(`请上传pdf、word 格式文件和 jpg、png、jpeg格式图片图片`);
+    ElMessage.error(`请上传pdf、word 格式文件和 jpg、png、jpeg格式图片`);
     return false;
-  } else if (
-    (fileList.includes(fileType) && file.size > 500 * 1024 * 1024) ||
-    file.size > 100 * 1024 * 1024
-  ) {
+  } else if (fileList.includes(fileType.toLowerCase()) ? file.size > 500 * 1024 * 1024 : file.size > 10 * 1024 * 1024) {
     ElMessage.error(
       `请上传≤10MB jpg、png、jpeg格式图片,及≤500MB pdf、doc文件`
     );
@@ -132,9 +212,26 @@ const upload = async (file: File) => {
 function handleActive(params) {
   console.log("handleActive", params);
 }
-function handleItem(type, item) {
-  console.log("handleItem", type, item);
-  getList();
+function getExtension(name, type) {
+  if (!name) return true;
+  let fileType = name.substring(name.lastIndexOf("."));
+  console.log("getExtension", fileType, type, name);
+  let fileList = [".doc", ".docx"];
+  if (type == "pdf" && fileType == ".pdf") {
+    return true;
+  }
+  if (type == "word" && fileList.includes(fileType)) {
+    return true;
+  }
+
+  if (type == "word" && fileList.includes(fileType)) {
+    return true;
+  }
+  return false;
+  // if(fileList.includes(fileType)){
+  //   return 'word'
+  // }
+  // return 'img'
 }
 async function handleAdd() {
   await addCaseFile({
@@ -144,6 +241,15 @@ async function handleAdd() {
   });
   getList();
 }
+async function handleView() {
+  await addCaseFile({
+    caseId: caseId.value,
+    fileInfo: fileInfo.value,
+    filesTypeName: ["案件卷宗"],
+  });
+  getList();
+}
+
 function handleDel(file) {
   ElMessageBox.confirm("确定删除?", "提示", {
     confirmButtonText: "确定",
@@ -162,10 +268,24 @@ const handleviewer = (file) => {
   showModal.value = true;
   console.log("file", file);
 };
+const handleItem = (item) => {
+  console.log("handleClick", item);
+  active.value.filesTypeId = item.filesTypeId;
+  active.value.caseFilesList = item.caseFilesList;
+  let newsrcList = [];
+  item.caseFilesList.map((ele) => {
+    if (isImage(ele.filesUrl)) {
+      newsrcList.push(ele.filesUrl);
+    }
+  });
+  srcList.value = newsrcList;
+};
 </script>
 <style lang="scss" scoped>
 .abstract {
   height: 100%;
+  display: flex;
+
   .el-form-item--label-top {
     margin-bottom: 14px;
   }
@@ -179,9 +299,92 @@ const handleviewer = (file) => {
       line-height: 32px;
     }
   }
-  .blList{
+  .blList {
+    width: var(--leftwidth);
+    height: 100%;
+    overflow-x: scroll;
     .name {
-      color: #26559B;
+      color: #26559b;
+    }
+    .listItem {
+      padding: 24px 48px;
+      width: calc(var(--leftwidth) - 0px);
+      cursor: pointer;
+    }
+    .active {
+      background: rgba(69, 144, 255, 0.1);
+    }
+  }
+  .abstractCentenr {
+    background: #f5f5f5;
+    height: 100%;
+    width: calc(100% - var(--leftwidth));
+    padding: 32px;
+    .centerTop {
+      height: 148px;
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 32px;
+      .upload {
+        height: 100%;
+        width: 100%;
+      }
+      .import {
+        margin-right: 20px;
+        padding-top: 40px;
+        height: 100%;
+        width: 148px;
+        text-align: center;
+        background: #fff;
+      }
+    }
+    .centerBottom {
+      .selcet {
+        // float: right;
+        // text-align: right;
+        display: flex;
+        justify-content: space-between;
+      }
+      .dossierList {
+        .item {
+          background: #fafafa;
+          border: 1px solid #d9d9d9;
+          padding: 0 42px;
+          border-radius: 2px;
+          width: 100%;
+          height: 80px;
+          line-height: 80px;
+          display: flex;
+          justify-content: space-between;
+          margin-bottom: 15px;
+          .itemLeft {
+            display: flex;
+            width: calc(100% - 78px);
+            align-items: center;
+            .itemText {
+              overflow: hidden;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+              width: calc(100% - 80px);
+            }
+            .iconfont {
+              margin-right: 20px;
+            }
+            .itemLeftImg {
+              width: 32px;
+              height: 32px;
+              margin-right: 20px;
+              border-radius: 2px;
+            }
+          }
+          .operation {
+            .el-icon {
+              cursor: pointer;
+              margin: 10px;
+            }
+          }
+        }
+      }
     }
   }
 }

+ 11 - 4
src/view/layout/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="layer">
     <login />
-    <ly-top class="top" />
+    <ly-top  class="top" v-if="hiddenTop" />
     <ly-slide class="slide" :names="menuRouteNames" v-if="!hiddenSlide" />
     <div class="content">
       <router-view v-slot="{ Component }" v-if="isSystem">
@@ -9,7 +9,7 @@
       </router-view>
       <template v-else>
         <div class="view" :class="{ full: hiddenSlide }">
-          <div class="main p-4" :class="{ fullmain: hiddenSlide }">
+          <div class="main" :class="{ fullmain: hiddenSlide }">
             <router-view v-slot="{ Component }">
               <component :is="Component" />
             </router-view>
@@ -51,6 +51,7 @@ import {
   getUrlSrc,
   getcaseLists,
   getCaseSceneList,
+  getCaseInfo,
   Extract,
 } from "@/store/case";
 import { json } from "stream/consumers";
@@ -100,12 +101,18 @@ watch(
   }
 );
 onMounted(() => {
+  getCaseInfo(caseId.value);
   // setupSDK(document.querySelector('iframe') as HTMLIFrameElement)
 });
 updateByTreeFileLists();
 const hiddenSlide = computed(
   () => !menuRouteNames.includes(router.currentRoute.value.name as string)
 );
+
+const hiddenTop = computed(
+  () => router.currentRoute.value.name != 'records' && router.currentRoute.value.name != 'login' && router.currentRoute.value.name != 'register' && router.currentRoute.value.name != 'forgetpassword'
+
+);
 const handleCommand = (command) => {
   let newid = command || sceneList.value[0].id;
   console.log("handleCommand", command, newid, sceneList.value);
@@ -164,13 +171,13 @@ const handleClick = (command: string) => {
       }
 
       .main {
-        margin: 0 0px 10px 0;
+        margin: 0;
         display: flex;
         flex-direction: column;
         overflow: hidden;
         flex: 1;
         background: #fff;
-        padding: 1rem 20px 1rem 32px;
+        // padding: 1rem 20px 1rem 32px;
         border-top: 1px solid #e6e6e6;
       }
       .fullmain {

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

@@ -85,10 +85,6 @@
             公安部鉴定中心 & 江门市公安局 & 四维时代
           </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>

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

@@ -13,8 +13,9 @@
     </el-menu> -->
     <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
       <el-tab-pane
-        v-for="item in routes"
+        v-for="(item,index) in routes"
         :label="item.meta.title"
+        v-show="true"
         :name="item.name"
         :key="item.name"
       />
@@ -23,25 +24,52 @@
 </template>
 
 <script setup lang="ts">
-import { ref, watch } from "vue";
+import { ref, watch, computed } from "vue";
 import subMenu from "./submenu.vue";
 import { getPermissionRoutes } from "@/store/permission";
 import { router } from "@/router";
+import { recursiveSearch } from "@/util/index.ts";
+import { updateByTreeFileLists, show, getCaseList } from "@/store/case";
 
 const props = defineProps<{ names: string[] }>();
 const activeName = ref(router.currentRoute.value.name || "scene");
-const routes = getPermissionRoutes(props.names);
+const check = ref(false);
+const routes = ref([]);
+routes.value = getPermissionRoutes(show.value?['abstract']:props.names);
+const caseId = computed(() => (router.currentRoute.value?.params?.caseId));
 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 });
+function getList() {
+  updateByTreeFileLists(caseId.value).then(async res => {
+  console.log("tab", show.value);
+    console.log('recursiveSearch', routes.value, props.names)
+    let homeList = await getCaseList({caseId: caseId.value, type: 'scene'})
+    let diversityList = await getCaseList({caseId: caseId.value, type: 'fusion'})
+    let showList = ['scene','diversity', 'originalPhoto','photos', 'sceneimg', 'dossier']
+    let showObj = {
+      'scene': homeList.length > 0,
+      'diversity': diversityList.length > 0,
+      'originalPhoto': recursiveSearch(res.find(ele => ele.filesTypeName == '原始照片').childrenList),
+      'photos': recursiveSearch(res.find(ele => ele.filesTypeName == '痕迹物证').childrenList),
+      'sceneimg': recursiveSearch(res.find(ele => ele.filesTypeName == '三录材料').childrenList),
+      'dossier': recursiveSearch(res.find(ele => ele.filesTypeName == '案件卷宗').childrenList),
+    }
+    console.log('recursiveSearch', showObj)
+    routes.value = getPermissionRoutes(props.names.filter(ele => showList.includes(ele)?showObj[ele]:true));
+    console.log('recursiveSearch', props.names.filter(ele => showList.includes(ele)?showObj[ele]:true))
+  })
+}
+watch(() => show.value,(newPath) => { 
+  newPath && getList()
+ },{ immediate: true });
 </script>
 
 <style lang="scss">
 .slide {
-  padding: 0 38px;
+  padding: 0 26px;
   background: #ffffff;
   .el-tabs__header {
     // margin: 0 0 24px;

+ 145 - 52
src/view/layout/top/index.vue

@@ -1,54 +1,53 @@
 <template>
   <div class="header-top">
-    <div class="title" >
-      <div class="edit" style="white-space: nowrap;text-overflow: ellipsis;">编辑</div>
-      <div class="span" :title="title">{{ title }}</div>
+    <div class="title">
+      <div class="edit" v-show="!show" style="white-space: nowrap; text-overflow: ellipsis">
+        编辑
+      </div>
+      <div class="span" :title="caseInfo.caseTitle">
+        {{ caseInfo.caseTitle }}
+      </div>
     </div>
-    <div class="oper-btns">
-      <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 class="oper-btns" v-show="!show">
+      <!-- <el-button class="newbut" style="padding: 5px 10px" type="primary" @click="handlemtk"> -->
+      <i title="重命名" class="iconfont icon-rename" @click="handlemtk"></i>
+      <!-- </el-button> -->
+      <!-- <el-button class="newbut" style="padding: 5px 10px" type="primary" > -->
+      <i title="媒体库" class="iconfont icon-media_l" @click="handleView"></i>
+      <!-- </el-button> -->
+      <div class="view" @click="handleSee">预览</div>
     </div>
-    <!-- <div class="oper-btns" v-if="user.info">
-      <div class="user-menu">
-        <img :src="user.info.avatar ? user.info.avatar : defAvatar" />
-        <el-dropdown>
-          <div style="outline: none">
-            <span class="oper-down">{{ user.info.nickName }}</span>
-            <el-icon><ArrowDown /></el-icon>
-          </div>
-          <template v-slot:dropdown>
-            <el-dropdown-menu class="menu-items-user">
-              <el-dropdown-item @click="updatePwdHandler">修改密码</el-dropdown-item>
-              <el-dropdown-item @click="logout"
-                ><span style="color: #fa5555">退出登录</span></el-dropdown-item
-              >
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-      </div>
-    </div> -->
+    <el-dialog v-model="form.dialogFormVisible" title="重命名" width="500">
+      <el-form :model="form">
+        <el-form-item :label-width="0">
+          <el-input v-model="form.name" maxlength="100" show-word-limit autocomplete="off" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer text-center" >
+          <el-button @click="form.dialogFormVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">
+            确定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 <script lang="ts" setup>
 import { openErrorMsg } from "@/request";
 import { user, logout as logoutRaw } from "@/store/user";
 import defAvatar from "@/assets/image/top_my.png";
-import { getCaseInfo, getCaseInfoData } from "@/store/case";
+import { getCaseInfo, getCaseInfoData, show } from "@/store/case";
 import { refreshRole, roleId } from "@/store/role";
 import { RouteName, router } from "@/router";
 import { confirm } from "@/helper/message";
 import { updatePwd } from "@/view/system/quisk";
-import { computed, ref, watch } from "vue";
-import { addCaseScenes } from "@/view/originalPhoto/quisk";
-
+import { computed, ref, watch, onMounted } from "vue";
+import { addShowCaseScenes } from "@/view/originalPhoto/quisk";
+import { ElMessage, ElMessageBox, formItemValidateStates } from "element-plus";
+import { setExample } from "@/app/criminal/store/example";
+// import { addCaseScenes } from "@/view/material/quisk";
 refreshRole();
 const caseId = computed(() => {
   const caseId = router.currentRoute.value.params.caseId;
@@ -56,10 +55,14 @@ const caseId = computed(() => {
     return Number(caseId);
   }
 });
-getCaseInfo(caseId.value)
+const form = ref({
+  name: "",
+  dialogFormVisible: false
+});
+const dialogFormVisible = ref(false);
+const formLabelWidth = "120px";
 // const useCaseStore = getCaseInfoData();
-const title =  computed(() => getCaseInfoData().caseTitle);
-console.log("useCaseStore", title);
+const caseInfo = computed(() => getCaseInfoData());
 // const title = ref('')
 // const getInfo = async () => {
 //   const res = await getCaseInfo(caseId.value);
@@ -67,19 +70,76 @@ console.log("useCaseStore", title);
 //   title.value = getCaseInfoData() || res?.caseTitle || "";
 // };
 // getInfo();
-watch(() => title.value, () => {
-  document.title = title.value;
-})
+watch(
+  () => caseInfo.value.caseTitle,
+  () => {
+    document.title = caseInfo.value && caseInfo.value.caseTitle;
+  }
+);
 const loginoutRaw = async () => {
   await logoutRaw();
   roleId.value = "";
   router.replace({ name: RouteName.login });
 };
 const handlemtk = () => {
-  window.open("/admin/index.html#/mediaLibrary/index");
+  // window.open("/admin/index.html#/mediaLibrary/index");
+  form.value.dialogFormVisible = true;
+  form.value.name = caseInfo.value.caseTitle;
+  // ElMessageBox.prompt("", "重命名", {
+  //   confirmButtonText: "确定",
+  //   customClass: "promptClass",
+  //   cancelButtonText: "取消",
+  //   showClose: true,
+  //   inputValue: caseInfo.value.caseTitle,
+  //   inputValidator: (value) => {
+  //     if (!value) {
+  //       return false;
+  //     }
+  //     return true;
+  //   },
+  //   inputErrorMessage: "请输入名称",
+  // })
+  //   .then(async ({ value }) => {
+  //     await setExample({
+  //       ...caseInfo.value,
+  //       caseTitle: value,
+  //       caseId: caseId.value,
+  //     });
+  //     ElMessage({
+  //       type: "success",
+  //       message: `修改成功`,
+  //     });
+  //     await getCaseInfo(caseId.value)
+  //   })
+  //   .catch(() => {
+  //     // ElMessage({
+  //     //   type: 'info',
+  //     //   message: 'Input canceled',
+  //     // })
+  //   });
 };
+const handleConfirm = async () => {
+      let value = form.value.name;
+      if(!value){
+        return  ElMessage.error("请输入修改名称");;
+      }
+      await setExample({
+        ...caseInfo.value,
+        caseTitle: value,
+        caseId: caseId.value,
+      });
+      ElMessage({
+        type: "success",
+        message: `修改成功`,
+      });
+      form.value.dialogFormVisible = false;
+      await getCaseInfo(caseId.value)
+}
 const handleSee = () => {
-  window.open(`/code/index.html?caseId=${caseId.value}#/show`);
+  window.open(window.location.origin + `/mix3d/?show=true#/abstract/${caseId.value}`);
+};
+ const handleView = async () => {
+  await addShowCaseScenes({fileTypes: [0,1,2,5], formats: [], upload:true})
 };
 const logout = async () => {
   if (await confirm("确定要退出登录吗?")) {
@@ -96,17 +156,50 @@ const updatePwdHandler = async () => {
 
 <style lang="scss" scoped>
 @import "./style.scss";
-.header-top{
+.header-top {
   width: 100%;
 }
-.title{
+.title {
   // display: inline-block;
   max-width: calc(50% + 300px);
-  .span{
-  overflow:hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  -o-text-overflow:ellipsis;
+  font-size: 32px;
+  .span {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    -o-text-overflow: ellipsis;
+  }
+}
+.oper-btns {
+  display: flex;
+  align-items: center;
+  .iconfont {
+    font-size: 24px;
+    margin-right: 40px;
+    cursor: pointer;
+  }
+  .view {
+    height: 40px;
+    width: 88px;
+    text-align: center;
+    line-height: 40px;
+    box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.04);
+    border-radius: 2px;
+    border: 1px solid #26559b;
+    font-size: 14px;
+    color: #26559b;
+    cursor: pointer;
+  }
+}
+</style>
+<style lang="scss">
+.promptClass {
+  .el-message-box__header {
+    padding-left: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .el-message-box__content {
+    padding-left: 32px !important;
   }
 }
 </style>

+ 2 - 4
src/view/layout/top/style.scss

@@ -9,8 +9,7 @@
     display    : flex;
     align-items: center;
     color: #000;
-    font-weight: bold;
-    font-size: 24px;
+    font-size: 32px;
     margin-bottom: 0;
     img {
       width: 50px;
@@ -19,8 +18,7 @@
       height: 24px;
       line-height: 24px;
       font-family: Roboto, Roboto;
-      font-weight: bold;
-      font-size: 24px;
+      font-size: 32px;
       margin: 0 12px 0 0px;
       padding: 0 12px 0 0px;
       border-right: 1px solid #ECECEC;

+ 111 - 169
src/view/material/AddScenesImg.vue

@@ -1,184 +1,126 @@
 <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>
+  <VRModelList :params="params" :dictId="dictId" :uploadShow="true">
+    <template v-slot:header>
+      <el-form-item label="名称:" style="grid-area: 1 / 1 / 2 / 4">
+        <el-input v-model="params.pagging.state.query.title" 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="620" label="标题" v-slot:default="{ row }">
+          <span class="truncate" :title="row.title">{{ row.title || '-' }}</span>
+        </el-table-column>
+        <!-- <el-table-column label="来源" width="230" prop="resource"></el-table-column> -->
+        <el-table-column class="truncate" width="170" label="时间" prop="dictName" v-slot:default="{ row }">
+          <span :title="row.dictName" class="truncate">{{ dayjs(row.createTime).format("YYYY-MM-DD HH:mm") }}</span>
+        </el-table-column>
+      </el-table>
+    </template>
+  </VRModelList>
 </template>
+
 <script setup lang="ts">
-import { router } from "@/router";
-import { QuiskExpose } from "@/helper/mount";
+import VRModelList from "./newlist.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 { ElMessage, ElMessageBox } from "element-plus";
 import {
   getCaseSceneList,
-  getUrlSrc,
-  getSceneListTree,
-  setCaseaddOrUpdate,
+  getCaseScenes,
+  getSceneKey,
+  replaceCaseScenes,
+  caseExportImg,
 } 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();
+import { QuiskExpose } from "@/helper/mount";
+import { ElTable, useTransitionFallthroughEmits } from "element-plus";
+import dayjs from "dayjs";
+const options = ref([]);
+const props = defineProps<{ caseId: number, dictId: number, type: number }>();
+const params = useScenePaggingParams(true);
+console.log("formats", params);
+const caseScenes = ref([]);
+const tableRef = ref<InstanceType<typeof ElTable>>();
+// const 
+watch(
+  () => params.pagging.state.query,
+  () => {
+    params.pagging.state.query.type = props.type;
+    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 handleItem = (item: any) => {
-  scenes.value.num = item.num;
-  scenes.value.item = item;
+const selectable = (row) => {
+  return useTransitionFallthroughEmits;
+} 
+
+const del = async (row) => {
+  ElMessageBox.confirm('确定要删除吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    const res = await delDictFileLists(row.id);
+    params.pagging.queryReset()
+  });
+  // 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;
 };
-const handleShow = () => {
-  show.value.left = !show.value.left
+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;
 }
-geiList();
+// 初始化数据
 onMounted(async () => {
-  let win = scene.value.contentWindow;
-  sdk.value = await findObjectAttr(win, "__sdk")
-  console.log("sdk", sdk);
+  const res = await getDictFileLists(1);
+  console.log('onMounted', res);
+  options.value = res;
 });
+const submit = () => {
+  console.log(caseScenes.value);
+  if(caseScenes.value && caseScenes.value.length === 0){
+      ElMessage.error("请至少选中一项数据!");
+      throw "请至少选中一项数据!";
+  };
+  
+  return caseScenes.value.map((item) => item.uploadId || item.id);
+};
 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);
-  },
+  submit,
 });
 </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>

+ 6 - 6
src/view/material/addLibrary.vue

@@ -46,8 +46,7 @@
         :before-remove="isAnimation?removeFile5:removeFile"
       >
         <div type="primary" :disabled="!!file">
-          <div>点击或拖拽文件上传 {{ isAnimation?'5MB':'2GB' }}</div>
-          <div class="">以内的{{ formatDesc }}</div>
+          <div>点击或拖拽文件上传 {{ isAnimation?'5MB':'10MB' }}以内的{{ formatDesc }}</div>
         </div>
         <template v-slot:file="{ file }: { file: UploadFile }">
           <div class="file" @click.stop="previewFile()">
@@ -85,6 +84,7 @@ import { QuiskExpose } from "@/helper/mount";
 const props = defineProps<{
   caseId: number;
   fileType: number;
+  dictId: number;
 }>();
 
 const isAnimation = ref(false)
@@ -93,7 +93,7 @@ const caseFile = ref({
   filesTypeId: props.fileType,
   filesTitle: "",
   type: '',
-  dictId: ''
+  dictId: props.dictId || ''
 });
 const options = ref([])
 onMounted(async () => {
@@ -103,8 +103,8 @@ onMounted(async () => {
 });
 
 const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
-  maxSize: 2000 * 1024 * 1024,
-  formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3", ".shp"],
+  maxSize: 10 * 1024 * 1024,
+  formats: [".jpg", ".png", ".jpeg"],
 });
 
 const { size:size5, fileList:fileList5, upload:upload5, removeFile:removeFile5, previewFile:previewFile5, file:file5, accept:accept5 } = useUpload({
@@ -113,7 +113,7 @@ const { size:size5, fileList:fileList5, upload:upload5, removeFile:removeFile5,
 });
 const formatDesc = computed(() =>
 {
-  return isAnimation.value?'zip、glb上传':'jpg、png、jpeg、mp4、wav、mp3、shp上传'
+  return isAnimation.value?'zip、glb上传':'jpg、png、jpeg上传'
 }
 );
 

+ 21 - 15
src/view/material/addScenes.vue

@@ -33,19 +33,18 @@
       >
         <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>
+          <span class="truncate" :title="row.name || row.title">{{ row.name || row.title }}</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 }">
+        <el-table-column class="truncate" width="120" 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"
@@ -64,8 +63,9 @@
 <script setup lang="ts">
 import VRModelList from "./list.vue";
 import { Scene } from "@/store/scene";
-import { CaseScenes, getDictFileLists, delDictFileLists } from "@/store/case";
+import { CaseScenes, getDictFileLists, delDictFileLists, addByMediaLiBrary, uploadNewFile } from "@/store/case";
 import { useScenePaggingParams } from "./pagging";
+import { ElMessage, ElMessageBox } from "element-plus";
 import { onMounted, ref, watch, watchEffect } from "vue";
 import {
   getCaseSceneList,
@@ -76,8 +76,8 @@ import {
 import { QuiskExpose } from "@/helper/mount";
 import { ElTable } from "element-plus";
 const options = ref([]);
-const props = defineProps<{ caseId: number, formats: Array<string> }>();
-const params = useScenePaggingParams();
+const props = defineProps<{ caseId: number, filesTypeId: number, formats: Array<string> ,fileTypes: Array<string>, }>();
+const params = useScenePaggingParams(false);
 console.log("formats", params);
 const caseScenes = ref([]);
 const tableRef = ref<InstanceType<typeof ElTable>>();
@@ -86,7 +86,9 @@ watch(
   () => params.pagging.state.query,
   () => {
     params.pagging.state.query.status = 2;
+    params.pagging.state.query.fileTypes = props.fileTypes;
     params.pagging.state.query.caseId = props.caseId;
+    if (props.fileTypes) params.pagging.state.query.fileTypes = props.fileTypes;
   },
   { immediate: true, deep: true }
 );
@@ -104,17 +106,22 @@ watchEffect(() => {
 });
 
 const selectable = (row) => {
-  console.log("row", row);
-  const fileType = row.fileUrl?.substring(row.fileUrl.lastIndexOf("."))
-      .toLowerCase();
-  return props.formats.includes(fileType);
+  let url  = row.listCover || row.fileUrl;
+  if(!url) return
+  console.log("row", row, url, props.formats);
+  const fileType = url.substring(url.lastIndexOf(".")).toLowerCase();
+  return props.formats && props.formats.includes(fileType);
 } 
 
 const del = async (row) => {
-  if (await confirm("确定要删除吗?")) {
+  ElMessageBox.confirm("确定要删除吗?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     const res = await delDictFileLists(row.id);
     params.pagging.queryReset()
-  }
+  });
 }
 const changeSelection = (selectScenes) => {
   if (params.pagging.loading) return;
@@ -137,9 +144,8 @@ onMounted(async () => {
   console.log('onMounted', res);
   options.value = res;
 });
-const submit = () => {
-  console.log(caseScenes.value);
-  return caseScenes.value;
+const submit = async () => {
+  return caseScenes.value.map((item) => item.uploadId || item.id);
   // () => replaceCaseScenes(props.caseId, caseScenes.value)
 };
 defineExpose<QuiskExpose>({

+ 68 - 0
src/view/material/formData.ts

@@ -0,0 +1,68 @@
+export const recorderInfoType =
+    [
+        {
+            type: 0,
+            typeLabel: "笔录人",
+            name: "",
+            unit: "",
+            job: "",
+        },
+        {
+            type: 1,
+            typeLabel: "制图人",
+            name: "",
+            unit: "",
+            job: "",
+        },
+        {
+            type: 2,
+            typeLabel: "照相人",
+            name: "",
+            unit: "",
+            job: "",
+        },
+        {
+            type: 3,
+            typeLabel: "录像人",
+            name: "",
+            unit: "",
+            job: "",
+        },
+        {
+            type: 4,
+            typeLabel: "法医",
+            name: "",
+            unit: "",
+            job: "",
+        },
+        {
+            type: 5,
+            typeLabel: "录音人",
+            name: "",
+            unit: "",
+            job: "",
+        },
+
+    ]
+
+
+export const ChangeReasonType = [
+    {
+        id: 0,
+        name: "事主进入",
+        flag: "true",
+        value: ""
+    },
+    {
+        id: 1,
+        name: "报案人进入",
+        flag: "true",
+        value: ""
+    },
+    {
+        id: 2,
+        name: "其他",
+        flag: "true",
+        value: ""
+    }
+]

+ 0 - 1
src/view/material/index.vue

@@ -71,7 +71,6 @@
             :multiple="false"
             :show-file-list="false"
             drag
-            :limit="1"
             :before-upload="upload"
             :file-list="fileLists"
             :http-request="uploadNewFile"

+ 3 - 3
src/view/material/list.vue

@@ -2,7 +2,7 @@
     <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" @click="params.pagging.refresh">查询</el-button> -->
         <el-button type="primary" plain @click="params.pagging.queryReset"
           >重置</el-button
         >
@@ -16,7 +16,7 @@
         :http-request="() => {}"
         :accept="accept"
       > -->
-        <el-button style="margin-left: 12px" type="primary" @click="handleAdd">上传文件</el-button>
+        <!-- <el-button style="margin-left: 12px" type="primary" @click="handleAdd">上传文件</el-button> -->
       <!-- </el-upload> -->
       </el-form-item>
     </el-form>
@@ -49,7 +49,7 @@ 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"],
+    formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3"],
   });
 const headOptions = [
   { value: SceneType.SWKK, name: SceneTypeDesc[SceneType.SWKK] },

+ 67 - 0
src/view/material/newlist.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 v-if="uploadShow" 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> ,uploadShow: boolean, dictId: number}>();
+
+const { size, fileList, upload, removeFile, previewFile, file, accept } =
+  useUpload({
+    maxSize: 2000 * 1024 * 1024,
+    formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3"],
+  });
+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({dictId: props.dictId, fileList: [file] });
+  props.params.pagging.refresh();
+};
+</script>

+ 6 - 6
src/view/material/pagging.ts

@@ -1,16 +1,16 @@
 import { usePagging } from "@/hook/pagging";
 import { SceneType, getScenePagging } from "@/store/scene";
-import { getFilepageList } from "@/store/case";
+import { getCaseFusionImgList, getFilepageList } from "@/store/case";
 
 import { computed, reactive, watch, watchEffect } from "vue";
 
-export const useScenePaggingParams = () => {
+export const useScenePaggingParams = (type) => {
   const pagging = usePagging({
-    get: getFilepageList,
+    get: type?getCaseFusionImgList:getFilepageList,
     paramsTemlate: {
       sceneName: "",
       modelTitle: "",
-      name: "",
+      title: "",
       dictId: "",
       snCode: "",
       type: '1',
@@ -19,9 +19,9 @@ export const useScenePaggingParams = () => {
 
   const isSwmx = computed(() => pagging.state.query.type === SceneType.SWMX);
   const keyword = computed({
-    get: () => pagging.state.query.name,
+    get: () => pagging.state.query.title,
     set: (val: string) => {
-      pagging.state.query.name = val;
+      pagging.state.query.title = val;
     },
   });
 

+ 196 - 68
src/view/material/photos.vue

@@ -3,72 +3,99 @@
     <!-- <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 v-for="(item, index) in list" :key="index">
+          <div class="ItemTitle"
+          
+            @click="handleClick(item, index)"
+            v-show="show ? item.caseFilesList : true"
+            :class="{ active: childrenList.value == item.filesTypeId }"
+          >
+          <svgImg class="svg" :svgUrl="pngTosvg(item.iconList && item.iconList[0]?.iconUrl)" alt="" />
+          <span>{{ item.filesTypeName }}</span>
           </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"
+          <!-- <div
+            class="itemList"
+            v-for="(items, indexs) in item.childrenList"
             :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 class="ItemTitle">{{ items.filesTypeName }}</div>
+          </div> -->
+        </div>
+      </div>
+    </div>
+    <div class="abstractCentenr">
+      <div class="centerTop" v-show="!show">
+        <div class="import" @click="handleYr">
+          <el-icon size="26" color="#26559B">
+          <i style="font-size: 26px; color: #26559B" class="iconfont icon-import"></i></el-icon>
+          <div class="name">导入</div>
+        </div>
+        <el-upload
+          class="upload-demo upload"
+          drag
+          :before-upload="upload"
+          :http-request="(e) => uploadNewFile({dictId:childrenList.item && childrenList.item.dictId, file: e.file, caseId:caseId})"
+          :on-success="handleSuccess"
+          :on-preview="previewFile"
+          :on-progress="changeFile"
+          :show-file-list="false"
+          :accept="accept"
+          :before-remove="removeFile"
+          action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
+          multiple
+        >
+          <el-icon class="el-icon--upload" style="margin-top: 20px" size="26"
+            >
+            <i style="font-size: 26px; color: #26559B" class="iconfont icon-Upload" @click="handleView"></i>
+            </el-icon>
+          <div class="el-upload__text">
+            点击或拖拽文件上传<br />支持 jpg、png、jpeg ≤ 50MB
           </div>
+        </el-upload>
+      </div>
+      <div class="centerBottom">
+        <div class="imgList">
+          <viewImg ref="viewImgRef" :key="childrenList.value" @refresh="getList" :list="childrenList.caseFilesList || []" @handleEdit="handleEdit" @handleDel="handleDel" />
         </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 svgImg from "./svg.vue";
+import viewImg from "@/components/viewImg/list.vue";
 import { computed, ref } from "vue";
-import { updateByTreeFileLists, getByTreeFileLists, Extract } from "@/store/case";
-import { Plus, Upload, FullScreen } from "@element-plus/icons-vue";
+import { updateByTreeFileLists, getByTreeFileLists, uploadNewFile, addByMediaLiBrary, show } from "@/store/case";
+import { useUpload } from "@/hook/upload";
+import {
+  Upload,
+  DocumentAdd,
+  CircleCheck,
+  Delete,
+  Edit,
+} from "@element-plus/icons-vue";
 import { router } from "@/router";
-import { addCaseFile, setTypeFile, addSceneImg } from "./quisk";
+import { addCaseFile, setTypeFile, addCaseScenes } from "./quisk";
 const caseId = computed(() => router.currentRoute.value?.params?.caseId);
-const active1 = ref(0);
+const filesTypeId = ref(0);
 const active2 = ref(0);
 const active = ref(true);
 const settype = ref(false);
+const fileList = ref([]);
+const { size, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: 50 * 1024 * 1024,
+  formats: [".jpg",".png",".jpeg",".bmp"],//".mp4", ".wav", ".mp3", ".shp"
+});
+const viewImgRef = ref(null)
 const childrenList = ref({
   list: [],
   value: "",
+  item: {},
   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 = ref([]);
 const list = ref([]);
 const activeNames = ref(["1"]);
 const handleChange = (val: string[]) => {
@@ -78,40 +105,85 @@ 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 || [];
+      res.find((ele) => ele.filesTypeName == "痕迹物证")?.childrenList || [];
+    srcList.value = list.value;
+    let activeIndex = srcList.value.findIndex((item) => childrenList.value.value?item.filesTypeId == childrenList.value.value:item.caseFilesList);
+    if(!childrenList.value.value && activeIndex == -1){
+      activeIndex = 0
+    }
+    let activeItem = srcList.value[activeIndex]; //.find((item) => item.caseFilesList && item.caseFilesList.length);
+    if (!activeItem) {
+      return;
+    }
+    childrenList.value.item = activeItem;
+    childrenList.value.value = activeItem.filesTypeId;
+    childrenList.value.caseFilesList = activeItem.caseFilesList;
   });
 }
 async function handleAdd() {
   await addCaseFile({ caseId: caseId.value, filesTypeName: ["原始照片"] });
   getList();
 }
-async function handleSceneImg() {
-  Extract.value = true;
-  await addSceneImg({ caseId: caseId.value, filesTypeName: ["原始照片"] });
+const handleEdit = async (lists) => {
+  console.log("handleEdit", list.value, lists, lists.map(ele => ele.filesId));
+  await setTypeFile({
+    caseId: caseId.value,
+    // filesId: lists.map(ele => ele.filesId),
+    filesTypeId: childrenList.value.value,
+    fileOptions: list.value,
+    filesIds: lists.map(ele => ele.filesId)
+  });
   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);
+const handleClick = (item, index) => {
+  let childrenLists = list.value;
+  let activeItem = childrenLists[index];
+  if (!activeItem) {
+    return;
+  }
+  childrenList.value.value = activeItem.filesTypeId;
+  childrenList.value.item = activeItem;
+  childrenList.value.caseFilesList = activeItem.caseFilesList ;
+  // childrenList.value.list = list.value[val.index]
+  //   ? list.value[val.index].childrenList
+  //   : [];
+  // childrenList.value.value = active.value;
+  // active2.value = 0;
 };
+
+function pngTosvg(src) {
+  if (!src) return
+  return src.replace(".png", ".svg");
+  
+}
+async function handleYr() {
+  let uploadIds = await addCaseScenes({caseId: caseId.value, fileTypes: [0], formats: [".jpg",".png",".jpeg"] })//filesTypeId: childrenList.value.value,
+  if(uploadIds == false || uploadIds && uploadIds.length == 0 ){
+    return
+  }
+  await addByMediaLiBrary({
+    caseId: caseId.value,
+    uploadIds,
+    filesTypeId: childrenList.value.value,
+  });
+  getList();
+}
+async function changeFile(response, uploadFile, uploadFiles) {
+  console.log("changeFile", response, uploadFile, uploadFiles);
+}
+async function handleSuccess(response, uploadFile, uploadFiles) {
+  console.log("handleSuccess", response, uploadFile, uploadFiles);
+  await addByMediaLiBrary({
+    caseId: caseId.value,
+    uploadId: response.data.id,
+    filesTypeId: childrenList.value.value,
+  });
+  getList()
+}
 async function handleItem(type, item) {
   console.log("handleItem", type, item);
   if (type == "edit") {
@@ -127,9 +199,65 @@ async function handleItem(type, item) {
 <style scoped lang="scss">
 .scene {
   height: 100%;
-  overflow-x: scroll;
+  display: flex;
   .scene-list {
-    // height: 100%;
+    width: var(--leftwidth);
+    height: 100%;
+    overflow-x: scroll;
+    .list {
+      .listTitle {
+        padding: 29px 48px 13px 48px;
+        font-weight: bold;
+        font-size: 16px;
+        color: rgba(0, 0, 0, 0.85);
+        line-height: 22px;
+      }
+        .ItemTitle {
+          display: flex;
+          align-items: center;
+          padding: 20px 48px;
+          font-weight: 400;
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.85);
+          line-height: 22px;
+          .svg{
+            margin-right: 10px;
+            path {
+              fill: #ff0000;
+            }
+          }
+        }
+      .itemList {
+      }
+      .active {
+        background: rgba(69, 144, 255, 0.1);
+      }
+    }
+  }
+  .abstractCentenr {
+    background: #f5f5f5;
+    height: 100%;
+    width: 100%;
+    padding: 32px;
+    .centerTop {
+      height: 148px;
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 32px;
+      .upload {
+        height: 100%;
+        width: calc(100% - 170px);
+      }
+      .import {
+        margin-right: 20px;
+        padding-top: 40px;
+        height: 100%;
+        width: 148px;
+        text-align: center;
+        cursor: pointer;
+        background: #fafafa;
+      }
+    }
   }
   .title0 {
     font-family: Microsoft YaHei, Microsoft YaHei;
@@ -139,7 +267,7 @@ async function handleItem(type, item) {
     line-height: 22px;
   }
   .demo-tabs {
-    padding: 12px 0 ;
+    margin: 12px 0;
   }
 }
 </style>

+ 7 - 3
src/view/material/quisk.ts

@@ -12,11 +12,14 @@ export const addCaseFile = quiskMountFactory(AddCaseFile, {
   width: 570,
 });
 
-export const addSceneImg = quiskMountFactory(AddScenesImg, {
-  title: "视图截取",
+export const addSceneImg1 = quiskMountFactory(AddScenesImg, {
+  title: "导入方位图",
+  width: 955,
+});
+export const addSceneImg2 = quiskMountFactory(AddScenesImg, {
+  title: "导入平面图",
   width: 955,
 });
-
 export const addCaseScenes = quiskMountFactory(AddScenes, {
   title: "媒体库",
   width: 1000,
@@ -37,3 +40,4 @@ export const setTypeFile = quiskMountFactory(setType, {
   title: "修改分类",
   width: 550,
 });
+

+ 7 - 3
src/view/material/rollMakingList.vue

@@ -74,7 +74,7 @@ import {
   replaceCaseScenes,
 } from "@/store/case";
 import { QuiskExpose } from "@/helper/mount";
-import { ElTable } from "element-plus";
+import { ElTable, ElMessageBox } from "element-plus";
 const options = ref([]);
 const props = defineProps<{ caseId: number, formats: Array<string> }>();
 const params = useScenePaggingParams();
@@ -111,10 +111,14 @@ const selectable = (row) => {
 } 
 
 const del = async (row) => {
-  if (await confirm("确定要删除吗?")) {
+  ElMessageBox.confirm('确定要删除吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     const res = await delDictFileLists(row.id);
     params.pagging.queryReset()
-  }
+  });
 }
 const changeSelection = (selectScenes) => {
   if (params.pagging.loading) return;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1043 - 335
src/view/material/sceneImg.vue


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

@@ -41,6 +41,7 @@ const props = defineProps<{
   filesTypeId: number;
   filesId: number;
   fileOptions: Array<any>;
+  filesIds: Array<number>;
 }>();
 const fileOptions = ref([])
 const caseFile = ref({
@@ -48,6 +49,7 @@ const caseFile = ref({
   filesTypeId: props.filesTypeId,
   filesTitle: "",
   filesId: props.filesId,
+  filesIds: props.filesIds,
 });
 onMounted(async () => {
   if(props.fileOptions?.length){
@@ -105,6 +107,7 @@ defineExpose<QuiskExpose>({
       filesTypeId = caseFile.value.filesTypeId
     }
     // let filesTypeId = caseFile.value.filesTypeId?.slice(-1)
+    console.log('getupdateFileType', filesTypeId, caseFile.value)
     await getupdateFileType({...caseFile.value , filesTypeId});
     return caseFile.value;
   },

+ 123 - 0
src/view/material/svg.vue

@@ -0,0 +1,123 @@
+<template>
+  <div class="svg-container" v-html="processedSvg" :style="containerStyle"></div>
+</template>
+
+<script setup>
+import { ref, watch, computed } from 'vue';
+
+const props = defineProps({
+  // SVG图片的URL
+  svgUrl: {
+    type: String,
+    required: true
+  },
+  // 要设置的颜色
+  color: {
+    type: String,
+    default: '#262626'
+  },
+  // SVG宽度
+  width: {
+    type: String,
+    default: '16px'
+  },
+  // SVG高度
+  height: {
+    type: String,
+    default: '16px'
+  }
+});
+
+const rawSvg = ref('');
+const processedSvg = ref('');
+const error = ref(null);
+
+// 容器样式
+const containerStyle = computed(() => ({
+  width: props.width,
+  height: props.height,
+  display: 'inline-block'
+}));
+
+// 加载SVG
+const loadSvg = async () => {
+  try {
+    error.value = null;
+    const response = await fetch(props.svgUrl);
+    
+    if (!response.ok) {
+      throw new Error(`无法加载SVG: ${response.statusText}`);
+    }
+    
+    rawSvg.value = await response.text();
+  } catch (err) {
+    error.value = err.message;
+    console.error('加载SVG时出错:', err);
+  }
+};
+
+// 处理SVG并修改颜色
+const updateSvgColor = () => {
+  if (!rawSvg.value) return;
+  
+  // 创建临时DOM元素来处理SVG
+  const parser = new DOMParser();
+  const doc = parser.parseFromString(rawSvg.value, 'image/svg+xml');
+  const svgElement = doc.querySelector('svg');
+  
+  if (!svgElement) {
+    processedSvg.value = rawSvg.value;
+    return;
+  }
+  
+  // 移除可能影响颜色的属性
+  svgElement.removeAttribute('fill');
+  svgElement.removeAttribute('stroke');
+  
+  // 添加CSS类以便通过样式控制
+  svgElement.classList.add('dynamic-svg');
+  
+  // 修改所有路径和形状的颜色
+  const elements = svgElement.querySelectorAll('path, circle, rect, polygon, polyline, line, ellipse');
+  elements.forEach(el => {
+    // 保存原始颜色以便可能的恢复
+    if (!el.dataset.originalFill) {
+      el.dataset.originalFill = el.getAttribute('fill') || '';
+    }
+    if (!el.dataset.originalStroke) {
+      el.dataset.originalStroke = el.getAttribute('stroke') || '';
+    }
+    
+    // 设置新颜色
+    if (el.dataset.originalFill && el.dataset.originalFill !== 'none') {
+      el.setAttribute('fill', props.color);
+    }
+    if (el.dataset.originalStroke && el.dataset.originalStroke !== 'none') {
+      el.setAttribute('stroke', props.color);
+    }
+  });
+  
+  // 将处理后的SVG转换回字符串
+  const serializer = new XMLSerializer();
+  processedSvg.value = serializer.serializeToString(svgElement);
+};
+
+// 监听属性变化并重新加载/处理SVG
+watch(() => props.svgUrl, loadSvg, { immediate: true });
+watch(() => props.color, updateSvgColor);
+watch(rawSvg, updateSvgColor);
+</script>
+
+<style scoped>
+.svg-container {
+  overflow: hidden;
+}
+
+/* 备用样式,在直接修改属性不生效时使用 */
+:deep(.dynamic-svg) {
+  width: 100%;
+  height: 100%;
+}
+
+/* 可以根据需要添加更多样式 */
+</style>

+ 11 - 2
src/view/organization/index.vue

@@ -96,11 +96,20 @@ const setHandler = async (dept: Organization) => {
   }
 };
 const delHandler = async (deptId: Organization["id"]) => {
-  if (await confirm("确认要删除组织吗?")) {
+    ElMessageBox.confirm('确认要删除组织吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     await delOrganization(deptId);
     ElMessage.success({ message: "删除成功", type: "success" });
     refresh();
-  }
+  });
+  // if (await confirm("确认要删除组织吗?")) {
+  //   await delOrganization(deptId);
+  //   ElMessage.success({ message: "删除成功", type: "success" });
+  //   refresh();
+  // }
 };
 
 const refresh = async () => {

+ 3 - 0
src/view/originalPhoto/addCaseFile.vue

@@ -148,6 +148,9 @@ const handleExceed = (option) => {
 
 const handleAdd = async () => {
   let fileId =  await addCaseScenes({formats: props.fileInfo?.formats || [".jpg", ".jpeg", ".png"]});
+  if(fileId == false || fileId && fileId.length == 0 ){
+    return
+  }
   mtkList.value = fileId.map(ele => {
     return {
       filesUrl: ele.fileUrl,

+ 2 - 2
src/view/originalPhoto/addLibrary.vue

@@ -104,7 +104,7 @@ onMounted(async () => {
 
 const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
   maxSize: 2000 * 1024 * 1024,
-  formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3", ".shp"],
+  formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3"],
 });
 
 const { size:size5, fileList:fileList5, upload:upload5, removeFile:removeFile5, previewFile:previewFile5, file:file5, accept:accept5 } = useUpload({
@@ -113,7 +113,7 @@ const { size:size5, fileList:fileList5, upload:upload5, removeFile:removeFile5,
 });
 const formatDesc = computed(() =>
 {
-  return isAnimation.value?'zip、glb上传':'jpg、png、jpeg、mp4、wav、mp3、shp上传'
+  return isAnimation.value?'zip、glb上传':'jpg、png、jpeg、mp4、wav、mp3上传'
 }
 );
 

+ 19 - 10
src/view/originalPhoto/addScenes.vue

@@ -1,5 +1,5 @@
 <template>
-  <VRModelList :params="params">
+  <VRModelList :params="params" :uploadShow="uploadShow">
     <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="请输入" />
@@ -74,11 +74,12 @@ import {
   replaceCaseScenes,
 } from "@/store/case";
 import { QuiskExpose } from "@/helper/mount";
-import { ElTable } from "element-plus";
+import { ElTable, ElMessageBox } from "element-plus";
 const options = ref([]);
-const props = defineProps<{ caseId: number, formats: Array<string> }>();
+const props = defineProps<{ caseId: number, formats: Array<string> ,fileTypes: Array<string>, upload: boolean }>();
 const params = useScenePaggingParams();
-console.log("formats", params);
+const uploadShow = ref(props.upload);
+console.log("formats", props);
 const caseScenes = ref([]);
 const tableRef = ref<InstanceType<typeof ElTable>>();
 // const 
@@ -87,6 +88,8 @@ watch(
   () => {
     params.pagging.state.query.status = 2;
     params.pagging.state.query.caseId = props.caseId;
+    // if (props.fileTypes) params.pagging.state.query.fileFormats = props.formats.map(ele => ele.replace(/\./g, ""));
+    if (props.fileTypes) params.pagging.state.query.fileTypes = props.fileTypes;
   },
   { immediate: true, deep: true }
 );
@@ -105,16 +108,23 @@ watchEffect(() => {
 
 const selectable = (row) => {
   console.log("row", row);
-  const fileType = row.fileUrl?.substring(row.fileUrl.lastIndexOf("."))
-      .toLowerCase();
+  const fileType = row.fileUrl?.substring(row.fileUrl.lastIndexOf(".")).toLowerCase();
   return props.formats.includes(fileType);
 } 
 
 const del = async (row) => {
-  if (await confirm("确定要删除吗?")) {
+    ElMessageBox.confirm('确定要删除吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     const res = await delDictFileLists(row.id);
     params.pagging.queryReset()
-  }
+  });
+  // if (await confirm("确定要删除吗?")) {
+  //   const res = await delDictFileLists(row.id);
+  //   params.pagging.queryReset()
+  // }
 }
 const changeSelection = (selectScenes) => {
   if (params.pagging.loading) return;
@@ -138,8 +148,7 @@ onMounted(async () => {
   options.value = res;
 });
 const submit = () => {
-  console.log(caseScenes.value);
-  return caseScenes.value;
+  return caseScenes.value.map((item) => item.uploadId || item.id) || [];
   // () => replaceCaseScenes(props.caseId, caseScenes.value)
 };
 defineExpose<QuiskExpose>({

+ 204 - 50
src/view/originalPhoto/index.vue

@@ -3,64 +3,90 @@
     <!-- <el-button class="w-full my-4"  style="margin-left: 0; margin-right: 0">添加场景</el-button> -->
     <div class="scene-list">
       <div class="list">
-        <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"
+        <div v-for="(item, index) in list" :key="index"  v-show="show?recursiveSearch(item.childrenList):true">
+          <div class="listTitle">{{ item.filesTypeName }}</div>
+          <div
+            class="itemList"
+            v-for="(items, indexs) in item.childrenList"
+            @click="handleClick(index, indexs)"
+            :class="{ active: childrenList.value == items.filesTypeId }"
             :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 class="ItemTitle">{{ items.filesTypeName }}</div>
           </div>
         </div>
-
-        <viewImg
-          :list="childrenList.caseFilesList || []"
-          width="128px"
-          height="128px"
-          @handleItem="handleItem"
-        />
+      </div>
+    </div>
+    <div class="abstractCentenr">
+      <div class="centerTop" v-show="!show">
+        <div class="import" @click="handleImport">
+          <el-icon size="26" color="#26559B">
+          <i style="font-size: 26px; color: #26559B" class="iconfont icon-import"></i></el-icon>
+          <div class="name">导入</div>
+        </div>
+        <el-upload
+          class="upload-demo upload"
+          drag
+          :before-upload="upload"
+          :http-request="(e) => uploadNewFile({file: e.file, caseId:caseId})"
+          :on-success="handleSuccess"
+          :on-preview="previewFile"
+          :show-file-list="false"
+          :accept="accept"
+          :before-remove="removeFile"
+          action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
+          multiple
+        >
+          <el-icon class="el-icon--upload" style="margin-top: 20px" size="26"
+            >
+            <i style="font-size: 26px; color: #26559B" class="iconfont icon-Upload"></i>
+            </el-icon>
+          <div class="el-upload__text">
+            点击或拖拽文件上传<br />支持 jpg、png、jpeg ≤ 50MB
+          </div>
+        </el-upload>
+      </div>
+      <div class="centerBottom">
+        <div class="imgList">
+          <viewImg ref="viewImgRef" :key="childrenList.value" :list="childrenList.caseFilesList" @handleEdit="handleEdit" @refresh="getList" @handleDel="handleDel" />
+        </div>
       </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 { Upload } from "@element-plus/icons-vue";
+import viewImg from "@/components/viewImg/list.vue";
+import { computed, ref, watch } from "vue";
+import { updateByTreeFileLists, getByTreeFileLists, uploadNewFile, addByMediaLiBrary, show } from "@/store/case";
+import { useUpload } from "@/hook/upload";
+import { recursiveSearch } from "@/util/index.ts";
+import {
+  Upload,
+  DocumentAdd,
+  CircleCheck,
+  Delete,
+  Edit,
+} from "@element-plus/icons-vue";
 import { router } from "@/router";
-import { addCaseFile, setTypeFile } from "./quisk";
+import { addCaseFile, setTypeFile, addCaseScenes } from "./quisk";
 const caseId = computed(() => router.currentRoute.value?.params?.caseId);
-const active1 = ref(0);
+const filesTypeId = ref(0);
 const active2 = ref(0);
 const active = ref(true);
 const settype = ref(false);
+const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: 50 * 1024 * 1024,
+  formats: [".jpg", ".png", ".jpeg"],//".mp4", ".wav", ".mp3", ".shp"
+});
+const viewImgRef = ref(null)
 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 = ref([]);
 const list = ref([]);
 const activeNames = ref(["1"]);
 const handleChange = (val: string[]) => {
@@ -70,30 +96,67 @@ getList();
 function handleActive(params) {
   console.log("handleActive", params);
 }
+
 function getList() {
   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 || [];
+    let items = []
+    if(show.value){//展示也过滤现场照片空数据
+      list.value.map(ele => {
+        ele.childrenList = ele.childrenList?.filter(element => element.caseFilesList) || []
+        return ele
+      })
+    }
+    list.value.map((item) => {
+      items.push(...item.childrenList)
+    });
+    srcList.value = items
+    let activeIndex = srcList.value.findIndex((item) => item.filesTypeId == childrenList.value.value) || 0;
+    if(!childrenList.value.value){
+      activeIndex = 0
+    }
+    console.log("srcList.value", activeIndex, childrenList.value.value, srcList.value);
+    let activeItem = srcList.value[activeIndex]; //.find((item) => item.caseFilesList && item.caseFilesList.length);
+    if (!activeItem) {
+      return;
+    }
+    console.log("srcList.value", srcList.value, activeItem);
+    childrenList.value.value = activeItem.filesTypeId;
+    childrenList.value.caseFilesList = activeItem.caseFilesList || [];
   });
 }
 async function handleAdd() {
   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 handleClick = (index, indexs) => {
+  let childrenLists = list.value[index].childrenList;
+  let activeItem = childrenLists[indexs];
+  console.log("handleClick", activeItem, index, indexs);
+  if (!activeItem) {
+    return;
+  }
+  childrenList.value.value = activeItem.filesTypeId;
+  let caseFilesList = activeItem.caseFilesList && activeItem.caseFilesList.map(item => {
+      item.checked = false;
+      return item;
+    }) || [];
+  childrenList.value.caseFilesList = caseFilesList
 };
 const handleClick2 = (val) => {
-  childrenList.value.caseFilesList = childrenList.value.list[val.index]?.caseFilesList || [];
+  childrenList.value.caseFilesList =
+    childrenList.value.list[val.index]?.caseFilesList || [];
   console.log("handleClick2", active2.value, val);
 };
+async function handleSuccess(response) {
+  await addByMediaLiBrary({
+    caseId: caseId.value,
+    uploadId: response.data.id,
+    filesTypeId: childrenList.value.value,
+  });
+  getList()
+}
 async function handleItem(type, item) {
   console.log("handleItem", type, item);
   if (type == "edit") {
@@ -105,13 +168,104 @@ async function handleItem(type, item) {
   }
   getList();
 }
+const handleEdit = async (lists) => {
+  console.log("handleEdit", list.value, lists, lists.map(ele => ele.filesId));
+    await setTypeFile({
+      caseId: caseId.value,
+      filesTypeId: childrenList.value.value,
+      fileOptions: list.value,
+      filesIds: lists.map(ele => ele.filesId)
+    });
+    getList();
+};
+function closeChecked() {
+  console.log("closeChecked", viewImgRef.value);
+  // viewImgRef.value.mylist = []
+  childrenList.value.caseFilesList.forEach(item => {
+    item.checked = false;
+  })
+}
+async function handleImport() {
+  let uploadIds = await addCaseScenes({caseId: caseId.value, fileTypes: [0], formats: [".jpg",".png",".jpeg"] })
+  if(uploadIds == false || uploadIds && uploadIds.length == 0 ){
+    return
+  }
+  await addByMediaLiBrary({
+    caseId: caseId.value,
+    uploadIds,
+    filesTypeId: childrenList.value.value,
+  });
+  getList();
+}
+watch(
+  childrenList.value.value,
+  (newValue) => {
+    closeChecked();
+  },
+  { immediate: true }
+);
 </script>
 <style scoped lang="scss">
 .scene {
   height: 100%;
-  overflow-x: scroll;
+  display: flex;
   .scene-list {
-    // height: 100%;
+    width: var(--leftwidth);
+    height: 100%;
+    overflow-x: scroll;
+    .list {
+      .listTitle {
+        padding: 29px 48px 13px 48px;
+        font-weight: bold;
+        font-size: 16px;
+        color: rgba(0, 0, 0, 0.85);
+        line-height: 22px;
+      }
+      .itemList {
+        .ItemTitle {
+          padding: 25px 48px;
+          font-weight: 400;
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.85);
+          line-height: 22px;
+        }
+      }
+      .active {
+        background: rgba(69, 144, 255, 0.1);
+      }
+    }
+  }
+  .abstractCentenr {
+    background: #f5f5f5;
+    height: 100%;
+    width: 100%;
+    padding: 32px;
+    .centerTop {
+      height: 148px;
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 32px;
+      .upload {
+        height: 100%;
+        width: calc(100% - 170px);
+      }
+      .import {
+        margin-right: 20px;
+        padding-top: 40px;
+        height: 100%;
+        width: 148px;
+        text-align: center;
+        background: #fafafa;
+      }
+    }
+    .centerBottom {
+      .selcet {
+        // float: right;
+        // text-align: right;
+        display: flex;
+        justify-content: space-between;
+      }
+    }
   }
   .title0 {
     font-family: Microsoft YaHei, Microsoft YaHei;
@@ -120,7 +274,7 @@ async function handleItem(type, item) {
     color: rgba(0, 0, 0, 0.85);
     line-height: 22px;
   }
-  .demo-tabs{
+  .demo-tabs {
     margin: 12px 0;
   }
 }

+ 4 - 4
src/view/originalPhoto/list.vue

@@ -2,7 +2,7 @@
     <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" @click="params.pagging.refresh">查询</el-button> -->
         <el-button type="primary" plain @click="params.pagging.queryReset"
           >重置</el-button
         >
@@ -16,7 +16,7 @@
         :http-request="() => {}"
         :accept="accept"
       > -->
-        <el-button style="margin-left: 12px" type="primary" @click="handleAdd">上传文件</el-button>
+        <el-button v-if="uploadShow" style="margin-left: 12px" type="primary" @click="handleAdd">上传文件</el-button>
       <!-- </el-upload> -->
       </el-form-item>
     </el-form>
@@ -44,12 +44,12 @@ import { useUpload } from "@/hook/upload";
 import { addLibraryFile } from "./quisk";
 
 // const { pagging } = useScenePaggingParams();
-const props = defineProps<{ params: ReturnType<typeof useScenePaggingParams> }>();
+const props = defineProps<{ params: ReturnType<typeof useScenePaggingParams> ,uploadShow: boolean}>();
 
 const { size, fileList, upload, removeFile, previewFile, file, accept } =
   useUpload({
     maxSize: 2000 * 1024 * 1024,
-    formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3", ".shp"],
+    formats: [".jpg", ".png", ".jpeg", ".mp4", ".wav", ".mp3"],
   });
 const headOptions = [
   { value: SceneType.SWKK, name: SceneTypeDesc[SceneType.SWKK] },

+ 1 - 0
src/view/originalPhoto/pagging.ts

@@ -14,6 +14,7 @@ export const useScenePaggingParams = () => {
       dictId: "",
       snCode: "",
       type: '1',
+      fileTypes: []
     },
   });
 

+ 6 - 0
src/view/originalPhoto/quisk.ts

@@ -15,6 +15,12 @@ export const addCaseScenes = quiskMountFactory(AddScenes, {
   width: 1000,
 });
 
+export const addShowCaseScenes = quiskMountFactory(AddScenes, {
+  title: "媒体库",
+  width: 1000,
+  showSave: false,
+});
+
 export const addLibraryFile = quiskMountFactory(AddLibrary, {
   title: "上传文件",
   width: 550,

+ 3 - 1
src/view/originalPhoto/setType.vue

@@ -41,6 +41,7 @@ const props = defineProps<{
   filesTypeId: number;
   filesId: number;
   fileOptions: Array<any>;
+  filesIds: Array<number>;
 }>();
 const fileOptions = ref([])
 const caseFile = ref({
@@ -48,6 +49,7 @@ const caseFile = ref({
   filesTypeId: props.filesTypeId,
   filesTitle: "",
   filesId: props.filesId,
+  filesIds: props.filesIds,
 });
 onMounted(async () => {
   if(props.fileOptions?.length){
@@ -55,7 +57,6 @@ onMounted(async () => {
   }else{
     fileOptions.value = await updateSelectByTreeFileLists();
   }
-  console.log('caseFile', caseFile)
   console.log(fileOptions.value);
 });
 console.log(caseFile.value, '===caseFile');
@@ -105,6 +106,7 @@ defineExpose<QuiskExpose>({
       filesTypeId = caseFile.value.filesTypeId
     }
     // let filesTypeId = caseFile.value.filesTypeId?.slice(-1)
+    console.log('getupdateFileType', filesTypeId, caseFile.value)
     await getupdateFileType({...caseFile.value , filesTypeId});
     return caseFile.value;
   },

+ 12 - 3
src/view/role/index.vue

@@ -76,7 +76,7 @@ import comCompany from "@/components/company-select/index.vue";
 import comPagination from "@/components/pagination/index.vue";
 import { getPageList, axios, removeRole } from "@/request";
 import { Role, delRole, getRolePagging } from "@/store/role";
-import { ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
 import { confirm } from "@/helper/message";
 import { showRole, addRole, editRole } from "@/view/window";
 
@@ -92,11 +92,20 @@ const delInfo = async (row: Role) => {
   if (row.type == 0) {
     return ElMessage.warning("预设角色不能删除");
   }
-  if (await confirm("删除角色后,相关用户需重新配置角色,确认要删除组织吗?")) {
+    ElMessageBox.confirm('删除角色后,相关用户需重新配置角色,确认要删除组织吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     await delRole(row.id);
     ElMessage.success("操作成功");
     refresh();
-  }
+  });
+  // if (await confirm("删除角色后,相关用户需重新配置角色,确认要删除组织吗?")) {
+  //   await delRole(row.id);
+  //   ElMessage.success("操作成功");
+  //   refresh();
+  // }
 };
 
 const addHandler = async () => {

+ 0 - 1
src/view/system/style.scss

@@ -66,7 +66,6 @@
 
 .panel-form-item .el-button {
   line-height: 26px;
-  font-weight: bold;
   font-size  : 16px;
 }
 

+ 17 - 7
src/view/user/index.vue

@@ -91,7 +91,7 @@ import comHead from "@/components/head/index.vue";
 import comCompany from "@/components/company-select/index.vue";
 import comPagination from "@/components/pagination/index.vue";
 import { UserInfo, changeUserStatus, delUser, getUserPagging, user } from "@/store/user";
-import { ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
 import { confirm } from "@/helper/message";
 import { addUser, editUser } from "./quisk";
 
@@ -106,15 +106,25 @@ const { state, queryReset, refresh, changPageCurrent, changPageSize } = usePaggi
 });
 
 const delInfo = async (row: UserInfo) => {
-  if (
-    await confirm(
-      "用户被删除后,无法登录使用,无法编辑场景(可将该用户关联的相机绑定到其他管理员),确认要删除用户吗?"
-    )
-  ) {
+  
+  ElMessageBox.confirm('用户被删除后,无法登录使用,无法编辑场景(可将该用户关联的相机绑定到其他管理员),确认要删除用户吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     delUser(row.id);
     ElMessage.success("操作成功");
     refresh();
-  }
+  });
+  // if (
+  //   await confirm(
+  //     "用户被删除后,无法登录使用,无法编辑场景(可将该用户关联的相机绑定到其他管理员),确认要删除用户吗?"
+  //   )
+  // ) {
+  //   delUser(row.id);
+  //   ElMessage.success("操作成功");
+  //   refresh();
+  // }
 };
 
 const addHandler = async () => {

+ 187 - 78
src/view/vrmodel/index.vue

@@ -1,64 +1,83 @@
 <template>
-  <div class="scene">
-    <!-- <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-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 class="scene flex justify-end content-center">
+    <div class="scene-list" v-if="list.length">
+      <div class="scene-title" v-if="!show ">
+        <el-button class="newbut" @click="handleAdd">
+          <i class="iconfont icon-import" style="margin-right: 5px"></i>
+          导入
+        </el-button>
       </div>
-      <!-- <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;margin-top: 8px">
+      <div
+        class="list"
+        v-if="active"
+      >
         <div
-          class="listItem flex justify-between py-4"
+          class="listItem py-4"
           v-for="(item, index) in list"
           :key="index"
+          :class="{active: item.num === sceneItem.num, disabled: !item.viewAuth}"
+          @click="handleItem(item)"
         >
-          <span class="anmc" :title="item.name">{{
-            item.name || "多元融合"
-          }}</span>
-          <div class="cursor-pointer" quaternary type="primary">
-            <span
-              class="mr-4"
-              @click="handlegotoelT(item)"
-              :style="{ color: item.inFusion ? '#ccc' : '' }"
-              >移除</span
-            >
-            <span :style="{ color: item.isLaser ? '#ccc' : '' }" @click="handlegotoEdit(item)">编辑</span>
+          <div class="anmc" :title="item.title">{{ item.title || item.sceneName }}</div>
+          <div class="cursor-pointer" v-show="!show" v-if="item.num === sceneItem.num && (item.editAuth || caseInfoData.isAuthor)" quaternary type="primary">
+            <el-icon v-if="item.editAuth" style="margin-right: 34px">
+              <EditPen @click="handlegotoEdit(item)" />
+            </el-icon>
+            <el-icon>
+              <CircleClose @click="handlegotoelT(item)" />
+            </el-icon>
+          </div>
+          <div class="cursor-pointer" v-show="!show" v-else-if="!item.viewAuth && caseInfoData.isAuthor">
+            <el-icon>
+              <CircleClose @click.stop="handlegotoelT(item)" />
+            </el-icon>
           </div>
-          <!-- <div @click="handlegotoEdit(item)" class="cursor-pointer" quaternary type="primary">编辑</div> -->
         </div>
       </div>
-      <!-- <el-button
-                class="w-full"
-                type="primary"
-                @click="submitForm"
-              >
-                保存
-              </el-button> -->
     </div>
+      <div
+        class="app-scene"
+        ref="sceneRef"
+        :style="{
+          width: list.length == 0?'100%':'',
+
+          height: '100%',
+        }"
+      >
+      <iframe
+        v-if="list && list.length && sceneURL"
+        :src="sceneURL"
+        frameborder="0"
+        :style="{ width: '100%', height: '100%' }"
+      ></iframe>
+      <div class="import" v-else @click="handleAdd" v-show="!show">
+        <el-icon size="26" color="#26559B">
+          <i style="font-size: 26px; color: #26559B" class="iconfont icon-import"></i></el-icon>
+        <div class="name">导入</div>
+      <div class="noData" >
+        <div class="name" style="margin-top: 25px;">暂无数据</div>
+      </div>
+      </div>
+      </div>
   </div>
 </template>
 
 <script setup lang="ts">
 import {
   getCaseSceneList,
+  getCaseList,
   getUrlSrc,
   getSceneListTree,
   setCaseaddOrUpdate,
+  show,
+  caseInfoData,
 } from "@/store/case";
 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 { Plus, DocumentAdd, EditPen } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
 import ModelContent from "./modelContent.vue";
 import { useScenePaggingParams } from "./pagging";
 import { computed, ref, onMounted } from "vue";
@@ -67,18 +86,26 @@ const active = ref(true);
 const list = ref([]);
 const isEdit = ref(false);
 const showModal = ref(false);
+const sceneURL = ref('');
+const sceneNum = ref('');
+const sceneItem = ref({
+  id: '',
+  num: '',
+});
 const params = useScenePaggingParams();
 const caseId = computed(() => router.currentRoute.value?.params?.caseId);
 onMounted(() => {
   geiList();
-  console.log("router.currentRoute.value", caseId);
 });
 async function geiList(refresh) {
-  list.value = await getCaseSceneList(caseId.value, refresh);
-  console.log("res", list.value);
+  list.value = await getCaseList({caseId: caseId.value, type: 'scene'}) || [];
+  sceneItem.value = (list.value && list.value.find(ele => ele.viewAuth)) || {};
+  console.log("sceneURL", sceneItem.value);
+  sceneURL.value = getUrlSrc({...sceneItem.value, type: sceneItem.value.sceneType}, caseId.value);
+  console.log("sceneURL", list.value, sceneURL.value);
 }
 function handlegotoEdit(record) {
-  if(record.isLaser) return;
+  if (record.isLaser) return;
 
   let url =
     record.type == 2 || record.type == 5
@@ -92,59 +119,141 @@ function handleAdddyrh(record) {
 }
 async function handlegotoelT(record) {
   isEdit.value = true;
-  // if (record.inFusion) {
-  //   ElMessage.error("无法移除,场景已加入多元融合,请进入多元融合移除场景后再试");
-  //   return
-  // }
-  if (record.inFusion || (await confirm("确定要移除当前场景吗?"))) {
-    list.value = list.value.filter((item) => item.id !== record.id);
+  if (record.inFusion) {
+    list.value = list.value.filter((item) => item.num !== record.num);
     submitForm();
+    return;
   }
-  // console.log("handleActive", params);
+  ElMessageBox.confirm('确定要移除当前场景吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    list.value = list.value.filter((item) => item.num !== record.num);
+    sceneItem.value = (list.value && list.value.find(ele => ele.viewAuth)) || {};
+    sceneURL.value = getUrlSrc({...sceneItem.value, type: sceneItem.value.sceneType}, caseId.value);
+    submitForm();
+  });
 }
 async function submitForm() {
   isEdit.value = false;
   let sublist = getSceneListTree(list.value);
   const apiData = {
-    // caseTitle: caseId.value,
     caseId: caseId.value,
     sceneNumParam: sublist,
   };
-  setCaseaddOrUpdate(apiData).then((res) => {
-    geiList(true);
-    ElMessage({
-      message: "操作成功",
-      type: "success",
+  setCaseaddOrUpdate(apiData)
+    .then((res) => {
+      geiList(true);
+      ElMessage({
+        message: "操作成功",
+        type: "success",
+      });
+    })
+    .catch((errr) => {
+      console.log("setCaseaddOrUpdateerrr", errr);
+      return geiList(true);
     });
-  }).catch((errr) => {
-    console.log("setCaseaddOrUpdateerrr", errr);
-    return geiList(true);
-  })
-};
-  // window.location.reload()
-  // console.log("handleActive", params);
+}
 async function handleAdd() {
-  // if(isEdit.value){
-  //   await confirm("请先保存当前移除的场景信息!")
-  //   return
-  // }
-  let numList = list.value.map((item) => item.num);
-  console.log("handleAdd", numList);
-  let val = await tableModelScene({ numList: numList });
-  geiList();
+  let numList = [], noEditList = [];
+  list.value.map((item) => {
+    // item.num
+    numList.push(item.num);
+    if(!item.editAuth) {
+      noEditList.push(item.num)
+    }
+  });
+  let val = await tableModelScene({ numList: numList, noEditList });
+  geiList(true);
   if (val) {
     console.log("刷新列表");
   }
 }
-</script>
-<style scoped>
-.anmc {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  width: 190px;
+function handleItem(item) {
+  console.log("handleItem", item);
+  if(!item.viewAuth) return ElMessage.error("您没有查看权限,请联系管理员开通");
+  sceneItem.value = item;
+  sceneURL.value = getUrlSrc({...sceneItem.value, type: sceneItem.value.sceneType}, caseId.value);
+  
 }
-.cursor-pointer {
-  color: var(--primaryColor);
+</script>
+
+<style scoped lang="scss">
+.scene {
+  height: 100%;
+
+  .scene-list {
+    width: var(--leftwidth);
+    height: 100%;
+    overflow-y: scroll;
+    overflow-x: hidden;
+    &::-webkit-scrollbar {
+      display: none;
+    }
+    .scene-title{
+      height: 64px;
+      padding: 24px 48px 0 48px;
+      .newbut{
+        position: relative;
+        left: -16px;
+      }
+    }
+    .list{
+      .listItem{
+        width: calc(var(--leftwidth) - 0px);
+        padding: 25px 48px;
+      }
+      .active{
+        background: rgba(69,144,255,0.1);;
+      }
+      .disabled{
+        color: rgba(0,0,0,0.3);
+        cursor:not-allowed;
+      }
+    }
+  }
+  .app-scene {
+    background: #f5f5f5;
+    width: calc(100% - var(--leftwidth));
+    position: relative;
+  border-right: 48px solid #fff;
+  border-bottom: 48px solid #fff;
+    .import {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      text-align: center;
+      margin-right: 20px;
+      padding-top: 40px;
+      height: 148px;
+      width: 148px;
+      text-align: center;
+      background: #fafafa;
+      cursor: pointer;
+      .noData{
+        position: absolute;
+        top: 140px;
+        width: 100%;
+        // transform: translate(50%, -50%);
+      }
+    }
+  }
+  .anmc {
+    width: 100%;
+    overflow: hidden;
+    margin-bottom: 16px;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    word-break: break-all;
+    /*! autoprefixer: off */
+    -webkit-box-orient: vertical;
+  }
+  .cursor-pointer {
+    color: var(--primaryColor);
+    text-align: right;
+  }
 }
 </style>

+ 2 - 2
src/view/vrmodel/list.vue

@@ -33,8 +33,8 @@ import { useScenePaggingParams } from "./pagging";
 defineProps<{ params: ReturnType<typeof useScenePaggingParams> }>();
 
 const headOptions = [
-  { value: SceneType.SWKK, name: SceneTypeDesc[SceneType.SWKK] },
-  { value: SceneType.SWKJ, name: SceneTypeDesc[SceneType.SWKJ] },
+  // { 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] },

+ 9 - 2
src/view/vrmodel/pagging.ts

@@ -6,7 +6,8 @@ export const useScenePaggingParams = () => {
   const pagging = usePagging({
     get: getScenePagging,
     paramsTemlate: {
-      isObj: SceneType.SWKK,
+      isObj: 1,
+      sceneType: 0, //SceneType.SWKK,
       sceneName: "",
       modelTitle: "",
       deptId: "",
@@ -25,6 +26,12 @@ export const useScenePaggingParams = () => {
       pagging.state.query.sceneName = val;
     },
   });
+  const sceneType = computed({
+    get: () => pagging.state.query.sceneType,
+    set: (val) => {
+      pagging.state.query.sceneType = val;
+    },
+  });
 
   let oldSnCode = pagging.state.query.snCode;
   watchEffect(() => {
@@ -50,6 +57,6 @@ export const useScenePaggingParams = () => {
     pagging.state.query.type = type;
   };
 
-  return reactive({ pagging, keyword, isSwmx });
+  return reactive({ pagging, keyword, isSwmx, sceneType });
 };
 export type ScenePagging = ReturnType<typeof useScenePaggingParams>["pagging"];

+ 1 - 1
src/view/vrmodel/quisk.ts

@@ -10,7 +10,7 @@ export const editModelScene = quiskMountFactory(EditModel, {
   width: 500,
 });
 export const tableModelScene = quiskMountFactory(tableModel, {
-  title: "添加场景",
+  title: "导入实景三维",
   width: 1000,
 });
 export type SceneDpwnloadProps = { scene: QuoteScene };

+ 22 - 10
src/view/vrmodel/sceneContent.vue

@@ -16,8 +16,8 @@
         {{ pagging.state.pag.size * (pagging.state.pag.currentPage - 1) + $index + 1 }}
       </div>
     </el-table-column> -->
-    <el-table-column label="场景标题" prop="sceneName"></el-table-column>
-    <el-table-column label="S/N码" prop="snCode"></el-table-column>
+    <el-table-column width="500" label="场景标题" show-overflow-tooltip prop="sceneName"></el-table-column>
+    <!-- <el-table-column label="案件名称" prop="snCode"></el-table-column> -->
     <!-- <el-table-column label="浏览数量" prop="viewCount"></el-table-column> -->
     <el-table-column label="拍摄时间" prop="createTime" v-slot:default="{ row }">
       {{ row.createTime.substr(0, 16) }}
@@ -94,6 +94,7 @@ import { QuoteSceneStatusDesc } from "@/constant/scene";
 import { OpenType, openSceneUrl } from "../case/help";
 import { confirm } from "@/helper/message";
 import { router } from "@/router";
+import { ElTable, ElMessageBox } from "element-plus";
 import { sceneDownload } from "./quisk";
 import { useScenePaggingParams, ScenePagging } from "./pagging";
 import { QuiskExpose } from "@/helper/mount";
@@ -106,7 +107,7 @@ import {
   setCaseaddOrUpdate,
 } from "@/store/case";
 // const params = useScenePaggingParams();
-const props = defineProps<{ pagging: ScenePagging, numList: Array<string> }>();
+const props = defineProps<{ pagging: ScenePagging, numList: Array<string>, noEditList: Array<string> }>();
 const caseId = computed(() => (router.currentRoute.value?.params?.caseId));
 const tableRef = ref(null);
 const caseScenes = ref([
@@ -116,7 +117,7 @@ const caseScenes = ref([
   },
   {
     type: 1,
-    numList: [],
+    numList: JSON.parse(JSON.stringify(props.numList)) || [],
   }
 ]);
 const selectList = ref({
@@ -124,13 +125,16 @@ const selectList = ref({
   1: [],
 })
 const pagScenes = props.pagging.state.table.rows;
-caseScenes.value = getSceneListTree()
-console.log('caseScenes', selectList, pagScenes);
+// caseScenes.value = getSceneListTree()
+console.log('caseScenes', selectList, pagScenes, props);
 const submit = async () => {
   const apiData = {
     caseId: caseId.value,
+    number: caseId.value,
     sceneNumParam: caseScenes.value,
   };
+  console.log('apiData', apiData);
+
   await setCaseaddOrUpdate(apiData)
   // params.pagging.queryReset()
   }
@@ -142,15 +146,24 @@ defineExpose<QuiskExpose>({
 
 const isObj = ref(props.pagging.state.query?.isObj);
 const delSceneHandler = async (scene: QuoteScene) => {
-  if (await confirm("确定要删除当前场景吗?")) {
+    ElMessageBox.confirm('确定要删除当前场景吗?', "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
     await delQuoteScene(scene);
     props.pagging.refresh();
-  }
+  });
+  // if (await confirm("确定要删除当前场景吗?")) {
+  //   await delQuoteScene(scene);
+  //   props.pagging.refresh();
+  // }
 };
 console.log('propsnumList', props.numList);
 const selectable = (row) => {
   // let selectlist = selectList.find(item => item.type == params.pagging.state.query?.isObj)?.numList;
-  if (row.inCase) return false;
+  // if (row.inCase) return false;
+  if (props.numList.includes(row.num)) return false;
   return row.statusString == '计算成功'// && !selectlist.includes(row.num)
 }
 
@@ -177,7 +190,6 @@ watchEffect(() => {
   )!.numList;
   props.pagging.state.table.rows
   // console.log('watchEffect', selectKeys);
-  console.log('watchEffect', caseScenes.value, selectKeys);
   changIng = true;
   props.pagging.state.table.rows.forEach((scene) => {
     tableRef.value!.toggleRowSelection(scene, selectKeys.includes(scene.num));

+ 30 - 24
src/view/vrmodel/tableModel.vue

@@ -4,24 +4,30 @@
       <el-form-item label="场景名称:" style="width: 250px">
         <el-input v-model="params.keyword" placeholder="请输入"></el-input>
       </el-form-item>
-      <!-- <el-form-item label="类型:" style="width: 250px">
+      <el-form-item style="width: 250px">
         <el-select
-          v-model="params.pagging.state.query.sceneType"
+          v-model="params.sceneType"
           placeholder="Select"
           size="large"
           style="width: 240px"
         >
-      <el-option
-        v-for="item in options"
-        :key="item.value"
-        :label="item.label"
-        :value="item.value"
-      />
-    </el-select>
-      </el-form-item> -->
+          <el-option
+            v-for="item in options"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
     </template>
     <template v-slot:content>
-      <component :is="component" :numList="numList" :pagging="params.pagging" ref="contentRef" />
+      <component
+        :is="component"
+        :numList="numList"
+        :noEditList="noEditList"
+        :pagging="params.pagging"
+        ref="contentRef"
+      />
     </template>
   </List>
 </template>
@@ -34,28 +40,28 @@ import ModelContent from "./modelContent.vue";
 import { useScenePaggingParams } from "./pagging";
 import { QuiskExpose } from "@/helper/mount";
 import { computed, ref } from "vue";
-defineProps<{ numList: Array<string> }>();
+defineProps<{ numList: Array<string>, noEditList: Array<string> }>();
 const options = [
   {
-    value: '1',
-    label: 'Mesh 场景',
+    value: 0,
+    label: "我的场景",
   },
   {
-    value: '2',
-    label: '点云场景',
+    value: 1,
+    label: "共享场景",
   },
-]
+];
 
 const params = useScenePaggingParams();
-console.log('params.isSwmx', params.isSwmx, 'ModelContent');
+console.log("params.isSwmx", params.isSwmx, "ModelContent");
 const component = computed(() => (params.isSwmx ? ModelContent : SceneContent));
-const  contentRef = ref(null);
+const contentRef = ref(null);
 const submit = async () => {
-    // replaceCaseScenes(props.caseId, caseScenes.value)
-    await contentRef.value?.submit()
-    // console.log("submit", caseScenes.value);
-  }
+  // replaceCaseScenes(props.caseId, caseScenes.value)
+  await contentRef.value?.submit();
+  // console.log("submit", caseScenes.value);
+};
 defineExpose<QuiskExpose>({
-  submit
+  submit,
 });
 </script>

+ 15 - 13
vite.config.ts

@@ -10,6 +10,7 @@ if (process.argv.length > 3) {
 }
 
 const dev = true;
+const url = 'http://192.168.0.125:1804';
 
 export default defineConfig({
   define: {
@@ -45,17 +46,21 @@ export default defineConfig({
     port: 5173,
     host: "0.0.0.0",
     proxy: {
-      "/api": {
-        target: dev ? "http://survey.4dkankan.com" : "mix3d.4dkankan.com",
+      '/api': {
+        target: url,
         changeOrigin: true,
-        secure: false,
-        rewrite: (path) => path.replace(new RegExp(`^/api`), ""),
+        // 正确重写路径(根据实际情况调整)
+        rewrite: (path) => path.replace(/^\/api/, '')
       },
       "/fusion": {
-        target: dev ? "http://survey.4dkankan.com" : "mix3d.4dkankan.com",
+        target: url,
         changeOrigin: true,
         secure: false,
-        rewrite: (path) => path.replace(new RegExp(`^/api`), "/fusion"),
+      },
+      "/service": {
+        target: url,
+        changeOrigin: true,
+        // 正确重写路径(根据实际情况调整)
       },
       "/dev-code": {
         // target: "https://localhost:7173/",
@@ -71,15 +76,12 @@ export default defineConfig({
       //   changeOrigin: true,
       //   rewrite: (path) => path.replace(new RegExp(`^/swkk`), ""),
       // },
-      "/service": {
-        target: dev ? "http://survey.4dkankan.com" : "https://www.4dkankan.com",
-        changeOrigin: true,
-      },
       "/swss": {
         target: dev
           ? "https://uat-laser.4dkankan.com/uat"
           : "https://laser.4dkankan.com",
         changeOrigin: true,
+        secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/swss`), ""),
       },
       "/swkk": {
@@ -87,13 +89,13 @@ export default defineConfig({
           ? "https://survey.4dkankan.com/swkk"
           : "https://laser.4dkankan.com",
         changeOrigin: true,
+        secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/swkk`), ""),
       },
       "/oss": {
-        target: dev
-          ? "https://survey.4dkankan.com/oss"
-          : "https://laser.4dkankan.com",
+        target: url + "/oss",
         changeOrigin: true,
+        secure: false,
         rewrite: (path) => path.replace(new RegExp(`^/oss`), ""),
       },
       "/laser": {