feat(t9): P76 导出锚点判树修正 + P77 应用内详情形态 + 观察项①勾选回填 + 演练桩
build / build (push) Successful in 3m17s

P76(T8 验收遗留):快应用上下文的导出按钮此前恒按中心会话树判"导出"锚点
(Data.vue exportable 读 store.modules),锚点判定不生效(fail-closed 方向,
有授权也不画按钮)。本轮把 AppContext 拉到的**目标应用树**经 modules prop 下传,
Data.vue 的权限判据改为 permsTree(app=应用树 / center=会话树),canExport 纯函数不动。
Stats.vue 未接:本页无锚点判定也无 store.modules 引用,加 prop 是死代码(差异记 T9 报告)。

P77(T8 验收遗留):应用上下文内的"详情"不再跳回中心路由 /admin/data/detail。
形态二选一取**子路由 /app/:appCode/data/detail**(同一个 AnswerDetail.vue):
地址保留 uuid 可深链/刷新、详情页只有一份实现、back() 本就按 app 回应用数据视图,
写 parentPath 反而造假(menuRoute 的 ROUTE_PATHS 只收静态 path,应用段导航在页内轻导航)。
goDetail 按 props.context 分派;back() 改读 route.params.appCode,撤掉 query.app 双轨。

观察项①(T8 验收记档):确认跨触底页勾选**会被清空**——view-ui-plus 的 data 监听器
用 makeObjBaseData 重建 objData,_isChecked 只认行上的 _checked(点选不回写行),
整组替换后勾选视觉与 getSelection 归零而"已选 N 条"仍累计。新增 withSelectionState()
在 reload/loadMore 两条装载路径按 selected 回填 _checked,selected 为唯一事实源。

演练桩(本地件,dev:mock 生效):baseapi 增 fc-drill 快应用模块树(方案 §6.2 五行含
导入/导出两个 path 空锚点),dataapi 增 /api/form/resolve 桩(快应用数据视图唯一的
合规取数路径是 code→resolve→uuid,契约 §1)。其余外指 appCode 仍旧返回空树(P3 口径)。

门禁:npm run build ✓ / npm run type-check 0 错 ✓;
t1~t9 全量复跑:t5 13/0/1、t6 22/0/2、t7 12/0/0、t8 21/0/1、t9 12/0/1
(skip 均为 headless Edge --dump-dom 吐 0 字节的 P73 环境事实,不假通过)。
本提交含 T6~T8 三轮堆在工作区的既有改动(沿 T8 验收 §5 推送分层)。
This commit is contained in:
chenxing
2026-10-15 12:57:10 +08:00
parent fdd9aaaa3b
commit 14d45f7e3c
61 changed files with 9236 additions and 72 deletions
+57
View File
@@ -0,0 +1,57 @@
/**
* 填报面 API(用户面,CAS 会话):resolve / submit / my-list / upload。
*
* 沿 P49 写法:alovaInstance.Get<T>(url, {params}) / Post<T>(url, body, {params})。
* 与后端 FormResolveController / FormAnswerController 对齐。
*/
import { alovaInstance } from "@/utils/alova/alova-iview";
import type { ResolvedForm, SubmitRequest, SubmitView, MyAnswerItem } from "@/types/answer";
import type { AnswerDetail, MyAnswerViewItem } from "@/types/data";
/** 解析当前发布版本(契约 §1:入口寻址) */
export function resolveForm(code: string, appCode?: string) {
const params: Record<string, string> = { code };
if (appCode) params.appCode = appCode;
return alovaInstance.Get<ResolvedForm>("/api/form/resolve", { params }).send();
}
/** 提交答卷(契约 §5) */
export function submitAnswer(body: SubmitRequest) {
return alovaInstance.Post<SubmitView>("/api/form/submit", body).send();
}
/** 我的答卷列表(按 submitter 过滤,"我的提交"段) */
export function listMyAnswers(formCode: string, appCode?: string) {
const params: Record<string, string> = { formCode };
if (appCode) params.appCode = appCode;
return alovaInstance.Get<MyAnswerItem[]>("/api/form/my/list", { params }).send();
}
/** 软删我的答卷(确认对话框后调用) */
export function deleteMyAnswer(answerId: string) {
return alovaInstance.Post<void>("/api/form/my/delete", { answerId }).send();
}
/** 编辑我的答卷(按钉住 version 校验) */
export function editMyAnswer(answerId: string, values: Record<string, any>, summary?: string) {
return alovaInstance.Post<SubmitView>("/api/form/my/edit", { answerId, values, summary }).send();
}
// ==================== 我的填写两视图(T8,/my/submit 与 /my/about) ====================
// 两个视图 = 两个端点、两个谓词,不是一个端点加 view 参数(方案 §6.3"鉴权分支容易写漏")。
// 页面侧按路由分派,不做 query 参数分支;身份一律会话推导(V17),这里不传 userId。
/** 我提交的(submitter=会话用户,跨表单、不带值文档) */
export function listMySubmitted() {
return alovaInstance.Get<MyAnswerViewItem[]>("/api/form/my/submit-list").send();
}
/** 关于我的(subject=会话用户,含管理员代填的那几条——V18 的另一半) */
export function listAnswersAboutMe() {
return alovaInstance.Get<MyAnswerViewItem[]>("/api/form/my/about-list").send();
}
/** 我的答卷详情(钉住版本的 groups + 值文档,与数据管理面同一个 AnswerDetail 形状) */
export function getMyAnswerDetail(answerId: string) {
return alovaInstance.Get<AnswerDetail>("/api/form/my/detail", { params: { answerId } }).send();
}