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:
@@ -22,7 +22,7 @@ import { resolveForm } from "@/api/module/fill";
|
||||
import AnswerValueView from "@/components/answer/AnswerValueView.vue";
|
||||
import AnswerEditModal from "@/components/answer/AnswerEditModal.vue";
|
||||
import { useDefaultStore } from "@/store";
|
||||
import { hasPage } from "@/utils/modulePerms";
|
||||
import { managesForm } from "@/store";
|
||||
import { statusDisplay } from "@/utils/dataValue";
|
||||
import type { AnswerDetail } from "@/types/data";
|
||||
import type { StatusDefView } from "@/types/form";
|
||||
@@ -36,8 +36,14 @@ const detail = ref<AnswerDetail | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref("");
|
||||
|
||||
/** 数据管理权(授权模块行判定;填表人身份进来时这里就是 false——V29 前端半句) */
|
||||
const canManage = computed(() => hasPage(store.modules, "/admin/data"));
|
||||
/**
|
||||
* 数据管理权(契约 v2 C4②:该表单 ALL 档;`modulePerms.hasPage` 随模块行退役删除)。
|
||||
* 判据=这张表在 `my/managed` 清单里;填表人身份进来时就是 false(V29 前端半句不变),
|
||||
* 接口侧的拒绝仍由后端守卫兜底——藏按钮不等于授权。
|
||||
*/
|
||||
const canManage = computed(() =>
|
||||
managesForm(store.managed, detail.value?.appCode, detail.value?.code)
|
||||
);
|
||||
const liveStatusDefs = ref<StatusDefView[]>([]);
|
||||
const statusModal = ref<{ visible: boolean; target: string }>({ visible: false, target: "" });
|
||||
const editOpen = ref(false);
|
||||
|
||||
+12
-20
@@ -13,10 +13,9 @@
|
||||
* 4. **数据范围以后端为准**:响应里的 `scopes` 只用来把"你现在看到的是哪个范围"讲清楚,
|
||||
* 前端不据此再过滤一遍(任务书 T8 红线)。
|
||||
*
|
||||
* 快应用上下文(AppContext)复用本组件:传 `context="app"` + `app-code` + `codes`
|
||||
* (候选来自 `GET /api/form/my/fillable?appCode=` 按层级筛出的 code,经 resolve 拿 uuid——
|
||||
* 定义管理权不在快应用上下文里,§3.4;P95 W5 起不再从该应用模块树挑 `/f/` 行)
|
||||
* + `modules`(**同一棵原始模块树**,只给按钮级权限判定用,P76)。
|
||||
* 快应用上下文(通用壳)复用本组件:传 `context="app"` + `app-code` + `codes`
|
||||
* (候选由壳按"我管理的表单 ∩ 该应用"算出,经 resolve 拿 uuid——定义管理权不在通用壳里,§3.4)。
|
||||
* 导出按钮的显隐不再需要宿主传任何权限载体:本组件自己按 `my/managed` 清单判 ALL 档(契约 v2 §2.4)。
|
||||
*/
|
||||
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
@@ -25,8 +24,7 @@ import { actionColumn } from "@/utils/tableActions";
|
||||
import { useElementHeight } from "@/utils/hook/useElementHeight";
|
||||
import { useFormCandidates } from "@/utils/hook/useFormCandidates";
|
||||
import { dataApi, downloadBlob, exportBlob } from "@/api/module/data";
|
||||
import { useDefaultStore } from "@/store";
|
||||
import { canExport } from "@/utils/modulePerms";
|
||||
import { managesForm, useDefaultStore } from "@/store";
|
||||
import AnswerEditModal from "@/components/answer/AnswerEditModal.vue";
|
||||
import {
|
||||
cellText,
|
||||
@@ -42,7 +40,6 @@ import {
|
||||
import { scopeLabel, type AnswerDetail, type AnswerRow, type FilterField, type FilterItem, type ListView } from "@/types/data";
|
||||
import type { StatusDefView } from "@/types/form";
|
||||
import type { DesignField } from "@/types/formSchema";
|
||||
import type { IModule } from "@/types/Basics";
|
||||
|
||||
/** 行视图 + 勾选态(`_checked` 是 view-ui-plus Table 的行内选中字段,见 withSelectionState) */
|
||||
type RowVM = AnswerRow & { _checked?: boolean };
|
||||
@@ -54,20 +51,13 @@ const props = withDefaults(
|
||||
appCode?: string;
|
||||
/** app 上下文的候选 code(来自 /api/form/my/fillable 的层级筛取,P95 W5) */
|
||||
codes?: string[];
|
||||
/**
|
||||
* 按钮级权限判定的**那棵树**(P76):app 上下文必须由宿主传入目标应用的模块树
|
||||
* (GetUserMod?appCode= 的结果)——中心会话树里没有快应用的"导出"锚点行,
|
||||
* 拿它判锚点等于永远判不中(fail-closed 方向:有授权也不画按钮)。
|
||||
* center 上下文不传,仍按 `store.modules`(会话自己的树)判页面行。
|
||||
*/
|
||||
modules?: IModule[] | null;
|
||||
/**
|
||||
* 被宿主页面内嵌(快应用上下文 /app/:appCode/)时置 true:只渲染内容本体,
|
||||
* 不再套一层 `.page-wrapper > .main-container`——否则会出现双层白卡与双份内边距。
|
||||
*/
|
||||
embedded?: boolean;
|
||||
}>(),
|
||||
{ context: "center", appCode: "", codes: () => [], modules: null, embedded: false }
|
||||
{ context: "center", appCode: "", codes: () => [], embedded: false }
|
||||
);
|
||||
|
||||
const route = useRoute();
|
||||
@@ -446,12 +436,14 @@ async function submitBatchStatus() {
|
||||
// ==================== 导出 ====================
|
||||
|
||||
/**
|
||||
* 权限判定用的树(P76):app 上下文=宿主编进来的**目标应用树**(快应用的"导出"锚点行在这里),
|
||||
* center 上下文=会话自己的树(按 `/admin/data` 页面行判,手册 §2.2 中心表没有导出行)。
|
||||
* 两种上下文各自吃自己的树,`canExport` 的纯函数判据不变。
|
||||
* 导出按钮显隐(契约 v2 §2.4:`modulePerms.canExport` 随模块行退役删除):
|
||||
* 判据换成"<b>该表单 ALL 档持有</b>"的清单形态——当前选中的这张表在 `my/managed` 里,就画导出。
|
||||
* 中心与快应用两种上下文从此**同一条判据**(不再有"两棵树"的差别,B13/P78 遗迹清零);
|
||||
* 接口侧的 403 兜底仍在后端守卫,这里只决定画不画。
|
||||
*/
|
||||
const permsTree = computed(() => (props.context === "app" ? props.modules : store.modules));
|
||||
const exportable = computed(() => canExport(permsTree.value, props.context === "app" ? "app" : "center"));
|
||||
const exportable = computed(() =>
|
||||
managesForm(store.managed, current.value?.appCode || props.appCode, current.value?.code)
|
||||
);
|
||||
const exporting = ref(false);
|
||||
|
||||
function askExport() {
|
||||
|
||||
Reference in New Issue
Block a user