bill 1 rok temu
rodzic
commit
24275b15be

+ 10 - 0
src/assets/style/public.scss

@@ -23,6 +23,16 @@ body {
 
   font-family: "Microsoft YaHei";
   color      : var(--colorColor);
+  overflow   : auto;
+
+}
+
+#app {
+  position  : relative;
+  min-width : 1280px;
+  min-height: 760px;
+  height    : 100%;
+  overflow  : hidden;
 }
 
 .fill.el-button {

+ 291 - 290
src/view/case/draw/board/editCAD/Controls/UIControl.js

@@ -1,321 +1,322 @@
-import { coordinate } from '../Coordinate.js'
-import LayerEvents from '../enum/LayerEvents.js'
-import UIEvents from '../enum/UIEvents.js'
-import VectorType from '../enum/VectorType.js'
-import { stateService } from '../Service/StateService.js'
-import { floorplanService } from '../Service/FloorplanService.js'
-import { historyService } from '../Service/HistoryService.js'
-import { elementService } from '../Service/ElementService'
-import { mathUtil } from '../MathUtil.js'
-import { wallService } from '../Service/WallService.js'
-import { tagService } from '../Service/TagService.js'
-import { tableService } from '../Service/TableService.js'
-import Constant from '../Constant'
-import { addWall } from '../Controls/AddWall'
-import { floorplanData } from '../FloorplanData.js'
-import { signService } from '../Service/SignService.js'
-import mitt from 'mitt'
-import {history} from '../History/History.js'
-import { iconService } from '../Service/IconService.js'
+import { coordinate } from "../Coordinate.js";
+import LayerEvents from "../enum/LayerEvents.js";
+import UIEvents from "../enum/UIEvents.js";
+import VectorType from "../enum/VectorType.js";
+import { stateService } from "../Service/StateService.js";
+import { floorplanService } from "../Service/FloorplanService.js";
+import { historyService } from "../Service/HistoryService.js";
+import { elementService } from "../Service/ElementService";
+import { mathUtil } from "../MathUtil.js";
+import { wallService } from "../Service/WallService.js";
+import { tagService } from "../Service/TagService.js";
+import { tableService } from "../Service/TableService.js";
+import Constant from "../Constant";
+import { addWall } from "../Controls/AddWall";
+import { floorplanData } from "../FloorplanData.js";
+import { signService } from "../Service/SignService.js";
+import mitt from "mitt";
+import { history } from "../History/History.js";
+import { iconService } from "../Service/IconService.js";
 
-export default class UIControl{
-    constructor(layer) {
-        this.layer = layer
-        this.bus = mitt()
-        this.selectUI = null;
+export default class UIControl {
+  constructor(layer) {
+    this.layer = layer;
+    this.bus = mitt();
+    this.selectUI = null;
 
-        // this.bus.emit('')
-    }
+    // this.bus.emit('')
+  }
 
-    //点击左侧栏后,更新事件
-    updateEventNameForSelectUI() {
-        elementService.hideAll()
-        //正在添加tag的时候,需要先删除
-        const eventName = stateService.getEventName()
-        // if (eventName == LayerEvents.AddTag) {
-        //     let item = stateService.getDraggingItem()
-        //     if (item && item.type == VectorType.Tag) {
-        //         floorplanService.deleteTag(item.vectorId)
-        //     }
-        // }
-        // stateService.clearItems()
-        if (this.selectUI == UIEvents.Wall) 
-        {
-            stateService.setEventName(LayerEvents.AddWall)
-        } 
-        else if (this.selectUI == UIEvents.Table ) 
-        {
-            stateService.setEventName(LayerEvents.AddTable)
-        } 
-        else if (this.selectUI == UIEvents.Rectangle ) 
-        {
-            stateService.setEventName(LayerEvents.AddRectangle)
-        } 
-        else if (this.selectUI == UIEvents.Circle ) 
-        {
-            stateService.setEventName(LayerEvents.AddCircle)
-        } 
-        else if (this.selectUI == UIEvents.Arrow ) 
-        {
-            stateService.setEventName(LayerEvents.AddArrow)
-        }
-        else if (this.selectUI == UIEvents.Icon ) 
-        {
-            stateService.setEventName(LayerEvents.AddIcon)
-        }  
-        else if (this.selectUI == UIEvents.Tag) 
-        {
-            stateService.setEventName(LayerEvents.AddTag)
-        } 
-        else if (
-            this.selectUI == UIEvents.Cigaret ||
-            this.selectUI == UIEvents.FirePoint ||
-            this.selectUI == UIEvents.LeftFootPrint ||
-            this.selectUI == UIEvents.RightFootPrint ||
-            this.selectUI == UIEvents.LeftShoePrint ||
-            this.selectUI == UIEvents.RightShoePrint ||
-            this.selectUI == UIEvents.FingerPrint ||
-            this.selectUI == UIEvents.DeadBody ||
-            this.selectUI == UIEvents.BloodStain 
-        ) {
-            stateService.setEventName(LayerEvents.AddSign)
-        }
+  //点击左侧栏后,更新事件
+  updateEventNameForSelectUI() {
+    elementService.hideAll();
+    //正在添加tag的时候,需要先删除
+    const eventName = stateService.getEventName();
+    // if (eventName == LayerEvents.AddTag) {
+    //     let item = stateService.getDraggingItem()
+    //     if (item && item.type == VectorType.Tag) {
+    //         floorplanService.deleteTag(item.vectorId)
+    //     }
+    // }
+    // stateService.clearItems()
+    if (this.selectUI == UIEvents.Wall) {
+      stateService.setEventName(LayerEvents.AddWall);
+    } else if (this.selectUI == UIEvents.Table) {
+      stateService.setEventName(LayerEvents.AddTable);
+    } else if (this.selectUI == UIEvents.Rectangle) {
+      stateService.setEventName(LayerEvents.AddRectangle);
+    } else if (this.selectUI == UIEvents.Circle) {
+      stateService.setEventName(LayerEvents.AddCircle);
+    } else if (this.selectUI == UIEvents.Arrow) {
+      stateService.setEventName(LayerEvents.AddArrow);
+    } else if (this.selectUI == UIEvents.Icon) {
+      stateService.setEventName(LayerEvents.AddIcon);
+    } else if (this.selectUI == UIEvents.Tag) {
+      stateService.setEventName(LayerEvents.AddTag);
+    } else if (
+      this.selectUI == UIEvents.Cigaret ||
+      this.selectUI == UIEvents.FirePoint ||
+      this.selectUI == UIEvents.LeftFootPrint ||
+      this.selectUI == UIEvents.RightFootPrint ||
+      this.selectUI == UIEvents.LeftShoePrint ||
+      this.selectUI == UIEvents.RightShoePrint ||
+      this.selectUI == UIEvents.FingerPrint ||
+      this.selectUI == UIEvents.DeadBody ||
+      this.selectUI == UIEvents.BloodStain
+    ) {
+      stateService.setEventName(LayerEvents.AddSign);
     }
+  }
 
-    /**
-     * @param {*} type 部件类型
-     * @param {*} name 属性名称
-     * @param {*} value 属性值
-     */
-    async setAttributes(type, name, value) {
-        let item = stateService.getFocusItem()
-        switch (name) {
-            case 'delete':
-                this.deleteItem()
-                break;
-            case 'update':
-                if(type == VectorType.Tag){
-                    const tag = floorplanService.getTag(item.vectorId)
-                    tag.setValue(value)
-                }
-                else if(type == VectorType.Table){
-                    const table = floorplanService.getTable(item.vectorId)
-                    table.setValue(value)
-                }
-                else if(type == VectorType.Title){
-                    floorplanService.updateTitle(value);
-                }
-                else if(type == VectorType.BgImage){
-                    await floorplanService.updateBgImage(value);
-                }
-                else if(type == VectorType.Compass){
-                    floorplanService.updateCompass(value);
-                }
-                break;
+  /**
+   * @param {*} type 部件类型
+   * @param {*} name 属性名称
+   * @param {*} value 属性值
+   */
+  async setAttributes(type, name, value) {
+    let item = stateService.getFocusItem();
+    switch (name) {
+      case "delete":
+        this.deleteItem();
+        break;
+      case "update":
+        if (type == VectorType.Tag) {
+          const tag = floorplanService.getTag(item.vectorId);
+          tag.setValue(value);
+        } else if (type == VectorType.Table) {
+          const table = floorplanService.getTable(item.vectorId);
+          table.setValue(value);
+        } else if (type == VectorType.Title) {
+          floorplanService.updateTitle(value);
+        } else if (type == VectorType.BgImage) {
+          await floorplanService.updateBgImage(value);
+        } else if (type == VectorType.Compass) {
+          floorplanService.updateCompass(value);
         }
-        history.save()
-        stateService.clearFocusItem();
-        this.bus.emit('hideAttribute')
-        this.bus.emit('hideUI')
-        this.layer.renderer.autoRedraw()
+        break;
     }
+    history.save();
+    stateService.clearFocusItem();
+    // this.bus.emit('hideAttribute')
+    // this.bus.emit('hideUI')
+    this.layer.renderer.autoRedraw();
+  }
 
-    showAttributes(item) {
-        let type = item.type;
-        let value = null;
-        switch (item.type) {
-            case VectorType.Tag:
-                const tag = floorplanService.getTag(item.vectorId)
-                if(!tag){
-                    return;
-                }
-                value = tag.value;
-                break;
-            case VectorType.Table:
-                const table = floorplanService.getTable(item.vectorId)
-                if(!table){
-                    return;
-                }
-                const cellIds = table.cells;
-                value = [];
-                for(let i=0;i<cellIds.length;++i){
-                    for(let j=0;j<cellIds[i].length;++j){
-                        const cell = floorplanService.getCell(cellIds[i][j])
-                        value.push({
-                            width:cell.width,
-                            height:cell.height,
-                            value:cell.value,
-                            colIndex:cell.colIndex,
-                            rowIndex:cell.rowIndex
-                        })
-                    }
-                }
-                break;
-            case VectorType.Title:
-                const title = floorplanService.getTitle()
-                if(!title){
-                    return;
-                }
-                value = title.value;
-                break;
-            case VectorType.Compass:
-                const compass = floorplanService.getCompass()
-                if(!compass){
-                    return;
-                }
-                value = compass.angle;
-                break;
+  showAttributes(item) {
+    let type = item.type;
+    let value = null;
+    switch (item.type) {
+      case VectorType.Tag:
+        const tag = floorplanService.getTag(item.vectorId);
+        if (!tag) {
+          return;
+        }
+        value = tag.value;
+        break;
+      case VectorType.Table:
+        const table = floorplanService.getTable(item.vectorId);
+        if (!table) {
+          return;
+        }
+        const cellIds = table.cells;
+        value = [];
+        for (let i = 0; i < cellIds.length; ++i) {
+          for (let j = 0; j < cellIds[i].length; ++j) {
+            const cell = floorplanService.getCell(cellIds[i][j]);
+            value.push({
+              width: cell.width,
+              height: cell.height,
+              value: cell.value,
+              colIndex: cell.colIndex,
+              rowIndex: cell.rowIndex,
+            });
+          }
+        }
+        break;
+      case VectorType.Title:
+        const title = floorplanService.getTitle();
+        if (!title) {
+          return;
         }
-        this.bus.emit('showAttribute',{
-            type:type,
-            value:value
-        })
+        value = title.value;
+        break;
+      case VectorType.Compass:
+        const compass = floorplanService.getCompass();
+        if (!compass) {
+          return;
+        }
+        value = compass.angle;
+        break;
     }
+    this.bus.emit("showAttribute", {
+      type: type,
+      value: value,
+    });
+  }
 
-    clearUI() {
-        this.selectUI = null
-        this.bus.emit('hideAttribute')
-        this.bus.emit('hideUI')
-    }
+  clearUI() {
+    this.selectUI = null;
+    this.bus.emit("hideAttribute");
+    this.bus.emit("hideUI");
+  }
 
-    deleteItem() {
-        let item = stateService.getFocusItem()
-        if (item) {
-            if (item.type == VectorType.Wall) {
-                floorplanService.deleteWall(item.vectorId)
-            } else if (item.type == VectorType.Rectangle) {
-                floorplanService.deleteRectangle(item.vectorId)
-            } else if (item.type == VectorType.Circle) {
-                floorplanService.deleteCircle(item.vectorId)
-            } else if (item.type == VectorType.Arrow) {
-                floorplanService.deleteArrow(item.vectorId)
-            } else if (item.type == VectorType.Icon) {
-                iconService.deleteIcon(item.vectorId)
-            }  else if (item.type == VectorType.Tag) {
-                floorplanService.deleteTag(item.vectorId)
-            } else if (item.type == VectorType.Table) {
-                floorplanService.deleteTable(item.vectorId)
-            } else if (signService.isSign(item.type)) {
-                floorplanService.deleteSign(item.vectorId)
-            } else if (item.type == VectorType.WallCorner) {
-                wallService.deleteWallCorner(item.vectorId)
-            }
-            history.save()
-            this.layer.renderer.autoRedraw()
-        }
+  deleteItem() {
+    let item = stateService.getFocusItem();
+    if (item) {
+      if (item.type == VectorType.Wall) {
+        floorplanService.deleteWall(item.vectorId);
+      } else if (item.type == VectorType.Rectangle) {
+        floorplanService.deleteRectangle(item.vectorId);
+      } else if (item.type == VectorType.Circle) {
+        floorplanService.deleteCircle(item.vectorId);
+      } else if (item.type == VectorType.Arrow) {
+        floorplanService.deleteArrow(item.vectorId);
+      } else if (item.type == VectorType.Icon) {
+        iconService.deleteIcon(item.vectorId);
+      } else if (item.type == VectorType.Tag) {
+        floorplanService.deleteTag(item.vectorId);
+      } else if (item.type == VectorType.Table) {
+        floorplanService.deleteTable(item.vectorId);
+      } else if (signService.isSign(item.type)) {
+        floorplanService.deleteSign(item.vectorId);
+      } else if (item.type == VectorType.WallCorner) {
+        wallService.deleteWallCorner(item.vectorId);
+      }
+      history.save();
+      this.layer.renderer.autoRedraw();
     }
+  }
 
-    getSignTypeForUI() {
-        if (this.selectUI == UIEvents.Cigaret) {
-            return VectorType.Cigaret
-        } else if (this.selectUI == UIEvents.FirePoint) {
-            return VectorType.FirePoint
-        } else if (this.selectUI == UIEvents.LeftFootPrint) {
-            return VectorType.LeftFootPrint
-        } else if (this.selectUI == UIEvents.RightFootPrint) {
-            return VectorType.RightFootPrint
-        } else if (this.selectUI == UIEvents.LeftShoePrint) {
-            return VectorType.LeftShoePrint
-        } else if (this.selectUI == UIEvents.RightShoePrint) {
-            return VectorType.RightShoePrint
-        } else if (this.selectUI == UIEvents.FingerPrint) {
-            return VectorType.FingerPrint
-        } else if (this.selectUI == UIEvents.DeadBody) {
-            return VectorType.DeadBody
-        } else if (this.selectUI == UIEvents.BloodStain) {
-            return VectorType.BloodStain
-        }
+  getSignTypeForUI() {
+    if (this.selectUI == UIEvents.Cigaret) {
+      return VectorType.Cigaret;
+    } else if (this.selectUI == UIEvents.FirePoint) {
+      return VectorType.FirePoint;
+    } else if (this.selectUI == UIEvents.LeftFootPrint) {
+      return VectorType.LeftFootPrint;
+    } else if (this.selectUI == UIEvents.RightFootPrint) {
+      return VectorType.RightFootPrint;
+    } else if (this.selectUI == UIEvents.LeftShoePrint) {
+      return VectorType.LeftShoePrint;
+    } else if (this.selectUI == UIEvents.RightShoePrint) {
+      return VectorType.RightShoePrint;
+    } else if (this.selectUI == UIEvents.FingerPrint) {
+      return VectorType.FingerPrint;
+    } else if (this.selectUI == UIEvents.DeadBody) {
+      return VectorType.DeadBody;
+    } else if (this.selectUI == UIEvents.BloodStain) {
+      return VectorType.BloodStain;
     }
+  }
 
-    exportJSON() {
-        const json = {
-            version: floorplanData.version,
-            floors: floorplanData.floors,
-            currentId: floorplanService.getCurrentId(),
-        }
-        return json
-    }
+  exportJSON() {
+    const json = {
+      version: floorplanData.version,
+      floors: floorplanData.floors,
+      currentId: floorplanService.getCurrentId(),
+    };
+    return json;
+  }
 
-    downloadCadImg(canvas, filename) {
-        // 图片导出为 png 格式
-        var type = 'png'
-        var imgData = canvas.toDataURL(type, 1)
+  downloadCadImg(canvas, filename) {
+    // 图片导出为 png 格式
+    var type = "png";
+    var imgData = canvas.toDataURL(type, 1);
 
-        let blobImg = this.base64ToBlob(imgData)
-        return blobImg
+    let blobImg = this.base64ToBlob(imgData);
+    return blobImg;
 
-        // 加工image data,替换mime type
-        //imgData = imgData.replace(this._fixType(type), 'image/octet-stream')
+    // 加工image data,替换mime type
+    //imgData = imgData.replace(this._fixType(type), 'image/octet-stream')
 
-        // download
-        //this.saveFile(imgData, filename)
-    }
+    // download
+    //this.saveFile(imgData, filename)
+  }
 
-    saveFile(data, filename) {
-        var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
-        save_link.href = data
-        save_link.download = filename
+  saveFile(data, filename) {
+    var save_link = document.createElementNS(
+      "http://www.w3.org/1999/xhtml",
+      "a"
+    );
+    save_link.href = data;
+    save_link.download = filename;
 
-        var event = document.createEvent('MouseEvents')
-        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
-        save_link.dispatchEvent(event)
-    }
+    var event = document.createEvent("MouseEvents");
+    event.initMouseEvent(
+      "click",
+      true,
+      false,
+      window,
+      0,
+      0,
+      0,
+      0,
+      0,
+      false,
+      false,
+      false,
+      false,
+      0,
+      null
+    );
+    save_link.dispatchEvent(event);
+  }
 
-    _fixType(type) {
-        type = type.toLowerCase().replace(/jpg/i, 'jpeg')
-        var r = type.match(/png|jpeg|bmp|gif/)[0]
-        return 'image/' + r
-    }
+  _fixType(type) {
+    type = type.toLowerCase().replace(/jpg/i, "jpeg");
+    var r = type.match(/png|jpeg|bmp|gif/)[0];
+    return "image/" + r;
+  }
 
-    base64ToBlob(base64) {
-        let arr = base64.split(','),
-            mime = arr[0].match(/:(.*?);/)[1],
-            bstr = atob(arr[1]),
-            n = bstr.length,
-            u8arr = new Uint8Array(n)
-        while (n--) {
-            u8arr[n] = bstr.charCodeAt(n)
-        }
-        return new Blob([u8arr], { type: mime })
+  base64ToBlob(base64) {
+    let arr = base64.split(","),
+      mime = arr[0].match(/:(.*?);/)[1],
+      bstr = atob(arr[1]),
+      n = bstr.length,
+      u8arr = new Uint8Array(n);
+    while (n--) {
+      u8arr[n] = bstr.charCodeAt(n);
     }
+    return new Blob([u8arr], { type: mime });
+  }
 
-    //截图
-    menu_screenShot(fileName) {
-        // this.menu_flex();
-        // this.layer.stopAddVector()
-        // setTimeout(function(){
-        //     this.downloadCadImg(this.layer.canvas,fileName)
-        // }.bind(this),100)
+  //截图
+  menu_screenShot(fileName) {
+    // this.menu_flex();
+    // this.layer.stopAddVector()
+    // setTimeout(function(){
+    //     this.downloadCadImg(this.layer.canvas,fileName)
+    // }.bind(this),100)
 
-        this.layer.stopAddVector()
-        return this.downloadCadImg(this.layer.canvas,fileName)
-    }
+    this.layer.stopAddVector();
+    return this.downloadCadImg(this.layer.canvas, fileName);
+  }
 
-    menu_flex() {
-        coordinate.reSet()
-        this.layer.renderer.autoRedraw()
-    }
+  menu_flex() {
+    coordinate.reSet();
+    this.layer.renderer.autoRedraw();
+  }
 
-    initTopTable(value){
-        let center = {
-            x:770,
-            y:200
-        }
-        center = coordinate.getXYFromScreen(center)
-        let table = tableService.createTable(center)
-        table.setValue(value)
-        this.layer.renderer.autoRedraw()
-    }
+  initTopTable(value) {
+    let center = {
+      x: 770,
+      y: 200,
+    };
+    center = coordinate.getXYFromScreen(center);
+    let table = tableService.createTable(center);
+    table.setValue(value);
+    this.layer.renderer.autoRedraw();
+  }
 
-    initDownTable(value){
-        let center = {
-            x:770,
-            y:520
-        }
-        center = coordinate.getXYFromScreen(center)
-        let table = tableService.createTable(center)
-        table.setValue(value)
-        this.layer.renderer.autoRedraw()
-    }
-    /******************************************************************************************************************************************************************/
+  initDownTable(value) {
+    let center = {
+      x: 770,
+      y: 520,
+    };
+    center = coordinate.getXYFromScreen(center);
+    let table = tableService.createTable(center);
+    table.setValue(value);
+    this.layer.renderer.autoRedraw();
+  }
+  /******************************************************************************************************************************************************************/
 }

+ 18 - 4
src/view/case/draw/board/index.js

@@ -22,8 +22,22 @@ export const create = async (store, canvas) => {
   const layer = new Layer();
   await layer.start(canvas, store);
   layer.uiControl.bus.on("showAttribute", ({ type, value: data }) => {
+    data = data || {
+      color: "#000",
+      fontSize: 12,
+    };
+
     const shape = {
-      data: { type },
+      data: { type, color: data.color, fontSize: data.fontSize },
+      setColor: (ncolor) => {
+        shape.data.color = ncolor;
+        update({ color });
+      },
+      setFontSize: (fontSize) => {
+        shape.data.fontSize = fontSize;
+        update({ fontSize });
+      },
+
       delete: () => {
         layer.uiControl.clearUI();
         layer.uiControl.setAttributes(type, "delete");
@@ -47,18 +61,17 @@ export const create = async (store, canvas) => {
       }
       case title:
       case text: {
-        console.log(data);
         data = data || "";
         shape.data.text = data;
         shape.setText = (newData) => {
           shape.data.text = newData;
+          console.error(newData);
           update(newData);
         };
         break;
       }
       case compass: {
         data = data || 0;
-        console.log(data);
         shape.data.rotate = data;
         shape.setRotate = (newData) => {
           shape.data.rotate = newData;
@@ -66,13 +79,14 @@ export const create = async (store, canvas) => {
         };
       }
     }
+
+    console.log(shape);
     refs.bus.emit("selectShape", shape);
   });
   layer.uiControl.bus.on("hideAttribute", () => {
     refs.bus.emit("selectShape", null);
   });
   history.bus.on("undoAvailable", (availabe) => {
-    console.log("0.0.0.0", !availabe);
     refs.bus.emit("backDisabled", !availabe);
   });
   history.bus.on("redoAvailable", (availabe) =>

+ 38 - 0
src/view/case/draw/edit-shape/compass.vue

@@ -0,0 +1,38 @@
+<template>
+  <!-- <el-form-item label="方向:">
+    <el-button
+      type="primary"
+      @click="
+        () => {
+          setRotate((value + 90) % 360);
+          emit('blur');
+        }
+      "
+    >
+      旋转
+    </el-button>
+  </el-form-item> -->
+  <el-form-item label="方向:">
+    <el-slider
+      style="width: 100px"
+      :model-value="value"
+      @update:model-value="val => setRotate(val as number)"
+      :min="0"
+      :max="360"
+    />
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { ref } from "vue";
+import { BoardShape } from "../board";
+import { ElSlider } from "element-plus";
+
+const props = defineProps<{ shape: BoardShape }>();
+const emit = defineEmits<{ (e: "blur"): void }>();
+const value = ref<number>(props.shape.data.rotate);
+
+const setRotate = (edg: number) => {
+  value.value = edg;
+  props.shape.setRotate(edg);
+};
+</script>

+ 22 - 0
src/view/case/draw/edit-shape/index.ts

@@ -0,0 +1,22 @@
+import { markRaw, reactive } from "vue";
+
+const componentLoads = import.meta.glob("./*.vue");
+
+export const components: { [key in string]: any } = reactive({});
+
+const map = {
+  label: ["Circle", "Rectangle", "Wall"],
+};
+
+Object.entries(componentLoads).map(([name, fn]) => {
+  name = name.substring(2, name.lastIndexOf(".vue"));
+  fn().then((mudule) => {
+    const component = (mudule as any).default;
+    markRaw(component as any);
+    const keys = [name, ...(map[name] ? map[name] : [])];
+    keys.forEach((name) => {
+      components[name] = component;
+      components[name.slice(0, 1).toUpperCase() + name.slice(1)] = component;
+    });
+  });
+});

+ 19 - 0
src/view/case/draw/edit-shape/label.vue

@@ -0,0 +1,19 @@
+<template>
+  <el-form-item label="颜色:">
+    <el-color-picker v-model="value" />
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { ref } from "vue";
+import { BoardShape } from "../board";
+import { ElColorPicker } from "element-plus";
+
+const props = defineProps<{ shape: BoardShape }>();
+const emit = defineEmits<{ (e: "blur"): void }>();
+const value = ref<string>(props.shape.data.color || "#000000");
+
+const setColor = (color: string) => {
+  value.value = color;
+  props.shape.setRotate(color);
+};
+</script>

+ 32 - 0
src/view/case/draw/edit-shape/table.vue

@@ -0,0 +1,32 @@
+<template>
+  <el-form-item label="内容:">
+    <el-button type="primary" @click="() => editTable()">编辑</el-button>
+  </el-form-item>
+
+  <el-form-item label="删除:">
+    <el-button type="primary" @click="$emit('delete')">删除</el-button>
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { BoardShape } from "../board";
+import { editEshapeTable } from "@/view/case/quisk";
+
+const props = defineProps<{ shape: BoardShape }>();
+const emit = defineEmits<{
+  (e: "delete"): void;
+  (e: "blur"): void;
+  (e: "inputIng", ing: boolean): void;
+}>();
+
+const editTable = async (track = false) => {
+  emit("inputIng", true);
+  const data = await editEshapeTable({ content: props.shape.data.content, track });
+  if (data) {
+    props.shape.setContent(data);
+  }
+  emit("blur");
+  emit("inputIng", false);
+};
+
+props.shape.autoSet && editTable(true);
+</script>

+ 51 - 0
src/view/case/draw/edit-shape/tag.vue

@@ -0,0 +1,51 @@
+<template>
+  <el-form-item label="内容:">
+    <el-input
+      type="textarea"
+      @focus="$emit('inputIng', true)"
+      @blur="$emit('inputIng', false)"
+      :maxlength="50"
+      style="width: 200px"
+      v-model="text"
+      @change="shape.setText(text)"
+    >
+      <template #append>
+        <el-button type="primary" @click="$emit('blur')">确定</el-button>
+      </template>
+    </el-input>
+  </el-form-item>
+
+  <Label :shape="shape" @blur="emit('blur')" />
+
+  <el-form-item label="字号:">
+    <el-select v-model="fontSize" placeholder="选择字号" style="width: 200px">
+      <el-option v-for="item in fontSizeOptions" v-bind="item" :key="item.value" />
+    </el-select>
+  </el-form-item>
+
+  <el-form-item label="删除:">
+    <el-button type="primary" @click="$emit('delete')">删除</el-button>
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { ref, watchEffect } from "vue";
+import { BoardShape } from "../board";
+import Label from "./label.vue";
+
+const props = defineProps<{ shape: BoardShape }>();
+const emit = defineEmits<{
+  (e: "delete"): void;
+  (e: "blur"): void;
+  (e: "inputIng", ing: boolean): void;
+}>();
+
+const fontSizeRange = [8, 30];
+const fontSizeOptions: { value: number; label: string }[] = [];
+for (let i = fontSizeRange[0]; i <= fontSizeRange[1]; i++) {
+  fontSizeOptions.push({ value: i, label: i.toString() });
+}
+
+const text = ref(props.shape.data.text);
+const fontSize = ref(props.shape.data.fontSize);
+// watchEffect(() => props.shape.setFontSize(fontSize.value));
+</script>

+ 29 - 0
src/view/case/draw/edit-shape/title.vue

@@ -0,0 +1,29 @@
+<template>
+  <el-form-item label="内容:">
+    <el-input
+      @focus="$emit('inputIng', true)"
+      @blur="$emit('inputIng', false)"
+      :maxlength="500"
+      style="width: 220px"
+      v-model="value"
+      @change="shape.setText(value)"
+    >
+      <template #append>
+        <el-button type="primary" @click="$emit('blur')">确定</el-button>
+      </template>
+    </el-input>
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { ref } from "vue";
+import { BoardShape } from "../board";
+
+const props = defineProps<{ shape: BoardShape }>();
+const emit = defineEmits<{
+  (e: "delete"): void;
+  (e: "blur"): void;
+  (e: "inputIng", ing: boolean): void;
+}>();
+
+const value = ref(props.shape.data.text);
+</script>

+ 123 - 37
src/view/case/draw/editEshapeTable.vue

@@ -1,36 +1,57 @@
 <template>
-  <table class="content-table" ref="tableRef">
-    <tr class="header">
-      <th v-for="(col, i) in rows[0]">列 {{ i + 1 }}</th>
-      <th>删除</th>
-    </tr>
-    <tr v-for="(row, rowIndex) in rows" class="row" :key="rowIndex">
-      <td
-        v-for="(col, colIndex) in row"
-        class="col"
-        :key="colIndex"
-        @click="inputPos = [rowIndex, colIndex]"
-      >
-        <input
-          :ref="(dom: any) => inputRef = dom"
-          @blur="inputPos = null"
-          v-if="inputPos && rowIndex === inputPos[0] && colIndex === inputPos[1]"
-          :value="col"
-          @change="ev => setColValue(rowIndex, colIndex, (ev.target as any).value)"
-        />
-        <span v-else>{{ col }}</span>
-      </td>
-      <td>
-        <el-button type="primary" plain size="small" @click="delRow(rowIndex)">
-          <el-icon><Minus /></el-icon>
-        </el-button>
-      </td>
-    </tr>
-  </table>
+  <div class="content-table-layer">
+    <table class="content-table" ref="tableRef">
+      <tr class="header">
+        <th
+          class="sel-th"
+          v-for="(col, colIndex) in rows[0]"
+          @click="select = { col: colIndex }"
+          :class="{ active: colIndex === select.col }"
+        >
+          <el-button type="primary" plain size="small" @click.stop="delColumn(colIndex)">
+            <el-icon><Minus /></el-icon>
+          </el-button>
+        </th>
+        <td></td>
+      </tr>
+      <tr v-for="(row, rowIndex) in rows" class="row" :key="rowIndex">
+        <td
+          v-for="(col, colIndex) in row"
+          class="col"
+          :key="colIndex"
+          @click="inputPos = [rowIndex, colIndex]"
+        >
+          <input
+            :ref="(dom: any) => inputRef = dom"
+            @blur="inputPos = null"
+            v-if="inputPos && rowIndex === inputPos[0] && colIndex === inputPos[1]"
+            :value="col"
+            :style="getBound(rowIndex, colIndex)"
+            @change="ev => setColValue(rowIndex, colIndex, (ev.target as any).value)"
+          />
+          <span v-else :style="getBound(rowIndex, colIndex)">{{ col || "" }}&nbsp;</span>
+        </td>
+        <th
+          class="sel-th del-col"
+          @click="select = { row: rowIndex }"
+          width="100px"
+          :class="{ active: rowIndex === select.row }"
+        >
+          <el-button type="primary" plain size="small" @click="delRow(rowIndex)">
+            <el-icon><Minus /></el-icon>
+          </el-button>
+        </th>
+      </tr>
+    </table>
+  </div>
+  <div class="setter"></div>
   <div class="add-row-layout">
     <el-button type="primary" @click="addRow">
       <el-icon><Plus /></el-icon> 行
     </el-button>
+    <el-button type="primary" @click="addCloumn">
+      <el-icon><Plus /></el-icon> 列
+    </el-button>
   </div>
 </template>
 
@@ -51,6 +72,23 @@ const props = defineProps<{ content: EshapeTableContent; track?: boolean }>();
 const bindContent = ref(props.content.map((item) => ({ ...item })));
 const inputPos = ref<[number, number] | null>(null);
 const inputRef = ref<HTMLInputElement>();
+const select = ref<{ col?: number; row?: number }>({});
+
+const getBound = (rowIndex, colIndex) => {
+  const item = bindContent.value.find(
+    (item) => item.rowIndex === rowIndex && item.colIndex === colIndex
+  );
+  const bound = [170, 25 - 2];
+  if (item && item.width && item.height) {
+    bound[0] = item.width;
+    bound[1] = item.height;
+  }
+
+  return {
+    width: bound[0] + "px",
+    "min-height": bound[1] + "px",
+  };
+};
 
 watchEffect(
   () => {
@@ -77,6 +115,24 @@ const delRow = (rowIndex: number) => {
   }
 };
 
+const delColumn = (columnIndex: number) => {
+  if (rows.value[0].length === 1) {
+    ElMessage.error("表格最少需要保留一列!");
+  } else {
+    console.log(columnIndex, bindContent.value);
+    bindContent.value = bindContent.value
+      .filter((item) => item.colIndex !== columnIndex)
+      .map((item) => {
+        if (item.colIndex > columnIndex) {
+          return { ...item, colIndex: item.colIndex - 1 };
+        } else {
+          return item;
+        }
+      });
+    console.log(columnIndex, bindContent.value);
+  }
+};
+
 const addRow = () => {
   const colSize = rows.value[0].length;
   const rowSize = rows.value.length;
@@ -90,6 +146,28 @@ const addRow = () => {
     });
   }
 };
+const addCloumn = () => {
+  const colSize = rows.value[0].length;
+  const rowSize = rows.value.length;
+  for (let i = 0; i < rowSize; i++) {
+    bindContent.value.push({
+      width: 0,
+      height: 0,
+      colIndex: colSize,
+      rowIndex: i,
+      value: "",
+    });
+  }
+  // for (let i = 0; i < colSize; i++) {
+  //   bindContent.value.push({
+  //     width: 0,
+  //     height: 0,
+  //     colIndex: i,
+  //     rowIndex: rowSize,
+  //     value: "",
+  //   });
+  // }
+};
 
 const setColValue = (rowIndex: number, colIndex: number, val: string) => {
   const item = bindContent.value.find(
@@ -117,6 +195,7 @@ defineExpose<QuiskExpose>({
 
       for (let j = 0; j < cols.length; j++) {
         const col = cols[j] as HTMLElement;
+        console.log(bindContent, i, j);
         const item = bindContent.value.find(
           (item) => item.rowIndex === i && item.colIndex === j
         )!;
@@ -131,11 +210,11 @@ defineExpose<QuiskExpose>({
 
 <style lang="scss" scoped>
 .content-table {
-  width: 100%;
-  height: 100%;
+  margin: 0 auto;
   border-collapse: collapse;
   --border-color: #f0f2f5;
   margin-bottom: 10px;
+  table-layout: fixed;
 
   th {
     background: #fafafb;
@@ -158,13 +237,6 @@ defineExpose<QuiskExpose>({
     text-align: center;
   }
 
-  .col:nth-child(1) {
-    width: 90px;
-  }
-  .col:nth-child(2) {
-    width: 170px;
-  }
-
   .col {
     cursor: pointer;
 
@@ -183,13 +255,27 @@ defineExpose<QuiskExpose>({
       text-align: center;
       line-height: 24px;
     }
+    span {
+      word-break: break-all;
+    }
   }
   .col:hover {
     background-color: rgba(133, 194, 255, 0.1);
   }
+  .sel-th {
+    cursor: pointer;
+  }
+  .active {
+    box-shadow: 0 0 0 1px var(--el-color-primary) inset;
+  }
 }
 
 .add-row-layout {
   text-align: center;
 }
+
+.content-table-layer {
+  overflow: auto;
+  text-align: center;
+}
 </style>

+ 43 - 78
src/view/case/draw/eshape.vue

@@ -1,104 +1,49 @@
 <template>
-  <div class="def-shape-edit">
+  <div class="def-shape-edit" v-if="editComponent">
     <el-icon class="def-close-shape-edit" @click="emit('update:shape', null)">
       <Close />
     </el-icon>
-    <el-form inline>
-      <el-form-item label="内容:" v-if="textType.includes(type)">
-        <el-input
-          @focus="inputIng = true"
-          @blur="inputIng = false"
-          :maxlength="type === 'Tag' ? 500 : 50"
-          style="width: 220px"
-          v-model="meta!.value"
-          @change="meta!.update"
-        >
-          <template #append>
-            <el-button type="primary" @click="meta!.update(true)">确定</el-button>
-          </template>
-        </el-input>
-      </el-form-item>
-
-      <el-form-item label="内容:" v-if="ContentType.includes(type)">
-        <el-button type="primary" @click="() => editTable()">编辑</el-button>
-      </el-form-item>
-
-      <el-form-item label="方向:" v-if="CompassType.includes(type)">
-        <el-button type="primary" @click="meta!.update()"> 旋转 </el-button>
-      </el-form-item>
-
-      <el-form-item label="删除:" v-if="!nDelType.includes(type)">
-        <el-button type="primary" @click="delHandler">删除</el-button>
-      </el-form-item>
+    <el-form class="def-shape-edit-form" label-width="60px">
+      <component
+        v-if="editComponent"
+        :is="editComponent"
+        :shape="props.shape"
+        @delete="delHandler"
+        @inputIng="(bol) => (inputIng = bol)"
+        @blur="emit('update:shape', null)"
+      />
     </el-form>
   </div>
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, onUnmounted, reactive, ref, watchEffect } from "vue";
+import { computed, onMounted, onUnmounted, ref } from "vue";
 import { BoardShape, compass, title } from "./board";
-import { editEshapeTable } from "@/view/case/quisk";
+import { components } from "./edit-shape";
 
 const props = defineProps<{ shape: BoardShape }>();
 const emit = defineEmits<{
   (e: "update:shape", value: BoardShape | null): void;
 }>();
-
+const editComponent = computed(() => {
+  const type = props.shape.data.type;
+  console.log(type);
+  if (type && components[type]) {
+    return components[type];
+  }
+});
 const inputIng = ref(false);
-const type = computed(() => props.shape.data.type);
-const textType = ["Tag", title];
-const CompassType = [compass];
-const nDelType = [title, compass];
-const ContentType = ["Table"];
 
 const delHandler = () => {
   props.shape.delete();
   emit("update:shape", null);
 };
-
-const editTable = async (track = false) => {
-  inputIng.value = true;
-  const data = await editEshapeTable({ content: props.shape.data.content, track });
-  if (data) {
-    props.shape.setContent(data);
-  }
-  emit("update:shape", null);
-  inputIng.value = false;
-};
-
-if (props.shape.autoSet && ContentType.includes(type.value)) {
-  editTable(true);
-}
-
-const meta = computed(() => {
-  if (textType.includes(type.value)) {
-    const data = reactive({
-      value: props.shape.data.text,
-      update: (quit?: boolean) => {
-        props.shape.setText(data.value);
-        if (quit) {
-          emit("update:shape", null);
-        }
-      },
-    });
-    return data;
-  } else if (CompassType.includes(type.value)) {
-    return reactive({
-      value: props.shape.data.rotate,
-      update: () => {
-        props.shape.setRotate((props.shape.data.rotate + 90) % 360);
-        emit("update:shape", null);
-      },
-    });
-  }
-});
-
 // del快捷键删除
 const keydownHandler = (ev: KeyboardEvent) => {
   if (
     !inputIng.value &&
     ["Backspace", "Delete"].includes(ev.key) &&
-    !nDelType.includes(type)
+    ![title, compass].includes(props.shape.data.type)
   ) {
     delHandler();
   }
@@ -122,7 +67,7 @@ onUnmounted(() =>
   display: flex;
   align-items: center;
   justify-content: center;
-  padding: 15px;
+  padding: 15px 25px 0 10px;
   width: fit-content;
 }
 .def-close-shape-edit {
@@ -130,8 +75,8 @@ onUnmounted(() =>
   font-size: 14px;
   color: rgba(0, 0, 0, 0.85);
   position: absolute;
-  right: 15px;
-  top: 15px;
+  right: 5px;
+  top: 5px;
   cursor: pointer;
 }
 </style>
@@ -146,4 +91,24 @@ onUnmounted(() =>
     }
   }
 }
+
+.def-shape-edit-form {
+  max-width: 500px;
+  overflow: hidden;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  .el-form-item {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+  }
+
+  // .el-form-item:nth-child(2n - 1) {
+  //   width: 70%;
+  // }
+  // .el-form-item:nth-child(2n) {
+  //   width: 30%;
+  // }
+}
 </style>

+ 8 - 3
src/view/case/draw/index.vue

@@ -177,10 +177,15 @@ const exportHandler = async () => {
 }
 
 .df-board {
+  --w: 297px;
+  --h: 210px;
+  --padding: 20px;
+  --calc: 3.5;
+
   border: 1px solid #000;
-  outline: 10px solid #fff;
-  width: 940px;
-  height: 670px;
+  outline: calc(var(--padding) * var(--calc)) solid #fff;
+  width: calc((var(--w) - var(--padding)) * var(--calc));
+  height: calc((var(--h) - var(--padding)) * var(--calc));
   box-sizing: border-box;
   canvas {
     background: #fff;

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

@@ -22,7 +22,6 @@ export const addCaseScenes = quiskMountFactory(AddScenes, {
 
 const editEshapeTableRaw = quiskMountFactory(EditEshapeTable, {
   title: "表格内容编辑",
-  width: 460,
 })<EshapeTableContent>;
 export const editEshapeTable = (
   props: Parameters<typeof editEshapeTableRaw>["0"]

+ 1 - 1
vite.config.ts

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