Kaynağa Gözat

Merge branch 'master' of http://192.168.0.115:3000/bill/public-fuse

# Conflicts:
#	src/view/case/draw/board/editCAD/Controls/UIControl.js
xushiting 1 yıl önce
ebeveyn
işleme
5ee103afa6

+ 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 {

+ 21 - 0
src/view/case/draw/board/editCAD/Controls/MoveCustomImage.js

@@ -0,0 +1,21 @@
+import { floorplanService } from '../Service/FloorplanService'
+import { mathUtil } from '../MathUtil.js'
+import { coordinate } from '../Coordinate'
+import Constant from '../Constant'
+
+export default class MoveCustomImage {
+    constructor() {
+
+    }
+
+    moveFullCustomImage(dx,dy, customImageId) {
+        let customImage = floorplanService.getCustomImage(customImageId)
+        customImage.center = {
+            x:customImage.center.x + dx/coordinate.res * Constant.defaultZoom/coordinate.zoom,
+            y:customImage.center.y - dy/coordinate.res * Constant.defaultZoom/coordinate.zoom,
+        }
+    }
+}
+
+const moveCustomImage = new MoveCustomImage()
+export { moveCustomImage }

+ 367 - 311
src/view/case/draw/board/editCAD/Controls/UIControl.js

@@ -1,344 +1,400 @@
-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 { customImageService } from "../Service/CustomImageService.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)
+  /**
+   * @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)
+            if(value.hasOwnProperty('version')){
+                tag.setColor(value.color)
+                tag.setFontSize(value.fontSize)
+                tag.setValue(value.text)
+            }
+            else{
+                tag.setValue(value)
+            }
         }
-        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)
+        else if(type == VectorType.Arrow){
+            const arrow = floorplanService.getArrow(item.vectorId)
+            if(value.hasOwnProperty('version')){
+                arrow.setColor(value.color)
+            }
         }
-    }
+        else if(type == VectorType.Wall){
+            const wall = floorplanService.getWall(item.vectorId)
+            if(value.hasOwnProperty('version')){
+                wall.setColor(value.color)
+            }
+        }
+        else if(type == VectorType.Rectangle){
+            const rectangle = floorplanService.getRectangle(item.vectorId)
+            if(value.hasOwnProperty('version')){
+                rectangle.setColor(value.color)
+            }
+        }
+        else if(type == VectorType.Circle){
+            const circle = floorplanService.getCircle(item.vectorId)
+            if(value.hasOwnProperty('version')){
+                circle.setColor(value.color)
+            }
+        }
+        else if(type == VectorType.Table){
+            const table = floorplanService.getTable(item.vectorId)
+            if(value.hasOwnProperty('version')){
+                table.setValue(value.content)
+            }
+            else{
+                table.setValue(value)
+            }
+        }
+        else if(type == VectorType.Title){
+            if(value.hasOwnProperty('version')){
+                floorplanService.updateTitle(value.text);
+            }
+            else{
+                floorplanService.updateTitle(value);
+            }
 
-    /**
-     * @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)
-                    if(value.hasOwnProperty('version')){
-                        tag.setColor(value.color)
-                        tag.setFontSize(value.fontSize)
-                        tag.setValue(value.content)
-                    }
-                    else{
-                        tag.setValue(value)
-                    }
-                }
-                else if(type == VectorType.Arrow){
-                    const arrow = floorplanService.getArrow(item.vectorId)
-                    arrow.setColor(value.color)
-                }
-                else if(type == VectorType.Wall){
-                    const wall = floorplanService.getWall(item.vectorId)
-                    wall.setColor(value.color)
-                }
-                else if(type == VectorType.Rectangle){
-                    const rectangle = floorplanService.getRectangle(item.vectorId)
-                    rectangle.setColor(value.color)
-                }
-                else if(type == VectorType.Circle){
-                    const circle = floorplanService.getCircle(item.vectorId)
-                    circle.setColor(value.color)
-                }
-                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;
+            text
+        }
+        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;
+      case "upload":
+        if(type == VectorType.CustomImage){
+          customImageService.setCustomImageSrc(value.src);
+          stateService.setEventName(LayerEvents.AddCustomImage);
+        }  
+        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 = {
+            version:'2.0',
+            text:tag.value,
+            color: tag.color,
+            fontSize: tag.fontSize,
+        };
+        break;
+      case VectorType.Table:
+        const table = floorplanService.getTable(item.vectorId);
+        if (!table) {
+          return;
         }
-        this.bus.emit('showAttribute',{
-            type:type,
-            value:value
-        })
+        const cellIds = table.cells;
+        let content = [];
+        for (let i = 0; i < cellIds.length; ++i) {
+          for (let j = 0; j < cellIds[i].length; ++j) {
+            const cell = floorplanService.getCell(cellIds[i][j]);
+            content.push({
+              width: cell.width,
+              height: cell.height,
+              value: cell.value,
+              colIndex: cell.colIndex,
+              rowIndex: cell.rowIndex,
+            });
+          }
+        }
+        value = {
+            version:'2.0',
+            content: content,
+        };
+        break;
+      case VectorType.Title:
+        const title = floorplanService.getTitle();
+        if (!title) {
+          return;
+        }
+        value = {
+            version:'2.0',
+            text: title.value,
+        };
+        break;
+      case VectorType.Compass:
+        const compass = floorplanService.getCompass();
+        if (!compass) {
+          return;
+        }
+        value = compass.angle;
+        break;
+      case VectorType.CustomImage:
+        const customImage = floorplanService.getCustomImage(item.vectorId);
+        if (!customImage) {
+          return;
+        }
+        value = {
+            version:'2.0',
+            src: customImage.src,
+        };
+        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);
+      } else if (item.type == VectorType.CustomImage) {
+        floorplanService.deleteCustomImage(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();
+  }
+  /******************************************************************************************************************************************************************/
 }

+ 1 - 0
src/view/case/draw/board/editCAD/FloorplanData.js

@@ -20,6 +20,7 @@ export default class FloorplanData {
         this.floors[floorNum].arrows = {}
         this.floors[floorNum].icons = {}
         this.floors[floorNum].signs = {}
+        this.floors[floorNum].customImages = {}
 
         // this.floors[floorNum].title = new Title();
         // this.floors[floorNum].image = new BgImage();

+ 35 - 0
src/view/case/draw/board/editCAD/Geometry/CustomImage.js

@@ -0,0 +1,35 @@
+import VectorType from '../enum/VectorType.js'
+import Geometry from './Geometry'
+
+export default class CustomImage extends Geometry {
+    constructor(src,center,vectorId) {
+        super()
+        this.center = center
+        this.src = src;
+        this.image = null;
+        this.width = 40;
+        this.height = 30;
+        this.angle = 0 //逆时针为负,顺时针为正。单位是:°
+        this.scale = 1 //缩放比例
+        this.geoType = VectorType.CustomImage
+        this.setId(vectorId)
+    }
+
+    isContain(position) {
+        const dis = mathUtil.getDistance(position, this.center)
+        let len = this.getLen() * this.scale
+        if (dis < len / 2) {
+            return SelectState.Select
+        } else {
+            return null
+        }
+    }
+
+    setImageData(imgData){
+        this.image = imgData;
+    }
+
+    setSrc(src){
+        this.src = src;
+    }
+}

+ 54 - 2
src/view/case/draw/board/editCAD/Layer.js

@@ -35,6 +35,8 @@ import { wallService } from "./Service/WallService";
 import { history } from "./History/History.js";
 import { signService } from "./Service/SignService";
 import { iconService } from "./Service/IconService";
+import { customImageService } from "./Service/CustomImageService.js";
+import { moveCustomImage } from "./Controls/MoveCustomImage.js";
 
 export default class Layer {
   constructor() {
@@ -516,6 +518,32 @@ export default class Layer {
           mathUtil.clonePoint(sign.center, position);
         }
         break;
+      case LayerEvents.AddCustomImage:
+        needAutoRedraw = true;
+        if (draggingItem == null) {
+          const customImage = customImageService.createCustomImage(position)
+          if (customImage.vectorId) {
+            stateService.setSelectItem(
+              customImage.vectorId,
+              VectorType.CustomImage,
+              SelectState.All
+            );
+            stateService.setDraggingItem(stateService.selectItem);
+          }
+        } else {
+          moveCustomImage.moveFullCustomImage(dx, dy, draggingItem.vectorId);
+        }
+        this.lastX = X;
+        this.lastY = Y;
+        break;
+      case LayerEvents.MoveCustomImage:
+        needAutoRedraw = true;
+        if (draggingItem != null) {
+          moveCustomImage.moveFullCustomImage(dx, dy, draggingItem.vectorId);
+          this.lastX = X;
+          this.lastY = Y;
+        }
+        break;
       case LayerEvents.MoveSign:
         needAutoRedraw = true;
         const sign = floorplanService.getSign(draggingItem.vectorId);
@@ -799,6 +827,19 @@ export default class Layer {
           history.save();
         }
         break;
+      case LayerEvents.MoveCustomImage:
+        if (focusItem == null) {
+          needAutoRedraw = true;
+          history.save();
+        } else {
+          this.uiControl.showAttributes(focusItem);
+        }
+        break;
+      case LayerEvents.AddCustomImage:
+        needAutoRedraw = true;
+        this.uiControl.showAttributes(focusItem);
+        history.save();
+        break;
     }
     this.lastX = null;
     this.lastY = null;
@@ -894,7 +935,9 @@ export default class Layer {
           stateService.setEventName(LayerEvents.MoveTitle);
         } else if (selectItem.type == VectorType.Compass) {
           stateService.setEventName(LayerEvents.MoveCompass);
-        }
+        } else if (selectItem.type == VectorType.CustomImage) {
+          stateService.setEventName(LayerEvents.MoveCustomImage);
+        } 
       } else if (eventName == LayerEvents.AddWall) {
         stateService.setEventName(LayerEvents.AddingWall);
       }
@@ -911,7 +954,10 @@ export default class Layer {
         stateService.setEventName(LayerEvents.AddingArrow);
       } else if (eventName == LayerEvents.AddTable) {
         stateService.clearEventName();
-      } else if (
+      } else if (eventName == LayerEvents.AddCustomImage) {
+        stateService.clearEventName();
+      }
+      else if (
         eventName != LayerEvents.AddWall &&
         eventName != LayerEvents.AddingWall
       ) {
@@ -949,6 +995,12 @@ export default class Layer {
           floorplanService.deleteSign(draggingItem.vectorId);
           stateService.clearItems();
         }
+      } else if (eventName == LayerEvents.AddCustomImage) {
+        if (draggingItem && draggingItem.vectorId) {
+          customImageService.deleteCustomImage(draggingItem.vectorId);
+          this.uiControl.clearUI();
+          stateService.clearItems();
+        }
       }
     } else {
       stateService.setEventName(LayerEvents.AddWall);

+ 43 - 5
src/view/case/draw/board/editCAD/ListenLayer.js

@@ -52,6 +52,11 @@ export default class ListenLayer {
             state: null,
         }
 
+        this.customImageInfo = {
+            customImageId: null,
+            state: null,
+        }
+
         this.titleInfo = {
             titleId: null,
             state: null,
@@ -254,6 +259,7 @@ export default class ListenLayer {
             iconInfo: {},
             tagInfo: {},
             signInfo: {},
+            customImageInfo: {},
             titleInfo: {},
             compassInfo: {},
         }
@@ -388,6 +394,19 @@ export default class ListenLayer {
             }
         }
 
+        const customImages = floorplanService.getCustomImages()
+        for (const customImageId in customImages) {
+            const customImage = floorplanService.getCustomImage(customImageId)
+            const location = customImage.isContain(position)
+            if (location) {
+                result.customImageInfo = {
+                    customImageId: customImageId,
+                    state: 'all',
+                }
+                break
+            }
+        }
+
         const title = floorplanService.getTitle();
         const titleFLag = title.isContain(position)
         if(titleFLag){
@@ -520,7 +539,13 @@ export default class ListenLayer {
             state: nearest.compassInfo.state,
         }
 
-        return flag1 || flag2 || flag3 || flag4  || flag5 || flag6 || flag7 || flag8 || flag9 || flag10 || flag11
+        const flag12 = this.isChanged(nearest.customImageInfo.customImageId, nearest.customImageInfo.state, 12)
+        this.customImageInfo = {
+            customImageId: nearest.customImageInfo.customImageId,
+            state: nearest.customImageInfo.state,
+        }
+
+        return flag1 || flag2 || flag3 || flag4  || flag5 || flag6 || flag7 || flag8 || flag9 || flag10 || flag11 || flag12
     }
 
     isChanged(vectorId, state, type, index) {
@@ -640,7 +665,15 @@ export default class ListenLayer {
                 flag = true
             }
         }
-
+        else if (type == 12) {
+            if (state == null && state == this.customImageInfo.state) {
+                flag = false
+            } else if (this.customImageInfo.customImageId == vectorId && state == this.customImageInfo.state) {
+                flag = false
+            } else {
+                flag = true
+            }
+        } 
         return flag
     }
 
@@ -682,9 +715,6 @@ export default class ListenLayer {
             }
         } else if (this.arrowInfo.arrowId != null && this.arrowInfo.state != null) {
             stateService.setSelectItem(this.arrowInfo.arrowId, VectorType.Arrow, this.arrowInfo.state)
-        } else if (this.signInfo.signsId != null && this.signInfo.state != null) {
-            const sign = floorplanService.getSign(this.signInfo.signsId)
-            stateService.setSelectItem(this.signInfo.signsId, sign.geoType, this.signInfo.state)
         } else if (this.tableInfo.tableId != null && this.tableInfo.state != null) {
             const table = floorplanService.getTable(this.tableInfo.tableId)
             stateService.setSelectItem(this.tableInfo.tableId, table.geoType, this.tableInfo.state)
@@ -694,6 +724,9 @@ export default class ListenLayer {
         } else if (this.compassInfo.compassId != null && this.compassInfo.state != null) {
             const compass = floorplanService.getCompass()
             stateService.setSelectItem(this.compassInfo.compassId, compass.geoType, this.compassInfo.state)
+        } else if (this.customImageInfo.customImageId != null && this.customImageInfo.state != null) {
+            const customImage = floorplanService.getCustomImage(this.customImageInfo.customImageId)
+            stateService.setSelectItem(this.customImageInfo.customImageId, customImage.geoType, this.customImageInfo.state)
         }
         else {
             stateService.clearSelectItem()
@@ -746,6 +779,11 @@ export default class ListenLayer {
             state: null,
         }
 
+        this.customImageInfo = {
+            customImageId: null,
+            state: null,
+        }
+
         this.modifyPoint = null
     }
 }

+ 6 - 1
src/view/case/draw/board/editCAD/Load.js

@@ -7,6 +7,7 @@ import { circleService } from './Service/CircleService.js'
 import { arrowService } from './Service/ArrowService.js'
 import { iconService } from './Service/IconService.js'
 import { tableService } from './Service/TableService.js'
+import { customImageService } from './Service/CustomImageService.js'
 
 export default class Load {
     constructor(layer) {
@@ -53,6 +54,11 @@ export default class Load {
                 tag.setValue(floor.tags[key].value)
             }
 
+            for (let key in floor.customImages) {
+                let customImage = customImageService.createCustomImage(floor.customImages[key].center, floor.customImages[key].vectorId)
+                customImage.setSrc(floor.customImages[key].src)
+            }
+
             for (let key in floor.tables) {
                 let table = tableService.createTable(floor.tables[key].center, floor.tables[key].vectorId, i)
                 table.rowLen = floor.tables[key].rowLen
@@ -85,7 +91,6 @@ export default class Load {
             for (let key in floor.icons) {
                 iconService.createIcon2(floor.icons[key].center,floor.icons[key].radius,floor.icons[key].value,floor.icons[key].vectorId,i)
             }
-            //要更新value
 
             for (let key in floor.signs) {
                 let sign = signService.createSign(floor.signs[key].center, floor.signs[key].geoType, floor.signs[key].vectorId,i)

+ 29 - 0
src/view/case/draw/board/editCAD/Service/CustomImageService.js

@@ -0,0 +1,29 @@
+import VectorType from '../enum/VectorType.js'
+import CustomImage from '../Geometry/CustomImage.js'
+import { mathUtil } from '../MathUtil.js'
+import { floorplanService } from './FloorplanService'
+import Constant from '../Constant'
+
+export default class CustomImageService {
+    constructor() {
+        this.src = null;
+    }
+
+    setCustomImageSrc(src){
+        this.src = src;
+    }
+
+    createCustomImage(center,vectorId) {
+        const customImage = new CustomImage(this.src, center,vectorId)
+        floorplanService.addCustomImage(customImage)
+        this.src = null;
+        return customImage
+    }
+
+    deleteCustomImage(customImageId, floorNum) {
+        floorplanService.deleteCustomImage(customImageId, floorNum)
+    }
+}
+
+const customImageService = new CustomImageService()
+export { customImageService }

+ 31 - 0
src/view/case/draw/board/editCAD/Service/FloorplanService.js

@@ -284,6 +284,29 @@ export class FloorplanService {
         return floorplanData.floors[floor].tags
     }
 
+    addCustomImage(customImage, floor) {
+        if (floor == null || typeof floor == 'undefined') {
+            floor = this.currentFloor
+        }
+        floorplanData.floors[floor].customImages[customImage.vectorId] = customImage
+    }
+
+    getCustomImage(customImageId, floor) {
+        if (floor == null || typeof floor == 'undefined') {
+            floor = this.currentFloor
+        }
+        return floorplanData.floors[floor].customImages[customImageId]
+    }
+
+    deleteCustomImage(customImageId, floor) {
+        if (floor == null || typeof floor == 'undefined') {
+            floor = this.currentFloor
+        }
+        let customImage = this.getCustomImage(customImageId, floor)
+        customImage = null
+        delete floorplanData.floors[floor].customImages[tagId]
+    }
+
     addTable(table, floor) {
         if (floor == null || typeof floor == 'undefined') {
             floor = this.currentFloor
@@ -378,6 +401,13 @@ export class FloorplanService {
         return floorplanData.floors[floor].signs
     }
 
+    getCustomImages(){
+        if (floor == null || typeof floor == 'undefined') {
+            floor = this.currentFloor
+        }
+        return floorplanData.floors[floor].customImages
+    }
+
     clear() {
         if (floorplanData.floors[this.currentFloor]) {
             floorplanData.floors[this.currentFloor].points = {}
@@ -388,6 +418,7 @@ export class FloorplanService {
             floorplanData.floors[this.currentFloor].tables = {}
             floorplanData.floors[this.currentFloor].cells = {}
             floorplanData.floors[this.currentFloor].signs = {}
+            floorplanData.floors[this.currentFloor].customImages = {}
             floorplanData.floors[this.currentFloor].arrows = {}
             floorplanData.floors[this.currentFloor].icons = []
         }

+ 4 - 0
src/view/case/draw/board/editCAD/Service/StateService.js

@@ -64,6 +64,10 @@ export default class StateService {
             }
         } else if(type == VectorType.Arrow){
             this.selectItem.selectIndex = state
+        } else if (type == VectorType.CustomImage) {
+            if (state == SelectState.Select) {
+                this.selectItem.selectIndex = SelectState.All
+            }
         }
     }
 

+ 3 - 1
src/view/case/draw/board/editCAD/enum/HistoryEvents.js

@@ -39,7 +39,9 @@ const HistoryEvents = {
     DeleteSign: 'deleteSign',
     ModifySign: 'modifySign',
 
-    //ModifyAngle: 'modifyAngle',
+    AddCustomImage: 'addCustomImage',
+    DeleteCustomImage: 'deleteCustomImage',
+    ModifyCustomImage: 'modifyCustomImage',
 
     ModifyTitle: 'modifyTitle',
     ModifyImage: 'modifyImage',

+ 3 - 0
src/view/case/draw/board/editCAD/enum/LayerEvents.js

@@ -32,6 +32,9 @@ const LayerEvents = {
     AddTable: 'addTable',
     MoveTable: 'moveTable',
 
+    AddCustomImage: 'addCustomImage',
+    MoveCustomImage: 'moveCustomImage',
+
     AddSign: 'addSign',
     MoveSign: 'moveSign',
 

+ 1 - 0
src/view/case/draw/board/editCAD/enum/UIEvents.js

@@ -6,6 +6,7 @@ const UIEvents = {
     Circle:'Circle',
     Arrow:'Arrow',
     Icon:'Icon',
+    CustomImage:'CustomImage',
 
     Cigaret: 'Cigaret', 
     FirePoint: 'FirePoint', 

+ 1 - 0
src/view/case/draw/board/editCAD/enum/VectorType.js

@@ -8,6 +8,7 @@ const VectorType = {
     Rectangle:'Rectangle',
     Circle:'Circle',
     Arrow:'Arrow',
+    CustomImage:'CustomImage',
     Icon:'Icon',
     Cigaret: 'Cigaret',                  //烟头
     FirePoint: 'FirePoint',              //起火点

+ 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();
 }