浏览代码

feat(records): save ui

gemercheung 1 年之前
父节点
当前提交
ed8be09f1b

+ 3 - 1
package.json

@@ -21,10 +21,12 @@
     "mitt": "^3.0.1",
     "qs": "^6.11.2",
     "sass": "^1.64.2",
+    "swiper": "^11.1.4",
     "unplugin-element-plus": "^0.7.2",
     "unplugin-vue-define-options": "^1.3.12",
     "vue": "^3.3.4",
     "vue-cropper": "^1.0.9",
+    "vue-draggable-plus": "^0.5.0",
     "vue-router": "^4.2.4"
   },
   "devDependencies": {
@@ -34,4 +36,4 @@
     "vite": "^4.4.5",
     "vue-tsc": "^1.8.5"
   }
-}
+}

文件差异内容过多而无法显示
+ 793 - 1053
pnpm-lock.yaml


+ 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 = [

+ 12 - 6
src/app/fire/store/fire.ts

@@ -36,7 +36,7 @@ export type Fire = {
   organizerDeptName: string;
   organizerUsers: string;
   projectAddress: string;
-  latlng: string;
+  latAndLong: string;
   projectName: string;
   projectSite: string;
   projectSiteCode: string;
@@ -61,7 +61,8 @@ type FirePaggingParams = PaggingReq<Fire & { queryType: FirePaggingRoute }>;
 export const getFirePagging = async (params: FirePaggingParams) =>
   (await axios.get(getFireList, { params })).data as PaggingRes<Fire>;
 
-export const addFire = async (fire: Omit<Fire, "id">) => axios.post(insertFire, fire);
+export const addFire = async (fire: Omit<Fire, "id">) =>
+  axios.post(insertFire, fire);
 
 export const setFire = async (fire: Fire) => await axios.post(updateFire, fire);
 
@@ -83,8 +84,13 @@ export type FireLeaveMsg = {
   createTime: number;
 };
 
-export const getFireLeaveMsgPagging = async (params: PaggingReq<{ projectId: string }>) =>
-  (await axios.get(getMessageList, { params })).data as PaggingRes<FireLeaveMsg>;
+export const getFireLeaveMsgPagging = async (
+  params: PaggingReq<{ projectId: string }>
+) =>
+  (await axios.get(getMessageList, { params }))
+    .data as PaggingRes<FireLeaveMsg>;
 
-export const addFireLeaveMsg = (params: { content: string; projectId: string }) =>
-  axios.post(insertMessage, params);
+export const addFireLeaveMsg = (params: {
+  content: string;
+  projectId: string;
+}) => axios.post(insertMessage, params);

+ 3 - 2
src/app/fire/view/dispatch/editFire.vue

@@ -141,7 +141,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 +175,8 @@ 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}`;
     info.value = {
       lat: e.data.lat,
       lng: e.data.lng,

+ 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>

+ 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";

+ 80 - 76
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,73 @@ 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 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,13 +208,16 @@ 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}
 export const onLineCheck = "/web/fireProject/onLineCheck";
+
+export const getSysSetting = `/fusion-xj/systemSetting/info`;
+export const updateSysSetting = `/fusion-xj/systemSetting/save`;

+ 6 - 1
src/router/config.ts

@@ -91,7 +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" },
+      },
     ],
   },
   {

+ 1 - 0
src/router/routeName.ts

@@ -16,6 +16,7 @@ export const RouteName = {
   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();
+};

+ 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];
+  }
+};

+ 124 - 0
src/view/case/addPhotoFile.vue

@@ -0,0 +1,124 @@
+<template>
+  <el-form
+    ref="form"
+    :model="caseFile"
+    label-width="90px"
+    class="camera-from dispatch-file-from"
+  >
+    <el-form-item label="附件:" class="mandatory">
+      <el-upload
+        class="upload-demo"
+        :multiple="false"
+        :limit="1"
+        :disabled="!!file"
+        :before-upload="upload"
+        :file-list="fileList"
+        :http-request="() => {}"
+        :on-preview="previewFile"
+        :accept="accept"
+        :before-remove="removeFile"
+      >
+        <el-button type="primary" :disabled="!!file">
+          <el-icon><Upload /></el-icon>上传
+        </el-button>
+        <template v-slot:tip>
+          <div class="el-upload__tip">注:可上传{{ size }}以内的{{ formatDesc }}</div>
+        </template>
+        <template v-slot:file="{ file }: { file: UploadFile }">
+          <div class="file" @click.stop="previewFile()">
+            <div>
+              <el-icon><Document /></el-icon>
+              <span class="name">{{ file.name }}</span>
+            </div>
+            <el-icon @click.stop="removeFile()"><Close /></el-icon>
+          </div>
+        </template>
+      </el-upload>
+    </el-form-item>
+    <el-form-item label="附件标题:" class="mandatory">
+      <el-input
+        v-model="caseFile.filesTitle"
+        placeholder="请输入最多不能超过50字"
+        maxlength="50"
+        show-word-limit
+      ></el-input>
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import {
+  DrawFormatDesc,
+  DrawFormats,
+  FileDrawType,
+  OtherFormatDesc,
+  OtherFormats,
+} from "@/constant/caseFile";
+import { maxFileSize } from "@/constant/caseFile";
+import { useUpload } from "@/hook/upload";
+import { CaseFile, addCaseFile } from "@/store/caseFile";
+import { ElMessage, UploadFile } from "element-plus";
+import { computed, ref, watchEffect } from "vue";
+import { QuiskExpose } from "@/helper/mount";
+
+const props = defineProps<{
+  caseId: number;
+  fileType: number;
+}>();
+
+const caseFile = ref<CaseFile>({
+  caseId: props.caseId,
+  filesTypeId: props.fileType,
+  filesTitle: "",
+} as any);
+
+const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
+  maxSize: maxFileSize,
+  formats: props.fileType === FileDrawType ? DrawFormats : OtherFormats,
+});
+
+const formatDesc = computed(() =>
+  props.fileType === FileDrawType ? DrawFormatDesc : OtherFormatDesc
+);
+
+watchEffect(() => {
+  if (file.value?.name) {
+    caseFile.value.filesTitle = file.value?.name.substring(0, 50);
+  }
+});
+
+defineExpose<QuiskExpose>({
+  async submit() {
+    if (!file.value) {
+      ElMessage.error("请上传附件");
+      throw "请上传附件";
+    } else if (!caseFile.value.filesTitle.trim()) {
+      ElMessage.error("附件标题不能为空!");
+      throw "附件标题不能为空!";
+    }
+
+    await addCaseFile({ ...caseFile.value, file: file.value });
+    return caseFile.value;
+  },
+});
+</script>
+
+<style scoped lang="scss">
+.upload-demo {
+  overflow: hidden;
+}
+
+.file {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  > div {
+    display: flex;
+    align-items: center;
+  }
+
+  .name {
+    margin-left: 10px;
+  }
+}
+</style>

+ 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"],
   })
 );
 

+ 172 - 0
src/view/case/photos/draggable.vue

@@ -0,0 +1,172 @@
+<template>
+  <div class="VueDraggable">
+    <VueDraggable v-if="list.length" class="draggable" ref="el" v-model="list" @sort="onChange">
+      <div class="item" v-for="(item, index) in list" :key="item.id" @click="handleItem(index)">
+        <img class="itemImg" src="https://4dscene.4dage.com/new4dkk/v2/lang/images/home/caseList/bwg.png" alt="" />
+        <div class="text">{{ item.name }}</div>
+        <CircleCloseFilled @click.stop="handleDet(index)" class="itemIcon" />
+      </div>
+    </VueDraggable>
+    <el-empty class="empty" v-else description="请上传现场照片" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch } from 'vue'
+import { Case } from "@/store/case";
+import { VueDraggable } from 'vue-draggable-plus'
+// import { IconRabbish } from '@element-plus/icons-vue'
+const props = defineProps({ sortType: Boolean });
+const emit = defineEmits<{
+  (e: "changeList", value: Case[] | null): void;
+  (e: "handleItem", value: Number | null): void;
+}>();
+const list = ref([
+  {
+  name: 'Joao1',
+  id: 1
+},
+{
+  name: 'Jean2',
+  id: 2
+},
+{
+  name: 'Johanna3',
+  id: 3
+},
+{
+  name: 'Juan4',
+  id: 4
+},
+{
+  name: 'Joao5',
+  id: 5
+},
+{
+  name: 'Jean6',
+  id: 6
+},
+{
+  name: 'Johanna7',
+  id: 7
+},
+{
+  name: 'Juan8',
+  id: 8
+},{
+  name: 'Juan4',
+  id: 4
+},
+{
+  name: 'Joao5',
+  id: 5
+},
+{
+  name: 'Jean6',
+  id: 6
+},
+{
+  name: 'Johanna7',
+  id: 7
+},
+{
+  name: 'Juan8',
+  id: 8
+},{
+  name: 'Juan4',
+  id: 4
+},
+{
+  name: 'Joao5',
+  id: 5
+},
+{
+  name: 'Jean6',
+  id: 6
+},
+{
+  name: 'Johanna7',
+  id: 7
+},
+{
+  name: 'Juan8',
+  id: 8
+},{
+  name: 'Juan4',
+  id: 4
+},
+{
+  name: 'Joao5',
+  id: 5
+},
+{
+  name: 'Jean6',
+  id: 6
+},
+{
+  name: 'Johanna7',
+  id: 7
+},
+{
+  name: 'Juan8',
+  id: 8
+}
+])
+
+watch(()=>props.sortType,(newValue, oldValue)=>{
+    console.log('sum is changed',newValue,oldValue);
+    emit("changeList", list.value);
+},{ deep: true, immediate:true})
+
+function onChange(event: any) {
+  emit("changeList", list.value);
+}
+function handleItem(index) {
+  emit("handleItem", index);
+  
+}
+function handleDet(index: Number) {
+  list.value.splice(index, 1)
+  emit("changeList", list.value);
+  console.log('handleDet', list.value);
+}
+onMounted(() => {
+  emit("changeList", list.value);
+  // emit("update:list", props.list.value);
+})
+
+
+</script>
+<style lang="scss" scoped>
+.empty{
+  width: 200px;
+}
+.draggable{
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  .item {
+    position: relative;
+    // flex: 0 0 50%; /* 每个子元素占用50%的宽度 */
+    width: calc(50% - 4px);
+    margin-top: 16px;
+    .itemImg{
+      width: 100%;
+    }
+    .itemIcon{
+      width: 20px;
+      height: 20px;
+      color: var(--el-color-primary);
+      position: absolute;
+      right: -10px;
+      top: -10px;
+      display: none;
+    }
+    &:hover{
+      .itemIcon{
+        display:block;
+      }
+    }
+  }
+}
+</style>

+ 160 - 1
src/view/case/photos/index.vue

@@ -1,8 +1,167 @@
 <template>
-  <div>照片</div>
+  <div class="photo">
+    <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
+        >
+      </div>
+      <draggable :sortType="sortType" @changeList="changeList" @handleItem="handleItem" />
+    </div>
+    <div class="right">
+      <swiper
+        class="swiper"
+        slides-per-view="auto"
+        :space-between="24"
+        :centeredSlides="true"
+        @swiper="onSwiper"
+        style="height: 100%"
+        @slideChange="onSlideChange"
+      >
+        <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="text">{{ eleItem.name }}</div>
+            </div>
+            <div class="page">
+              <span style="margin-right: 16px">第 {{ index + 1 }} 页</span>
+              <span>共 {{ newlist.length }} 页</span>
+            </div>
+          </div>
+        </swiper-slide>
+      </swiper>
+    </div>
+  </div>
 </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 { addCaseFile } from "@/store/caseFile";
+import { addCaseFile } from "../quisk";
+import draggable from "./draggable.vue";
 const props = defineProps({ caseId: Number });
+const newlist = ref([]);
+const swiperRef = ref(null);
+const caseId = ref(props.caseId);
+const sortType = ref(false);
 console.log(props);
+const addCaseFileHandler = async () => {
+  await addCaseFile({ caseId: caseId.value, fileType: "currentTypeId.value!" });
+  refresh();
+};
+const changeList = (list) => {
+  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];
+        newList.push(newItem);
+      }
+    }
+  });
+  newlist.value = 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);
+  swiperRef.value.slideTo(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 {
+  display: flex;
+  height: 100%;
+  .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);
+    // box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.15);
+  }
+  .right {
+    width: calc(100% - 260px);
+    background-color: var(--bgColor);
+    padding-left: 24px;
+    height: calc(100% - 0px);
+    .swiperItem {
+      height: calc(100vh - 155.16px);
+      width: calc((100vh - 156.16px) * 0.707);
+      background: #ffffff;
+      .swiperList {
+        padding: 0 60px;
+        height: 100%;
+        .page {
+          font-weight: 400;
+          font-size: 12px;
+          color: rgba(0, 0, 0, 0.85);
+          line-height: 22px;
+          text-align: right;
+          margin-top: 30px;
+        }
+        .itemper {
+          height: calc(50% - 100px);
+          padding: 60px 0 0 0;
+          .text {
+            margin-top: 16px;
+            border-radius: 0px 0px 0px 0px;
+            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);
+          }
+          .itemImg {
+            width: 100%;
+            height: calc(100% - 48px);
+            display: block;
+            object-fit: cover;
+          }
+        }
+
+        .oneItemper {
+          height: calc(100% - 120px);
+          .itemImg {
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 344 - 0
src/view/case/records/index copy.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>

+ 6 - 1
src/view/case/records/index.vue

@@ -2,7 +2,7 @@
   <!-- 勘验笔录{{ props.caseId }} -->
   <div class="records">
     <div class="header">
-      <el-button type="primary">保存</el-button>
+      <el-button type="primary" @click="handleSave">保存</el-button>
       <el-button>导出</el-button>
     </div>
     <h3 class="title">基本信息</h3>
@@ -147,6 +147,7 @@
 import { onMounted, ref, watch } from 'vue';
 import { reactive } from 'vue'
 const props = defineProps({ caseId: Number })
+
 console.log(props)
 
 const data = reactive({
@@ -231,6 +232,10 @@ const addWitness = () => {
   })
 }
 
+const handleSave = () => {
+  console.log('data', data)
+
+}
 
 </script>
 

+ 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,

文件差异内容过多而无法显示
+ 1100 - 0
yarn.lock