Bläddra i källkod

Merge branch 'dev' of http://192.168.0.115:3000/bill/traffic-laser into dev

bill 1 år sedan
förälder
incheckning
97bab51845
46 ändrade filer med 905 tillägg och 964 borttagningar
  1. 1 1
      server/test/a0k4xu045_202305311600080410/attach/sceneStore
  2. BIN
      src/assets/images/law/1.jpg
  3. BIN
      src/assets/images/law/10.jpg
  4. BIN
      src/assets/images/law/11.jpg
  5. BIN
      src/assets/images/law/12.jpg
  6. BIN
      src/assets/images/law/13.jpg
  7. BIN
      src/assets/images/law/14.jpg
  8. BIN
      src/assets/images/law/15.jpg
  9. BIN
      src/assets/images/law/16.jpg
  10. BIN
      src/assets/images/law/17.jpg
  11. BIN
      src/assets/images/law/18.jpg
  12. BIN
      src/assets/images/law/19.jpg
  13. BIN
      src/assets/images/law/2.jpg
  14. BIN
      src/assets/images/law/20.jpg
  15. BIN
      src/assets/images/law/21.jpg
  16. BIN
      src/assets/images/law/22.jpg
  17. BIN
      src/assets/images/law/23.jpg
  18. BIN
      src/assets/images/law/24.jpg
  19. BIN
      src/assets/images/law/25.jpg
  20. BIN
      src/assets/images/law/26.jpg
  21. BIN
      src/assets/images/law/27.jpg
  22. BIN
      src/assets/images/law/3.jpg
  23. BIN
      src/assets/images/law/4.jpg
  24. BIN
      src/assets/images/law/5.jpg
  25. BIN
      src/assets/images/law/6.jpg
  26. BIN
      src/assets/images/law/7.jpg
  27. BIN
      src/assets/images/law/8.jpg
  28. BIN
      src/assets/images/law/9.jpg
  29. 1 0
      src/main.ts
  30. 2 10
      src/router/constant.ts
  31. 28 60
      src/router/info.ts
  32. 0 308
      src/views/scene/home.vue
  33. 36 5
      src/views/scene/index.vue
  34. 90 57
      src/views/scene/photo.vue
  35. 0 314
      src/views/tables/FG.html
  36. 68 25
      src/views/tables/ask.vue
  37. 30 5
      src/views/tables/author/author-two.vue
  38. 4 0
      src/views/tables/explorate-one.vue
  39. 0 126
      src/views/tables/explorate.vue
  40. 46 47
      src/views/tables/extract.vue
  41. 5 2
      src/views/tables/identification.vue
  42. 253 0
      src/views/tables/index.vue
  43. 22 0
      src/views/tables/law.vue
  44. 16 4
      src/views/tables/legacy.vue
  45. 139 0
      src/views/tables/write/doc.vue
  46. 164 0
      src/views/tables/write/index.vue

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
server/test/a0k4xu045_202305311600080410/attach/sceneStore


BIN
src/assets/images/law/1.jpg


BIN
src/assets/images/law/10.jpg


BIN
src/assets/images/law/11.jpg


BIN
src/assets/images/law/12.jpg


BIN
src/assets/images/law/13.jpg


BIN
src/assets/images/law/14.jpg


BIN
src/assets/images/law/15.jpg


BIN
src/assets/images/law/16.jpg


BIN
src/assets/images/law/17.jpg


BIN
src/assets/images/law/18.jpg


BIN
src/assets/images/law/19.jpg


BIN
src/assets/images/law/2.jpg


BIN
src/assets/images/law/20.jpg


BIN
src/assets/images/law/21.jpg


BIN
src/assets/images/law/22.jpg


BIN
src/assets/images/law/23.jpg


BIN
src/assets/images/law/24.jpg


BIN
src/assets/images/law/25.jpg


BIN
src/assets/images/law/26.jpg


BIN
src/assets/images/law/27.jpg


BIN
src/assets/images/law/3.jpg


BIN
src/assets/images/law/4.jpg


BIN
src/assets/images/law/5.jpg


BIN
src/assets/images/law/6.jpg


BIN
src/assets/images/law/7.jpg


BIN
src/assets/images/law/8.jpg


BIN
src/assets/images/law/9.jpg


+ 1 - 0
src/main.ts

@@ -15,6 +15,7 @@ import { router, setupRouter } from "@/router";
 import appConfig from "./appConfig";
 import { currentApp, setCurrentApp } from "@/store/app";
 import App from "./main.vue";
+import 'swiper/swiper.min.css';
 const app = createApp(App);
 
 setCurrentApp(appConfig);

+ 2 - 10
src/router/constant.ts

@@ -9,16 +9,12 @@ export const readyRouteName = {
   measure: 'measure',
   graphic: 'graphic',
   scene: 'scene',
-  home: 'home',
   photos: 'photos',
   accidents: 'accidents',
   roads: 'roads',
   tabulation: 'tabulation',
-  ask: 'ask',
   gena4: 'gena4',
-  explorate: 'explorate',
-  extract: 'extract',
-  identification: 'identification',
+  tables: 'tables',
   demo: 'demo',
 } as const;
 
@@ -48,16 +44,12 @@ export const readyRouteMeta: RouteMetaRaw = {
   [readyRouteName.measure]: { title: ui18n.t('measure.name') },
   [readyRouteName.graphic]: { title: '绘图' },
   [readyRouteName.scene]: { title: '绘图' },
-  [readyRouteName.home]: { title: '首页' },
   [readyRouteName.photos]: { title: '相册' },
   [readyRouteName.accidents]: { title: '事故照片' },
   [readyRouteName.roads]: { title: '道路照片' },
   [readyRouteName.tabulation]: { title: '制表' },
   [readyRouteName.gena4]: { title: '生成A4' },
-  [readyRouteName.ask]: { title: '询问笔录' },
-  [readyRouteName.explorate]: { title: '道路交通事故现场勘查笔录' },
-  [readyRouteName.extract]: { title: '当事人血样(尿样)提取登记表' },
-  [readyRouteName.identification]: { title: '道路交通事故认定书(简易程序)' },
+  [readyRouteName.tables]: { title: '道路交通事故现场勘查笔录' },
   [readyRouteName.demo]: { title: '表格' },
 };
 

+ 28 - 60
src/router/info.ts

@@ -1,107 +1,75 @@
-import { modeFlags, ModeFlag } from "@/store/sys";
-import { ComputedRef } from "vue";
-import { RouteRecordRaw } from "vue-router";
-import {
-  RouteNameRaw,
-  RouteMetaRaw,
-  readyRouteMeta,
-  readyRouteName,
-  defRouteName,
-} from "./constant";
+import { modeFlags, ModeFlag } from '@/store/sys';
+import { ComputedRef } from 'vue';
+import { RouteRecordRaw } from 'vue-router';
+import { RouteNameRaw, RouteMetaRaw, readyRouteMeta, readyRouteName, defRouteName } from './constant';
 
-export type RouteAtom<
-  T extends ModeFlag = any,
-  key = RouteNameRaw<T>[keyof RouteNameRaw<T>]
-> = {
+export type RouteAtom<T extends ModeFlag = any, key = RouteNameRaw<T>[keyof RouteNameRaw<T>]> = {
   path: string;
   name: key;
   meta: key extends keyof RouteMetaRaw<T> ? RouteMetaRaw<T>[key] : never;
-  component: RouteRecordRaw["component"];
+  component: RouteRecordRaw['component'];
   redirect?: string;
   children?: RoutesRaw<T>;
 };
 
 export type RoutesRaw<T extends ModeFlag = any> = RouteAtom<T>[];
 
-import graphic from "@/views/graphic/index.vue";
+import graphic from '@/views/graphic/index.vue';
 export const writeRoutesRaw: RoutesRaw<typeof modeFlags.LOGIN> = [
   {
-    path: "/graphic/:mode/:action/:id",
+    path: '/graphic/:mode/:action/:id',
     name: readyRouteName.graphic,
     meta: readyRouteMeta.graphic,
-    component: () => import("@/views/graphic/index.vue"),
+    component: () => import('@/views/graphic/index.vue'),
   },
   {
-    path: "/scene",
+    path: '/scene',
     name: readyRouteName.scene,
     meta: readyRouteMeta.scene,
-    component: () => import("@/views/scene/index.vue"),
-  },
-  {
-    path: "/home",
-    name: readyRouteName.home,
-    meta: readyRouteMeta.home,
-    component: () => import("@/views/scene/home.vue"),
+    component: () => import('@/views/scene/index.vue'),
   },
+
   {
-    path: "/photos",
+    path: '/photos',
     name: readyRouteName.photos,
     meta: readyRouteMeta.photos,
-    component: () => import("@/views/photos/index.vue"),
+    component: () => import('@/views/photos/index.vue'),
   },
   {
-    path: "/accidents",
+    path: '/accidents',
     name: readyRouteName.accidents,
     meta: readyRouteMeta.accidents,
-    component: () => import("@/views/accidents/index.vue"),
+    component: () => import('@/views/accidents/index.vue'),
   },
   {
-    path: "/gena4/:id1/:id2",
+    path: '/gena4/:id1/:id2',
     name: readyRouteName.gena4,
     meta: readyRouteMeta.gena4,
-    component: () => import("@/views/accidents/print.vue"),
+    component: () => import('@/views/accidents/print.vue'),
   },
   {
-    path: "/roads",
+    path: '/roads',
     name: readyRouteName.roads,
     meta: readyRouteMeta.roads,
-    component: () => import("@/views/roads/index.vue"),
+    component: () => import('@/views/roads/index.vue'),
   },
   {
-    path: "/tabulation/:id",
+    path: '/tabulation/:id',
     name: readyRouteName.tabulation,
     meta: readyRouteMeta.tabulation,
-    component: () => import("@/views/roads/tabulation.vue"),
-  },
-  {
-    path: "/ask/:id",
-    name: readyRouteName.ask,
-    meta: readyRouteMeta.ask,
-    component: () => import("@/views/tables/ask.vue"),
-  },
-  {
-    path: "/explorate/:id",
-    name: readyRouteName.explorate,
-    meta: readyRouteMeta.explorate,
-    component: () => import("@/views/tables/explorate.vue"),
-  },
-  {
-    path: "/extract/:id",
-    name: readyRouteName.extract,
-    meta: readyRouteMeta.extract,
-    component: () => import("@/views/tables/extract.vue"),
+    component: () => import('@/views/roads/tabulation.vue'),
   },
   {
-    path: "/identification/:id",
-    name: readyRouteName.identification,
-    meta: readyRouteMeta.identification,
-    component: () => import("@/views/tables/identification.vue"),
+    path: '/tables/:name',
+    name: readyRouteName.tables,
+    meta: readyRouteMeta.tables,
+    component: () => import('@/views/tables/index.vue'),
   },
   {
-    path: "/demo/:id",
+    path: '/demo/:id',
     name: readyRouteName.demo,
     meta: readyRouteMeta.demo,
-    component: () => import("@/views/tables/demo.vue"),
+    component: () => import('@/views/tables/demo.vue'),
   },
 ];
 

+ 0 - 308
src/views/scene/home.vue

@@ -1,308 +0,0 @@
-<template>
-  <MainPanel>
-    <template v-slot:header>
-      <div class="photos-header">
-        <div class="left">
-          <ui-icon class="back-icon" type="return" ctrl style="margin-right: 10px" @click="router.back" />
-          <span> 标题 </span>
-        </div>
-      </div>
-    </template>
-
-    <div class="info-layout">
-      <div class="info-top">
-        <div class="info-top-left" :class="{ full: viewStatus }">
-          <Container @loaded="loaded = true" />
-          <template v-if="loaded && !trackMode">
-            <Menus @enter-child="childPage = true" @leave-child="childPage = false" />
-            <BasePoints v-if="currentView" />
-            <FixPoints />
-            <Measures />
-            <Photo />
-            <ButtonPane class="back fun-ctrl" size="48" @click="router.push('/scene')" v-if="!childPage">
-            <!-- <ButtonPane class="back fun-ctrl" size="48" @click="onScale" v-if="!childPage"> -->
-              <ui-icon type="screen_f" class="icon" />
-            </ButtonPane>
-            <Mode />
-          </template>
-        </div>
-        <div class="info-top-right" :class="{ full: viewStatus }">
-          <div class="input-item">
-            <p>事故时间:</p>
-            <input type="text" />
-          </div>
-          <div class="input-item">
-            <p>天气:</p>
-            <input type="text" />
-          </div>
-          <div class="input-item">
-            <p>地点:</p>
-            <input type="text" />
-          </div>
-          <div class="text-item">
-            <p>事故描述:</p>
-            <textarea class="info-textarea"></textarea>
-          </div>
-          <div class="info-btn">
-            <div class="right-btn">现场绘图(0)</div>
-            <div class="right-btn">事故照片(0)</div>
-          </div>
-        </div>
-      </div>
-      <div class="info-bottom" :class="{ full: viewStatus }">
-        <div v-for="(i, index) in list">
-          <ui-icon type="return"></ui-icon>
-          <span> {{ i.name }}</span>
-        </div>
-      </div>
-    </div>
-  </MainPanel>
-</template>
-
-<script lang="ts" setup>
-import MainPanel from '@/components/main-panel/index.vue';
-import Header from '@/components/photos/header.vue';
-import Container from './container.vue';
-import Mode from './mode.vue';
-import Menus from './menus/pane.vue';
-import BasePoints from '@/views/scene/covers/basePoints.vue';
-import FixPoints from '@/views/scene/covers/fixPoints.vue';
-import Measures from '@/views/scene/covers/measures.vue';
-import Photo from './photo.vue';
-import ButtonPane from '@/components/button-pane';
-import { customMap, disabledMap, useSDK } from '@/hook';
-import customSetup from '../../hook/custom';
-import UiIcon from '@/components/base/components/icon/index.vue';
-import { ref, watchEffect } from 'vue';
-import { back } from '@/store/sync';
-import { trackMode } from '@/views/scene/trackMeasureWidth';
-import { currentView } from './currentScene';
-import { api } from '@/store/sync';
-import { router, writeRouteName } from '@/router';
-const loaded = ref(false);
-const childPage = ref(false);
-const stopReg = watchEffect(() => {
-  if (loaded.value) {
-    const sdk = useSDK();
-    stopReg();
-    customSetup(sdk);
-    disabledMap.measure = false;
-  }
-});
-const viewStatus = ref(false);
-const onScale = () => {
-  viewStatus.value = !viewStatus.value;
-};
-const list = ref([
-  {
-    id: 1,
-    icon: 'return',
-    name: '勘查笔录',
-  },
-  {
-    id: 2,
-    icon: 'return',
-    name: '询问笔录',
-  },
-  {
-    id: 3,
-    icon: 'return',
-    name: '讯问笔录',
-  },
-  {
-    id: 4,
-    icon: 'return',
-    name: '事故认定',
-  },
-  {
-    id: 5,
-    icon: 'return',
-    name: '血样登记表',
-  },
-  {
-    id: 6,
-    icon: 'return',
-    name: '遗留物品清单',
-  },
-  {
-    id: 7,
-    icon: 'return',
-    name: '授权委托书',
-  },
-  {
-    id: 8,
-    icon: 'return',
-    name: '法律法规',
-  },
-]);
-</script>
-
-<style scoped lang="scss">
-.info-layout {
-  width: 100%;
-  height: 100%;
-  // padding-top: 50px;
-  background: rgba(22, 24, 26, 1);
-  padding: 70px 20px 20px 20px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: space-between;
-  .info-top {
-    width: 100%;
-    height: 83%;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    .info-top-left {
-      width: 70%;
-      height: 100%;
-      position: relative;
-      overflow: hidden;
-      transition: all 0.3s;
-      &.full {
-        position: fixed;
-        width: 100%;
-        height: 100%;
-        top: 0;
-        left: 0;
-        z-index: 1000;
-        transform-origin: center center;
-      }
-      .fun-ctrl {
-        position: absolute;
-        left: var(--boundMargin);
-        top: var(--boundMargin);
-        padding: 0;
-        width: 48px;
-        height: 48px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-      }
-    }
-    .info-top-right {
-      width: calc(30% - 20px);
-      height: 100%;
-      position: relative;
-      overflow: hidden;
-      font-size: 16px;
-      color: rgba(255, 255, 255, 0.8);
-      display: flex;
-      flex-direction: column;
-      justify-content: space-between;
-      &.full {
-        width: 0;
-      }
-      p {
-        margin-bottom: 4px;
-      }
-      .input-item {
-        height: 10%;
-
-        input {
-          width: 100%;
-          padding: 0 8px;
-          height: 56%;
-          background: rgba(255, 255, 255, 0.1);
-          border-radius: 4px 4px 4px 4px;
-          border: 1px solid rgba(255, 255, 255, 0.5);
-          color: rgba(255, 255, 255, 0.8);
-        }
-      }
-      .text-item {
-        width: 100%;
-        height: 35%;
-        .info-textarea {
-          width: 100%;
-          padding: 8px;
-          height: 87%;
-          background: rgba(255, 255, 255, 0.1);
-          border-radius: 4px 4px 4px 4px;
-          border: 1px solid rgba(255, 255, 255, 0.5);
-          color: rgba(255, 255, 255, 0.8);
-          outline: none;
-          resize: none;
-        }
-      }
-      .info-btn {
-        width: 100%;
-        height: 19.5%;
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-        .right-btn {
-          width: 296px;
-          height: 42.8%;
-          background: rgba(255, 255, 255, 0.1);
-          border-radius: 4px 4px 4px 4px;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-  }
-  .info-bottom {
-    width: 100%;
-    height: 14.49%;
-    display: flex;
-    flex-wrap: nowrap;
-    &.full {
-      height: 0;
-    }
-    > div {
-      width: 100%;
-      height: 100%;
-      margin-right: 20px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      background: rgba(255, 255, 255, 0.1);
-      font-size: 16px;
-      border-radius: 4px;
-      > span {
-        margin-top: 8px;
-      }
-      &:last-of-type {
-        margin-right: 0;
-      }
-    }
-  }
-}
-.photos-header {
-  width: 100%;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  position: relative;
-
-  .center {
-    position: absolute;
-    left: 0;
-    right: 0;
-    white-space: nowrap;
-    // pointer-events: none;
-    height: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    text-align: center;
-  }
-  .left,
-  .right {
-    z-index: 1;
-  }
-}
-
-.back-icon {
-  display: inline-flex;
-  width: 32px;
-  height: 32px;
-  background: rgba(255, 255, 255, 0.1);
-  border-radius: 24px 24px 24px 24px;
-  align-items: center;
-  justify-content: center;
-}
-</style>

+ 36 - 5
src/views/scene/index.vue

@@ -111,51 +111,60 @@ const onScale = () => {
   viewStatus.value = !viewStatus.value;
 };
 const goItem = (item) => {
-  if (item.id == 8) {
-    return;
-  }
-  router.push(`/demo/${item.id}`);
+  // if (item.id == 8) {
+  //   return;
+  // }
+  // router.push(`/demo/${item.id}`);
+  router.push(`/tables/${item.type}`);
 };
 const list = ref([
   {
     id: 1,
     icon: 'prospect',
     name: '勘查笔录',
+    type: 'explorate',
   },
   {
     id: 2,
     icon: 'inquiry',
     name: '询问笔录',
+    type: 'ask?type=1',
   },
   {
     id: 3,
     icon: 'question',
     name: '讯问笔录',
+    type: 'ask?type=2',
   },
   {
     id: 4,
     icon: 'accident',
     name: '事故认定',
+    type: 'identification',
   },
   {
     id: 5,
     icon: 'blood',
     name: '血样登记表',
+    type: 'extract',
   },
   {
     id: 6,
     icon: 'items',
     name: '遗留物品清单',
+    type: 'legacy',
   },
   {
     id: 7,
     icon: 'authorization',
     name: '授权委托书',
+    type: 'author',
   },
   {
     id: 8,
     icon: 'law',
     name: '法律法规',
+    type: 'law',
   },
 ]);
 onMounted(() => {
@@ -290,7 +299,8 @@ onActivated(async () => {
         flex-direction: column;
         justify-content: space-between;
         .right-btn {
-          width: 296px;
+          cursor: pointer;
+          width: 100%;
           height: 42.8%;
           background: rgba(255, 255, 255, 0.1);
           border-radius: 4px 4px 4px 4px;
@@ -320,6 +330,7 @@ onActivated(async () => {
       background: rgba(255, 255, 255, 0.1);
       font-size: 16px;
       border-radius: 4px;
+      cursor: pointer;
       > span {
         margin-top: 8px;
       }
@@ -367,3 +378,23 @@ onActivated(async () => {
   justify-content: center;
 }
 </style>
+<style lang="scss">
+#navCube {
+  opacity: 0;
+  pointer-events: none;
+}
+#home {
+  opacity: 0;
+  pointer-events: none;
+}
+.full {
+  #navCube {
+    opacity: 1;
+    pointer-events: auto;
+  }
+  #home {
+    opacity: 1;
+    pointer-events: auto;
+  }
+}
+</style>

+ 90 - 57
src/views/scene/photo.vue

@@ -5,45 +5,39 @@
       <ui-icon type="photo" class="icon" />
     </ButtonPane>
 
-    <img
-      v-if="showCoverUrl"
-      :src="showCoverUrl.value"
-      class="cover"
-      :style="{ opacity: showCoverUrl ? '1' : 0 }"
-      @click="router.push(writeRouteName.photos)"
-    />
+    <img v-if="showCoverUrl" :src="showCoverUrl.value" class="cover" :style="{ opacity: showCoverUrl ? '1' : 0 }" @click="router.push(writeRouteName.photos)" />
   </div>
 </template>
 
 <script setup lang="ts">
-import UiIcon from "@/components/base/components/icon/index.vue";
-import ButtonPane from "@/components/button-pane/index.vue";
-import { list } from "@/store/measure";
-import { fixPoints } from "@/store/fixPoint";
-import { baseLines } from "@/store/baseLine";
-import { basePoints } from "@/store/basePoint";
-import { photos } from "@/store/photos";
-import { useSDK } from "@/hook/useLaser";
-import { genUseLoading } from "@/hook";
-
-import { disabledMap } from "@/hook/custom";
-import { base64ToBlob, formatDate, getId } from "@/utils";
-import { computed, nextTick, ref, watchEffect } from "vue";
-import { api, downloadImage, uploadImage } from "@/store/sync";
-import { router, writeRouteName } from "@/router";
-import { LaserSDK, Pos, Pos3D } from "@/sdk";
-import { useStaticUrl } from "@/hook/useStaticUrl";
-import { Loading } from "@kankan/components/index";
-import { generateMixMenus, MenuRaw, menus, findMenuByKey } from "./menus/menus";
+import UiIcon from '@/components/base/components/icon/index.vue';
+import ButtonPane from '@/components/button-pane/index.vue';
+import { list } from '@/store/measure';
+import { fixPoints } from '@/store/fixPoint';
+import { baseLines } from '@/store/baseLine';
+import { basePoints } from '@/store/basePoint';
+import { photos } from '@/store/photos';
+import { useSDK } from '@/hook/useLaser';
+import { genUseLoading } from '@/hook';
+
+import { disabledMap } from '@/hook/custom';
+import { base64ToBlob, formatDate, getId } from '@/utils';
+import { computed, nextTick, ref, watchEffect } from 'vue';
+import { api, downloadImage, uploadImage } from '@/store/sync';
+import { router, writeRouteName } from '@/router';
+import { LaserSDK, Pos, Pos3D } from '@/sdk';
+import { useStaticUrl } from '@/hook/useStaticUrl';
+import { Loading } from '@kankan/components/index';
+import { generateMixMenus, MenuRaw, menus, findMenuByKey } from './menus/menus';
 const menusMix = computed(() => menus);
-const store = generateMixMenus("children", (m) => m, menusMix.value);
+const store = generateMixMenus('children', (m) => m, menusMix.value);
 const showCoverUrl = computed(() => {
   if (photos.value[photos.value.length - 1]?.url) {
     return useStaticUrl(photos.value[photos.value.length - 1].url);
   }
 });
 
-import mp3url from "./camera1.mp3";
+import mp3url from './camera1.mp3';
 const tempPhoto = ref<string>();
 const coverRef = ref<HTMLImageElement>();
 const audio = new Audio(mp3url);
@@ -57,7 +51,7 @@ watchEffect(() => {
 });
 const screenshot = async (sdk: LaserSDK) => {
   const dom = sdk.scene.el;
-  dom.style.pointerEvents = "none";
+  dom.style.pointerEvents = 'none';
 
   const getScreenshot = async (down = false) => {
     const data = sdk.scene.screenshot(dom.offsetWidth, dom.offsetHeight);
@@ -65,9 +59,7 @@ const screenshot = async (sdk: LaserSDK) => {
     const blob = base64ToBlob(base64);
     let url: string;
     if (down) {
-      const filename = `img_${formatDate(new Date(), "yyyyMMddhhmmss")}_${
-        data.meterPerPixel || 1
-      }_${new Date().getTime().toString().substring(8)}.jpg`;
+      const filename = `img_${formatDate(new Date(), 'yyyyMMddhhmmss')}_${data.meterPerPixel || 1}_${new Date().getTime().toString().substring(8)}.jpg`;
       url = await uploadImage(blob, filename);
       await downloadImage(blob, filename);
     } else {
@@ -87,7 +79,7 @@ const screenshot = async (sdk: LaserSDK) => {
   baseLines.value.concat(list.value).forEach((item) => (item.show = true));
   await nextTick();
 
-  dom.style.pointerEvents = "all";
+  dom.style.pointerEvents = 'all';
   return {
     rawUrl: screenshotRaw.url,
     url: screenshot.url,
@@ -95,28 +87,27 @@ const screenshot = async (sdk: LaserSDK) => {
   };
 };
 
-const getCurrentScreens = (poss: Array<Pos3D>): Array<Pos> =>
-  poss.map(getCurrentScreen).filter((pos) => !!pos);
+const getCurrentScreens = (poss: Array<Pos3D>): Array<Pos> => poss.map(getCurrentScreen).filter((pos) => !!pos);
 
 const photo = async () => {
   Loading.show();
-  console.log("播放音频");
+  console.log('播放音频');
   try {
     await audio.play();
   } catch (e) {
-    console.error("播放音频错误", e);
+    console.error('播放音频错误', e);
   }
-  console.log("音频播放结束");
+  console.log('音频播放结束');
   const sdk = useSDK();
-  console.log("开始截图");
+  console.log('开始截图');
   const data = await screenshot(sdk);
-  console.log("截图完成");
+  console.log('截图完成');
   tempPhoto.value = await api.getFile(data.rawUrl);
-  console.log("获取到临时文件");
+  console.log('获取到临时文件');
   await nextTick();
 
   const handler = async () => {
-    coverRef.value.removeEventListener("animationend", handler);
+    coverRef.value.removeEventListener('animationend', handler);
     tempPhoto.value = null;
 
     photos.value.push({
@@ -135,18 +126,14 @@ const photo = async () => {
           }
         })
         .filter((poss) => poss?.pos.length === 2),
-      baseLines: baseLines.value
-        .map((data) => getCurrentScreens(data.points))
-        .filter((poss) => poss.length === 2),
-      fixPoints: fixPoints.value
-        .map((data) => ({ text: data.text, pos: getCurrentScreen(data.pos) }))
-        .filter((data) => !!data.pos),
+      baseLines: baseLines.value.map((data) => getCurrentScreens(data.points)).filter((poss) => poss.length === 2),
+      fixPoints: fixPoints.value.map((data) => ({ text: data.text, pos: getCurrentScreen(data.pos) })).filter((data) => !!data.pos),
       basePoints: getCurrentScreens(basePoints.value.map((data) => data.pos)),
     });
 
     Loading.hide();
   };
-  coverRef.value.addEventListener("animationend", handler);
+  coverRef.value.addEventListener('animationend', handler);
 };
 </script>
 
@@ -184,15 +171,27 @@ const photo = async () => {
   border-radius: 24px;
   overflow: hidden;
 }
-
 .face-animation {
-  pointer-events: none;
-  position: absolute;
-  left: 0;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  animation: 1s linear 1 both photo-face;
+    pointer-events: none;
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 100vw;
+    height: 100vh;
+    animation: 1s linear 1 both small-photo-face;
+  }
+.full {
+  .face-animation {
+    pointer-events: none;
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    animation: 1s linear 1 both photo-face;
+  }
 }
 .face-animation.start {
 }
@@ -229,4 +228,38 @@ const photo = async () => {
     top: calc(calc(50%) + 20px);
   }
 }
+@keyframes small-photo-face {
+  from {
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    margin-left: 0;
+    margin-top: 0;
+    border-radius: 0;
+  }
+  30%,
+  40%,
+  to {
+    left: 50%;
+    top: 100px;
+    width: 48px;
+    height: 48px;
+    margin-left: -24px;
+    border-radius: 50%;
+    z-index: 3;
+  }
+
+  70% {
+    left: calc(70% - 60px);
+    top: calc(calc(50% - 48px) / 1.5);
+
+ 
+  }
+
+  to {
+    left: calc(100% - 63px);
+    top: calc(calc(50%) + 20px);
+  }
+}
 </style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 314
src/views/tables/FG.html


+ 68 - 25
src/views/tables/ask.vue

@@ -1,22 +1,25 @@
 <!--  -->
 <template>
   <!-- <iframe style="width:100%;height: 100%;" src="./static/html/FG.html" frameborder="0"></iframe> -->
+
   <div class="ask-content">
-    <div class="num-box">
+    <!-- <Write v-if="isWrite" :text="text" :textIndex="textIndex" @onTextConfirm="onTextConfirm"> </Write> -->
+    <div v-show="!isWrite" class="num-box">
       <span>第</span>
-      <div class="input-box" contenteditable></div>
+      <div class="input-box">1</div>
       <span style="margin-right: 30px">页</span> <span>共</span>
-      <div class="input-box" contenteditable></div>
+      <div class="input-box">{{ page }}</div>
       <span>页</span>
     </div>
-    <div class="num-box" style="padding-right: 114px">
+    <div v-show="!isWrite" class="num-box" style="padding-right: 114px">
       <span>第</span>
       <div class="input-box" contenteditable></div>
       <span>次</span>
     </div>
 
-    <h2 class="title">询问/<span class="through">讯问</span>笔录</h2>
-    <div class="container">
+    <h2 v-show="!isWrite" class="title" v-if="type == '1'">询问/<span class="through">讯问</span>笔录</h2>
+    <h2 v-show="!isWrite" class="title" v-else><span class="through">询问</span>/讯问笔录</h2>
+    <div class="container" v-show="!isWrite">
       <div class="line">
         <span>时间</span>
         <div class="write-line" contenteditable></div>
@@ -28,7 +31,8 @@
         <div class="write-line left" contenteditable></div>
       </div>
       <div class="line">
-        <span>询问/<span class="through">讯问</span>人</span>
+        <span v-if="type == '1'">询问/<span class="through">讯问</span>人</span>
+        <span v-else><span class="through">询问</span>/讯问人</span>
         <div class="write-line" contenteditable></div>
         <span>工作单位</span>
         <div class="write-line" contenteditable></div>
@@ -40,7 +44,8 @@
         <div class="write-line" contenteditable></div>
       </div>
       <div class="line">
-        <span>被询问/<span class="through">讯问</span>人</span>
+        <span v-if="type == '1'">被询问/<span class="through">讯问</span>人</span>
+        <span v-else>被<span class="through">询问</span>/讯问人</span>
         <div style="flex: 0.5" class="write-line" contenteditable></div>
         <span>性别</span>
         <div style="flex: 0.3" class="write-line" contenteditable></div>
@@ -65,7 +70,8 @@
         <div class="write-line left" contenteditable></div>
       </div>
       <div class="line worap">
-        <span style="line-height: 40px"> (口头传唤/被扭送/自动投案的被询问/<span class="through">讯问</span>人于</span>
+        <span v-if="type == '1'" style="line-height: 40px"> (口头传唤/被扭送/自动投案的被询问/<span class="through">讯问</span>人于</span>
+        <span v-else style="line-height: 40px"> (口头传唤/被扭送/自动投案的被<span class="through">询问</span>/讯问人于</span>
         <div class="write-line" style="width: 40%" contenteditable></div>
         <span style="line-height: 40px"> 到达,</span>
         <div class="write-line" style="" contenteditable></div>
@@ -74,35 +80,71 @@
         <div class="write-line" style="" contenteditable></div>
         <span> )。</span>
       </div>
-      <div class="more-line">
-        <textarea v-if="!downMode" v-model="text" name=""></textarea>
-        <div class="downMode">{{ text }}</div>
+      <div class="more-line" @click="goWrite" id="line-box">
+        <!-- <div class="more-line"> -->
+        <!-- <textarea v-if="!downMode" v-model="text" name=""></textarea> -->
+        <!-- <textarea v-model="text" name=""  id="line-box"></textarea> -->
+        <div class="downMode" v-html="text"></div>
         <div class="more-line-box">
-          <div class="item"></div>
-          <div class="item"></div>
-          <div class="item"></div>
-          <div class="item"></div>
-          <div class="item"></div>
-          <div class="item"></div>
+          <div class="item" v-for="(i, index) in 10"></div>
         </div>
       </div>
     </div>
-    <div class="bottom-name">
-      <span>被询问人:</span>
+    <div class="bottom-name" v-show="!isWrite">
+      <span v-if="type == '1'">被询问人:</span>
+      <span v-else>被讯问人:</span>
       <div style="flex: 1" contenteditable></div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { reactive, ref, toRefs, onBeforeMount, onMounted, defineProps } from 'vue';
+import { reactive, ref, toRefs, onBeforeMount, onMounted, defineProps, defineEmits } from 'vue';
+import { router } from '@/router';
+import Write from './write/index.vue';
+import { bus } from '@/hook/useGraphic';
 const props = defineProps({
   downMode: {
     type: Boolean,
     default: false,
   },
+  text: {
+    type: String,
+    default: '',
+  },
+  page: {
+    type: Number,
+    default: 1,
+  },
+});
+const emits = defineEmits(['onTextConfirm', 'goWrite']);
+const isWrite = ref(false);
+// const text = ref('');
+const type = ref(router.currentRoute.value.query.type);
+// const page = ref(1);
+const textIndex = ref(0);
+const onTextConfirm = (data) => {
+  text.value = data.text;
+  // page.value = data.page;
+  isWrite.value = false;
+  emits('onTextConfirm', data);
+};
+
+const goWrite = () => {
+  let text = window.getSelection();
+  textIndex.value = text.anchorOffset;
+
+  // isWrite.value = true;
+  emits('goWrite', { textIndex: textIndex.value });
+};
+// const handlerBus = (data) => {
+//   console.error(data);
+//   textIndex.value = data.textIndex;
+//   isWrite.value = true;
+// };
+onMounted(() => {
+  // bus.on('goWrite', handlerBus);
 });
-const text = ref('');
 </script>
 <style lang="scss" scoped>
 div[contenteditable] {
@@ -186,7 +228,8 @@ div[contenteditable] {
     }
     .more-line {
       width: 100%;
-      height: 240px;
+      // height: 240px;
+      height: 400px;
       line-height: 40px;
       position: relative;
       overflow-y: hidden;
@@ -222,7 +265,7 @@ div[contenteditable] {
         top: 0;
         left: 0;
         z-index: 2;
-        white-space: pre-line;
+        white-space: pre-wrap;
         text-align: justify;
       }
       .more-line-box {
@@ -234,7 +277,7 @@ div[contenteditable] {
         // z-index: 1;
         .item {
           width: 100%;
-          height: 16.6%;
+          height: 10%;
           border-bottom: 1px solid #000;
         }
       }

+ 30 - 5
src/views/tables/author/author-two.vue

@@ -2,10 +2,22 @@
 <template>
   <div class="author">
     <div class="content">
+      <!-- <div class="check-item" @click="checkOptions(checkData1, index)" v-for="(i, index) in checkData1.options">
+        <div class="item">
+          <ui-icon :type="checkData1.check == i.id ? 'rb_y' : 'rb_n'"></ui-icon>
+          <span>{{ i.title }}</span>
+          <div v-if="i.id == 1 || i.id == 4" class="input-box" style="flex: 1">
+            <input type="text" />
+          </div>
+        </div>
+      </div> -->
       <div class="check-item" @click="checkOptions(checkData, index)" v-for="(i, index) in checkData.options">
-        <div style="display: inline-block">
+        <div class="item" :style="i.id == 1 || i.id == 4 ? '' : 'display: inline-block'">
           <ui-icon :type="checkData.check == i.id ? 'rb_y' : 'rb_n'"></ui-icon>
           <span>{{ i.title }}</span>
+          <div v-if="i.id == 1 || i.id == 4" class="input-box" style="flex: 1">
+            <input type="text" />
+          </div>
         </div>
       </div>
 
@@ -26,6 +38,15 @@ import { reactive, ref, toRefs, onBeforeMount, onMounted } from 'vue';
 const checkOptions = (item, index) => {
   item.check = item.options[index].id;
 };
+const checkData1 = ref({
+  check: 0,
+  options: [
+    {
+      id: 1,
+      title: '...',
+    },
+  ],
+});
 const checkData = ref({
   check: 0,
   options: [
@@ -79,13 +100,17 @@ div[contenteditable] {
       display: flex;
       align-items: center;
       justify-content: flex-start;
-      margin-bottom: 20px;
+      // margin-bottom: 20px;
     }
     .check-item {
       margin-bottom: 30px;
-
-      span {
-        line-height: 30px;
+      .item {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        span {
+          line-height: 30px;
+        }
       }
     }
   }

+ 4 - 0
src/views/tables/explorate-one.vue

@@ -683,6 +683,7 @@ div {
             display: flex;
             align-items: center;
             justify-content: center;
+            line-height: 30px;
           }
           .road-num {
             width: 23.8%;
@@ -699,6 +700,7 @@ div {
             display: flex;
             align-items: center;
             justify-content: center;
+            line-height: 30px;
           }
         }
         > div {
@@ -765,6 +767,7 @@ div {
                 > div {
                   flex: 1;
                   height: 30px;
+                  line-height: 30px;
                   outline: none;
                   display: flex;
                   align-items: center;
@@ -781,6 +784,7 @@ div {
                 > div {
                   flex: 1;
                   height: 30px;
+                  line-height: 30px;
                   outline: none;
                   display: flex;
                   align-items: center;

+ 0 - 126
src/views/tables/explorate.vue

@@ -1,126 +0,0 @@
-<!--  -->
-<template>
-  <MainPanel>
-    <template v-slot:header>
-      <Header title="道路交通事故现场勘查笔录" type="return">
-        <ui-button type="primary" width="96px" @click="getLayoutImage"> 完成 </ui-button>
-      </Header>
-    </template>
-
-    <div class="mySwiper">
-      <div class="swiper-wrapper">
-        <div class="swiper-slide" v-for="(i, index) in eleList">
-          <div class="warpper" :class="{ downMode }" :id="`layoutRef${index}`">
-            <component :downMode="downMode" :is="i"></component>
-          </div>
-        </div>
-      </div>
-    </div>
-  </MainPanel>
-</template>
-
-<script setup lang="ts">
-import { reactive, ref, toRefs, onBeforeMount, onMounted, nextTick } from 'vue';
-import { router } from '@/router';
-import Swiper from 'swiper';
-import 'swiper/swiper.min.css';
-import html2canvas from 'html2canvas';
-import { downloadImage, uploadImage } from '@/store/sync';
-import Message from '@/components/base/components/message/message.vue';
-import Header from '@/components/photos/header.vue';
-import MainPanel from '@/components/main-panel/index.vue';
-import one from './explorate-one.vue';
-import two from './explorate-two.vue';
-import three from './explorate-three.vue';
-import four from './explorate-four.vue';
-
-import identification from './identification.vue';
-
-//授权委托书
-import authorOne from './author/author-one.vue';
-import authorTwo from './author/author-two.vue';
-
-// 血样提取
-import extract from './extract.vue';
-
-//遗留物品
-import legacy from './legacy.vue';
-//询问、讯问
-import ask from './ask.vue';
-// const eleList = ref([one, two, three, four]);
-// const eleList = ref([authorOne, authorTwo]);
-const eleList = ref([ask]);
-
-const downMode = ref(false);
-const getLayoutImage = async () => {
-  downMode.value = true;
-  await nextTick();
-
-  eleList.value.forEach(async (element, index) => {
-    // console.error(layoutRef.value);
-    let ele = document.getElementById(`layoutRef${index}`);
-    const canvas = await html2canvas(ele);
-    Message.success({ msg: '已保存至相册', time: 2000 });
-
-    const blob = await new Promise<Blob>((resolve) => canvas.toBlob(resolve, 'image/jpeg', 0.95));
-    await downloadImage(blob, '12312.jpg');
-  });
-  downMode.value = false;
-
-  // return await uploadImage(blob);
-};
-onMounted(() => {
-  var swiper = new Swiper('.mySwiper', {
-    on: {
-      init: function (swiper) {
-        // for (let i = 0; i < imageList.value.length; i++) {
-        //     vmZoom.value[i] = document.getElementById(`vmRef_${i}`)
-        //     zoomElement(vmZoom.value[i])
-        // }
-      },
-      transitionStart: function (swiper) {
-        // alert(swiper.previousIndex)
-        // console.log(vmZoom.value[swiper.previousIndex].style.transform)
-        // let scale = getTransform(vmZoom.value[swiper.previousIndex])
-      },
-
-      touchStart: function (swiper, event) {
-        // console.log(swiper.previousIndex)
-      },
-    },
-  });
-});
-</script>
-<style lang="scss" scoped>
-.mySwiper {
-  height: 100%;
-  width: 100%;
-  color: #000;
-  padding-top: 50px;
-  font-size: 20px;
-
-  font-family: sr, st;
-  .swiper-wrapper {
-    height: 100%;
-    width: 100%;
-    .swiper-slide {
-      height: 100%;
-      width: 100%;
-      overflow: auto;
-      > .warpper {
-        padding: 20px 50px 30px;
-        &.downMode {
-          padding: 125px 100px 75px;
-          width: 1050px;
-          height: 1485px;
-          box-sizing: border-box;
-        }
-        // height: 100%;
-        // width: 100%;
-        // line-height: 100px;
-        // text-align: center;
-      }
-    }
-  }
-}
-</style>

+ 46 - 47
src/views/tables/extract.vue

@@ -1,77 +1,83 @@
 <!--  -->
 <template>
-  <div class="explorate" :class="{ downMode }" ref="layoutRef">
+  <div class="explorate" ref="layoutRef">
     <h2 class="title">当事人血样(尿样)提取登记表</h2>
 
     <div class="container">
       <table>
         <tr>
-          <td width="15%" colspan="2">姓名</td>
-          <td width="20%"><input type="text" /></td>
+          <td width="12%" colspan="2">姓名</td>
+          <td width="20%"><div contenteditable></div></td>
           <td width="10%">性别</td>
-          <td width="5%"><input type="text" /></td>
+          <td width="8%"><div contenteditable></div></td>
           <td width="20%">身份证号码</td>
-          <td width="30%" colspan="2"><input type="text" /></td>
+          <td width="30%" colspan="2"><div contenteditable></div></td>
         </tr>
         <tr>
           <td width="15%" colspan="2">事故时间</td>
-          <td width="35%" colspan="3"><input type="text" /></td>
+          <td width="35%" colspan="3"><div contenteditable></div></td>
           <td width="20%">地点</td>
-          <td width="30%" colspan="2"><input type="text" /></td>
+          <td width="30%" colspan="2"><div contenteditable></div></td>
         </tr>
         <tr>
           <td colspan="2">血样(尿样)提取时间</td>
-          <td colspan="6"><input type="text" /></td>
+          <td colspan="6"><div contenteditable></div></td>
         </tr>
         <tr>
           <td colspan="2">血样(尿样)提取地点</td>
-          <td colspan="6"><input type="text" /></td>
+          <td colspan="6"><div contenteditable></div></td>
         </tr>
         <tr>
           <td width="10%" rowspan="4" colspan="1">血样(尿样)提取人员填写</td>
           <td width="5%" rowspan="3">提取登记</td>
           <td colspan="3">A样本盛装容器编号</td>
-          <td><input type="text" /></td>
+          <td><div contenteditable></div></td>
           <td width="10%">提取量</td>
           <td>
-            <div><input type="text" />ml</div>
+            <div>
+              <div contenteditable></div>
+              ml
+            </div>
           </td>
         </tr>
         <tr>
           <td colspan="3">B样本盛装容器编号</td>
-          <td><input type="text" /></td>
+          <td><div contenteditable></div></td>
           <td>提取量</td>
           <td>
-            <div><input type="text" />ml</div>
+            <div>
+              <div contenteditable></div>
+              ml
+            </div>
           </td>
         </tr>
         <tr>
           <td colspan="1">消毒名称</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
           <td>密封方式</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
         </tr>
         <tr>
           <td width="10%" colspan="2">提取人员单位</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
           <td>提取人员(签名)</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
         </tr>
         <tr>
           <td>通知家属情况</td>
-          <td colspan="7"><input type="text" /></td>
+          <td colspan="7"><div contenteditable></div></td>
         </tr>
         <tr>
           <td>被提取人(签名)</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
           <td>见证人(签名)</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
           <td>交通警察(签名)</td>
-          <td colspan="2"><input type="text" /></td>
+          <td colspan="2"><div contenteditable></div></td>
         </tr>
         <tr>
           <td>办案单位</td>
-          <td colspan="7"><input type="text" /></td>
+          <td colspan="7"><div contenteditable></div></td>
         </tr>
       </table>
     </div>
@@ -80,28 +86,15 @@
 
 <script setup lang="ts">
 import { reactive, ref, toRefs, onBeforeMount, onMounted, nextTick } from 'vue';
-import html2canvas from 'html2canvas';
-import { downloadImage, uploadImage } from '@/store/sync';
-import Message from '@/components/base/components/message/message.vue';
-const layoutRef = ref<HTMLDivElement>();
-const downMode = ref(false);
-const getLayoutImage = async () => {
-  downMode.value = true;
-  await nextTick();
-  console.error(layoutRef.value);
-  const canvas = await html2canvas(layoutRef.value);
-  Message.success({ msg: '已保存至相册', time: 2000 });
-  downMode.value = false;
-  const blob = await new Promise<Blob>((resolve) => canvas.toBlob(resolve, 'image/jpeg', 0.95));
-  await downloadImage(blob, '12312.jpg');
-  // return await uploadImage(blob);
-};
 </script>
 <style lang="scss" scoped>
-td[contenteditable] {
-  // display: flex;
-  // align-items: center;
-  // justify-content: center;
+div[contenteditable] {
+  outline: none;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  // height: 100%;
+  width: 100%;
 }
 .explorate {
   color: #000;
@@ -145,17 +138,23 @@ td[contenteditable] {
           > div {
             display: flex;
             align-items: center;
-            justify-content: flex-start;
-            input {
+            justify-content: center;
+            // word-break: break-all;
+            white-space: nowrap;
+            line-height: 110px;
             width: 100%;
-            height: 100%;
-            text-align: center;
-          }
+              height: 100%;
+            input {
+              width: 100%;
+              height: 100%;
+              text-align: center;
+            }
           }
           input {
             width: 100%;
             height: 100%;
             text-align: center;
+            line-height: 110px;
           }
         }
       }

+ 5 - 2
src/views/tables/identification.vue

@@ -144,6 +144,7 @@ const topList = ref({
   ],
 });
 onMounted(() => {
+
 });
 </script>
 <style lang="scss" scoped>
@@ -172,7 +173,7 @@ div[contenteditable] {
   .table-layout {
     border: 1px solid #000;
     .input-box {
-      padding: 0 10px 10px;
+      padding: 0 10px;
     }
     .msg-box {
       border-bottom: 1px solid #000;
@@ -199,7 +200,6 @@ div[contenteditable] {
       border-collapse: collapse;
       border-bottom: 1px solid #000;
       tr {
-        // border-bottom: 1px solid #000;
         &:last-of-type {
           td {
             border-bottom: none;
@@ -219,6 +219,7 @@ div[contenteditable] {
         }
         > div {
           outline: none;
+          word-break: break-all;
         }
       }
     }
@@ -236,6 +237,7 @@ div[contenteditable] {
         align-items: center;
         justify-content: center;
         text-align: center;
+        box-sizing: border-box;
       }
       .info {
         padding: 0 10px 10px;
@@ -264,6 +266,7 @@ div[contenteditable] {
         align-items: center;
         justify-content: center;
         text-align: center;
+        box-sizing: border-box;
       }
       .info {
         padding: 0 10px 10px;

+ 253 - 0
src/views/tables/index.vue

@@ -0,0 +1,253 @@
+<!--  -->
+<template>
+  <MainPanel>
+    <template v-slot:header>
+      <Header :title="headerTitle" :on-back="onBack" type="return">
+        <ui-button v-if="tableType != 'law'" type="primary" width="96px" @click="saveHandler"> {{ isWrite ? '确定' : '完成' }} </ui-button>
+      </Header>
+    </template>
+
+    <div v-show="!isWrite" class="mySwiper" v-if="loaded">
+      <div class="swiper-wrapper">
+        <div class="swiper-slide" v-for="(i, index) in eleList">
+          <div class="warpper" :class="{ downMode, 'no-padding': tableType == 'law' }" :id="`layoutRef${index}`">
+            <component :page="askPage" :pageIndex="index" :text="text" @onTextChange="onTextChange" @onTextConfirm="onTextConfirm" @goWrite="goWrite" :downMode="downMode" :is="i"></component>
+          </div>
+        </div>
+      </div>
+    </div>
+    <Write ref="com" v-if="isWrite" :text="text" :textIndex="textIndex" @onTextConfirm="onTextConfirm"> </Write>
+  </MainPanel>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, watch, onMounted, nextTick, onActivated, onDeactivated } from 'vue';
+import { router } from '@/router';
+import Swiper from 'swiper';
+import html2canvas from 'html2canvas';
+import { downloadImage, uploadImage } from '@/store/sync';
+import { genUseLoading } from '@/hook';
+import Write from './write/index.vue';
+import Message from '@/components/base/components/message/message.vue';
+import Header from '@/components/photos/header.vue';
+import MainPanel from '@/components/main-panel/index.vue';
+import one from './explorate-one.vue';
+import two from './explorate-two.vue';
+import three from './explorate-three.vue';
+import four from './explorate-four.vue';
+
+import identification from './identification.vue';
+
+//授权委托书
+import authorOne from './author/author-one.vue';
+import authorTwo from './author/author-two.vue';
+
+// 血样提取
+import extract from './extract.vue';
+
+//遗留物品
+import legacy from './legacy.vue';
+//询问、讯问
+import ask from './ask.vue';
+import doc from './write/doc.vue';
+
+//法规
+import law from './law.vue';
+// const eleList = ref([one, two, three, four]);
+// const eleList = ref([authorOne, authorTwo]);
+const com = ref(null); // 通过 模板ref 绑定子组件
+const eleList = ref([]);
+const headerTitle = ref('');
+const tableType = ref<string | string[]>();
+const downMode = ref(false);
+const askPage = ref(1);
+const askText = ref('');
+
+const isWrite = ref(false);
+const text = ref('');
+const textIndex = ref(0);
+
+const setAskPage = () => {
+  loaded.value = false;
+  eleList.value = [ask];
+  if (askPage.value > 1) {
+    for (let i = 0; i < askPage.value - 1; i++) {
+      eleList.value.push(doc);
+    }
+  }
+  initSwiper();
+};
+const goWrite = (data) => {
+  console.error(data);
+  isWrite.value = true;
+  textIndex.value = data.textIndex;
+};
+const onTextConfirm = (data) => {
+  console.error(data);
+  askPage.value = data.page;
+  text.value = data.text;
+  isWrite.value = false;
+  setAskPage();
+};
+const onTextChange = (data) => {
+  text.value = data.text;
+};
+
+const saveHandler = genUseLoading(async () => {
+  if (isWrite.value) {
+    com.value.onConfirm();
+    return;
+  }
+  await getLayoutImage();
+  // router.replace('/scene');
+});
+
+const getLayoutImage = async () => {
+  downMode.value = true;
+  await nextTick();
+  let num = 0;
+  return new Promise((res, rej) => {
+    eleList.value.forEach(async (element, index) => {
+      // console.error(layoutRef.value);
+      let ele = document.getElementById(`layoutRef${index}`);
+
+      const canvas = await html2canvas(ele);
+      Message.success({ msg: '已保存至相册', time: 2000 });
+      const blob = await new Promise<Blob>((resolve) => {
+        return canvas.toBlob(resolve, 'image/jpeg', 0.95);
+      });
+      await downloadImage(blob, `tables_${index}.jpg`);
+      num++;
+
+      if (num == eleList.value.length) {
+        downMode.value = false;
+        res(true);
+      }
+    });
+  });
+
+  // return await uploadImage(blob);
+};
+const initTables = () => {
+  tableType.value = router.currentRoute.value.params.name;
+  switch (tableType.value) {
+    case 'explorate':
+      eleList.value = [one, two, three, four];
+      headerTitle.value = '道路交通事故现场勘查笔录';
+      break;
+    case 'ask':
+      eleList.value = [ask];
+      if (router.currentRoute.value.query.type == '1') {
+        headerTitle.value = '询问笔录';
+      } else {
+        headerTitle.value = '讯问笔录';
+      }
+
+      break;
+    case 'author':
+      eleList.value = [authorOne, authorTwo];
+      headerTitle.value = '授权委托书';
+      break;
+    case 'legacy':
+      eleList.value = [legacy];
+      headerTitle.value = '道路交通事故现场遗留物品清单';
+      break;
+    case 'extract':
+      eleList.value = [extract];
+      headerTitle.value = '当事人血样(尿样)提取登记表';
+      break;
+    case 'identification':
+      eleList.value = [identification];
+      headerTitle.value = '道路交通事故认定书(简易程序)';
+      break;
+
+    case 'law':
+      eleList.value = [law];
+      headerTitle.value = '法律法规';
+      break;
+  }
+};
+const mySwiper = ref(null);
+const loaded = ref(false);
+const initSwiper = () => {
+  if (mySwiper.value) {
+    mySwiper.value.destroy(true);
+  }
+  loaded.value = true;
+  mySwiper.value = null;
+  nextTick(() => {
+    if (loaded.value) {
+      mySwiper.value = new Swiper('.mySwiper', {
+        on: {
+          init: function (swiper) {
+            // initPage();
+            // console.error(swiper);
+          },
+          transitionStart: function (swiper) {},
+          touchStart: function (swiper, event) {},
+        },
+      });
+    }
+  });
+};
+const onBack = () => {
+  if (isWrite.value) {
+    isWrite.value = false;
+    return;
+  }
+  router.back();
+};
+onActivated(() => {
+  initTables();
+  initSwiper();
+});
+const scale = ref('1');
+const initPage = () => {
+  let parent = document.getElementById('layoutRef0');
+  let children = parent.children[0];
+  // console.error(768 / children.offsetHeight);
+  scale.value = ((768 - 100) / children.clientHeight).toFixed(2);
+};
+onMounted(() => {});
+onDeactivated(() => {
+  isWrite.value = false;
+  text.value = '';
+  loaded.value = false;
+});
+</script>
+<style lang="scss" scoped>
+.mySwiper {
+  height: 100%;
+  width: 100%;
+  color: #000;
+  padding-top: 50px;
+  font-size: 20px;
+
+  font-family: sr, st;
+  .swiper-wrapper {
+    height: 100%;
+    width: 100%;
+    .swiper-slide {
+      height: 100%;
+      width: 100%;
+      overflow: auto;
+      > .warpper {
+        padding: 20px 50px 30px;
+        &.no-padding {
+          padding: 0;
+        }
+        &.downMode {
+          padding: 125px 100px 75px;
+          width: 1050px;
+          height: 1485px;
+          box-sizing: border-box;
+        }
+        // height: 100%;
+        // width: 100%;
+        // line-height: 100px;
+        // text-align: center;
+      }
+    }
+  }
+}
+</style>

+ 22 - 0
src/views/tables/law.vue

@@ -0,0 +1,22 @@
+<!--  -->
+<template>
+  <div v-for="(i, index) in 27" class="item">
+    <img :src="getImageUrl(index)" alt="" />
+    <!-- <img :src="`@/assets/images/law/${index}.jpg`" alt="" /> -->
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, onMounted } from 'vue';
+
+const getImageUrl = (index) => {
+  return new URL(`/src/assets/images/law/${index + 1}.jpg`, import.meta.url).href;
+};
+</script>
+<style lang="scss" scoped>
+.item {
+  img {
+    width: 100%;
+  }
+}
+</style>

+ 16 - 4
src/views/tables/legacy.vue

@@ -1,16 +1,27 @@
 <!--  -->
 <template>
   <div class="legacy">
-    <h2 class="title">当事人血样(尿样)提取登记表</h2>
+    <h2 class="title">道路交通事故现场遗留物品清单</h2>
     <table>
       <tr>
         <td colspan="7" style="text-align: left">
-          <div><span>事故时间:</span><input type="text" /></div>
+          <div style="display: flex; align-items: center; justify-content: flex-start; height: 100%">
+            <span style="height: 100%; line-height: 60px">事故时间:</span>
+            <div style="height: 100%; flex: 1" class="input-box">
+              <input type="text" style="width: 100%; height: 100%" />
+            </div>
+          </div>
         </td>
       </tr>
+
       <tr>
         <td colspan="7" style="text-align: left">
-          <div><span>事故地点:</span><input type="text" /></div>
+          <div style="display: flex; align-items: center; justify-content: flex-start; height: 100%">
+            <span style="height: 100%; line-height: 60px">事故地点:</span>
+            <div style="height: 100%; flex: 1" class="input-box">
+              <input type="text" style="width: 100%; height: 100%" />
+            </div>
+          </div>
         </td>
       </tr>
       <tr>
@@ -195,7 +206,8 @@ div[contenteditable] {
         > div[contenteditable] {
           width: 100%;
           height: 100%;
-          line-height: 60px;
+          line-height: 80px;
+          // line-height: 60px;
         }
       }
     }

+ 139 - 0
src/views/tables/write/doc.vue

@@ -0,0 +1,139 @@
+<!--  -->
+<template>
+  <div class="num-box">
+    <span>第</span>
+    <div class="input-box">{{ pageIndex + 1 }}</div>
+    <span style="margin-right: 30px">页</span> <span>共</span>
+    <div class="input-box">{{ page }}</div>
+    <span>页</span>
+  </div>
+
+  <div class="write-box" id="view-container">
+    <div class="content" :style="`height:${inputHeight}px;`" @click="goWrite">
+      <!-- <div contenteditable v-html="text" :style="`height:${lineCount * 40}px;`" @keydown="hanlderWrite($event)" id="view-info"></div> -->
+      <div :style="`transform:translateY(${pageIndex == 1 ? -400 : -400 - (pageIndex - 1) * inputHeight}px);`" id="view-info" v-html="text"></div>
+      <div class="msg-box">{{ text }}</div>
+      <div class="item" :style="`top:${index * 40}px;`" v-for="(i, index) in lineCount"></div>
+    </div>
+  </div>
+
+  <div class="bottom-name">
+    <span v-if="type == '1'">被询问人:</span>
+    <span v-else>被讯问人:</span>
+    <div style="flex: 1" contenteditable></div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, onMounted, nextTick, defineProps, defineEmits } from 'vue';
+import { router } from '@/router';
+
+const props = defineProps({
+  text: {
+    type: String,
+    default: '',
+  },
+  page: {
+    type: Number,
+    default: 1,
+  },
+  pageIndex: {
+    type: Number,
+    default: 1,
+  },
+});
+const emits = defineEmits(['goWrite']);
+const type = ref(router.currentRoute.value.query.type);
+const lineCount = ref(1);
+const inputHeight = ref(0);
+const getLineCount = () => {
+  let containerH = document.getElementById('view-container').clientHeight;
+  let count = Math.floor(containerH / 40);
+  lineCount.value = count;
+  inputHeight.value = count * 40;
+  // textAreaHeight.value = lineCount.value * 40;
+};
+
+const goWrite = () => {
+  let text = window.getSelection();
+  let textIndex = text.anchorOffset;
+  emits('goWrite', { textIndex });
+};
+
+onMounted(async () => {
+  await nextTick();
+  getLineCount();
+});
+</script>
+<style lang="scss" scoped>
+div[contenteditable] {
+  outline: none;
+}
+.bottom-name {
+  width: 100%;
+  height: 40px;
+  line-height: 40px;
+  margin: 28px auto 0;
+  display: flex;
+  font-size: 24px;
+  font-family: SimSun-Regular, SimSun;
+}
+.num-box {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  margin-bottom: 10px;
+  font-family: SimSun-Regular, SimSun;
+  .input-box {
+    width: 64px;
+    height: 30px;
+    line-height: 30px;
+    border-bottom: 1px solid #000;
+    text-align: center;
+    margin: 0 5px;
+  }
+}
+.write-box {
+  width: 100%;
+  // height: calc(100vh - 100px);
+  height: 1080px;
+  font-family: SimSun-Regular, SimSun;
+  overflow: hidden;
+  .content {
+    overflow: hidden;
+    padding-bottom: 5px;
+    position: relative;
+    font-size: 24px;
+    .item {
+      width: 100%;
+      height: 40px;
+      border-bottom: 1px solid #000;
+      box-sizing: border-box;
+      position: absolute;
+      left: 0;
+    }
+    #view-info {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      // height: 100%;
+      line-height: 40px;
+      outline: none;
+      resize: none;
+      z-index: 2;
+      overflow: hidden;
+      white-space: pre-wrap;
+    }
+    .msg-box {
+      min-height: 40px;
+      white-space: pre-wrap;
+      opacity: 0;
+      position: absolute;
+      width: 100%;
+      line-height: 40px;
+      z-index: 1;
+    }
+  }
+}
+</style>

+ 164 - 0
src/views/tables/write/index.vue

@@ -0,0 +1,164 @@
+<!--  -->
+<template>
+  <div class="warpper">
+    <div class="write-box" id="container">
+      <!-- <div @click="onConfirm">确定</div> -->
+      <!--  -->
+      <div class="content" id="content" :style="`height:${inputHeight}px;`">
+        <!-- <div contenteditable v-html="text" :style="`height:${lineCount * 40}px;`" @keydown="hanlderWrite($event)" id="write-info"></div> -->
+        <textarea maxLength="1000" :style="`height:${lineCount * 40}px;`" @paste="onPaste" @keydown="hanlderWrite($event)" id="write-info" v-model="inputText"> </textarea>
+        <div id="msg" class="msg-box">{{ inputText }}</div>
+        <div class="item" :style="`top:${index * 40}px;`" v-for="(i, index) in lineCount"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, toRefs, onBeforeMount, computed, onMounted, nextTick, defineEmits, defineProps } from 'vue';
+
+const props = defineProps({
+  text: {
+    type: String,
+    default: '',
+  },
+  textIndex: {
+    type: Number,
+    default: 0,
+  },
+});
+const emits = defineEmits(['onTextConfirm', 'onTextChange']);
+const inputText = ref('');
+const lineCount = ref(1);
+const inputHeight = ref(0);
+const getLineCount = () => {
+  let containerH = document.getElementById('container').clientHeight;
+  let count = Math.floor(containerH / 40);
+  lineCount.value = count;
+  inputHeight.value = count * 40;
+  // textAreaHeight.value = lineCount.value * 40;
+};
+const onConfirm = () => {
+  let page = 1;
+
+  if (msgHeight.value < 400) {
+    page = 1;
+  } else {
+    page = page + Math.ceil((msgHeight.value - 400) / 1080);
+  }
+  emits('onTextConfirm', { text: inputText.value, msgHeight: msgHeight.value, page });
+};
+defineExpose({ onConfirm });
+const msgHeight = ref(40);
+const hanlderWrite = (e) => {
+  let msgH = document.getElementById('msg').clientHeight;
+  msgHeight.value = msgH;
+  let msgCount = Math.floor(msgH / 40);
+  let containerH = document.getElementById('container').clientHeight;
+  let containerCount = Math.floor(containerH / 40);
+  // text.value = e.target.innerHTML;
+  if (msgCount > containerCount) {
+    if (e && e.keyCode == 13) {
+      msgCount++;
+    }
+
+    lineCount.value = msgCount;
+  } else {
+    lineCount.value = containerCount;
+  }
+
+  emits('onTextChange', { text: inputText.value });
+
+  // textAreaHeight.value = msgH;
+};
+const onPaste = (e) => {
+  setTimeout(() => {
+    // let msgH = document.getElementById('msg').clientHeight;
+    hanlderWrite(e);
+    let content = document.getElementById('content');
+    content.scrollTo(0, 9999999);
+  }, 100);
+};
+const setFocusAt = (focusIndex) => {
+  let txtarea = document.getElementById('write-info');
+  setCaretPosition(txtarea, focusIndex);
+};
+
+//设置光标位置
+const setCaretPosition = (ctrl, pos) => {
+  if (ctrl.setSelectionRange) {
+    ctrl.focus();
+    ctrl.setSelectionRange(pos, pos);
+  } else if (ctrl.createTextRange) {
+    var range = ctrl.createTextRange();
+    range.collapse(true);
+    range.moveEnd('character', pos);
+    range.moveStart('character', pos);
+    range.select();
+  }
+};
+
+onMounted(async () => {
+  await nextTick();
+  getLineCount();
+  inputText.value = props.text;
+  setTimeout(() => {
+    hanlderWrite();
+
+    if (props.text && props.textIndex) {
+      console.error(props.textIndex);
+      setFocusAt(props.textIndex);
+    }
+  }, 0);
+});
+</script>
+<style lang="scss" scoped>
+.warpper {
+  padding: 70px 50px 30px;
+}
+.write-box {
+  width: 100%;
+  height: calc(100vh - 100px);
+  font-size: 24px;
+  color: #000;
+  overflow: hidden;
+  font-family: SimSun-Regular, SimSun;
+  .content {
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-bottom: 5px;
+    position: relative;
+    .item {
+      width: 100%;
+      height: 40px;
+      border-bottom: 1px solid #000;
+      box-sizing: border-box;
+      position: absolute;
+      left: 0;
+    }
+    #write-info {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      line-height: 40px;
+      outline: none;
+      resize: none;
+      z-index: 2;
+      overflow: hidden;
+      font-size: 24px;
+      font-family: SimSun-Regular, SimSun;
+    }
+    .msg-box {
+      min-height: 40px;
+      white-space: pre-wrap;
+      opacity: 0;
+      position: absolute;
+      width: 100%;
+      line-height: 40px;
+      z-index: 1;
+    }
+  }
+}
+</style>