update at 2026-02-07 14:16:46
This commit is contained in:
@@ -1,28 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import selectAllIcon from '../assets/icons/selectall.svg'
|
||||
import unselectAllIcon from '../assets/icons/unselectall.svg'
|
||||
import { deleteFontFile, renameFontFile } from '../services/font-file-api'
|
||||
import type { FontInfo, FontTreeNode } from '../types/font'
|
||||
import type { FontTreeNode } from '../types/font'
|
||||
import { useFontStore } from '../stores/fontStore'
|
||||
import { toFontInfoList } from '../utils/font-list'
|
||||
|
||||
const props = defineProps<{
|
||||
nodes: FontTreeNode[]
|
||||
}>()
|
||||
|
||||
const fontStore = useFontStore()
|
||||
const contextMenu = ref<{
|
||||
visible: boolean
|
||||
x: number
|
||||
y: number
|
||||
fontInfo: FontInfo | null
|
||||
}>({
|
||||
visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
fontInfo: null,
|
||||
})
|
||||
|
||||
function toggleExpand(node: FontTreeNode) {
|
||||
const next = !node.expanded
|
||||
@@ -81,105 +67,6 @@ function handleCategorySelectAll(node: FontTreeNode, event: Event) {
|
||||
ids.forEach(id => fontStore.addToPreview(id))
|
||||
}
|
||||
}
|
||||
|
||||
function closeContextMenu() {
|
||||
contextMenu.value.visible = false
|
||||
contextMenu.value.fontInfo = null
|
||||
}
|
||||
|
||||
function openFontContextMenu(node: FontTreeNode, event: MouseEvent) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
if (node.type !== 'font' || !node.fontInfo) {
|
||||
closeContextMenu()
|
||||
return
|
||||
}
|
||||
|
||||
const menuWidth = 140
|
||||
const menuHeight = 80
|
||||
const safeX = Math.min(event.clientX, window.innerWidth - menuWidth - 8)
|
||||
const safeY = Math.min(event.clientY, window.innerHeight - menuHeight - 8)
|
||||
|
||||
contextMenu.value = {
|
||||
visible: true,
|
||||
x: Math.max(8, safeX),
|
||||
y: Math.max(8, safeY),
|
||||
fontInfo: node.fontInfo,
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRenameFromContextMenu() {
|
||||
const targetFont = contextMenu.value.fontInfo
|
||||
if (!targetFont) {
|
||||
return
|
||||
}
|
||||
|
||||
closeContextMenu()
|
||||
|
||||
const renamed = window.prompt('请输入新的字体名称', targetFont.name)
|
||||
if (renamed === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const nextName = renamed.trim()
|
||||
if (!nextName || nextName === targetFont.name) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const fontList = await renameFontFile(targetFont.path, nextName)
|
||||
fontStore.replaceFonts(toFontInfoList(fontList))
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '字体重命名失败'
|
||||
window.alert(message)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteFromContextMenu() {
|
||||
const targetFont = contextMenu.value.fontInfo
|
||||
if (!targetFont) {
|
||||
return
|
||||
}
|
||||
|
||||
closeContextMenu()
|
||||
|
||||
const confirmed = window.confirm(`确认删除字体“${targetFont.name}”?此操作不可恢复。`)
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const fontList = await deleteFontFile(targetFont.path)
|
||||
fontStore.replaceFonts(toFontInfoList(fontList))
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '字体删除失败'
|
||||
window.alert(message)
|
||||
}
|
||||
}
|
||||
|
||||
function handleGlobalPointerDown() {
|
||||
if (!contextMenu.value.visible) {
|
||||
return
|
||||
}
|
||||
closeContextMenu()
|
||||
}
|
||||
|
||||
function handleGlobalKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') {
|
||||
closeContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('pointerdown', handleGlobalPointerDown)
|
||||
window.addEventListener('keydown', handleGlobalKeyDown)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('pointerdown', handleGlobalPointerDown)
|
||||
window.removeEventListener('keydown', handleGlobalKeyDown)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -242,7 +129,6 @@ onBeforeUnmount(() => {
|
||||
v-for="child in node.children"
|
||||
:key="child.name"
|
||||
class="flex items-center gap-2 border-b border-[#c9cdd4] pb-2 relative"
|
||||
@contextmenu.prevent="openFontContextMenu(child, $event)"
|
||||
>
|
||||
<!-- 水平连接线 -->
|
||||
<div class="tree-horizontal-line"></div>
|
||||
@@ -283,29 +169,6 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<teleport to="body">
|
||||
<div
|
||||
v-if="contextMenu.visible"
|
||||
class="fixed z-[2000] min-w-[140px] rounded-md border border-[#d9d9d9] bg-white shadow-md overflow-hidden"
|
||||
:style="{ left: `${contextMenu.x}px`, top: `${contextMenu.y}px` }"
|
||||
@pointerdown.stop
|
||||
@contextmenu.prevent
|
||||
>
|
||||
<button
|
||||
class="w-full h-9 px-3 text-left text-sm text-[#1f2329] hover:bg-[#f2f3f5] border-0 bg-transparent cursor-pointer"
|
||||
@click="handleRenameFromContextMenu"
|
||||
>
|
||||
重命名
|
||||
</button>
|
||||
<button
|
||||
class="w-full h-9 px-3 text-left text-sm text-[#f53f3f] hover:bg-[#fff2f0] border-0 bg-transparent cursor-pointer"
|
||||
@click="handleDeleteFromContextMenu"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user