重构: tool_registry拆分及多批改进
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user