Selaa lähdekoodia

验证工具编号复制自动加一

huangyan 7 kuukautta sitten
vanhempi
commit
776fcb1457
3 muutettua tiedostoa jossa 533 lisäystä ja 463 poistoa
  1. 1 1
      app/src/App.vue
  2. 1 1
      app/vite.config.js
  3. 531 461
      tiny-whiteboard/src/Elements.js

+ 1 - 1
app/src/App.vue

@@ -976,7 +976,7 @@ onMounted(() => {
   // 监听数据变化
   app.on('change', data => {
     showGrid.value = data.state.showGrid
-    localStorage.setItem('TINY_WHITEBOARD_DATA', JSON.stringify(data))
+    // localStorage.setItem('TINY_WHITEBOARD_DATA', JSON.stringify(data))
   })
   // 监听滚动变化
   app.on('scrollChange', (x, y) => {

+ 1 - 1
app/vite.config.js

@@ -7,7 +7,7 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
 const TEMPLATE_API_URL = 'api/';
 // https://vitejs.dev/config/
 export default defineConfig({
-  base: '/',
+  base: './',
   define: {
     'process.env.TEMPLATE_API_URL': JSON.stringify(TEMPLATE_API_URL),
   },

+ 531 - 461
tiny-whiteboard/src/Elements.js

@@ -7,471 +7,541 @@ import Arrow from './elements/Arrow'
 import Image from './elements/Image'
 import Line from './elements/Line'
 import Text from './elements/Text'
-import {
-  getTowPointDistance,
-  throttle,
-  computedLineWidthBySpeed,
-  createImageObj
-} from './utils'
-import { DRAG_ELEMENT_PARTS } from './constants'
+import {computedLineWidthBySpeed, createImageObj, getTowPointDistance, throttle} from './utils'
+import {DRAG_ELEMENT_PARTS} from './constants'
 
 // 元素管理类
 export default class Elements {
-  constructor(app) {
-    this.app = app
-    // 所有元素
-    this.elementList = []
-    // 当前激活元素
-    this.activeElement = null
-    // 当前正在创建新元素
-    this.isCreatingElement = false
-    // 当前正在调整元素
-    this.isResizing = false
-    // 当前正在调整的元素
-    this.resizingElement = null
-    // 稍微缓解一下卡顿
-    this.handleResize = throttle(this.handleResize, this, 16)
-  }
-
-  // 序列化当前画布上的所有元素
-  serialize(stringify = false) {
-    let data = this.elementList.map(element => {
-      return element.serialize()
-    })
-    return stringify ? JSON.stringify(data) : data
-  }
-
-  // 获取当前画布上的元素数量
-  getElementsNum() {
-    return this.elementList.length
-  }
-
-  // 当前画布上是否有元素
-  hasElements() {
-    return this.elementList.length > 0
-  }
-
-  // 添加元素
-  addElement(element) {
-    this.elementList.push(element)
-    return this
-  }
-
-  // 向前添加元素
-  unshiftElement(element) {
-    this.elementList.unshift(element)
-    return this
-  }
-
-  // 添加元素到指定位置
-  insertElement(element, index) {
-    this.elementList.splice(index, 0, element)
-  }
-
-  // 删除元素
-  deleteElement(element) {
-    let index = this.getElementIndex(element)
-    if (index !== -1) {
-      this.elementList.splice(index, 1)
-      if (element.isActive) {
-        this.cancelActiveElement(element)
-      }
-    }
-    return this
-  }
-
-  // 删除全部元素
-  deleteAllElements() {
-    this.activeElement = null
-    this.elementList = []
-    this.isCreatingElement = false
-    this.isResizing = false
-    this.resizingElement = null
-    return this
-  }
-
-  // 获取元素在元素列表里的索引
-  getElementIndex(element) {
-    return this.elementList.findIndex(item => {
-      return item === element
-    })
-  }
-
-  // 根据元素数据创建元素
-  createElementsFromData(data) {
-    data.forEach(item => {
-      let element = this.pureCreateElement(item)
-      element.isActive = false
-      element.isCreating = false
-      this.addElement(element)
-    })
-    this.app.group.initIdToElementList(this.elementList)
-    return this
-  }
-
-  // 是否存在激活元素
-  hasActiveElement() {
-    return !!this.activeElement
-  }
-
-  // 设置激活元素
-  setActiveElement(element) {
-    this.cancelActiveElement()
-    this.activeElement = element
-    if (element) {
-      element.isActive = true
-    }
-    this.app.emit('activeElementChange', this.activeElement)
-    return this
-  }
-
-  // 取消当前激活元素
-  cancelActiveElement() {
-    if (!this.hasActiveElement()) {
-      return this
-    }
-    this.activeElement.isActive = false
-    this.activeElement = null
-    this.app.emit('activeElementChange', this.activeElement)
-    return this
-  }
-
-  // 检测是否点击选中元素
-  checkIsHitElement(e) {
-    // 判断是否选中元素
-    let x = e.unGridClientX
-    let y = e.unGridClientY
-    // 从后往前遍历元素,默认认为新创建的元素在上一层
-    for (let i = this.elementList.length - 1; i >= 0; i--) {
-      let element = this.elementList[i]
-      if (element.isHit(x, y)) {
-        return element
-      }
-    }
-    return null
-  }
-
-  // 纯创建元素
-  pureCreateElement(opts = {}) {
-    switch (opts.type) {
-      case 'rectangle':
-        return new Rectangle(opts, this.app)
-      case 'diamond':
-        return new Diamond(opts, this.app)
-      case 'triangle':
-        return new Triangle(opts, this.app)
-      case 'circle':
-        return new Circle(opts, this.app)
-      case 'freedraw':
-        return new Freedraw(opts, this.app)
-      case 'image':
-        return new Image(opts, this.app)
-      case 'arrow':
-        return new Arrow(opts, this.app)
-      case 'line':
-        return new Line(opts, this.app)
-      case 'text':
-        return new Text(opts, this.app)
-      default:
-        return null
+    constructor(app) {
+        this.app = app
+        // 所有元素
+        this.elementList = []
+        // 当前激活元素
+        this.activeElement = null
+        // 当前正在创建新元素
+        this.isCreatingElement = false
+        // 当前正在调整元素
+        this.isResizing = false
+        // 当前正在调整的元素
+        this.resizingElement = null
+        // 稍微缓解一下卡顿
+        this.handleResize = throttle(this.handleResize, this, 16)
+    }
+
+    // 序列化当前画布上的所有元素
+    serialize(stringify = false) {
+        let data = this.elementList.map(element => {
+            return element.serialize()
+        })
+        return stringify ? JSON.stringify(data) : data
+    }
+
+    // 获取当前画布上的元素数量
+    getElementsNum() {
+        return this.elementList.length
+    }
+
+    // 当前画布上是否有元素
+    hasElements() {
+        return this.elementList.length > 0
+    }
+
+    // 添加元素
+    addElement(element) {
+        this.elementList.push(element)
+        return this
     }
-  }
-
-  // 创建元素
-  createElement(opts = {}, callback = () => {}, ctx = null, notActive) {
-    if (this.hasActiveElement() || this.isCreatingElement) {
-      return this
-    }
-    let element = this.pureCreateElement(opts)
-    if (!element) {
-      return this
-    }
-    this.addElement(element)
-    if (!notActive) {
-      this.setActiveElement(element)
-    }
-    this.isCreatingElement = true
-    callback.call(ctx, element)
-    return this
-  }
-
-  // 复制元素
-  copyElement(element, notActive = false, pos) {
-    return new Promise(async resolve => {
-      if (!element) {
-        return resolve()
-      }
-      let data = this.app.group.handleCopyElementData(element.serialize())
-      // 图片元素需要先加载图片
-      if (data.type === 'image') {
-        data.imageObj = await createImageObj(data.url)
-      }
-      this.createElement(
-        data,
-        element => {
-          this.app.group.handleCopyElement(element)
-          element.startResize(DRAG_ELEMENT_PARTS.BODY)
-          // 默认偏移原图形20像素
-          let ox = 20
-          let oy = 20
-          // 指定了具体坐标则使用具体坐标
-          if (pos) {
-            ox = pos.x - element.x - element.width / 2
-            oy = pos.y - element.y - element.height / 2
-          }
-          // 如果开启了网格,那么要坐标要吸附到网格
-          let gridAdsorbentPos = this.app.coordinate.gridAdsorbent(ox, oy)
-          element.resize(
-            null,
-            null,
-            null,
-            gridAdsorbentPos.x,
-            gridAdsorbentPos.y
-          )
-          element.isCreating = false
-          if (notActive) {
+
+    // 向前添加元素
+    unshiftElement(element) {
+        this.elementList.unshift(element)
+        return this
+    }
+
+    // 添加元素到指定位置
+    insertElement(element, index) {
+        this.elementList.splice(index, 0, element)
+    }
+
+    // 删除元素
+    deleteElement(element) {
+        let index = this.getElementIndex(element)
+        if (index !== -1) {
+            this.elementList.splice(index, 1)
+            if (element.isActive) {
+                this.cancelActiveElement(element)
+            }
+        }
+        return this
+    }
+
+    // 删除全部元素
+    deleteAllElements() {
+        this.activeElement = null
+        this.elementList = []
+        this.isCreatingElement = false
+        this.isResizing = false
+        this.resizingElement = null
+        return this
+    }
+
+    // 获取元素在元素列表里的索引
+    getElementIndex(element) {
+        return this.elementList.findIndex(item => {
+            return item === element
+        })
+    }
+
+    // 根据元素数据创建元素
+    createElementsFromData(data) {
+        data.forEach(item => {
+            let element = this.pureCreateElement(item)
             element.isActive = false
-          }
-          this.isCreatingElement = false
-          resolve(element)
-        },
-        this,
-        notActive
-      )
-    })
-  }
-
-  // 正在创建类矩形元素
-  creatingRectangleLikeElement(type, x, y, offsetX, offsetY) {
-    this.createElement({
-      type,
-      x: x,
-      y: y,
-      width: offsetX,
-      height: offsetY
-    })
-    this.activeElement.updateSize(offsetX, offsetY)
-  }
-
-  // 正在创建圆形元素
-  creatingCircle(x, y, e) {
-    this.createElement({
-      type: 'circle',
-      x: x,
-      y: y
-    })
-    let radius = getTowPointDistance(e.clientX, e.clientY, x, y)
-    this.activeElement.updateSize(radius, radius)
-  }
-
-  // 正在创建自由画笔元素
-  creatingFreedraw(e, event) {
-    this.createElement({
-      type: 'freedraw'
-    })
-    let element = this.activeElement
-    // 计算画笔粗细
-    let lineWidth = computedLineWidthBySpeed(
-      event.mouseSpeed,
-      element.lastLineWidth
-    )
-    element.lastLineWidth = lineWidth
-    element.addPoint(e.clientX, e.clientY, lineWidth)
-    // 绘制自由线不重绘,采用增量绘制,否则会卡顿
-    let { coordinate, ctx, state } = this.app
-    // 事件对象的坐标默认是加上了画布偏移量的,临时绘制的时候不需要,所以需要减去
-    let tfp = coordinate.transformToCanvasCoordinate(
-      coordinate.subScrollX(event.lastMousePos.x),
-      coordinate.subScrollY(event.lastMousePos.y)
-    )
-    let ttp = coordinate.transformToCanvasCoordinate(
-      coordinate.subScrollX(e.clientX),
-      coordinate.subScrollY(e.clientY)
-    )
-    ctx.save()
-    ctx.scale(state.scale, state.scale)
-    element.singleRender(tfp.x, tfp.y, ttp.x, ttp.y, lineWidth)
-    ctx.restore()
-  }
-
-  // 正在创建图片元素
-  creatingImage(e, { width, height, imageObj, url, ratio }) {
-    // 吸附到网格,如果网格开启的话
-    let gp = this.app.coordinate.gridAdsorbent(
-      e.unGridClientX - width / 2,
-      e.unGridClientY - height / 2
-    )
-    this.createElement({
-      type: 'image',
-      x: gp.x,
-      y: gp.y,
-      url: url,
-      imageObj: imageObj,
-      width: width,
-      height: height,
-      ratio: ratio
-    })
-  }
-
-  // 正在编辑文本元素
-  editingText(element) {
-    if (element.type !== 'text') {
-      return
-    }
-    element.noRender = true
-    this.setActiveElement(element)
-  }
-
-  // 完成文本元素的编辑
-  completeEditingText() {
-    let element = this.activeElement
-    if (!element || element.type !== 'text') {
-      return
-    }
-    if (!element.text.trim()) {
-      // 没有输入则删除该文字元素
-      this.deleteElement(element)
-      this.setActiveElement(null)
-      return
-    }
-    element.noRender = false
-  }
-
-  // 完成箭头元素的创建
-  completeCreateArrow(e) {
-    this.activeElement.addPoint(e.clientX, e.clientY)
-  }
-
-  // 正在创建箭头元素
-  creatingArrow(x, y, e) {
-    this.createElement(
-      {
-        type: 'arrow',
-        x,
-        y
-      },
-      element => {
-        element.addPoint(x, y)
-      }
-    )
-    this.activeElement.updateFictitiousPoint(e.clientX, e.clientY)
-  }
-
-  // 正在创建线段/折线元素
-  creatingLine(x, y, e, isSingle = false, notCreate = false) {
-    if (!notCreate) {
-      this.createElement(
-        {
-          type: 'line',
-          x,
-          y,
-          isSingle
-        },
-        element => {
-          element.addPoint(x, y)
+            element.isCreating = false
+            this.addElement(element)
+        })
+        this.app.group.initIdToElementList(this.elementList)
+        return this
+    }
+
+    // 是否存在激活元素
+    hasActiveElement() {
+        return !!this.activeElement
+    }
+
+    // 设置激活元素
+    setActiveElement(element) {
+        this.cancelActiveElement()
+        this.activeElement = element
+        if (element) {
+            element.isActive = true
+        }
+        this.app.emit('activeElementChange', this.activeElement)
+        return this
+    }
+
+    // 取消当前激活元素
+    cancelActiveElement() {
+        if (!this.hasActiveElement()) {
+            return this
+        }
+        this.activeElement.isActive = false
+        this.activeElement = null
+        this.app.emit('activeElementChange', this.activeElement)
+        return this
+    }
+
+    // 检测是否点击选中元素
+    checkIsHitElement(e) {
+        // 判断是否选中元素
+        let x = e.unGridClientX
+        let y = e.unGridClientY
+        // 从后往前遍历元素,默认认为新创建的元素在上一层
+        for (let i = this.elementList.length - 1; i >= 0; i--) {
+            let element = this.elementList[i]
+            if (element.isHit(x, y)) {
+                return element
+            }
+        }
+        return null
+    }
+
+    // 纯创建元素
+    pureCreateElement(opts = {}) {
+        switch (opts.type) {
+            case 'rectangle':
+                return new Rectangle(opts, this.app)
+            case 'diamond':
+                return new Diamond(opts, this.app)
+            case 'triangle':
+                return new Triangle(opts, this.app)
+            case 'circle':
+                return new Circle(opts, this.app)
+            case 'freedraw':
+                return new Freedraw(opts, this.app)
+            case 'image':
+                return new Image(opts, this.app)
+            case 'arrow':
+                return new Arrow(opts, this.app)
+            case 'line':
+                return new Line(opts, this.app)
+            case 'text':
+                return new Text(opts, this.app)
+            default:
+                return null
         }
-      )
-    }
-    let element = this.activeElement
-    if (element) {
-      element.updateFictitiousPoint(e.clientX, e.clientY)
-    }
-  }
-
-  // 完成线段/折线元素的创建
-  completeCreateLine(e, completeCallback = () => {}) {
-    let element = this.activeElement
-    let x = e.clientX
-    let y = e.clientY
-    if (element && element.isSingle) {
-      // 单根线段模式,鼠标松开则代表绘制完成
-      element.addPoint(x, y)
-      completeCallback()
-    } else {
-      // 绘制折线模式,鼠标松开代表固定一个端点
-      this.createElement({
-        type: 'line',
-        isSingle: false
-      })
-      element = this.activeElement
-      element.addPoint(x, y)
-      element.updateFictitiousPoint(x, y)
-    }
-  }
-
-  // 创建元素完成
-  completeCreateElement() {
-    this.isCreatingElement = false
-    let element = this.activeElement
-    if (!element) {
-      return this
-    }
-    // 由多个端点构成的元素需要根据端点计算外包围框
-    if (['freedraw', 'arrow', 'line'].includes(element.type)) {
-      element.updateMultiPointBoundingRect()
-    }
-    element.isCreating = false
-    this.app.emitChange()
-    return this
-  }
-
-  // 为激活元素设置样式
-  setActiveElementStyle(style = {}) {
-    if (!this.hasActiveElement()) {
-      return this
-    }
-    Object.keys(style).forEach(key => {
-      this.activeElement.style[key] = style[key]
-      if (key === 'fontSize' && this.activeElement.type === 'text') {
-        this.activeElement.updateTextSize()
-      }
-    })
-    return this
-  }
-
-  // 检测指定位置是否在元素调整手柄上
-  checkInResizeHand(x, y) {
-    // 按住了拖拽元素的某个部分
-    let element = this.activeElement
-    let hand = element.dragElement.checkPointInDragElementWhere(x, y)
-    if (hand) {
-      return {
-        element,
-        hand
-      }
-    }
-    return null
-  }
-
-  // 检查是否需要进行元素调整操作
-  checkIsResize(x, y, e) {
-    if (!this.hasActiveElement()) {
-      return false
-    }
-    let res = this.checkInResizeHand(x, y)
-    if (res) {
-      this.isResizing = true
-      this.resizingElement = res.element
-      this.resizingElement.startResize(res.hand, e)
-      this.app.cursor.setResize(res.hand)
-      return true
-    }
-    return false
-  }
-
-  // 进行元素调整操作
-  handleResize(...args) {
-    if (!this.isResizing) {
-      return
-    }
-    this.resizingElement.resize(...args)
-    this.app.render.render()
-  }
-
-  // 结束元素调整操作
-  endResize() {
-    this.isResizing = false
-    this.resizingElement.endResize()
-    this.resizingElement = null
-  }
+    }
+
+    // 创建元素
+    createElement(opts = {}, callback = () => {
+    }, ctx = null, notActive) {
+        if (this.hasActiveElement() || this.isCreatingElement) {
+            return this
+        }
+        let element = this.pureCreateElement(opts)
+        if (!element) {
+            return this
+        }
+        this.addElement(element)
+        if (!notActive) {
+            this.setActiveElement(element)
+        }
+        this.isCreatingElement = true
+        callback.call(ctx, element)
+        return this
+    }
+
+    // 复制元素
+    // copyElement(element, notActive = false, pos) {
+    //   return new Promise(async resolve => {
+    //     if (!element) {
+    //       return resolve()
+    //     }
+    //     let data = this.app.group.handleCopyElementData(element.serialize())
+    //     // 图片元素需要先加载图片
+    //     if (data.type === 'image') {
+    //       data.imageObj = await createImageObj(data.url)
+    //     }
+    //     this.createElement(
+    //       data,
+    //       element => {
+    //         this.app.group.handleCopyElement(element)
+    //         element.startResize(DRAG_ELEMENT_PARTS.BODY)
+    //         // 默认偏移原图形20像素
+    //         let ox = 20
+    //         let oy = 20
+    //         // 指定了具体坐标则使用具体坐标
+    //         if (pos) {
+    //           ox = pos.x - element.x - element.width / 2
+    //           oy = pos.y - element.y - element.height / 2
+    //         }
+    //         // 如果开启了网格,那么要坐标要吸附到网格
+    //         let gridAdsorbentPos = this.app.coordinate.gridAdsorbent(ox, oy)
+    //         element.resize(
+    //           null,
+    //           null,
+    //           null,
+    //           gridAdsorbentPos.x,
+    //           gridAdsorbentPos.y
+    //         )
+    //         element.isCreating = false
+    //         if (notActive) {
+    //           element.isActive = false
+    //         }
+    //         this.isCreatingElement = false
+    //         resolve(element)
+    //       },
+    //       this,
+    //       notActive
+    //     )
+    //   })
+    // }
+    copyElement(element, notActive = false, pos) {
+        return new Promise(async resolve => {
+            if (!element) {
+                return resolve();
+            }
+
+            let data = this.app.group.handleCopyElementData(element.serialize());
+
+            // 图片元素需要先加载图片
+            if (data.type === 'image') {
+                data.imageObj = await createImageObj(data.url);
+            }
+            // 判断是否为数字类型,并将字符串转换为数字
+            if (data.type === 'text') {
+                console.log(data)
+                if (typeof data.text === 'string') {
+                    console.log(data.text)
+                    if (isNumeric(data.text)) {
+                        // 保留前导零
+                        const num = parseFloat(data.text)
+                        const incrementedNum = num + 1;
+                        // 使用 toString 并指定基数为 10 来避免科学计数法
+                        data.text = incrementedNum.toString(10).padStart(data.text.length, '0')
+                    }
+                }
+            }
+
+            this.createElement(
+                data,
+                element => {
+                    this.app.group.handleCopyElement(element);
+                    element.startResize(DRAG_ELEMENT_PARTS.BODY);
+
+                    // 默认偏移原图形20像素
+                    let ox = 20;
+                    let oy = 20;
+
+                    // 指定了具体坐标则使用具体坐标
+                    if (pos) {
+                        ox = pos.x - element.x - element.width / 2;
+                        oy = pos.y - element.y - element.height / 2;
+                    }
+
+                    // 如果开启了网格,那么要坐标要吸附到网格
+                    let gridAdsorbentPos = this.app.coordinate.gridAdsorbent(ox, oy);
+                    element.resize(
+                        null,
+                        null,
+                        null,
+                        gridAdsorbentPos.x,
+                        gridAdsorbentPos.y
+                    );
+                    element.isCreating = false;
+
+                    if (notActive) {
+                        element.isActive = false;
+                    }
+
+                    this.isCreatingElement = false;
+                    resolve(element);
+                },
+                this,
+                notActive
+            );
+        });
+    }
+
+    // 正在创建类矩形元素
+    creatingRectangleLikeElement(type, x, y, offsetX, offsetY) {
+        this.createElement({
+            type,
+            x: x,
+            y: y,
+            width: offsetX,
+            height: offsetY
+        })
+        this.activeElement.updateSize(offsetX, offsetY)
+    }
+
+    // 正在创建圆形元素
+    creatingCircle(x, y, e) {
+        this.createElement({
+            type: 'circle',
+            x: x,
+            y: y
+        })
+        let radius = getTowPointDistance(e.clientX, e.clientY, x, y)
+        this.activeElement.updateSize(radius, radius)
+    }
+
+    // 正在创建自由画笔元素
+    creatingFreedraw(e, event) {
+        this.createElement({
+            type: 'freedraw'
+        })
+        let element = this.activeElement
+        // 计算画笔粗细
+        let lineWidth = computedLineWidthBySpeed(
+            event.mouseSpeed,
+            element.lastLineWidth
+        )
+        element.lastLineWidth = lineWidth
+        element.addPoint(e.clientX, e.clientY, lineWidth)
+        // 绘制自由线不重绘,采用增量绘制,否则会卡顿
+        let {coordinate, ctx, state} = this.app
+        // 事件对象的坐标默认是加上了画布偏移量的,临时绘制的时候不需要,所以需要减去
+        let tfp = coordinate.transformToCanvasCoordinate(
+            coordinate.subScrollX(event.lastMousePos.x),
+            coordinate.subScrollY(event.lastMousePos.y)
+        )
+        let ttp = coordinate.transformToCanvasCoordinate(
+            coordinate.subScrollX(e.clientX),
+            coordinate.subScrollY(e.clientY)
+        )
+        ctx.save()
+        ctx.scale(state.scale, state.scale)
+        element.singleRender(tfp.x, tfp.y, ttp.x, ttp.y, lineWidth)
+        ctx.restore()
+    }
+
+    // 正在创建图片元素
+    creatingImage(e, {width, height, imageObj, url, ratio}) {
+        // 吸附到网格,如果网格开启的话
+        let gp = this.app.coordinate.gridAdsorbent(
+            e.unGridClientX - width / 2,
+            e.unGridClientY - height / 2
+        )
+        this.createElement({
+            type: 'image',
+            x: gp.x,
+            y: gp.y,
+            url: url,
+            imageObj: imageObj,
+            width: width,
+            height: height,
+            ratio: ratio
+        })
+    }
+
+    // 正在编辑文本元素
+    editingText(element) {
+        if (element.type !== 'text') {
+            return
+        }
+        element.noRender = true
+        this.setActiveElement(element)
+    }
+
+    // 完成文本元素的编辑
+    completeEditingText() {
+        let element = this.activeElement
+        if (!element || element.type !== 'text') {
+            return
+        }
+        if (!element.text.trim()) {
+            // 没有输入则删除该文字元素
+            this.deleteElement(element)
+            this.setActiveElement(null)
+            return
+        }
+        element.noRender = false
+    }
+
+    // 完成箭头元素的创建
+    completeCreateArrow(e) {
+        this.activeElement.addPoint(e.clientX, e.clientY)
+    }
+
+    // 正在创建箭头元素
+    creatingArrow(x, y, e) {
+        this.createElement(
+            {
+                type: 'arrow',
+                x,
+                y
+            },
+            element => {
+                element.addPoint(x, y)
+            }
+        )
+        this.activeElement.updateFictitiousPoint(e.clientX, e.clientY)
+    }
+
+    // 正在创建线段/折线元素
+    creatingLine(x, y, e, isSingle = false, notCreate = false) {
+        if (!notCreate) {
+            this.createElement(
+                {
+                    type: 'line',
+                    x,
+                    y,
+                    isSingle
+                },
+                element => {
+                    element.addPoint(x, y)
+                }
+            )
+        }
+        let element = this.activeElement
+        if (element) {
+            element.updateFictitiousPoint(e.clientX, e.clientY)
+        }
+    }
+
+    // 完成线段/折线元素的创建
+    completeCreateLine(e, completeCallback = () => {
+    }) {
+        let element = this.activeElement
+        let x = e.clientX
+        let y = e.clientY
+        if (element && element.isSingle) {
+            // 单根线段模式,鼠标松开则代表绘制完成
+            element.addPoint(x, y)
+            completeCallback()
+        } else {
+            // 绘制折线模式,鼠标松开代表固定一个端点
+            this.createElement({
+                type: 'line',
+                isSingle: false
+            })
+            element = this.activeElement
+            element.addPoint(x, y)
+            element.updateFictitiousPoint(x, y)
+        }
+    }
+
+    // 创建元素完成
+    completeCreateElement() {
+        this.isCreatingElement = false
+        let element = this.activeElement
+        if (!element) {
+            return this
+        }
+        // 由多个端点构成的元素需要根据端点计算外包围框
+        if (['freedraw', 'arrow', 'line'].includes(element.type)) {
+            element.updateMultiPointBoundingRect()
+        }
+        element.isCreating = false
+        this.app.emitChange()
+        return this
+    }
+
+    // 为激活元素设置样式
+    setActiveElementStyle(style = {}) {
+        if (!this.hasActiveElement()) {
+            return this
+        }
+        Object.keys(style).forEach(key => {
+            this.activeElement.style[key] = style[key]
+            if (key === 'fontSize' && this.activeElement.type === 'text') {
+                this.activeElement.updateTextSize()
+            }
+        })
+        return this
+    }
+
+    // 检测指定位置是否在元素调整手柄上
+    checkInResizeHand(x, y) {
+        // 按住了拖拽元素的某个部分
+        let element = this.activeElement
+        let hand = element.dragElement.checkPointInDragElementWhere(x, y)
+        if (hand) {
+            return {
+                element,
+                hand
+            }
+        }
+        return null
+    }
+
+    // 检查是否需要进行元素调整操作
+    checkIsResize(x, y, e) {
+        if (!this.hasActiveElement()) {
+            return false
+        }
+        let res = this.checkInResizeHand(x, y)
+        if (res) {
+            this.isResizing = true
+            this.resizingElement = res.element
+            this.resizingElement.startResize(res.hand, e)
+            this.app.cursor.setResize(res.hand)
+            return true
+        }
+        return false
+    }
+
+    // 进行元素调整操作
+    handleResize(...args) {
+        if (!this.isResizing) {
+            return
+        }
+        this.resizingElement.resize(...args)
+        this.app.render.render()
+    }
+
+    // 结束元素调整操作
+    endResize() {
+        this.isResizing = false
+        this.resizingElement.endResize()
+        this.resizingElement = null
+    }
+}
+
+//判断是否可以转换为数字
+function isNumeric(str) {
+    const trimmedStr = str.trim();
+    const num = Number(trimmedStr);
+    return !isNaN(num) && trimmedStr === num.toString().padStart(trimmedStr.length, '0');
 }