Files
excel/src/components/spreadsheet/Toolbar.vue
2026-01-22 17:00:24 +08:00

141 lines
6.0 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 { ref } from 'vue'
import {
Undo2, Redo2, PaintRoller,
Bold, Italic, Underline, Strikethrough,
AlignLeft, AlignCenter, AlignRight,
Image as ImageIcon, Plus,
Type, Palette, ChevronDown
} from 'lucide-vue-next'
import { readFilesAsDataURL } from '@/utils/file'
import ColorPicker from './ColorPicker.vue'
const props = defineProps(['currentStyle'])
const emit = defineEmits(['action'])
const fileInput = ref(null)
const triggerImageUpload = () => fileInput.value.click()
const handleImageUpload = async (e) => {
try {
const images = await readFilesAsDataURL(e.target.files)
if (images.length > 0) {
emit('action', 'insertImages', images)
}
} catch (error) {
console.error("图片读取失败", error)
}
e.target.value = ''
}
const handleColorChange = (type, color) => {
emit('action', 'setStyle', { [type]: color })
}
const handleFontChange = (e) => emit('action', 'setStyle', { fontFamily: e.target.value })
const handleFontSizeChange = (e) => emit('action', 'setStyle', { fontSize: e.target.value })
const btnClass = "p-1.5 rounded hover:bg-gray-200 transition text-gray-700 flex items-center justify-center cursor-pointer relative"
const activeClass = "bg-gray-300 text-black"
const dividerClass = "w-px h-5 bg-gray-300 mx-1 self-center"
</script>
<template>
<div class="h-10 bg-[#fbfbfb] border-b border-gray-300 flex items-center px-3 gap-1 select-none shrink-0 overflow-x-auto whitespace-nowrap z-40">
<!-- 历史 -->
<div class="flex gap-0.5">
<button :class="btnClass" title="撤销 (Ctrl+Z)" @click="$emit('action', 'undo')"><Undo2 class="w-4 h-4" /></button>
<button :class="btnClass" title="重做 (Ctrl+Y)" @click="$emit('action', 'redo')"><Redo2 class="w-4 h-4" /></button>
</div>
<div :class="dividerClass"></div>
<!-- 字体与字号 -->
<div class="flex gap-1 items-center">
<div class="relative group">
<select
class="appearance-none bg-transparent hover:bg-gray-200 pl-2 pr-6 py-1 rounded text-xs text-gray-700 outline-none cursor-pointer w-24 border border-transparent hover:border-gray-300 transition-colors"
:value="currentStyle.fontFamily || 'Arial'"
@change="handleFontChange"
>
<option value="Arial">Arial</option>
<option value="Helvetica">Helvetica</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Verdana">Verdana</option>
</select>
<ChevronDown class="w-3 h-3 absolute right-1 top-1.5 text-gray-500 pointer-events-none" />
</div>
<div class="relative group">
<select
class="appearance-none bg-transparent hover:bg-gray-200 pl-2 pr-5 py-1 rounded text-xs text-gray-700 outline-none cursor-pointer w-14 border border-transparent hover:border-gray-300 transition-colors"
:value="currentStyle.fontSize || 11"
@change="handleFontSizeChange"
>
<option v-for="size in [9,10,11,12,14,18,24,36]" :key="size" :value="size">{{ size }}</option>
</select>
<ChevronDown class="w-3 h-3 absolute right-1 top-1.5 text-gray-500 pointer-events-none" />
</div>
</div>
<div :class="dividerClass"></div>
<!-- 样式 -->
<div class="flex gap-0.5">
<button :class="[btnClass, currentStyle.bold ? activeClass : '']" @click="$emit('action', 'toggleStyle', 'bold')"><Bold class="w-4 h-4" /></button>
<button :class="[btnClass, currentStyle.italic ? activeClass : '']" @click="$emit('action', 'toggleStyle', 'italic')"><Italic class="w-4 h-4" /></button>
<button :class="[btnClass, currentStyle.underline ? activeClass : '']" @click="$emit('action', 'toggleStyle', 'underline')"><Underline class="w-4 h-4" /></button>
<button :class="[btnClass, currentStyle.strike ? activeClass : '']" @click="$emit('action', 'toggleStyle', 'strike')"><Strikethrough class="w-4 h-4" /></button>
</div>
<div :class="dividerClass"></div>
<!-- 颜色选择 (使用新组件) -->
<div class="flex gap-0.5">
<ColorPicker
type="text"
:icon-component="Type"
:color="currentStyle.color"
@change="(c) => handleColorChange('color', c)"
/>
<ColorPicker
type="fill"
:icon-component="Palette"
:color="currentStyle.bg"
@change="(c) => handleColorChange('bg', c)"
/>
</div>
<div :class="dividerClass"></div>
<!-- 对齐方式 (修复状态回显确保 Cell.vue 中使用了 flex justify 来实现对齐store 中存储的是 'left'/'center'/'right') -->
<div class="flex gap-0.5">
<button :class="[btnClass, (!currentStyle.align || currentStyle.align === 'left') ? activeClass : '']" @click="$emit('action', 'setAlign', 'left')"><AlignLeft class="w-4 h-4" /></button>
<button :class="[btnClass, currentStyle.align === 'center' ? activeClass : '']" @click="$emit('action', 'setAlign', 'center')"><AlignCenter class="w-4 h-4" /></button>
<button :class="[btnClass, currentStyle.align === 'right' ? activeClass : '']" @click="$emit('action', 'setAlign', 'right')"><AlignRight class="w-4 h-4" /></button>
</div>
<div :class="dividerClass"></div>
<!-- 插入 -->
<div class="flex gap-0.5">
<button :class="btnClass" title="插入图片" @click="triggerImageUpload">
<ImageIcon class="w-4 h-4" />
</button>
<input type="file" ref="fileInput" class="hidden" accept="image/*" multiple @change="handleImageUpload">
</div>
<div :class="dividerClass"></div>
<div class="flex gap-1">
<button class="flex items-center gap-1 px-2 py-1 hover:bg-gray-200 rounded text-xs text-gray-700 transition" @click="$emit('action', 'addRow')">
<Plus class="w-3 h-3" />
</button>
<button class="flex items-center gap-1 px-2 py-1 hover:bg-gray-200 rounded text-xs text-gray-700 transition" @click="$emit('action', 'addCol')">
<Plus class="w-3 h-3" />
</button>
</div>
</div>
</template>