修复: 多视图UX(知识库窄屏CSS+任务详情路由+项目灵感视图)
This commit is contained in:
@@ -126,15 +126,13 @@ const stats = computed(() => [
|
||||
])
|
||||
|
||||
function getProjectStage(status: string): { stage: string; stageLabelKey: string; progress: number } {
|
||||
// stage(颜色档)与 stageLabelKey(文案)语义必须一致:
|
||||
// planning/cancelled 不再借用 coding/testing 颜色(否则颜色撒谎),改用专属颜色档。
|
||||
// F-09 对齐:project status 死键(in_progress/paused/cancelled)已从后端映射移除,
|
||||
// 此处 switch 同步收敛,与 constants/project.ts 的 PROJECT_STAGE_INFO 语义一致。
|
||||
switch (status) {
|
||||
case 'planning': return { stage: 'planning', stageLabelKey: 'planning', progress: 20 }
|
||||
case 'in_progress': return { stage: 'coding', stageLabelKey: 'coding', progress: 55 }
|
||||
case 'paused': return { stage: 'testing', stageLabelKey: 'paused', progress: 40 }
|
||||
case 'active': return { stage: 'coding', stageLabelKey: 'coding', progress: 55 }
|
||||
case 'completed': return { stage: 'release', stageLabelKey: 'done', progress: 100 }
|
||||
case 'cancelled': return { stage: 'cancelled', stageLabelKey: 'cancelled', progress: 0 }
|
||||
default: return { stage: 'coding', stageLabelKey: 'coding', progress: 30 }
|
||||
default: return { stage: 'planning', stageLabelKey: 'planning', progress: 20 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,8 +206,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
@@ -219,6 +219,7 @@ import type { IdeaRecord } from '../api/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const store = useProjectStore()
|
||||
|
||||
// 确认弹层状态机抽至 composables/useConfirm(原 4 视图重复:Projects/ProjectDetail/Ideas/Settings)
|
||||
@@ -448,6 +449,27 @@ async function onStatusChange(e: Event) {
|
||||
onMounted(async () => {
|
||||
ensureLoaded() // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
|
||||
await store.loadIdeas()
|
||||
// 从路由参数恢复选中灵感(修复 /ideas/:id 直接访问空白页 B-260615-36)
|
||||
if (route.params.id) {
|
||||
const id = route.params.id as string
|
||||
const exists = store.ideas.some(i => i.id === id)
|
||||
if (!exists) {
|
||||
Message.warning(t('ideas.notFound'))
|
||||
}
|
||||
selectedId.value = id
|
||||
}
|
||||
})
|
||||
|
||||
// 同组件内路由变化时同步 selectedId(列表点击跳 /ideas/:id)
|
||||
watch(() => route.params.id, (id) => {
|
||||
if (id) {
|
||||
const ideaId = id as string
|
||||
const exists = store.ideas.some(i => i.id === ideaId)
|
||||
if (!exists) {
|
||||
Message.warning(t('ideas.notFound'))
|
||||
}
|
||||
selectedId.value = ideaId
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -811,62 +833,8 @@ onMounted(async () => {
|
||||
margin-bottom: var(--df-gap-page);
|
||||
}
|
||||
|
||||
/* ===== 灵感描述 Markdown 渲染(B-260615-25,样式同 TaskDetail B-24 .ai-md 收敛) ===== */
|
||||
/* ===== 灵感描述 Markdown 渲染(B-260615-25,基础样式收敛至全局 ai-md.css) ===== */
|
||||
.detail-desc.ai-md { white-space: normal; color: var(--df-text-secondary); }
|
||||
.detail-desc.ai-md :deep(p) { margin: 0 0 6px; }
|
||||
.detail-desc.ai-md :deep(p:last-child) { margin-bottom: 0; }
|
||||
.detail-desc.ai-md :deep(ul), .detail-desc.ai-md :deep(ol) { margin: 4px 0; padding-left: 20px; }
|
||||
.detail-desc.ai-md :deep(li) { margin: 2px 0; line-height: 1.5; }
|
||||
.detail-desc.ai-md :deep(code) {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 12px;
|
||||
padding: 1px 5px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: var(--df-radius-sm);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.detail-desc.ai-md :deep(pre) {
|
||||
margin: 8px 0;
|
||||
padding: 10px 12px;
|
||||
background: var(--df-bg);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.detail-desc.ai-md :deep(pre code) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
color: var(--df-text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.detail-desc.ai-md :deep(blockquote) {
|
||||
margin: 6px 0;
|
||||
padding: 4px 12px;
|
||||
border-left: 2px solid var(--df-accent);
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.detail-desc.ai-md :deep(h1), .detail-desc.ai-md :deep(h2), .detail-desc.ai-md :deep(h3) {
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
margin: 8px 0 4px;
|
||||
}
|
||||
.detail-desc.ai-md :deep(h1) { font-size: 16px; }
|
||||
.detail-desc.ai-md :deep(h2) { font-size: 14px; }
|
||||
.detail-desc.ai-md :deep(h3) { font-size: 13px; }
|
||||
.detail-desc.ai-md :deep(a) { color: var(--df-accent); text-decoration: none; }
|
||||
.detail-desc.ai-md :deep(a:hover) { text-decoration: underline; }
|
||||
.detail-desc.ai-md :deep(strong) { font-weight: 500; color: var(--df-text); }
|
||||
.detail-desc.ai-md :deep(hr) { border: none; border-top: 0.5px solid var(--df-border); margin: 8px 0; }
|
||||
.detail-desc.ai-md :deep(table) {
|
||||
width: 100%; border-collapse: collapse; margin: 6px 0;
|
||||
font-size: 12px; overflow-x: auto; display: block;
|
||||
}
|
||||
.detail-desc.ai-md :deep(th), .detail-desc.ai-md :deep(td) {
|
||||
padding: 4px 8px; border: 0.5px solid var(--df-border); text-align: left;
|
||||
}
|
||||
.detail-desc.ai-md :deep(th) { font-weight: 500; background: var(--df-bg); }
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: var(--df-gap-page);
|
||||
|
||||
@@ -564,7 +564,7 @@ onMounted(() => {
|
||||
|
||||
/* ===== 左右分栏 ===== */
|
||||
.kn-layout {
|
||||
display: grid; grid-template-columns: 360px 1fr; gap: var(--df-gap-page);
|
||||
display: grid; grid-template-columns: minmax(240px, 360px) 1fr; gap: var(--df-gap-page);
|
||||
flex: 1; min-height: 0;
|
||||
}
|
||||
.kn-list-panel, .kn-detail-panel {
|
||||
@@ -573,7 +573,7 @@ onMounted(() => {
|
||||
border-radius: var(--df-radius-lg);
|
||||
overflow: hidden; display: flex; flex-direction: column;
|
||||
}
|
||||
.kn-detail-panel { overflow-y: auto; padding: 20px; }
|
||||
.kn-detail-panel { overflow-y: auto; padding: 20px; min-width: 0; }
|
||||
|
||||
/* 左侧搜索 + 分类 */
|
||||
.search-bar { padding: 12px 12px 0; }
|
||||
@@ -631,62 +631,8 @@ onMounted(() => {
|
||||
.detail-field label { display: block; font-size: 12px; color: var(--df-text-secondary); margin-bottom: 4px; }
|
||||
.detail-content { font-size: 13px; color: var(--df-text); line-height: 1.6; white-space: pre-wrap; }
|
||||
|
||||
/* ===== 知识内容 Markdown 渲染(B-260615-25,样式同 TaskDetail B-24 .ai-md 收敛) ===== */
|
||||
/* ===== 知识内容 Markdown 渲染(B-260615-25,基础样式收敛至全局 ai-md.css) ===== */
|
||||
.detail-content.ai-md { white-space: normal; }
|
||||
.detail-content.ai-md :deep(p) { margin: 0 0 6px; }
|
||||
.detail-content.ai-md :deep(p:last-child) { margin-bottom: 0; }
|
||||
.detail-content.ai-md :deep(ul), .detail-content.ai-md :deep(ol) { margin: 4px 0; padding-left: 20px; }
|
||||
.detail-content.ai-md :deep(li) { margin: 2px 0; line-height: 1.5; }
|
||||
.detail-content.ai-md :deep(code) {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 12px;
|
||||
padding: 1px 5px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: var(--df-radius-sm);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.detail-content.ai-md :deep(pre) {
|
||||
margin: 8px 0;
|
||||
padding: 10px 12px;
|
||||
background: var(--df-bg);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.detail-content.ai-md :deep(pre code) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
color: var(--df-text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.detail-content.ai-md :deep(blockquote) {
|
||||
margin: 6px 0;
|
||||
padding: 4px 12px;
|
||||
border-left: 2px solid var(--df-accent);
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.detail-content.ai-md :deep(h1), .detail-content.ai-md :deep(h2), .detail-content.ai-md :deep(h3) {
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
margin: 8px 0 4px;
|
||||
}
|
||||
.detail-content.ai-md :deep(h1) { font-size: 16px; }
|
||||
.detail-content.ai-md :deep(h2) { font-size: 14px; }
|
||||
.detail-content.ai-md :deep(h3) { font-size: 13px; }
|
||||
.detail-content.ai-md :deep(a) { color: var(--df-accent); text-decoration: none; }
|
||||
.detail-content.ai-md :deep(a:hover) { text-decoration: underline; }
|
||||
.detail-content.ai-md :deep(strong) { font-weight: 500; color: var(--df-text); }
|
||||
.detail-content.ai-md :deep(hr) { border: none; border-top: 0.5px solid var(--df-border); margin: 8px 0; }
|
||||
.detail-content.ai-md :deep(table) {
|
||||
width: 100%; border-collapse: collapse; margin: 6px 0;
|
||||
font-size: 12px; overflow-x: auto; display: block;
|
||||
}
|
||||
.detail-content.ai-md :deep(th), .detail-content.ai-md :deep(td) {
|
||||
padding: 4px 8px; border: 0.5px solid var(--df-border); text-align: left;
|
||||
}
|
||||
.detail-content.ai-md :deep(th) { font-weight: 500; background: var(--df-bg); }
|
||||
.detail-tags { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.tag { font-size: 11px; padding: 2px 8px; border-radius: var(--df-radius-xs); background: rgba(108,99,255,0.1); color: var(--df-accent); }
|
||||
.muted { color: var(--df-text-dim); font-size: 12px; }
|
||||
@@ -778,4 +724,10 @@ onMounted(() => {
|
||||
.form-input:focus, .form-select:focus { border-color: var(--df-accent); }
|
||||
.form-textarea { min-height: 80px; resize: vertical; font-family: inherit; }
|
||||
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
|
||||
|
||||
/* ===== 响应式:窄屏详情标题防竖线化(B-260616-19) ===== */
|
||||
@media (max-width: 760px) {
|
||||
.kn-layout { grid-template-columns: 1fr; }
|
||||
.detail-title { font-size: 15px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -272,7 +272,7 @@ const currentProject = computed(() =>
|
||||
)
|
||||
// B-260615-25:项目描述 Markdown 渲染(复用 AiChat/TaskDetail 同款渲染器,模块级单例),
|
||||
// useRendered 封装 computed(读 mdReady 触发响应式 + renderMd)+ ensureLoaded(幂等预热)
|
||||
const { rendered: renderedDesc, ensureLoaded: ensureMdLoaded } = useRendered(
|
||||
const { rendered: renderedDesc, ensureLoaded } = useRendered(
|
||||
() => currentProject.value?.description ?? '',
|
||||
)
|
||||
const stageKey = computed(() => currentProject.value?.status ?? 'planning')
|
||||
@@ -378,7 +378,7 @@ async function relocateDir() {
|
||||
await checkPath()
|
||||
} catch (e: any) {
|
||||
console.error('重定位失败:', e)
|
||||
Message.error(t('projectDetail.relocateFailed', { msg: e?.toString() ?? '未知错误' }))
|
||||
Message.error(t('projectDetail.relocateFailed', { msg: e?.toString() ?? t('common.unknownError') }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ watch(() => store.pendingApproval, (newApproval) => {
|
||||
|
||||
// ── 生命周期 ──
|
||||
onMounted(async () => {
|
||||
ensureMdLoaded() // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
|
||||
ensureLoaded() // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
|
||||
await store.loadProjects()
|
||||
await store.loadTasks() // 全量加载,详情页用 computed 过滤本项目(避免覆盖全局 tasks 单例)
|
||||
unlisten = await store.startEventListener()
|
||||
@@ -544,62 +544,8 @@ onUnmounted(() => {
|
||||
border: 0.5px solid var(--df-border);
|
||||
}
|
||||
|
||||
/* ===== 项目描述 Markdown 渲染(B-260615-25,样式同 TaskDetail B-24 .ai-md 收敛) ===== */
|
||||
/* ===== 项目描述 Markdown 渲染(B-260615-25,基础样式收敛至全局 ai-md.css) ===== */
|
||||
.description.ai-md { font-size: 14px; color: var(--df-text); line-height: 1.6; }
|
||||
.description.ai-md :deep(p) { margin: 0 0 6px; }
|
||||
.description.ai-md :deep(p:last-child) { margin-bottom: 0; }
|
||||
.description.ai-md :deep(ul), .description.ai-md :deep(ol) { margin: 4px 0; padding-left: 20px; }
|
||||
.description.ai-md :deep(li) { margin: 2px 0; line-height: 1.5; }
|
||||
.description.ai-md :deep(code) {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 12px;
|
||||
padding: 1px 5px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: var(--df-radius-sm);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.description.ai-md :deep(pre) {
|
||||
margin: 8px 0;
|
||||
padding: 10px 12px;
|
||||
background: var(--df-bg);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.description.ai-md :deep(pre code) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
color: var(--df-text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.description.ai-md :deep(blockquote) {
|
||||
margin: 6px 0;
|
||||
padding: 4px 12px;
|
||||
border-left: 2px solid var(--df-accent);
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.description.ai-md :deep(h1), .description.ai-md :deep(h2), .description.ai-md :deep(h3) {
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
margin: 8px 0 4px;
|
||||
}
|
||||
.description.ai-md :deep(h1) { font-size: 16px; }
|
||||
.description.ai-md :deep(h2) { font-size: 14px; }
|
||||
.description.ai-md :deep(h3) { font-size: 13px; }
|
||||
.description.ai-md :deep(a) { color: var(--df-accent); text-decoration: none; }
|
||||
.description.ai-md :deep(a:hover) { text-decoration: underline; }
|
||||
.description.ai-md :deep(strong) { font-weight: 500; color: var(--df-text); }
|
||||
.description.ai-md :deep(hr) { border: none; border-top: 0.5px solid var(--df-border); margin: 8px 0; }
|
||||
.description.ai-md :deep(table) {
|
||||
width: 100%; border-collapse: collapse; margin: 6px 0;
|
||||
font-size: 12px; overflow-x: auto; display: block;
|
||||
}
|
||||
.description.ai-md :deep(th), .description.ai-md :deep(td) {
|
||||
padding: 4px 8px; border: 0.5px solid var(--df-border); text-align: left;
|
||||
}
|
||||
.description.ai-md :deep(th) { font-weight: 500; background: var(--df-bg); }
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user