新增 AutoRDO 需求清洗工作台与消息中枢,迭代 OneOS V2 设计规范及租赁合同/工作台/车辆等原型,同步云效技能与导航注册;并归档一批 legacy 原型快照。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-07-28 15:50:35 +08:00
parent b14425da5a
commit 5d51a6bf7a
1393 changed files with 488042 additions and 10046 deletions

View File

@@ -1,10 +1,16 @@
/**
* OneOS 浅色 / 暗色主题localStorage + 跨 iframe postMessage
* OneOS 浅色 / 暗色主题 & RuoYi 偏好设置localStorage + 跨 iframe postMessage
* 暗色参照原型导航页VoltAgent 深色画布)。
* 不对「旧 ONEOS」目录下原型生效。
*/
import './oneos-theme-content.css';
import {
type RuoYiSettings,
readStoredRuoYiSettings,
persistRuoYiSettings,
DEFAULT_RUOYI_SETTINGS,
} from './RuoYiSettingsDrawer';
export type OneOsTheme = 'light' | 'dark';
@@ -30,29 +36,101 @@ export function isLegacyOneOsPrototype(id?: string | null): boolean {
}
export function readStoredOneOsTheme(): OneOsTheme {
if (typeof window === 'undefined') return 'light';
try {
const v = window.localStorage.getItem(ONEOS_THEME_STORAGE_KEY);
if (v === 'dark' || v === 'light') return v;
} catch {
/* ignore */
const settings = readStoredRuoYiSettings();
if (settings.themeMode === 'dark') return 'dark';
if (settings.themeMode === 'light') return 'light';
if (typeof window !== 'undefined') {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return 'light';
}
export function applyOneOsTheme(theme: OneOsTheme, root: HTMLElement = document.documentElement) {
root.setAttribute(ONEOS_THEME_ATTR, theme);
if (root === document.documentElement) {
document.documentElement.style.colorScheme = theme;
export function applyRuoYiSettingsToDocument(
doc: Document = document,
settings?: Partial<RuoYiSettings>,
) {
if (!doc || !doc.documentElement) return;
const currentSettings: RuoYiSettings = {
...readStoredRuoYiSettings(),
...settings,
};
const isDark =
currentSettings.themeMode === 'dark' ||
(currentSettings.themeMode === 'system' &&
typeof window !== 'undefined' &&
window.matchMedia('(prefers-color-scheme: dark)').matches);
const themeValue = isDark ? 'dark' : 'light';
doc.documentElement.setAttribute('data-oneos-theme', themeValue);
doc.documentElement.setAttribute('data-ds-mode', themeValue);
doc.documentElement.setAttribute('data-oneos-theme-mode', themeValue);
if (isDark) {
doc.documentElement.classList.add('dark');
if (doc.body) doc.body.classList.add('dark');
} else {
doc.documentElement.classList.remove('dark');
if (doc.body) doc.body.classList.remove('dark');
}
// 注入或更新主题 CSS 变量覆盖节点
let styleEl = doc.getElementById('oneos-ruoyi-theme-style-override') as HTMLStyleElement | null;
if (!styleEl) {
styleEl = doc.createElement('style');
styleEl.id = 'oneos-ruoyi-theme-style-override';
doc.head?.appendChild(styleEl);
}
const primary = currentSettings.primaryColor || '#533AFD';
const radius = `${Math.round((currentSettings.borderRadius ?? 0.5) * 16)}px`;
const fontSize = `${currentSettings.fontSize || 14}px`;
styleEl.textContent = `
:root, html, body, [data-oneos-theme], [data-ds-mode] {
--oneos-primary: ${primary} !important;
--ln-primary: ${primary} !important;
--el-color-primary: ${primary} !important;
--ln-primary-hover: ${primary} !important;
--ln-primary-focus: ${primary} !important;
--ruoyi-menu-active-text: ${primary} !important;
--oneos-radius: ${radius} !important;
--ln-radius: ${radius} !important;
font-size: ${fontSize} !important;
}
`;
}
export function persistOneOsTheme(theme: OneOsTheme) {
try {
window.localStorage.setItem(ONEOS_THEME_STORAGE_KEY, theme);
} catch {
/* ignore */
}
export function syncRuoYiSettingsToAllIframes(settings: RuoYiSettings) {
if (typeof document === 'undefined') return;
const frames = document.querySelectorAll('iframe');
frames.forEach((frame) => {
try {
if (frame.contentDocument) {
applyRuoYiSettingsToDocument(frame.contentDocument, settings);
}
} catch {
/* ignore cross-origin */
}
try {
const isDark =
settings.themeMode === 'dark' ||
(settings.themeMode === 'system' &&
window.matchMedia('(prefers-color-scheme: dark)').matches);
frame.contentWindow?.postMessage(
{
type: ONEOS_THEME_MESSAGE,
theme: isDark ? 'dark' : 'light',
settings,
},
'*',
);
} catch {
/* ignore */
}
});
}
export function broadcastOneOsTheme(theme: OneOsTheme) {
@@ -63,20 +141,24 @@ export function broadcastOneOsTheme(theme: OneOsTheme) {
} catch {
/* ignore */
}
const frames = document.querySelectorAll('iframe');
frames.forEach((frame) => {
try {
frame.contentWindow?.postMessage({ type: ONEOS_THEME_MESSAGE, theme }, '*');
} catch {
/* ignore */
}
});
const settings = readStoredRuoYiSettings();
syncRuoYiSettingsToAllIframes({ ...settings, themeMode: theme });
}
export function applyOneOsTheme(theme: OneOsTheme, root: HTMLElement = document.documentElement) {
const current = readStoredRuoYiSettings();
const nextSettings: RuoYiSettings = {
...current,
themeMode: theme,
};
persistRuoYiSettings(nextSettings);
applyRuoYiSettingsToDocument(root.ownerDocument || document, nextSettings);
}
export function setOneOsTheme(theme: OneOsTheme) {
applyOneOsTheme(theme);
persistOneOsTheme(theme);
broadcastOneOsTheme(theme);
const settings = readStoredRuoYiSettings();
syncRuoYiSettingsToAllIframes({ ...settings, themeMode: theme });
}
export function toggleOneOsTheme(current?: OneOsTheme): OneOsTheme {
@@ -87,7 +169,6 @@ export function toggleOneOsTheme(current?: OneOsTheme): OneOsTheme {
type BootstrapOptions = {
prototypeId?: string;
/** 强制跳过(如旧 ONEOS */
skip?: boolean;
};
@@ -98,25 +179,24 @@ export function bootstrapOneOsTheme(options: BootstrapOptions = {}) {
if (typeof window === 'undefined') return;
if (options.skip || isLegacyOneOsPrototype(options.prototypeId)) return;
const fromQuery = new URLSearchParams(window.location.search).get('oneosTheme');
const initial: OneOsTheme =
fromQuery === 'dark' || fromQuery === 'light' ? fromQuery : readStoredOneOsTheme();
applyOneOsTheme(initial);
persistOneOsTheme(initial);
const currentSettings = readStoredRuoYiSettings();
applyRuoYiSettingsToDocument(document, currentSettings);
window.addEventListener('message', (event) => {
const data = event.data;
if (!data || data.type !== ONEOS_THEME_MESSAGE) return;
if (data.theme !== 'dark' && data.theme !== 'light') return;
applyOneOsTheme(data.theme);
persistOneOsTheme(data.theme);
if (data.settings) {
applyRuoYiSettingsToDocument(document, data.settings);
} else if (data.theme === 'dark' || data.theme === 'light') {
applyRuoYiSettingsToDocument(document, { themeMode: data.theme });
}
});
window.addEventListener('storage', (event) => {
if (event.key !== ONEOS_THEME_STORAGE_KEY) return;
if (event.newValue === 'dark' || event.newValue === 'light') {
applyOneOsTheme(event.newValue);
if (event.key === RUOYI_SETTINGS_STORAGE_KEY) {
const nextSettings = readStoredRuoYiSettings();
applyRuoYiSettingsToDocument(document, nextSettings);
}
});
}