Files
svc-hall-hall/src/App.vue
T
chenxing 351a513547
build / build (push) Successful in 2m12s
feat(p3.3): 应用内审批前端——flow-approval 组件族本地化 + /todo 我的审批页 + 详情审批卡片
- 从 Notice/front 复制 flow-approval 六件并本地修复(漂移登记组件 README,Notice 不回改):
  · P0-04:四个写调用点(List 通过/驳回 + Card 通过/驳回)全部实际携带本行 bizId,
    选中 bizId 随弹窗生命周期锁定/清理
  · P0-05:删 useElementHeight/yh-* 平台布局依赖,本地 fa-* 样式(theme-base.css --sh-* 令牌)
  · P0-06:types.ts 本地契约——增 my(行=ApplyVo 子集/FlowPage 完整元数据/状态 0=待提交
    禁止当全部,全部用 -1 哨兵)、删 instances(不代理全校视角)、approve/reject 增 bizId 参
  · P1-05:卡片轨迹不自取(详情页把 /progress 转换 TraceVo 经 props 传入);节点摘要取
    active steps 节点名;MULTI_TASK 逐条列出本人待办各带动作,不默认 tasks[0];
    未进流程(flowInstanceId 空)详情页不渲染卡片(显式决策)
  · P1-09:FlowTaskEmbed 消息四条件(打开中+严格 origin+本 iframe+taskId/bizId 上下文一致),
    换址重置高度;宿主换址请求带代际号,关闭/换件废弃在途响应并清上下文;
    open-biz 只用与当前办件一致的 bizId 构造 /apply-detail?id=,不透传任意 URL
- ApprovalApi 类挂 svc.ts(todo/my/byBiz/approve/reject/recordUrl/taskPageUrl,写面
  taskId/applyId 查询参数+opinion/edgeName JSON body 两形态区分)+ approvalAdapter.ts 唯一映射
- /todo 路由 + 顶部导航第三链接「我的审批」+ MyApprovals.vue(双页签+记录抽屉+完整办理)
- ApplyDetail 挂审批卡片(审批操作区)+ TraceVo 转换器
- 小屏基础响应式(drawer/modal 宽度自适应、列表横向滚动;1200px 全量适配不在本批)
- 门禁:vue-tsc(ts4.9 外挂兼容组合)错误数 HEAD=工作区=18 零新增(仓内 vue-tsc1.8+ts5.9
  崩溃系历史遗留);vite build 绿(MyApprovals/approvalAdapter chunk 产出);
  preview 冒烟:/todo 渲染完整(导航/页签/表格空态/API 失败优雅降级)、详情页无崩溃(Stage S4/P3.3)
2026-10-19 23:59:48 +08:00

96 lines
4.3 KiB
Vue
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.
<script setup lang="ts">
/**
* 公共办事大厅外壳(学生/教师/访客入口):
* 顶栏(站点名 + 在线办理/我的办件导航 + 当前用户)+ 内容区。
* 与后台前端(front/)刻意不同:不渲染平台模块菜单(app_module 是管理端契约),
* 只做两个入口——办事大厅、我的办件(详情/办理页从列表进入)。
*
* 皮肤三层优先级(T3 弱联动,2026-10-03):
* CMS 站点推荐(/api/hall/site 的 theme/themeParams,经 utils/cmsTheme 映射;
* stale-while-revalidate——localStorage 缓存先画、后台刷新,失败静默)
* > env-config 部署级皮肤 > default。
* 推荐值、不保证生效:任一层缺失/非法都回落下一层(utils/theme.ts)。
*/
import { onMounted, ref } from "vue";
import { baseApi } from "@/api/module/base";
import config from "@/assets/config/host.config";
import { applyTheme, resolveTheme, type ThemeManifest } from "@/utils/theme";
import { toHallSkin } from "@/utils/cmsTheme";
import { useSite } from "@/utils/site";
import { hallApi } from "@/api/module/svc";
const userName = ref("");
const theme = ref<ThemeManifest>(resolveTheme(null));
// CMS 指南页来路(T4 回跳闭环):仅 /#/apply 深链带合法 from 参数时非空
const { portalFrom, currentSiteCode } = useSite();
onMounted(async () => {
// ① 基线:env-config 部署级皮肤(现状语义不变,先应用保证首屏观感)
theme.value = applyTheme(config.theme, config.themeParams);
// ② 站点推荐:CMS 站点主题(弱联动)——缓存先画,后台刷新
applySiteThemeRecommend();
// 用户信息失败不影响办理页面使用
try {
const user: any = await baseApi.getUserInfo().send();
userName.value = user?.name || "";
} catch (e: any) {
// CAS 未登录时平台层会跳转登录页(error.message === "redirect"),此处不打扰用户
if (e?.message !== "redirect") {
console.warn("[public-hall] 获取用户信息失败", e);
}
}
});
/**
* 站点主题推荐应用(stale-while-revalidate):
* 未登录时该请求会 302 跳 CAS(与 items 同行为,页面本来就要登录);
* 任何失败静默保留现皮肤——推荐、不保证生效,绝不阻塞首屏。
*/
async function applySiteThemeRecommend() {
const code = currentSiteCode.value; // useSite() 模块级 ref,当前视图 onMounted 已 resolve
if (!code) return;
const cacheKey = `svchall.public.siteTheme.${code}`;
// ① stale:同步读缓存立即应用(二次访问无闪色)
try {
const cached = JSON.parse(localStorage.getItem(cacheKey) || "null");
const stale = toHallSkin(cached);
if (stale) theme.value = applyTheme(stale.name, stale.params);
} catch {
// 坏缓存当没有
}
// ② revalidate:后台刷新(失败静默保留现皮肤——推荐、不保证生效)
try {
const info: any = await hallApi.site(code).send();
const pick = toHallSkin({ theme: info?.theme, themeParams: info?.themeParams });
localStorage.setItem(cacheKey, JSON.stringify({
theme: info?.theme || null, themeParams: info?.themeParams || null,
}));
if (pick) theme.value = applyTheme(pick.name, pick.params);
} catch (e) {
console.warn("[public-hall] 站点主题获取失败,沿用当前皮肤", e);
}
}
</script>
<template>
<div class="sh-shell">
<header class="sh-header">
<div class="sh-brand">
办事大厅
<small>在线办理 · {{ theme.title }}</small>
</div>
<nav class="sh-nav">
<router-link to="/hall">办事大厅</router-link>
<router-link to="/my">我的办件</router-link>
<router-link to="/todo">我的审批</router-link>
<a v-if="portalFrom" :href="portalFrom" class="sh-back-portal">返回门户</a>
</nav>
<span class="sh-user" v-if="userName">{{ userName }}</span>
</header>
<main class="sh-main">
<router-view :key="$route.fullPath" />
</main>
<footer class="sh-footer">云海平台 · 办事大厅(在线申请入口)</footer>
</div>
</template>