Files
aida_front/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue
李志鹏 43e3a79124 点选
2026-01-19 10:47:30 +08:00

488 lines
9.1 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.
<template>
<transition name="fade">
<div
class="part-selector-toolbar"
v-if="visible"
:class="{ active: !closePanel }"
>
<div class="btn" @click="setClosePanel">
<i class="fi fi-br-angle-left"></i>
</div>
<!-- 顶部选区类型工具栏 -->
<div class="toolbar-section">
<div class="toolbar-header">
<div class="header-title">
{{ t("Canvas.GarmentPartSelector") }}
</div>
<!-- 移除关闭按钮完全通过工具切换控制显示隐藏 -->
<div class="tip">
<div>
<img
src="/src/assets/images/canvas/shubiao-l.png"
/>
<span>Left Click: Add</span>
</div>
<div>
<img
src="/src/assets/images/canvas/shubiao-r.png"
/>
<span>Right Click: Remove</span>
</div>
</div>
</div>
<div class="tool-types">
<div
v-for="item in toolList"
:key="item.type"
:class="[
'tool-btn',
{ active: toolType === item.type },
]"
@click="setPartType(item.type)"
>
<svg-icon :name="item.icon" :size="item.size" />
<span>{{ item.label }}</span>
</div>
</div>
<!-- 分割线 -->
<div class="toolbar-divider"></div>
<!-- 底部选区操作工具栏 -->
<div class="tool-actions">
<div class="action-btn" @click="onCreate">
<svg-icon name="CPaste" size="16" />
<span class="btn-text">{{
$t("Canvas.creation")
}}</span>
</div>
<div class="action-btn" @click="onCopyCreate">
<svg-icon name="CCut" size="26" />
<span class="btn-text">{{
$t("Canvas.CreateAndCopy")
}}</span>
</div>
<div class="action-btn" @click="onReset">
<svg-icon name="CCut" size="26" />
<span class="btn-text">清空当前点位</span>
</div>
</div>
</div>
</div>
</transition>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import {
CreateSelectionCommand,
InvertSelectionCommand,
FeatherSelectionCommand,
FillSelectionCommand,
} from "../commands/SelectionCommands";
import { ToolCommand } from "../commands/ToolCommands";
import {
LassoCutoutCommand,
ClearSelectionCommand,
// CutSelectionToNewLayerCommand,
} from "../commands/LassoCutoutCommand";
import { OperationType } from "../utils/layerHelper";
import { ClearSelectionContentCommand } from "../commands/ClearSelectionContentCommand";
import { CutSelectionToNewLayerCommand } from "../commands/CutSelectionToNewLayerCommand";
const props = defineProps({
canvas: {
type: Object,
required: true,
},
commandManager: {
type: Object,
required: true,
},
partManager: {
type: Object,
required: true,
},
partManager: {
type: Object,
required: true,
},
layerManager: {
type: Object,
required: true,
},
toolManager: {
type: Object,
required: true,
},
activeTool: {
type: String,
required: false,
default: null,
},
});
// 响应式数据
const visible = ref(false);
const toolType = ref(OperationType.PART);
//打开隐藏操作面板
const closePanel = ref(false);
const setClosePanel = () => {
closePanel.value = !closePanel.value;
};
const toolList = [
{
type: OperationType.PART,
label: "Point Selection",
icon: "CPoint",
size: "20",
},
{
type: OperationType.PART_RECTANGLE,
label: "Marquee Selection",
icon: "CMarquee",
size: "20",
},
{
type: OperationType.PART_BRUSH,
label: "Brush Selection",
icon: "CBrush2",
size: "16",
},
{
type: OperationType.PART_ERASER,
label: "Erase",
icon: "CEraser2",
size: "22",
},
];
// 国际化
const { t } = useI18n();
onMounted(() => {});
// 监听 activeTool 变化
watch(
() => props.activeTool,
(newTool) => {
// 当工具为LASSO或AREA类型时显示选区面板
const selectionTools = [
OperationType.PART,
OperationType.PART_RECTANGLE,
OperationType.PART_BRUSH,
OperationType.PART_ERASER,
];
if (selectionTools.includes(newTool)) {
show();
// 根据工具类型设置选区类型
toolType.value = newTool;
// 更新选区管理器的选区类型
// if (props.partManager) {
// props.partManager.setPartType(toolType.value);
// props.partManager.setupPartEvents();
// }
} else {
close();
}
},
{ immediate: true }
);
/**
* 显示面板
*/
function show() {
visible.value = true;
closePanel.value = true;
}
/**
* 关闭面板
*/
function close() {
visible.value = false;
}
/**
* 设置选区类型
*/
function setPartType(type) {
toolType.value = type;
// 通过 ToolManager 切换工具,这会自动通知 partManager
if (props.toolManager) {
props.toolManager.setToolWithCommand(type);
}
// // 备用方案:如果没有 toolManager直接更新 partManager
// else if (props.partManager) {
// props.partManager.setPartType(type);
// props.partManager.setupPartEvents();
// }
}
// 创建
function onCreate() {
props.partManager.createPart();
}
// 复制并创建
function onCopyCreate() {
}
// 清空当前点位
function onReset() {
props.partManager.clearPart();
}
</script>
<style scoped lang="less">
.part-selector-toolbar {
position: absolute;
bottom: 22px;
left: 20px;
right: 20px;
max-width: min(90vw, 700px);
margin: 0 auto;
background-color: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
border-radius: 8px;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
z-index: 1000;
color: #333;
border: 1px solid rgba(0, 0, 0, 0.05);
user-select: none;
&.active {
transform: translateY(100%);
> .btn {
> i {
transform: rotate(90deg);
}
}
}
> .btn {
width: 100%;
text-align: center;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
height: 22px;
> i {
font-size: 1.4rem;
transform: rotate(270deg);
}
}
}
/* 平板和手机适配 */
@media screen and (max-width: 768px) {
.part-selector-toolbar {
bottom: 15px;
left: 15px;
right: 15px;
max-width: calc(100vw - 30px);
border-radius: 6px;
}
}
@media screen and (max-width: 480px) {
.part-selector-toolbar {
bottom: 10px;
left: 10px;
right: 10px;
max-width: calc(100vw - 20px);
}
}
.part-selector-toolbar.is-active {
transform: translateY(0);
}
.toolbar-header {
// display: flex;
// justify-content: center;
// align-items: center;
padding: 8px 0;
// border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background-color: rgba(255, 255, 255, 0.8);
border-radius: 8px 8px 0 0;
position: relative;
> .tip {
position: absolute;
top: 0;
right: 0;
display: flex;
> div {
display: flex;
align-items: center;
justify-content: center;
margin-left: 10px;
> img {
width: 12px;
height: 12px;
margin-right: 5px;
}
> span {
font-size: 10px;
color: #333;
}
}
}
}
.header-title {
font-size: 13px;
font-weight: 500;
color: #333;
text-align: left;
}
.header-btn {
background: none;
border: none;
color: #333;
font-size: 12px;
cursor: pointer;
padding: 3px 0;
border-radius: 3px;
transition: background-color 0.2s ease;
min-width: 32px;
}
.header-btn:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.close-btn {
color: #666;
}
.toolbar-section {
padding: 0 3rem 1.2rem;
}
.tool-types {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
padding: 10px 0;
}
.tool-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.05);
border: none;
border-radius: 6px;
padding: 6px;
color: #333;
cursor: pointer;
transition: all 0.2s;
}
.tool-btn span {
margin-top: 0;
font-size: 12px;
}
.tool-btn svg {
width: 24px;
height: 24px;
}
.tool-btn:hover {
background-color: rgba(0, 0, 0, 0.08);
}
.tool-btn.active {
background-color: #007aff;
color: white;
}
.toolbar-divider {
height: 1px;
background-color: rgba(0, 0, 0, 0.05);
margin-bottom: 5px;
}
.tool-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 5px;
padding: 0 30px;
}
/* 平板适配 - 每行4个按钮 */
@media screen and (max-width: 768px) {
.tool-actions {
grid-template-columns: repeat(3, 1fr);
gap: 8px 6px;
padding: 0 8px;
}
}
/* 手机适配 - 每行3个按钮 */
@media screen and (max-width: 480px) {
.tool-actions {
grid-template-columns: repeat(3, 1fr);
gap: 6px 4px;
padding: 0 6px;
}
.header-btn {
font-size: 11px;
padding: 2px 4px;
min-width: 28px;
}
}
.action-btn {
display: flex;
// flex-direction: column;
flex-direction: row;
align-items: center;
justify-content: center;
background: none;
border: none;
color: #333;
cursor: pointer;
padding: 0;
gap: 4px;
.c-svg {
width: auto;
}
}
.action-btn svg {
width: 22px;
height: 22px;
margin-bottom: 8px;
}
.btn-text {
display: block;
font-size: 12px;
text-align: center;
}
.action-btn:hover {
color: #007aff;
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>