bill 1 年間 前
コミット
fc20631cfb

+ 7 - 2
src/app/criminal/view/example/index.vue

@@ -1,9 +1,12 @@
 <template>
   <com-head :options="[{ name: '案件管理', value: '2' }]">
     <el-form label-width="84px" inline>
-      <el-form-item label="标题:" style="grid-area: 1 / 1 / 2 / 4">
+      <el-form-item label="标题:">
         <el-input v-model="state.query.caseTitle" placeholder="请输入"></el-input>
       </el-form-item>
+      <el-form-item label="承办单位:">
+        <com-company v-model="state.query.deptId" />
+      </el-form-item>
       <el-form-item class="searh-btns" style="grid-area: 1 / 4 / 2 / 6">
         <el-button type="primary" @click="refresh">查询</el-button>
         <el-button type="primary" plain @click="queryReset">重置</el-button>
@@ -34,6 +37,7 @@
         </div>
       </el-table-column>
       <el-table-column label="标题" prop="caseTitle"></el-table-column>
+      <el-table-column label="承办单位" prop="deptName"></el-table-column>
       <el-table-column label="创建时间" prop="createTime"></el-table-column>
       <el-table-column
         label="操作"
@@ -73,6 +77,7 @@
 
 <script setup lang="ts">
 import comHead from "@/components/head/index.vue";
+import comCompany from "@/components/company-select/index.vue";
 import comPagination from "@/components/pagination/index.vue";
 import { usePagging } from "@/hook/pagging";
 import { Example, delExample, getExamplePagging } from "@/app/criminal/store/example";
@@ -86,7 +91,7 @@ const { state, refresh, queryReset, del, changPageSize, changPageCurrent } = use
   mapper: {
     delMsg: "删除案件,相关档案也会一并删除,确定要删除吗?",
   },
-  paramsTemlate: { caseTitle: "" },
+  paramsTemlate: { caseTitle: "", deptId: "" },
 });
 
 const addHandler = async () => {

+ 13 - 1
src/app/index.ts

@@ -1,22 +1,30 @@
 import { Routes } from "@/router";
 
 import { appConstant as criminalConstant } from "./criminal/constant";
-import { appConstant as fireConstant } from "./fire/constant";
 import {
   routes as cRoutes,
   menuRouteNames as cMenuRouteNames,
 } from "./criminal/routeConfig";
+
+import { appConstant as fireConstant } from "./fire/constant";
 import {
   routes as fireRoutes,
   menuRouteNames as firelMenuRouteNames,
 } from "./fire/routeConfig";
 
+import { appConstant as jmfireConstant } from "./jmfire/constant";
+import {
+  routes as jmfireRoutes,
+  menuRouteNames as jmfirelMenuRouteNames,
+} from "./jmfire/routeConfig";
+
 export type AppConstant = {
   title: string;
   ico: string;
   desc: string;
   banner: string;
   deptId: number;
+  loginComponent?: any;
 };
 
 export let appConstant: AppConstant;
@@ -32,4 +40,8 @@ if (VITE_APP_APP === "fire") {
   appRoutes = cRoutes;
   menuRouteNames = cMenuRouteNames;
   appConstant = criminalConstant;
+} else if (VITE_APP_APP === "jmfire") {
+  appRoutes = jmfireRoutes;
+  menuRouteNames = jmfirelMenuRouteNames;
+  appConstant = jmfireConstant;
 }

+ 12 - 0
src/app/jmfire/constant.ts

@@ -0,0 +1,12 @@
+import { AppConstant } from "../";
+import banner from "@/assets/image/jmfirebg@2x.png";
+import ico from "@/assets/image/jmlogo.png";
+
+export const appConstant: AppConstant = {
+  title: "火灾调查三维远程勘验平台",
+  desc: "Three-dimensional remote prospecting platform for fire scenes",
+  ico,
+  banner,
+  loginComponent: () => import("./view/login/index.vue"),
+  deptId: 1,
+};

BIN
src/app/jmfire/images/banner@2x.png


+ 34 - 0
src/app/jmfire/routeConfig.ts

@@ -0,0 +1,34 @@
+import { RouteName } from "@/router/routeName";
+import { Routes } from "@/router/config";
+
+export const FireRouteName = {
+  ...RouteName,
+  dispatch: "dispatch",
+  teaching: "teaching",
+} as const;
+
+export const menuRouteNames = [
+  // FireRouteName.home,
+  FireRouteName.vrmodel,
+  FireRouteName.camera,
+  FireRouteName.dispatch,
+  FireRouteName.teaching,
+  FireRouteName.organization,
+  FireRouteName.role,
+  FireRouteName.user,
+];
+
+export const routes: Routes = [
+  {
+    name: FireRouteName.dispatch,
+    path: "dispatch",
+    component: () => import("../fire/view/dispatch/index.vue"),
+    meta: { title: "火调管理", icon: "iconfire_management" },
+  },
+  {
+    name: FireRouteName.teaching,
+    path: "teaching",
+    component: () => import("../fire/view/dispatch/index.vue"),
+    meta: { title: "教学平台", icon: "iconfire_study" },
+  },
+];

+ 7 - 0
src/app/jmfire/useStyle.scss

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

+ 356 - 0
src/app/jmfire/view/login/index.vue

@@ -0,0 +1,356 @@
+<template>
+  <div class="system-layer" :style="{ backgroundImage: `url(${appConstant.banner})` }">
+    <div class="content">
+      <div class="login-layer">
+        <div class="content">
+          <div class="info">
+            <img src="@/app/jmfire/images/banner@2x.png" />
+          </div>
+          <el-form class="panel login" :model="form" @submit.stop>
+            <el-form-item class="panel-form-item">
+              <p class="err-info">{{ verification.phone }}</p>
+              <el-input
+                :maxlength="11"
+                v-model.trim="form.phone"
+                placeholder="手机号"
+                @keydown.enter="submitClick"
+              ></el-input>
+            </el-form-item>
+            <el-form-item class="panel-form-item">
+              <p class="err-info">{{ verification.psw }}</p>
+              <el-input
+                v-model="form.psw"
+                :maxlength="16"
+                placeholder="密码"
+                :type="flag ? 'password' : 'text'"
+                @keydown.enter="submitClick"
+              >
+                <template v-slot:suffix>
+                  <img
+                    v-if="flag"
+                    @click="flag = !flag"
+                    style="width: 20px; margin: 15px"
+                    src="@/assets/image/pasword.png"
+                    alt=""
+                  />
+                  <el-icon :size="20" @click="flag = !flag" class="icon-style" v-else>
+                    <View />
+                  </el-icon>
+                </template>
+              </el-input>
+            </el-form-item>
+
+            <el-form-item class="panel-form-item code-form-item">
+              <p class="err-info">{{ verification.code }}</p>
+              <el-input
+                v-model="form.code"
+                placeholder="验证码"
+                @keydown.enter="submitClick"
+                class="code-input"
+              >
+                <template v-slot:append>
+                  <img :src="codeImg" class="code-img" @click="refer" />
+                </template>
+              </el-input>
+            </el-form-item>
+
+            <el-form-item class="panel-form-item">
+              <el-button type="primary" class="fill" @click="submitClick">登录</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { reactive, watch, ref, computed } from "vue";
+import { openErrorMsg, baseURL, getCode } from "@/request";
+import { PHONE } from "@/constant/REG";
+import { guid, strToParams } from "@/util";
+import { RouteName, router } from "@/router";
+import { login } from "@/store/system";
+import { appConstant } from "@/app";
+import { user } from "@/store/user";
+
+// 是否显示明文密码
+const flag = ref(true);
+// 表单
+const form = reactive({
+  phone: localStorage.getItem("userName") || "",
+  psw: localStorage.getItem("password") || "",
+  code: "",
+  remember: import.meta.env.DEV || localStorage.getItem("remember") === "1",
+});
+const verification = reactive({ phone: "", psw: "", code: "" });
+// 验证
+watch(
+  form,
+  () => {
+    console.log("form", form);
+    if (!form.phone) {
+      verification.phone = "请输入手机号";
+    } else if (form.phone == "88888888888") {
+      verification.phone = "";
+    } else {
+      verification.phone = PHONE.REG.test(form.phone) ? "" : PHONE.tip;
+    }
+    if (!form.psw) {
+      verification.psw = "请输入密码";
+    } else {
+      verification.psw = "";
+    }
+    if (!form.code.trim()) {
+      verification.code = "请输入验证码";
+    } else {
+      verification.code = "";
+    }
+  },
+  { immediate: true }
+);
+
+// 图片验证码
+const imgKey = ref(guid());
+const refer = () => (imgKey.value = guid());
+const codeImg = computed(() => baseURL + getCode + "?key=" + imgKey.value);
+
+// 表单提交
+const submitClick = async () => {
+  if (verification.phone && verification.phone !== "88888888888") {
+    return openErrorMsg(verification.phone);
+  }
+  if (verification.psw) return openErrorMsg(verification.psw);
+  if (verification.code) return openErrorMsg(verification.code);
+
+  try {
+    await login({ phoneNum: form.phone, code: form.code, password: form.psw });
+
+    if (form.remember) {
+      localStorage.setItem("userName", form.phone);
+      localStorage.setItem("password", form.psw);
+      localStorage.setItem("remember", "1");
+    } else {
+      localStorage.setItem("userName", "");
+      localStorage.setItem("password", "");
+      localStorage.setItem("remember", "0");
+    }
+
+    const params = strToParams(window.location.search);
+    if ("redirect" in params) {
+      const url = new URL(unescape(params.redirect));
+      url.searchParams.delete("token");
+      url.searchParams.append("token", user.value.token);
+      window.location.replace(url);
+    } else {
+      router.replace({ name: RouteName.scene });
+    }
+  } catch (e) {
+    console.error(e);
+    return refer();
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.system-layer {
+  width: 100%;
+  min-height: 100%;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  background: no-repeat left bottom;
+  background-size: cover;
+}
+
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+}
+
+.login-layer {
+  text-align: right;
+}
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+}
+.info {
+  color: #fff;
+  flex: none;
+  text-align: left;
+  img {
+    width: 376px;
+    height: 376px;
+  }
+  h1 {
+    font-size: 2.8rem;
+    line-height: 3.7rem;
+    margin-bottom: 0.7rem;
+  }
+  p {
+    font-size: 2rem;
+    line-height: 2.2rem;
+  }
+}
+
+.top-text {
+  margin-bottom: 50px;
+  pointer-events: none;
+  height: 153px;
+  min-width: 1200px;
+  img {
+    position: absolute;
+    right: 0;
+  }
+}
+.login {
+  width: 320px;
+  padding: 40px 40px 30px;
+  position: relative;
+  display: inline-block;
+
+  h2 {
+    padding-left: 0;
+    padding-bottom: 0;
+    border-bottom: none;
+    margin-bottom: 2.14rem;
+
+    span {
+      color: #646566;
+      font-size: 1.33rem;
+      margin-top: 0.71rem;
+      display: block;
+    }
+  }
+
+  .panel-form-item {
+    padding-left: 0;
+    padding-right: 0;
+    .icon-style {
+      margin-right: 14px;
+      font-size: 20px;
+      line-height: 50px;
+    }
+  }
+
+  .more a:first-child::after {
+    content: "";
+    position: absolute;
+    right: -5px;
+    width: 1px;
+    height: 8px;
+    background: #dcdee0;
+    top: 50%;
+    transform: translateY(-50%);
+  }
+}
+
+.code-img {
+  width: 100%;
+  height: 100%;
+  // object-fit: cover;
+}
+</style>
+
+<style lang="scss">
+.panel {
+  background: rgba(255, 255, 255, 0.7);
+  box-shadow: 0px 2px 20px 0px rgba(5, 38, 38, 0.15);
+  width: 600px;
+  padding: 30px 0 40px;
+  text-align: initial;
+
+  h2 {
+    color: #323233;
+    font-size: 1.85rem;
+    margin-bottom: 2.14rem;
+    font-weight: normal;
+    padding-left: 60px;
+    padding-bottom: 20px;
+    border-bottom: 1px solid #e9e9e9;
+  }
+
+  .panel-form-item {
+    position: relative;
+    padding-bottom: 2.14rem;
+    margin: 0;
+    padding-left: 90px;
+    padding-right: 90px;
+
+    &.remember {
+      padding: 0;
+    }
+
+    .err-info {
+      position: absolute;
+      top: 100%;
+      left: 20px;
+      font-size: 1rem;
+      line-height: 2.14rem;
+      color: #fa5555;
+    }
+  }
+
+  .more {
+    text-align: center;
+
+    a {
+      color: #323233;
+      line-height: 21px;
+      font-size: 16px;
+      margin: 0 5px;
+      position: relative;
+      text-decoration: none;
+      cursor: pointer;
+    }
+  }
+}
+
+.panel-form-item .el-select {
+  width: 100%;
+}
+
+.panel-form-item .el-button,
+.panel-form-item .el-input__inner {
+  height: 50px;
+  font-size: 1.14rem;
+}
+
+.panel-form-item .el-button {
+  line-height: 26px;
+  font-weight: bold;
+  font-size: 16px;
+}
+
+.panel-form-item .el-form-item__label {
+  line-height: 50px;
+}
+.login .code-form-item .el-input {
+  display: flex;
+}
+
+.login .code-form-item .el-input-group__append {
+  flex: none;
+  margin-left: 10px;
+  width: 95px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0;
+}
+
+.login .code-form-item .el-input__inner {
+  flex: 1;
+}
+.login .code-form-item .el-input-group__append,
+.login .code-form-item .el-input__inner {
+  border-radius: 4px;
+}
+input[type="password"]::-ms-reveal {
+  display: none;
+}
+</style>

BIN
src/assets/image/jmfirebg@2x.png


BIN
src/assets/image/jmlogo.png


+ 1 - 1
src/constant/caseFile.ts

@@ -4,7 +4,7 @@ export const FileDrawType = 1;
 export const DrawFormats = [".jpg", ".jpeg", ".png"];
 export const OtherFormats = [".pdf", ".jpeg", ".doc", ".docx", ".jpg", ".png"];
 export const BoardTypeDesc = {
-  [BoardType.scene]: "户型图",
+  [BoardType.scene]: "现场图",
   [BoardType.map]: "方位图",
 };
 export const DrawFormatDesc = "jpg、png等格式的文件";

+ 6 - 1
src/hook/upload.ts

@@ -21,7 +21,12 @@ const defaultUpload = (
 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 format = computed(
+    () =>
+      `${props.formats.join("、")}${
+        props.formats.length > 1 ? "等" : ""
+      }格式的文件`
+  );
   const accept = computed(() =>
     props.formats
       .map((format) => {

+ 4 - 2
src/router/config.ts

@@ -1,5 +1,5 @@
 import { RouteName } from "./routeName";
-import { appRoutes } from "@/app";
+import { appConstant, appRoutes } from "@/app";
 
 export { RouteName };
 export type Routes = Route[];
@@ -12,11 +12,13 @@ export type Route = {
   children?: Routes;
 };
 
+console.log(appConstant.loginComponent);
 export const system: Routes = [
   {
     name: RouteName.login,
     path: "/login",
-    component: () => import("@/view/system/index.vue"),
+    component:
+      appConstant.loginComponent || (() => import("@/view/system/index.vue")),
     meta: { title: "登录" },
   },
   {

+ 9 - 5
src/view/case/draw/slider.vue

@@ -1,9 +1,9 @@
 <template>
   <div class="df-slide-content">
-    <h3>{{ BoardTypeDesc[type] }}</h3>
+    <h3>{{ fileDesc[type] }}</h3>
     <div class="def-image-set">
       <el-button type="primary" @click="emit('trackImage')" ghost>
-        设置{{ BoardTypeDesc[type] }}</el-button
+        设置{{ fileDesc[type] }}</el-button
       >
       <el-upload
         :multiple="false"
@@ -21,7 +21,7 @@
           :class="{ dispable: percentage }"
           :style="{ width: '100%' }"
         >
-          {{ percentage ? "文件上传中" : "上传" + BoardTypeDesc[type] }}
+          {{ percentage ? "文件上传中" : "上传" + fileDesc[type] }}
         </el-button>
       </el-upload>
     </div>
@@ -46,9 +46,9 @@
 <script setup lang="ts">
 import { metas, labels, images, shapes, MetaShapeType } from "./board";
 import { BoardType } from "@/store/caseFile";
-import { BoardTypeDesc, OtherFormats, maxFileSize } from "@/constant/caseFile";
+import { maxFileSize } from "@/constant/caseFile";
 import { useUpload } from "@/hook/upload";
-import { watchEffect } from "vue";
+import { computed, watchEffect } from "vue";
 import { imageCropper } from "@/view/system/quisk";
 import { fixImageSize } from "@/util/image-rotate";
 
@@ -57,6 +57,10 @@ defineProps<{
   addShape: MetaShapeType | null;
 }>();
 
+const fileDesc = {
+  [BoardType.scene]: "户型图",
+  [BoardType.map]: "方位图",
+};
 const typesShapes = [
   { name: "标注", shapes: labels },
   { name: "图例", shapes: images },

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

@@ -33,7 +33,7 @@ export const editEshapeTable = (
 
 export const showCaseScenes = quiskMountFactory(SceneList, {
   title: "案件场景管理",
-  width: 800,
+  width: 900,
   hideFloor: true,
 });
 

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

@@ -14,7 +14,7 @@
       {{ row.createTime.substr(0, 16) }}
     </el-table-column>
     <el-table-column label="所属架构" prop="deptName"></el-table-column>
-    <el-table-column label="操作" v-slot:default="{ row }: { row: Scene }">
+    <el-table-column label="操作" v-slot:default="{ row }: { row: Scene }" width="160">
       <span
         class="oper-span"
         v-pdpath="['view']"

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

@@ -4,7 +4,7 @@
       <el-form-item label="所属架构:">
         <com-select v-model="params.pagging.state.query.deptId" />
       </el-form-item>
-      <el-form-item label="sn码:" v-if="!params.isSwmx">
+      <el-form-item label="S/N码:" v-if="!params.isSwmx">
         <el-input
           v-model="params.pagging.state.query.snCode"
           placeholder="请输入"

+ 31 - 15
src/view/vrmodel/modelContent.vue

@@ -1,20 +1,28 @@
 <template>
   <div class="body-head">
     <h3 style="visibility: hidden">场景管理</h3>
-    <el-upload
-      class="upload-demo"
-      :multiple="false"
-      :limit="1"
-      :show-file-list="false"
-      :http-request="() => {}"
-      :file-list="fileList"
-      :disabled="percentage || !operateIsPermissionByPath('sync')"
-      :before-upload="uploadCheck"
-    >
-      <el-button v-pdpath="'sync'" type="primary">
-        <el-icon><Upload /></el-icon>{{ percentage ? "文件上传中" : "上传数据" }}
-      </el-button>
-    </el-upload>
+
+    <el-tooltip
+      class="item"
+      effect="dark"
+      :content="`请上传${format}(支持obj/ply/las/osgb/b3dm格式的数据),大小在${size}以内 `"
+      placement="bottom-start"
+      ><el-upload
+        class="upload-demo"
+        :multiple="false"
+        :limit="1"
+        :accept="accept"
+        :show-file-list="false"
+        :http-request="() => {}"
+        :file-list="fileList"
+        :disabled="percentage || !operateIsPermissionByPath('sync')"
+        :before-upload="uploadCheck"
+      >
+        <el-button v-pdpath="'sync'" type="primary">
+          <el-icon><Upload /></el-icon>{{ percentage ? "文件上传中" : "上传数据" }}
+        </el-button>
+      </el-upload>
+    </el-tooltip>
   </div>
 
   <el-table
@@ -109,7 +117,15 @@ const delOrCancel = async (scene: ModelScene) => {
   }
 };
 
-const { percentage, upload: uploadCheck, fileList, file, removeFile } = useUpload({
+const {
+  percentage,
+  upload: uploadCheck,
+  fileList,
+  size,
+  format,
+  removeFile,
+  accept,
+} = useUpload({
   maxSize: ModelMaxSize,
   formats: ModelSupportFormats,
   upload: async (file, onPercentage) => {

+ 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 = "fire";
+let app = "criminal";
 if (process.argv.length > 3) {
   app = process.argv[process.argv.length - 1].trim();
 }