bill 1 tahun lalu
induk
melakukan
3de130ce35

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "axios": "^1.4.0",
     "element-plus": "^2.3.8",
     "js-base64": "^3.7.5",
+    "mime": "^3.0.0",
     "mitt": "^3.0.1",
     "qs": "^6.11.2",
     "sass": "^1.64.2",

+ 8 - 0
pnpm-lock.yaml

@@ -9,6 +9,7 @@ specifiers:
   axios: ^1.4.0
   element-plus: ^2.3.8
   js-base64: ^3.7.5
+  mime: ^3.0.0
   mitt: ^3.0.1
   qs: ^6.11.2
   sass: ^1.64.2
@@ -28,6 +29,7 @@ dependencies:
   axios: 1.4.0
   element-plus: 2.3.8_vue@3.3.4
   js-base64: 3.7.5
+  mime: 3.0.0
   mitt: 3.0.1
   qs: 6.11.2
   sass: 1.64.2
@@ -868,6 +870,12 @@ packages:
       mime-db: 1.52.0
     dev: false
 
+  /mime/3.0.0:
+    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+    dev: false
+
   /minimatch/9.0.3:
     resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
     engines: {node: '>=16 || 14 >=14.17'}

+ 0 - 3
src/App.vue

@@ -9,7 +9,4 @@
 
 <script setup lang="ts">
 import Locale from "@/config/locale.vue";
-import { appConstant } from "@/app";
-
-document.title = appConstant.title;
 </script>

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

@@ -51,7 +51,12 @@
     <div class="el-form-item">
       <el-col :span="12">
         <el-form-item label="事故日期" class="mandatory" placeholder="请选择事故日期">
-          <el-date-picker type="date" v-model="accidentDate" style="width: 100%" />
+          <el-date-picker
+            type="date"
+            v-model="accidentDate"
+            style="width: 100%"
+            :disabled-date="(date) => date.getTime() > new Date().getTime()"
+          />
         </el-form-item>
       </el-col>
       <el-col :span="12">
@@ -125,4 +130,3 @@ defineExpose<QuiskExpose>({
   },
 });
 </script>
-@/hook/helper/cascader@/helper/mount

+ 16 - 14
src/app/fire/view/dispatch/header.vue

@@ -13,15 +13,15 @@
       <el-form-item label="起火地址:">
         <el-input
           v-model="pagging.state.query.projectAddress"
-          placeholder="请输入起火地址"
+          placeholder="请输入"
         ></el-input>
       </el-form-item>
       <el-form-item label="起火场所:">
         <el-cascader
           style="width: 100%"
           v-model="projectSite"
-          placeholder="起火场所"
-          :options="[{ label: '全部', value: '' }].concat(place)"
+          placeholder="请选择"
+          :options="[{ label: '全部', value: UN_REQ_NUM.toString() }].concat(place)"
           :props="{ expandTrigger: 'hover', checkStrictly: true }"
         ></el-cascader>
       </el-form-item>
@@ -32,6 +32,7 @@
         <el-date-picker
           type="date"
           v-model="pagging.state.query.accidentDate"
+          placeholder="请选择"
           style="width: 100%"
         />
       </el-form-item>
@@ -39,8 +40,8 @@
         <el-cascader
           style="width: 100%"
           v-model="fireReason"
-          placeholder="火灾原因:"
-          :options="[{ label: '全部', value: '' }].concat(reason)"
+          placeholder="请选择"
+          :options="[{ label: '全部', value: UN_REQ_NUM.toString() }].concat(reason)"
           :props="{ expandTrigger: 'hover', checkStrictly: true }"
         ></el-cascader>
       </el-form-item>
@@ -69,21 +70,22 @@ import { genCascaderValue, getCode, getRaw } from "@/helper/cascader";
 import { reason, place, fireStatuOptions } from "@/app/fire/constant/fire";
 import comHead from "@/components/head/index.vue";
 import { computed, ref, watchEffect } from "vue";
+import { UN_REQ_NUM } from "@/constant/sys";
 
 const props = defineProps<{ pagging: FirePagging; isTeached: boolean }>();
 const head = computed(() => [
   { name: props.isTeached ? "教学平台" : "火调管理", value: "2" },
 ]);
 
-const projectSite = ref<string[]>([]);
-watchEffect(
-  () =>
-    (props.pagging.state.query.projectSiteCode = getCode(
-      place,
-      getRaw(projectSite.value)
-    ))
-);
+const projectSite = ref<string[]>([UN_REQ_NUM.toString()]);
+watchEffect(() => {
+  props.pagging.state.query.projectSiteCode = getCode(place, getRaw(projectSite.value));
+});
+watchEffect(() => {
+  if (!props.pagging.state.query.projectSiteCode) {
+    projectSite.value = [UN_REQ_NUM.toString()];
+  }
+});
 
 const fireReason = genCascaderValue(ref(props.pagging.state.query), "fireReason");
 </script>
-@/hook/helper/cascader

+ 12 - 9
src/app/fire/view/dispatch/pagging.ts

@@ -5,9 +5,10 @@ import {
   delFire,
   getFirePagging,
 } from "@/app/fire/store/fire";
-import { computed, watchEffect } from "vue";
+import { computed, watch, watchEffect } from "vue";
 import { router } from "@/router";
 import { FireRouteName } from "@/app/fire/routeConfig";
+import { UN_REQ_NUM } from "@/constant/sys";
 
 export const useFirePagging = () => {
   const isTeached = computed(
@@ -28,18 +29,20 @@ export const useFirePagging = () => {
       accidentDate: "",
       status: FireStatus.all,
       projectSiteCode: "",
-      fireReason: "",
+      fireReason: UN_REQ_NUM.toString(),
       organizerUsers: "",
-      queryType: FirePaggingRoute.teached,
     } as any,
   });
 
-  watchEffect(() => {
-    pagging.state.query.queryType = isTeached.value
-      ? FirePaggingRoute.teached
-      : FirePaggingRoute.fire;
-  });
-
+  watch(
+    () => [pagging.state.query.queryType, isTeached.value],
+    () => {
+      pagging.state.query.queryType = isTeached.value
+        ? FirePaggingRoute.teached
+        : FirePaggingRoute.fire;
+    },
+    { flush: "sync", immediate: true }
+  );
   return { pagging, isTeached };
 };
 export type FirePagging = ReturnType<typeof useFirePagging>["pagging"];

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

@@ -305,7 +305,7 @@ body {
 .el-message-box__title span::before {
   content               : "\e7a3";
   font-size             : 1.5rem;
-  margin-right          : 5px;
+  margin-right          : 10px;
   color                 : #faad14;
   font-family           : "element-icons" !important;
   speak                 : none;

+ 2 - 2
src/constant/caseFile.ts

@@ -1,8 +1,8 @@
 import { BoardType } from "@/store/caseFile";
 
 export const FileDrawType = 1;
-export const DrawFormats = [".pdf", ".doc", ".docx"];
-export const OtherFormats = DrawFormats;
+export const DrawFormats = [".jpg", ".jpeg", ".png"];
+export const OtherFormats = [".pdf", ".doc", ".docx"];
 
 export const BoardTypeDesc = {
   [BoardType.scene]: "现场图",

+ 1 - 1
src/helper/cascader.ts

@@ -37,7 +37,7 @@ export const getCode = (options: CascaderOptions, raw: string) => {
         break;
       }
     } else {
-      rets += "0";
+      // rets += "0";
       break;
     }
   }

+ 2 - 2
src/helper/message.ts

@@ -5,10 +5,10 @@ import { markRaw } from "vue";
 export const confirm = (msg: string) =>
   ElMessageBox.confirm(msg, "系统提示", {
     type: "warning",
-    icon: markRaw(InfoFilled),
+    // icon: markRaw(InfoFilled),
   });
 export const alert = (msg: string) =>
   ElMessageBox.alert(msg, "系统提示", {
     type: "warning",
-    icon: markRaw(SuccessFilled),
+    // icon: markRaw(SuccessFilled),
   });

+ 1 - 1
src/hook/pagging.ts

@@ -92,7 +92,7 @@ export const usePagging = <PARAM, RAW, NRAW>(
     } as any;
 
     const params: any = Object.keys(paramsRaw)
-      .filter((key) => paramsRaw[key] !== UN_REQ_NUM)
+      .filter((key) => ![UN_REQ_NUM, "" + UN_REQ_NUM].includes(paramsRaw[key]))
       .reduce((t, k) => {
         t[k] = paramsRaw[k];
         if (t[k] instanceof Date) {

+ 11 - 0
src/hook/upload.ts

@@ -1,5 +1,6 @@
 import { ElMessage } from "element-plus";
 import { computed, ref } from "vue";
+import mime from "mime";
 
 export type UploadProps<T> = {
   maxSize: number;
@@ -21,6 +22,15 @@ export const useUpload = <T>(props: UploadProps<T>) => {
   const percentage = ref<number>();
   const size = computed(() => Math.floor(props.maxSize / 1024 / 1024) + "M");
   const format = computed(() => `${props.formats.join("、")}等格式的文件`);
+  const accept = computed(() =>
+    props.formats
+      .map((format) => {
+        const index = format.indexOf(".");
+        const ext = ~index ? format.substring(index + 1) : format;
+        return mime.getType(ext) as string;
+      })
+      .join(", ")
+  );
   const fileRef = ref<File>();
 
   const removeFile = () => {
@@ -61,6 +71,7 @@ export const useUpload = <T>(props: UploadProps<T>) => {
     percentage,
     size,
     format,
+    accept,
     upload,
   };
 };

+ 1 - 0
src/main.ts

@@ -8,6 +8,7 @@ import ElementPlus from "element-plus";
 import { setApp } from "@/helper/mount";
 import { router } from "./router";
 import { appConstant } from "./app";
+import "@/store/system";
 
 const app = createApp(App);
 

+ 8 - 0
src/store/case.ts

@@ -6,8 +6,10 @@ import {
   repCaseScenes,
   setCasePsw,
   syncInfo,
+  updateCaseFile,
 } from "@/request";
 import { ModelScene, QuoteScene, Scene, SceneType } from "./scene";
+import { CaseFile } from "./caseFile";
 
 export type Case = {
   caseId: number;
@@ -27,6 +29,12 @@ export const getCaseSharePWD = async (params: { caseId: number }) =>
 export const getCaseInfo = async (caseId: number) =>
   (await axios.get<Case>(caseInfo, { params: { caseId } })).data;
 
+export const updateCaseInfo = async (caseFile: CaseFile) =>
+  await axios.post(updateCaseFile, {
+    filesId: caseFile.filesId,
+    filesTitle: caseFile.filesTitle,
+  });
+
 export const getCaseSceneList = async (caseId: number): Promise<Scene[]> => {
   return (await axios.get(caseSceneList, { params: { caseId } })).data;
 };

+ 6 - 0
src/store/system.ts

@@ -3,6 +3,7 @@ import { encodePwd } from "@/util";
 import { user } from "./user";
 import { refreshRole } from "./role";
 import { appConstant } from "@/app";
+import { ref, watchEffect } from "vue";
 
 export type LoginProps = {
   phoneNum: string;
@@ -10,6 +11,11 @@ export type LoginProps = {
   password: string;
 };
 
+export const title = ref(appConstant.title);
+export const desc = ref(appConstant.desc);
+
+watchEffect(() => (document.title = title.value + " | " + desc.value));
+
 export const login = async (props: LoginProps) => {
   const res = await axios.post(userLogin, {
     ...props,

+ 3 - 4
src/store/user.ts

@@ -37,10 +37,9 @@ export type UserInfo = {
   userName: string;
 };
 
-type Params = Pick<
-  UserInfo,
-  "nickName" | "status" | "userName" | "departmentId"
->;
+type Params = Pick<UserInfo, "nickName" | "status" | "userName"> & {
+  deptId: string;
+};
 export const getUserPagging = async (params: PaggingReq<Params>) =>
   (await axios.get<PaggingRes<UserInfo>>(getUserList, { params })).data;
 

+ 3 - 0
src/view/camera/bind.vue

@@ -39,6 +39,9 @@ const users = ref<UserInfo[]>([]);
 watchEffect(async () => {
   if (bindCamera.value.deptId) {
     users.value = await getUsers(bindCamera.value.deptId);
+    if (users.value.length === 1) {
+      bindCamera.value.userId = users.value[0].id;
+    }
   }
 });
 

+ 15 - 15
src/view/camera/index.vue

@@ -64,19 +64,12 @@ import comPagination from "@/components/pagination/index.vue";
 import comHead from "@/components/head/index.vue";
 import { usePagging } from "@/hook/pagging";
 import { bindCamera } from "./quisk";
-import { getCameraPagging, delCamera, addCamera } from "@/store/camera";
+import { getCameraPagging, delCamera, addCamera, Camera } from "@/store/camera";
+import { confirm } from "@/helper/message";
+import { ElMessage } from "element-plus";
 
-const {
-  state,
-  queryReset,
-  refresh,
-  del,
-  changPageCurrent,
-  changPageSize,
-  add,
-} = usePagging({
+const { state, queryReset, refresh, changPageCurrent, changPageSize, add } = usePagging({
   get: getCameraPagging,
-  del: delCamera,
   add: addCamera,
   paramsTemlate: {
     deptId: "",
@@ -84,13 +77,20 @@ const {
     type: "2",
     searchKey: "",
   },
-  mapper: {
-    delMsg:
-      "解绑相机,该相机拍摄的场景也将一并解绑(场景在云端存储,不会删除)确定要解绑吗?",
-  },
 });
 
 const addCameraHandler = async () => {
   (await bindCamera({})) && (await refresh());
 };
+
+const del = async (camera: Camera) => {
+  if (
+    await confirm(
+      "解绑相机,该相机拍摄的场景也将一并解绑(场景在云端存储,不会删除)确定要解绑吗?"
+    )
+  ) {
+    await delCamera(camera);
+    ElMessage.success("解绑成功");
+  }
+};
 </script>

+ 9 - 3
src/view/case/addCaseFile.vue

@@ -20,7 +20,7 @@
         :limit="1"
         :before-upload="upload"
         :on-preview="previewFile"
-        :disabled="!!file"
+        :accept="accept"
         :before-remove="removeFile"
       >
         <el-button type="primary" :disabled="!!file">
@@ -40,7 +40,7 @@ import { maxFileSize } from "@/constant/caseFile";
 import { useUpload } from "@/hook/upload";
 import { CaseFile, addCaseFile } from "@/store/caseFile";
 import { ElMessage } from "element-plus";
-import { ref } from "vue";
+import { ref, watchEffect } from "vue";
 import { QuiskExpose } from "@/helper/mount";
 
 const props = defineProps<{
@@ -54,11 +54,17 @@ const caseFile = ref<CaseFile>({
   filesTitle: "",
 } as any);
 
-const { size, format, upload, removeFile, previewFile, file } = useUpload({
+const { size, format, upload, removeFile, previewFile, file, accept } = useUpload({
   maxSize: maxFileSize,
   formats: props.fileType === FileDrawType ? DrawFormats : OtherFormats,
 });
 
+watchEffect(() => {
+  if (file.value?.name) {
+    caseFile.value.filesTitle = file.value?.name;
+  }
+});
+
 defineExpose<QuiskExpose>({
   async submit() {
     if (!file.value) {

+ 1 - 1
src/view/case/addScenes.vue

@@ -25,7 +25,7 @@
           v-if="pagging.state.query.type === SceneType.SWMX"
         />
         <el-table-column label="拍摄时间" prop="createTime" v-slot:default="{ row }">
-          {{ row.createTime.substr(0, 11) }}
+          {{ row.createTime }}
         </el-table-column>
       </el-table>
     </template>

+ 54 - 4
src/view/case/caseFile.vue

@@ -23,7 +23,32 @@
           {{ $index + 1 }}
         </div>
       </el-table-column>
-      <el-table-column label="名称" prop="filesTitle"></el-table-column>
+      <el-table-column label="名称" v-slot:default="{ row }: { row: CaseFile }">
+        <span v-if="!inputCaseTitles.includes(row)">
+          {{ row.filesTitle }}
+          <el-icon class="edit-title" @click="inputCaseTitles.push(row)">
+            <EditPen />
+          </el-icon>
+        </span>
+        <ElInput
+          v-model="row.filesTitle"
+          placeholder="请输入文件名"
+          focus
+          v-else
+          style="width: 300px"
+        >
+          <template v-slot:suffix>
+            <el-button
+              type="primary"
+              plain
+              @click="updateFileTitle(row)"
+              class="input-inner-btn"
+            >
+              确定
+            </el-button>
+          </template>
+        </ElInput>
+      </el-table-column>
       <el-table-column label="创建时间" prop="createTime"></el-table-column>
       <el-table-column label="操作" v-slot:default="{ row }: { row: CaseFile }">
         <span class="oper-span" @click="query(row)"> 查看 </span>
@@ -45,8 +70,9 @@ import comHead from "@/components/head/index.vue";
 import { confirm } from "@/helper/message";
 import { RouteName, router } from "@/router";
 import { FileDrawType, BoardTypeDesc } from "@/constant/caseFile";
-import { computed, onMounted, ref, watchEffect } from "vue";
+import { computed, onMounted, onUnmounted, ref, watchEffect } from "vue";
 import { addCaseFile } from "./quisk";
+import { title, desc } from "@/store/system";
 import {
   CaseFile,
   CaseFileType,
@@ -55,6 +81,9 @@ import {
   delCaseFile,
   BoardType,
 } from "@/store/caseFile";
+import { getCaseInfo, updateCaseInfo } from "@/store/case";
+import { appConstant } from "@/app";
+import { ElIcon, ElInput } from "element-plus";
 
 const caseId = computed(() => {
   const caseId = router.currentRoute.value.params.caseId;
@@ -63,6 +92,13 @@ const caseId = computed(() => {
   }
 });
 
+const inputCaseTitles = ref<CaseFile[]>([]);
+
+const updateFileTitle = async (caseFile: CaseFile) => {
+  await updateCaseInfo(caseFile);
+  inputCaseTitles.value = inputCaseTitles.value.filter((item) => item !== caseFile);
+};
+
 const currentTypeId = ref<number>();
 const types = ref<CaseFileType[]>([]);
 const options = computed(() =>
@@ -101,7 +137,21 @@ const gotoDraw = (type: BoardType, id: number) => {
 
 onMounted(async () => {
   types.value = await getCaseFileTypes();
-  console.log(types.value);
   currentTypeId.value = types.value[0].filesTypeId;
+
+  title.value = "卷宗管理 | " + (await getCaseInfo(caseId.value!)).caseTitle;
+  desc.value = "";
 });
-</script>
+
+onUnmounted(() => {
+  title.value = appConstant.title;
+  desc.value = appConstant.desc;
+});
+</script>
+
+<style scoped lang="scss">
+.edit-title {
+  cursor: pointer;
+  margin-left: 10px;
+}
+</style>

File diff ditekan karena terlalu besar
+ 908 - 858
src/view/case/draw/board/editCAD/Layer.js


+ 1 - 1
src/view/case/draw/board/shape.js

@@ -51,7 +51,7 @@ export const images = [
 ];
 
 export const metas = {
-  [brokenLine]: { desc: "折", icon: brokenLineSVG },
+  [brokenLine]: { desc: "折线", icon: brokenLineSVG },
   [text]: { desc: "文本", icon: textSVG },
   [table]: { desc: "表格", icon: tableSVG },
   [rect]: { desc: "矩形", icon: rectSVG },

+ 12 - 4
src/view/case/draw/eshape.vue

@@ -6,7 +6,9 @@
     <el-form inline>
       <el-form-item label="内容:" v-if="textType.includes(type)">
         <el-input
-          :maxlength="50"
+          @focus="inputIng = true"
+          @blur="inputIng = false"
+          :maxlength="type === 'Tag' ? 500 : 50"
           style="width: 220px"
           v-model="meta!.value"
           @change="meta!.update"
@@ -33,7 +35,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, onUnmounted, reactive } from "vue";
+import { computed, onMounted, onUnmounted, reactive, ref, watchEffect } from "vue";
 import { BoardShape, compass, title } from "./board";
 import { editEshapeTable } from "@/view/case/quisk";
 
@@ -42,6 +44,7 @@ const emit = defineEmits<{
   (e: "update:shape", value: BoardShape | null): void;
 }>();
 
+const inputIng = ref(false);
 const type = computed(() => props.shape.data.type);
 const textType = ["Tag", title];
 const CompassType = [compass];
@@ -54,11 +57,13 @@ const delHandler = () => {
 };
 
 const editTable = async (track = false) => {
+  inputIng.value = true;
   const data = await editEshapeTable({ content: props.shape.data.content, track });
   if (data) {
     props.shape.setContent(data);
   }
   emit("update:shape", null);
+  inputIng.value = false;
 };
 
 if (props.shape.autoSet && ContentType.includes(type.value)) {
@@ -89,9 +94,12 @@ const meta = computed(() => {
 });
 
 // del快捷键删除
-
 const keydownHandler = (ev: KeyboardEvent) => {
-  if (["Backspace", "Delete"].includes(ev.key) && !nDelType.includes(type)) {
+  if (
+    !inputIng.value &&
+    ["Backspace", "Delete"].includes(ev.key) &&
+    !nDelType.includes(type)
+  ) {
     delHandler();
   }
 };

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

@@ -38,7 +38,7 @@
 import Header from "./header.vue";
 import Slider from "./slider.vue";
 import Eshape from "./eshape.vue";
-import { computed, ref } from "vue";
+import { computed, nextTick, ref } from "vue";
 import { RouteName, router } from "@/router";
 import { useBoard, title } from "./board/useBoard";
 import { selectFuseImage, selectMapImage } from "@/view/case/quisk";
@@ -126,6 +126,9 @@ const saveHandler = async () => {
       name: RouteName.drawCaseFile,
       params: { caseId: args.caseId, type: args.type, id: data.filesId },
     });
+
+    await nextTick();
+    location.reload();
   }
 };
 

+ 83 - 3
src/view/case/draw/selectFuseImage.vue

@@ -6,12 +6,28 @@
     <div class="content-layout">
       <div class="house-tags">
         <h4>请选择要同步到现场图的标注:</h4>
-        <div class="tagging-transfer">
+        <div
+          class="tagging-transfer"
+          v-sortable="{ handle: '.value-option' }"
+          ref="dragLayout"
+        >
           <el-transfer
             :titles="['所有', '需要']"
             v-model="transferValue"
             :data="transferSource"
-          />
+            target-order="push"
+          >
+            <template #default="{ option }">
+              <span
+                :class="{ 'value-option': transferValue.includes(option.key) }"
+                :id="'o' + option.key"
+                :draggable="transferValue.includes(option.key)"
+              >
+                <span>{{ option.label }}</span>
+                <el-icon v-if="transferValue.includes(option.key)"><Rank /></el-icon>
+              </span>
+            </template>
+          </el-transfer>
         </div>
       </div>
       <div class="house-image-layout">
@@ -38,7 +54,7 @@ import {
   FuseImageType,
   fuseImageJoinHot,
 } from "@/view/case/help";
-import { computed, onMounted, ref } from "vue";
+import { computed, nextTick, onMounted, ref, watchEffect } from "vue";
 import { QuiskExpose } from "@/helper/mount";
 
 export type FuseImage = { blob: Blob | null; taggings: CaseTagging[] };
@@ -91,6 +107,49 @@ defineExpose<QuiskExpose>({
 onMounted(async () => {
   taggings.value = await getCaseTaggings(props.caseId);
 });
+
+watchEffect(async (onClanup) => {
+  transferValue.value.join("");
+  await nextTick();
+  const desps: (() => void)[] = [];
+  let repKey: number | null;
+  for (const key of transferValue.value) {
+    const $option = document.querySelector("#o" + key) as HTMLDivElement;
+    const startHandler = () => {
+      $option.classList.add("dragging");
+      repKey = key;
+
+      $option.addEventListener("dragend", function endHandler(ev) {
+        $option.classList.remove("dragging");
+        $option.removeEventListener("dragend", endHandler);
+      });
+    };
+    const dragoverHandler = (ev: Event) => ev.preventDefault();
+    const dropHandler = (ev: Event) => {
+      ev.preventDefault();
+      if (repKey && key !== repKey) {
+        const oldIndex = transferValue.value.indexOf(key);
+        const newIndex = transferValue.value.indexOf(repKey);
+        transferValue.value[newIndex] = key;
+        transferValue.value[oldIndex] = repKey;
+      }
+    };
+
+    $option.addEventListener("dragstart", startHandler);
+    $option.addEventListener("dragover", dragoverHandler);
+    $option.addEventListener("drop", dropHandler);
+
+    desps.push(() => {
+      $option.removeEventListener("dragstart", startHandler);
+      $option.removeEventListener("dragover", startHandler);
+      $option.removeEventListener("drop", dropHandler);
+    });
+  }
+
+  onClanup(() => {
+    desps.forEach((desp) => desp());
+  });
+});
 </script>
 
 <style lang="scss" scoped>
@@ -189,4 +248,25 @@ onMounted(async () => {
 .icon {
   cursor: pointer;
 }
+
+.value-option {
+  display: flex;
+  align-items: center;
+
+  span {
+    flex: 1;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  i {
+    flex: 0 0 auto;
+  }
+}
+</style>
+
+<style>
+.value-option.dragging {
+  opacity: 0.5;
+}
 </style>

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

@@ -11,6 +11,7 @@
         :show-file-list="false"
         :disabled="!!percentage"
         :before-upload="upload"
+        :accept="accept"
         :file-list="[]"
       >
         <el-button
@@ -66,7 +67,7 @@ const emit = defineEmits<{
   (e: "selectImage", val: Blob): void;
 }>();
 
-const { percentage, upload, file, removeFile } = useUpload({
+const { percentage, upload, file, removeFile, accept } = useUpload({
   maxSize: maxFileSize,
   formats: [".jpg", ".jpeg", ".png", ".svg"],
 });

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

@@ -2,8 +2,8 @@
   <div class="header-top">
     <div class="title">
       <h2>
-        {{ appConstant.title }}
-        <span>{{ appConstant.desc }}</span>
+        {{ title }}
+        <span>{{ desc }}</span>
       </h2>
     </div>
     <div class="oper-btns" v-if="user.info">
@@ -36,7 +36,7 @@ import { refreshRole, roleId } from "@/store/role";
 import { RouteName, router } from "@/router";
 import { confirm } from "@/helper/message";
 import { updatePwd } from "@/view/system/quisk";
-import { appConstant } from "@/app";
+import { title, desc } from "@/store/system";
 
 refreshRole();
 
@@ -60,4 +60,4 @@ const updatePwdHandler = async () => {
 
 <style lang="scss" scoped>
 @import "./style.scss";
-</style>
+</style>

+ 13 - 7
src/view/system/imageCropper.vue

@@ -15,6 +15,8 @@
       :fixed="!!fixed"
       :fixedNumber="fixed"
       fixedBox
+      :canMoveBox="false"
+      @realTime="realTimeHandler"
     />
   </div>
 </template>
@@ -34,16 +36,20 @@ const props = defineProps<CropperProps>();
 const url = computed(() =>
   typeof props.img === "string" ? props.img : URL.createObjectURL(props.img)
 );
+const scale = ref(1);
 const cropperRef = ref<any>();
+const realTimeHandler = (data: any) => {
+  const transform = data.img.transform as string;
+  if (transform) {
+    const result = transform.match(/scale\((\d+(\.\d+)?)\)/);
+    if (result) {
+      scale.value = Number(result[1]);
+    }
+  }
+};
 
 defineExpose<QuiskExpose>({
-  submit() {
-    return new Promise((resolve) => {
-      cropperRef.value.getCropBlob((blob: Blob) => {
-        resolve(blob);
-      });
-    });
-  },
+  submit: () => new Promise((resolve) => cropperRef.value.getCropBlob(resolve)),
 });
 </script>
 

+ 1 - 0
src/view/system/quisk.ts

@@ -4,6 +4,7 @@ import UpdatePwd from "./updatePsw.vue";
 
 export const imageCropper = quiskMountFactory(ImageCropper, {
   title: "图片裁剪",
+  width: 600,
 })<Blob>;
 
 export const updatePwd = quiskMountFactory(UpdatePwd, {

+ 1 - 1
src/view/user/edit.vue

@@ -103,7 +103,7 @@ const deptPath = ref<string[]>([]);
 
 defineExpose<QuiskExpose>({
   async submit() {
-    if (!bindUser.value.nickName) {
+    if (!bindUser.value.nickName.trim()) {
       ElMessage.error("请输入用户姓名");
       throw "请输入用户姓名";
     }

+ 6 - 6
src/view/user/index.vue

@@ -2,7 +2,7 @@
   <com-head :options="[{ name: '用户管理', value: '2' }]">
     <el-form label-width="84px">
       <el-form-item label="所属架构:">
-        <com-company v-model="state.query.departmentId" />
+        <com-company v-model="state.query.deptId" />
       </el-form-item>
       <el-form-item label="用户账号:">
         <el-input v-model="state.query.userName" placeholder="请输入手机号"></el-input>
@@ -51,7 +51,7 @@
       <el-table-column label="操作" v-slot:default="{ row }">
         <span
           class="oper-span"
-          v-pddept="[row, 'edit']"
+          v-pdpath="['edit']"
           :class="{ disable: row.type === 0 }"
           @click="editHandler(row)"
           >编辑</span
@@ -60,7 +60,7 @@
           class="oper-span"
           @click="switchUserStatus(row)"
           :class="{ disable: user.info.id == row.id }"
-          v-pddept="[row, 'disabled']"
+          v-pdpath="['disabled']"
           style="color: var(--primaryColor)"
           >{{ row.status ? "禁用" : "启用" }}</span
         >
@@ -69,7 +69,7 @@
           style="color: var(--primaryColor)"
           @click="delInfo(row)"
           :class="{ disable: user.info.id == row.id }"
-          v-pddept="[row, 'del']"
+          v-pdpath="['del']"
           >删除</span
         >
       </el-table-column>
@@ -100,7 +100,7 @@ const { state, queryReset, refresh, changPageCurrent, changPageSize } = usePaggi
   paramsTemlate: {
     nickName: "",
     status: "" as any,
-    departmentId: "",
+    deptId: "",
     userName: "",
   },
 });
@@ -140,4 +140,4 @@ const switchUserStatus = async (row: UserInfo) => {
 };
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss"></style>

+ 1 - 1
src/view/vrmodel/sceneContent.vue

@@ -19,7 +19,7 @@
     <el-table-column label="S/N码" prop="snCode"></el-table-column>
     <!-- <el-table-column label="浏览数量" prop="viewCount"></el-table-column> -->
     <el-table-column label="拍摄时间" prop="createTime" v-slot:default="{ row }">
-      {{ row.createTime.substr(0, 11) }}
+      {{ row.createTime }}
     </el-table-column>
     <el-table-column label="状态" v-slot:default="{ row }: { row: QuoteScene }">
       {{ QuoteSceneStatusDesc[row.status] }}

+ 1 - 1
vite.config.ts

@@ -3,7 +3,7 @@ import vue from "@vitejs/plugin-vue";
 import { resolve } from "path";
 import ElementPlus from "unplugin-element-plus/vite";
 
-let app = "criminal";
+let app = "fire";
 if (process.argv.length > 3) {
   app = process.argv[process.argv.length - 1].trim();
 }