fix: 修复子路径部署静态资源引用

- 修复 admin 与 usercenter 登录验证码资源在 /flow 子路径下的加载路径

- 统一 logo、空状态图、兜底头像与模型服务商图标的 BASE_URL 处理

- 补齐 usercenter 公共布局与 loading 注入的子路径兼容
This commit is contained in:
2026-03-20 13:02:39 +08:00
parent 39a6daf8fe
commit 07d8193e80
15 changed files with 102 additions and 19 deletions

View File

@@ -14,6 +14,7 @@ defineProps({
},
});
const emptyImageUrl = `${import.meta.env.BASE_URL || '/'}empty.png`;
const themeMode = ref(preferences.theme.mode);
watch(
() => preferences.theme.mode,
@@ -26,7 +27,7 @@ watch(
<template>
<div class="res-container">
<JsonViewer v-if="value" :value="value" copyable :theme="themeMode" />
<ElEmpty image="/empty.png" v-else />
<ElEmpty :image="emptyImageUrl" v-else />
</div>
</template>

View File

@@ -17,6 +17,7 @@ const props = withDefaults(defineProps<PageDataProps>(), {
pageSizes: () => [10, 20, 50, 100],
extraQueryParams: () => ({}),
});
const emptyImageUrl = `${import.meta.env.BASE_URL || '/'}empty.png`;
// 响应式数据
const pageList = ref([]);
@@ -119,6 +120,6 @@ onMounted(() => {
/>
</div>
</template>
<ElEmpty image="/empty.png" v-else />
<ElEmpty :image="emptyImageUrl" v-else />
</div>
</template>

View File

@@ -12,6 +12,9 @@ import { useAuthStore } from '#/store';
defineOptions({ name: 'Login' });
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
const authStore = useAuthStore();
const assetBase = import.meta.env.BASE_URL || '/';
const captchaAssetBase = `${assetBase}tac`;
const captchaButtonUrl = `${assetBase}tac-btn.png`;
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
@@ -69,11 +72,11 @@ function onSubmit(values: any) {
const style = {
logoUrl: null, // 去除logo
// logoUrl: "/xx/xx/xxx.png" // 替换成自定义的logo
btnUrl: '/tac-btn.png',
btnUrl: captchaButtonUrl,
};
window
// @ts-ignore
.initTAC('/tac', config, style)
.initTAC(captchaAssetBase, config, style)
.then((tac: any) => {
tac.init(); // 调用init则显示验证码
})

View File

@@ -19,6 +19,7 @@ const props = withDefaults(defineProps<ExecResultProps>(), {
showMessage: true,
pollingData: {},
});
const emptyImageUrl = `${import.meta.env.BASE_URL || '/'}empty.png`;
const finalNode = computed(() => {
const nodes = props.nodeJson;
@@ -93,7 +94,7 @@ function getResult(res: any) {
<ShowJson :value="result" />
</div>
<div>
<ElEmpty image="/empty.png" v-if="!result" />
<ElEmpty :image="emptyImageUrl" v-if="!result" />
</div>
</div>
</template>

View File

@@ -16,6 +16,7 @@ const ids = reactive({
const conversationInfo = ref<any>();
const messageList = ref<any[]>([]);
const loading = ref(true);
const logoUrl = `${import.meta.env.BASE_URL || '/'}logo.svg`;
onMounted(() => {
if (route.params.id) {
@@ -80,7 +81,7 @@ function getMessageList() {
</div>
<!-- Right -->
<img src="/logo.svg" class="w-40 max-sm:w-28" />
<img :src="logoUrl" class="w-40 max-sm:w-28" />
</div>
</ElHeader>
<ElMain class="relative max-sm:mt-2 max-sm:!p-0" v-loading="loading">

View File

@@ -20,6 +20,7 @@ async function viteInjectAppLoadingPlugin(
const { version } = await readPackageJSON(process.cwd());
const envRaw = isBuild ? 'prod' : 'dev';
const cacheName = `'${env.VITE_APP_NAMESPACE}-${version}-${envRaw}-preferences-theme'`;
const appBase = JSON.stringify(ensureTrailingSlash(env.VITE_BASE || '/'));
// 获取缓存的主题
// 保证黑暗主题下刷新页面时loading也是黑暗主题
@@ -29,7 +30,7 @@ async function viteInjectAppLoadingPlugin(
document.documentElement.classList.toggle('dark', /dark/.test(theme));
setTimeout(() => {
if (/dark/.test(theme)) {
document.querySelector('#__app-loading__ img').src = '/logoDark.svg';
document.querySelector('#__app-loading__ img').src = ${appBase} + 'logoDark.svg';
}
})
</script>
@@ -68,4 +69,8 @@ async function getLoadingRawByHtmlTemplate(loadingTemplate: string) {
return await fsp.readFile(appLoadingPath, 'utf8');
}
function ensureTrailingSlash(path: string) {
return path.endsWith('/') ? path : `${path}/`;
}
export { viteInjectAppLoadingPlugin };

View File

@@ -1,5 +1,7 @@
import type { Preferences } from './types';
const assetBase = import.meta.env.BASE_URL || '/';
const defaultPreferences: Preferences = {
app: {
accessMode: 'frontend',
@@ -65,8 +67,9 @@ const defaultPreferences: Preferences = {
logo: {
enable: true,
fit: 'contain',
source: '/logo.svg',
sourceDark: '/logoDark.svg',
source: `${assetBase}logo.svg`,
sourceDark: `${assetBase}logoDark.svg`,
sourceMini: `${assetBase}logoMini.svg`,
},
navigation: {
accordion: true,

View File

@@ -148,6 +148,10 @@ interface LogoPreferences {
source: string;
/** 暗色主题logo地址 (可选,若不设置则使用 source) */
sourceDark?: string;
/** 侧边栏收起时 logo 地址 (可选,若不设置则使用 source) */
sourceMini?: string;
/** 暗色主题下侧边栏收起 logo 地址 (可选,若不设置则按 sourceMini/sourceDark/source 回退) */
sourceMiniDark?: string;
}
interface NavigationPreferences {

View File

@@ -30,6 +30,14 @@ interface Props {
* @zh_CN 暗色主题 Logo 图标 (可选,若不设置则使用 src)
*/
srcDark?: string;
/**
* @zh_CN 侧边栏收起时 Logo 图标 (可选,若不设置则使用 src)
*/
srcMini?: string;
/**
* @zh_CN 暗色主题下侧边栏收起时 Logo 图标 (可选,若不设置则按 srcMini/srcDark/src 回退)
*/
srcMiniDark?: string;
/**
* @zh_CN Logo 文本
*/
@@ -50,6 +58,8 @@ const props = withDefaults(defineProps<Props>(), {
logoSize: 120,
src: '',
srcDark: '',
srcMini: '',
srcMiniDark: '',
theme: 'light',
fit: 'cover',
});
@@ -59,7 +69,10 @@ const props = withDefaults(defineProps<Props>(), {
*/
const logoSrc = computed(() => {
if (props.collapsed) {
return '/logoMini.svg';
if (props.theme === 'dark' && props.srcMiniDark) {
return props.srcMiniDark;
}
return props.srcMini || props.src;
}
// 如果是暗色主题且提供了 srcDark则使用暗色主题的 logo
if (props.theme === 'dark' && props.srcDark) {

View File

@@ -260,6 +260,14 @@ const headerSlots = computed(() => {
:collapsed="logoCollapsed"
:src="preferences.logo.source"
:src-dark="preferences.logo.sourceDark"
:src-mini="preferences.logo.sourceMini ?? preferences.logo.source ?? ''"
:src-mini-dark="
preferences.logo.sourceMiniDark ??
preferences.logo.sourceMini ??
preferences.logo.sourceDark ??
preferences.logo.source ??
''
"
:text="preferences.app.name"
:theme="showHeaderNav ? headerTheme : theme"
@click="clickLogo"
@@ -353,6 +361,14 @@ const headerSlots = computed(() => {
:fit="preferences.logo.fit"
:src="preferences.logo.source"
:src-dark="preferences.logo.sourceDark"
:src-mini="preferences.logo.sourceMini ?? preferences.logo.source ?? ''"
:src-mini-dark="
preferences.logo.sourceMiniDark ??
preferences.logo.sourceMini ??
preferences.logo.sourceDark ??
preferences.logo.source ??
''
"
:text="preferences.app.name"
:theme="theme"
>