Files
aida_front/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue
2026-01-19 16:57:11 +08:00

464 lines
10 KiB
Vue

<script setup>
import { ref, inject, computed, onMounted, onUnmounted } from "vue";
import { findLayerRecursively, OperationType } from "../utils/layerHelper";
import ToolButton from "../../ExistsImageList/ToolButton.vue";
import { useI18n } from 'vue-i18n'
const emit = defineEmits([
"tool-selected",
"trigger-image-upload",
"add-text",
"undo",
"redo",
"toggle-minimap",
"zoom-in",
"zoom-out",
"toggle-red-green-mode",
"undo-redo-status-changed",
"trigger-library"
]);
const {t,locale} = useI18n()
const props = defineProps({
activeTool: String,
minimapEnabled: {
type: Boolean,
default: true,
},
isRedGreenMode: {
type: Boolean,
default: false,
},
clothingMinIOPath: {
type: String,
default: "", // 衣服底图URL-线稿
},
});
const commandManager = inject("commandManager");
const layerManager = inject("layerManager"); // 图层管理器
const lastSelectLayerId = inject("lastSelectLayerId"); // 上次选中的图层ID
// 撤销/重做按钮状态
const canUndo = ref(false);
const canRedo = ref(false);
// 颜色填充相关
const currLayerId = ref(null); // 当前图层ID
const fillColor = ref("#ffffff"); // 默认填充颜色
const fillColorRef = ref(null);
// 监听命令管理器状态变化
commandManager.setChangeCallback((info) => {
canUndo.value = info.canUndo;
canRedo.value = info.canRedo;
emit("undo-redo-status-changed", {
canUndo: canUndo.value,
canRedo: canRedo.value,
commandManager,
});
});
// 撤销/重做操作
const undoFun = () => commandManager.undo();
const redoFun = () => commandManager.redo();
// 普通模式工具列表
const normalToolsList = ref([
{
id: "undo",
title: t("Canvas.undo"),
action: undo,
icon: { name: "CUndo", size: "20" },
class: "undo-btn",
},
{
id: "redo",
title: t("Canvas.Redo"),
action: redo,
icon: { name: "CRedo", size: "20" },
class: "redo-btn",
},
{
id: OperationType.DRAW,
title: t("Canvas.Drawing"),
action: () => selectTool(OperationType.DRAW),
icon: { name: "CBrush", size: "24" },
class: "draw-btn",
},
{
id: OperationType.ERASER,
title: t("Canvas.Eraser"),
action: () => selectTool(OperationType.ERASER),
icon: { name: "CEraser", size: "22" },
class: "eraser-btn",
},
{
id: "fillColor",
title: t("Canvas.FillColor"),
action: () => fillColorRef.value.click(),
icon: { name: "CThemeColor", size: "22" },
class: "fill-color-btn",
},
{
id: OperationType.PAN,
title: t("Canvas.Pan"),
action: () => selectTool(OperationType.PAN),
icon: { name: "CHand", size: "28" },
class: "hand-btn",
},
{
id: OperationType.SELECT,
title: t("Canvas.Select"),
action: () => selectTool(OperationType.SELECT),
icon: { name: "CSelect", size: "28" },
class: "select-btn",
},
{
id: OperationType.LIQUIFY,
title: t("Canvas.Liquefying"),
action: () => selectTool(OperationType.LIQUIFY),
icon: { name: "CLiquefying", size: "32" },
class: "liquify-btn",
},
{
id: OperationType.LASSO,
title: t("Canvas.Lasso"),
action: () => selectTool(OperationType.LASSO),
icon: { name: "CLasso", size: "28" },
class: "lasso-btn",
activeList: [
OperationType.LASSO,
OperationType.LASSO_RECTANGLE,
OperationType.AREA_CUSTOM,
OperationType.AREA_RECTANGLE,
],
},
{
id: "zoomIn",
title: t("Canvas.ZoomIn"),
action: zoomIn,
icon: { name: "CZoomIn", size: "30" },
class: "zoom-in-btn",
},
{
id: "zoomOut",
title: t("Canvas.ZoomOut"),
action: zoomOut,
icon: { name: "CZoomOut", size: "26" },
class: "zoom-out-btn",
},
{
id: "upload",
title: t("Canvas.Upload"),
action: triggerImageUpload,
icon: { name: "CUpload", size: "26" },
class: "upload-btn",
},
{
id: "library",
title: t("LibraryPage.library"),
action: triggerLibrary,
icon: { name: "CLibrary", size: "26" },
class: "library-btn",
},
{
id: "addText",
title: t("Canvas.AddText"),
action: () => addText(),
icon: { name: "CFont", size: "20" },
class: "text-btn",
},
{
id: OperationType.PART,
title: t("Canvas.GarmentPartSelector"),
action: () => selectTool(OperationType.PART),
icon: { name: "CPart", size: "28" },
class: "part-btn",
activeList: [
OperationType.PART,
OperationType.PART_RECTANGLE,
OperationType.PART_BRUSH,
OperationType.PART_ERASER,
],
},
{
id: "help",
title: t("Canvas.help"),
action: () => openTutorial(),
icon: { name: "CHelp", size: "30" },
class: "text-btn",
style: {
'position': 'absolute',
'bottom': '0',
},
},
]);
// 红绿图模式工具列表
const redGreenToolsList = ref([
{
id: "undo",
title: t("Canvas.undo"),
action: undo,
icon: { name: "CUndo", size: "20" },
class: "undo-btn",
},
{
id: "redo",
title: t("Canvas.Redo"),
action: redo,
icon: { name: "CRedo", size: "20" },
class: "redo-btn",
},
{
id: OperationType.RED_BRUSH,
title: "Red Brush (R)",
action: () => selectTool(OperationType.RED_BRUSH, true),
icon: { name: "CBrush", size: "24" },
class: "red-brush-btn",
style: { color: "#FF0000" },
},
{
id: OperationType.GREEN_BRUSH,
title: "Green Brush (G)",
action: () => selectTool(OperationType.GREEN_BRUSH, true),
icon: { name: "CBrush", size: "24" },
class: "green-brush-btn",
style: { color: "#00AA00" },
},
{
id: OperationType.ERASER,
title: t("Canvas.Eraser"),
action: () => selectTool(OperationType.ERASER, true),
icon: { name: "CEraser", size: "22" },
class: "eraser-btn",
},
{
id: "zoomIn",
title: t("Canvas.ZoomIn"),
action: zoomIn,
icon: { name: "CZoomIn", size: "30" },
class: "zoom-in-btn",
},
{
id: "zoomOut",
title: t("Canvas.ZoomOut"),
action: zoomOut,
icon: { name: "CZoomOut", size: "26" },
class: "zoom-out-btn",
},
]);
// 根据模式选择工具列表
const toolsList = computed(() => {
const list = props.isRedGreenMode ? redGreenToolsList.value : normalToolsList.value;
return list.filter(tool => {
if(tool.id === OperationType.PART){
return !!props.clothingMinIOPath;
}
return true;
});
});
function selectTool(tool, isRedGreenMode = false) {
emit("tool-selected", tool, isRedGreenMode);
}
function triggerImageUpload() {
emit("trigger-image-upload");
}
function triggerLibrary() {
emit("trigger-library");
}
function addText() {
emit("add-text");
}
function openTutorial() {
if(locale.value == 'ENGLISH'){
window.open('https://aida-user-manual.super.site/specific-scenarios/freely-sketching-in-canvas', '_blank');
}else{
window.open('https://aida-user-manual-chinese.super.site/%e4%bd%bf%e7%94%a8%e7%94%bb%e5%b8%83%e8%bf%9b%e8%a1%8c%e7%bc%96%e8%be%91 ', '_blank');
}
}
function undo() {
if (!canUndo.value) return;
undoFun();
emit("undo", {
canUndo: canUndo.value,
canRedo: canRedo.value,
commandManager,
});
}
function redo() {
if (!canRedo.value) return;
emit("redo", {
canUndo: canUndo.value,
canRedo: canRedo.value,
commandManager,
});
redoFun();
}
function toggleMinimap() {
emit("toggle-minimap", !props.minimapEnabled);
}
function zoomIn() {
emit("zoom-in");
}
function zoomOut() {
emit("zoom-out");
}
function toggleRedGreenMode() {
emit("toggle-red-green-mode");
}
// 键盘快捷键处理
function handleKeyDown(event) {
// 在红绿图模式下处理特定快捷键
if (props.isRedGreenMode) {
const key = event.key.toUpperCase();
// 当处于输入状态时不触发快捷键
if (event.target.tagName === "INPUT" || event.target.tagName === "TEXTAREA") {
return;
}
switch (key) {
case "R":
selectTool(OperationType.RED_BRUSH, true);
event.preventDefault();
break;
case "G":
selectTool(OperationType.GREEN_BRUSH, true);
event.preventDefault();
break;
case "E":
selectTool(OperationType.ERASER, true);
event.preventDefault();
break;
}
}
}
const fillInputTimeout = ref(null);
// 填充颜色选择器
function fillColorChange() {
clearTimeout(fillInputTimeout.value);
fillInputTimeout.value = setTimeout(() => {
layerManager.fillLayerBackground(lastSelectLayerId.value, fillColor.value, true);
}, 100);
}
onMounted(() => {
// 添加键盘事件监听
window.addEventListener("keydown", handleKeyDown);
});
onUnmounted(() => {
// 移除键盘事件监听
window.removeEventListener("keydown", handleKeyDown);
});
// 处理工具按钮点击
const handleToolClick = (tool) => {
tool.action();
};
</script>
<template>
<div class="tools-sidebar">
<input
class="fillColor-input"
v-model="fillColor"
ref="fillColorRef"
type="color"
@input="fillColorChange"
style="width: 0; height: 0; opacity: 0"
/>
<div class="tools-list">
<slot name="customToolsTop" :tool-button-props="{ activeTool, canUndo, canRedo }" />
<ToolButton
v-for="tool in toolsList"
:key="tool.id"
:tool="tool"
:active-tool="activeTool"
:can-undo="canUndo"
:can-redo="canRedo"
@click="handleToolClick"
tip-body
/>
<!-- 自定义工具栏按钮插槽 -->
<slot name="customToolsBottom" :tool-button-props="{ activeTool, canUndo, canRedo }" />
</div>
</div>
</template>
<style scoped>
.tools-sidebar {
display: flex;
flex-direction: column;
padding: 1.5rem 1.0rem;
border-right: .1rem solid #e0e0e0;
background-color: #ffffff;
user-select: none;
min-width: 5.8rem;
height: 100%;
padding-top: .5rem;
padding-bottom: 4rem;
position: relative;
/* overflow-y: auto; */
/* overflow-x: hidden; */
}
.fillColor-input{
position: absolute;
top: 0;
right: 0;
}
.tools-list{
display: flex;
flex-direction: column;
gap: 0.5rem;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
.tools-list::-webkit-scrollbar {
display: none;
}
.red-green-mode {
background-color: #060505;
}
.mode-indicator {
margin-bottom: 1.0rem;
padding: .8rem;
border-radius: 4px;
background-color: #ffcccc;
color: #a33;
font-size: 1.4rem;
text-align: center;
}
.mode-label {
font-weight: bold;
}
.mode-hint {
font-size: 1.2rem;
margin-top: .4rem;
}
</style>