Web优化 2

This commit is contained in:
2026-06-12 14:36:41 +08:00
parent 7e9df04bf8
commit 61a1a4d368
31 changed files with 551 additions and 918 deletions
@@ -1,10 +1,7 @@
<template>
<AppShell>
<NSpin :show="loading">
<div
v-if="data"
class="space-y-6"
>
<div v-if="data" class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
@@ -12,10 +9,7 @@
<h1 class="text-2xl font-semibold">
{{ data.requirement.title }}
</h1>
<NTag
:type="data.requirement.status === 'open' ? 'success' : 'default'"
size="small"
>
<NTag :type="data.requirement.status === 'open' ? 'success' : 'default'" size="small">
{{ data.requirement.status === 'open' ? '开放' : '关闭' }}
</NTag>
</div>
@@ -29,30 +23,16 @@
>
关闭需求
</NButton>
<NButton
v-else
type="primary"
size="small"
:loading="toggling"
@click="onReopen"
>
<NButton v-else type="primary" size="small" :loading="toggling" @click="onReopen">
重新开放
</NButton>
<NButton
size="small"
@click="router.back()"
>
返回
</NButton>
<NButton size="small" @click="router.back()"> 返回 </NButton>
</div>
</div>
<!-- Phase flow bar阶段跳转始终由用户确认触发 -->
<div class="flex items-center gap-1">
<template
v-for="(phase, idx) in PHASES"
:key="phase"
>
<template v-for="(phase, idx) in PHASES" :key="phase">
<NButton
:type="phase === data.requirement.phase ? 'primary' : 'default'"
:secondary="phase !== data.requirement.phase"
@@ -62,10 +42,7 @@
>
{{ PHASE_LABELS[phase] }}
</NButton>
<span
v-if="idx < PHASES.length - 1"
class="text-gray-300 text-xs"
></span>
<span v-if="idx < PHASES.length - 1" class="text-gray-300 text-xs"></span>
</template>
</div>
@@ -74,18 +51,10 @@
<div class="w-[45%] shrink-0 space-y-6">
<!-- Description -->
<NCard title="描述">
<p
v-if="data.requirement.description"
class="text-gray-700 whitespace-pre-wrap"
>
<p v-if="data.requirement.description" class="text-gray-700 whitespace-pre-wrap">
{{ data.requirement.description }}
</p>
<p
v-else
class="text-gray-400 text-sm"
>
暂无描述
</p>
<p v-else class="text-gray-400 text-sm">暂无描述</p>
</NCard>
<!-- 当前阶段产物 AI 对话三阶段显示保留编辑功能 -->
@@ -122,34 +91,22 @@
<!-- Linked Issues -->
<NCard title="关联 Issue">
<NList
v-if="data.issues.length > 0"
:show-divider="false"
>
<NListItem
v-for="issue in data.issues"
:key="issue.id"
>
<NList v-if="data.issues.length > 0" :show-divider="false">
<NListItem v-for="issue in data.issues" :key="issue.id">
<NThing>
<template #header>
<span class="font-mono text-xs text-gray-400 mr-2">#{{ issue.number }}</span>
<span class="text-sm font-medium">{{ issue.title }}</span>
</template>
<template #header-extra>
<NTag
:type="issue.status === 'open' ? 'success' : 'default'"
size="tiny"
>
<NTag :type="issue.status === 'open' ? 'success' : 'default'" size="tiny">
{{ issue.status === 'open' ? '开放' : '关闭' }}
</NTag>
</template>
</NThing>
</NListItem>
</NList>
<NEmpty
v-else
description="暂无关联 Issue"
/>
<NEmpty v-else description="暂无关联 Issue" />
</NCard>
</div>
@@ -172,19 +129,12 @@
:slug="slug"
:requirement="data.requirement"
/>
<RequirementSummaryPanel
v-else
:slug="slug"
:number="number"
/>
<RequirementSummaryPanel v-else :slug="slug" :number="number" />
</div>
</div>
</div>
<div
v-else-if="!loading"
class="text-gray-400 text-sm py-8 text-center"
>
<div v-else-if="!loading" class="text-gray-400 text-sm py-8 text-center">
需求不存在或无权访问。
</div>
</NSpin>
@@ -195,17 +145,7 @@
import { ref, computed, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useDialog } from 'naive-ui'
import {
NCard,
NButton,
NTag,
NSpin,
NSelect,
NList,
NListItem,
NThing,
NEmpty,
} from 'naive-ui'
import { NCard, NButton, NTag, NSpin, NSelect, NList, NListItem, NThing, NEmpty } from 'naive-ui'
import AppShell from '@/layouts/AppShell.vue'
import { projectsApi } from '@/api/projects'
import { useRequirementsStore } from '@/stores/requirements'
@@ -295,8 +235,7 @@ function onArtifactSaved() {
async function onWorkspaceChange(v: string | null) {
if (!data.value) return
await projectsApi.updateRequirement(slug, number, { workspace_id: v })
// Re-fetch to keep data.requirement and data.issues consistent (mirrors onPhaseClick)
data.value = await projectsApi.getRequirement(slug, number)
data.value.requirement.workspace_id = v
}
function onPhaseClick(target: Phase) {
@@ -308,9 +247,8 @@ function onPhaseClick(target: Phase) {
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await store.changePhase(slug, number, target)
// Refresh local data to reflect the change
data.value = await projectsApi.getRequirement(slug, number)
const updated = await store.changePhase(slug, number, target)
data.value!.requirement = updated
},
})
}
@@ -319,7 +257,7 @@ async function onClose() {
toggling.value = true
try {
await store.close(slug, number)
data.value = await projectsApi.getRequirement(slug, number)
data.value!.requirement.status = 'closed'
} finally {
toggling.value = false
}
@@ -329,7 +267,7 @@ async function onReopen() {
toggling.value = true
try {
await store.reopen(slug, number)
data.value = await projectsApi.getRequirement(slug, number)
data.value!.requirement.status = 'open'
} finally {
toggling.value = false
}