fix: restore zq dashboard home quality

This commit is contained in:
2026-06-22 06:54:25 +08:00
parent e1ef4c1a70
commit fd36083ee7
2 changed files with 20 additions and 307 deletions
+1
View File
@@ -32,6 +32,7 @@ const modules = import.meta.glob([
'./langs/**/core.json', './langs/**/core.json',
'./langs/**/database-connection.json', './langs/**/database-connection.json',
'./langs/**/database-manager.json', './langs/**/database-manager.json',
'./langs/**/dashboard-design.json',
'./langs/**/dept.json', './langs/**/dept.json',
'./langs/**/dict.json', './langs/**/dict.json',
'./langs/**/file-manager.json', './langs/**/file-manager.json',
@@ -1,249 +1,28 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute } from 'vue-router';
import { IconifyIcon } from '@vben/icons';
import { ElEmpty, ElMessage, ElScrollbar } from 'element-plus'; import { ElEmpty, ElMessage, ElScrollbar } from 'element-plus';
import { getPageByCodeApi } from '#/api/core/page-manager'; import { getPageByCodeApi } from '#/api/core/page-manager';
import DashboardRenderer from '#/components/dashboard-design/DashboardRenderer.vue';
defineOptions({ name: 'PageRender' }); defineOptions({ name: 'PageRender' });
const route = useRoute(); const route = useRoute();
const router = useRouter();
const DashboardRenderer = defineAsyncComponent(
() => import('#/components/dashboard-design/DashboardRenderer.vue'),
);
const loading = ref(false); const loading = ref(false);
const pageConfig = ref(''); const pageConfig = ref('');
const pageName = ref('');
const aiHomeMenus = [ const pageCode = ref('');
{
color: 'rgba(59, 130, 246, 0.85)',
icon: 'lucide:bot',
id: 'ai-agent',
path: '/ai-platform/agent',
title: '智能体',
},
{
color: 'rgba(22, 163, 74, 0.85)',
icon: 'lucide:git-branch',
id: 'ai-workflow',
path: '/ai-platform/workflow',
title: '流程编排',
},
{
color: 'rgba(15, 118, 110, 0.85)',
icon: 'lucide:database',
id: 'knowledge-base',
path: '/ai-platform/knowledge-base',
title: '知识库',
},
{
color: 'rgba(124, 58, 237, 0.85)',
icon: 'lucide:history',
id: 'ai-workflow-runs',
path: '/ai-platform/workflow-runs',
title: '执行历史',
},
{
color: 'rgba(71, 85, 105, 0.85)',
icon: 'lucide:settings',
id: 'model-config',
path: '/ai-platform/model',
title: '模型配置',
},
{
color: 'rgba(249, 115, 22, 0.85)',
icon: 'lucide:message-square-code',
id: 'codex-chat',
path: '/agent-chat/codex',
title: 'Codex',
},
];
const adminHomeMenus = [
{
bgColor: 'linear-gradient(135deg, #60a5fa, #2563eb)',
icon: 'lucide:users',
id: 'system-user',
path: '/system/user',
title: '用户管理',
},
{
bgColor: 'linear-gradient(135deg, #a78bfa, #7c3aed)',
icon: 'lucide:shield-check',
id: 'system-role',
path: '/system/role',
title: '角色权限',
},
{
bgColor: 'linear-gradient(135deg, #fb923c, #f97316)',
icon: 'lucide:menu',
id: 'system-menu',
path: '/system/menu',
title: '菜单管理',
},
{
bgColor: 'linear-gradient(135deg, #2dd4bf, #0f766e)',
icon: 'lucide:key-round',
id: 'system-permission',
path: '/system/new-permission',
title: 'API 权限',
},
{
bgColor: 'linear-gradient(135deg, #4ade80, #16a34a)',
icon: 'lucide:megaphone',
id: 'announcement',
path: '/message/announcement',
title: '公告管理',
},
{
bgColor: 'linear-gradient(135deg, #94a3b8, #475569)',
icon: 'lucide:inbox',
id: 'message-list',
path: '/message/list',
title: '消息列表',
},
];
const opsHomeMenus = [
{
icon: 'lucide:send',
id: 'requirements-agent',
path: '/ai-platform/agent',
title: '需求分析',
},
{
icon: 'lucide:workflow',
id: 'collaboration-flow',
path: '/ai-platform/workflow',
title: '协作流程',
},
{
icon: 'lucide:activity',
id: 'observability',
path: '/ai-platform/workflow-runs',
title: '可观测性',
},
{
icon: 'lucide:sliders-horizontal',
id: 'model-config',
path: '/ai-platform/model',
title: '模型配置',
},
];
const homeSections = [
{
description: '智能体、流程、知识库、模型和运行历史',
id: 'ai',
menus: aiHomeMenus,
title: 'AI 协作',
},
{
description: '用户、角色、菜单、权限和公告消息',
id: 'admin',
menus: adminHomeMenus,
title: '基础管理',
},
{
description: '需求分析、协作流程、可观测性和模型配置',
id: 'ops',
menus: opsHomeMenus,
title: '协作运维',
},
];
function toQuickLinksWidget(widget: any, title: string, menus: any[]) {
return {
...widget,
props: {
...widget.props,
columns: widget.w >= 6 ? 3 : 4,
menus,
rows: 2,
title,
},
title,
type: 'quick-links',
};
}
function toApprovalCenterWidget(widget: any, title: string, menus: any[]) {
return {
...widget,
props: {
...widget.props,
menus,
showMore: false,
title,
},
title,
type: 'approval-center',
};
}
function toStaticAppsWidget(widget: any, title: string, menus: any[]) {
return {
...widget,
props: {
...widget.props,
maxCount: menus.length,
menus,
showMore: false,
title,
},
title,
type: 'my-apps',
};
}
function getLightMainHomeConfig(config: Record<string, any>) {
const next = JSON.parse(JSON.stringify(config));
if (!Array.isArray(next.widgets)) return next;
const hasApprovalCenter = next.widgets.some(
(widget: any) => widget.type === 'approval-center',
);
next.widgets = next.widgets.map((widget: any) => {
if (widget.type === 'welcome-card') {
return {
...widget,
props: {
...widget.props,
subtitle: '今天是个好日子',
title: widget.props?.title || '欢迎回来',
},
};
}
if (widget.type === 'approval-center') {
return toApprovalCenterWidget(widget, '审批中心', aiHomeMenus);
}
if (widget.type === 'my-apps') {
return toStaticAppsWidget(widget, '我的应用', adminHomeMenus);
}
if (widget.type === 'quick-links') {
if (!hasApprovalCenter && widget.props?.title === 'AI 协作') {
return toApprovalCenterWidget(widget, '审批中心', aiHomeMenus);
}
return toQuickLinksWidget(widget, '快捷入口', opsHomeMenus);
}
return widget;
});
return next;
}
function getPageCode(): string { function getPageCode(): string {
if (route.query.pageCode) return route.query.pageCode as string; if (route.query.pageCode) {
if (route.params.code) return route.params.code as string; return route.query.pageCode as string;
}
if (route.params.code) {
return route.params.code as string;
}
const pathParts = route.path.split('/').filter(Boolean); const pathParts = route.path.split('/').filter(Boolean);
const length = pathParts.length; const length = pathParts.length;
@@ -254,12 +33,6 @@ function getPageCode(): string {
return ''; return '';
} }
const isMainHome = computed(() => getPageCode() === 'main_home');
function goTo(path: string) {
router.push(path);
}
async function loadPageData() { async function loadPageData() {
const code = getPageCode(); const code = getPageCode();
if (!code) { if (!code) {
@@ -267,22 +40,16 @@ async function loadPageData() {
return; return;
} }
if (code === 'main_home') { pageCode.value = code;
pageConfig.value = '';
loading.value = false;
return;
}
loading.value = true; loading.value = true;
try { try {
const page = await getPageByCodeApi(code); const page = await getPageByCodeApi(code);
const config = pageName.value = page.name;
pageConfig.value =
page.page_config && Object.keys(page.page_config).length > 0 page.page_config && Object.keys(page.page_config).length > 0
? code === 'main_home' ? JSON.stringify(page.page_config)
? getLightMainHomeConfig(page.page_config) : '';
: page.page_config
: undefined;
pageConfig.value = config ? JSON.stringify(config) : '';
} catch (error: any) { } catch (error: any) {
ElMessage.error(error?.message || '加载页面失败'); ElMessage.error(error?.message || '加载页面失败');
} finally { } finally {
@@ -304,62 +71,7 @@ watch(
<template> <template>
<ElScrollbar class="rounded-[8px] py-3"> <ElScrollbar class="rounded-[8px] py-3">
<div <div v-loading="loading" class="h-[calc(100vh-120px)] rounded-[8px] px-3">
v-if="isMainHome"
class="min-h-[calc(100vh-120px)] rounded-[8px] px-3 pb-6"
>
<div class="mb-4 px-1">
<h1 class="text-foreground text-xl font-semibold">AI Agent Admin</h1>
<p class="text-muted-foreground mt-1 text-sm">
面向智能体协作的轻量后台
</p>
</div>
<div class="grid gap-3 xl:grid-cols-3">
<section
v-for="section in homeSections"
:key="section.id"
class="border-border bg-card rounded-[8px] border p-4"
>
<div class="mb-4">
<h2 class="text-foreground text-sm font-semibold">
{{ section.title }}
</h2>
<p class="text-muted-foreground mt-1 text-xs">
{{ section.description }}
</p>
</div>
<div class="grid grid-cols-2 gap-2">
<button
v-for="item in section.menus"
:key="item.id"
class="border-border hover:border-primary/70 hover:bg-accent group flex min-h-20 flex-col items-start justify-between rounded-[8px] border p-3 text-left transition-colors"
type="button"
@click="goTo(item.path)"
>
<span
class="flex size-8 items-center justify-center rounded-[6px] text-white shadow-sm"
:style="{ background: item.color || item.bgColor }"
>
<IconifyIcon :icon="item.icon" class="size-4" />
</span>
<span
class="text-foreground group-hover:text-primary mt-3 text-sm font-medium"
>
{{ item.title }}
</span>
</button>
</div>
</section>
</div>
</div>
<div
v-else
v-loading="loading"
class="h-[calc(100vh-120px)] rounded-[8px] px-3"
>
<DashboardRenderer v-if="pageConfig" :config="pageConfig" /> <DashboardRenderer v-if="pageConfig" :config="pageConfig" />
<ElEmpty v-else-if="!loading" description="暂无页面配置" /> <ElEmpty v-else-if="!loading" description="暂无页面配置" />
</div> </div>