Merge remote-tracking branch 'origin/dhk-1' into dhk-1
This commit is contained in:
commit
f3202f95b6
4 files changed
+41
-12
No files matched your search
@@ -9,7 +9,7 @@ import { useParams } from "react-router-dom";
|
|||||||
|
|
||||||
import { useTheme } from "@/contexts/ThemeContext";
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
import { useLightApp } from "@/strategy-components/api/light-apps";
|
import { useLightApp } from "@/strategy-components/api/light-apps";
|
||||||
import { buildLightAppUrl } from "@/strategy-components/lib/light-app";
|
import { buildLightAppUrl, type AppThemeColor } from "@/strategy-components/lib/light-app";
|
||||||
|
|
||||||
export default function LightAppIframeView() {
|
export default function LightAppIframeView() {
|
||||||
const { appId } = useParams<{ appId: string }>();
|
const { appId } = useParams<{ appId: string }>();
|
||||||
@@ -17,8 +17,14 @@ export default function LightAppIframeView() {
|
|||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
const { data: app, isLoading, error } = useLightApp(appId);
|
const { data: app, isLoading, error } = useLightApp(appId);
|
||||||
|
|
||||||
// 依赖 theme:切换主题色时重建 src,把对应主题色重新传给被嵌套应用。
|
// 由 useTheme 的 theme 直接换算主题色并显式传入,切主题时 src 立即重建、
|
||||||
const src = useMemo(() => (app ? buildLightAppUrl(app) : ""), [app, theme]);
|
// 把新主题色传给被嵌套应用(不依赖 <html> 的 dark class,避免时序读旧值)。
|
||||||
|
// TODO: 后续接入 blue 主题后,这里区分 blue 与 dark-blue。
|
||||||
|
const themeColor: AppThemeColor = isDark ? "dark-blue" : "red";
|
||||||
|
const src = useMemo(
|
||||||
|
() => (app ? buildLightAppUrl(app, undefined, themeColor) : ""),
|
||||||
|
[app, themeColor],
|
||||||
|
);
|
||||||
|
|
||||||
const centerClass = `flex h-full w-full items-center justify-center text-sm ${
|
const centerClass = `flex h-full w-full items-center justify-center text-sm ${
|
||||||
isDark ? "text-slate-400" : "text-slate-500"
|
isDark ? "text-slate-400" : "text-slate-500"
|
||||||
|
|||||||
@@ -707,8 +707,8 @@ function cleanParams(params: LightAppRouteParam[]): LightAppRouteParam[] {
|
|||||||
base.customValue = (p.customValue ?? "").trim();
|
base.customValue = (p.customValue ?? "").trim();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// theme:存映射串(或纯值),运行时按当前主题解析取值
|
// theme:规范化为完整映射串(red=..;blue=..;dark-blue=..),运行时按当前主题取值
|
||||||
base.themeValue = (p.themeValue ?? "").trim();
|
base.themeValue = stringifyThemeMap(parseThemeMap(p.themeValue));
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
|
|
||||||
import { apiFetch } from "@/core/api/fetch-client";
|
import { apiFetch } from "@/core/api/fetch-client";
|
||||||
import { getBackendBaseURL } from "@/core/config";
|
import { getBackendBaseURL } from "@/core/config";
|
||||||
|
import { parseThemeMap, stringifyThemeMap } from "@/strategy-components/lib/light-app";
|
||||||
|
|
||||||
export type LightAppType = "window_open" | "iframe";
|
export type LightAppType = "window_open" | "iframe";
|
||||||
export type RouteParamType = "login" | "theme";
|
export type RouteParamType = "login" | "theme";
|
||||||
@@ -104,7 +105,12 @@ function toApp(s: ServerApp): LightApp {
|
|||||||
paramType: p.paramType,
|
paramType: p.paramType,
|
||||||
source: p.source ?? null,
|
source: p.source ?? null,
|
||||||
customValue: p.customValue ?? null,
|
customValue: p.customValue ?? null,
|
||||||
themeValue: p.themeValue ?? null,
|
// 主题参数读取时规范化为完整映射串(red=..;blue=..;dark-blue=..),
|
||||||
|
// 让旧的纯值/部分映射也能随主题切换,无需重新保存。
|
||||||
|
themeValue:
|
||||||
|
p.paramType === "theme"
|
||||||
|
? stringifyThemeMap(parseThemeMap(p.themeValue))
|
||||||
|
: (p.themeValue ?? null),
|
||||||
})),
|
})),
|
||||||
mountLocation: s.mount_location ?? null,
|
mountLocation: s.mount_location ?? null,
|
||||||
icon: s.icon ?? null,
|
icon: s.icon ?? null,
|
||||||
|
|||||||
@@ -40,6 +40,16 @@ export const APP_THEME_COLORS: { key: AppThemeColor; label: string; dot: string
|
|||||||
{ key: "dark-blue", label: "深蓝色", dot: "#002B5C" },
|
{ key: "dark-blue", label: "深蓝色", dot: "#002B5C" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题键别名:兼容历史/手填里用 light/dark 等写法的映射键,归一到当前主题色键。
|
||||||
|
* 查映射时按别名顺序尝试,避免「键对不上 → 回退到 red」的恒红 bug。
|
||||||
|
*/
|
||||||
|
const THEME_KEY_ALIASES: Record<AppThemeColor, string[]> = {
|
||||||
|
red: ["red", "light"],
|
||||||
|
blue: ["blue"],
|
||||||
|
"dark-blue": ["dark-blue", "darkblue", "dark"],
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取我方当前主题色,以 ThemeContext 写在 <html> 上的 `dark` class 为准
|
* 读取我方当前主题色,以 ThemeContext 写在 <html> 上的 `dark` class 为准
|
||||||
* (含嵌入场景的强制主题)。当前:深色=dark-blue,浅色=red。
|
* (含嵌入场景的强制主题)。当前:深色=dark-blue,浅色=red。
|
||||||
@@ -73,8 +83,8 @@ export function stringifyThemeMap(map: Record<string, string>): string {
|
|||||||
* 按我方当前主题,从配置中取目标轻应用对应的值(跳转 / iframe 嵌套时注入)。
|
* 按我方当前主题,从配置中取目标轻应用对应的值(跳转 / iframe 嵌套时注入)。
|
||||||
*
|
*
|
||||||
* 配置支持两种写法(管理员在「应用管理」主题参数里配置):
|
* 配置支持两种写法(管理员在「应用管理」主题参数里配置):
|
||||||
* 1) 映射串:`red=red;blue=blue;dark-blue=darkBlue` —— 按当前主题取对应值;
|
* 1) 映射串:`red=red;blue=blue;dark-blue=darkBlue` —— 按当前主题(含别名)取对应值;
|
||||||
* 缺当前主题映射时回退到 red,再回退到首个值,最后回退到主题色名本身。
|
* 取不到时回退到主题色名本身(identity),而非盲目回退首个值(会恒为 red)。
|
||||||
* 2) 纯值:`red` —— 不区分主题,原样返回。
|
* 2) 纯值:`red` —— 不区分主题,原样返回。
|
||||||
*/
|
*/
|
||||||
export function resolveThemeParamValue(
|
export function resolveThemeParamValue(
|
||||||
@@ -86,7 +96,11 @@ export function resolveThemeParamValue(
|
|||||||
// 不含键值分隔符 → 纯值,不区分主题原样返回
|
// 不含键值分隔符 → 纯值,不区分主题原样返回
|
||||||
if (!/[=:]/.test(value)) return value;
|
if (!/[=:]/.test(value)) return value;
|
||||||
const map = parseThemeMap(value);
|
const map = parseThemeMap(value);
|
||||||
return (map[theme] ?? map.red ?? Object.values(map)[0] ?? theme).trim();
|
for (const key of THEME_KEY_ALIASES[theme] ?? [theme]) {
|
||||||
|
if (map[key] != null) return map[key].trim();
|
||||||
|
}
|
||||||
|
// 映射里没有当前主题(含别名)→ 退回主题色名本身,避免恒为 red
|
||||||
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 读取并解析 localStorage 中的 userInfo。 */
|
/** 读取并解析 localStorage 中的 userInfo。 */
|
||||||
@@ -105,10 +119,12 @@ export function readUserInfo(): Record<string, unknown> {
|
|||||||
export function resolveRouteParamValue(
|
export function resolveRouteParamValue(
|
||||||
param: LightAppRouteParam,
|
param: LightAppRouteParam,
|
||||||
userInfo: Record<string, unknown> = readUserInfo(),
|
userInfo: Record<string, unknown> = readUserInfo(),
|
||||||
|
theme?: AppThemeColor,
|
||||||
): string {
|
): string {
|
||||||
if (param.paramType === "theme") {
|
if (param.paramType === "theme") {
|
||||||
// 主题参数:按我方当前系统主题,从管理员配置的映射中取目标应用对应的值。
|
// 主题参数:按我方当前主题,从管理员配置的映射中取目标应用对应的值。
|
||||||
return resolveThemeParamValue(param.themeValue);
|
// theme 显式传入时优先(避免切主题瞬间 DOM 的 dark class 还没更新导致读旧值)。
|
||||||
|
return resolveThemeParamValue(param.themeValue, theme);
|
||||||
}
|
}
|
||||||
// login
|
// login
|
||||||
if (param.source === "other") {
|
if (param.source === "other") {
|
||||||
@@ -130,10 +146,11 @@ export function resolveRouteParamValue(
|
|||||||
export function buildLightAppUrl(
|
export function buildLightAppUrl(
|
||||||
app: Pick<LightApp, "url" | "routeParams">,
|
app: Pick<LightApp, "url" | "routeParams">,
|
||||||
userInfo: Record<string, unknown> = readUserInfo(),
|
userInfo: Record<string, unknown> = readUserInfo(),
|
||||||
|
theme?: AppThemeColor,
|
||||||
): string {
|
): string {
|
||||||
const pairs = (app.routeParams ?? [])
|
const pairs = (app.routeParams ?? [])
|
||||||
.filter((p) => p.key)
|
.filter((p) => p.key)
|
||||||
.map((p) => `${encodeURIComponent(p.key)}=${encodeURIComponent(resolveRouteParamValue(p, userInfo))}`);
|
.map((p) => `${encodeURIComponent(p.key)}=${encodeURIComponent(resolveRouteParamValue(p, userInfo, theme))}`);
|
||||||
if (pairs.length === 0) return app.url;
|
if (pairs.length === 0) return app.url;
|
||||||
const sep = app.url.includes("?") ? "&" : "?";
|
const sep = app.url.includes("?") ? "&" : "?";
|
||||||
return `${app.url}${sep}${pairs.join("&")}`;
|
return `${app.url}${sep}${pairs.join("&")}`;
|
||||||
|
|||||||
Reference in new issue
Block a user