Files
ai-doc-template-system/docs/AI文档模板系统-像素级任务清单-20260701.md
T
2026-07-01 20:37:42 +08:00

34 KiB
Raw Blame History

AI 文档模板系统 — 像素级任务清单

每个任务 ~0.25–0.5 天,可独立分配,验收精确到文件/函数/交互效果。 聚焦:模板标注配置页(第一阶段 MVP)。 UI 库:Ant Design Vue


第一阶段:MVP 任务总表(73 个)

# 任务名 预估 状态
环境搭建
001 初始化 FastAPI 项目 + main.py + health 接口 后端 0.25d 已完成
002 配置 MySQL 连接 + SQLAlchemy session 后端 0.25d 已完成
003 配置 MinIO 客户端 后端 0.25d 已完成
004 Alembic 初始化 + 第一个迁移脚本 后端 0.25d 已完成
005 Docker Compose 编排(MySQL + MinIO 后端 0.25d 已完成
006 初始化 Vue3 + Vite 项目 前端 0.25d 已完成
007 安装 Ant Design Vue + 全局注册 前端 0.25d 已完成
008 安装 Vue Router + 配置路由结构 前端 0.25d 已完成
009 安装 Pinia + 创建根 store 前端 0.25d 已完成
010 安装 Axios + 创建 API 客户端 base.ts 前端 0.25d 已完成
011 定义全局 CSS 变量 前端 0.25d 已完成
数据库建表(3 个)
012 创建 template 表 + SQLAlchemy Model 后端 0.25d 已完成
013 创建 template_block 表 + SQLAlchemy Model 后端 0.25d 已完成
014 创建 block_config 表 + SQLAlchemy Model(含 unique 约束) 后端 0.25d 已完成
模板上传
015 实现文件接收 + .docx 格式校验 + 大小校验 后端 0.25d 已完成
016 实现文件存储到 MinIO 后端 0.25d 已完成
017 实现 template 表 insert 后端 0.25d 已完成
018 组装上传接口 POST /api/templates/upload 后端 0.25d 已完成
Word 解析
019 实现 docx 文件打开 + 逐段落遍历 后端 0.25d 已完成
020 实现标题识别(Heading 1-6)→ type=heading 后端 0.25d 已完成
021 实现段落识别 → type=paragraph 后端 0.25d 已完成
022 实现表格识别 + 行数列数提取 → type=table 后端 0.25d 已完成
023 实现 block_id 生成器(顺序编号) 后端 0.25d 已完成
024 实现层级构建(parent_block_id+ 结构树 tree 输出 后端 0.25d 已完成
025 将解析结果批量写入 template_block 表 后端 0.25d 已完成
HTML 预览生成
026 标题 → <h2 data-block-id> 转换 + 居中加粗样式 后端 0.25d 已完成
027 段落 → <p data-block-id> 转换 + 首行缩进样式 后端 0.25d 已完成
028 表格 → <table data-block-id> 转换 + 边框样式 后端 0.25d 已完成
029 拼接完整 HTML 字符串(带内联 CSS) 后端 0.25d 已完成
查询 + 配置接口
030 实现 GET /api/templates/{id} 组装全部数据 后端 0.5d 已完成
031 实现 POST /api/templates/{id}/blocks/{blockId}/config upsert 后端 0.25d 已完成
032 实现 GET /api/data-sources 返回预设列表 后端 0.25d 已完成
033 实现 POST /api/data-sources 新增数据源 后端 0.25d 已完成
四栏布局
034 实现四栏 CSS Grid 布局 前端 0.5d 已完成
035 实现左侧菜单 shell 组件 前端 0.25d 已完成
036 实现顶部工具栏 shell 组件 前端 0.25d 已完成
037 实现配置面板 shell 组件 前端 0.25d 已完成
038 实现预览区 shell 组件(A4 纸效果) 前端 0.25d 已完成
039 实现结构树 shell 组件 前端 0.25d 已完成
左侧菜单
040 菜单数据模型(JSON+ 子菜单配置 前端 0.25d 已完成
041 菜单展开/收起交互 + 箭头旋转动画 前端 0.25d 已完成
042 菜单选中高亮(蓝色左侧竖条) 前端 0.25d 已完成
顶部工具栏
043 面包屑导航渲染 前端 0.25d 已完成
044 模板名称 + 版本号标签展示 前端 0.25d 已完成
045 保存状态指示器(已保存/未保存 圆点切换) 前端 0.25d 已完成
046 操作按钮区(预览/保存/生成测试/导出模板/全屏) 前端 0.25d 已完成
Word 预览区
047 v-html 渲染后端 HTML + 为 [data-block-id] 添加 doc-block class 前端 0.25d 未完成
048 点击事件委托 + block_id 提取 前端 0.25d 未完成
049 选中高亮(蓝色虚线 outline + 浅蓝背景) 前端 0.25d 未完成
050 区域类型颜色映射(蓝/黄/绿/紫/灰)+ 保存后更新 前端 0.5d 未完成
051 block_id 标签 absolute 定位 + hover 显示 前端 0.25d 未完成
052 显示/隐藏标签 toggle 开关 前端 0.25d 未完成
053 缩放控制(70%/100%/150% scale 切换) 前端 0.25d 未完成
结构树
054 递归树组件 + 数据绑定 前端 0.5d 未完成
055 展开/折叠交互 + 图标 前端 0.25d 未完成
056 状态圆点(已配置绿/待审核黄/已禁用红/未配置灰) 前端 0.25d 未完成
057 点击树节点 → 预览区 scrollIntoView + 闪烁高亮 前端 0.5d 未完成
配置面板
058 区域名称 input(默认取 text_preview 前端 0.25d 未完成
059 区域类型 select 前端 0.25d 未完成
060 数据来源 tag chips 多选组件 前端 0.5d 未完成
061 提示词 textarea + 字数统计 前端 0.5d 未完成
062 输出格式 select 前端 0.25d 未完成
063 是否需要审核 radio 前端 0.25d 未完成
064 备注 textarea + 字数统计 前端 0.25d 未完成
065 保存按钮 + loading 状态 + 成功/失败提示 前端 0.25d 未完成
066 未选中区域时的空状态占位提示 前端 0.25d 未完成
Pinia Store
067 templateStore 前端 0.5d 未完成
068 selectionStore 前端 0.25d 未完成
069 uiStore 前端 0.25d 未完成
三区联动
070 watch selectedBlockId → 配置面板加载 前端 0.5d 未完成
071 watch selectedBlockId → 结构树节点高亮 前端 0.25d 未完成
072 页面初始化 loading(全页遮罩 + a-spin 前端 0.25d 未完成
073 加载失败错误页 + 重新加载按钮 前端 0.25d 未完成
总计 ~18.25d

第一阶段 MVP 任务卡(逐个)


环境搭建(001011


001 · 初始化 FastAPI 项目 + main.py + health 接口

:后端 预估0.25d 产出app/main.py

做什么

  • 创建 app/ 目录,创建 app/main.py
  • 创建 FastAPI 实例,添加 GET /health 返回 {"status": "ok"}
  • 添加 CORS 中间件(允许前端 localhost 访问)
  • 创建目录结构:api/, models/, services/, schemas/

验收

  • uvicorn app.main:app --reload 启动成功
  • 访问 /docs 显示 Swagger 页面
  • 访问 /health 返回 200

002 · 配置 MySQL 连接 + SQLAlchemy session

:后端 预估0.25d 产出app/database.py

做什么

  • 创建 app/database.py
  • 配置 SQLAlchemy engine(读取环境变量 DB_HOST/DB_PORT/DB_USER/DB_PASS/DB_NAME
  • 创建 SessionLocal 工厂
  • 创建 Base 基类
  • 创建 get_db 依赖注入函数

验收

  • 应用启动时不会报数据库连接错误

003 · 配置 MinIO 客户端

:后端 预估0.25d 产出app/services/storage.py

做什么

  • 创建 app/services/storage.py
  • 初始化 minio.Minio 客户端
  • 实现 upload_file(bucket, file_path, content) 函数
  • 实现 download_file(bucket, file_path) 函数
  • 实现 ensure_bucket(bucket_name) 自动创建 bucket

验收

  • MinIO 未启动时降级为本地文件存储

004 · Alembic 初始化 + 第一个迁移脚本

:后端 预估0.25d 产出alembic.ini, migrations/

做什么

  • 运行 alembic init alembic
  • 配置 alembic.ini 中的数据库连接
  • 配置 env.py 导入 Base
  • 生成第一个空迁移

验收

  • alembic upgrade head 成功执行
  • alembic history 显示初始迁移

005 · Docker Compose 编排

:后端 预估0.25d 产出docker-compose.yml

做什么

  • 创建 docker-compose.yml
  • MySQL 8.0 服务(端口 3306,初始化数据库 ai_doc_template
  • MinIO 服务(端口 9000 + 9001 console
  • 网络配置,服务间可通信

验收

  • docker-compose up -d 启动成功

006 · 初始化 Vue3 + Vite 项目

:前端 预估0.25d 产出:前端项目根目录

做什么

  • 运行 npm create vite@latestVue3 + TypeScript
  • 安装依赖 npm install
  • 确认 npm run dev 可启动
  • 清理默认 App.vue 内容,保留 Hello World

验收

  • npm run dev 启动成功

007 · 安装 Ant Design Vue + 全局注册

:前端 预估0.25d 产出src/main.ts

做什么

  • 运行 npm install ant-design-vue
  • main.tsimport Antd from 'ant-design-vue' + app.use(Antd)
  • 全局导入样式:import 'ant-design-vue/dist/reset.css'
  • 验证:在 App.vue 中使用一个 <a-button> 看能否渲染

验收

  • Ant Design Vue 按钮可正常显示
  • 无控制台报错

008 · 安装 Vue Router + 配置路由结构

:前端 预估0.25d 产出src/router/index.ts

做什么

  • 运行 npm install vue-router@4
  • 创建 src/router/index.ts
  • 配置路由:/ → TemplateCenter, /template/:id → TemplateEdit
  • main.tsapp.use(router)
  • 添加 router-view 到 App.vue

验收

  • 访问 / 显示模板中心页
  • 访问 /template/1 显示模板编辑页

009 · 安装 Pinia + 创建根 store

:前端 预估0.25d 产出src/stores/index.ts

做什么

  • 运行 npm install pinia
  • main.tsapp.use(createPinia())
  • 创建一个简单的 counter store 验证可用

验收

  • Pinia 正常注册,无控制台报错

010 · 安装 Axios + 创建 API 客户端

:前端 预估0.25d 产出src/api/base.ts

做什么

  • 运行 npm install axios
  • 创建 src/api/base.ts
  • 创建 axios 实例,baseURL 设为 /api
  • 添加请求/响应拦截器

验收

  • 调用 api.get('/health') 能收到后端响应

011 · 定义全局 CSS 变量

:前端 预估0.25d 产出src/styles/variables.css

做什么

  • 创建 src/styles/variables.css
  • 定义 :root 变量:
:root {
  --primary: #5b5bd6;
  --primary-hover: #4a4ac0;
  --primary-soft: #eeeefb;
  --text: #1a1d24;
  --text-2: #5b626e;
  --text-3: #9aa1ad;
  --border: #e0e2e6;
  --border-light: #eaecef;
  --bg: #f5f6f8;
  --bg-card: #fff;
  --bg-soft: #f0f1f3;
  --success: #1a8c4a;
  --warn: #d48a00;
  --danger: #d33;
  --font: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
  --fs-body: 13px;
  --fs-sm: 12px;
  --radius: 6px;
  --sidebar-w: 220px;
  --config-panel-w: 380px;
  --tree-panel-w: 240px;
  --toolbar-h: 48px;
}
  • main.ts 中 import

验收

  • 全局样式变量可在任意组件中使用 var(--primary)

数据库建表(012014


012 · 创建 template 表 + Model

:后端 预估0.25d 产出app/models/template.py

做什么

  • 定义 Template Model(字段见附录)
  • 生成 Alembic 迁移并执行

验收

  • 表创建成功,可通过 Model 插入一条记录

013 · 创建 template_block 表 + Model

:后端 预估0.25d 产出app/models/template_block.py

做什么

  • 定义 TemplateBlock Model
  • 外键关联到 template.id
  • 生成迁移并执行

验收

  • 插入数据时外键约束生效

014 · 创建 block_config 表 + Model

:后端 预估0.25d 产出app/models/block_config.py

做什么

  • 定义 BlockConfig Model
  • 添加唯一约束 (template_id, block_id)
  • 生成迁移并执行

验收

  • 重复 (template_id, block_id) 插入时更新而非报错

模板上传(015018


015 · 文件接收 + 格式校验 + 大小校验

:后端 预估0.25d 产出app/services/template_service.py 中的 validate_file()

做什么

  • 校验扩展名为 .docx
  • 校验 Content-Type
  • 校验文件大小 ≤ 50MB

验收

  • 上传 .txt 返回 400
  • 上传 >50MB 返回 413

016 · 文件存储到 MinIO

:后端 预估0.25d 产出app/services/template_service.py 中的 save_file()

做什么

  • 调用 storage 服务的 upload_file
  • 路径格式:/templates/{template_id}/{filename}
  • MinIO 不可用时降级本地存储

验收

  • 文件保存到指定路径

017 · template 表 insert

:后端 预估0.25d 产出app/services/template_service.py 中的 create_template_record()

做什么

  • 写入 template 表
  • 返回创建的 template 对象

验收

  • 数据库有正确记录

018 · 组装上传接口

:后端 预估0.25d 产出app/api/templates.py 中的 upload 路由

做什么

  • 创建 app/api/templates.py
  • 实现 POST /api/templates/upload 路由
  • 调用 015→016→017 三个步骤

验收

  • Postman 上传成功,返回 {"template_id": 1, "name": "...", "status": "uploaded"}

Word 解析(019025


019 · docx 文件打开 + 逐段落遍历

:后端 预估0.25d 产出app/services/doc_parser.py 中的 parse_document()

做什么

  • 使用 python-docx 打开文件
  • 遍历 document.paragraphs

验收

  • 返回段落列表

020 · 标题识别(Heading 1-6

:后端 预估0.25d 产出app/services/doc_parser.py 中的 classify_paragraph()

做什么

  • 判断 paragraph.style.name 是否以 "Heading" 开头
  • 提取标题级别和文本

验收

  • Heading 1 → level 1
  • Heading 2 → level 2

021 · 段落识别

:后端 预估0.25d 产出app/services/doc_parser.py

做什么

  • 非标题段落 → type="paragraph"
  • 提取文本和样式名称

验收

  • 正文段落 type=paragraph

022 · 表格识别 + 行数列数提取

:后端 预估0.25d 产出app/services/doc_parser.py 中的 parse_tables()

做什么

  • 遍历 document.tables
  • 提取行数、列数、表头

验收

  • 表格行数列数正确

023 · block_id 生成器

:后端 预估0.25d 产出app/services/doc_parser.py 中的 generate_block_id()

做什么

  • 格式:block_001, block_002, ... 递增

验收

  • 输入 1 → block_001
  • 输入 12 → block_012

024 · 层级构建 + 结构树输出

:后端 预估0.25d 产出app/services/doc_parser.py 中的 build_tree()

做什么

  • 根据 heading level 确定父子关系
  • 填充 parent_block_id
  • 输出嵌套 tree 结构

验收

  • 三级标题层级正确
  • 表格归属于正确的父章节

025 · 解析结果批量写入 template_block 表

:后端 预估0.25d 产出app/services/doc_parser.py 中的 save_blocks()

做什么

  • 批量 insert 到 template_block 表
  • 先删除旧记录(重解析场景)

验收

  • 数据库有对应的 block 记录

HTML 预览生成(026029


026 · 标题 → <h2 data-block-id> 转换

:后端 预估0.25d 产出app/services/html_generator.py 中的 render_heading()

做什么

  • 输出 <h2 data-block-id="block_002" style="...">标题内容</h2>

验收

  • 标签带 data-block-id,样式居中加粗

027 · 段落 → <p data-block-id> 转换

:后端 预估0.25d 产出app/services/html_generator.py 中的 render_paragraph()

做什么

  • 输出 <p data-block-id="block_003" style="text-indent:2em;...">段落内容</p>

验收

  • 首行缩进,行高 1.8

028 · 表格 → <table data-block-id> 转换

:后端 预估0.25d 产出app/services/html_generator.py 中的 render_table()

做什么

  • 输出完整 HTML <table> 带 data-block-id

验收

  • 表格正确渲染,边框可见

029 · 拼接完整 HTML 字符串

:后端 预估0.25d 产出app/services/html_generator.py 中的 generate_preview_html()

做什么

  • 按 sort_order 排序
  • 遍历 blocks 调用对应的 render 函数
  • 拼接为完整 HTML

验收

  • 返回字符串可被前端 v-html 渲染

查询 + 配置接口(030033


030 · 模板详情查询接口

:后端 预估0.5d 产出app/api/templates.py 中的 GET 路由

做什么

  • 实现 GET /api/templates/{template_id}
  • 查询 template 表 + template_block 表 + block_config 表
  • 调用 html_generator 生成 preview_html
  • 返回完整 JSON

验收

  • 返回数据包含 template, preview_html, blocks, tree, configs

031 · 保存区域配置接口

:后端 预估0.25d 产出app/api/blocks.py 中的 POST 路由

做什么

  • 实现 POST /api/templates/{template_id}/blocks/{block_id}/config
  • upsert 语义

验收

  • 保存后数据库记录正确
  • 重复调用更新记录

032 · 获取数据源列表

:后端 预估0.25d 产出app/api/data_sources.py 中的 GET 路由

做什么

  • 返回预设列表:财务部、运行部、生技部、安全监察部等

验收

  • 返回 JSON 数组

033 · 新增数据源

:后端 预估0.25d 产出app/api/data_sources.py 中的 POST 路由

做什么

  • 接收 {name, category} 并存储

验收

  • 新增后 GET 列表包含新数据源

四栏布局(034039


034 · 四栏 CSS Grid 布局

:前端 预估0.5d 产出src/views/TemplateEdit.vue

做什么

.layout {
  display: grid;
  grid-template-columns: var(--sidebar-w) var(--config-panel-w) 1fr var(--tree-panel-w);
  grid-template-rows: var(--toolbar-h) 1fr;
  height: 100vh;
}
  • 工具栏 grid-column: 1 / -1
  • 左侧菜单 grid-column: 1
  • 配置面板 grid-column: 2
  • 预览区 grid-column: 3
  • 结构树 grid-column: 4

验收

  • 页面渲染为四栏布局
  • 各区域宽度正确

035 · 左侧菜单 shell

:前端 预估0.25d 产出src/components/TemplateEdit/LeftMenu.vue

做什么

  • 固定宽度 220px
  • overflow-y: auto, border-right, 白色背景

验收

  • 渲染在正确位置

036 · 顶部工具栏 shell

:前端 预估0.25d 产出src/components/TemplateEdit/TopToolbar.vue

做什么

  • 固定高度 48px, flex 布局, border-bottom

验收

  • 渲染在顶部

037 · 配置面板 shell

:前端 预估0.25d 产出src/components/TemplateEdit/ConfigPanel.vue

做什么

  • 固定宽度 380px, overflow-y: auto
  • border-right: 1px solid

验收

  • 渲染在正确位置

038 · 预览区 shell

:前端 预估0.25d 产出src/components/TemplateEdit/WordPreview.vue

做什么

  • flex: 1, overflow-y: auto, 背景色 #f0f1f3
  • 内部 A4 纸容器:794px 宽, 白色背景, box-shadow

验收

  • A4 纸效果可见

039 · 结构树 shell

:前端 预估0.25d 产出src/components/TemplateEdit/StructureTree.vue

做什么

  • 固定宽度 240px, overflow-y: auto
  • border-left: 1px solid

验收

  • 渲染在右侧

左侧菜单(040042


040 · 菜单数据模型

:前端 预估0.25d 产出src/components/TemplateEdit/menu-data.ts

做什么

interface MenuItem {
  label: string
  icon: string
  children?: MenuItem[]
  route?: string
}
  • 完整菜单数据(模板中心/生成文档/历史记录/系统配置 及子菜单)

验收

  • 数据结构可被菜单组件消费

041 · 菜单展开/收起 + 箭头动画

:前端 预估0.25d 产出LeftMenu.vue

做什么

  • 渲染菜单树
  • 有子菜单的项显示箭头图标
  • 点击切换展开/收起,箭头旋转动画

验收

  • 点击展开子菜单,箭头旋转 90°,动画流畅

042 · 菜单选中高亮

:前端 预估0.25d 产出LeftMenu.vue

做什么

  • 当前选中菜单添加 active class
  • 高亮样式:background: #eeeefb; color: #5b5bd6;
  • 左侧竖条 3pxborder-left: 3px solid #5b5bd6;

验收

  • 点击菜单项高亮,切换时旧高亮移除

顶部工具栏(043046


043 · 面包屑导航

:前端 预估0.25d 产出TopToolbar.vue

做什么

  • 显示面包屑:模板中心 / 模板名称 / 区域配置
  • 每段用 / 分隔,最后一段加粗

验收

  • 面包屑渲染正确

044 · 模板名称 + 版本号

:前端 预估0.25d 产出TopToolbar.vue

做什么

  • 从 Pinia store 读取模板名称显示
  • 版本号显示为灰色小标签

验收

  • 模板名称正确读取

045 · 保存状态指示器

:前端 预估0.25d 产出TopToolbar.vue

做什么

  • 小圆点 + 文字:绿色"已保存" / 黄色"未保存"
  • 从 uiStore 读取 saveStatus

验收

  • 初始"未保存",保存成功后变"已保存"

046 · 操作按钮区

:前端 预估0.25d 产出TopToolbar.vue

做什么

  • 使用 Ant Design Vue <a-button size="small">
  • 按钮:预览、保存(primary)、生成测试、导出模板、全屏
  • 按钮间距 8px

验收

  • 5 个按钮分布整齐,保存按钮高亮

Word 预览区(047053


047 · v-html 渲染后端 HTML

:前端 预估0.25d 产出WordPreview.vue

做什么

  • <div v-html="previewHtml" class="doc-content" @click="handleClick"></div>
  • 在 mounted 中为所有 [data-block-id] 元素添加 class doc-block

验收

  • HTML 内容渲染正确

048 · 点击事件委托 + block_id 提取

:前端 预估0.25d 产出WordPreview.vue handleClick

做什么

function handleClick(event: MouseEvent) {
  const target = (event.target as HTMLElement).closest('[data-block-id]')
  if (!target) { selectionStore.cancelSelection(); return }
  const blockId = target.getAttribute('data-block-id')
  selectionStore.selectBlock(blockId)
}

验收

  • 点击段落提取到正确的 block_id
  • 点击空白区域取消选中

049 · 选中高亮

:前端 预估0.25d 产出WordPreview.vue

做什么

  • 选中 block 添加 class active-region
  • CSSoutline: 2px solid #5b5bd6; outline-offset: -2px; background: #f0f0ff;

验收

  • 选中后蓝色虚线边框可见

050 · 区域类型颜色映射

:前端 预估0.5d 产出WordPreview.vue

做什么

  • 根据 region_type 添加颜色 class
    • ai_generate → 浅蓝 #e8f0fe + 蓝色左边框
    • manual → 浅黄 #fef7e0 + 黄色左边框
    • fixed → 浅绿 #e6f4ea + 绿色左边框
    • table → 浅紫 #f3e8ff + 紫色左边框
  • 保存配置后立即更新

验收

  • 保存后预览区颜色变化正确

051 · block_id 标签

:前端 预估0.25d 产出WordPreview.vue

做什么

  • 每个 block 左下角显示标签 block_003
  • 标签样式:position: absolute; left: -80px; font-size: 11px;
  • hover 时透明度 0→1CSS transition

验收

  • 鼠标移入时标签可见

052 · 显示/隐藏标签 toggle

:前端 预估0.25d 产出TopToolbar.vue + WordPreview.vue

做什么

  • 工具栏添加 Ant Design Vue <a-switch>
  • 标签:显示区域标识
  • 切换时更新 uiStore.showLabels

验收

  • 关闭后所有标签隐藏

053 · 缩放控制

:前端 预估0.25d 产出WordPreview.vue

做什么

  • 工具栏缩放控制:<a-button>-</a-button> 100% <a-button>+</a-button>
  • 级别:70% / 100% / 150%
  • .doc-content 应用 transform: scale(N)

验收

  • 缩放后内容清晰,交互正常

结构树(054057


054 · 递归树组件 + 数据绑定

:前端 预估0.5d 产出StructureTree.vue

做什么

  • 递归组件,接收 tree 数据
  • 渲染文档章节层级

验收

  • 树结构与解析结果一致,递归渲染无报错

055 · 展开/折叠 + 图标

:前端 预估0.25d 产出StructureTree.vue

做什么

  • 箭头旋转 90° 表示展开
  • 图标:heading→📄, paragraph→📝, table→📊

验收

  • 点击箭头展开/折叠,图标正确

056 · 状态圆点

:前端 预估0.25d 产出StructureTree.vue

做什么

  • 从 Pinia store 的 configs 读取配置
  • 圆点:已配置=绿色 / 待审核=黄色 / 已禁用=红色 / 未配置=灰色
  • 圆点样式:width: 8px; height: 8px; border-radius: 50%;

验收

  • 保存后圆点变为绿色

057 · 树节点 → 预览区滚动定位

:前端 预估0.5d 产出StructureTree.vue + WordPreview.vue

做什么

  • 点击树节点:
    1. selectionStore.selectBlock(blockId)
    2. document.querySelector([data-block-id="${blockId}"])?.scrollIntoView({ behavior: 'smooth', block: 'center' })
  • 闪烁效果:添加 class flash0.3s 后移除

验收

  • 点击树节点后预览区滚动到正确位置

配置面板(058066


058 · 区域名称 input

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-input v-model:value="form.regionName" placeholder="区域名称" :disabled="!selectedBlockId" />
  • 选中 block 时默认填充 text_preview

验收

  • 选中后显示 block 名称,可编辑

059 · 区域类型 select

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-select v-model:value="form.regionType">
  • 选项:AI 生成区域 / 人工填写区域 / 固定区域 / 表格生成区域

验收

  • 切换选项值变化

060 · 数据来源 tag chips

:前端 预估0.5d 产出ConfigPanel.vue

做什么

  • 已选项显示为 <a-tag closable @close="removeSource(item)">财务部</a-tag>
  • 末尾" + 添加"按钮,点击弹出 <a-select> 下拉
  • 已选的不可重复添加

验收

  • 标签显示正确,可删除可添加

061 · 提示词 textarea + 字数统计

:前端 预估0.5d 产出ConfigPanel.vue

做什么

  • <a-textarea v-model:value="form.prompt" :rows="4" :maxlength="1000" show-count />
  • 超过 850 字时数字变橙色,超过 1000 变红

验收

  • 输入字数实时统计

062 · 输出格式 select

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-select v-model:value="form.outputFormat">
  • 选项:正式报告段落 / 分条说明 / 表格数据 / JSON 数据 / 标题文本

验收

  • 选项切换正常

063 · 是否需要审核 radio

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-radio-group v-model:value="form.needReview">
  • <a-radio :value="true">是</a-radio>
  • <a-radio :value="false">否</a-radio>

验收

  • 切换值变化

064 · 备注 textarea

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-textarea v-model:value="form.remark" :rows="2" :maxlength="200" show-count />

验收

  • 输入正常,字数统计正确

065 · 保存按钮 + 流程

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • <a-button type="primary" @click="saveConfig" :loading="saving" style="width:100%">保存该区域配置</a-button>
  • 点击后:
    1. saving = true
    2. 调用 POST 接口
    3. 成功 → saving = false + message.success('保存成功')
    4. 失败 → saving = false + message.error('保存失败')

验收

  • 保存成功/失败有对应提示

066 · 未选中空状态

:前端 预估0.25d 产出ConfigPanel.vue

做什么

  • selectedBlockId === null 时显示:
  • 居中图标 + "点击文档中的区域开始配置"
  • 隐藏所有表单字段

验收

  • 初始显示空状态,选中后显示表单

Pinia Store067069


067 · templateStore

:前端 预估0.5d 产出src/stores/templateStore.ts

做什么

  • state: template, previewHtml, blocks, tree, configs, dataSources
  • actions: fetchTemplate(id), updateConfig(blockId, config)
  • getters: getBlockById(blockId), getConfigByBlockId(blockId)

验收

  • 调用 fetchTemplate 后所有数据填充

068 · selectionStore

:前端 预估0.25d 产出src/stores/selectionStore.ts

做什么

  • state: selectedBlockId: string | null
  • actions: selectBlock(id), cancelSelection()

验收

  • selectBlock 后 selectedBlockId 更新

069 · uiStore

:前端 预估0.25d 产出src/stores/uiStore.ts

做什么

  • state: zoomLevel: 100, showLabels: true, saveStatus: 'saved' | 'unsaved' | 'saving'
  • actions: setZoom(level), toggleLabels(), setSaveStatus(status)

验收

  • 状态可读可写,所有组件响应变化

三区联动(070073


070 · watch selectedBlockId → 配置加载

:前端 预估0.5d 产出ConfigPanel.vue

做什么

watch(() => selectionStore.selectedBlockId, (blockId) => {
  if (!blockId) { form.value = {}; return }
  const config = templateStore.configs[blockId]
  if (config) {
    form.value = { ...config }
  } else {
    const block = templateStore.getBlockById(blockId)
    form.value = {
      regionName: block?.text_preview || '',
      regionType: 'ai_generate',
      dataSources: [],
      prompt: '',
      outputFormat: 'formal_paragraph',
      needReview: true,
      remark: '',
      enabled: true
    }
  }
})

验收

  • 已配置的 block 加载已有配置
  • 未配置的 block 显示默认值

071 · watch selectedBlockId → 树节点高亮

:前端 预估0.25d 产出StructureTree.vue

做什么

watch(() => selectionStore.selectedBlockId, (blockId) => {
  highlightNode(blockId)
  if (blockId) expandParents(blockId)
})

验收

  • 选中后树节点高亮,父级自动展开

072 · 页面初始化 loading

:前端 预估0.25d 产出TemplateEdit.vue

做什么

  • 页面 mounted 时:
    1. loading = true
    2. 调用 templateStore.fetchTemplate(id)
    3. 成功 → loading = false
  • loading 状态:全屏半透明遮罩 + <a-spin> 加载动画

验收

  • 加载时显示 loading,完成后内容出现

073 · 加载失败错误页

:前端 预估0.25d 产出TemplateEdit.vue

做什么

  • 加载失败时显示:错误图标 + "加载失败" + 错误信息 + 重新加载按钮
  • 点击重新加载重试 fetchTemplate

验收

  • 模拟网络断开时显示错误页

附录:数据库表结构

template 表

CREATE TABLE template (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(200) NOT NULL,
  type VARCHAR(50) DEFAULT 'report',
  version VARCHAR(20) DEFAULT 'v1.0.0',
  original_file_path VARCHAR(500) NOT NULL,
  status TINYINT DEFAULT 1,
  created_by VARCHAR(50),
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

template_block 表

CREATE TABLE template_block (
  id INT AUTO_INCREMENT PRIMARY KEY,
  template_id INT NOT NULL,
  block_id VARCHAR(50) NOT NULL,
  parent_block_id VARCHAR(50),
  block_type VARCHAR(20) NOT NULL,
  block_name VARCHAR(200),
  text_preview VARCHAR(500),
  level INT DEFAULT 0,
  sort_order INT DEFAULT 0,
  table_rows INT,
  table_cols INT,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (template_id) REFERENCES template(id) ON DELETE CASCADE,
  INDEX idx_template_id (template_id)
);

block_config 表

CREATE TABLE block_config (
  id INT AUTO_INCREMENT PRIMARY KEY,
  template_id INT NOT NULL,
  block_id VARCHAR(50) NOT NULL,
  region_name VARCHAR(200),
  region_type VARCHAR(30) DEFAULT 'ai_generate',
  data_sources TEXT,
  prompt TEXT,
  output_format VARCHAR(30) DEFAULT 'formal_paragraph',
  need_review TINYINT DEFAULT 1,
  remark TEXT,
  enabled TINYINT DEFAULT 1,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  FOREIGN KEY (template_id) REFERENCES template(id) ON DELETE CASCADE,
  UNIQUE KEY uk_template_block (template_id, block_id)
);

附录:第一阶段接口清单

方法 路径 任务
GET /health 001
POST /api/templates/upload 018
GET /api/templates/{template_id} 030
POST /api/templates/{template_id}/blocks/{block_id}/config 031
GET /api/data-sources 032
POST /api/data-sources 033

附录:Ant Design Vue 组件对照

用途 Ant Design Vue 组件
按钮 <a-button>
文本输入 <a-input v-model:value>
下拉选择 <a-select v-model:value>
多行文本 <a-textarea v-model:value>(自带 show-count 字数统计)
标签 <a-tag closable>
单选框 <a-radio-group> <a-radio>
开关 <a-switch>
加载动画 <a-spin>
消息提示 message.success() / message.error()(从 ant-design-vue import