Files
aida_front/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersPanel.vue
bighuixiang afa3b69f71 feat: 1.固定图层缩略图(完成)
2.工具栏新增插槽(完成)
3.loadJSON的元素顺序回发生错误
2025-06-25 01:03:39 +08:00

1362 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { computed, ref, nextTick, inject } from "vue";
import { findLayerRecursively, isGroupLayer } from "../../utils/layerHelper";
import ContextMenu from "./ContextMenu.vue";
import LayerItem from "./LayerItem.vue";
import LayersList from "./LayersList.vue"; // 引入 LayersList 组件
// // 导入命令类
// import {
// ReorderLayersCommand,
// // ReorderChildLayersCommand,
// GroupLayersCommand,
// UngroupLayersCommand,
// } from "../../commands/LayerCommands";
import { generateId } from "../../utils/helper";
const props = defineProps({
// layers: Array,
activeLayerId: String,
activeElementId: String,
thumbnailManager: Object, // 添加缩略图管理器属性
showFixedLayer: {
type: Boolean,
default: false, // 默认不显示固定层
},
});
const emit = defineEmits([
"add-layer",
"add-group-layer",
"set-active-layer",
"toggle-layer-visibility",
"move-layer-up",
"move-layer-down",
"remove-layer",
"reorder-layers", // 新增:图层重新排序事件
"reorder-child-layers", // 新增:子图层重新排序事件
]);
const layers = inject("layers", []);
const layerManager = inject("layerManager", {});
// 注入命令管理器
const commandManager = inject("commandManager", null);
// 编辑状态管理
const editingLayerId = ref(null);
const editingLayerName = ref("");
// 多选状态管理
const selectedLayerIds = ref([]);
const isMultiSelectMode = ref(false);
const lastSelectedIndex = ref(-1);
// 组图层展开/收起状态管理
const expandedGroupIds = ref(new Set());
// 长按检测(移动端)
const touchTimer = ref(null);
const touchStartPos = ref({ x: 0, y: 0 });
const LONG_PRESS_DELAY = 500; // 长按触发时间(毫秒)
const TOUCH_MOVE_THRESHOLD = 10; // 触摸移动阈值(像素)
// 右键菜单状态管理
const contextMenuVisible = ref(false);
const contextMenuPosition = ref({ x: 0, y: 0 });
const contextMenuLayer = ref(null);
const contextMenuItems = ref([]);
// 计算属性:可排序的根级图层(排除背景层和固定层)
const sortableRootLayers = computed(() => {
if (!layers) return [];
return layers.value.filter(
(layer) => !layer.parentId && !layer.isFixed && !layer.isBackground
);
});
// 计算属性:不可排序的固定图层(背景层和固定层)
const fixedLayers = computed(() => {
if (!layers) return [];
return layers.value.filter((layer) => {
if (props.showFixedLayer)
return !layer.parentId && (layer.isFixed || layer.isBackground);
return !layer.parentId && layer.isBackground; // 只显示背景层,不显示固定层 - 固定层用来做红绿图模式 和 放模特
});
});
// 计算属性:获取当前选中的图层
const selectedLayers = computed(() => {
return sortableRootLayers.value.filter((layer) =>
selectedLayerIds.value.includes(layer.id)
);
});
// 计算属性:检查是否有选中的图层可以分组
const canGroupLayers = computed(() => {
// 直接基于 selectedLayerIds 和 sortableRootLayers 计算,确保响应式
if (selectedLayerIds.value.length < 2) return false;
const selectedLayers = sortableRootLayers.value.filter((layer) =>
selectedLayerIds.value.includes(layer.id)
);
return selectedLayers.every(
(layer) => !layer.isBackground && !layer.isFixed && !isGroupLayerType(layer)
);
});
// 计算属性:检查是否有选中的组图层可以解组
const canUngroupLayers = computed(() => {
if (selectedLayerIds.value.length !== 1) return false;
const selectedLayers = sortableRootLayers.value.filter((layer) =>
selectedLayerIds.value.includes(layer.id)
);
return selectedLayers.length === 1 && isGroupLayerType(selectedLayers[0]);
});
// 计算属性:可以复制的图层
const canCopyLayers = computed(() => {
if (selectedLayerIds.value.length === 0) return false;
const selectedLayers = sortableRootLayers.value.filter((layer) =>
selectedLayerIds.value.includes(layer.id)
);
return selectedLayers.every((layer) => !layer.isBackground && !layer.isFixed);
});
// 计算属性:可以锁定/解锁的图层
const canToggleLock = computed(() => {
if (selectedLayerIds.value.length === 0) return false;
const selectedLayers = sortableRootLayers.value.filter((layer) =>
selectedLayerIds.value.includes(layer.id)
);
return selectedLayers.every((layer) => !layer.isBackground && !layer.isFixed);
});
function addLayer() {
emit("add-layer");
}
function addGroupLayer() {
emit("add-group-layer");
}
function setActiveLayer(layerId) {
emit("set-active-layer", layerId);
}
function toggleLayerVisibility(layerId) {
emit("toggle-layer-visibility", layerId);
}
function moveLayerUp(layerId) {
emit("move-layer-up", layerId);
}
function moveLayerDown(layerId) {
emit("move-layer-down", layerId);
}
function removeLayer(layerId, isDisabled = false) {
if (isDisabled) return;
emit("remove-layer", layerId);
}
function renameLayer(layerId, newName) {
// emit("rename-layer", layerId, newName);
layerManager.renameLayer(layerId, newName);
}
// 修复复选框点击逻辑,支持单击取消或选中,不退出多选模式
const handleCheckboxClick = (layerId, event) => {
// 阻止事件冒泡,避免触发父元素的点击事件
if (event) {
event.stopPropagation();
event.preventDefault();
}
const index = selectedLayerIds.value.indexOf(layerId);
if (index > -1) {
selectedLayerIds.value.splice(index, 1); // 取消选中
} else {
selectedLayerIds.value.push(layerId); // 选中
}
// 更新多选模式状态
isMultiSelectMode.value = selectedLayerIds.value.length > 0;
// 如果有选中的图层,设置最后一个为活动图层
// if (selectedLayerIds.value.length > 0) {
// setActiveLayer(selectedLayerIds.value[selectedLayerIds.value.length - 1]);
// }
};
// 检查图层是否被选中
const isLayerSelected = (layerId) => {
return selectedLayerIds.value.includes(layerId);
};
// 多选相关方法
function toggleLayerSelection(layer, event) {
console.log("layer, event", layer, event);
// 防止在编辑模式下触发选择
if (editingLayerId.value === layer.id) {
return;
}
const isShift = event.shiftKey;
const layerIndex = sortableRootLayers.value.findIndex(
(l) => l.id === layer.id
);
console.log("isShift", isShift);
if (isShift && lastSelectedIndex.value !== -1) {
// Shift + 点击:范围选择
selectLayerRange(lastSelectedIndex.value, layerIndex);
} else {
isMultiSelectMode.value = true;
// 普通点击或Ctrl/Cmd + 点击:切换单个图层选择状态
const index = selectedLayerIds.value.indexOf(layer.id);
if (index > -1) {
selectedLayerIds.value.splice(index, 1);
} else {
selectedLayerIds.value.push(layer.id);
}
lastSelectedIndex.value = layerIndex;
// 设置活动图层为最后选中的图层
// if (selectedLayerIds.value.includes(layer.id)) {
// setActiveLayer(layer.id);
// } else if (selectedLayerIds.value.length > 0) {
// // 如果取消选中当前活动图层,设置最后一个选中的图层为活动图层
// setActiveLayer(selectedLayerIds.value[selectedLayerIds.value.length - 1]);
// }
}
// 始终保持多选模式状态
// isMultiSelectMode.value = selectedLayerIds.value.length > 0;
}
function selectLayerRange(startIndex, endIndex) {
const start = Math.min(startIndex, endIndex);
const end = Math.max(startIndex, endIndex);
selectedLayerIds.value = [];
for (let i = start; i <= end; i++) {
if (sortableRootLayers.value[i]) {
selectedLayerIds.value.push(sortableRootLayers.value[i].id);
}
}
isMultiSelectMode.value = selectedLayerIds.value.length > 1;
}
function selectAllLayers() {
selectedLayerIds.value = [];
sortableRootLayers.value.forEach((layer) => {
if (!layer.isBackground && !layer.isFixed) {
selectedLayerIds.value.push(layer.id);
}
});
isMultiSelectMode.value = selectedLayerIds.value.length > 1;
}
function clearSelection() {
selectedLayerIds.value = [];
isMultiSelectMode.value = false;
lastSelectedIndex.value = -1;
}
// 保留原函数作为向后兼容,但现在直接返回计算属性的值
function getSelectedLayers() {
return selectedLayers.value;
}
// 移动端长按多选支持
function handleTouchStart(event, layer) {
if (editingLayerId.value === layer.id) return;
const touch = event.touches[0];
touchStartPos.value = { x: touch.clientX, y: touch.clientY };
touchTimer.value = setTimeout(() => {
// 长按触发多选
handleLongPress(layer);
}, LONG_PRESS_DELAY);
}
function handleTouchMove(event) {
if (touchTimer.value) {
const touch = event.touches[0];
const deltaX = Math.abs(touch.clientX - touchStartPos.value.x);
const deltaY = Math.abs(touch.clientY - touchStartPos.value.y);
// 如果移动距离超过阈值,取消长按
if (deltaX > TOUCH_MOVE_THRESHOLD || deltaY > TOUCH_MOVE_THRESHOLD) {
clearTimeout(touchTimer.value);
touchTimer.value = null;
}
}
}
function handleTouchEnd() {
if (touchTimer.value) {
clearTimeout(touchTimer.value);
touchTimer.value = null;
}
}
function handleLongPress(layer) {
// 长按进入多选模式
if (!selectedLayerIds.value.includes(layer.id)) {
selectedLayerIds.value.push(layer.id);
}
isMultiSelectMode.value = true;
// 提供触觉反馈(如果支持)
if (navigator.vibrate) {
navigator.vibrate(50);
}
}
// 分组操作
async function groupSelectedLayers() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length < 2) {
console.warn("至少需要选择两个图层才能分组");
return;
}
// 检查是否包含不能分组的图层
const invalidLayers = selectedLayers.filter(
(layer) => layer.isBackground || layer.isFixed || isGroupLayerType(layer)
);
if (invalidLayers.length > 0) {
console.warn("选择的图层中包含背景层、固定层或组图层,无法分组");
return;
}
try {
const layerIds = selectedLayers.map((layer) => layer.id);
const groupName = generateId("GroupLayer_");
await layerManager?.groupLayers(layerIds, groupName);
console.log(`✅ 成功创建图层组: ${groupName}`);
// 清除选择状态
clearSelection();
} catch (error) {
console.error("❌ 创建图层组失败:", error);
}
}
// 解组操作
async function ungroupSelectedLayer() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length !== 1) {
console.warn("只能选择一个组图层进行解组");
return;
}
const groupLayer = selectedLayers[0];
if (!isGroupLayerType(groupLayer)) {
console.warn("选择的图层不是组图层");
return;
}
try {
const childLayerIds = await layerManager?.ungroupLayers(groupLayer.id);
console.log(
`✅ 成功解组图层组: ${groupLayer.name}, 子图层: ${childLayerIds}`
);
// 清除选择状态
clearSelection();
} catch (error) {
console.error("❌ 解组图层失败:", error);
}
}
// 删除选中的图层
function deleteSelectedLayers() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length === 0) {
console.warn("没有选择要删除的图层");
return;
}
// 检查是否包含不能删除的图层
const undeletableLayers = selectedLayers.filter(
(layer) => layer.isBackground || layer.isFixed
);
if (undeletableLayers.length > 0) {
console.warn("选择的图层中包含背景层或固定层,无法删除");
return;
}
// 检查删除后是否还有足够的普通图层
const remainingNormalLayers = layers.value.filter(
(layer) =>
!layer.isBackground &&
!layer.isFixed &&
!selectedLayerIds.value.includes(layer.id)
).length;
if (remainingNormalLayers < 1) {
console.warn("不能删除所有普通图层");
return;
}
// 确认删除
if (selectedLayers.length > 1) {
if (!confirm(`确定要删除选中的 ${selectedLayers.length} 个图层吗?`)) {
return;
}
}
// 删除图层
selectedLayers.forEach((layer) => {
removeLayer(layer.id);
});
// 清除选择状态
clearSelection();
}
// 双击重命名相关方法
function handleLayerDoubleClick(layer) {
// 不允许重命名背景层和固定层
if (layer.isBackground || layer.isFixed) {
return;
}
startEditing(layer);
}
function startEditing(layer) {
editingLayerId.value = layer.id;
editingLayerName.value = layer.name;
// 下一帧聚焦输入框并选中文本
nextTick(() => {
const inputElement = document.querySelector(
`input[data-layer-id="${layer.id}"]`
);
if (inputElement) {
inputElement.focus();
inputElement.select();
}
});
}
function confirmEdit() {
if (editingLayerId.value && editingLayerName.value.trim()) {
const trimmedName = editingLayerName.value.trim();
renameLayer(editingLayerId.value, trimmedName);
}
cancelEdit();
}
function cancelEdit() {
editingLayerId.value = null;
editingLayerName.value = "";
}
function handleEditKeydown(event) {
if (event.key === "Enter") {
event.preventDefault();
confirmEdit();
} else if (event.key === "Escape") {
event.preventDefault();
cancelEdit();
}
}
// 获取图层缩略图URL
function getLayerThumbnail(layerId) {
if (props.thumbnailManager) {
return props.thumbnailManager.getLayerThumbnail(layerId);
}
return null;
}
// 获取图层类型图标
function getLayerTypeIcon(layer) {
if (!layer) return "🖼️";
if (isGroupLayer(layer)) {
return "📁";
}
if (layer.fabricObject) {
switch (layer.fabricObject.type) {
case "image":
return "🖼️";
case "text":
return "📝";
case "rect":
return "▢";
case "circle":
return "⬤";
case "path":
return "✎";
default:
return "⬤";
}
}
return "🖼️";
}
// 获取图层的子图层
function getChildLayers(parentId) {
if (!layers || !parentId) return [];
return layers.value.filter((layer) => layer.parentId === parentId);
}
// 检查图层是否为分组
function isGroupLayerType(layer) {
return isGroupLayer(layer);
}
// 计算属性:检查组图层是否展开
const isGroupExpanded = (groupId) => {
return expandedGroupIds.value.has(groupId);
};
// 切换组图层展开/收起状态
const toggleGroupExpanded = (groupId) => {
if (expandedGroupIds.value.has(groupId)) {
expandedGroupIds.value.delete(groupId);
} else {
expandedGroupIds.value.add(groupId);
}
// 触发响应式更新
expandedGroupIds.value = new Set(expandedGroupIds.value);
};
// 渲染单个图层项(递归组件)
function renderLayerItem(layer, index) {
if (!layer) return null;
const isGroup = isGroupLayerType(layer);
const children = isGroup ? getChildLayers(layer.id) : [];
return {
id: layer.id,
name: layer.name,
isGroup: isGroup,
children: children,
fabricObject: layer.fabricObject,
visible: layer.visible,
};
}
// 处理图层点击事件
function handleLayerClick(layer, event) {
// 阻止事件冒泡
event.stopPropagation();
// 如果按住修饰键,执行多选逻辑
if (
event.ctrlKey ||
event.metaKey ||
event.shiftKey ||
isMultiSelectMode.value
) {
toggleLayerSelection(layer, event);
} else {
// 普通点击:进入单选模式
// selectedLayerIds.value = [layer.id];
// isMultiSelectMode.value = false;
if (!isMultiSelectMode.value) {
// 如果不是多选模式,才可激活图层
// 1.如果是组,则设置组下的第一个子图层为活动图层
// 2.否则直接设置活动图层
if (
isGroupLayerType(layer) &&
layer.children &&
layer.children.length > 0
) {
// 如果是组图层,设置第一个子图层为活动图层
setActiveLayer(layer.children[0].id, { parentId: layer.id });
} else {
// 否则直接设置当前图层为活动图层
setActiveLayer(layer.id);
}
}
lastSelectedIndex.value = sortableRootLayers.value.findIndex(
(l) => l.id === layer.id
);
}
}
// 右键菜单相关方法
function showContextMenu(event, layer) {
event.preventDefault();
event.stopPropagation();
// 如果右键的图层不在选中列表中,先选中它
if (!selectedLayerIds.value.includes(layer.id)) {
selectedLayerIds.value = [layer.id];
isMultiSelectMode.value = false;
setActiveLayer(layer.id);
}
contextMenuLayer.value = layer;
contextMenuPosition.value = { x: event.clientX, y: event.clientY };
// 构建菜单项
buildContextMenuItems(layer);
contextMenuVisible.value = true;
}
function hideContextMenu() {
contextMenuVisible.value = false;
contextMenuLayer.value = null;
contextMenuItems.value = [];
}
function buildContextMenuItems(layer) {
const selectedLayers = getSelectedLayers();
const isMultiple = selectedLayers.length > 1;
const isGroupLayer = isGroupLayerType(layer);
contextMenuItems.value = [
// 重命名
{
label: "重命名",
icon: "CFont",
disabled: isMultiple || layer.isBackground || layer.isFixed,
action: () => startEditing(layer),
},
{ type: "divider" },
// 复制
{
label: isMultiple ? `复制 ${selectedLayers.length} 个图层` : "复制图层",
icon: "CPaste",
disabled: !canCopyLayers.value,
action: () => copySelectedLayers(),
},
// 删除
{
label: isMultiple ? `删除 ${selectedLayers.length} 个图层` : "删除图层",
icon: "CDelete",
disabled: layer.isBackground || layer.isFixed || !canDeleteLayers(),
danger: true,
action: () => deleteSelectedLayers(),
},
// 栅格化图层
{
label: "栅格化图层",
icon: "CPicture",
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canRasterizeLayer?.(layer.id),
action: () => {
rasterizeLayer(layer.id);
hideContextMenu();
},
},
// 栅格化图层
{
label: "导出图层",
icon: "CExport",
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canRasterizeLayer?.(layer.id),
action: () => {
exportLayerToImage(layer.id);
hideContextMenu();
},
},
{ type: "divider" },
// 分组操作 - 带子菜单
{
label: "分组操作",
icon: "CGroup",
children: [
{
label: "创建组",
icon: "CCreateGroup",
disabled: !canGroupLayers.value,
action: () => {
groupSelectedLayers();
hideContextMenu();
},
},
{
label: "解组",
icon: "CCancelGroup",
disabled: !canUngroupLayers.value,
action: () => {
ungroupSelectedLayer();
hideContextMenu();
},
},
// {
// label: "合并组", // 不需要了 同栅格化功能一样了
// icon: "CMergeGroup",
// disabled: !isGroupLayer || isMultiple,
// action: () => {
// mergeGroupLayer(layer.id);
// hideContextMenu();
// },
// },
],
},
// 图层操作 - 带子菜单
{
label: "图层操作",
icon: "CLayout",
children: [
// // 锁定/解锁
// {
// label: layer.locked ? "解锁图层" : "锁定图层",
// icon: layer.locked ? "CUnLock" : "CLock",
// disabled: !canToggleLock.value,
// action: () => toggleSelectedLayersLock(),
// },
// // 显示/隐藏
// {
// label: layer.visible ? "隐藏图层" : "显示图层",
// icon: layer.visible ? "CUnEye" : "CEye",
// action: () => toggleSelectedLayersVisibility(),
// },
// { type: "divider" },
{
label: "置顶",
icon: "CBottom",
inverIcon: true, // 倒置图标
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canMoveToTop?.(layer.id),
action: () => {
moveLayerToTop(layer.id);
hideContextMenu();
},
},
{
label: "向上移动",
icon: "CUp",
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canMoveToTop?.(layer.id),
action: () => {
moveLayerUp(layer.id);
hideContextMenu();
},
},
{
label: "向下移动",
icon: "CDown",
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canMoveToBottom?.(layer.id),
action: () => {
moveLayerDown(layer.id);
hideContextMenu();
},
},
{
label: "置底",
icon: "CBottom",
disabled:
layer.isBackground ||
layer.isFixed ||
!layerManager?.canMoveToBottom?.(layer.id),
action: () => {
moveLayerToBottom(layer.id);
hideContextMenu();
},
},
],
},
].filter((item) => item !== null);
}
// 右键菜单操作方法
function copySelectedLayers() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length === 0) return;
if (selectedLayers.length === 1) {
layerManager.copyLayer(selectedLayers[0].id);
console.log(`✅ 已复制图层: ${selectedLayers[0].name}`);
} else {
// 多个图层复制逻辑(如果需要支持)
console.log(`✅ 已复制 ${selectedLayers.length} 个图层`);
}
hideContextMenu();
}
function toggleSelectedLayersLock() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length === 0) return;
selectedLayers.forEach((layer) => {
if (!layer.isBackground && !layer.isFixed) {
layerManager.toggleLayerLock(layer.id);
}
});
hideContextMenu();
}
function toggleSelectedLayersLockByLayer(layer) {
if (!layer.isBackground && !layer.isFixed) {
layerManager.toggleLayerLock(layer.id);
}
}
function toggleSelectedLayersVisibility() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length === 0) return;
selectedLayers.forEach((layer) => {
layerManager.toggleLayerVisibility(layer.id);
});
hideContextMenu();
}
function canDeleteLayers() {
const selectedLayers = getSelectedLayers();
if (selectedLayers.length === 0) return false;
// 检查是否包含不能删除的图层
const undeletableLayers = selectedLayers.filter(
(layer) => layer.isBackground || layer.isFixed
);
if (undeletableLayers.length > 0) return false;
// 检查删除后是否还有足够的普通图层
const remainingNormalLayers = layers.value.filter(
(layer) =>
!layer.isBackground &&
!layer.isFixed &&
!selectedLayerIds.value.includes(layer.id)
).length;
return remainingNormalLayers >= 1;
}
// 子图层操作方法
function toggleChildLayerVisibility(childLayerId, parentId) {
if (layerManager.toggleChildLayerVisibility) {
layerManager.toggleChildLayerVisibility(childLayerId, parentId);
}
}
function toggleChildLayerLock(childLayerId, parentId) {
if (layerManager.toggleChildLayerLock) {
layerManager.toggleChildLayerLock(childLayerId, parentId);
}
}
function deleteChildLayer(childLayerId, parentId) {
if (layerManager.removeChildLayer) {
layerManager.removeChildLayer(childLayerId, parentId);
}
}
function renameChildLayer(childLayerId, parentId, newName) {
if (layerManager.renameChildLayer) {
layerManager.renameChildLayer(childLayerId, parentId, newName);
}
}
// 子图层编辑相关方法
function startChildLayerEdit(childLayer) {
childLayer.editing = true;
childLayer.tempName = childLayer.name;
nextTick(() => {
const inputElement = document.querySelector(
`input[data-child-layer-id="${childLayer.id}"]`
);
if (inputElement) {
inputElement.focus();
inputElement.select();
}
});
}
function finishChildLayerEdit(childLayer) {
if (childLayer.tempName && childLayer.tempName.trim()) {
const trimmedName = childLayer.tempName.trim();
renameLayer(childLayer.id, trimmedName);
childLayer.name = trimmedName;
}
childLayer.editing = false;
childLayer.tempName = "";
}
function cancelChildLayerEdit(childLayer) {
childLayer.editing = false;
childLayer.tempName = "";
}
// 子图层编辑按键处理
function handleChildLayerEditKeydown(event, childLayer) {
if (event.key === "Enter") {
event.preventDefault();
finishChildLayerEdit(childLayer);
} else if (event.key === "Escape") {
event.preventDefault();
cancelChildLayerEdit(childLayer);
}
}
// 选中子图层
function selectChildLayer(layerId, parentId) {
if (!isMultiSelectMode.value)
layerManager?.setActiveLayer(layerId, { parentId });
}
// 子图层右键菜单处理
function showChildLayerContextMenu(event, childLayer) {
event.preventDefault();
event.stopPropagation();
// 先选中子图层
selectChildLayer(childLayer.id, childLayer.parentId);
// 构建子图层菜单项
contextMenuLayer.value = childLayer;
contextMenuPosition.value = { x: event.clientX, y: event.clientY };
buildChildLayerContextMenuItems(childLayer);
contextMenuVisible.value = true;
// 点击其他地方关闭菜单
nextTick(() => {
document.addEventListener("click", hideContextMenu, { once: true });
document.addEventListener("contextmenu", hideContextMenu, { once: true });
});
}
function buildChildLayerContextMenuItems(childLayer) {
contextMenuItems.value = [
// 重命名
{
label: "重命名",
icon: "CFont",
disabled: childLayer.isBackground || childLayer.isFixed,
action: () => startChildLayerEdit(childLayer),
},
{ type: "divider" },
// 锁定/解锁
{
label: childLayer.locked ? "解锁图层" : "锁定图层",
icon: childLayer.locked ? "CUnLock" : "CLock",
disabled: childLayer.isBackground || childLayer.isFixed,
action: () => toggleChildLayerLock(childLayer.id),
},
// 显示/隐藏
{
label: childLayer.visible ? "隐藏图层" : "显示图层",
icon: childLayer.visible ? "CUnEye" : "CEye",
action: () =>
toggleChildLayerVisibility(childLayer.id, childLayer.parentId),
},
{ type: "divider" },
// 移出组
{
label: "移出组",
icon: "CPlus",
disabled: false,
action: () => moveChildLayerOutOfGroup(childLayer.id),
},
].filter((item) => item !== null);
}
// 将子图层移出组
function moveChildLayerOutOfGroup(childLayerId) {
console.log(`移出组操作: ${childLayerId}`);
// TODO: 实现将子图层移出组的逻辑
hideContextMenu();
}
// 点击空白区域清除选择
function handlePanelClick(event) {
// 如果点击的是面板空白区域,清除多选状态
if (event.target === event.currentTarget) {
if (isMultiSelectMode.value) {
clearSelection();
}
}
}
// 处理根级图层拖拽排序
async function handleRootLayersSort(event) {
console.log("🔄 图层拖拽排序开始:", { event });
const { newIndex, oldIndex } = event;
const layerId = sortableRootLayers.value[oldIndex]?.id;
if (!commandManager) {
console.warn("命令管理器未注入,使用回退方案");
emit("reorder-layers", {
oldIndex: oldIndex,
newIndex: newIndex,
layerId,
});
return;
}
const layerToMove = sortableRootLayers.value[oldIndex];
if (!layerToMove) {
console.error("找不到要移动的图层");
return;
}
try {
layerManager?.reorderLayers(oldIndex, newIndex, layerId);
console.log(
`✅ 图层排序命令执行成功: ${layerToMove.name} (${oldIndex} -> ${newIndex})`
);
} catch (error) {
console.error("❌ 图层排序命令执行失败:", error);
emit("reorder-layers", {
oldIndex: oldIndex,
newIndex: newIndex,
layerId: layerToMove.id,
});
}
}
// 处理子图层拖拽排序
async function handleChildLayersSort(event, childrenLayers, parentId) {
console.log("🔄 子图层拖拽排序开始:", { event, parentId });
const { newIndex, oldIndex } = event;
const layerId = childrenLayers?.[oldIndex]?.id;
if (!commandManager) {
console.warn("命令管理器未注入,使用回退方案");
emit("reorder-child-layers", {
parentId,
oldIndex,
newIndex,
layerId,
});
return;
}
// 获取父图层
const parentLayer = layers.value.find((layer) => layer.id === parentId);
if (!parentLayer) {
console.error("❌ 找不到父图层:", parentId);
return;
}
// 获取父图层的子图层列表
const layerToMove = childrenLayers[oldIndex];
if (!layerToMove) {
console.error(
"❌ 找不到要移动的子图层oldIndex:",
oldIndex,
"childLayers:",
childrenLayers
);
return;
}
console.log("📝 子图层排序详情:", {
parentId,
parentLayerName: parentLayer.name,
layerToMove: layerToMove.name,
oldIndex,
newIndex,
totalChildLayers: childrenLayers.length,
});
try {
layerManager?.moveLayerToIndex({ parentId, oldIndex, newIndex, layerId });
console.log(
`✅ 子图层排序命令执行成功: ${layerToMove.name} (${oldIndex} -> ${newIndex})`
);
} catch (error) {
console.error("❌ 子图层排序命令执行失败:", error);
emit("reorder-child-layers", {
parentId,
oldIndex,
newIndex,
layerId: layerToMove.id,
});
}
}
// 栅格化图层
async function rasterizeLayer(layerId) {
if (!layerManager?.rasterizeLayer) {
console.warn("栅格化功能不可用");
return;
}
try {
const success = await layerManager.rasterizeLayer(layerId);
if (success) {
console.log(`✅ 成功栅格化图层: ${layerId}`);
} else {
console.warn("栅格化图层失败");
}
} catch (error) {
console.error("栅格化图层时发生错误:", error);
}
}
// 导出图层
async function exportLayerToImage(layerId) {
if (!layerManager?.rasterizeLayer) {
console.warn("导出图层功能不可用");
return;
}
try {
const success = await layerManager.exportLayerToImage(layerId);
if (success) {
console.log(`✅ 成功导出图层: ${layerId}`);
} else {
console.warn("导出图层失败");
}
} catch (error) {
console.error("导出图层时发生错误:", error);
}
}
// 合并组图层
async function mergeGroupLayer(groupId) {
if (!layerManager?.mergeGroupLayers) {
console.warn("合并组功能不可用");
return;
}
try {
const childLayerId = await layerManager.mergeGroupLayers(groupId);
if (childLayerId) {
const groupLayer = layers.value.find((l) => l.id === groupId);
console.log(
`✅ 成功合并组图层: ${
groupLayer?.name || groupId
}, 生成 ${childLayerId} 图层`
);
} else {
console.warn("合并组图层失败");
}
} catch (error) {
console.error("合并组图层时发生错误:", error);
}
}
// 置顶图层
function moveLayerToTop(layerId) {
if (!layerManager?.moveLayer) {
console.warn("移动图层功能不可用");
return;
}
const success = layerManager.moveLayer(layerId, "toTop");
if (success) {
const layer = layers.value.find((l) => l.id === layerId);
console.log(`✅ 成功置顶图层: ${layer?.name || layerId}`);
} else {
console.warn("置顶图层失败");
}
}
// 置底图层
function moveLayerToBottom(layerId) {
if (!layerManager?.moveLayer) {
console.warn("移动图层功能不可用");
return;
}
const success = layerManager.moveLayer(layerId, "toBottom");
if (success) {
const layer = layers.value.find((l) => l.id === layerId);
console.log(`✅ 成功置底图层: ${layer?.name || layerId}`);
} else {
console.warn("置底图层失败");
}
}
// 事件转发方法
const forwardEvent = (eventName, ...args) => {
emit(eventName, ...args);
};
</script>
<template>
<div class="layers-panel-inner" @click="handlePanelClick">
<div class="layers-header">
<h3>
{{ $t("图层") }}
{{ selectedLayerIds.length > 0 ? `(${selectedLayerIds.length})` : "" }}
</h3>
<div class="layer-actions-group">
<!-- 多选操作按钮组 -->
<div v-if="isMultiSelectMode" class="multi-select-actions">
<div
class="group-btn action-btn"
:class="{ disabled: !canGroupLayers }"
@click="groupSelectedLayers"
:title="$t('创建组')"
>
<SvgIcon name="CCreateGroup" size="20"></SvgIcon>
</div>
<div
class="ungroup-btn action-btn"
:class="{ disabled: !canUngroupLayers }"
@click="ungroupSelectedLayer"
:title="$t('解组')"
>
<SvgIcon name="CCancelGroup" size="20"></SvgIcon>
</div>
<div
class="delete-selected-btn action-btn"
@click="deleteSelectedLayers"
:title="$t('删除选中图层')"
>
<SvgIcon name="CDelete" size="16"></SvgIcon>
</div>
<div
class="clear-selection-btn action-btn"
@click="clearSelection"
:title="$t('清除选择')"
>
<SvgIcon name="CCloseNo" size="14"></SvgIcon>
</div>
</div>
<!-- 常规操作按钮 -->
<div v-else class="normal-actions">
<div
class="add-layer-btn action-btn"
@click="addLayer"
:title="$t('添加图层')"
>
<SvgIcon name="CPlus" size="16"></SvgIcon>
</div>
<div
class="select-all-btn action-btn"
@click="selectAllLayers"
:title="$t('全选图层')"
>
<SvgIcon name="CCheckbox" size="16"></SvgIcon>
</div>
</div>
</div>
</div>
<!-- 多选提示条 -->
<div v-if="isMultiSelectMode" class="multi-select-info">
<span>已选择 {{ selectedLayerIds.length }} 个图层</span>
<small>提示按住 Ctrl/Cmd 多选Shift 范围选择长按进入多选模式</small>
</div>
<!-- 图层列表组件 -->
<LayersList
:layers="layers"
:active-layer-id="activeLayerId"
:sortable-root-layers="sortableRootLayers"
:fixed-layers="fixedLayers"
:selected-layer-ids="selectedLayerIds"
:is-multi-select-mode="isMultiSelectMode"
:editing-layer-id="editingLayerId"
:editing-layer-name="editingLayerName"
:thumbnail-manager="thumbnailManager"
:expanded-group-ids="expandedGroupIds"
:isChild="false"
group-name="layers-root"
@layer-click="handleLayerClick"
@layer-double-click="handleLayerDoubleClick"
@context-menu="showContextMenu"
@checkbox-change="handleCheckboxClick"
@toggle-visibility="toggleLayerVisibility"
@toggle-lock="toggleSelectedLayersLockByLayer"
@delete="removeLayer"
@edit-confirm="confirmEdit"
@edit-cancel="cancelEdit"
@edit-keydown="handleEditKeydown"
@touch-start="handleTouchStart"
@touch-move="handleTouchMove"
@touch-end="handleTouchEnd"
@update:editing-name="editingLayerName = $event"
@root-layers-sort="handleRootLayersSort"
@child-layers-sort="handleChildLayersSort"
@select-child-layer="selectChildLayer"
@start-child-layer-edit="startChildLayerEdit"
@child-context-menu="showChildLayerContextMenu"
@toggle-group-expanded="toggleGroupExpanded"
@toggle-child-visibility="toggleChildLayerVisibility"
@toggle-child-lock="toggleChildLayerLock"
@delete-child="deleteChildLayer"
@rename-child="renameChildLayer"
/>
<!-- 固定层背景层和固定层 -->
<div v-if="fixedLayers.length > 0" class="fixed-layers">
<!-- 遍历固定层 -->
<!-- :thumbnail-url="getLayerThumbnail(layer.id)" -->
<LayerItem
v-for="layer in fixedLayers"
:key="layer.id"
:layer="layer"
:is-child="false"
:is-active="layer.id === activeLayerId"
:is-selected="false"
:is-multi-select-mode="false"
:is-editing="editingLayerId === layer.id"
:editing-name="editingLayerName"
:can-delete="false"
:isHidenDragHandle="true"
@toggle-visibility="
(...args) => forwardEvent('toggle-layer-visibility', ...args)
"
@edit-confirm="(...args) => forwardEvent('edit-confirm', ...args)"
@edit-cancel="(...args) => forwardEvent('edit-cancel', ...args)"
@edit-keydown="(...args) => forwardEvent('edit-keydown', ...args)"
@update:editing-name="
(...args) => forwardEvent('update:editing-name', ...args)
"
/>
</div>
<!-- 右键菜单 -->
<ContextMenu
:visible="contextMenuVisible"
:position="contextMenuPosition"
:items="contextMenuItems"
@close="hideContextMenu"
/>
</div>
</template>
<style scoped lang="less">
@import "./layersPanel.less";
</style>