fix: 修复管理端静态资源基础路径
- 统一内置品牌资源与空状态图片的 BASE_URL 解析 - 应用启动时自动归一化历史偏好里的内置资源路径 - 多个空状态组件改为复用公共资源地址工具
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
} from 'element-plus';
|
||||
|
||||
import { hasPermission } from '#/api/common/hasPermission.ts';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
const props = defineProps({
|
||||
titleKey: {
|
||||
@@ -137,9 +138,7 @@ const filteredActions = computed(() => {
|
||||
</div>
|
||||
|
||||
<div v-if="data.length === 0" class="empty-state">
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
/>
|
||||
<ElEmpty :image="getEmptyStateImageUrl(preferences.theme.mode)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
ElMessageBox,
|
||||
} from 'element-plus';
|
||||
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
@@ -245,7 +247,7 @@ const handleDeleteClick = (event: any, item: any) => {
|
||||
<!-- 无数据提示 -->
|
||||
<div v-if="categoryData.length === 0 && !loading" class="no-data">
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
:image="getEmptyStateImageUrl(preferences.theme.mode)"
|
||||
:description="$t('common.noDataAvailable')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ElEmpty } from 'element-plus';
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
|
||||
import 'vue3-json-viewer/dist/vue3-json-viewer.css';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
defineProps({
|
||||
value: {
|
||||
@@ -26,10 +27,7 @@ watch(
|
||||
<template>
|
||||
<div class="res-container">
|
||||
<JsonViewer v-if="value" :value="value" copyable :theme="themeMode" />
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
v-else
|
||||
/>
|
||||
<ElEmpty :image="getEmptyStateImageUrl(preferences.theme.mode)" v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { preferences } from '@easyflow/preferences';
|
||||
import { ElEmpty, ElPagination } from 'element-plus';
|
||||
|
||||
import { api } from '#/api/request';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
interface PageDataProps {
|
||||
pageUrl: string;
|
||||
@@ -126,9 +127,7 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
<slot v-else name="empty">
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
/>
|
||||
<ElEmpty :image="getEmptyStateImageUrl(preferences.theme.mode)" />
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
ElIcon,
|
||||
} from 'element-plus';
|
||||
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
menus: T[];
|
||||
@@ -182,7 +184,7 @@ const isSvgString = (icon: any) => {
|
||||
</div>
|
||||
<ElEmpty
|
||||
v-if="menus.length <= 0"
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
:image="getEmptyStateImageUrl(preferences.theme.mode)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { initPreferences } from '@easyflow/preferences';
|
||||
import { initPreferences, preferences, updatePreferences } from '@easyflow/preferences';
|
||||
import { unmountGlobalLoading } from '@easyflow/utils';
|
||||
|
||||
import { overridesPreferences } from './preferences';
|
||||
import {
|
||||
normalizeBrandAssetPreferences,
|
||||
overridesPreferences,
|
||||
} from './preferences';
|
||||
|
||||
/**
|
||||
* 应用初始化完成之后再进行页面加载渲染
|
||||
@@ -19,6 +22,11 @@ async function initApplication() {
|
||||
overrides: overridesPreferences,
|
||||
});
|
||||
|
||||
const { changed, normalized } = normalizeBrandAssetPreferences(preferences);
|
||||
if (changed) {
|
||||
updatePreferences(normalized);
|
||||
}
|
||||
|
||||
// 启动应用并挂载
|
||||
// vue应用主要逻辑及视图
|
||||
const { bootstrap } = await import('./bootstrap');
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { defineOverridesPreferences } from '@easyflow/preferences';
|
||||
|
||||
const assetBase = import.meta.env.BASE_URL || '/';
|
||||
|
||||
/**
|
||||
* @description 项目配置文件
|
||||
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
|
||||
@@ -11,9 +13,75 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
name: import.meta.env.VITE_APP_TITLE,
|
||||
accessMode: 'mixed',
|
||||
},
|
||||
auth: {
|
||||
sloganImage: `${assetBase}slogan.svg`,
|
||||
},
|
||||
logo: {
|
||||
source: `${assetBase}logo.svg`,
|
||||
sourceDark: `${assetBase}logoDark.svg`,
|
||||
sourceMini: `${assetBase}logoMini.svg`,
|
||||
sourceMiniDark: `${assetBase}logoMiniDark.svg`,
|
||||
},
|
||||
transition: {
|
||||
enable: false,
|
||||
loading: false,
|
||||
progress: false,
|
||||
},
|
||||
});
|
||||
|
||||
const BUILTIN_BRAND_ASSETS = new Map([
|
||||
['/logo.svg', 'logo.svg'],
|
||||
['/logoDark.svg', 'logoDark.svg'],
|
||||
['/logoMini.svg', 'logoMini.svg'],
|
||||
['/logoMiniDark.svg', 'logoMiniDark.svg'],
|
||||
['/slogan.svg', 'slogan.svg'],
|
||||
]);
|
||||
|
||||
function normalizeBuiltinBrandAsset(path?: string) {
|
||||
if (!path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const fileName = BUILTIN_BRAND_ASSETS.get(path);
|
||||
if (!fileName) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return `${assetBase}${fileName}`;
|
||||
}
|
||||
|
||||
export function normalizeBrandAssetPreferences(preferences: {
|
||||
auth?: { sloganImage?: string };
|
||||
logo?: {
|
||||
source?: string;
|
||||
sourceDark?: string;
|
||||
sourceMini?: string;
|
||||
sourceMiniDark?: string;
|
||||
};
|
||||
}) {
|
||||
const normalized = {
|
||||
auth: {
|
||||
sloganImage: normalizeBuiltinBrandAsset(preferences.auth?.sloganImage),
|
||||
},
|
||||
logo: {
|
||||
source: normalizeBuiltinBrandAsset(preferences.logo?.source),
|
||||
sourceDark: normalizeBuiltinBrandAsset(preferences.logo?.sourceDark),
|
||||
sourceMini: normalizeBuiltinBrandAsset(preferences.logo?.sourceMini),
|
||||
sourceMiniDark: normalizeBuiltinBrandAsset(
|
||||
preferences.logo?.sourceMiniDark,
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const changed =
|
||||
normalized.auth.sloganImage !== preferences.auth?.sloganImage ||
|
||||
normalized.logo.source !== preferences.logo?.source ||
|
||||
normalized.logo.sourceDark !== preferences.logo?.sourceDark ||
|
||||
normalized.logo.sourceMini !== preferences.logo?.sourceMini ||
|
||||
normalized.logo.sourceMiniDark !== preferences.logo?.sourceMiniDark;
|
||||
|
||||
return {
|
||||
changed,
|
||||
normalized,
|
||||
};
|
||||
}
|
||||
|
||||
13
easyflow-ui-admin/app/src/utils/assets.ts
Normal file
13
easyflow-ui-admin/app/src/utils/assets.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const assetBase = import.meta.env.BASE_URL || '/';
|
||||
|
||||
export function getPublicAssetUrl(path: string) {
|
||||
if (!path) {
|
||||
return assetBase;
|
||||
}
|
||||
const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
|
||||
return `${assetBase}${normalizedPath}`;
|
||||
}
|
||||
|
||||
export function getEmptyStateImageUrl(themeMode?: string) {
|
||||
return getPublicAssetUrl(`empty${themeMode === 'dark' ? '-dark' : ''}.png`);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { getBotDetails, getSessionList } from '#/api';
|
||||
import BotAvatar from '#/components/botAvatar/botAvatar.vue';
|
||||
import Chat from '#/components/chat/chat.vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -132,7 +133,7 @@ const updateActive = (_sessionId?: number | string) => {
|
||||
@update:active="updateActive"
|
||||
/>
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
:image="getEmptyStateImageUrl(preferences.theme.mode)"
|
||||
v-show="sessionList.length === 0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ElEmpty, ElMessage, ElRow } from 'element-plus';
|
||||
|
||||
import ShowJson from '#/components/json/ShowJson.vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
import ExecResultItem from '#/views/ai/workflow/components/ExecResultItem.vue';
|
||||
|
||||
export interface ExecResultProps {
|
||||
@@ -87,7 +88,7 @@ function getResult(res: any) {
|
||||
</div>
|
||||
<div>
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
:image="getEmptyStateImageUrl(preferences.theme.mode)"
|
||||
v-if="!result"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user