fix: restore dashboard runtime home

This commit is contained in:
2026-06-11 09:11:46 +08:00
parent 87a01d70fb
commit e2b1901741
12 changed files with 153 additions and 394 deletions
+91 -9
View File
@@ -13990,11 +13990,49 @@
"id": "725fb629-8f27-4738-a335-42159dc2b9bb", "id": "725fb629-8f27-4738-a335-42159dc2b9bb",
"minH": 2, "minH": 2,
"minW": 6, "minW": 6,
"type": "approval-center", "type": "quick-links",
"props": { "props": {
"title": "审批中心", "title": "AI 协作",
"showMore": true, "columns": 3,
"routePrefix": "/app/workflow_center" "rows": 2,
"menus": [
{
"id": "ai-agent",
"title": "智能体",
"icon": "lucide:square-user",
"path": "/ai-platform/agent",
"bgColor": ""
},
{
"id": "ai-workflow",
"title": "流程编排",
"icon": "lucide:workflow",
"path": "/ai-platform/workflow",
"bgColor": ""
},
{
"id": "ai-knowledge",
"title": "知识库",
"icon": "lucide:library-big",
"path": "/ai-platform/knowledge-base",
"bgColor": ""
},
{
"id": "ai-model",
"title": "模型配置",
"icon": "lucide:settings",
"path": "/ai-platform/model",
"bgColor": ""
},
{
"id": "codex",
"title": "Codex",
"icon": "lucide:code",
"path": "/agent-chat/codex",
"bgColor": ""
},
null
]
}, },
"style": { "style": {
"backgroundColor": "", "backgroundColor": "",
@@ -14014,7 +14052,7 @@
"titleAlign": "left", "titleAlign": "left",
"titleFontWeight": "normal" "titleFontWeight": "normal"
}, },
"title": "审批中心" "title": "AI 协作"
}, },
{ {
"h": 3, "h": 3,
@@ -14027,9 +14065,53 @@
"minW": 6, "minW": 6,
"type": "my-apps", "type": "my-apps",
"props": { "props": {
"title": "我的应用", "title": "基础管理",
"maxCount": 8, "maxCount": 6,
"showMore": false "showMore": false,
"apps": [
{
"id": "system-user",
"code": "system-user",
"name": "用户管理",
"icon": "lucide:users",
"path": "/system/user"
},
{
"id": "system-role",
"code": "system-role",
"name": "角色权限",
"icon": "lucide:shield-check",
"path": "/system/role"
},
{
"id": "system-permission",
"code": "system-permission",
"name": "权限管理",
"icon": "lucide:key-round",
"path": "/system/new-permission"
},
{
"id": "system-menu",
"code": "system-menu",
"name": "菜单管理",
"icon": "lucide:menu",
"path": "/system/menu"
},
{
"id": "announcement",
"code": "announcement",
"name": "公告管理",
"icon": "lucide:megaphone",
"path": "/message/announcement"
},
{
"id": "message-list",
"code": "message-list",
"name": "消息中心",
"icon": "lucide:message-square-text",
"path": "/message/list"
}
]
}, },
"style": { "style": {
"backgroundColor": "", "backgroundColor": "",
@@ -14049,7 +14131,7 @@
"titleAlign": "left", "titleAlign": "left",
"titleFontWeight": "normal" "titleFontWeight": "normal"
}, },
"title": "我的应用" "title": "基础管理"
}, },
{ {
"h": 6, "h": 6,
@@ -38,6 +38,10 @@ const widgetComponents: Record<
'announcement-list': defineAsyncComponent( 'announcement-list': defineAsyncComponent(
() => import('./widgets/AnnouncementList.vue'), () => import('./widgets/AnnouncementList.vue'),
), ),
'approval-center': defineAsyncComponent(
() => import('./widgets/ApprovalCenter.vue'),
),
'my-apps': defineAsyncComponent(() => import('./widgets/MyApps.vue')),
'notice-list': defineAsyncComponent(() => import('./widgets/NoticeList.vue')), 'notice-list': defineAsyncComponent(() => import('./widgets/NoticeList.vue')),
'quick-links': defineAsyncComponent(() => import('./widgets/QuickLinks.vue')), 'quick-links': defineAsyncComponent(() => import('./widgets/QuickLinks.vue')),
'server-monitor': defineAsyncComponent( 'server-monitor': defineAsyncComponent(
@@ -15,18 +15,34 @@ const props = defineProps<{
widget: DashboardWidget; widget: DashboardWidget;
}>(); }>();
type AppLink = Pick<ApplicationListItem, 'code' | 'icon' | 'id' | 'name'> & {
path?: string;
};
// 应用列表 // 应用列表
const appList = ref<ApplicationListItem[]>([]); const appList = ref<AppLink[]>([]);
const loading = ref(false); const loading = ref(false);
const configuredApps = computed<AppLink[]>(() => props.widget.props.apps || []);
// 最大显示数量 // 最大显示数量
const maxCount = computed(() => props.widget.props.maxCount || 8); const maxCount = computed(() => props.widget.props.maxCount || 8);
// 显示的应用列表 // 显示的应用列表
const displayApps = computed(() => appList.value.slice(0, maxCount.value)); const displayApps = computed(() =>
(configuredApps.value.length > 0 ? configuredApps.value : appList.value).slice(
0,
maxCount.value,
),
);
// 加载已发布的应用列表 // 加载已发布的应用列表
const loadApps = async () => { const loadApps = async () => {
if (configuredApps.value.length > 0) {
appList.value = configuredApps.value;
return;
}
loading.value = true; loading.value = true;
try { try {
const res = await getApplicationListApi({ const res = await getApplicationListApi({
@@ -43,8 +59,13 @@ const loadApps = async () => {
}; };
// 点击应用 // 点击应用
const handleClick = (app: ApplicationListItem) => { const handleClick = (app: AppLink) => {
window.open(`${window.location.origin}/app/${app.code}`, '_blank'); const path = app.path || `/app/${app.code}`;
if (path.startsWith('http://') || path.startsWith('https://')) {
window.open(path, '_blank');
return;
}
window.open(`${window.location.origin}${path}`, '_blank');
}; };
// 点击更多 // 点击更多
@@ -61,6 +61,22 @@
"evening": "晚上好" "evening": "晚上好"
} }
}, },
"approvalCenter": {
"initiated": "我发起的",
"pending": "我的待办",
"handling": "我的在办",
"signing": "我的待签",
"handled": "我的已办",
"copy": "抄送我的",
"start": "发起流程",
"more": "更多",
"config": "审批中心配置",
"routePrefix": "路由前缀"
},
"myApps": {
"more": "更多",
"noData": "暂无应用"
},
"serverMonitor": { "serverMonitor": {
"core": "核", "core": "核",
"thread": "线程", "thread": "线程",
+1 -1
View File
@@ -16,7 +16,7 @@ export const overridesPreferences = defineOverridesPreferences({
enablePreferences: false, enablePreferences: false,
accessMode: 'mixed', accessMode: 'mixed',
authPageLayout: 'panel-center', authPageLayout: 'panel-center',
defaultHomePath: '/control-center', defaultHomePath: '/page-render/main_home',
layout: 'header-sidebar-nav', layout: 'header-sidebar-nav',
}, },
shortcutKeys: { shortcutKeys: {
+3 -3
View File
@@ -79,8 +79,8 @@ function normalizeBackendRoute<
if (route.name !== 'ControlCenter') return route; if (route.name !== 'ControlCenter') return route;
const normalized = { ...route }; const normalized = { ...route };
normalized.path = '/control-center'; normalized.path = '/page-render/main_home';
normalized.component = '_core/control-center/index'; normalized.component = 'online-dev/page-render/index';
normalized.query = undefined; normalized.query = undefined;
return normalized; return normalized;
} }
@@ -138,7 +138,6 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
'../views/_core/announcement/index.vue', '../views/_core/announcement/index.vue',
'../views/_core/announcement/list.vue', '../views/_core/announcement/list.vue',
'../views/_core/authentication/login.vue', '../views/_core/authentication/login.vue',
'../views/_core/control-center/index.vue',
'../views/_core/authentication/oauth-callback.vue', '../views/_core/authentication/oauth-callback.vue',
'../views/_core/fallback/**/*.vue', '../views/_core/fallback/**/*.vue',
'../views/_core/file-preview/index.vue', '../views/_core/file-preview/index.vue',
@@ -156,6 +155,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
'../views/ai-platform/workflow/editor/index.vue', '../views/ai-platform/workflow/editor/index.vue',
'../views/ai-platform/workflow/index.vue', '../views/ai-platform/workflow/index.vue',
'../views/ai-platform/workflow-runs/index.vue', '../views/ai-platform/workflow-runs/index.vue',
'../views/online-dev/page-render/index.vue',
]); ]);
const layoutMap: ComponentRecordType = { const layoutMap: ComponentRecordType = {
+1 -1
View File
@@ -191,7 +191,7 @@ function setupAccessGuard(router: Router) {
if (to.path === subAppRootPath) { if (to.path === subAppRootPath) {
const defaultHome = const defaultHome =
preferences.app.defaultHomePath || '/control-center'; preferences.app.defaultHomePath || '/page-render/main_home';
// 确保路径包含子应用前缀 // 确保路径包含子应用前缀
const subAppTargetPath = defaultHome.startsWith(subAppRootPath) const subAppTargetPath = defaultHome.startsWith(subAppRootPath)
? defaultHome ? defaultHome
@@ -1,20 +1,11 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{
meta: {
hideInMenu: true,
title: '控制中心',
},
name: 'ControlCenterStatic',
path: '/control-center',
component: () => import('#/views/_core/control-center/index.vue'),
},
{ {
meta: { hideInMenu: true }, meta: { hideInMenu: true },
name: 'LegacyMainHomeRedirect', name: 'ControlCenterLegacyRedirect',
path: '/page-render/main_home', path: '/control-center',
redirect: '/control-center', redirect: '/page-render/main_home',
}, },
{ {
meta: { hideInMenu: true }, meta: { hideInMenu: true },
@@ -1,222 +0,0 @@
<script lang="ts" setup>
import type { DashboardConfig, DashboardWidget } from '#/components/dashboard-design';
import { ElScrollbar } from 'element-plus';
import DashboardRenderer from '#/components/dashboard-design/DashboardRenderer.vue';
defineOptions({ name: 'ControlCenter' });
type QuickLink = null | {
bgColor: string;
icon: string;
id: string;
path: string;
title: string;
};
const AI_LINKS: QuickLink[] = [
{
bgColor: '',
icon: 'lucide:square-user',
id: 'ai-agent',
path: '/ai-platform/agent',
title: '智能体',
},
{
bgColor: '',
icon: 'lucide:workflow',
id: 'ai-workflow',
path: '/ai-platform/workflow',
title: '流程编排',
},
{
bgColor: '',
icon: 'lucide:library-big',
id: 'ai-knowledge',
path: '/ai-platform/knowledge-base',
title: '知识库',
},
{
bgColor: '',
icon: 'lucide:settings',
id: 'ai-model',
path: '/ai-platform/model',
title: '模型配置',
},
{
bgColor: '',
icon: 'lucide:code',
id: 'codex',
path: '/agent-chat/codex',
title: 'Codex',
},
null,
];
const ADMIN_LINKS: QuickLink[] = [
{
bgColor: '',
icon: 'lucide:users',
id: 'system-user',
path: '/system/user',
title: '用户管理',
},
{
bgColor: '',
icon: 'lucide:shield-check',
id: 'system-role',
path: '/system/role',
title: '角色权限',
},
{
bgColor: '',
icon: 'lucide:key-round',
id: 'system-permission',
path: '/system/new-permission',
title: '权限管理',
},
{
bgColor: '',
icon: 'lucide:menu',
id: 'system-menu',
path: '/system/menu',
title: '菜单管理',
},
{
bgColor: '',
icon: 'lucide:megaphone',
id: 'announcement',
path: '/message/announcement',
title: '公告管理',
},
{
bgColor: '',
icon: 'lucide:message-square-text',
id: 'message-list',
path: '/message/list',
title: '消息中心',
},
];
function widget(
id: string,
type: DashboardWidget['type'],
layout: Pick<DashboardWidget, 'h' | 'w' | 'x' | 'y'>,
props: Record<string, any>,
): DashboardWidget {
return {
id,
i: id,
type,
...layout,
props,
style: {
borderRadius: 8,
padding: 0,
shadowEnabled: true,
},
};
}
const dashboardConfig: DashboardConfig = {
id: 'control-center',
name: '控制中心',
columns: 12,
rowHeight: 90,
margin: [12, 12],
showOuterMargin: false,
widgets: [
widget(
'welcome',
'welcome-card',
{ x: 0, y: 0, w: 8, h: 1 },
{
title: '欢迎回来罗',
subtitle: '今天是个好日子',
showTime: true,
},
),
widget(
'weather',
'weather',
{ x: 8, y: 0, w: 4, h: 1 },
{
title: '今日天气',
latitude: 39.9042,
longitude: 116.4074,
cityName: '北京',
refreshInterval: 600_000,
},
),
widget(
'ai-links',
'quick-links',
{ x: 0, y: 1, w: 4, h: 2 },
{
title: 'AI 协作',
columns: 3,
rows: 2,
menus: AI_LINKS,
},
),
widget(
'admin-links',
'quick-links',
{ x: 4, y: 1, w: 4, h: 2 },
{
title: '基础管理',
columns: 3,
rows: 2,
menus: ADMIN_LINKS,
},
),
widget(
'announcements',
'announcement-list',
{ x: 8, y: 1, w: 4, h: 2 },
{
title: '最新公告',
limit: 5,
},
),
widget(
'server-monitor',
'server-monitor',
{ x: 0, y: 3, w: 8, h: 2 },
{
title: '服务器信息',
refreshInterval: 5000,
},
),
widget(
'messages',
'notice-list',
{ x: 8, y: 3, w: 4, h: 2 },
{
title: '消息通知',
limit: 5,
},
),
widget(
'quick-links',
'quick-links',
{ x: 0, y: 5, w: 4, h: 2 },
{
title: '快捷入口',
columns: 3,
rows: 2,
menus: AI_LINKS,
},
),
],
};
</script>
<template>
<ElScrollbar class="rounded-[8px] py-3">
<div class="h-[calc(100vh-120px)] rounded-[8px] px-3">
<DashboardRenderer :config="dashboardConfig" />
</div>
</ElScrollbar>
</template>
@@ -99,7 +99,6 @@ const componentKeys: string[] = Object.keys(
'../../../_core/announcement/list.vue', '../../../_core/announcement/list.vue',
'../../../_core/authentication/login.vue', '../../../_core/authentication/login.vue',
'../../../_core/authentication/oauth-callback.vue', '../../../_core/authentication/oauth-callback.vue',
'../../../_core/control-center/index.vue',
'../../../_core/fallback/**/*.vue', '../../../_core/fallback/**/*.vue',
'../../../_core/menu/index.vue', '../../../_core/menu/index.vue',
'../../../_core/permission/index.vue', '../../../_core/permission/index.vue',
@@ -114,13 +113,15 @@ const componentKeys: string[] = Object.keys(
'../../../ai-platform/workflow/editor/index.vue', '../../../ai-platform/workflow/editor/index.vue',
'../../../ai-platform/workflow/index.vue', '../../../ai-platform/workflow/index.vue',
'../../../ai-platform/workflow-runs/index.vue', '../../../ai-platform/workflow-runs/index.vue',
'../../../online-dev/page-render/index.vue',
]), ]),
) )
.filter((item) => !item.includes('/modules/')) .filter((item) => !item.includes('/modules/'))
.map((v) => { .map((v) => {
const path = v const path = v
.replace('../../../_core/', '/_core/') .replace('../../../_core/', '/_core/')
.replace('../../../ai-platform/', '/ai-platform/'); .replace('../../../ai-platform/', '/ai-platform/')
.replace('../../../online-dev/', '/online-dev/');
return path.endsWith('.vue') ? path.slice(0, -4) : path; return path.endsWith('.vue') ? path.slice(0, -4) : path;
}); });
@@ -53,7 +53,7 @@ const appDynamicTitle = ref(true);
const appWatermark = ref(false); const appWatermark = ref(false);
const appWatermarkContent = ref(''); const appWatermarkContent = ref('');
const appEnableCheckUpdates = ref(true); const appEnableCheckUpdates = ref(true);
const appDefaultHomePath = ref('/control-center'); const appDefaultHomePath = ref('/page-render/main_home');
const appEnablePreferences = ref(true); const appEnablePreferences = ref(true);
const footerEnable = ref(true); const footerEnable = ref(true);
@@ -106,7 +106,7 @@ function getDefaultConfig() {
defaultHomePath: defaultHomePath:
overrides.app?.defaultHomePath || overrides.app?.defaultHomePath ||
preferences.app.defaultHomePath || preferences.app.defaultHomePath ||
'/control-center', '/page-render/main_home',
enablePreferences: overrides.app?.enablePreferences ?? true, enablePreferences: overrides.app?.enablePreferences ?? true,
}, },
footer: { footer: {
@@ -233,7 +233,7 @@ async function loadConfig() {
// 获取带子应用前缀的首页路径 // 获取带子应用前缀的首页路径
function getDefaultHomePathWithPrefix(): string { function getDefaultHomePathWithPrefix(): string {
const path = appDefaultHomePath.value || '/control-center'; const path = appDefaultHomePath.value || '/page-render/main_home';
const appCode = appContextStore.appCode; const appCode = appContextStore.appCode;
// 如果是子应用模式且路径不包含子应用前缀,自动添加 // 如果是子应用模式且路径不包含子应用前缀,自动添加
if (appCode && !path.startsWith(`/app/${appCode}`)) { if (appCode && !path.startsWith(`/app/${appCode}`)) {
@@ -15,139 +15,6 @@ const pageConfig = ref<string>('');
const pageName = ref(''); const pageName = ref('');
const pageCode = ref(''); const pageCode = ref('');
type QuickLink = null | {
bgColor: string;
icon: string;
id: string;
path: string;
title: string;
};
const MAIN_HOME_AI_LINKS: QuickLink[] = [
{
bgColor: '',
icon: 'lucide:square-user',
id: 'ai-agent',
path: '/ai-platform/agent',
title: '智能体',
},
{
bgColor: '',
icon: 'lucide:workflow',
id: 'ai-workflow',
path: '/ai-platform/workflow',
title: '流程编排',
},
{
bgColor: '',
icon: 'lucide:library-big',
id: 'ai-knowledge',
path: '/ai-platform/knowledge-base',
title: '知识库',
},
{
bgColor: '',
icon: 'lucide:settings',
id: 'ai-model',
path: '/ai-platform/model',
title: '模型配置',
},
{
bgColor: '',
icon: 'lucide:code',
id: 'codex',
path: '/agent-chat/codex',
title: 'Codex',
},
null,
];
const MAIN_HOME_ADMIN_LINKS: QuickLink[] = [
{
bgColor: '',
icon: 'lucide:users',
id: 'system-user',
path: '/system/user',
title: '用户管理',
},
{
bgColor: '',
icon: 'lucide:shield-check',
id: 'system-role',
path: '/system/role',
title: '角色权限',
},
{
bgColor: '',
icon: 'lucide:key-round',
id: 'system-permission',
path: '/system/new-permission',
title: '权限管理',
},
{
bgColor: '',
icon: 'lucide:menu',
id: 'system-menu',
path: '/system/menu',
title: '菜单管理',
},
{
bgColor: '',
icon: 'lucide:megaphone',
id: 'announcement',
path: '/message/announcement',
title: '公告管理',
},
{
bgColor: '',
icon: 'lucide:message-square-text',
id: 'message-list',
path: '/message/list',
title: '消息中心',
},
];
function toQuickLinksWidget(
widget: Record<string, any>,
title: string,
menus: QuickLink[],
) {
return {
...widget,
title,
type: 'quick-links',
props: {
...widget.props,
columns: 3,
menus,
rows: 2,
title,
},
};
}
function normalizeMainHomeConfig(config: Record<string, any>) {
if (pageCode.value !== 'main_home' || !Array.isArray(config.widgets)) {
return config;
}
return {
...config,
widgets: config.widgets.map((widget: Record<string, any>) => {
if (widget.type === 'approval-center') {
return toQuickLinksWidget(widget, 'AI 协作', MAIN_HOME_AI_LINKS);
}
if (widget.type === 'my-apps') {
return toQuickLinksWidget(widget, '基础管理', MAIN_HOME_ADMIN_LINKS);
}
if (widget.type === 'quick-links') {
return toQuickLinksWidget(widget, '快捷入口', MAIN_HOME_AI_LINKS);
}
return widget;
}),
};
}
function getPageCode(): string { function getPageCode(): string {
if (route.query.pageCode) { if (route.query.pageCode) {
return route.query.pageCode as string; return route.query.pageCode as string;
@@ -179,11 +46,10 @@ async function loadPageData() {
try { try {
const page = await getPageByCodeApi(code); const page = await getPageByCodeApi(code);
pageName.value = page.name; pageName.value = page.name;
const config = pageConfig.value =
page.page_config && Object.keys(page.page_config).length > 0 page.page_config && Object.keys(page.page_config).length > 0
? normalizeMainHomeConfig(page.page_config) ? JSON.stringify(page.page_config)
: null; : '';
pageConfig.value = config ? JSON.stringify(config) : '';
} catch (error: any) { } catch (error: any) {
ElMessage.error(error?.message || '加载页面失败'); ElMessage.error(error?.message || '加载页面失败');
} finally { } finally {