From 58c930123a037a045d61917440f9562c42dd2e23 Mon Sep 17 00:00:00 2001 From: Lan Date: Wed, 3 Jun 2026 12:21:44 +0800 Subject: [PATCH] feat: show admin dashboard version --- src/composables/usePublicConfigBootstrap.ts | 3 +- src/i18n/locales/en-US.ts | 3 ++ src/i18n/locales/zh-CN.ts | 3 ++ src/services/config.ts | 48 +++++++++++++-------- src/stores/configStore.ts | 16 ++++++- src/types/config.ts | 12 ++++++ src/views/manage/DashboardView.vue | 25 +++++++++++ 7 files changed, 89 insertions(+), 21 deletions(-) diff --git a/src/composables/usePublicConfigBootstrap.ts b/src/composables/usePublicConfigBootstrap.ts index 43b5482..d32c536 100644 --- a/src/composables/usePublicConfigBootstrap.ts +++ b/src/composables/usePublicConfigBootstrap.ts @@ -13,7 +13,8 @@ export function usePublicConfigBootstrap() { return } - const notifyMessage = configStore.applyRemoteConfig(res.detail) + configStore.applyPublicMeta(res.detail.meta) + const notifyMessage = configStore.applyRemoteConfig(res.detail.config) if (notifyMessage) { alertStore.showAlert(notifyMessage, 'success') } diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index b0305a5..ebeb8f5 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -68,6 +68,9 @@ export default { refreshing: 'Refreshing', lastUpdated: 'Last updated: {time}', loadFailed: 'Failed to load dashboard data', + footerProduct: 'FileCodeBox Admin Console', + runtimeVersion: 'Runtime Version', + versionPending: 'Pending sync', fileHealth: 'File Health', fileHealthDesc: 'Status insights for available, risky, and pending file queues.', activeFileRatio: 'Active Ratio', diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 7bf827e..98347f7 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -68,6 +68,9 @@ export default { refreshing: '刷新中', lastUpdated: '最近更新:{time}', loadFailed: '仪表盘数据加载失败', + footerProduct: 'FileCodeBox 管理后台', + runtimeVersion: '运行版本', + versionPending: '待同步', fileHealth: '文件健康', fileHealthDesc: '基于状态洞察统计可取件、风险与待处理队列。', activeFileRatio: '有效占比', diff --git a/src/services/config.ts b/src/services/config.ts index 0f183bf..4a48b6a 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -1,38 +1,48 @@ import api from './client' -import type { ApiResponse, ConfigState } from '@/types' - -type PublicConfigEnvelope = { - config?: Partial - meta?: unknown -} +import type { ApiResponse, ConfigState, PublicConfigPayload } from '@/types' const isPublicConfigEnvelope = ( - detail: ConfigState | PublicConfigEnvelope | null | undefined -): detail is PublicConfigEnvelope => { + detail: ConfigState | PublicConfigPayload | null | undefined +): detail is PublicConfigPayload => { return !!detail && typeof detail === 'object' && 'config' in detail } +const normalizeUserConfigResponse = ( + response: ApiResponse +): ApiResponse => { + if (isPublicConfigEnvelope(response.detail)) { + return { + ...response, + detail: { + config: response.detail.config, + meta: response.detail.meta + } + } + } + + return { + ...response, + detail: { + config: response.detail ?? {} + } + } +} + export class ConfigService { static async getConfig(): Promise> { return api.get('/admin/config/get') } - static async getUserConfig(): Promise> { + static async getUserConfig(): Promise> { try { const response = (await api.get('/api/v1/config')) as ApiResponse< - ConfigState | PublicConfigEnvelope + ConfigState | PublicConfigPayload > - if (isPublicConfigEnvelope(response.detail) && response.detail.config) { - return { - ...response, - detail: response.detail.config as ConfigState - } - } - - return response as ApiResponse + return normalizeUserConfigResponse(response) } catch { - return api.post('/') + const response = (await api.post('/')) as ApiResponse + return normalizeUserConfigResponse(response) } } diff --git a/src/stores/configStore.ts b/src/stores/configStore.ts index 6cd4d68..6400f4b 100644 --- a/src/stores/configStore.ts +++ b/src/stores/configStore.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { computed, ref } from 'vue' -import type { ConfigState } from '@/types' +import type { ConfigState, PublicConfigMeta } from '@/types' import { DEFAULT_PUBLIC_CONFIG, readNotifyKey, @@ -16,8 +16,10 @@ export const useConfigStore = defineStore('config', () => { ...DEFAULT_PUBLIC_CONFIG, ...toPublicConfig(readStoredConfig>()) }) + const publicMeta = ref({}) const uploadSizeLimit = computed(() => config.value.uploadSize) + const appVersion = computed(() => publicMeta.value.version || '') const updateConfig = (nextConfig: Partial) => { config.value = { @@ -45,6 +47,15 @@ export const useConfigStore = defineStore('config', () => { return `${notifyTitle}: ${notifyContent}` } + const applyPublicMeta = (nextMeta: PublicConfigMeta | undefined) => { + if (!nextMeta) return + + publicMeta.value = { + ...publicMeta.value, + ...nextMeta + } + } + const reloadStoredConfig = () => { config.value = { ...DEFAULT_PUBLIC_CONFIG, @@ -53,8 +64,11 @@ export const useConfigStore = defineStore('config', () => { } return { + appVersion, config, + publicMeta, uploadSizeLimit, + applyPublicMeta, applyRemoteConfig, updateConfig, reloadStoredConfig diff --git a/src/types/config.ts b/src/types/config.ts index 1272c0d..cedc534 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -15,6 +15,18 @@ export interface ThemeChoice { version: string } +export interface PublicConfigMeta { + version?: string + api?: Record + features?: Record + limits?: Record +} + +export interface PublicConfigPayload { + config: Partial + meta?: PublicConfigMeta +} + export interface ConfigState { name: string description: string diff --git a/src/views/manage/DashboardView.vue b/src/views/manage/DashboardView.vue index 19ac285..676a494 100644 --- a/src/views/manage/DashboardView.vue +++ b/src/views/manage/DashboardView.vue @@ -221,12 +221,33 @@ + +
+ {{ t('admin.dashboard.footerProduct') }} + + {{ t('admin.dashboard.runtimeVersion') }} + + {{ versionText }} + + +