2025-10-28 14:02:20 +08:00
|
|
|
|
import { generateId, optimizeCanvasRendering, getLayerObjectsZIndex } from "../utils/helper";
|
2025-06-22 13:52:28 +08:00
|
|
|
|
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
|
2025-06-09 10:25:54 +08:00
|
|
|
|
import { Command } from "./Command";
|
2025-09-24 16:26:40 +08:00
|
|
|
|
import i18n from "@/lang/index.ts";
|
|
|
|
|
|
const { t } = i18n.global;
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本内容命令
|
|
|
|
|
|
* 用于更改文本图层的文本内容
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextContentCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本内容",
|
|
|
|
|
|
description: "修改文本图层的文本内容",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newText = options.newText;
|
|
|
|
|
|
this.oldText = this.textObject.text;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("text", this.newText);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("text", this.oldText);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本字体命令
|
|
|
|
|
|
* 用于更改文本图层的字体
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextFontCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本字体",
|
|
|
|
|
|
description: "修改文本图层的字体",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newFont = options.newFont;
|
|
|
|
|
|
this.oldFont = this.textObject.fontFamily;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("fontFamily", this.newFont);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("fontFamily", this.oldFont);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本尺寸命令
|
|
|
|
|
|
* 用于更改文本图层的字体大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextSizeCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本尺寸",
|
|
|
|
|
|
description: "修改文本图层的字体大小",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newSize = options.newSize;
|
|
|
|
|
|
this.oldSize = this.textObject.fontSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("fontSize", this.newSize);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("fontSize", this.oldSize);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本颜色命令
|
|
|
|
|
|
* 用于更改文本图层的颜色
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextColorCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本颜色",
|
|
|
|
|
|
description: "修改文本图层的颜色",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newColor = options.newColor;
|
|
|
|
|
|
this.oldColor = this.textObject.fill;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("fill", this.newColor);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("fill", this.oldColor);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本对齐方式命令
|
|
|
|
|
|
* 用于更改文本图层的对齐方式
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextAlignCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本对齐",
|
|
|
|
|
|
description: "修改文本图层的对齐方式",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newAlign = options.newAlign;
|
|
|
|
|
|
this.oldAlign = this.textObject.textAlign;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("textAlign", this.newAlign);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("textAlign", this.oldAlign);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本样式命令
|
|
|
|
|
|
* 用于更改文本图层的样式(粗体、斜体、下划线等)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextStyleCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本样式",
|
|
|
|
|
|
description: "修改文本图层的样式",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.property = options.property; // 'fontWeight', 'fontStyle', 'underline', 'linethrough', 'overline'
|
|
|
|
|
|
this.newValue = options.newValue;
|
|
|
|
|
|
this.oldValue = this.textObject[this.property];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set(this.property, this.newValue);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set(this.property, this.oldValue);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本间距命令
|
|
|
|
|
|
* 用于更改文本图层的字符间距或行高
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextSpacingCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本间距",
|
|
|
|
|
|
description: "修改文本图层的字符间距或行高",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.property = options.property; // 'charSpacing' 或 'lineHeight'
|
|
|
|
|
|
this.newValue = options.newValue;
|
|
|
|
|
|
this.oldValue = this.textObject[this.property];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set(this.property, this.newValue);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set(this.property, this.oldValue);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本背景颜色命令
|
|
|
|
|
|
* 用于更改文本图层的背景颜色
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextBackgroundCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本背景",
|
|
|
|
|
|
description: "修改文本图层的背景颜色",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newColor = options.newColor;
|
|
|
|
|
|
this.oldColor = this.textObject.textBackgroundColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("textBackgroundColor", this.newColor);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("textBackgroundColor", this.oldColor);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文本透明度命令
|
|
|
|
|
|
* 用于更改文本图层的透明度
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class TextOpacityCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "修改文本透明度",
|
|
|
|
|
|
description: "修改文本图层的透明度",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.newOpacity = options.newOpacity;
|
|
|
|
|
|
this.oldOpacity = this.textObject.opacity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
this.textObject.set("opacity", this.newOpacity);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
this.textObject.set("opacity", this.oldOpacity);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 组合文本编辑命令
|
|
|
|
|
|
* 用于一次性应用多个文本属性更改
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class CompositeTextCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "组合文本编辑",
|
|
|
|
|
|
description: "组合多个文本编辑操作",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.textObject = options.textObject;
|
|
|
|
|
|
this.changes = options.changes; // {property: newValue} 形式的对象
|
|
|
|
|
|
this.oldValues = {};
|
|
|
|
|
|
|
|
|
|
|
|
// 保存所有属性的旧值
|
|
|
|
|
|
for (const property in this.changes) {
|
|
|
|
|
|
if (this.textObject[property] !== undefined) {
|
|
|
|
|
|
this.oldValues[property] = this.textObject[property];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
execute() {
|
|
|
|
|
|
for (const property in this.changes) {
|
|
|
|
|
|
this.textObject.set(property, this.changes[property]);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
undo() {
|
|
|
|
|
|
for (const property in this.oldValues) {
|
|
|
|
|
|
this.textObject.set(property, this.oldValues[property]);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建文本命令
|
|
|
|
|
|
* 用于创建文本对象和图层的组合操作
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class CreateTextCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "创建文本",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.layerManager = options.layerManager;
|
|
|
|
|
|
this.x = options.x;
|
|
|
|
|
|
this.y = options.y;
|
|
|
|
|
|
this.textOptions = options.textOptions || {};
|
2025-11-11 17:35:00 +08:00
|
|
|
|
this.options = options;
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 生成唯一ID
|
2025-06-30 18:05:20 +08:00
|
|
|
|
this.textId = options?.textId || generateId("text_");
|
2025-06-22 13:52:28 +08:00
|
|
|
|
this.layerId = options?.layerId || generateId("text_layer_");
|
|
|
|
|
|
|
|
|
|
|
|
// 生成的对象和图层信息
|
|
|
|
|
|
this.textObject = null;
|
|
|
|
|
|
this.oldActiveLayerId = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 默认文本属性
|
|
|
|
|
|
this.defaultOptions = {
|
2025-11-11 17:35:00 +08:00
|
|
|
|
text: options.text || t('Canvas.DoubleClickText'),
|
2025-06-22 13:52:28 +08:00
|
|
|
|
fontFamily: "Arial",
|
|
|
|
|
|
fontSize: 24,
|
|
|
|
|
|
fontWeight: "normal",
|
|
|
|
|
|
fontStyle: "normal",
|
|
|
|
|
|
textAlign: "left",
|
|
|
|
|
|
fill: "#000000",
|
|
|
|
|
|
opacity: 1,
|
|
|
|
|
|
underline: false,
|
|
|
|
|
|
overline: false,
|
|
|
|
|
|
linethrough: false,
|
|
|
|
|
|
textBackgroundColor: "transparent",
|
|
|
|
|
|
lineHeight: 1.16,
|
|
|
|
|
|
charSpacing: 0,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async execute() {
|
|
|
|
|
|
if (!this.canvas || !this.layerManager) {
|
|
|
|
|
|
console.error("Canvas或LayerManager不存在");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 保存当前活动图层
|
|
|
|
|
|
this.oldActiveLayerId = this.layerManager.activeLayerId?.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 合并默认选项和用户选项
|
|
|
|
|
|
const finalOptions = {
|
|
|
|
|
|
...this.defaultOptions,
|
|
|
|
|
|
...this.textOptions,
|
|
|
|
|
|
left: this.x,
|
|
|
|
|
|
top: this.y,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await optimizeCanvasRendering(this.canvas, async () => {
|
|
|
|
|
|
// 创建文本对象
|
|
|
|
|
|
this.textObject = new fabric.IText(finalOptions.text, {
|
|
|
|
|
|
...finalOptions,
|
|
|
|
|
|
originX: "center",
|
|
|
|
|
|
originY: "center",
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-11 17:35:00 +08:00
|
|
|
|
// 创建文本图层 取15
|
|
|
|
|
|
const layerName = this.options.text?.substring(0, 25) || t('Canvas.TextLayer');
|
2025-06-22 13:52:28 +08:00
|
|
|
|
const layer = createLayer({
|
|
|
|
|
|
id: this.layerId,
|
|
|
|
|
|
name: layerName,
|
|
|
|
|
|
type: LayerType.TEXT,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 设置对象的图层关联
|
|
|
|
|
|
this.textObject.set({
|
|
|
|
|
|
id: this.textId,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
layerName: layerName,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 智能插入图层到合适位置
|
|
|
|
|
|
this._insertLayerAtCorrectPosition(layer);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到画布
|
|
|
|
|
|
this.canvas.add(this.textObject);
|
|
|
|
|
|
|
|
|
|
|
|
// 取消其他对象的选中状态
|
|
|
|
|
|
this.canvas.discardActiveObject();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置新创建的文本对象为活动对象
|
|
|
|
|
|
this.canvas.setActiveObject(this.textObject);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新图层的对象列表
|
|
|
|
|
|
if (layer) {
|
|
|
|
|
|
layer.fabricObjects = layer.fabricObjects || [];
|
2025-07-14 01:00:23 +08:00
|
|
|
|
layer.fabricObjects.push(this.textObject.toObject(["id", "layerId", "layerName"]));
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 现在可以安全地设置为活动图层
|
|
|
|
|
|
this.layerManager.setActiveLayer(this.layerId);
|
|
|
|
|
|
|
2025-11-06 11:27:12 +08:00
|
|
|
|
// 重新排序图层对象
|
|
|
|
|
|
await this.layerManager?.layerSort?.rearrangeObjects();
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 更新对象交互性
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到选择工具
|
|
|
|
|
|
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-07-14 01:00:23 +08:00
|
|
|
|
console.log(`✅ 文本对象已创建: "${finalOptions.text}",位置: (${this.x}, ${this.y})`);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
return this.textObject;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("创建文本对象失败:", error);
|
|
|
|
|
|
// 如果创建失败,需要清理已创建的资源
|
|
|
|
|
|
await this.undo();
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 智能插入图层到正确位置
|
|
|
|
|
|
* 根据当前激活图层位置确定新图层插入位置
|
|
|
|
|
|
* @param {Object} newLayer 要插入的新图层
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_insertLayerAtCorrectPosition(newLayer) {
|
|
|
|
|
|
const layers = this.layerManager.layers.value;
|
|
|
|
|
|
const currentActiveLayerId = this.layerManager.activeLayerId?.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有当前激活图层,插入到顶部(索引0)
|
|
|
|
|
|
if (!currentActiveLayerId) {
|
|
|
|
|
|
layers.splice(0, 0, newLayer);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找当前激活图层的位置
|
|
|
|
|
|
const {
|
|
|
|
|
|
layer: activeLayer,
|
|
|
|
|
|
parent: parentLayer,
|
|
|
|
|
|
index: activeIndex,
|
|
|
|
|
|
} = this._findLayerPosition(currentActiveLayerId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!activeLayer) {
|
|
|
|
|
|
// 没找到激活图层,插入到顶部
|
|
|
|
|
|
layers.splice(0, 0, newLayer);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确定插入位置
|
|
|
|
|
|
let insertIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (parentLayer) {
|
|
|
|
|
|
// 当前激活图层是子图层
|
|
|
|
|
|
// 在同一父图层内,插入到激活子图层之上
|
|
|
|
|
|
insertIndex = Math.max(0, activeIndex);
|
|
|
|
|
|
parentLayer.children = parentLayer.children || [];
|
|
|
|
|
|
parentLayer.children.splice(insertIndex, 0, newLayer);
|
|
|
|
|
|
|
2025-07-14 01:00:23 +08:00
|
|
|
|
console.log(`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 当前激活图层是一级图层
|
|
|
|
|
|
// 在一级图层中,插入到激活图层之上
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
insertIndex = Math.max(0, activeLayerIndex);
|
|
|
|
|
|
layers.splice(insertIndex, 0, newLayer);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`新图层已插入到一级图层位置: ${insertIndex}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查找图层位置信息
|
|
|
|
|
|
* @param {String} layerId 图层ID
|
|
|
|
|
|
* @returns {Object} 包含图层、父图层和索引的对象
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_findLayerPosition(layerId) {
|
|
|
|
|
|
const layers = this.layerManager.layers.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 先在一级图层中查找
|
|
|
|
|
|
for (let i = 0; i < layers.length; i++) {
|
|
|
|
|
|
const layer = layers[i];
|
2026-01-12 09:42:07 +08:00
|
|
|
|
if (layer.isPrintTrimsGroup) continue;
|
2025-06-22 13:52:28 +08:00
|
|
|
|
if (layer.id === layerId) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
layer: layer,
|
|
|
|
|
|
parent: null,
|
|
|
|
|
|
index: i,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在子图层中查找
|
|
|
|
|
|
if (layer.children && Array.isArray(layer.children)) {
|
|
|
|
|
|
for (let j = 0; j < layer.children.length; j++) {
|
|
|
|
|
|
const childLayer = layer.children[j];
|
|
|
|
|
|
if (childLayer.id === layerId) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
layer: childLayer,
|
|
|
|
|
|
parent: layer,
|
|
|
|
|
|
index: j,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
layer: null,
|
|
|
|
|
|
parent: null,
|
|
|
|
|
|
index: -1,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getInfo() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: this.name,
|
|
|
|
|
|
textId: this.textObject?.id,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
text: this.textOptions.text || this.defaultOptions.text,
|
|
|
|
|
|
position: { x: this.x, y: this.y },
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async undo() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 从画布移除文本对象
|
|
|
|
|
|
if (this.textObject && this.canvas) {
|
|
|
|
|
|
this.canvas.remove(this.textObject);
|
|
|
|
|
|
}
|
2025-10-28 14:02:20 +08:00
|
|
|
|
const layerObjects = getLayerObjectsZIndex(this.canvas, this.layerId);
|
|
|
|
|
|
layerObjects.forEach((obj) => {
|
|
|
|
|
|
if (obj.id === this.textObject?.id) {
|
|
|
|
|
|
this.canvas.remove(obj.object);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 智能移除创建的图层
|
|
|
|
|
|
if (this.layerId && this.layerManager) {
|
|
|
|
|
|
this._removeLayerFromCorrectPosition();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复原活动图层
|
|
|
|
|
|
if (this.oldActiveLayerId && this.layerManager) {
|
|
|
|
|
|
// 检查原活动图层是否还存在
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const originalLayer = this.layerManager.getLayerById(this.oldActiveLayerId);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
if (originalLayer) {
|
|
|
|
|
|
this.layerManager.setActiveLayer(this.oldActiveLayerId);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果原图层不存在,设置为第一个可用的普通图层
|
|
|
|
|
|
const availableLayers = this.layerManager.layers.value.filter(
|
|
|
|
|
|
(layer) => !layer.isBackground && !layer.isFixed && !layer.locked
|
|
|
|
|
|
);
|
|
|
|
|
|
if (availableLayers.length > 0) {
|
|
|
|
|
|
this.layerManager.setActiveLayer(availableLayers[0].id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新对象交互性
|
|
|
|
|
|
await this.layerManager.updateLayersObjectsInteractivity();
|
|
|
|
|
|
|
|
|
|
|
|
// 重新渲染画布
|
|
|
|
|
|
if (this.canvas) {
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`↩️ 文本创建操作已撤销`);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("撤销文本创建操作失败:", error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 智能移除图层
|
|
|
|
|
|
* 根据图层位置(一级图层或子图层)进行相应的移除操作
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_removeLayerFromCorrectPosition() {
|
|
|
|
|
|
const layers = this.layerManager.layers.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找图层位置信息
|
|
|
|
|
|
const positionInfo = this._findLayerPosition(this.layerId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!positionInfo.layer) {
|
|
|
|
|
|
console.warn(`要移除的图层不存在: ${this.layerId}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (positionInfo.parent) {
|
|
|
|
|
|
// 从子图层中移除
|
|
|
|
|
|
if (positionInfo.parent.children && positionInfo.index >= 0) {
|
|
|
|
|
|
positionInfo.parent.children.splice(positionInfo.index, 1);
|
2025-07-14 01:00:23 +08:00
|
|
|
|
console.log(`已从子图层移除: ${this.layerId} (父图层: ${positionInfo.parent.name})`);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 从一级图层中移除
|
|
|
|
|
|
if (positionInfo.index >= 0) {
|
|
|
|
|
|
layers.splice(positionInfo.index, 1);
|
|
|
|
|
|
console.log(`已从一级图层移除: ${this.layerId}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|