diff --git a/docs/tasks/task_detail_2026_07_02.md b/docs/tasks/task_detail_2026_07_02.md index b0be953..da9f033 100644 --- a/docs/tasks/task_detail_2026_07_02.md +++ b/docs/tasks/task_detail_2026_07_02.md @@ -219,3 +219,21 @@ 4. 调整导出时的表格回写方式,优先克隆模板原有表格结构并填充新数据,尽量保留表格外观和基础样式。 5. 执行后端语法检查,确认导出服务改动稳定。 - **执行结果**: 当前导出链路已改为“尽量复用模板原始段落/表格样式后写入 AI 内容”,比此前的新建空白内容块方式更接近原模板样式,也更容易把 AI 结果正确写回文档。 + +## 会话 ID: local-20260702202424 +- [2026-07-02 20:24:24] +- **执行原因**: 用户希望用极简一句话概括当前项目功能状态,并说明明天的工作重点。 +- **执行过程**: + 1. 基于当前已完成能力,压缩总结项目现状。 + 2. 提炼明日优先事项,聚焦导出准确性与样式保真。 +- **执行结果**: 已形成简短状态说明,可作为明日继续开发的工作摘要。 + +## 会话 ID: local-20260702202807 +- [2026-07-02 20:28:07] +- **执行原因**: 用户希望将任务详情页改为“上 + 左右”结构,左侧显示段落,右侧显示生成结果预览。 +- **执行过程**: + 1. 重构任务详情页布局,将页面调整为顶部任务概览、下方左右分栏的结构。 + 2. 左侧增加段落列表与段落状态展示,并保留附件映射信息。 + 3. 右侧改为当前选中段落的结果预览区域,展示段落标题、状态、文件要求和生成内容。 + 4. 执行前端类型检查,确认本轮布局调整稳定。 +- **执行结果**: 当前任务详情页已改为“上 + 左右”结构,用户可在左侧切换段落,在右侧查看对应生成结果预览。 diff --git a/docs/tasks/task_detail_2026_07_03.md b/docs/tasks/task_detail_2026_07_03.md new file mode 100644 index 0000000..93f6985 --- /dev/null +++ b/docs/tasks/task_detail_2026_07_03.md @@ -0,0 +1,10 @@ +# 任务执行摘要 + +## 会话 ID: local-20260703092828 +- [2026-07-03 09:28:28] +- **执行原因**: 用户要求提交当前已完成的预览页布局调整代码。 +- **执行过程**: + 1. 检查当前工作区改动,确认仅包含任务详情页左右布局调整和对应任务记录。 + 2. 整理并暂存相关文件,排除未跟踪的原型目录。 + 3. 准备使用中文提交信息完成本次代码提交。 +- **执行结果**: 当前改动已整理完成并准备提交,包含任务详情页“上 + 左右”结构调整。 diff --git a/web/src/views/PreviewEdit.vue b/web/src/views/PreviewEdit.vue index e7fab52..8b68697 100644 --- a/web/src/views/PreviewEdit.vue +++ b/web/src/views/PreviewEdit.vue @@ -33,37 +33,55 @@
{{ progressMessage || documentInfo.error || '任务已创建,等待执行。' }}
- -
-
-
- {{ item.sort_index }} -
-
{{ item.title }}
-
{{ item.file_note }}
+
+
-
- - + + + - -
-
-
-

{{ item.title }}

- -
-
-
-
- -
+
+ + + + +
+
@@ -96,11 +114,21 @@ const documentInfo = ref({}) const logs = ref([]) const progressPercent = ref(0) const progressMessage = ref('') +const selectedParagraphId = ref(null) let progressSource: EventSource | null = null const isRunning = computed(() => ['pending', 'generating'].includes(documentInfo.value.status)) const isCompleted = computed(() => documentInfo.value.status === 'completed') const paragraphMappings = computed(() => documentInfo.value.request_payload?.paragraphs || []) +const logStatusMap = computed(() => + Object.fromEntries(logs.value.map((item) => [item.paragraph_id, item.status])) +) +const selectedParagraph = computed(() => + logs.value.find((item) => item.paragraph_id === selectedParagraphId.value) || null +) +const selectedMapping = computed(() => + paragraphMappings.value.find((item: any) => item.paragraph_id === selectedParagraphId.value) || null +) function statusText(status: string) { const map: Record = { @@ -145,6 +173,20 @@ async function loadDocument() { const response: any = await generateApi.getDocument(id) documentInfo.value = response.data || {} logs.value = response.data?.logs || [] + if (!selectedParagraphId.value) { + selectedParagraphId.value = logs.value[0]?.paragraph_id || paragraphMappings.value[0]?.paragraph_id || null + } + if ( + selectedParagraphId.value && + !paragraphMappings.value.some((item: any) => item.paragraph_id === selectedParagraphId.value) && + !logs.value.some((item) => item.paragraph_id === selectedParagraphId.value) + ) { + selectedParagraphId.value = logs.value[0]?.paragraph_id || paragraphMappings.value[0]?.paragraph_id || null + } +} + +function selectParagraph(paragraphId: number) { + selectedParagraphId.value = paragraphId } function bindProgress() { @@ -254,12 +296,31 @@ onBeforeUnmount(() => { } .status-card, -.mapping-card, .preview-card { margin-bottom: 16px; border-radius: 18px; } +.detail-layout { + display: flex; + gap: 16px; + align-items: flex-start; +} + +.detail-left { + width: 360px; + flex-shrink: 0; +} + +.detail-right { + min-width: 0; + flex: 1; +} + +.left-card { + border-radius: 18px; +} + .status-message { margin-top: 12px; font-size: 13px; @@ -276,6 +337,18 @@ onBeforeUnmount(() => { border: 1px solid #e5e7eb; border-radius: 14px; background: #fafbfc; + cursor: pointer; + transition: all 0.15s ease; +} + +.mapping-item:hover { + border-color: #c7d2fe; + background: #f8faff; +} + +.mapping-item.active { + border-color: #4f46e5; + background: #eef2ff; } .mapping-top { @@ -312,6 +385,10 @@ onBeforeUnmount(() => { color: #6b7280; } +.mapping-status { + margin-top: 10px; +} + .mapping-files { display: flex; flex-wrap: wrap; @@ -334,16 +411,12 @@ onBeforeUnmount(() => { color: #9ca3af; } -.preview-wrap { - display: grid; - gap: 16px; -} - .preview-section { padding: 16px; border-radius: 14px; background: #fff; border: 1px solid #e5e7eb; + min-height: 480px; } .preview-section-head { @@ -358,6 +431,19 @@ onBeforeUnmount(() => { margin: 0; } +.preview-note { + margin-bottom: 12px; + font-size: 13px; + color: #6b7280; +} + +.preview-files { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 14px; +} + :deep(.result-text) { margin: 0 0 12px; line-height: 1.8;