调整,音乐模块

This commit is contained in:
zhongluofeng
2026-09-12 11:05:26 +08:00
parent 27ad5d89a5
commit d702ed0d31
71 changed files with 13647 additions and 387 deletions
+157
View File
@@ -0,0 +1,157 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { ArrowLeft, Search } from '@lucide/vue'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Separator } from '@/components/ui/separator'
import { useSearchStore } from '@/stores/searchStore'
import { registerAllTools, TOOLS_META } from './tools'
import {
getAllTools, getTool, searchTools,
CATEGORY_ORDER, CATEGORY_LABEL, type DevTool
} from './registry'
const searchStore = useSearchStore()
registerAllTools()
const allTools = getAllTools()
// ===== 列表视图:搜索 + 分类筛选 =====
const query = ref('')
const category = ref<'all' | DevTool['category']>('all')
const filteredTools = computed(() => {
const list = searchTools(query.value)
if (category.value === 'all') return list
return list.filter(t => t.category === category.value)
})
interface ToolGroup {
category: DevTool['category']
label: string
tools: DevTool[]
}
const groupedTools = computed<ToolGroup[]>(() => {
if (category.value !== 'all') {
return [{
category: category.value as DevTool['category'],
label: CATEGORY_LABEL[category.value as DevTool['category']],
tools: filteredTools.value
}]
}
const groups: ToolGroup[] = []
for (const cat of CATEGORY_ORDER) {
const tools = filteredTools.value.filter(t => t.category === cat)
if (tools.length > 0) groups.push({ category: cat, label: CATEGORY_LABEL[cat], tools })
}
return groups
})
// ===== 详情视图 =====
const selectedId = ref<string | null>(null)
const selectedTool = computed<DevTool | null>(() => {
if (!selectedId.value) return null
return getTool(selectedId.value) ?? null
})
function openTool(id: string) {
selectedId.value = id
}
// ===== 全局搜索跳转:工具索引 → 打开对应工具 =====
onMounted(() => {
const actions = new Map<number, () => void>()
TOOLS_META.forEach((t, i) => {
actions.set(i, () => openTool(t.id))
})
searchStore.registerActions('devtools', actions)
})
watch(selectedId, () => {
query.value = ''
})
</script>
<template>
<div class="h-full p-6 overflow-hidden flex flex-col">
<!-- 列表视图 -->
<template v-if="!selectedTool">
<div class="flex items-center gap-3 mb-4 shrink-0 flex-wrap">
<div class="relative flex-1 min-w-[180px] max-w-xs">
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<Input v-model="query" placeholder="搜索工具..." class="h-8 pl-8 text-sm" />
</div>
<Button
v-for="cat in (['all', ...CATEGORY_ORDER] as const)"
:key="cat"
size="sm"
variant="ghost"
class="h-7 px-2.5 text-xs"
:class="category === cat ? 'bg-secondary text-foreground' : 'text-muted-foreground'"
@click="category = cat as typeof category"
>
{{ cat === 'all' ? '全部' : CATEGORY_LABEL[cat as DevTool['category']] }}
</Button>
<span class="text-xs text-muted-foreground ml-auto">{{ (category === 'all' ? allTools : filteredTools).length }} 个工具</span>
</div>
<ScrollArea class="flex-1 min-h-0 -mr-3 pr-3">
<div v-if="filteredTools.length === 0" class="h-full flex items-center justify-center text-muted-foreground text-sm">
没有找到匹配的工具
</div>
<!-- 按分类分组 -->
<div v-for="group in groupedTools" :key="group.category" class="mb-5">
<div class="flex items-center gap-2 mb-2">
<span class="text-xs font-medium text-muted-foreground">{{ group.label }}</span>
<Separator class="flex-1" />
</div>
<div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-3">
<Card
v-for="tool in group.tools"
:key="tool.id"
class="cursor-pointer transition-all hover:border-primary/50 hover:shadow-sm !py-0 !gap-0"
@click="openTool(tool.id)"
>
<CardContent class="p-4 flex flex-col gap-2">
<div class="size-9 rounded-lg bg-primary/10 text-primary flex items-center justify-center">
<component :is="tool.icon" class="size-5" />
</div>
<div class="flex flex-col gap-0.5">
<span class="text-sm font-medium leading-tight">{{ tool.name }}</span>
<span class="text-xs text-muted-foreground leading-snug line-clamp-2">{{ tool.description }}</span>
</div>
</CardContent>
</Card>
</div>
</div>
</ScrollArea>
</template>
<!-- 工具详情视图 -->
<template v-else>
<div class="flex items-center gap-3 mb-4 shrink-0">
<Button size="sm" variant="ghost" class="gap-1 h-8" @click="selectedId = null">
<ArrowLeft class="size-4" />
返回
</Button>
<div class="size-8 rounded-lg bg-primary/10 text-primary flex items-center justify-center">
<component :is="selectedTool.icon" class="size-4" />
</div>
<div class="flex flex-col">
<span class="text-sm font-medium leading-tight">{{ selectedTool.name }}</span>
<span class="text-xs text-muted-foreground leading-snug">{{ selectedTool.description }}</span>
</div>
</div>
<ScrollArea class="flex-1 min-h-0 -mr-3 pr-3">
<div class="max-w-3xl px-1 pb-6">
<component :is="selectedTool.component" />
</div>
</ScrollArea>
</template>
</div>
</template>