t13:前端换形——audience 数组多选 + 冻结/匿名窗口开关,fillable 行去 canFill/myTier
- types/form.ts:AUDIENCE 改字符串码闭集(含 ANONYMOUS)+ audienceText(); FormView/FormConfigRequest 加 audience 数组、frozen、anonCookieMinutes - types/data.ts:FillableForm 去 canFill/myTier(C2,入集即可填); DATA_SCOPE_LABELS 由四枚举改两档(ALL/SELF,§19 作废-1) - FormConfigModal:面向人群改复选组(多选共集)+ 冻结开关(文案指路"喂外部业务应用的表单请开启") + 匿名防重窗口;含 ANONYMOUS 时限次置灰并在提交前挡(与后端同判据) - Forms.vue 面向列、My.vue「可填写」卡集、fill.ts/routes.ts 注记同步换形 - AppContext.vue:填报 tab 直接吃入集行;数据/统计两档的驱动位改由 T14 的 managed 端点接, 本批不留"按 myTier 猜"的兼容分支(壳零自有权限逻辑) - mock:formapi/dataapi 的 audience 数组形态 + 匿名×限次跨字段约束
This commit is contained in:
+11
-14
@@ -41,20 +41,17 @@ const rawTree = ref<IModule[]>([]);
|
||||
const loading = ref(false);
|
||||
const errorText = ref("");
|
||||
|
||||
/** 填报 tab 的行:后端 canFill 判定(层②"只能代填"的行 canFill=false,不进这张列表) */
|
||||
const fillCards = computed(() => fillRows.value.filter((r) => r.canFill));
|
||||
/** 数据管理候选=层②∪层③;统计候选=层③(与后端 FillAccessService 的三个谓词一一对应) */
|
||||
const manageRows = computed(() => fillRows.value.filter((r) => r.myTier === "MANAGE" || r.myTier === "ADMIN"));
|
||||
const adminRows = computed(() => fillRows.value.filter((r) => r.myTier === "ADMIN"));
|
||||
|
||||
function codesOf(rows: FillableForm[]): string[] {
|
||||
const codes: string[] = [];
|
||||
for (const row of rows) if (!codes.includes(row.code)) codes.push(row.code);
|
||||
return codes;
|
||||
}
|
||||
|
||||
const dataCodes = computed(() => codesOf(manageRows.value));
|
||||
const statsCodes = computed(() => codesOf(adminRows.value));
|
||||
/** 填报 tab 的行:契约 v2 C2 之后入集即等价于可填(行里没有 canFill,前端不再筛) */
|
||||
const fillCards = computed(() => fillRows.value);
|
||||
/**
|
||||
* 数据管理/统计两档的驱动位在 T13 这一批处于<b>换轨空窗</b>:
|
||||
* 原驱动是 P95 的 `myTier`(三层模型的最高层),随 §19 作废-3 一起删掉了;
|
||||
* 新驱动是 T14 的 `GET /api/form/my/managed`(创建人 ∪ form_admin 的"我管理的表单")。
|
||||
* 本批同一次执行内就会接上,空窗期这两档不出现在壳上——<b>刻意不留"按 myTier 猜"的兼容分支</b>,
|
||||
* 免得留下第二套判定口径(红线:壳零自有权限逻辑)。
|
||||
*/
|
||||
const dataCodes = computed<string[]>(() => []);
|
||||
const statsCodes = computed<string[]>(() => []);
|
||||
const showDataTab = computed(() => dataCodes.value.length > 0);
|
||||
const showStatsTab = computed(() => statsCodes.value.length > 0);
|
||||
|
||||
|
||||
+10
-9
@@ -9,11 +9,11 @@
|
||||
* 两者是**两个端点、两个谓词**,页面按路由分派;顶部 Tab 只是 UI 导航(切路由),不是接口参数。
|
||||
* 管理员代填后:这条答卷在**填表人的"我提交的"**里出现,同时在**被代填人的"关于我的"**里出现(V18)。
|
||||
*
|
||||
* P91 起第三视图「可填写」(/my/fillable),P95 W4 换数据源:这不再是答卷视图,而是**填报入口列表**。
|
||||
* 数据源从"会话菜单树里挑 path 形如 /f/{code} 的行"改成 `GET /api/form/my/fillable`——
|
||||
* 谁能填哪张表由中心按三层模型(audience × 身份 × 管理员两表)自解析,
|
||||
* P91 起第三视图「可填写」(/my/fillable),P95 W4 换数据源、契约 v2 C2 定行形状:这不再是答卷视图,
|
||||
* 而是**填报入口列表**。数据源从"会话菜单树里挑 path 形如 /f/{code} 的行"改成 `GET /api/form/my/fillable`——
|
||||
* 谁能填哪张表由中心按 <b>audience 门</b>(会话身份类型 ∈ 表单面向人群)自解析,
|
||||
* UserBase 侧不再需要逐表单配 `/f/` 模块行(任务书 P95 §0.1/§0.2 红线 6:分享链接与直链解析不动)。
|
||||
* 前端照旧**不做本地角色过滤**:接口下发 canFill=true 的才画卡(canFill 是后端算的,不是我筛的)。
|
||||
* 前端照旧**不做本地角色过滤**:接口下发什么就画什么(入集即等价于可填,行里不再有 canFill)。
|
||||
*
|
||||
* 三条交互口径沿 T7 的"我的提交"段(不另造轮子):
|
||||
* 1. **编辑入口只在"我提交的"**:编辑=填表人的动作;"关于我的"整列只读
|
||||
@@ -67,7 +67,7 @@ const VIEW_META = {
|
||||
fillable: {
|
||||
title: "可填写",
|
||||
endpoint: "GET /api/form/my/fillable",
|
||||
predicate: "谓词:后端按三层模型解析出的填报入口(canFill=true 才画卡)",
|
||||
predicate: "谓词:会话身份类型 ∈ form.audience(audience 门,与提交时同一公式)",
|
||||
note: "这里是当前账号能填报的表单。表单发布后按面向人群自动出现在这里——不需要有人在基础平台逐张表配模块行。"
|
||||
}
|
||||
} as const;
|
||||
@@ -84,12 +84,13 @@ const loadedOnce = ref(false);
|
||||
const errorText = ref("");
|
||||
|
||||
/**
|
||||
* 「可填写」的卡片集=接口里 `canFill=true` 的行。
|
||||
* 「可填写」的卡片集=接口下发的全部行。
|
||||
*
|
||||
* <p>过滤条件只有这一条,且判定<b>来自后端</b>:`canFill=false` 的行(教师×面向学生那种"只能代填")
|
||||
* 由后端按三层模型算出,前端只是不把它画成"进入填报"的卡——前端不重新按角色判一遍(T8 红线:范围以后端为准)。</p>
|
||||
* <p>契约 v2 C2 之后行里没有 `canFill`:<b>入集即等价于可填</b>(后端按 audience 门筛过一次,
|
||||
* 不再有"进了列表但不能自填、只给代填"的中间态)。前端依旧不做任何本地身份过滤
|
||||
* (T8 红线:范围与资格以后端为准,这里只画后端给的东西)。</p>
|
||||
*/
|
||||
const fillableCards = computed<FillableForm[]>(() => fillableRows.value.filter((r) => r.canFill));
|
||||
const fillableCards = computed<FillableForm[]>(() => fillableRows.value);
|
||||
|
||||
/** 会话用户 id:判"这条是不是我提交的"(编辑/删除入口显隐),身份来源仍是会话(不传后端) */
|
||||
const sessionUserId = computed(() => store.sessionUserId);
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useElementHeight } from "@/utils/hook/useElementHeight";
|
||||
import FormConfigModal from "@/components/form/FormConfigModal.vue";
|
||||
import FormAdminModal from "@/components/admin/FormAdminModal.vue";
|
||||
import type { FormView } from "@/types/form";
|
||||
import { AUDIENCE, FORM_STATUS } from "@/types/form";
|
||||
import { FORM_STATUS, audienceText } from "@/types/form";
|
||||
import { CODE_PATTERN, MAX_NAME_LEN } from "@/types/formSchema";
|
||||
|
||||
const router = useRouter();
|
||||
@@ -86,8 +86,9 @@ const columns = computed(() => [
|
||||
{
|
||||
title: "面向",
|
||||
key: "audience",
|
||||
width: 90,
|
||||
render: (h2: any, p: any) => h2("span", p.row.audience === AUDIENCE.TEACHER ? "面向教师" : "面向学生")
|
||||
width: 120,
|
||||
// 人群是多值(契约 v2 C1),列宽给得比原来宽一点:"学生/教师/匿名" 三种组合都要能读完
|
||||
render: (h2: any, p: any) => h2("span", audienceText(p.row.audience))
|
||||
},
|
||||
{
|
||||
title: "截止",
|
||||
|
||||
Reference in New Issue
Block a user