Pārlūkot izejas kodu

Merge branch 'xj' of http://192.168.0.115:3000/bill/public-fuse into xj

tangning 1 gadu atpakaļ
vecāks
revīzija
3c28fc1864
49 mainītis faili ar 51689 papildinājumiem un 186 dzēšanām
  1. 3 3
      package.json
  2. 30 0
      pnpm-lock.yaml
  3. 14 0
      public/xfile-viewer/.prettierrc
  4. 247 0
      public/xfile-viewer/index.html
  5. BIN
      public/xfile-viewer/publish/20231025172413.dcm
  6. BIN
      public/xfile-viewer/publish/20231025172413.raw
  7. 9084 0
      public/xfile-viewer/publish/lib/cornerstone.js
  8. 3 0
      public/xfile-viewer/publish/lib/cornerstoneMath.min.js
  9. 38415 0
      public/xfile-viewer/publish/lib/cornerstoneTools.js
  10. 2 0
      public/xfile-viewer/publish/lib/cornerstoneWADOImageLoader.bundle.min.js
  11. 3 0
      public/xfile-viewer/publish/lib/cornerstoneWebImageLoader.min.js
  12. 3 0
      public/xfile-viewer/publish/lib/dicomParser.min.js
  13. 2643 0
      public/xfile-viewer/publish/lib/hammer.js
  14. 1 0
      src/app/fire/routeConfig.ts
  15. 2 0
      src/app/fire/store/fire.ts
  16. 11 14
      src/app/fire/view/dispatch/editFire.vue
  17. 5 1
      src/assets/style/public.scss
  18. 9 0
      src/assets/style/theme/0084f4.scss
  19. 9 0
      src/assets/style/theme/009688.scss
  20. 9 0
      src/assets/style/theme/0096c7.scss
  21. 9 0
      src/assets/style/theme/0960bd.scss
  22. 9 0
      src/assets/style/theme/536dfe.scss
  23. 9 0
      src/assets/style/theme/9c27b0.scss
  24. 9 0
      src/assets/style/theme/ee4f12.scss
  25. 9 0
      src/assets/style/theme/ff5c93.scss
  26. 9 0
      src/assets/style/theme/ff9800.scss
  27. 11 12
      src/components/head/index.vue
  28. 2 2
      src/constant/caseFile.ts
  29. 1 1
      src/constant/scene.ts
  30. 2 0
      src/hook/upload.ts
  31. 4 2
      src/main.ts
  32. 2 0
      src/request/config.ts
  33. 87 77
      src/request/urls.ts
  34. 6 0
      src/router/config.ts
  35. 2 0
      src/router/routeName.ts
  36. 72 0
      src/setSystem.ts
  37. 12 0
      src/store/case.ts
  38. 5 4
      src/store/permission.ts
  39. 1 1
      src/store/scene.ts
  40. 73 0
      src/util/index.ts
  41. 12 1
      src/view/case/caseFile.vue
  42. 1 1
      src/view/case/draw/slider.vue
  43. 60 48
      src/view/case/photos/index.vue
  44. 355 3
      src/view/case/records/index.vue
  45. 344 0
      src/view/case/records/manifest.vue
  46. 2 2
      src/view/layout/slide/index.vue
  47. 50 0
      src/view/setting/index.vue
  48. 33 2
      src/view/system/imageCropper.vue
  49. 5 12
      vite.config.ts

+ 3 - 3
package.json

@@ -5,8 +5,8 @@
   "type": "module",
   "scripts": {
     "dev": "vite",
-    "build": "vue-tsc --noEmit && npm run build-quisk",
-    "build-quisk": "vite build ./ fire && vite build ./ criminal && vite build ./ xmfire",
+    "build": "npm run build-quisk",
+    "build-quisk": "vite build ./ fire ",
     "preview": "vite preview"
   },
   "dependencies": {
@@ -36,4 +36,4 @@
     "vite": "^4.4.5",
     "vue-tsc": "^1.8.5"
   }
-}
+}

+ 30 - 0
pnpm-lock.yaml

@@ -41,6 +41,9 @@ importers:
       sass:
         specifier: ^1.64.2
         version: 1.77.6
+      swiper:
+        specifier: ^11.1.4
+        version: 11.1.4
       unplugin-element-plus:
         specifier: ^0.7.2
         version: 0.7.2(rollup@3.29.4)
@@ -53,6 +56,9 @@ importers:
       vue-cropper:
         specifier: ^1.0.9
         version: 1.1.3
+      vue-draggable-plus:
+        specifier: ^0.5.0
+        version: 0.5.0(@types/sortablejs@1.15.8)
       vue-router:
         specifier: ^4.2.4
         version: 4.4.0(vue@3.4.31(typescript@5.5.3))
@@ -275,6 +281,9 @@ packages:
   '@types/qs@6.9.15':
     resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==}
 
+  '@types/sortablejs@1.15.8':
+    resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
+
   '@types/web-bluetooth@0.0.16':
     resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
 
@@ -665,6 +674,10 @@ packages:
     resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
 
+  swiper@11.1.4:
+    resolution: {integrity: sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==}
+    engines: {node: '>= 4.7.0'}
+
   to-fast-properties@2.0.0:
     resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
     engines: {node: '>=4'}
@@ -741,6 +754,15 @@ packages:
       '@vue/composition-api':
         optional: true
 
+  vue-draggable-plus@0.5.0:
+    resolution: {integrity: sha512-A5TT5+M5JceROSjPO9aDZTsrSN1TetEs419czPlboomarSiGIBIxTp2WD7XH53EHMrbO7Qo+leRiHWV/rMlyjA==}
+    peerDependencies:
+      '@types/sortablejs': ^1.15.0
+      '@vue/composition-api': '*'
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   vue-router@4.4.0:
     resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
     peerDependencies:
@@ -900,6 +922,8 @@ snapshots:
 
   '@types/qs@6.9.15': {}
 
+  '@types/sortablejs@1.15.8': {}
+
   '@types/web-bluetooth@0.0.16': {}
 
   '@vitejs/plugin-vue@4.6.2(vite@4.5.3(@types/node@20.14.9)(sass@1.77.6))(vue@3.4.31(typescript@5.5.3))':
@@ -1349,6 +1373,8 @@ snapshots:
 
   source-map-js@1.2.0: {}
 
+  swiper@11.1.4: {}
+
   to-fast-properties@2.0.0: {}
 
   to-regex-range@5.0.1:
@@ -1404,6 +1430,10 @@ snapshots:
     dependencies:
       vue: 3.4.31(typescript@5.5.3)
 
+  vue-draggable-plus@0.5.0(@types/sortablejs@1.15.8):
+    dependencies:
+      '@types/sortablejs': 1.15.8
+
   vue-router@4.4.0(vue@3.4.31(typescript@5.5.3)):
     dependencies:
       '@vue/devtools-api': 6.6.3

+ 14 - 0
public/xfile-viewer/.prettierrc

@@ -0,0 +1,14 @@
+{
+    "printWidth": 200,
+    "tabWidth": 4,
+    "useTabs": false,
+    "semi": false,
+    "singleQuote": true,
+    "arrowParens": "avoid",
+    "bracketSpacing": true,
+    "disableLanguages": [],
+    "eslintIntegration": false,
+    "stylelintIntegration": false,
+    "tslintIntegration": false,
+    "proseWrap": "preserve"
+}

+ 247 - 0
public/xfile-viewer/index.html

@@ -0,0 +1,247 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+        <title>文档浏览</title>
+        <script src="./publish/lib/hammer.js"></script>
+        <script src="./publish/lib/cornerstone.js"></script>
+        <script src="./publish/lib/cornerstoneMath.min.js"></script>
+        <script src="./publish/lib/cornerstoneWADOImageLoader.bundle.min.js"></script>
+        <script src="./publish/lib/cornerstoneWebImageLoader.min.js"></script>
+        <script src="./publish/lib/cornerstoneTools.js"></script>
+        <script src="./publish/lib/dicomParser.min.js"></script>
+        <style>
+            .disabled {
+                opacity: 0.5;
+                pointer-events: none;
+            }
+            select {
+                outline: none;
+            }
+            html,
+            body {
+                width: 100%;
+                height: 100%;
+            }
+            body {
+                margin: 0;
+                overflow: hidden;
+            }
+            #dicomImage {
+                width: 100%;
+                height: 100%;
+            }
+            #toolbar {
+                position: absolute;
+                left: 50%;
+                top: 24px;
+                padding: 10px;
+                transform: translateX(-50%);
+                background-color: #fff;
+                border: solid 1px #e5e5e5;
+                border-radius: 6px;
+                z-index: 999;
+                display: flex;
+                align-items: center;
+                justify-content: space-around;
+                box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
+                font-size: 12px;
+            }
+            #toolbar > div {
+                cursor: pointer;
+                padding: 0 5px;
+            }
+            #toolbar > div.active {
+                color: #1779ed;
+            }
+            #statbar {
+                cursor: pointer;
+                position: absolute;
+                left: 50%;
+                bottom: 24px;
+                padding: 10px;
+                transform: translateX(-50%);
+                background-color: #fff;
+                border: solid 1px #e5e5e5;
+                border-radius: 6px;
+                z-index: 999;
+                display: none;
+                align-items: center;
+                justify-content: space-around;
+                box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
+                font-size: 12px;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="dicomImage"></div>
+        <div id="toolbar">
+            <div id="Length">
+                标注:
+                <select onchange="tools.annotation(this.value)">
+                    <option value="">请选择</option>
+                    <option value="ArrowAnnotate">箭头</option>
+                    <option value="Length">长度</option>
+                    <option value="Angle">角度</option>
+                    <option value="RectangleRoi">矩形</option>
+                    <option value="EllipticalRoi">椭圆</option>
+                    <option value="FreehandRoi">面积</option>
+                    <option value="Probe">针探</option>
+                </select>
+            </div>
+            <div id="Wwwc" onclick="tools.wwwc()">调整窗宽窗高</div>
+            <div onclick="tools.download()">下载</div>
+        </div>
+        <div id="statbar" onclick="tools.annotation('')">退出</div>
+        <script>
+            const urlParams = key => {
+                let querys = window.location.search.substr(1).split('&')
+                for (let i = 0; i < querys.length; i++) {
+                    let keypair = querys[i].split('=')
+                    if (keypair.length === 2 && keypair[0] === key) {
+                        return keypair[1]
+                    }
+                }
+                return ''
+            }
+
+            const tools = {
+                name: '',
+                set active(name) {
+                    document.querySelectorAll('#toolbar >div').forEach(el => el.classList.remove('active'))
+                    if (name) {
+                        this.name = name
+                        document.getElementById(name).classList.add('active')
+                    } else {
+                        this.name = ''
+                    }
+                },
+                download() {
+                    var link = document.createElement('a')
+                    link.download = 'preview.png'
+                    link.href = document.querySelector('canvas').toDataURL('image/png').replace('image/png', 'image/octet-stream')
+                    link.click()
+                },
+                wwwc() {
+                    const name = 'Wwwc'
+                    if (this.name == name) {
+                        cornerstoneTools.setToolActive('Pan', {
+                            mouseButtonMask: 1,
+                        })
+                        this.active = ''
+                    } else {
+                        cornerstoneTools.setToolActive('Wwwc', {
+                            mouseButtonMask: 1,
+                        })
+                        this.active = name
+                    }
+                },
+                annotation(name) {
+                    const wwwc = document.getElementById('Wwwc')
+                    const leave = document.getElementById('statbar')
+                    const select = document.querySelector('select')
+                    if (name) {
+                        if (this.name == 'Wwwc') {
+                            this.wwwc()
+                        }
+                        wwwc.classList.add('disabled')
+                        leave.style.display = 'flex'
+                        select.disabled = true
+                        cornerstoneTools.setToolActive(name, { mouseButtonMask: 1 })
+                    } else {
+                        wwwc.classList.remove('disabled')
+                        leave.style.display = 'none'
+                        select.disabled = false
+                        document.querySelector('select').value = ''
+                        cornerstoneTools.setToolPassive(this.name)
+                        cornerstoneTools.setToolActive('Pan', {
+                            mouseButtonMask: 1,
+                        })
+                    }
+                    this.name = name
+                },
+            }
+            // 注册并挂载cornerstone及其cornerstoneTools,固定操作
+            cornerstoneTools.external.cornerstone = cornerstone
+            cornerstoneTools.external.cornerstoneMath = cornerstoneMath
+            cornerstoneTools.external.Hammer = Hammer
+            cornerstoneWADOImageLoader.external.dicomParser = dicomParser
+            cornerstoneWADOImageLoader.external.cornerstone = cornerstone
+            var file = urlParams('file')
+            if (!file) {
+                alert('文档不能为空')
+            } else {
+                var imageId = 'wadouri: ' + decodeURIComponent(file) //http://192.168.0.11:80/20231025172413.dcm'
+                // 初始化cornerstoneTools工具
+                cornerstoneTools.init([
+                    {
+                        moduleName: 'globalConfiguration',
+                        configuration: {
+                            showSVGCursors: true,
+                        },
+                    },
+                    {
+                        moduleName: 'segmentation',
+                        configuration: {
+                            outlineWidth: 2,
+                        },
+                    },
+                ])
+
+                // 获取要用于加载图片的div区域
+                var element = document.getElementById('dicomImage')
+                //激活获取到的用于图片加载的区域
+                cornerstone.enable(element)
+                //   // 从cornerstoneTools库中获取窗宽,窗高工具
+                //   const WwwcTool = cornerstoneTools.WwwcTool;
+                //   //添加获取到的窗宽,窗高工具
+                //   cornerstoneTools.addTool(WwwcTool);
+                //   // 绑定工具操作功能到鼠标左键
+                //   cornerstoneTools.setToolActive("Wwwc", {
+                //     mouseButtonMask: 1,
+                //   });
+                //使用loadAndCacheImage()方法加载并缓存图片,然后使用displayImage()方法显示图片。
+                cornerstone.loadAndCacheImage(imageId).then(function (image) {
+                    cornerstone.displayImage(element, image)
+                })
+
+                /* 平移、缩放  */
+                const PanTool = cornerstoneTools.PanTool
+                cornerstoneTools.addTool(PanTool)
+                cornerstoneTools.setToolActive('Pan', { mouseButtonMask: 1 })
+
+                const ZoomMouseWheelTool = cornerstoneTools.ZoomMouseWheelTool
+                cornerstoneTools.addTool(ZoomMouseWheelTool)
+                cornerstoneTools.setToolActive('ZoomMouseWheel', { mouseButtonMask: 1 })
+
+                /*  标注工具 */
+                // 长度标注
+                const LengthTool = cornerstoneTools.LengthTool
+                cornerstoneTools.addTool(LengthTool)
+
+                const AngleTool = cornerstoneTools.AngleTool
+                cornerstoneTools.addTool(AngleTool)
+
+                const ArrowAnnotateTool = cornerstoneTools.ArrowAnnotateTool
+                cornerstoneTools.addTool(ArrowAnnotateTool)
+
+                const RectangleRoiTool = cornerstoneTools.RectangleRoiTool
+                cornerstoneTools.addTool(RectangleRoiTool)
+
+                const EllipticalRoiTool = cornerstoneTools.EllipticalRoiTool
+                cornerstoneTools.addTool(EllipticalRoiTool)
+
+                const FreehandRoiTool = cornerstoneTools.FreehandRoiTool
+                cornerstoneTools.addTool(FreehandRoiTool)
+
+                const ProbeTool = cornerstoneTools.ProbeTool
+                cornerstoneTools.addTool(ProbeTool)
+
+                const WwwcTool = cornerstoneTools.WwwcTool
+                //添加获取到的窗宽,窗高工具
+                cornerstoneTools.addTool(WwwcTool)
+            }
+        </script>
+    </body>
+</html>

BIN
public/xfile-viewer/publish/20231025172413.dcm


BIN
public/xfile-viewer/publish/20231025172413.raw


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 9084 - 0
public/xfile-viewer/publish/lib/cornerstone.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 0
public/xfile-viewer/publish/lib/cornerstoneMath.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 38415 - 0
public/xfile-viewer/publish/lib/cornerstoneTools.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 0
public/xfile-viewer/publish/lib/cornerstoneWADOImageLoader.bundle.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 0
public/xfile-viewer/publish/lib/cornerstoneWebImageLoader.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 0
public/xfile-viewer/publish/lib/dicomParser.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2643 - 0
public/xfile-viewer/publish/lib/hammer.js


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

@@ -17,6 +17,7 @@ export const menuRouteNames = [
   FireRouteName.role,
   FireRouteName.user,
   FireRouteName.downloadLog,
+  FireRouteName.setting,
 ];
 
 export const routes: Routes = [

+ 2 - 0
src/app/fire/store/fire.ts

@@ -24,6 +24,7 @@ export type Fire = {
   createTime: string;
   creatorDeptId: string;
   caseId: number;
+  mapShow: boolean;
   creatorId: string;
   creatorName: string;
   deptId: string;
@@ -36,6 +37,7 @@ export type Fire = {
   organizerDeptName: string;
   organizerUsers: string;
   projectAddress: string;
+  latAndLong: string;
   latlng: string;
   projectName: string;
   projectSite: string;

+ 11 - 14
src/app/fire/view/dispatch/editFire.vue

@@ -99,15 +99,9 @@
         </el-form-item>
       </el-col>
       <el-col :span="12">
-        <!-- <el-form-item label="火灾原因" class="mandatory">
-          <el-cascader
-            style="width: 100%"
-            v-model="fireReason"
-            placeholder="火灾原因:"
-            :options="reason"
-            :props="{ expandTrigger: 'hover' }"
-          />
-        </el-form-item> -->
+        <el-form-item label="大屏显示" class="mandatory">
+          <el-switch v-model="bindFire.mapShow" :disabled="!bindFire.latlng" />
+        </el-form-item>
       </el-col>
     </div>
   </el-form>
@@ -141,7 +135,7 @@ const projectSite = genCascaderValue(bindFire, "projectSite");
 const accidentDate = ref(
   bindFire.value.accidentDate ? new Date(bindFire.value.accidentDate) : new Date()
 );
-const keyword = ref("");
+const keyword = ref(bindFire.value.projectAddress || "");
 const resultEl = ref<HTMLDivElement>();
 const searchAMap = ref<any>();
 type MapInfo = { lat: number; lng: number };
@@ -175,7 +169,9 @@ watchEffect(async (onCleanup) => {
     bindFire.value.projectAddress =
       e.data.pname + e.data.cityname + e.data.adname + e.data.address;
     keyword.value = bindFire.value.projectAddress;
-    bindFire.value.latlng = `${e.data.lat},${e.data.lng}`;
+    console.log(e.data);
+    bindFire.value.latAndLong = `${e.data.location.lat},${e.data.location.lng}`;
+    bindFire.value.latlng = `${e.data.location.lat},${e.data.location.lng}`;
     info.value = {
       lat: e.data.lat,
       lng: e.data.lng,
@@ -218,10 +214,11 @@ defineExpose<QuiskExpose>({
     } else if (!accidentDate) {
       ElMessage.error("勘验日期不能为空!");
       throw "勘验日期不能为空!";
-    } else if (!bindFire.value.fireReason || !bindFire.value.fireReason.trim()) {
-      ElMessage.error("火灾原因不能为空!");
-      throw "火灾原因不能为空!";
     }
+    // else if (!bindFire.value.fireReason || !bindFire.value.fireReason.trim()) {
+    //   ElMessage.error("火灾原因不能为空!");
+    //   throw "火灾原因不能为空!";
+    // }
 
     bindFire.value.accidentDate = dateFormat(accidentDate.value, "yyyy-MM-dd");
     bindFire.value.projectSiteCode = getCode(place, bindFire.value.projectSite);

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

@@ -568,4 +568,8 @@ html .el-input-group__append button.el-button {
 
 .el-color-predefine__color-selector {
   border: 1px solid #e5e5e5;
-}
+}
+
+// :root {
+//   --el-color-primary: green;
+// }

+ 9 - 0
src/assets/style/theme/0084f4.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #0084f4, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #0084f4;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/009688.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #009688, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/0096c7.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #0096c7, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/0960bd.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #0960bd, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/536dfe.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #536dfe, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/9c27b0.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #9c27b0, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/ee4f12.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #ee4f12, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/ff5c93.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #ff5c93, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 9 - 0
src/assets/style/theme/ff9800.scss

@@ -0,0 +1,9 @@
+@forward 'element-plus/theme-chalk/src/common/var.scss'with ($colors: ('primary': ('base': #ff9800, )),
+  $common-component-size: ('default': 40px));
+
+@use "element-plus/theme-chalk/src/index.scss"as *;
+
+.delBtn {
+  color   : #D8000A;
+  // color: rgb(250, 85, 85);
+}

+ 11 - 12
src/components/head/index.vue

@@ -1,16 +1,11 @@
 <template>
   <div class="head-layer">
-    <el-tabs
-      :modelValue="modelValue"
-      @update:modelValue="(str: any) => updateModelValue(str)"
-    >
-      <el-tab-pane
-        v-for="item in options"
-        :key="item.value"
-        :label="item.name"
-        :name="item.value"
-      >
+    <el-tabs :modelValue="modelValue" @update:modelValue="(str: any) => updateModelValue(str)">
+
+      <el-tab-pane v-for="item in options" :key="item.value" :label="item.name"
+        :name="item.value">
       </el-tab-pane>
+
     </el-tabs>
     <div class="head-content-layer" :class="{ show: show }" v-if="!notContent">
       <div class="head-content">
@@ -19,11 +14,15 @@
       <div class="display" @click="show = !show" v-if="showCtrl">
         <template v-if="show">
           <span>收起</span>
-          <el-icon><ArrowUp /></el-icon>
+          <el-icon>
+            <ArrowUp />
+          </el-icon>
         </template>
         <template v-else>
           <span>展开</span>
-          <el-icon><ArrowDown /></el-icon>
+          <el-icon>
+            <ArrowDown />
+          </el-icon>
         </template>
       </div>
     </div>

+ 2 - 2
src/constant/caseFile.ts

@@ -1,13 +1,13 @@
 import { BoardType } from "@/store/caseFile";
 
 export const FileDrawType = 1;
-export const DrawFormats = [".jpg", ".jpeg", ".png"];
+export const DrawFormats = [".jpg", ".jpeg", ".png", ".raw"];
 export const OtherFormats = [".pdf", ".jpeg", ".doc", ".docx", ".jpg", ".png"];
 export const BoardTypeDesc = {
   [BoardType.scene]: "现场图",
   [BoardType.map]: "方位图",
 };
-export const DrawFormatDesc = "jpg、png等格式的文件";
+export const DrawFormatDesc = "jpg、png、raw等格式的文件";
 export const OtherFormatDesc = "pdf、word、jpg、png等格式的文件";
 
 export const maxFileSize = 100 * 1024 * 1024;

+ 1 - 1
src/constant/scene.ts

@@ -54,6 +54,6 @@ export const ModelSceneStatusDesc: { [key in ModelSceneStatus]: string } = {
   [ModelSceneStatus.SUCCESS]: "成功",
 };
 
-export const ModelSupportType = ["obj", "ply", "las", "osgb", "b3dm"];
+export const ModelSupportType = ["obj", "ply", "las", "osgb", "b3dm", "laz"];
 export const ModelSupportFormats = [".zip"];
 export const ModelMaxSize = 1024 * 1024 * 1024;

+ 2 - 0
src/hook/upload.ts

@@ -68,6 +68,8 @@ export const useUpload = <T>(props: UploadProps<T>) => {
         file,
         (val) => (percentage.value = val)
       );
+      if (fileType === ".RAW") {
+      }
       percentage.value = undefined;
       return true;
     }

+ 4 - 2
src/main.ts

@@ -1,6 +1,5 @@
 import { createApp } from "vue";
-import "element-plus/dist/index.css";
-import "@/assets/style/public.scss";
+// import "element-plus/dist/index.css";
 import * as ElementPlusIconsVue from "@element-plus/icons-vue";
 import directiveSetup from "./directive/setup";
 import App from "./App.vue";
@@ -9,6 +8,7 @@ import { setApp } from "@/helper/mount";
 import { router } from "./router";
 import { appConstant } from "./app";
 import "@/store/system";
+import "@/assets/style/public.scss";
 
 const app = createApp(App);
 
@@ -26,3 +26,5 @@ $ico.setAttribute("rel", "icon");
 $ico.setAttribute("type", "image/svg+xml");
 $ico.setAttribute("href", appConstant.ico);
 document.head.appendChild($ico);
+
+import "./setSystem";

+ 2 - 0
src/request/config.ts

@@ -10,6 +10,7 @@ import {
   getModelSceneList,
   getRoleList,
   getSceneList,
+  getSysSetting,
   insertCaseFile,
   saveCaseFileInfo,
   sendUserMsg,
@@ -28,6 +29,7 @@ export const notLoginUrls = [
   userLogin,
   getCode,
   sendUserMsg,
+  getSysSetting,
   userReg,
   updatePsw,
   getCompanyList,

+ 87 - 77
src/request/urls.ts

@@ -1,45 +1,45 @@
-export const uploadFile = "/fusion/upload/file";
+export const uploadFile = "/fusion-xj/upload/file";
 
 /**  ----------------角色接口----------------   */
 export const getRoleList = "/web/role/getAllRoleList";
-export const getListByDeptId = "/fusion/web/role/getAllRoleList";
+export const getListByDeptId = "/fusion-xj/web/role/getAllRoleList";
 
 /** ------------------------------------------ */
 
 /**  ----------------用户接口----------------   */
 // 登录
-export const userLogin = "/fusion/fdLogin";
+export const userLogin = "/fusion-xj/fdLogin";
 // 权限
-export const userperInfo = "/fusion/web/user/getPerInfo";
-export const userInfo = "/fusion/web/user/getUserInfo";
+export const userperInfo = "/fusion-xj/web/user/getPerInfo";
+export const userInfo = "/fusion-xj/web/user/getUserInfo";
 // 注册
 export const userReg = "/web/user/register";
 // 发送注册短信
-export const sendUserMsg = "/fusion/notAuth/getMsgAuthCode";
+export const sendUserMsg = "/fusion-xj/notAuth/getMsgAuthCode";
 // 修改密码
-export const updatePsw = "/fusion/notAuth/changePassword";
+export const updatePsw = "/fusion-xj/notAuth/changePassword";
 // 新增用户
-export const userAdd = "/fusion/web/user/addUser";
+export const userAdd = "/fusion-xj/web/user/addUser";
 //修改用户
-export const userEdit = "/fusion/web/user/editUser";
+export const userEdit = "/fusion-xj/web/user/editUser";
 // 删除用户
-export const deleUser = "/fusion/web/user/delUser";
+export const deleUser = "/fusion-xj/web/user/delUser";
 // 获取用户信息
 export const getUserInfo = "/web/user/getUserInfo";
 
 // 转换四维场景的token
-export const getSWToken = "/fusion/scene/getFdTokenByNum";
+export const getSWToken = "/fusion-xj/scene/getFdTokenByNum";
 
 // 登出
-export const userLogout = "/fusion/fdLogout";
+export const userLogout = "/fusion-xj/fdLogout";
 // 获取用户列表
-export const getUserList = "/fusion/web/user/getUserList"; //修改2.0   getUserList ==》》 getPageList
+export const getUserList = "/fusion-xj/web/user/getUserList"; //修改2.0   getUserList ==》》 getPageList
 // 修改用户信息
 export const updateUser = "/web/user/changDeptAndRole";
 // 获取验证码
-export const getCode = "/fusion/notAuth/getLoginAuthCode";
+export const getCode = "/fusion-xj/notAuth/getLoginAuthCode";
 // 修改用户状态
-export const changeUserStatus = "/fusion/web/user/changeStatus";
+export const changeUserStatus = "/fusion-xj/web/user/changeStatus";
 export const updateUserPWD = "";
 /** ------------------------------------------ */
 
@@ -63,29 +63,29 @@ export const getCompanyList = "/web/department/getAll";
 
 /** ----------------VR模型接口---------------- */
 // 获取场景列表
-export const getSceneList = "/fusion/scene/list";
-export const delScene = "/fusion/scene/deleteNum";
-export const copyScene = "/fusion/scene/deleteNum";
-export const checkGenMeshScene = "/fusion/scene/sceneDetail";
-export const genMeshSceneByCloud = "/fusion/scene/buildSceneObj";
+export const getSceneList = "/fusion-xj/scene/list";
+export const delScene = "/fusion-xj/scene/deleteNum";
+export const copyScene = "/fusion-xj/scene/copyScene";
+export const checkGenMeshScene = "/fusion-xj/scene/sceneDetail";
+export const genMeshSceneByCloud = "/fusion-xj/scene/buildSceneObj";
 
 // 统计
-export const sceneStatistics = `/fusion/data/sceneGroupByDept`;
-export const caseStatistics = `/fusion/data/projectGroupByDept`;
-export const cameraTypeStatistics = `/fusion/data/cameraGroupType`;
-export const caseTimeStatistics = `/fusion/data/FireTrend`;
-export const casePlaceStatistics = `/fusion/data/FirePlaceTrend`;
-export const caseReasonStatistics = `/fusion/data/FireReasonTrend`;
+export const sceneStatistics = `/fusion-xj/data/sceneGroupByDept`;
+export const caseStatistics = `/fusion-xj/data/projectGroupByDept`;
+export const cameraTypeStatistics = `/fusion-xj/data/cameraGroupType`;
+export const caseTimeStatistics = `/fusion-xj/data/FireTrend`;
+export const casePlaceStatistics = `/fusion-xj/data/FirePlaceTrend`;
+export const caseReasonStatistics = `/fusion-xj/data/FireReasonTrend`;
 
 // 获取模型场景列表
-export const getModelSceneList = `/fusion/model/list`;
-export const updateModelScene = `/fusion/model/updateTitle`;
+export const getModelSceneList = `/fusion-xj/model/list`;
+export const updateModelScene = `/fusion-xj/model/updateTitle`;
 // 取消模型场景上传
-export const cancelUModel = `/fusion/model/cancelUpload`;
-export const deleteModel = `/fusion/model/delete`;
-export const copyModel = `/fusion/model/copy`;
-export const uploadModel = `/fusion/model/uploadObj`;
-export const getModelRunProgress = `/fusion/model/uploadObjProgress`;
+export const cancelUModel = `/fusion-xj/model/cancelUpload`;
+export const deleteModel = `/fusion-xj/model/delete`;
+export const copyModel = `/fusion-xj/model/copyModel`;
+export const uploadModel = `/fusion-xj/model/uploadObj`;
+export const getModelRunProgress = `/fusion-xj/model/uploadObjProgress`;
 
 // 通过场景标题模糊搜索场景
 export const getSceneByTitle = "/web/scene/getLocalScenes";
@@ -98,22 +98,22 @@ export const deleteScene = "/web/scene/delete";
 /** ------------------------------------------ */
 
 // ---------example案件接口--------
-export const exampleList = "/fusion/case/list";
-export const setExample = "/fusion/case/addOrUpdate";
-export const deleteExample = "/fusion/case/delete";
+export const exampleList = "/fusion-xj/case/list";
+export const setExample = "/fusion-xj/case/addOrUpdate";
+export const deleteExample = "/fusion-xj/case/delete";
 
 /**  case接口 */
 // 获取case场景列表
-export const caseSceneList = `/fusion/case/sceneList`;
-export const repCaseScenes = `/fusion/case/addScene`;
-export const syncInfo = `/fusion/caseLive/getTakeLookRoom`;
+export const caseSceneList = `/fusion-xj/case/sceneList`;
+export const repCaseScenes = `/fusion-xj/case/addScene`;
+export const syncInfo = `/fusion-xj/caseLive/getTakeLookRoom`;
 
 // 获取caseTaggings
-export const caseTaggingList = `/fusion/caseTag/allList`;
+export const caseTaggingList = `/fusion-xj/caseTag/allList`;
 
 /** ----------------相机接口----------------  getUserCameraList  ==>> getPageList /web/camera/getListByUser */
 // 获取相机列表
-export const getCameraList = "/fusion/web/camera/getUserCameraList";
+export const getCameraList = "/fusion-xj/web/camera/getUserCameraList";
 export const getListByUser = "/web/camera/getListByUser";
 // 获取相机选项
 export const getCameraOptions =
@@ -122,72 +122,79 @@ export const getCameraOptions =
 export const getCameraListByUser =
   "/web/camera/getCameraListByUser?pageNum=1&pageSize=100000";
 // 添加相机
-export const insertCamera = "/fusion/web/camera/bindNew";
+export const insertCamera = "/fusion-xj/web/camera/bindNew";
 // 添加相机管理人员
-export const getUserListSelect = "/fusion/web/user/getUserListSelect";
+export const getUserListSelect = "/fusion-xj/web/user/getUserListSelect";
 // 修改相机
-export const updateCamera = "/fusion/web/camera/edit";
+export const updateCamera = "/fusion-xj/web/camera/edit";
 // 删除相机
 export const deleteCamera = "/api/scene/camera/delete";
 // 相机绑定设备
-export const bindCamera = "/fusion/web/camera/bindNew";
+export const bindCamera = "/fusion-xj/web/camera/bindNew";
 // 相机解除绑定设备
-export const unbindCamera = "/fusion/web/camera/unbind";
+export const unbindCamera = "/fusion-xj/web/camera/unbind";
 /** ------------------------------------------ */
 
 /** ----------------组织架构---------------- */
 
-export const getTreeselect = "/fusion/web/department/treeselect";
-export const delTreeitem = "/fusion/web/department/del/";
-export const editTreeitem = "/fusion/web/department/edit";
-export const addTreeitem = "/fusion/web/department/add";
+export const getTreeselect = "/fusion-xj/web/department/treeselect";
+export const delTreeitem = "/fusion-xj/web/department/del/";
+export const editTreeitem = "/fusion-xj/web/department/edit";
+export const addTreeitem = "/fusion-xj/web/department/add";
 export const checkUserCamera = "/web/department/checkUserCamera/";
 /** ----------------火调项目---------------- */
 // 获取火调列表
-export const getFireList = "/fusion/web/fireProject/queryProject";
+export const getFireList = "/fusion-xj/web/fireProject/queryProject";
 // 新增火调
-export const insertFire = "/fusion/web/fireProject/addNewProject";
+export const insertFire = "/fusion-xj/web/fireProject/addNewProject";
 // 火调设置为教学
-export const deleteAttach = "/fusion/web/fireProject/setTeach";
+export const deleteAttach = "/fusion-xj/web/fireProject/setTeach";
 // 取消教学
-export const setUnTeach = "/fusion/web/fireProject/setUnTeach";
+export const setUnTeach = "/fusion-xj/web/fireProject/setUnTeach";
 
 // 修改火调
-export const updateFire = "/fusion/web/fireProject/updateProject";
+export const updateFire = "/fusion-xj/web/fireProject/updateProject";
 // 火调设置为教学
-export const setTeach = "/fusion/web/fireProject/setTeach";
+export const setTeach = "/fusion-xj/web/fireProject/setTeach";
 // 火调链接地址设置密码
-export const fireSetPsw = "/fusion/web/fireProject/updateRandomCode";
-export const getFirePsw = "/fusion/web/fireProject/getRandCode";
+export const fireSetPsw = "/fusion-xj/web/fireProject/updateRandomCode";
+export const getFirePsw = "/fusion-xj/web/fireProject/getRandCode";
 
 // 获取火调详情
-export const fireDetail = "/fusion/web/fireProject/getDetailWithoutAuth";
+export const fireDetail = "/fusion-xj/web/fireProject/getDetailWithoutAuth";
 // 获取火调详情
 // export const fireDetailByPsw = '/web/fireProject/getDetailWithoutAuth'
 
 // 获取火调详情
-export const fireDetailByPsw = "/fusion/web/fireProject/getDetailWithoutAuth"; //wxAnonGetDetail
+export const fireDetailByPsw =
+  "/fusion-xj/web/fireProject/getDetailWithoutAuth"; //wxAnonGetDetail
 /** ------------------------------------------ */
 
 // case相关
-export const caseInfo = "/fusion/case/getInfo";
-export const caseFileTypes = "/fusion/caseFilesType/allList";
-export const caseFiles = "/fusion/caseFiles/allList";
-export const caseFileInfo = "/fusion/caseFiles/info";
-export const saveCaseFileInfo = "/fusion/caseFiles/addOrUpdateImg";
-export const insertCaseFile = "/fusion/caseFiles/add";
-export const deleteCaseFile = "/fusion/caseFiles/delete";
-export const updateCaseFile = "/fusion/caseFiles/updateTitle";
+export const caseInfo = "/fusion-xj/case/getInfo";
+export const caseFileTypes = "/fusion-xj/caseFilesType/allList";
+export const caseFiles = "/fusion-xj/caseFiles/allList";
+export const caseFileInfo = "/fusion-xj/caseFiles/info";
+export const saveCaseFileInfo = "/fusion-xj/caseFiles/addOrUpdateImg";
+export const insertCaseFile = "/fusion-xj/caseFiles/add";
+export const deleteCaseFile = "/fusion-xj/caseFiles/delete";
+export const updateCaseFile = "/fusion-xj/caseFiles/updateTitle";
+
+//勘验笔录信息
+export const caseInquestInfo = "/fusion-xj/caseInquest/info";
+export const caseInquestOpt = "/fusion-xj/caseInquest/saveOrUpdate";
+export const caseInquestExport = "/fusion-xj/caseInquest/downDocx";
+
 
 // 火调链接地址设置密码
-export const setCasePsw = "/fusion/web/fireProject/updateRandomCode";
-export const getCasePsw = "/fusion/web/fireProject/getRandCode";
+export const setCasePsw = "/fusion-xj/web/fireProject/updateRandomCode";
+export const getCasePsw = "/fusion-xj/web/fireProject/getRandCode";
 
 /** ----------------留言管理---------------- */
 // 获取留言列表
-export const getMessageList = "/fusion/web/message/getList";
+export const getMessageList = "/fusion-xj/web/message/getList";
 // 新增火调
-export const insertMessage = "/fusion/web/message/addNew";
+export const insertMessage = "/fusion-xj/web/message/addNew";
 
 /** ----------------火调附件---------------- */
 // 获取火调列表
@@ -207,12 +214,12 @@ export const uploadAttachImage = "/web/fireProject/uploadImage";
 /** ------------------------------------------ */
 
 // 下载校验
-export const checkHasDownload = "/fusion/sceneDownLog/checkDownLoad";
+export const checkHasDownload = "/fusion-xj/sceneDownLog/checkDownLoad";
 // 下载获取进度条
-export const getDownloadProcess = "/fusion/sceneDownLog/downloadProcess";
+export const getDownloadProcess = "/fusion-xj/sceneDownLog/downloadProcess";
 // 下载
-export const downloadScene = "/fusion/sceneDownLog/downScene";
-export const downloadSceneList = "/fusion/sceneDownLog/list";
+export const downloadScene = "/fusion-xj/sceneDownLog/downScene";
+export const downloadSceneList = "/fusion-xj/sceneDownLog/list";
 // 带看相关接口
 export const offLine = "/web/fireProject/offLine"; //{roomId}
 export const onLine = "/web/fireProject/onLine"; //{roomId}
@@ -220,4 +227,7 @@ export const onLineCheck = "/web/fireProject/onLineCheck";
 // 照片制卷
 export const caseApiList = "/fusion-xj/caseImg/list";
 export const saveApiOrUpdate = "/fusion-xj/caseImg/saveOrUpdate";
-export const caseApiDel = "/fusion-xj/caseImg/delete";
+export const caseApiDel = "/fusion-xj/caseImg/delete";
+
+export const getSysSetting = `/fusion-xj/systemSetting/info`;
+export const updateSysSetting = `/fusion-xj/systemSetting/save`;

+ 6 - 0
src/router/config.ts

@@ -91,6 +91,12 @@ export const routes: Routes = [
         component: () => import("@/view/vrmodel/downloadLog.vue"),
         meta: { title: "下载记录", icon: "icon-query_home" },
       },
+      {
+        name: RouteName.setting,
+        path: "setting",
+        component: () => import("@/view/setting/index.vue"),
+        meta: { title: "系统设置", icon: "icon-nav-setup" },
+      },
     ],
   },
   {

+ 2 - 0
src/router/routeName.ts

@@ -15,6 +15,8 @@ export const RouteName = {
   role: "role",
   sceneInitiator: "sceneInitiator",
   sceneVisitor: "sceneVisitor",
+  records: "records",
+  setting: "setting",
 } as const;
 
 type RouteNamesType = typeof RouteName;

+ 72 - 0
src/setSystem.ts

@@ -0,0 +1,72 @@
+import { ref } from "vue";
+import { title } from "./store/system";
+import { appConstant } from "./app";
+import {
+  caseFileTypes,
+  caseFiles,
+  insertCaseFile,
+  deleteCaseFile,
+  updateCaseFile,
+  axios,
+  caseFileInfo,
+  saveCaseFileInfo,
+  getSysSetting,
+  updateSysSetting,
+} from "@/request";
+
+const modules = import.meta.glob("@/assets/style/theme/*.scss");
+
+axios.get(getSysSetting).then((data) => {
+  systemData.value.name = data.data.title;
+  systemData.value.color = data.data.themeColour;
+  refresh();
+});
+
+const update = () => {
+  axios.post(updateSysSetting, {
+    title: systemData.value.name,
+    themeColour: systemData.value.color,
+  });
+};
+
+export const themeColors = [
+  "0960bd",
+  "0084f4",
+  "009688",
+  "536dfe",
+  "ff5c93",
+  "ee4f12",
+  "0096c7",
+  "9c27b0",
+  "ff9800",
+];
+
+export const systemData = ref({
+  name: appConstant.name,
+  color: themeColors[0],
+});
+
+const refresh = () => {
+  title.value = systemData.value.name;
+
+  const key = Object.keys(modules).find((key) =>
+    key.includes(systemData.value.color)
+  );
+  if (key) {
+    return modules[key]();
+  } else {
+    return Promise.resolve();
+  }
+};
+
+export const setTheme = async (color: string) => {
+  systemData.value.color = color;
+  await update();
+  refresh();
+};
+
+export const setTitle = async (d: string) => {
+  systemData.value.name = d;
+  await update();
+  refresh();
+};

+ 12 - 0
src/store/case.ts

@@ -10,6 +10,8 @@ import {
   caseApiList,
   saveApiOrUpdate,
   caseApiDel,
+  caseInquestInfo,
+  caseInquestOpt
 } from "@/request";
 import { ModelScene, QuoteScene, Scene, SceneType } from "./scene";
 import { CaseFile } from "./caseFile";
@@ -82,6 +84,7 @@ export const getCaseScenes = (scenes: Scene[]) => {
 export const replaceCaseScenes = (caseId: number, caseScenes: CaseScenes) =>
   axios.post(repCaseScenes, { sceneNumParam: caseScenes, caseId });
 
+<<<<<<< HEAD
 export const caseImgList = (caseId: number, orderBy: string) =>
   axios.post(caseApiList, { orderBy: orderBy || '', caseId });
 
@@ -90,3 +93,12 @@ export const saveOrUpdate = (params: CaseImg) =>
 
 export const caseDel = (id: number) =>
   axios.post(caseApiDel, { id });
+=======
+
+export const getCaseInquestInfo = (caseId: number) =>
+  axios.get(caseInquestInfo, { params: { caseId } });
+
+
+export const saveCaseInquestInfo = (caseId: number, data) =>
+  axios.post(caseInquestOpt, { caseId, ...data });
+>>>>>>> 8ca77a2d349bd8fbdbd81a69bb09aa16be7dab3e

+ 5 - 4
src/store/permission.ts

@@ -18,11 +18,12 @@ changSaveLocal("permission", () => permission.value);
  * 获取具有权限的路由
  * @param routeNames 所有路由
  */
-export const getPermissionRoutes = (routeNames: string[]) => {
-  console.error(permission.value);
+export const getPermissionRoutes = (routeNames: string[], e: string[] = []) => {
   return routeNames
-    .filter((routeName) =>
-      permission.value.some((p) => p.resourceKey === routeName)
+    .filter(
+      (routeName) =>
+        e.includes(routeName) ||
+        permission.value.some((p) => p.resourceKey === routeName)
     )
     .map((routeName) => findRoute(routeName))
     .filter((route) => route) as Routes;

+ 1 - 1
src/store/scene.ts

@@ -164,7 +164,7 @@ export const delQuoteScene = (scene: QuoteScene) =>
   axios.get(delScene, { params: { num: scene.num } });
 
 export const copyQuoteScene = (scene: QuoteScene) =>
-  axios.get(copyScene, { params: { num: scene.num } });
+  axios.post(copyScene, { num: scene.num });
 
 export type QueryDownloadQuoteSceneParams = PaggingReq<{
   deptId: string;

+ 73 - 0
src/util/index.ts

@@ -353,3 +353,76 @@ export const getDomMatrix = (dom: HTMLElement) => {
     1,
   ];
 };
+
+// RGB转HSL
+function rgbToHsl(r, g, b) {
+  (r /= 255), (g /= 255), (b /= 255);
+  const max = Math.max(r, g, b),
+    min = Math.min(r, g, b);
+  let h,
+    s,
+    l = (max + min) / 2;
+
+  if (max === min) {
+    h = s = 0; // achromatic
+  } else {
+    const d = max - min;
+    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+    switch (max) {
+      case r:
+        h = (g - b) / d + (g < b ? 6 : 0);
+        break;
+      case g:
+        h = (b - r) / d + 2;
+        break;
+      case b:
+        h = (r - g) / d + 4;
+        break;
+    }
+    h /= 6;
+  }
+
+  return [h, s, l];
+}
+
+// HSL转RGB
+function hslToRgb(h, s, l) {
+  let r, g, b;
+
+  if (s === 0) {
+    r = g = b = l; // achromatic
+  } else {
+    const hue2rgb = (p, q, t) => {
+      if (t < 0) t += 1;
+      if (t > 1) t -= 1;
+      if (t < 1 / 6) return p + (q - p) * 6 * t;
+      if (t < 1 / 2) return q;
+      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+      return p;
+    };
+
+    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+    const p = 2 * l - q;
+    r = hue2rgb(p, q, h + 1 / 3);
+    g = hue2rgb(p, q, h);
+    b = hue2rgb(p, q, h - 1 / 3);
+  }
+  return [(r *= 255), (g *= 255), (b *= 255)];
+}
+
+export const rotateHue = (data: Uint8ClampedArray, hue: number) => {
+  console.log(hue);
+  // 修改每个像素的色相
+  for (let i = 0; i < data.length; i += 4) {
+    const hsl = rgbToHsl(data[i], data[i + 1], data[i + 2]);
+    hsl[0] += hue; // 调整色相,这里举例增加10%
+    if (hsl[0] > 1) {
+      hsl[0] -= 1;
+    }
+    const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
+    // console.log(rgb);
+    data[i] = rgb[0];
+    data[i + 1] = rgb[1];
+    data[i + 2] = rgb[2];
+  }
+};

+ 12 - 1
src/view/case/caseFile.vue

@@ -8,6 +8,9 @@
     <template v-else-if="currentTypeId === 3">
       <Records :caseId="caseId" />
     </template>
+    <template v-else-if="currentTypeId === 5">
+      <Manifest :caseId="caseId" />
+    </template>
     <template v-else>
       <div class="body-head">
         <h3 style="visibility: hidden">场景管理</h3>
@@ -91,6 +94,7 @@ import { appConstant } from "@/app";
 import { ElIcon, ElInput, ElMessage } from "element-plus";
 import Photos from "./photos/index.vue";
 import Records from "./records/index.vue";
+import Manifest from "./records/manifest.vue";
 
 const caseId = computed(() => {
   const caseId = router.currentRoute.value.params.caseId;
@@ -125,7 +129,14 @@ const refresh = async () => {
 };
 watchEffect(() => caseId.value && currentTypeId.value && refresh());
 
-const query = (file: CaseFile) => window.open(file.filesUrl + "?time=" + Date.now());
+const query = (file: CaseFile) => {
+  const ext = file.filesUrl.substring(file.filesUrl.lastIndexOf("."));
+  if (ext === ".raw") {
+    window.open(`/xfile-viewer/index.html?file=${file.filesUrl}&time=` + Date.now());
+  } else {
+    window.open(file.filesUrl + "?time=" + Date.now());
+  }
+};
 const del = async (file: CaseFile) => {
   if (await confirm("确定要删除此数据?")) {
     await delCaseFile({ caseId: caseId.value!, filesId: file.filesId });

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

@@ -107,7 +107,7 @@ const emit = defineEmits<{
 const cover = reactive(
   useUpload({
     maxSize: maxFileSize,
-    formats: [".jpg", ".png"],
+    formats: [".jpg", ".png", ".raw"],
   })
 );
 

+ 60 - 48
src/view/case/photos/index.vue

@@ -3,7 +3,12 @@
     <div class="left">
       <div class="upload">
         <el-button type="primary" @click="addCaseFileHandler"> 上传照片 </el-button>
-        <el-button type="primary" @click="sortType = !sortType" :icon="sortType?FullScreen:Menu">{{sortType?'横排':'竖排'}}</el-button>
+        <el-button
+          type="primary"
+          @click="sortType = !sortType"
+          :icon="sortType ? FullScreen : Menu"
+          >{{ sortType ? "横排" : "竖排" }}</el-button
+        >
       </div>
       <draggable :sortType="sortType" @changeList="changeList" @handleItem="handleItem" />
     </div>
@@ -14,18 +19,27 @@
         :space-between="24"
         :centeredSlides="true"
         @swiper="onSwiper"
-        style="height: 100%;"
+        style="height: 100%"
         @slideChange="onSlideChange"
       >
-        <swiper-slide class="swiperItem" v-for="(item,index) in newlist" :key="index">
+        <swiper-slide class="swiperItem" v-for="(item, index) in newlist" :key="index">
           <div class="swiperList">
-            <div class="itemper" :class="{oneItemper: sortType}" v-for="eleItem in item" :key="eleItem">
-              <img class="itemImg" src="https://4dscene.4dage.com/new4dkk/v2/lang/images/home/caseList/bwg.png" alt="" />
+            <div
+              class="itemper"
+              :class="{ oneItemper: sortType }"
+              v-for="eleItem in item"
+              :key="eleItem"
+            >
+              <img
+                class="itemImg"
+                src="https://4dscene.4dage.com/new4dkk/v2/lang/images/home/caseList/bwg.png"
+                alt=""
+              />
               <div class="text">{{ eleItem.name }}</div>
             </div>
             <div class="page">
-              <span style="margin-right: 16px">第 {{index + 1}} 页</span>
-              <span>共 {{newlist.length}} 页</span>
+              <span style="margin-right: 16px">第 {{ index + 1 }} 页</span>
+              <span>共 {{ newlist.length }} 页</span>
             </div>
           </div>
         </swiper-slide>
@@ -35,10 +49,10 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
-import { Menu, FullScreen } from '@element-plus/icons-vue'
-import { Swiper, SwiperSlide } from 'swiper/vue';
-import 'swiper/css';
+import { ref } from "vue";
+import { Menu, FullScreen } from "@element-plus/icons-vue";
+import { Swiper, SwiperSlide } from "swiper/vue";
+import "swiper/css";
 // import { addCaseFile } from "@/store/caseFile";
 import { addCaseImgFile } from "../quisk";
 import draggable from './draggable.vue';
@@ -58,99 +72,97 @@ const addCaseFileHandler = async () => {
   refresh();
 };
 const changeList = (list) => {
-  let newList = []
+  let newList = [];
   list.map((item, index) => {
-    if(sortType.value){
-        newList.push([item]);
-    }else{
-      if(index%2 ==0){
-        let newItem = list[index+1]?[item, list[index+1]]:[item];
+    if (sortType.value) {
+      newList.push([item]);
+    } else {
+      if (index % 2 == 0) {
+        let newItem = list[index + 1] ? [item, list[index + 1]] : [item];
         newList.push(newItem);
       }
     }
-  })
+  });
   newlist.value = newList;
-  console.log('changeList', newList);
-}
+  console.log("changeList", newList);
+};
 const onSwiper = (swiper) => {
   swiperRef.value = swiper;
-}
+};
 const onSlideChange = (swiper) => {
   console.log(swiper);
-}
+};
 const handleItem = (item) => {
-  let active = sortType.value ? item : Math.floor(item/2) 
+  let active = sortType.value ? item : Math.floor(item / 2);
   swiperRef.value.slideTo(active);
-  console.log('handleItem', item, active);
-}
+  console.log("handleItem", item, active);
+};
 const handleDetele = async (item) => {
   if (await confirm("删除该场景,将同时从案件和融合模型中移除,确定要删除吗?")) {
     const scenes = getCaseScenes(list.value.filter((item) => item !== scene));
     await replaceCaseScenes(props.caseId, scenes);
     refresh();
   }
-}
-
+};
 </script>
 <style lang="scss" scoped>
-.photo{
+.photo {
   display: flex;
   height: 100%;
-  .left{
+  .left {
     width: 260px;
     padding: 16px 24px 30px 0;
     height: calc(100% - 46.16px);
     overflow-y: auto;
-    background: #FFFFFF;
-    box-shadow: 10px 0 10px -10px rgba(0,0,0,0.15);
+    background: #ffffff;
+    box-shadow: 10px 0 10px -10px rgba(0, 0, 0, 0.15);
     // box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.15);
   }
-  .right{
-    width: calc(100% - 260px); 
+  .right {
+    width: calc(100% - 260px);
     background-color: var(--bgColor);
     padding-left: 24px;
     height: calc(100% - 0px);
-    .swiperItem{
+    .swiperItem {
       height: calc(100vh - 155.16px);
-      width: calc((100vh - 156.16px)*0.707);
-      background: #FFFFFF;
-      .swiperList{
+      width: calc((100vh - 156.16px) * 0.707);
+      background: #ffffff;
+      .swiperList {
         padding: 0 60px;
         height: 100%;
-        .page{
+        .page {
           font-weight: 400;
           font-size: 12px;
-          color: rgba(0,0,0,0.85);
+          color: rgba(0, 0, 0, 0.85);
           line-height: 22px;
           text-align: right;
           margin-top: 30px;
         }
-        .itemper{
+        .itemper {
           height: calc(50% - 100px);
           padding: 60px 0 0 0;
-          .text{
+          .text {
             margin-top: 16px;
             border-radius: 0px 0px 0px 0px;
-            border: 1px dotted #CCCCCC;
+            border: 1px dotted #cccccc;
             text-align: center;
             font-family: Microsoft YaHei, Microsoft YaHei;
             font-weight: 400;
             font-size: 14px;
             line-height: 30px;
-            color: rgba(0,0,0,0.85);
+            color: rgba(0, 0, 0, 0.85);
           }
-          .itemImg{
+          .itemImg {
             width: 100%;
             height: calc(100% - 48px);
             display: block;
             object-fit: cover;
           }
         }
-        
-        .oneItemper{
-          height: calc(100% - 120px);
-          .itemImg{
 
+        .oneItemper {
+          height: calc(100% - 120px);
+          .itemImg {
           }
         }
       }

+ 355 - 3
src/view/case/records/index.vue

@@ -1,9 +1,361 @@
 <template>
+  <!-- 勘验笔录{{ props.caseId }} -->
+  <div class="records">
+    <div class="header">
+      <el-button type="primary" @click="handleSave">保存</el-button>
+      <el-button>导出</el-button>
+    </div>
+    <h3 class="title">基本信息</h3>
+    <div class="content">
+
+      <div class="line">
+        <span>勘验次数:</span>
+        <span>第</span>
+        <el-input class="input" v-model="data.count" placeholder="" style="width: 80px;" />
+        <span>次勘验</span>
+      </div>
+
+      <div class="line">
+        <span>勘验时间:</span>
+        <div>
+          <el-input class="input" :maxlength="4" type="text" v-model="data.start.year" placeholder=""
+            style="width: 80px;" />
+          <span>年</span>
+          <el-input class="input" :maxlength="2" type="text" v-model="data.start.month" placeholder=""
+            style="width: 80px;" />
+          <span>月</span>
+          <el-input class="input" :maxlength="2" type="text" v-model="data.start.day" placeholder=""
+            style="width: 80px;" />
+          <span>日</span>
+        </div>
+        <span style="width: 60px;text-align: center">至</span>
+        <div>
+          <el-input class="input" :maxlength="4" v-model="data.end.year" placeholder="" style="width: 80px;" />
+          <span>年</span>
+          <el-input class="input" :maxlength="2" v-model="data.end.month" placeholder="" style="width: 80px;" />
+          <span>月</span>
+          <el-input class="input" :maxlength="2" v-model="data.end.day" placeholder="" style="width: 80px;" />
+          <span>日</span>
+        </div>
+      </div>
+
+      <div class="line">
+        <span>勘验地点:</span>
+        <el-input class="input" type="tel" v-model="data.address" placeholder="" style="width: 100%;" />
+      </div>
+      <div class="line">
+        <span>勘验人员姓名、单位、职务(含技术职务):</span>
+        <el-input class="input" type="tel" v-model="data.userInfo" placeholder="" style="width: 100%;" />
+
+      </div>
+
+      <div class="line">
+        <span>勘验气象条件(天气、风力、温度):</span>
+        <el-input class="input" type="tel" v-model="data.weather" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>勘验情况:</span>
+        <el-input type="textarea" :rows="4" v-model="data.situation" placeholder="" style="width: 100%;" />
+
+      </div>
+      <div class="textarea">
+        <span>一、环境勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.environment" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>二、初步勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.firstInquest" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>三、细项勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.carefulInquest" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>四、专项勘验</span>
+        <el-input type="textarea" :rows="6" v-model="data.specialInquest" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>提取物品描述:</span>
+        <el-input type="textarea" :rows="6" v-model="data.itemDescription" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>现场拍照制图描述:</span>
+        <el-input type="textarea" :rows="6" v-model="data.imgDescription" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="info">
+        <span class="sub-tit">勘验信息:</span>
+        <div class="inner">
+          <div class="sec">
+            <span>勘验负责人</span>
+            <el-input class="input" v-model="data.leader" placeholder="" />
+          </div>
+
+          <div class="sec">
+            <span> 记录人</span>
+            <el-input class="input" v-model="data.recorder" placeholder="" />
+          </div>
+
+          <div class="sec">
+            <span>勘验人</span>
+            <el-input class="input" v-model="data.inspector" placeholder="" />
+          </div>
+        </div>
+      </div>
+
+      <div class="gap"></div>
+      <!-- 证人 -->
+      <template v-for="index of witnessInfoes">
+        <div class="witnessInfo">
+          <span class="sub-tit">证人信息:</span>
+          <div class="line">
+            <span>证人或当事人:</span>
+            <el-input class="input" v-model="data.witnessInfo[index - 1].name" placeholder="" style="width: 180px;" />
+            <div>
+              <el-input class="input" v-model="data.witnessInfo[index - 1].year" placeholder="" style="width: 80px;" />
+              <span>年</span>
+              <el-input class="input" v-model="data.witnessInfo[index - 1].month" placeholder="" style="width: 80px;" />
+              <span>月</span>
+              <el-input class="input" v-model="data.witnessInfo[index - 1].day" placeholder="" style="width: 80px;" />
+              <span>日</span>
+            </div>
+
+            <span style="margin-left:50px">身份证件号码:</span>
+            <el-input class="input" v-model="data.witnessInfo[index - 1].id" placeholder="" style="width: 280px;" />
+          </div>
+        </div>
+
+      </template>
+
+      <div class="btn-container">
+        <el-button class="btn" @click="addwitnessInfo">+新增</el-button>
+      </div>
+
+      <div>
+      </div>
+    </div>
+  </div>
 
-  <div>勘验笔录</div>
 </template>
 <script setup>
+import { onMounted, ref, watch } from 'vue';
+import { reactive } from 'vue'
+import {
+  getCaseInquestInfo,
+  saveCaseInquestInfo
+} from "@/store/case";
+import { start } from 'repl';
+const props = defineProps({ caseId: Number })
 
-const props = defineProps({caseId: Number})
 console.log(props)
-</script>
+
+const data = reactive({
+  count: "",
+  start: {
+    year: "",
+    month: "",
+    day: ""
+  },
+  end: {
+    year: "",
+    month: "",
+    day: ""
+  },
+  address: '',
+  userInfo: '',
+  weather: '',
+  situation: '',
+  environment: '',  //环境勘验
+  firstInquest: '', //初步勘验
+  carefulInquest: '', //细项勘验
+  specialInquest: '', //专项勘验
+  itemDescription: '',
+  imgDescription: '',
+  leader: '',
+  recorder: '',
+  inspector: '',
+  witnessInfo: [{
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  }, {
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  }]
+})
+
+watch(data, newValue => {
+  // data.userName = newValue.userName.replace(/[^0-9]/g, '');
+  const sMonth = newValue.start.month.replace(/[^0-9]/g, '');
+  const sDay = newValue.start.day.replace(/[^0-9]/g, '');
+
+  const eMonth = newValue.end.month.replace(/[^0-9]/g, '');
+  const eDay = newValue.end.day.replace(/[^0-9]/g, '');
+
+  data.start.year = newValue.start.year.replace(/[^0-9]/g, '');
+  data.start.month = Number(sMonth) > 12 ? '12' : sMonth;
+  data.start.day = Number(sDay) > 31 ? '31' : sDay;
+
+  data.end.year = newValue.end.year.replace(/[^0-9]/g, '');
+  data.end.month = Number(eMonth) > 12 ? '12' : eMonth;
+  data.end.day = Number(eDay) > 31 ? '31' : eDay;
+
+  newValue.witnessInfo.forEach((item, key) => {
+    const year = newValue.witnessInfo[key].year.replace(/[^0-9]/g, '');
+    const month = newValue.witnessInfo[key].month.replace(/[^0-9]/g, '');
+    const day = newValue.witnessInfo[key].day.replace(/[^0-9]/g, '');
+    data.witnessInfo[key].year = year;
+    data.witnessInfo[key].month = Number(month) > 12 ? '12' : month;
+    data.witnessInfo[key].day = Number(day) > 31 ? '31' : day;
+  })
+
+}, {
+  immediate: true,
+  deep: true
+})
+
+const witnessInfoes = ref(2)
+
+onMounted(async () => {
+  const res = await getCaseInquestInfo(props.caseId);
+  console.log('res', res)
+})
+
+const addwitnessInfo = () => {
+  witnessInfoes.value += 1
+  data.witnessInfo.push({
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  })
+}
+
+const handleSave = async () => {
+  // console.log('data', data)
+  // const postData = {
+  //   startTime: data.start.year + data.start.month + data.start.day,
+  //   endTime: data.end.year + data.end.month + data.end.day,
+  //   ...data
+  // }
+  // await saveCaseInquestInfo(props.caseId, postData);
+}
+
+</script>
+
+<style lang="scss">
+.records {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 20px 0;
+
+  .header {
+    display: flex;
+    justify-content: flex-end;
+  }
+
+  .input {
+    height: 32px;
+    line-height: 32px;
+    margin: 0 8px;
+  }
+
+  .textarea {
+    margin-right: 8px;
+    margin-bottom: 20px;
+
+    span {
+      padding: 10px 0;
+      display: inline-block;
+    }
+
+    // margin: 0 8px;
+  }
+
+  .line {
+    display: inline-flex;
+    width: 100%;
+    flex-direction: row;
+    align-items: center;
+    margin-bottom: 25px;
+    line-height: 38px;
+
+    span {
+      white-space: nowrap;
+    }
+  }
+}
+
+.title {
+  text-align: center;
+}
+
+.sub-tit {
+  display: inline-block;
+  padding-bottom: 20px;
+}
+
+.info {
+  display: block;
+
+
+
+  .inner {
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+
+    .input {
+      flex: 1;
+    }
+
+    .sec {
+      flex: 1;
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+
+  }
+
+
+}
+
+.witnessInfo {
+  background: #F5F5F5;
+  padding: 15px;
+  margin-top: 20px;
+  margin-right: 8px;
+}
+
+.gap {
+  margin: 15px 0;
+}
+
+.btn-container {
+  padding: 20px 0;
+
+  .btn {
+    color: #26559B;
+    width: 100%;
+
+    &:hover {
+      background: #F5F5F5;
+      border-color: #dcdfe6;
+    }
+  }
+}
+</style>

+ 344 - 0
src/view/case/records/manifest.vue

@@ -0,0 +1,344 @@
+<template>
+  <!-- 勘验笔录{{ props.caseId }} -->
+  <div class="records">
+    <div class="header">
+      <el-button type="primary" @click="handleSave">保存</el-button>
+      <el-button>导出</el-button>
+    </div>
+    <h3 class="title">基本信息</h3>
+    <div class="content">
+
+      <div class="line">
+        <span>勘验次数:</span>
+        <span>第</span>
+        <el-input class="input" v-model="data.times" placeholder="" style="width: 80px;" />
+        <span>次勘验</span>
+      </div>
+
+      <div class="line">
+        <span>勘验时间:</span>
+        <div>
+          <el-input class="input" :maxlength="4" type="text" v-model="data.start.year" placeholder=""
+            style="width: 80px;" />
+          <span>年</span>
+          <el-input class="input" :maxlength="2" type="text" v-model="data.start.month" placeholder=""
+            style="width: 80px;" />
+          <span>月</span>
+          <el-input class="input" :maxlength="2" type="text" v-model="data.start.day" placeholder=""
+            style="width: 80px;" />
+          <span>日</span>
+        </div>
+        <span style="width: 60px;text-align: center">至</span>
+        <div>
+          <el-input class="input" :maxlength="4" v-model="data.end.year" placeholder="" style="width: 80px;" />
+          <span>年</span>
+          <el-input class="input" :maxlength="2" v-model="data.end.month" placeholder="" style="width: 80px;" />
+          <span>月</span>
+          <el-input class="input" :maxlength="2" v-model="data.end.day" placeholder="" style="width: 80px;" />
+          <span>日</span>
+        </div>
+      </div>
+
+      <div class="line">
+        <span>勘验地点:</span>
+        <el-input class="input" type="tel" v-model="data.location" placeholder="" style="width: 100%;" />
+      </div>
+      <div class="line">
+        <span>勘验人员姓名、单位、职务(含技术职务):</span>
+        <el-input class="input" type="tel" v-model="data.job" placeholder="" style="width: 100%;" />
+
+      </div>
+
+      <div class="line">
+        <span>勘验气象条件(天气、风力、温度):</span>
+        <el-input class="input" type="tel" v-model="data.condition" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>勘验情况:</span>
+        <el-input type="textarea" :rows="4" v-model="data.situation" placeholder="" style="width: 100%;" />
+
+      </div>
+      <div class="textarea">
+        <span>一、环境勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.situationEnv" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>二、初步勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.situationPrimary" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>三、细项勘验</span>
+        <el-input type="textarea" :rows="4" v-model="data.situationDetail" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>四、专项勘验</span>
+        <el-input type="textarea" :rows="6" v-model="data.situationTask" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>提取物品描述:</span>
+        <el-input type="textarea" :rows="6" v-model="data.objectDesc" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="textarea">
+        <span>现场拍照制图描述:</span>
+        <el-input type="textarea" :rows="6" v-model="data.sceneDesc" placeholder="" style="width: 100%;" />
+      </div>
+
+      <div class="info">
+        <span class="sub-tit">勘验信息:</span>
+        <div class="inner">
+          <div class="sec">
+            <span>勘验负责人</span>
+            <el-input class="input" v-model="data.start.month" placeholder="" />
+          </div>
+
+          <div class="sec">
+            <span> 记录人</span>
+            <el-input class="input" v-model="data.start.month" placeholder="" />
+          </div>
+
+          <div class="sec">
+            <span>勘验人</span>
+            <el-input class="input" v-model="data.start.month" placeholder="" />
+          </div>
+        </div>
+      </div>
+
+      <div class="gap"></div>
+      <!-- 证人 -->
+      <template v-for="index of witnesses">
+        <div class="witness">
+          <span class="sub-tit">证人信息:</span>
+          <div class="line">
+            <span>证人或当事人:</span>
+            <el-input class="input" v-model="data.witness[index - 1].name" placeholder="" style="width: 180px;" />
+            <div>
+              <el-input class="input" v-model="data.witness[index - 1].year" placeholder="" style="width: 80px;" />
+              <span>年</span>
+              <el-input class="input" v-model="data.witness[index - 1].month" placeholder="" style="width: 80px;" />
+              <span>月</span>
+              <el-input class="input" v-model="data.witness[index - 1].day" placeholder="" style="width: 80px;" />
+              <span>日</span>
+            </div>
+
+            <span style="margin-left:50px">身份证件号码:</span>
+            <el-input class="input" v-model="data.witness[index - 1].id" placeholder="" style="width: 280px;" />
+          </div>
+        </div>
+
+      </template>
+
+      <div class="btn-container">
+        <el-button class="btn" @click="addWitness">+新增</el-button>
+      </div>
+      <div>
+      </div>
+    </div>
+  </div>
+
+</template>
+<script setup>
+import { onMounted, ref, watch } from 'vue';
+import { reactive } from 'vue'
+const props = defineProps({ caseId: Number })
+
+console.log(props)
+
+const data = reactive({
+  times: "",
+  start: {
+    year: "",
+    month: "",
+    day: ""
+  },
+  end: {
+    year: "",
+    month: "",
+    day: ""
+  },
+  location: '',
+  job: '',
+  condition: '',
+  situation: '',
+  situationEnv: '',  //环境勘验
+  situationPrimary: '', //初步勘验
+  situationDetail: '', //初步勘验
+  situationTask: '', //专项勘验
+  objectDesc: '',
+  sceneDesc: '',
+  witness: [{
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  }, {
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  }]
+})
+
+watch(data, newValue => {
+  // data.userName = newValue.userName.replace(/[^0-9]/g, '');
+  const sMonth = newValue.start.month.replace(/[^0-9]/g, '');
+  const sDay = newValue.start.day.replace(/[^0-9]/g, '');
+
+  const eMonth = newValue.end.month.replace(/[^0-9]/g, '');
+  const eDay = newValue.end.day.replace(/[^0-9]/g, '');
+
+  data.start.year = newValue.start.year.replace(/[^0-9]/g, '');
+  data.start.month = Number(sMonth) > 12 ? '12' : sMonth;
+  data.start.day = Number(sDay) > 31 ? '31' : sDay;
+
+  data.end.year = newValue.end.year.replace(/[^0-9]/g, '');
+  data.end.month = Number(eMonth) > 12 ? '12' : eMonth;
+  data.end.day = Number(eDay) > 31 ? '31' : eDay;
+
+  newValue.witness.forEach((item, key) => {
+    const year = newValue.witness[key].year.replace(/[^0-9]/g, '');
+    const month = newValue.witness[key].month.replace(/[^0-9]/g, '');
+    const day = newValue.witness[key].day.replace(/[^0-9]/g, '');
+    data.witness[key].year = year;
+    data.witness[key].month = Number(month) > 12 ? '12' : month;
+    data.witness[key].day = Number(day) > 31 ? '31' : day;
+  })
+
+}, {
+  immediate: true,
+  deep: true
+})
+
+const witnesses = ref(2)
+onMounted(() => {
+})
+
+const addWitness = () => {
+  witnesses.value += 1
+  data.witness.push({
+    name: "",
+    year: "",
+    month: "",
+    day: "",
+    id: ""
+  })
+}
+
+const handleSave = () => {
+  console.log('data', data)
+
+}
+
+</script>
+
+<style lang="scss">
+.records {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 20px 0;
+
+  .header {
+    display: flex;
+    justify-content: flex-end;
+  }
+
+  .input {
+    height: 32px;
+    line-height: 32px;
+    margin: 0 8px;
+  }
+
+  .textarea {
+    margin-right: 8px;
+    margin-bottom: 20px;
+
+    span {
+      padding: 10px 0;
+      display: inline-block;
+    }
+
+    // margin: 0 8px;
+  }
+
+  .line {
+    display: inline-flex;
+    width: 100%;
+    flex-direction: row;
+    align-items: center;
+    margin-bottom: 25px;
+    line-height: 38px;
+
+    span {
+      white-space: nowrap;
+    }
+  }
+}
+
+.title {
+  text-align: center;
+}
+
+.sub-tit {
+  display: inline-block;
+  padding-bottom: 20px;
+}
+
+.info {
+  display: block;
+
+
+
+  .inner {
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+
+    .input {
+      flex: 1;
+    }
+
+    .sec {
+      flex: 1;
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+
+  }
+
+
+}
+
+.witness {
+  background: #F5F5F5;
+  padding: 15px;
+  margin-top: 20px;
+  margin-right: 8px;
+}
+
+.gap {
+  margin: 15px 0;
+}
+
+.btn-container {
+  padding: 20px 0;
+
+  .btn {
+    color: #26559B;
+    width: 100%;
+
+    &:hover {
+      background: #F5F5F5;
+      border-color: #dcdfe6;
+    }
+  }
+}
+</style>

+ 2 - 2
src/view/layout/slide/index.vue

@@ -17,11 +17,11 @@
 <script setup lang="ts">
 import subMenu from "./submenu.vue";
 import { getPermissionRoutes } from "@/store/permission";
-import { router } from "@/router";
+import { RouteName, router } from "@/router";
 
 const props = defineProps<{ names: string[] }>();
 
-const routes = getPermissionRoutes(props.names);
+const routes = getPermissionRoutes(props.names, [RouteName.setting]);
 </script>
 
 <style lang="scss" scoped>

+ 50 - 0
src/view/setting/index.vue

@@ -0,0 +1,50 @@
+<template>
+  <com-head :options="[{ name: '系统设置', value: '2' }]" notContent> </com-head>
+
+  <div class="body-layer" style="padding: 24px" v-loading="loading">
+    <el-form :model="form" label-width="auto" style="max-width: 600px">
+      <el-form-item label="系统标题">
+        <el-input v-model="form.name" />
+      </el-form-item>
+      <el-form-item label="系统主题色">
+        <el-radio-group v-model="form.color">
+          <el-radio :label="color" size="large" v-for="color in themeColors">
+            <span class="radio-box" :style="{ 'background-color': '#' + color }"></span>
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="onSubmit">提交</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+import comHead from "@/components/head/index.vue";
+import { setTheme, setTitle, systemData, themeColors } from "@/setSystem";
+
+import { reactive, ref } from "vue";
+
+// do not use same name with ref
+const form = reactive({
+  ...systemData.value,
+});
+
+const loading = ref(false);
+const onSubmit = async () => {
+  loading.value = true;
+  await setTheme(form.color);
+  await setTitle(form.name);
+  loading.value = false;
+};
+</script>
+
+<style lang="scss" scoped>
+.radio-box {
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  vertical-align: middle;
+}
+</style>

+ 33 - 2
src/view/system/imageCropper.vue

@@ -3,7 +3,11 @@
     <div
       class="vue-crop-layout"
       ref="layoutRef"
-      :style="{ width: sWidth + 'px', height: sHeight + 'px' }"
+      :style="{
+        width: sWidth + 'px',
+        height: sHeight + 'px',
+        '--filter': `hue-rotate(${hue}deg)`,
+      }"
     >
       <VueCropper
         class="cropper-cls"
@@ -18,6 +22,10 @@
       />
     </div>
     <div class="control">
+      <div class="slider-demo-block">
+        <span class="demonstration">色相调整</span>
+        <el-slider v-model="hue" :max="360" :min="0" show-input />
+      </div>
       <el-button type="primary" @click="cropperRef.rotateRight()"> 旋转 </el-button>
     </div>
   </div>
@@ -28,7 +36,7 @@ import "vue-cropper/dist/index.css";
 import { ref, computed } from "vue";
 import { VueCropper } from "vue-cropper";
 import { QuiskExpose } from "@/helper/mount";
-import { getDomMatrix } from "@/util";
+import { getDomMatrix, rotateHue } from "@/util";
 import { inverse, multiply, positionTransform, rotateZ, translate } from "@/util/mt4";
 
 type CropperProps = {
@@ -37,6 +45,7 @@ type CropperProps = {
 };
 const props = defineProps<CropperProps>();
 
+const hue = ref(90);
 // 样式控制
 const sWidth = 500;
 const sHeight = (props.fixed[1] / props.fixed[0]) * sWidth;
@@ -117,8 +126,14 @@ const clipImage = () => {
   const ch = (canvas.height = Math.abs(end[1] - start[1]));
   ctx.translate(cw / 2, ch / 2);
   ctx.rotate(data.rotate);
+
   ctx.drawImage(realImage, data.left, data.top, w, h, -w / 2, -h / 2, w, h);
 
+  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+  rotateHue(imageData.data, hue.value / 360);
+  ctx.putImageData(imageData, 0, 0);
+  console.log(imageData.data);
+
   return new Promise<Blob | null>((resolve) => canvas.toBlob(resolve));
 };
 
@@ -135,6 +150,18 @@ defineExpose<QuiskExpose>({
   margin-top: 20px;
   text-align: center;
 }
+.slider-demo-block {
+  max-width: 600px;
+  display: flex;
+  align-items: center;
+}
+.slider-demo-block .el-slider {
+  margin-top: 0;
+  margin-left: 12px;
+}
+.demonstration {
+  width: 100px;
+}
 </style>
 
 <style lang="scss">
@@ -142,5 +169,9 @@ defineExpose<QuiskExpose>({
   .cropper-view-box {
     outline-color: var(--el-color-primary);
   }
+
+  img {
+    filter: var(--filter);
+  }
 }
 </style>

+ 5 - 12
vite.config.ts

@@ -26,13 +26,6 @@ export default defineConfig({
       },
     ],
   },
-  css: {
-    preprocessorOptions: {
-      scss: {
-        additionalData: `@use "@/app/${app}/useStyle.scss" as *;`,
-      },
-    },
-  },
   plugins: [
     vue(),
     ElementPlus({
@@ -44,19 +37,19 @@ export default defineConfig({
     host: "0.0.0.0",
     proxy: {
       "/api": {
-        target: dev ? "http://test-mix3d.4dkankan.com" : "mix3d.4dkankan.com",
+        target: dev ? "http://xj-mix3d.4dkankan.com" : "mix3d.4dkankan.com",
         changeOrigin: true,
         rewrite: (path) => path.replace(new RegExp(`^/api`), ""),
       },
-      "/fusion": {
-        target: dev ? "https://test-mix3d.4dkankan.com" : "mix3d.4dkankan.com",
+      "/fusion-xj": {
+        target: dev ? "https://xj-mix3d.4dkankan.com" : "mix3d.4dkankan.com",
         changeOrigin: true,
-        rewrite: (path) => path.replace(new RegExp(`^/api`), "/fusion"),
+        rewrite: (path) => path.replace(new RegExp(`^/api`), "/fusion-xj"),
       },
       "/dev-code": {
         // target: "https://localhost:7173/",
         target: dev
-          ? "https://test-mix3d.4dkankan.com/code"
+          ? "https://xj-mix3d.4dkankan.com/code"
           : "https://mix3d.4dkankan.com/code",
         changeOrigin: true,
         secure: false,