2026-03-06 15:50:05 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="header-tools">
|
|
|
|
|
<template v-for="(v, i) in tools" :key="i">
|
|
|
|
|
<span class="line" v-if="v.type === 'line'"></span>
|
|
|
|
|
<span
|
|
|
|
|
v-else
|
|
|
|
|
class="icon"
|
|
|
|
|
@click="onClickTool(v)"
|
|
|
|
|
:class="{ active: v.name === tool, disabled: v.disabled }"
|
|
|
|
|
>
|
|
|
|
|
<svg-icon :name="v.icon" :size="v.iconSize" />
|
|
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
<button class="export" @click="emit('export')">
|
|
|
|
|
<span class="icon"><svg-icon name="export" size="12" /></span>
|
|
|
|
|
<span class="text">Export</span>
|
|
|
|
|
</button>
|
2026-03-12 15:51:18 +08:00
|
|
|
<button class="export" @click="emit('export-local')">
|
|
|
|
|
<span class="text">保存本地</span>
|
2026-03-11 15:34:56 +08:00
|
|
|
</button>
|
2026-03-12 15:51:18 +08:00
|
|
|
<button class="export" @click="emit('import-local')">
|
|
|
|
|
<span class="text">本地导入</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="workbench" @click="emit('export-close')">
|
2026-03-06 15:50:05 +08:00
|
|
|
<span class="icon"><svg-icon name="dc-workbench" size="20" /></span>
|
|
|
|
|
<span class="text">Workbench</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, inject, computed } from 'vue'
|
2026-03-09 13:44:32 +08:00
|
|
|
import { OperationType } from '../tools/layerHelper'
|
2026-03-06 15:50:05 +08:00
|
|
|
const props = defineProps({
|
|
|
|
|
zoom: { default: 1, type: Number },
|
|
|
|
|
step: { default: 0.1, type: Number }
|
|
|
|
|
})
|
2026-03-12 15:51:18 +08:00
|
|
|
const emit = defineEmits(['export', 'export-local', 'import-local', 'export-close'])
|
2026-03-11 15:34:56 +08:00
|
|
|
const importLocalImage = inject('importLocalImage') as () => void
|
2026-03-06 15:50:05 +08:00
|
|
|
const stateManager = inject('stateManager') as any
|
|
|
|
|
const toolManager = inject('toolManager') as any
|
2026-03-09 13:44:32 +08:00
|
|
|
const tool = computed(() => toolManager.currentTool.value)
|
2026-03-06 15:50:05 +08:00
|
|
|
const historyIndex = computed(() => stateManager.historyIndex.value)
|
|
|
|
|
const historyList = computed(() => stateManager.historyList.value)
|
|
|
|
|
const isUndo = computed(() => !historyList.value[historyIndex.value - 1])
|
|
|
|
|
const isRedo = computed(() => !historyList.value[historyIndex.value + 1])
|
|
|
|
|
const tools = ref([
|
2026-03-09 13:44:32 +08:00
|
|
|
{ name: OperationType.SELECT, icon: 'dc-select', iconSize: 16, disabled: ref(false) },
|
|
|
|
|
{ name: OperationType.PAN, icon: 'dc-move', iconSize: 18, disabled: ref(false) },
|
|
|
|
|
{ name: OperationType.DRAW, icon: 'dc-brush', iconSize: 18, disabled: ref(false) },
|
|
|
|
|
{ name: OperationType.ERASER, icon: 'dc-eraser', iconSize: 18, disabled: ref(false) },
|
2026-03-11 15:34:56 +08:00
|
|
|
{
|
|
|
|
|
name: OperationType.IMAGE,
|
|
|
|
|
icon: 'dc-image',
|
|
|
|
|
iconSize: 17,
|
|
|
|
|
disabled: ref(false),
|
|
|
|
|
on: () => importLocalImage()
|
|
|
|
|
},
|
2026-03-09 13:44:32 +08:00
|
|
|
{ name: OperationType.SELECTBOX, icon: 'dc-selectbox', iconSize: 16, disabled: ref(false) },
|
|
|
|
|
{ name: OperationType.RECTANGLE, icon: 'dc-rectangle', iconSize: 16, disabled: ref(false) },
|
2026-03-06 15:50:05 +08:00
|
|
|
{ type: 'line' },
|
|
|
|
|
{
|
2026-03-09 13:44:32 +08:00
|
|
|
name: OperationType.UNDO,
|
2026-03-06 15:50:05 +08:00
|
|
|
icon: 'dc-undo',
|
|
|
|
|
iconSize: 18,
|
|
|
|
|
disabled: isUndo,
|
|
|
|
|
on: () => stateManager.undoState()
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-09 13:44:32 +08:00
|
|
|
name: OperationType.REDO,
|
2026-03-06 15:50:05 +08:00
|
|
|
icon: 'dc-redo',
|
|
|
|
|
iconSize: 18,
|
|
|
|
|
disabled: isRedo,
|
|
|
|
|
on: () => stateManager.redoState()
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
const onClickTool = (tool: any) => {
|
|
|
|
|
if (tool.disabled?.value) return
|
|
|
|
|
if (tool.on) {
|
|
|
|
|
tool.on()
|
|
|
|
|
} else {
|
|
|
|
|
toolManager.setTool(tool.name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
|
.header-tools {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2.2rem;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
height: 5rem;
|
|
|
|
|
padding: 0.7rem 1.8rem;
|
|
|
|
|
border: 0.2rem solid #ebebeb;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 0.6rem;
|
|
|
|
|
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
user-select: none;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
> .line {
|
|
|
|
|
width: 0;
|
|
|
|
|
height: 100%;
|
|
|
|
|
border-left: 0.2rem solid #d9d9d9;
|
|
|
|
|
border-radius: 0.2rem;
|
|
|
|
|
margin: 0 0.6rem;
|
|
|
|
|
}
|
|
|
|
|
> .icon {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 3rem;
|
|
|
|
|
height: 3rem;
|
|
|
|
|
--svg-icon-color: #000;
|
|
|
|
|
border-radius: 0.4rem;
|
|
|
|
|
&:not(.disabled).active,
|
|
|
|
|
&:not(.disabled):hover {
|
|
|
|
|
background-color: #ebebeb;
|
|
|
|
|
}
|
|
|
|
|
&.disabled {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
> button {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: none;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 0.8rem;
|
|
|
|
|
&:active {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
> .export {
|
|
|
|
|
width: 10rem;
|
|
|
|
|
height: 3rem;
|
|
|
|
|
background-color: #0d0d0d;
|
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
border-radius: 0.4rem;
|
|
|
|
|
}
|
|
|
|
|
> .workbench {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: -2.4rem;
|
|
|
|
|
transform: translateX(100%);
|
|
|
|
|
height: 100%;
|
|
|
|
|
padding: 0 2.4rem;
|
|
|
|
|
border: 0.2rem solid #ebebeb;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 0.6rem;
|
|
|
|
|
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
|
|
|
|
font-size: 1.7rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|