Files
form-center-front/src/api/module/form.ts
T
chenxing 14d45f7e3c
build / build (push) Successful in 3m17s
feat(t9): P76 导出锚点判树修正 + P77 应用内详情形态 + 观察项①勾选回填 + 演练桩
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 推送分层)。
2026-10-15 12:57:10 +08:00

117 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 定义域管理端接口(后端 `FormAdminController`,/api/form/admin/*,CAS 会话 + 定义管理模块行把关)。
*
* 写法沿 T5 定稿的 P49:`alovaInstance.Get<T>(url, {params})` / `Post<T>(url, body, {params})`——
* 同一个 Method 对象、同一套拦截器与信封解包(Notice 的 `new Method<T>` 在 alova 3.5 类型下不合法)。
*
* 两条参数分工照后端原样,别自己发挥:
* - `uuid` 寻址表单(库内 id 从不外露,契约 §1/V5);
* - `schId` 只是**超管的目标域**选择(含 0=平台级),校级管理员传别的值直接被 FormAdminGuard 拒;
* 本端默认不传(会话校),只有超管切平台级上下文时才显式带。
* publish/disable/enable/softDelete 的 uuid 走 query(后端是 @RequestParam,没有请求体)。
*/
import { alovaInstance } from "@/utils/alova/alova-iview";
import type {
FormConfigRequest,
FormCopyRequest,
FormCreateRequest,
FormSaveRequest,
FormView,
StatusDefsRequest,
VersionView
} from "@/types/form";
const BASE = "/api/form/admin";
/** 目标域参数:只在超管显式选择上下文时带(undefined 一律剔除,避免 query 里出现空值) */
type Scope = { schId?: number };
function paramsOf(params: Record<string, unknown>): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(params)) {
if (v === undefined || v === null || v === "") continue;
out[k] = v;
}
return out;
}
class FormApi {
/** 表单列表(头表 + 版本指针,不含 schema 正文;无服务端分页,一期按校量级够用) */
list(scope: Scope = {}, filter: { appCode?: string; status?: number } = {}) {
return alovaInstance.Get<FormView[]>(`${BASE}/list`, {
params: paramsOf({ ...scope, ...filter })
});
}
/** 单个表单头表(状态/版本指针/表单级配置/状态定义都在这里,schema 正文要另查版本行) */
get(uuid: string, scope: Scope = {}) {
return alovaInstance.Get<FormView>(`${BASE}/get`, { params: paramsOf({ ...scope, uuid }) });
}
/** 版本行清单(升版流水账:version/published/changeLog/publishTime) */
versions(uuid: string, scope: Scope = {}) {
return alovaInstance.Get<VersionView[]>(`${BASE}/versions`, { params: paramsOf({ ...scope, uuid }) });
}
/** 单个版本行的 schema 正文(编辑回显;已发布行内容逐字节冻结,V7) */
version(uuid: string, version: number, scope: Scope = {}) {
return alovaInstance.Get<VersionView>(`${BASE}/version`, {
params: paramsOf({ ...scope, uuid, version })
});
}
/** 筛选候选列:只出显式 searchable 且当前发布版本里的字段,附件两类已被后端排除(P28) */
filterFields(uuid: string, scope: Scope = {}) {
return alovaInstance.Get<string[]>(`${BASE}/filterFields`, { params: paramsOf({ ...scope, uuid }) });
}
/** 新建草稿(建草稿即生成 uuid,跨版本不变;version 从 1 起) */
create(body: FormCreateRequest, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/create`, body, { params: paramsOf({ ...scope }) });
}
/** 保存:草稿直改;已发布表单=开草稿行,头表 status/current_version 不动(V7、P20) */
save(body: FormSaveRequest, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/save`, body, { params: paramsOf({ ...scope }) });
}
/** 发布:schema 契约校验 + 字段身份稳定 + 字典可解析,三道都过才把草稿行钉成版本并推指针 */
publish(uuid: string, changeLog?: string, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/publish`, {}, {
params: paramsOf({ ...scope, uuid, changeLog })
});
}
/** 停用:只断填报入口,数据可读可导(方案 §5.4) */
disable(uuid: string, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/disable`, {}, { params: paramsOf({ ...scope, uuid }) });
}
/** 停用恢复:重新发布当前版本,不产生新版本行 */
enable(uuid: string, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/enable`, {}, { params: paramsOf({ ...scope, uuid }) });
}
/** 软删(唯一删除形态,无物理删除接口) */
softDelete(uuid: string, scope: Scope = {}) {
return alovaInstance.Post<void>(`${BASE}/softDelete`, {}, { params: paramsOf({ ...scope, uuid }) });
}
/** 另存为副本(V41):新 code/新 uuid/version 从 1 起/schema 深拷贝,源表单零改动 */
copy(body: FormCopyRequest, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/copy`, body, { params: paramsOf({ ...scope }) });
}
/** 表单级配置(不进版本快照,改它不升版):audience/限次/截止/报名号/分组标签 */
config(body: FormConfigRequest, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/config`, body, { params: paramsOf({ ...scope }) });
}
/** 状态定义(V12):statusDefs 传 null=回默认「待处理/已处理」 */
statusDefs(body: StatusDefsRequest, scope: Scope = {}) {
return alovaInstance.Post<FormView>(`${BASE}/statusDefs`, body, { params: paramsOf({ ...scope }) });
}
}
export const formApi = new FormApi();