重构: tool_registry拆分及多批改进

This commit is contained in:
2026-06-19 00:10:14 +08:00
parent a2871a66e0
commit 60b01d03ee
33 changed files with 703 additions and 247 deletions

View File

@@ -33,23 +33,29 @@
<!-- 左侧灵感列表 -->
<section class="idea-list-panel">
<div class="idea-list">
<div
v-for="idea in filteredIdeas"
:key="idea.id"
class="idea-card"
:class="{ selected: selectedId === idea.id }"
@click="selectedId = idea.id"
>
<div class="idea-card-header">
<span class="idea-title">{{ idea.title }}</span>
<span class="idea-score" :class="scoreClass(idea.score)">{{ idea.score ?? '-' }}</span>
<!-- 三态:加载/错误/(对齐 Tasks.vue:43-45) -->
<div v-if="store.loading" class="empty-state">{{ $t('common.loading') }}</div>
<div v-else-if="store.error" class="empty-state">{{ store.error }}</div>
<div v-else-if="filteredIdeas.length === 0" class="empty-state">{{ $t('ideas.listEmpty') }}</div>
<template v-else>
<div
v-for="idea in filteredIdeas"
:key="idea.id"
class="idea-card"
:class="{ selected: selectedId === idea.id }"
@click="selectedId = idea.id"
>
<div class="idea-card-header">
<span class="idea-title">{{ idea.title }}</span>
<span class="idea-score" :class="scoreClass(idea.score)">{{ idea.score ?? '-' }}</span>
</div>
<p class="idea-desc-preview">{{ stripMd(idea.description).slice(0, 60) }}{{ stripMd(idea.description).length > 60 ? '...' : '' }}</p>
<div class="idea-card-footer">
<span class="status-tag" :class="'status-' + idea.status">{{ $t(statusLabelKey(idea.status)) }}</span>
<span class="idea-date">{{ formatDate(idea.created_at) }}</span>
</div>
</div>
<p class="idea-desc-preview">{{ stripMd(idea.description).slice(0, 60) }}{{ stripMd(idea.description).length > 60 ? '...' : '' }}</p>
<div class="idea-card-footer">
<span class="status-tag" :class="'status-' + idea.status">{{ $t(statusLabelKey(idea.status)) }}</span>
<span class="idea-date">{{ formatDate(idea.created_at) }}</span>
</div>
</div>
</template>
</div>
</section>
@@ -167,11 +173,14 @@
<button
v-if="currentIdea.status === 'approved' && !currentIdea.promoted_to"
class="btn btn-primary"
:disabled="promoting"
@click="promoteToProject"
>
{{ $t('ideas.promoteToProject') }}
{{ promoting ? $t('ideas.promoting') : $t('ideas.promoteToProject') }}
</button>
<button class="btn btn-ghost" :disabled="deleting" @click="deleteCurrentIdea">
{{ deleting ? $t('ideas.deleting') : $t('ideas.deleteIdea') }}
</button>
<button class="btn btn-ghost" @click="deleteCurrentIdea">{{ $t('ideas.deleteIdea') }}</button>
</div>
</div>
</section>
@@ -195,7 +204,9 @@
<textarea v-model="newIdeaDesc" :placeholder="$t('ideas.descPlaceholder')" rows="3" style="resize:vertical"></textarea>
<div class="modal-actions">
<button class="btn-cancel" @click="showCaptureModal = false">{{ $t('common.cancel') }}</button>
<button class="btn-confirm" @click="confirmCapture">{{ $t('common.confirm') }}</button>
<button class="btn-confirm" :disabled="creating" @click="confirmCapture">
{{ creating ? $t('ideas.creating') : $t('common.confirm') }}
</button>
</div>
</div>
</div>
@@ -238,6 +249,11 @@ const showCaptureModal = ref(false)
const newIdeaTitle = ref('')
const newIdeaDesc = ref('')
// ── 异步按钮禁用态(防双击重复提交,对齐 ProviderPanel.vue saving ref) ──
const creating = ref(false)
const deleting = ref(false)
const promoting = ref(false)
const filters: { key: FilterKey; labelKey: string; icon: string }[] = [
{ key: 'all', labelKey: 'ideas.filter.all', icon: '📋' },
{ key: 'hot', labelKey: 'ideas.filter.hot', icon: '🔥' },
@@ -383,23 +399,34 @@ function openCaptureModal() {
}
async function confirmCapture() {
if (!newIdeaTitle.value.trim()) return
await store.createIdea({
title: newIdeaTitle.value.trim(),
description: newIdeaDesc.value.trim() || undefined,
})
showCaptureModal.value = false
if (!newIdeaTitle.value.trim() || creating.value) return
creating.value = true
try {
await store.createIdea({
title: newIdeaTitle.value.trim(),
description: newIdeaDesc.value.trim() || undefined,
})
showCaptureModal.value = false
} finally {
creating.value = false
}
}
async function deleteCurrentIdea() {
if (!currentIdea.value) return
if (!currentIdea.value || deleting.value) return
if (!await confirmDialog(t('ideas.confirmDelete', { title: currentIdea.value.title }))) return
await store.deleteIdea(currentIdea.value.id)
selectedId.value = null
deleting.value = true
try {
await store.deleteIdea(currentIdea.value.id)
selectedId.value = null
} finally {
deleting.value = false
}
}
async function promoteToProject() {
if (!currentIdea.value) return
if (!currentIdea.value || promoting.value) return
promoting.value = true
try {
const res = await store.promoteIdea(currentIdea.value.id)
router.push(`/projects/${res.project_id}`)
@@ -407,6 +434,8 @@ async function promoteToProject() {
const msg = e?.toString() ?? t('ideas.promoteFailed')
console.error(t('ideas.promoteFailed'), e)
Message.error(msg)
} finally {
promoting.value = false
}
}