t14:前端模块行退役——左导航三支路自解析、AppContext 重构为通用壳两视图
- App.vue:引导只剩 GetUserInfo,之后并行取 fillable/managed/capabilities 三支构造导航
(取不到按空集/false 走,fail-closed,不造兜底菜单树);摘 GetUserMod 调用与 setModules
- store:modules/IModule 状态与 setModules 删除,改存三支路数据 + managesForm() 判"这张表我管不管得动"
- AppContext.vue:重写为通用壳两视图(§5.1)——管理员视图=数据列表+复制填报路径
(添加/导入 T18 接进这一档),填报者视图=仅填报 tab;判定只看 managed 是否非空,
壳零自有权限逻辑;GetUserMod?appCode= 拉目标应用树那一路与 :modules prop 链整条拆除
- Data.vue:导出按钮改"该表单 ALL 档(managed 成员)",中心与快应用两种上下文从此同一条判据;
AnswerDetail.vue 的管理动作区同判据;utils/modulePerms.ts(canExport/hasPage)删除
- utils/menuNav.ts:buildNavTree 与"从树渲染菜单"三条规则删除,只留导航数据结构与
高亮/面包屑/默认跳转;menuRoute 的 /f/{code} 形态识别按任务书保留(分享直链/历史链接)
- mock:baseapi 去 GetUserMod 与两棵模块树;dataapi 补 /my/managed 与 /my/capabilities 桩
This commit is contained in:
+53
-25
@@ -4,10 +4,9 @@ import layout from "./components/layout.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import config from "@/assets/config/host.config";
|
||||
import { baseApi } from "@/api/module/base";
|
||||
import { resolveMenuPage } from "@/utils/menuRoute";
|
||||
import { buildNavTree, firstNavPage, isSingleNav, type NavNode } from "@/utils/menuNav";
|
||||
import { getMyCapabilities, listFillableForms, listManagedForms } from "@/api/module/fill";
|
||||
import { firstNavPage, isSingleNav, type NavNode } from "@/utils/menuNav";
|
||||
import { Message } from "view-ui-plus";
|
||||
import type { IModule } from "@/types/Basics";
|
||||
import { useDefaultStore } from "@/store";
|
||||
|
||||
const router = useRouter();
|
||||
@@ -17,7 +16,7 @@ const isReady = ref(false);
|
||||
|
||||
/**
|
||||
* 页面即组件(方案 §3.3):被 iframe 嵌入时自动剥壳——不渲染顶栏/左导航/面包屑,
|
||||
* 也不发两个鉴权请求(嵌入上下文里本应用 /api/base/* 取不到会话会弹错误提示并画出带壳页面)。
|
||||
* 也不发会话请求(嵌入上下文里本应用 /api/base/* 取不到会话会弹错误提示并画出带壳页面)。
|
||||
* 显式 mode 参数(?mode=embed|page)的闭集判定在 T7 填报页落地,壳这层只兜 self != top 这一条。
|
||||
*/
|
||||
const isEmbedded = window.self !== window.top;
|
||||
@@ -37,11 +36,36 @@ const layoutData = reactive({
|
||||
baseHost: config.baseHost,
|
||||
ssoHost: config.ssoHost,
|
||||
userInfo: null as any,
|
||||
/** 已按「path 空=权限锚点不进导航」规则过滤的可渲染菜单树(见 utils/menuNav.ts) */
|
||||
/** 自解析出来的可渲染导航(契约 v2 §2.4 三支路;不再由 UserBase 模块树生成) */
|
||||
navList: [] as NavNode[],
|
||||
isAlone: false,
|
||||
});
|
||||
|
||||
/** 导航节点 id:layout 只做高亮/面包屑对照,不参与任何判定,序号稳定即可 */
|
||||
let navSeq = 1000;
|
||||
function navItem(name: string, page: string, icon: string): NavNode {
|
||||
return { id: navSeq++, modId: page, name, icon, page, external: false, raw: page, group: false, children: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* 三支路自解析(D5):可填的表单 / 我管理的表单 / 校管理员系统设置。
|
||||
*
|
||||
* <b>前端不自行判权</b>——每一支的画不画只看后端那三把接口给了什么:
|
||||
* `fillable`(入集即可填)、`managed`(创建人 ∪ 表单管理员)、`capabilities.schoolAdmin`。
|
||||
*/
|
||||
function buildSelfResolvedNav(): NavNode[] {
|
||||
const nav: NavNode[] = [navItem("我的填写", "/my", "ios-person"), navItem("可填的表单", "/my/fillable", "ios-create")];
|
||||
if (store.managed.length) {
|
||||
nav.push(navItem("我管理的表单", "/admin/data", "ios-list"));
|
||||
nav.push(navItem("统计", "/admin/stats", "ios-pie"));
|
||||
}
|
||||
if (store.capabilities.schoolAdmin) {
|
||||
nav.push(navItem("表单设计", "/admin/forms", "md-build"));
|
||||
nav.push(navItem("系统设置", "/admin/app", "md-settings"));
|
||||
}
|
||||
return nav;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 首次导航异步完成后再判默认跳转:mount 时 currentRoute 可能仍是初始 "/"
|
||||
await router.isReady();
|
||||
@@ -50,38 +74,42 @@ onMounted(async () => {
|
||||
if (isEmbedded) return;
|
||||
|
||||
try {
|
||||
// 1. 当前会话用户
|
||||
// 会话用户(GetUserMod 已随模块行退役删除,引导只剩这一把 + 三支路自解析)
|
||||
const user: any = await baseApi.getUserInfo().send();
|
||||
const { id, name, profilePicture } = user || {};
|
||||
// 存一份给页面用(如字典页判平台级维护权);权限判定仍在后端,这里只是不重复发一次请求
|
||||
store.setUser(user || null);
|
||||
|
||||
// 2. 当前用户已授权的模块树(左导航数据源;path 空的权限锚点行也在内,由导航层跳过)
|
||||
const modList: IModule[] = (await baseApi.getUserMod().send()) || [];
|
||||
// 三支路并行取(互相独立,串行只会让首屏多等两个 RTT)
|
||||
const [fillable, managed, capabilities] = await Promise.all([
|
||||
listFillableForms().catch((e: any) => {
|
||||
console.warn("[自解析] 可填清单取不到,按空集处理:", e?.message);
|
||||
return [];
|
||||
}),
|
||||
listManagedForms().catch((e: any) => {
|
||||
console.warn("[自解析] 管理清单取不到,按空集处理:", e?.message);
|
||||
return [];
|
||||
}),
|
||||
getMyCapabilities().catch((e: any) => {
|
||||
console.warn("[自解析] 能力位取不到,按无入口处理(fail-closed):", e?.message);
|
||||
return { schoolAdmin: false };
|
||||
})
|
||||
]);
|
||||
store.setFillable(fillable || []);
|
||||
store.setManaged(managed || []);
|
||||
store.setCapabilities(capabilities || null);
|
||||
|
||||
layoutData.userInfo = { id, name: name || "用户", avatar: profilePicture || "" };
|
||||
layoutData.navList = buildNavTree(modList, resolveMenuPage);
|
||||
layoutData.navList = buildSelfResolvedNav();
|
||||
layoutData.isAlone = isSingleNav(layoutData.navList);
|
||||
// 原始树留一份:按钮级权限(快应用的"导出"锚点)按它判,导航树已把锚点行过滤掉了
|
||||
store.setModules(modList);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// 冒烟取证:原始树 vs 可渲染导航(path 空的权限锚点应当只出现在前者)
|
||||
console.table(
|
||||
(modList || []).map((m: any) => ({
|
||||
name: m?.name,
|
||||
id: m?.id,
|
||||
modId: m?.modId,
|
||||
page: m?.page,
|
||||
icon: m?.icon,
|
||||
children: (m?.children || []).length
|
||||
}))
|
||||
);
|
||||
console.table(
|
||||
layoutData.navList.map((n) => ({ 渲染项: n.name, page: n.page, 分组头: n.group }))
|
||||
layoutData.navList.map((n) => ({ 渲染项: n.name, page: n.page }))
|
||||
);
|
||||
}
|
||||
|
||||
// 入口页:跳第一个可跳转菜单项(无授权用户即「我的填写」)
|
||||
// 入口页:跳第一个可跳转菜单项
|
||||
if (router.currentRoute.value.path === "/") {
|
||||
const first = firstNavPage(layoutData.navList);
|
||||
if (first) router.replace(first);
|
||||
@@ -91,7 +119,7 @@ onMounted(async () => {
|
||||
if (error?.message !== "redirect") {
|
||||
Message.error("载入用户信息失败,请检查后端服务是否正常!");
|
||||
}
|
||||
// 降级只降「导航为空」,不造兜底菜单树——静默回落会把「平台未配模块/未授权」伪装成正常
|
||||
// 降级只降「导航为空」,不造兜底菜单树——静默回落会把"接口没通"伪装成正常
|
||||
store.setUser(null);
|
||||
layoutData.userInfo = { name: "用户", avatar: "" };
|
||||
layoutData.navList = [];
|
||||
|
||||
Reference in New Issue
Block a user