feat(apply): 深链 from 参数解析 + 顶栏「返回门户」(T4)——site.ts 新增 resolveFrom(仅 http/https 且域名属本部署可信域,防伪协议/外域注入;session 级内存不落 localStorage),Apply.load 在 resolveSite 后调用,App.vue nav 按需渲染返回门户链接(nav 为主色底 header,白字+分隔线与既有风格一致)

This commit is contained in:
chenxing
2026-10-03 13:29:46 +08:00
parent 4243a5f0e6
commit f636075a5e
4 changed files with 38 additions and 2 deletions
+25 -1
View File
@@ -13,6 +13,8 @@ import config from "@/assets/config/host.config";
*/
const currentSiteCode = ref<string>(localStorage.getItem("svchall.public.site") || "");
const lastItemCode = ref<string>(localStorage.getItem("svchall.public.item") || "");
/** CMS 指南页来路(session 级内存,不落 localStorage——回跳是"这一次来的路",不是偏好) */
const portalFrom = ref<string>("");
export function useSite() {
/** 从 query / 运行时配置 / 本地记忆依次解析站点,命中即记住 */
@@ -29,6 +31,28 @@ export function useSite() {
return hit;
}
/**
* 解析并校验 from 参数(CMS 指南页深链带回,T4 回跳闭环):
* 仅接受 http/https 且 hostname 属于本部署可信域(当前页域名或 baseHost)的 URL,否则忽略——
* 防 javascript: 伪协议与外域注入。
*/
function resolveFrom(queryFrom?: unknown): string {
const raw = typeof queryFrom === "string" ? queryFrom.trim() : "";
if (!raw) return "";
try {
const u = new URL(raw);
if (u.protocol !== "http:" && u.protocol !== "https:") return "";
const mine = location.hostname;
const base = String(config.baseHost || "");
const baseHost = base ? new URL(base).hostname : "";
if (u.hostname !== mine && (!baseHost || u.hostname !== baseHost)) return "";
portalFrom.value = raw;
} catch {
// 非法 URL 当没有
}
return portalFrom.value;
}
function setSite(siteCode: string) {
currentSiteCode.value = siteCode;
if (siteCode) {
@@ -44,5 +68,5 @@ export function useSite() {
}
}
return { currentSiteCode, lastItemCode, resolveSite, setSite, rememberItem };
return { currentSiteCode, lastItemCode, portalFrom, resolveSite, resolveFrom, setSite, rememberItem };
}