画布问题id丢失、撤回...

This commit is contained in:
李志鹏
2025-11-05 16:28:37 +08:00
parent b1bb96b6fd
commit 4030f54334
17 changed files with 182 additions and 74 deletions

View File

@@ -42,13 +42,25 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.firstObj = null; // 用于存储组图层的原始对象 this.firstObj = null; // 用于存储组图层的原始对象
} }
async execute() { async execute(isUndo = false) {
const layer = this.layer; const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
this.layer = layer;
this.parent = parent;
console.log("==========",layer);
if (!layer) return false; if (!layer) return false;
this.oldFill = layer.fill ?? null; if(!isUndo){
this.oldFillColor = layer.oldFillColor ?? null; this.oldFill = layer.fill ?? null;
this.oldFillColor = layer.fillColor ?? null;
if(this.oldFill){
// 移除旧的填充对象
removeCanvasObjectByObject(this.canvas, this.oldFill);
}
}
const fillColor = isUndo ? this.oldFillColor : this.fillColor;
// 构建填充对象 // 构建填充对象
let clippingMaskFabricObject = null; let clippingMaskFabricObject = null;
if (layer.clippingMask) { if (layer.clippingMask) {
@@ -63,7 +75,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
height: clippingMaskFabricObject.height, height: clippingMaskFabricObject.height,
left: clippingMaskFabricObject.left || 0, left: clippingMaskFabricObject.left || 0,
top: clippingMaskFabricObject.top || 0, top: clippingMaskFabricObject.top || 0,
fill: this.fillColor, fill: fillColor,
layerId: this.layerId, layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"), id: this.oldFill?.id || generateId("fill-"),
selectable: false, selectable: false,
@@ -84,7 +96,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
height: originalInfo.height, height: originalInfo.height,
left: originalInfo.left + originalInfo.width / 2 || 0, left: originalInfo.left + originalInfo.width / 2 || 0,
top: originalInfo.top + originalInfo.height / 2 || 0, top: originalInfo.top + originalInfo.height / 2 || 0,
fill: this.fillColor, fill: fillColor,
layerId: this.layerId, layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"), id: this.oldFill?.id || generateId("fill-"),
selectable: false, selectable: false,
@@ -111,7 +123,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
height: clippingMaskFabricObject.height, height: clippingMaskFabricObject.height,
left: clippingMaskFabricObject.left || 0, left: clippingMaskFabricObject.left || 0,
top: clippingMaskFabricObject.top || 0, top: clippingMaskFabricObject.top || 0,
fill: this.fillColor, fill: fillColor,
layerId: this.layerId, layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"), id: this.oldFill?.id || generateId("fill-"),
selectable: false, selectable: false,
@@ -127,7 +139,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
height: this.canvasManager?.canvasHeight?.value || this.canvas.height, height: this.canvasManager?.canvasHeight?.value || this.canvas.height,
left: this.canvas.width / 2 || 0, left: this.canvas.width / 2 || 0,
top: this.canvas.height / 2 || 0, top: this.canvas.height / 2 || 0,
fill: this.fillColor, fill: fillColor,
layerId: this.layerId, layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"), id: this.oldFill?.id || generateId("fill-"),
selectable: false, selectable: false,
@@ -143,8 +155,8 @@ export class FillGroupLayerBackgroundCommand extends Command {
layer.fabricObjects = [ layer.fabricObjects = [
this.newFill.toObject(["id", "layerId"]) || this.newFill, this.newFill.toObject(["id", "layerId"]) || this.newFill,
]; ];
layer.fill = null; // this.newFill.toObject(["id", "layerId"]); layer.fill = this.newFill; // this.newFill.toObject(["id", "layerId"]);
layer.fillColor = this.fillColor; layer.fillColor = fillColor;
// 取消激活对象 // 取消激活对象
this.canvas.discardActiveObject(); // 取消当前活动对象 this.canvas.discardActiveObject(); // 取消当前活动对象
@@ -222,8 +234,8 @@ export class FillGroupLayerBackgroundCommand extends Command {
// this.group?.addWithUpdate?.(); // this.group?.addWithUpdate?.();
// layer.fabricObjects = [this.group?.toObject?.(["id", "layerId"]) || this.group]; // layer.fabricObjects = [this.group?.toObject?.(["id", "layerId"]) || this.group];
// this.canvas.renderAll(); // this.canvas.renderAll();
layer.fill = null; // this.newFill.toObject(["id", "layerId"]); layer.fill = this.newFill; // this.newFill.toObject(["id", "layerId"]);
layer.fillColor = this.fillColor; layer.fillColor = fillColor;
// 取消激活对象 // 取消激活对象
this.canvas.discardActiveObject(); // 取消当前活动对象 this.canvas.discardActiveObject(); // 取消当前活动对象
@@ -237,14 +249,11 @@ export class FillGroupLayerBackgroundCommand extends Command {
return true; return true;
} }
async undo() { async undo() {
this.layer.fillColor = this.oldFillColor; if (!this.originalInfo && this.layer.fill) {
this.layer.fill = this.oldFill;
if (!this.originalInfo && this.firstObj) {
this.canvas.discardActiveObject(); this.canvas.discardActiveObject();
this.canvas.remove(this.firstObj); this.canvas.remove(this.layer.fill);
this.canvas.renderAll(); this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail( this.parent && this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.parent.id this.parent.id
); );
this.canvasManager.thumbnailManager?.generateLayerThumbnail( this.canvasManager.thumbnailManager?.generateLayerThumbnail(
@@ -264,7 +273,8 @@ export class FillGroupLayerBackgroundCommand extends Command {
canvasObj?._objects?.length > 0 canvasObj?._objects?.length > 0
) { ) {
// 移除新添加的填充对象 // 移除新添加的填充对象
if (canvasObj._objects?.[0] === this.newFill) { // if (canvasObj._objects?.[0] === this.newFill) {
if (/^fill-/.test(canvasObj._objects?.[0]?.id)) {
canvasObj._objects.shift(); canvasObj._objects.shift();
canvasObj.addWithUpdate(); canvasObj.addWithUpdate();
canvasObj.setCoords(); canvasObj.setCoords();
@@ -284,6 +294,14 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.canvas.renderAll(); this.canvas.renderAll();
this.group = null; this.group = null;
} }
if(this.oldFill){
this.layer.fill = this.oldFill;
this.layer.fillColor = this.oldFillColor;
return this.execute(true);
}else{
this.layer.fill = null;
this.layer.fillColor = null;
}
this.canvas.discardActiveObject(); // 取消当前活动对象 this.canvas.discardActiveObject(); // 取消当前活动对象
// 重新排序 // 重新排序

View File

@@ -70,7 +70,7 @@ export class AddLayerCommand extends Command {
undo() { undo() {
// 从图层列表删除该图层 // 从图层列表删除该图层
this.layers.value = this.beforeLayers; this.layers.value = [...this.beforeLayers];
// 恢复原活动图层 // 恢复原活动图层
this.activeLayerId.value = this.oldActiveLayerId; this.activeLayerId.value = this.oldActiveLayerId;
@@ -563,6 +563,12 @@ export class RemoveLayerCommand extends Command {
allObjects.push(object); allObjects.push(object);
} }
} }
layer.fabricObjects?.forEach((fabric) => {
const { object } = findObjectById(this.canvas, fabric.id);
if (object && !allObjects.includes(object)) {
allObjects.push(object);
}
});
// 递归收集子图层的对象 // 递归收集子图层的对象
if (layer.children && Array.isArray(layer.children)) { if (layer.children && Array.isArray(layer.children)) {
@@ -609,7 +615,7 @@ export class RemoveLayerCommand extends Command {
// this.canvas.renderAll(); // this.canvas.renderAll();
// } // }
await this.layerManager?.updateLayersObjectsInteractivity?.(); await this.layerManager?.updateLayersObjectsInteractivity?.();
// this.canvas.renderAll(); this.canvas.renderAll();
console.log( console.log(
`✅ 已移除图层: ${this.removedLayer.name} (ID: ${this.layerId}),包含 ${this.originalObjects.length} 个对象` `✅ 已移除图层: ${this.removedLayer.name} (ID: ${this.layerId}),包含 ${this.originalObjects.length} 个对象`
@@ -2013,7 +2019,7 @@ export class LayerObjectsToGroupCommand extends Command {
console.error("图层或Canvas未初始化"); console.error("图层或Canvas未初始化");
return null; return null;
} }
this.activeLayer = this.layerManager.getActiveLayer();
// 查找图层中是否已有组对象 // 查找图层中是否已有组对象
const existingGroup = this._findExistingGroup(); const existingGroup = this._findExistingGroup();
this.existingGroupId = existingGroup?.id || null; this.existingGroupId = existingGroup?.id || null;
@@ -2067,10 +2073,10 @@ export class LayerObjectsToGroupCommand extends Command {
} }
// 更新交互性 // 更新交互性
this.layerManager?.updateLayersObjectsInteractivity?.(false); this.layerManager?.updateLayersObjectsInteractivity?.(false).then(()=>{
// 更新缩略图
// 更新缩略图 this._updateThumbnail();
this._updateThumbnail(); });
}); });
// 标记为非首次执行 // 标记为非首次执行
@@ -2220,7 +2226,7 @@ export class LayerObjectsToGroupCommand extends Command {
async undo() { async undo() {
if (!this.activeLayer || !this.canvas || !this.groupObjectId) return; if (!this.activeLayer || !this.canvas || !this.groupObjectId) return;
this.activeLayer = this.layerManager.getActiveLayer();
try { try {
await optimizeCanvasRendering(this.canvas, async () => { await optimizeCanvasRendering(this.canvas, async () => {
if (this.wasGroupCreated) { if (this.wasGroupCreated) {
@@ -2338,7 +2344,7 @@ export class LayerObjectsToGroupCommand extends Command {
// 将对象添加回画布 // 将对象添加回画布
this.canvas.add(obj); this.canvas.add(obj);
restoredObjects.push(obj); restoredObjects.push(obj.toObject("id", "layerId", "layerName"));
console.log( console.log(
`✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${ `✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${
@@ -2537,9 +2543,9 @@ export class LayerObjectsToGroupCommand extends Command {
* @private * @private
*/ */
_updateThumbnail() { _updateThumbnail() {
// this.canvas?.thumbnailManager?.generateLayerThumbnail?.( this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
// this.activeLayer.id this.activeLayer.id
// ); );
} }
getInfo() { getInfo() {

View File

@@ -343,7 +343,7 @@ export class RasterizeLayerCommand extends Command {
visible: this.layer.visible, visible: this.layer.visible,
locked: this.layer.locked, locked: this.layer.locked,
opacity: this.layer.opacity, opacity: this.layer.opacity,
fabricObjects: [rasterizedImage], fabricObjects: [],
}); });
// 更新图像对象的图层关联 // 更新图像对象的图层关联
@@ -353,7 +353,7 @@ export class RasterizeLayerCommand extends Command {
layerId: this.rasterizedLayerId, layerId: this.rasterizedLayerId,
layerName: this.rasterizedLayer.name, layerName: this.rasterizedLayer.name,
}); });
this.rasterizedLayer.fabricObjects = [rasterizedImage.toObject(["id", "layerId", "layerName"])]
// 在适当位置添加新的组合图层 // 在适当位置添加新的组合图层
this._replaceLayerInStructure(); this._replaceLayerInStructure();

View File

@@ -1,4 +1,4 @@
import { generateId, optimizeCanvasRendering } from "../utils/helper"; import { generateId, optimizeCanvasRendering, getLayerObjectsZIndex } from "../utils/helper";
import { createLayer, LayerType, OperationType } from "../utils/layerHelper"; import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
import { Command } from "./Command"; import { Command } from "./Command";
import i18n from "@/lang/index.ts"; import i18n from "@/lang/index.ts";
@@ -536,6 +536,12 @@ export class CreateTextCommand extends Command {
if (this.textObject && this.canvas) { if (this.textObject && this.canvas) {
this.canvas.remove(this.textObject); this.canvas.remove(this.textObject);
} }
const layerObjects = getLayerObjectsZIndex(this.canvas, this.layerId);
layerObjects.forEach((obj) => {
if (obj.id === this.textObject?.id) {
this.canvas.remove(obj.object);
}
});
// 智能移除创建的图层 // 智能移除创建的图层
if (this.layerId && this.layerManager) { if (this.layerId && this.layerManager) {

View File

@@ -488,7 +488,7 @@ watch(
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / 10px 10px; background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% / 10px 10px;
border-radius: 6px; border-radius: 6px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 5px; margin-bottom: 5px;
@@ -518,7 +518,7 @@ watch(
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / 10px 10px; background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% / 10px 10px;
} }
.opacity-color { .opacity-color {
@@ -542,7 +542,7 @@ watch(
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: #333; color: #333;
background: #f5f5f5; background: #F9FAFA;
padding: 2px 8px; padding: 2px 8px;
border-radius: 4px; border-radius: 4px;
min-width: 50px; min-width: 50px;

View File

@@ -1938,7 +1938,7 @@ const brushStore = BrushStore;
} }
.shadow-preview-box { .shadow-preview-box {
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / 10px 10px; background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% / 10px 10px;
border-radius: 8px; border-radius: 8px;
padding: 30px; padding: 30px;
display: flex; display: flex;

View File

@@ -484,7 +484,7 @@ h3 {
} }
.shortcuts-table th { .shortcuts-table th {
background-color: #f5f5f5; background-color: #F9FAFA;
} }
.touch-tips { .touch-tips {

View File

@@ -42,7 +42,7 @@
position: relative; position: relative;
&:hover:not(.disabled) { &:hover:not(.disabled) {
background-color: #f5f5f5; background-color: #F9FAFA;
} }
&:active:not(.disabled) { &:active:not(.disabled) {
@@ -76,7 +76,7 @@
} }
&.hovered { &.hovered {
background-color: #f5f5f5; background-color: #F9FAFA;
} }
} }

View File

@@ -61,12 +61,12 @@
.action-btn[disabled] { .action-btn[disabled] {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
} }
.action-btn[disabled]:hover { .action-btn[disabled]:hover {
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
} }
@@ -196,7 +196,7 @@
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
flex: none; flex: none;
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / 1rem 1rem; background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% / 1rem 1rem;
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
} }
.layer-thumbnail { .layer-thumbnail {

View File

@@ -72,12 +72,12 @@
&[disabled] { &[disabled] {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
&:hover { &:hover {
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
} }
@@ -244,7 +244,7 @@
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
flex: none; flex: none;
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% /
1rem 1rem; 1rem 1rem;
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
} }

View File

@@ -60,12 +60,12 @@
.action-btn[disabled] { .action-btn[disabled] {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
} }
.action-btn[disabled]:hover { .action-btn[disabled]:hover {
background-color: #f5f5f5; background-color: #F9FAFA;
color: #bfbfbf; color: #bfbfbf;
border-color: #e6e6e6; border-color: #e6e6e6;
} }
@@ -195,7 +195,7 @@
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
flex: none; flex: none;
background: repeating-conic-gradient(#f5f5f5 0% 25%, #ffffff 0% 50%) 50% / 1rem 1rem; background: repeating-conic-gradient(#F9FAFA 0% 25%, #ffffff 0% 50%) 50% / 1rem 1rem;
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
} }
.layer-thumbnail { .layer-thumbnail {

View File

@@ -114,6 +114,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
hideCanvas: {
type: Boolean,
default: false, // 是否隐藏画布-隐藏关闭部分功能
},
}); });
// 引用和状态 // 引用和状态
@@ -171,6 +175,15 @@ function toggleShortcutHelp() {
showShortcutHelp.value = !showShortcutHelp.value; showShortcutHelp.value = !showShortcutHelp.value;
} }
watch(()=>props.hideCanvas, (newVal)=>{
console.log("==========是否隐藏画布",newVal)
if(newVal){
keyboardManager?.removeEvents()
}else {
keyboardManager?.init()
}
})
// 工具选择处理 // 工具选择处理
function handleToolSelect(tool) { function handleToolSelect(tool) {
activeTool.value = tool; activeTool.value = tool;
@@ -693,25 +706,26 @@ function removeLayer(layerId) {
return; return;
} }
if (canvasManager && canvasManager.canvas) {
const layerToRemove = layers.value.find((l) => l.id === layerId);
if (layerToRemove) {
const elementIds = layerToRemove?.fabricObjects?.map((e) => e.id);
elementIds.forEach((elementId) => {
const objectToRemove = canvasManager.canvas
.getObjects()
.find((obj) => obj.id === elementId);
if (objectToRemove) {
canvasManager.canvas.remove(objectToRemove);
}
});
if (activeLayerId.value === layerId) {
activeElementId.value = null;
}
canvasManager.canvas.renderAll();
}
}
layerManager.removeLayer(layerId); layerManager.removeLayer(layerId);
// 此处删除画布上内容导致撤回操作无效(多余)
// if (canvasManager && canvasManager.canvas) {
// const layerToRemove = layers.value.find((l) => l.id === layerId);
// if (layerToRemove) {
// const elementIds = layerToRemove?.fabricObjects?.map((e) => e.id);
// elementIds.forEach((elementId) => {
// const objectToRemove = canvasManager.canvas
// .getObjects()
// .find((obj) => obj.id === elementId);
// if (objectToRemove) {
// canvasManager.canvas.remove(objectToRemove);
// }
// });
// if (activeLayerId.value === layerId) {
// activeElementId.value = null;
// }
// canvasManager.canvas.renderAll();
// }
// }
} }
function triggerImageUpload() { function triggerImageUpload() {

View File

@@ -1819,7 +1819,8 @@ export class LayerManager {
} }
// 检查是否是唯一的普通图层 // 检查是否是唯一的普通图层
const normalLayers = this.layers.value.filter((l) => !l.isBackground); const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed);
console.log("普通图层:", normalLayers)
if (normalLayers.length === 1) { if (normalLayers.length === 1) {
console.warn("不能剪切唯一的普通图层"); console.warn("不能剪切唯一的普通图层");
return null; return null;

View File

@@ -69,7 +69,7 @@ export class AnimationManager {
// 如果变化太小,直接应用缩放 // 如果变化太小,直接应用缩放
if (Math.abs(targetZoom - currentZoom) < 0.01) { if (Math.abs(targetZoom - currentZoom) < 0.01) {
this._applyZoom(point, targetZoom); // this._applyZoom(point, targetZoom);
return; return;
} }
@@ -121,7 +121,7 @@ export class AnimationManager {
this._zoomAnimation = null; this._zoomAnimation = null;
// 确保最终状态准确 // 确保最终状态准确
this._applyZoom(point, targetZoom, true); // this._applyZoom(point, targetZoom, true);
}, },
}; };
@@ -817,7 +817,7 @@ export class AnimationManager {
this._wasZooming = false; this._wasZooming = false;
// 确保最终状态准确 // 确保最终状态准确
this._applyZoom(point, targetZoom, true); // this._applyZoom(point, targetZoom, true);
}, },
}); });
} }

View File

@@ -215,6 +215,25 @@ export class KeyboardManager {
console.log(`键盘管理器已初始化,平台: ${this.platform}, 触摸设备: ${this.isTouchDevice}`); console.log(`键盘管理器已初始化,平台: ${this.platform}, 触摸设备: ${this.isTouchDevice}`);
} }
/**
* hide模式下关闭快捷键
*/
removeEvents() {
// 移除键盘事件监听
this.container.removeEventListener("keydown", this._handleKeyDown);
this.container.removeEventListener("keyup", this._handleKeyUp);
// 如果是触摸设备,移除触摸事件监听
if (this.isTouchDevice) {
this.container.removeEventListener("touchstart", this._handleTouchStart);
this.container.removeEventListener("touchmove", this._handleTouchMove);
this.container.removeEventListener("touchend", this._handleTouchEnd);
this.container.removeEventListener("touchcancel", this._handleTouchEnd);
}
}
/** /**
* 处理键盘按下事件 * 处理键盘按下事件
* @param {KeyboardEvent} event 键盘事件 * @param {KeyboardEvent} event 键盘事件

View File

@@ -159,6 +159,7 @@ onUnmounted(() => {
white-space: nowrap; white-space: nowrap;
font-size: 1.2rem; font-size: 1.2rem;
z-index: 9999; z-index: 9999;
pointer-events: none;
} }
.tool-tooltip:before { .tool-tooltip:before {

View File

@@ -157,7 +157,30 @@ function canvasProject() {
console.warn("没有找到保存的画布项目"); console.warn("没有找到保存的画布项目");
} }
} }
const exportJSON = () => {
console.log("导出JSON");
// 实现导出JSON逻辑
const json = canvasEditor.value.getJSON();
// 导出JSON文件
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "canvas_project.json";
a.click();
URL.revokeObjectURL(url);
};
// 复制JSON
const copyJSON = () => {
console.log("复制JSON");
// 实现复制JSON逻辑
const json = canvasEditor.value.getJSON();
// 复制JSON到剪贴板
navigator.clipboard.writeText(json);
};
const getLayers = ()=>{
console.log("==========layers",canvasEditor.value?.layers)
}
// 处理自定义工具点击 // 处理自定义工具点击
const handleCustomToolClick = (tool) => { const handleCustomToolClick = (tool) => {
tool.action(); tool.action();
@@ -255,6 +278,26 @@ const isShowLeft = ref(true);
<span></span> <span></span>
<div class="tool-tooltip">更换底图</div> <div class="tool-tooltip">更换底图</div>
</div> </div>
<div class="custom-tool-btn" @click="saveCanvas">
<span></span>
<div class="tool-tooltip">保存画布</div>
</div>
<div class="custom-tool-btn" @click="canvasProject">
<span></span>
<div class="tool-tooltip">读取画布</div>
</div>
<div class="custom-tool-btn" @click="exportJSON">
<span></span>
<div class="tool-tooltip">导出JSON</div>
</div>
<div class="custom-tool-btn" @click="copyJSON">
<span></span>
<div class="tool-tooltip">复制JSON</div>
</div>
<div class="custom-tool-btn" @click="getLayers">
<span></span>
<div class="tool-tooltip">查询图层</div>
</div>
</template> </template>
</CanvasEditor> </CanvasEditor>
</div> </div>