mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-05-27 19:03:47 +00:00
192 lines
6.0 KiB
Vue
192 lines
6.0 KiB
Vue
// @ts-nocheck
|
|
// 以下 6 个 placeholder 标识符由 build/vite/plugin/theme-plugin/injectClientPlugin.ts
|
|
// 在 transform hook 中以正则单词边界字符串替换为最终值。
|
|
// 不要写 `declare const __X__: T` —— 替换会破坏 declare 语句结构。
|
|
// vite 用 oxc/esbuild 编译 .ts 不做严格类型检查,未声明标识符能编过。
|
|
|
|
export const globalField = '__VITE_THEME__';
|
|
export const styleTagId = '__VITE_PLUGIN_THEME__';
|
|
export const darkStyleTagId = '__VITE_PLUGIN_DARK_THEME__';
|
|
export const linkID = '__VITE_PLUGIN_THEME-ANTD_DARK_THEME_LINK__';
|
|
|
|
const colorPluginOutputFileName = __COLOR_PLUGIN_OUTPUT_FILE_NAME__;
|
|
const isProd = __PROD__;
|
|
const colorPluginOptions = __COLOR_PLUGIN_OPTIONS__;
|
|
const injectTo = colorPluginOptions && colorPluginOptions.injectTo;
|
|
|
|
const debounceThemeRender = debounce(200, renderTheme);
|
|
|
|
export let darkCssIsReady = false;
|
|
|
|
(() => {
|
|
if (!(window as any)[globalField]) {
|
|
(window as any)[globalField] = {
|
|
styleIdMap: new Map(),
|
|
styleRenderQueueMap: new Map(),
|
|
};
|
|
}
|
|
setGlobalOptions('replaceStyleVariables', replaceStyleVariables);
|
|
if (!getGlobalOptions('defaultOptions')) {
|
|
setGlobalOptions('defaultOptions', colorPluginOptions);
|
|
}
|
|
})();
|
|
|
|
export function addCssToQueue(id: string, styleString: string) {
|
|
const styleIdMap: Map<string, string> = getGlobalOptions('styleIdMap');
|
|
if (!styleIdMap.get(id)) {
|
|
(window as any)[globalField].styleRenderQueueMap.set(id, styleString);
|
|
debounceThemeRender();
|
|
}
|
|
}
|
|
|
|
function renderTheme() {
|
|
const variables = getGlobalOptions('colorVariables');
|
|
if (!variables) return;
|
|
const styleRenderQueueMap: Map<string, string> = getGlobalOptions('styleRenderQueueMap');
|
|
const styleDom = getStyleDom(styleTagId);
|
|
let html = styleDom.innerHTML;
|
|
for (const [id, css] of styleRenderQueueMap.entries()) {
|
|
html += css;
|
|
(window as any)[globalField].styleRenderQueueMap.delete(id);
|
|
(window as any)[globalField].styleIdMap.set(id, css);
|
|
}
|
|
replaceCssColors(html, variables).then((processCss) => {
|
|
appendCssToDom(styleDom, processCss, injectTo);
|
|
});
|
|
}
|
|
|
|
export async function replaceStyleVariables({
|
|
colorVariables,
|
|
customCssHandler,
|
|
}: {
|
|
colorVariables: string[];
|
|
customCssHandler?: (css: string) => string;
|
|
}) {
|
|
setGlobalOptions('colorVariables', colorVariables);
|
|
const styleIdMap: Map<string, string> = getGlobalOptions('styleIdMap');
|
|
const styleRenderQueueMap: Map<string, string> = getGlobalOptions('styleRenderQueueMap');
|
|
if (!isProd) {
|
|
for (const [id, css] of styleIdMap.entries()) {
|
|
styleRenderQueueMap.set(id, css);
|
|
}
|
|
renderTheme();
|
|
} else {
|
|
try {
|
|
const cssText = await fetchCss(colorPluginOutputFileName);
|
|
const styleDom = getStyleDom(styleTagId);
|
|
const processCss = await replaceCssColors(cssText, colorVariables, customCssHandler);
|
|
appendCssToDom(styleDom, processCss, injectTo);
|
|
} catch (error: any) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function loadDarkThemeCss() {
|
|
if (darkCssIsReady || !__ANTD_DARK_PLUGIN_EXTRACT_CSS__) return;
|
|
if (__ANTD_DARK_PLUGIN_LOAD_LINK__) {
|
|
const linkTag = document.getElementById(linkID);
|
|
if (linkTag) {
|
|
linkTag.removeAttribute('disabled');
|
|
linkTag.setAttribute('rel', 'stylesheet');
|
|
}
|
|
} else {
|
|
const cssText = await fetchCss(__ANTD_DARK_PLUGIN_OUTPUT_FILE_NAME__);
|
|
const styleDom = getStyleDom(darkStyleTagId);
|
|
appendCssToDom(styleDom, cssText, injectTo);
|
|
}
|
|
darkCssIsReady = true;
|
|
}
|
|
|
|
export async function replaceCssColors(
|
|
css: string,
|
|
colors: string[],
|
|
customCssHandler?: (css: string) => string,
|
|
) {
|
|
let retCss = css;
|
|
const defaultOptions = getGlobalOptions('defaultOptions');
|
|
const colorVariables: string[] = defaultOptions ? defaultOptions.colorVariables || [] : [];
|
|
colorVariables.forEach(function (color, index) {
|
|
const reg = new RegExp(
|
|
color.replace(/,/g, ',\\s*').replace(/\s/g, '').replace('(', `\\(`).replace(')', `\\)`) +
|
|
'([\\da-f]{2})?(\\b|\\)|,|\\s)?',
|
|
'ig',
|
|
);
|
|
retCss = retCss.replace(reg, colors[index] + '$1$2').replace('$1$2', '');
|
|
if (customCssHandler && typeof customCssHandler === 'function') {
|
|
retCss = customCssHandler(retCss) || retCss;
|
|
}
|
|
});
|
|
return retCss;
|
|
}
|
|
|
|
export function setGlobalOptions(key: string, value: any) {
|
|
(window as any)[globalField][key] = value;
|
|
}
|
|
|
|
export function getGlobalOptions(key: string) {
|
|
return (window as any)[globalField][key];
|
|
}
|
|
|
|
export function getStyleDom(id: string): HTMLStyleElement {
|
|
let style = document.getElementById(id) as HTMLStyleElement | null;
|
|
if (!style) {
|
|
style = document.createElement('style');
|
|
style.setAttribute('id', id);
|
|
}
|
|
return style;
|
|
}
|
|
|
|
export async function appendCssToDom(
|
|
styleDom: HTMLStyleElement,
|
|
cssText: string,
|
|
appendTo: 'head' | 'body' | 'body-prepend' = 'body',
|
|
) {
|
|
styleDom.innerHTML = cssText;
|
|
if (appendTo === 'head') {
|
|
document.head.appendChild(styleDom);
|
|
} else if (appendTo === 'body') {
|
|
document.body.appendChild(styleDom);
|
|
} else if (appendTo === 'body-prepend') {
|
|
const firstChildren = document.body.firstChild;
|
|
document.body.insertBefore(styleDom, firstChildren);
|
|
}
|
|
}
|
|
|
|
function fetchCss(fileName: string): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const append = getGlobalOptions('appended');
|
|
if (append) {
|
|
setGlobalOptions('appended', false);
|
|
resolve('');
|
|
return;
|
|
}
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.onload = function () {
|
|
if (xhr.readyState === 4) {
|
|
if (xhr.status === 200) resolve(xhr.responseText);
|
|
else reject(xhr.status);
|
|
}
|
|
};
|
|
xhr.onerror = function (e) {
|
|
reject(e);
|
|
};
|
|
xhr.ontimeout = function (e) {
|
|
reject(e);
|
|
};
|
|
xhr.open('GET', fileName, true);
|
|
xhr.send();
|
|
});
|
|
}
|
|
|
|
function debounce<T extends (...args: any[]) => any>(delay: number, fn: T) {
|
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
return function (this: any, ...args: any[]) {
|
|
const ctx = this;
|
|
if (timer) clearTimeout(timer);
|
|
timer = setTimeout(function () {
|
|
fn.apply(ctx, args);
|
|
}, delay);
|
|
};
|
|
}
|