Ver código fonte

添加绘制相关类

bill 2 anos atrás
pai
commit
6c08764e51

+ 3 - 4
src/components/main-panel/index.vue

@@ -48,12 +48,11 @@ const layoutClass = computed(() => ({
 
 .header {
   color: rgba(var(--colors-primary-fill), 0.8);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 0 20px;
+  padding: 8px 20px;
   transition: top 0.3s ease;
   top: var(--header-top);
+  display: flex;
+  align-items: center;
 }
 
 .header .menu {

+ 79 - 0
src/components/photos/index.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="photos-layout">
+    <div class="photos">
+      <div
+          v-for="photo in data"
+          :key="photo.id"
+          class="photo"
+          @click="$emit('update:active', photo)"
+      >
+        <img :src="getStaticFile(photo.url)" v-if="!$slots.default" />
+        <slot :data="photo"  v-else/>
+        <ui-input
+          v-if="selectMode"
+          type="checkbox"
+          :modelValue="selects.includes(photo)"
+          @update:modelValue="selected => changeSelects(photo, selected)"
+          @click.stop
+          class="select"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import {getStaticFile} from "@/dbo/main";
+import UiInput from "@/components/base/components/input/index.vue";
+
+type Item = { url: string, id: string }
+
+const props = defineProps<{
+  data: Item[],
+  active?: Item,
+  selects?: Item[],
+  selectMode?: boolean
+}>()
+
+const emit = defineEmits<{
+  (e: "update:active", a: Item): void,
+  (e: "update:selects", a: Item[]): void,
+}>()
+
+const changeSelects = (item: Item, selected: boolean) => {
+  const olSelected = props.selects.includes(item)
+  if (selected !== olSelected) {
+    if (selected) {
+      emit('update:selects', [...props.selects, item])
+    } else {
+      emit('update:selects', props.selects.filter(oItem => oItem !== item))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.photos-layout {
+  position: absolute;
+  top: calc(var(--header-top) + var(--editor-head-height));
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow-y: auto;
+  background: #2E2E2E;
+}
+.photos {
+  display: grid;
+  grid-gap: 24px;
+  padding: 24px;
+  grid-template-columns: repeat(4, 1fr);
+}
+.photo {
+  position: relative;
+}
+.select {
+
+}
+.photo img {
+  width: 100%;
+}
+</style>

+ 1 - 0
src/graphic/Controls/UIControl.js

@@ -211,6 +211,7 @@ export default class UIControl {
 
   // value 为true则开 false则关
   menu_backgroundImg(value) {
+    console.log(value)
     //
     this.graphicStateUI.showBackImage = value;
     this.layer.renderer.autoRedraw();

+ 37 - 30
src/graphic/Renderer/Draw.js

@@ -13,7 +13,7 @@ const help = {
   getVectorStyle(vector, geoType = vector.geoType) {
     const geoId = vector?.vectorId;
     if (!geoId) {
-      return Style[geoType];
+      return [Style[geoType], undefined];
     }
 
     const itemsEntry = [
@@ -21,22 +21,27 @@ const help = {
       [stateService.getDraggingItem(), "Dragging"],
       [stateService.getFocusItem(), "Focus"],
     ];
-
-    return itemsEntry.reduce((prev, [item, attr]) => {
-      if (
-        item &&
-        // item.type === VectorType[geoType] &&
-        geoId === item.vectorId
-      ) {
-        if (Style[attr] && Style[attr][geoType]) {
-          return Style[attr][geoType];
+    let currentAttr
+
+    return [
+      itemsEntry.reduce((prev, [item, attr]) => {
+        if (
+          item &&
+          // item.type === VectorType[geoType] &&
+          geoId === item.vectorId
+        ) {
+          if (Style[attr] && Style[attr][geoType]) {
+            currentAttr = attr
+            return Style[attr][geoType];
+          }
         }
-      }
-      return prev;
-    }, Style[geoType]);
+        return prev;
+      }, Style[geoType]),
+      currentAttr
+    ];
   },
   setVectorStyle(ctx, vector, geoType = vector.geoType) {
-    const styles = help.getVectorStyle(vector, geoType);
+    const [styles, attr] = help.getVectorStyle(vector, geoType);
     for (const style in styles) {
       if (typeof styles[style] === "function") {
         styles[style](ctx, vector);
@@ -44,7 +49,7 @@ const help = {
         ctx[style] = styles[style];
       }
     }
-    return styles;
+    return [styles, attr];
   },
   transformCoves(lines) {
     return lines.map((line) =>
@@ -394,16 +399,9 @@ export default class Draw {
     const end = coordinate.getScreenXY(endReal);
     const ctx = this.context;
 
-    const ange = 30;
-    const L = 20;
-    let a = Math.atan2(end.y - start.y, end.x - start.x);
-    let xC = end.x - L * Math.cos(a + (ange * Math.PI) / 180); // θ=30
-    let yC = end.y - L * Math.sin(a + (ange * Math.PI) / 180);
-    let xD = end.x - L * Math.cos(a - (ange * Math.PI) / 180);
-    let yD = end.y - L * Math.sin(a - (ange * Math.PI) / 180);
     ctx.save();
 
-    const style = help.setVectorStyle(this.context, vector, "Arrow");
+    const [style] = help.setVectorStyle(this.context, vector, "Arrow");
     if (vector.arrowColor) {
       ctx.strokeStyle = vector.arrowColor;
     }
@@ -438,7 +436,7 @@ export default class Draw {
     });
     const pt = coordinate.getScreenXY(vector.position);
     const target = coordinate.getScreenXY(vector.popPosition);
-    const style = help.setVectorStyle(ctx, vector);
+    const [style] = help.setVectorStyle(ctx, vector);
     const radius = help.getReal(vector.radius || style.radius);
     const offset = radius / 2;
     const targetPts =
@@ -515,7 +513,7 @@ export default class Draw {
   drawPoint(vector) {
     const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
     const ctx = this.context;
-    const style = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
+    const [style] = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
     if (vector.color) {
       ctx.strokeStyle = vector.color;
     }
@@ -560,11 +558,17 @@ export default class Draw {
     const oldFont = this.context.font;
     this.context.font = `${vector.fontSize}px Microsoft YaHei`;
     this.drawTextByInfo(vector.center, vector.value, 0, false);
+
+    const ctx = this.context
+    const pt = coordinate.getScreenXY(vector.center);
+    const text = ctx.measureText(vector.value);
+    pt.x -= text.width / 2;
+    pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
+
     this.context.font = oldFont;
   }
 
   drawLine(vector) {
-    console.log(vector)
     if ([UIEvents.Arrow, UIEvents.MeasureLine].includes(vector.category)) {
       return this.drawArrow(vector);
     }
@@ -574,7 +578,7 @@ export default class Draw {
     const end = coordinate.getScreenXY(endReal);
 
     this.context.save();
-    const style = help.setVectorStyle(
+    const [style, attr] = help.setVectorStyle(
       this.context,
       vector,
       vector.category || vector.geoType
@@ -587,8 +591,11 @@ export default class Draw {
     this.context.lineTo(end.x, end.y);
     this.context.stroke();
     this.context.restore();
-    this.drawPoint(startReal)
-    this.drawPoint(endReal)
+
+    // if (attr) {
+    //   this.drawPoint(startReal)
+    //   this.drawPoint(endReal)
+    // }
   }
 
   drawElementLine(element) {
@@ -598,7 +605,7 @@ export default class Draw {
     end = coordinate.getScreenXY(end);
 
     this.context.save();
-    const style = help.setVectorStyle(
+    const [style] = help.setVectorStyle(
       this.context,
       element,
       element.category || element.geoType

+ 1 - 0
src/views/accidents/index.vue

@@ -1,6 +1,7 @@
 <template>
   <MainPanel>
     <template v-slot:header>
+      <ui-icon type="close" ctrl style="margin-right: 10px" @click="router.back" />
       标注照片({{accidentPhotos.length}})
     </template>
 

+ 0 - 2
src/views/graphic/header.vue

@@ -1,11 +1,9 @@
 <template>
   <div class="graphic-header" v-if="data">
-
     <div class="title">
       <ui-icon type="close" @click="router.back" />
       <p>{{ isRoad ? '现场绘图' : '事故照片' }}</p>
     </div>
-
     <div class="actions">
       <div
           v-for="menu in menus"

+ 13 - 36
src/views/photos/index.vue

@@ -1,22 +1,21 @@
 <template>
   <MainPanel>
     <template v-slot:header>
+      <ui-icon type="close" ctrl style="margin-right: 10px" @click="router.back" />
       照片管理
     </template>
 
-    <div class="photos-layout">
-      <div class="photos">
-        <div v-for="photo in sortPhotos" :key="photo.id" class="photo" @click="active = photo">
-
-          <img :src="getStaticFile(photo.url)"  />
-        </div>
-      </div>
-
-      <ButtonPane class="back fun-ctrl" @click="router.push(writeRouteName.scene)">
-        <ui-icon type="close" class="icon" />
-      </ButtonPane>
-    </div>
+    <Photos
+      v-model:active="active"
+      v-model:selects="selects"
+      :data="sortPhotos"
+      :select-mode="true"
+    />
+    <ButtonPane class="back fun-ctrl" @click="router.push(writeRouteName.scene)">
+      <ui-icon type="close" class="icon" />
+    </ButtonPane>
   </MainPanel>
+
   <FillSlide :data="sortPhotos" v-model:active="active" @quit="active = null" v-if="active">
     <template v-slot:header>
       <div class="btns">
@@ -44,19 +43,17 @@
 import MainPanel from '@/components/main-panel/index.vue'
 import FillSlide from '@/components/fill-slide/index.vue'
 import {PhotoRaw, photos} from '@/store/photos'
-import {getStaticFile} from "@/dbo/main";
 import UiIcon from "@/components/base/components/icon/index.vue";
 import {router, writeRouteName} from '@/router'
 import ButtonPane from "@/components/button-pane/index.vue";
 import {computed, ref} from "vue";
 import {Mode} from '@/views/graphic/menus'
 import UiButton from "@/components/base/components/button/index.vue";
-import { accidentPhotos } from "@/store/accidentPhotos"
-import { roadPhotos } from "@/store/roadPhotos"
-import {getId} from "@/utils";
+import Photos from '@/components/photos'
 
 const sortPhotos = computed(() => photos.value.reverse())
 const active = ref<PhotoRaw>()
+const selects = ref<PhotoRaw[]>([])
 const delPhoto = () => {
   const index = photos.value.indexOf(active.value)
   if (~index) {
@@ -71,26 +68,6 @@ const gotoDraw = (mode: Mode) => {
 </script>
 
 <style scoped lang="scss">
-.photos-layout {
-  position: absolute;
-  top: calc(var(--header-top) + var(--editor-head-height));
-  left: 0;
-  right: 0;
-  bottom: 0;
-  overflow-y: auto;
-}
-.photos {
-  display: grid;
-  grid-gap: 10px;
-  padding: 10px;
-  //grid-template-rows: auto;
-  grid-template-columns: repeat(5, 1fr);
-}
-.photo img {
-  width: 100%;
-}
-
-
 .back {
   right: var(--boundMargin);
   bottom: var(--boundMargin);

+ 1 - 0
src/views/roads/index.vue

@@ -1,6 +1,7 @@
 <template>
   <MainPanel>
     <template v-slot:header>
+      <ui-icon type="close" ctrl style="margin-right: 10px" @click="router.back" />
       现场图管理({{sortPhotos.length}})
     </template>
 

+ 2 - 4
src/views/sys/head/style.scss

@@ -1,9 +1,7 @@
 .header {
   color: rgba(var(--colors-primary-fill), 0.8);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 0 20px;
+  padding: 14px 20px;
+
   transition: top 0.3s ease;
   top: var(--header-top);
   backdrop-filter: blur(4px);