【项目实训(个人4)】
继续进行法律文书智能摘要系统的开发本次开发周期内我主要围绕文书管理系统的核心体验进行了五项功能迭代与多项优化工作。首先我打通了文书管理与示例展示之间的壁垒在管理页面中直接嵌入示例卡片并支持按类型过滤解决了原本两个模块相互割裂的问题。接着我为文书管理增加了批量删除与批量导出能力通过后端专职接口与ZIP打包策略实现了文档的高效批处理。在阅读体验方面我设计并实现了悬浮词典功能采用本地词库预加载与后端异步更新的双轨机制为用户提供即点即查的法律术语解释。同时我还引入了主题调节与字体缩放功能支持明暗/护眼三种模式并持久化用户偏好。最复杂的是富文本批注系统的重构我通过增加DOM锚点定位、引入Tiptap编辑器、实现双向联动高亮彻底解决了原有批注无法在原文中定位展示的问题。此外我还对示例同步刷新、页面跳转逻辑、全局搜索行为以及上传文档流程进行了细致的优化修复了悬浮词典因编码问题导致的编译错误和富文本高亮不更新等关键缺陷。以下将按功能模块详细记录开发过程与心得体会。具体内容1.连接示例展示与文书阅读功能之前文书管理与示例展示各自分裂这里在文书管理中加入示例的卡片展示提供只看示例的按钮可以直接过滤出用户自定义的示例2.文书批量管理文书管理中也加入批量删除功能可全部选择或是部分选择还有批量导出功能全部导出或是部分导出1. 批量删除 (Batch Delete)前端交互在文书原生大表格的最左侧插入一列包含Checkbox复选框的列。表头包含全选/反选操作。选中文档后顶部工作区会浮现“已选择 X 份文档 —— [批量删除] | [批量导出]”的控制栏。后端支持可以直接由前端使用Promise.all并发调用现有的单个文档删除接口或者在documents.py新增一个专职的批量删除接口POST /api/documents/batch_delete(入参doc_ids: List[str]) 来统一处理事务。考虑到未来的扩展性我们将新增专职的批量删除后端接口。2. 批量导出 (Batch Export)后端导出端点新增GET /api/documents/export接收类似选中的doc_id列表。打包策略 (ZIP Generation) 因为本系统是文书解析系统后端会将选中文件的相关信息打包成一个 ZIP 归档直接流式下载到用户浏览器。对于每份入选文书ZIP 包内将包含提取出的大模型结构化抽取 JSON 元数据 (案号_法院等抽取字段.json)。文段纯文本 (xxx原文档.txt)。(详见下方的 Open Questions 环节)3. UI 交互组件 (DocumentsListView 改造)状态追踪引入selectedIds ref(new Setstring())等响应式状态来追踪高亮。吸顶动作栏当selectedIds.size 0时在搜索框区域下方展开一个显眼的浮动工具栏提供带有 Element Plus Icon 的“批量删除”与“打包导出”动作底座。关于批量导出功能有几个选择Option A把原始上传进来的那个源文件真实的 PDF/WORD/TXT 原件打包成一个导出压缩包.zip给下载下来。Option B不仅仅是原文件把这篇文书被 AI 解析切块后提取的干净纯文本以及它的案号等字段塞进 ZIP 里一起下载Option C只导出系统带案号、法院、类型的 Excel 大表格/CSV表。这里按照optionB开发3.悬浮词典功能首先在底层数据处理上采用本地词库预加载 后端 API 异步更新的双轨机制。前端在初始化阶段直接importlegal_terms.json确保ReaderView和DocumentView在渲染瞬时即可通过全局正则表达式完成文本扫描将术语包装为具有特定样式的span标签从而消除因网络请求导致的页面闪烁。后续开发中后端GET /api/terms接口将作为词库的热更新源通过对比版本号实现增量同步确保用户在不更新代码的情况下也能获取最新法律术语解释。在交互逻辑实现上核心组件TermHighlighter将采用事件委托机制来监听点击行为。匹配到的术语会被赋予data-term属性通过在组件根容器统一监听click事件来识别触发目标这种方式能有效避免在大篇幅文书中因绑定过多监听器而产生的性能损耗。当用户点击高亮术语时TermTooltip组件将根据点击处的 DOM 坐标getBoundingClientRect计算弹出位置以气泡卡片形式展示通俗解释及法条索引并监听全局点击事件以实现“点击外部自动关闭”的逻辑。在系统集成与扩展方面功能将同步下沉至ReaderView的原文块与DocumentView的结构化摘要区。通过统一的Props接口将原始字符串注入TermHighlighter组件实现两处场景下识别逻辑的高度复用与视觉一致性。这种架构设计为后续的功能留出了空间当词库需求从静态查阅转向动态维护时后端可无缝接入 SQLite 数据库通过POST/PUT接口支持管理员对“善意取得”或“诉讼时效”等词条进行实时修订与案例补充而无需改动前端展示逻辑。文件作用TermHighlighter.vue核心扫描引擎构建联合正则、渲染高亮 HTML、事件委托监听点击TermTooltip.vue浮动卡片 UI显示通俗解释 / 法条 / 案例Teleport 到body层防遮挡ReaderView.vue集成TermHighlighter替换原文段落DocumentView.vue集成到文书内容正文块和分块视图两处backend/app/api/terms.pyGET /api/terms、GET /api/terms/{name}、POST /api/terms三个接口修改文件ReaderView.vue— 阅读器正文块接入TermHighlighterDocumentView.vue— 智能演示板正文块分块视图接入TermHighlighterbackend/main.py— 注册terms_router交互体验法律术语自动显示为蓝色虚线下划线视觉清晰点击词条后弹出精美浮动卡片带入场动画显示通俗解释 / 相关法条 /别名点击卡片外任意区域自动关闭不打断阅读支持深色模式4.主题调节功能可以设计明暗等三种颜色并能记录用户偏好可以调节字体大小文件改动内容stores/theme.ts【新增】Pinia Store管理light/dark/sepia三档初始化时自动读取localStorageApp.vue① 将isDark: boolean改为themeStore.theme② 新增html.sepia { ... }CSS 变量覆写块米黄暖色护眼调ReaderView.vue顶栏新增[☀️][][]主题切换器 [A−] 15px [A]字体大小调节控件使用方式打开任意文书的阅读页点击文书管理中的阅读按钮右上角能看到三个主题按钮 字体大小调节点击任意主题全站即刻切换因为走的是同一个 Pinia Store关掉浏览器再打开偏好自动恢复localStorage持久化5.加入富文本编辑功能问题根因现在批注系统只存了选中文字的字符串完全没有 DOM 位置信息所以原文无法高亮——就像 Word 批注只存了被引文本但不知道它在第几页第几行。解决思路三层叠加渲染原文段落└─ 层1底批注高亮 mark stylebackground:#409eff40└─ 层2中术语识别 span classlegal-term-chip下划线虚线└─ 层3上交互事件 click/mouseup关键数据扩展给 Annotation 加锚点// 现在存的没有位置{ selectedText: 善意取得, comment: ... }// 改为有了精确位置{selectedText: 善意取得,comment: ...,richComment: { type: doc, ... }, // Tiptap 富文本 JSONanchorRange: {blockIndex: 3, // 在第3个段落startOffset: 42, // 从第42个字开始endOffset: 46 // 到第46个字结束}}两个最难的点同一段落多批注不重叠用区间合并算法把多条批注的字符范围排序后切割成片段列表渲染初版直接限制不允许重叠。与TermHighlighter协同批注高亮mark背景色和术语高亮span下划线作用域不同可以叠加但两者都需要v-html渲染需要把高亮计算合并到同一个渲染函数里。Tiptap 用在哪只用在右侧批注框用来写批注内容不用在原文区域。原文维持只读通过mark高亮即可。分三个 PR数据层后端Schema 前端API扩展anchorRange高亮渲染器原文mark高亮 双向联动Tiptap 富文本编辑器批注内容区替换文件改动schemas.py【后端】新增AnchorRange模型给AnnotationBase/AnnotationUpdate添加anchorRange和richComment可选字段storage.ts【前端类型】同步扩展AnchorRange接口 Annotation类型的两个新字段useAnnotationHighlight.ts【新建 Composable】核心区间合并算法把某段落内的多条批注anchorRange切割为片段列表转换为带mark标签的 HTMLAnnotationPanel.vue【全量重写】Tiptap 富文本编辑器加粗/斜体/下划线/列表 颜色选择器 编辑/删除 active-annotation事件ReaderView.vue【全量重写】加载批注后渲染内联mark高亮点击高亮 → 右侧卡片激活滚动点击批注卡片 → 左侧文本滚动定位交互体验拖选原文文字 → mouseup → 批注面板弹出添加表单含引用预览→ Tiptap 富文本框输入内容 → 保存→ 原文对应位置出现彩色下划线高亮→ 点击高亮 → 右侧批注卡片激活→ 点击右侧卡片 → 左侧原文滚动到位功能优化1.取消示例的时候要刷新一次这样卡片才能正确实现改造SampleLoader.vue将它内封获取数据的函数借由defineExpose({ fetchSamples })暴露出来允许外接系统随时强制它重新拉取最新卡片。连接DocumentsListView.vue为SampleLoader refsampleLoaderRef /打上响应式的锚点。注入同步刷新钩子在原先的handleToggleSample业务逻辑线中也就是你一旦点击星星后等后端接口确认删除或添加完毕立马静默执行一句sampleLoaderRef.value.fetchSamples()2.文书示例与文书管理页面跳转不合理返回路径写死在“文书详请/演示界面 (DocumentView)”中左上角的「返回」按钮被强行写死为了router.push(/)回到首页。导致用户从“文书管理 (DocumentsListView)”表格中点击一篇文章进去后无法退回原表格打断了连续办公的心流。场景割裂“管理资产”和“查阅解析演示”应该是同一套连贯动作目前的跳页割裂感偏重。这里用分屏式设计逻辑在“文书管理”的大表格里点击某一行不再是发生全屏的暴力跳转而是从屏幕右侧平滑滑出一个宽大的抽屉面板 (Drawer)或者是直接将页面一分为二左边列表右边展示信息。体验可以在右侧抽屉里看到所有的结构化字段、类案推荐。看完关掉抽屉文书表格就安静地停留在那里。勾选多个进行批量打包将变得极其流畅。文书管理中这里的操作有阅读也有详情的选项在阅读后返回的是文书管理而不是详情页面3.右上角的查询功能现在的状况是在这个悬浮的框或者按下CtrlK里输入关键字敲击回车后不管处在哪个页面它都会简单粗暴地把您切回“工作台主页 (/)”。既然工作台本身只是用来拉取概览和基础信息的主页这种行为等于粗暴地打断了工作流。为了解决这个问题有两个优化方向方案 A (沉浸式下拉)彻底抛弃页面跳转。当敲入字符时直接在这个黑色的弹窗下面延伸拉出结果列表就像用苹果电脑的聚焦搜索或者手机下拉搜索框一样。点击想要看的文书后再单独打开详情或侧边栏全程不需要离开目前的界面背景。方案 B (导流至表格管理流)让搜索框变成一个全局快捷键按下回车后统一跃迁至《文书管理》页面/documents并且自动帮把关键字填进去把表格筛好。接下去的操作就是极其顺畅的选行 - 滑出面板审阅 - 关面板看下一篇。这里选择了方案B文件改动内容App.vuehandleSearch中的跳转目标从/?search...改为/documents?search...HomeView.vue删除onMounted中监听?searchURL参数的冗余逻辑主页恢复为干净的工作台DocumentsListView.vue新增useRoute在onMounted时读取route.query.search自动填入过滤框4.文书管理页面的上传文档功能核心问题有两个跳出问题点击上传文档按钮直接router.push(/)把用户踢出管理页完全没有必要无刷新问题FileUpload.vue上传完成后会强制跳转到/document/:id演示详情页或/documents但上传成功后管理列表并没有机会获知要刷新自己解决方案上传框以弹窗对话框的形式内嵌在管理页里上传完成后弹窗关闭下方表格自动刷新出新文档。全程不换页可以直接点详情打开右侧抽屉查看解析。遇到的问题及解决方法1.悬浮词典功能报错之前用 PowerShell 的Set-Content -NoNewline写入文件时默认使用了系统 ANSI 编码GBK导致文件中部分 UTF-8 汉字被截断Vue 编译器无法解析被破坏的闭合标签。修复步骤用git show d6a6db6:...将文件还原到上一个干净的提交用 Python始终用encodingutf-8读写重新将 TermHighlighter 的集成干净地写入验证文件无\ufffd替换字符后提交今后策略对所有含中文的 Vue 文件统一用 Python 脚本或直接使用编辑工具进行修改绕开 PowerShell 的编码陷阱。重启npm run dev后可以正常编译了。2.富文本编辑中不显示高亮高亮不显示ReaderView的annotations数组只在onMounted加载一次AnnotationPanel内部保存/删除后不会通知父组件刷新删除不生效同理删除后只更新了 Panel 内部状态ReaderView的高亮层没有感知到修复分两步给AnnotationPanel加changed事件ReaderView监听后刷新Bug根因修复高亮不显示AnnotationPanel保存后只更新了内部的annotationsReaderView的annotations永远是onMounted时加载的初始值AnnotationPanel新增emit(changed)ReaderView监听changed→ 调用getAnnotations重新拉取删除后高亮残留同上删除只更新了面板侧删除成功后同样触发emit(changed) 加了try/catch防止接口报错时静默失败高亮更新不及时blockHtml是普通函数Vue 模板内多次调用会有微小时序差异改为computed(() blocks.value.map(...))计算属性当annotations变化时原子性地重算所有块的 HTMLElMessage 文字错editingId.value在消息之前就已被清为null导致永远显示批注已保存提前用wasEditing捕获标志位再清空editingId小结通过本轮开发文书管理系统在批量操作效率、阅读辅助体验以及批注交互完整性三个维度上都有了质的提升。批量删除与导出的加入让用户能够高效管理大量文书悬浮词典与主题调节功能显著优化了长时间阅读的舒适度而富文本批注系统的重构则彻底解决了“批注无法定位原文”的历史痛点。在开发过程中我深刻体会到数据锚点设计对于富文本交互的关键作用。同时编码问题带来的教训也提醒我始终重视文件编码一致性。后续我计划继续完善词库的后端管理界面并探索批注的协同编辑可能性让文书分析工作更加智能与高效。