Add lightweight AI admin dashboard
This commit is contained in:
@@ -191,6 +191,9 @@ web/apps/web-ele/src/views/online-dev/*
|
|||||||
!web/apps/web-ele/src/views/online-dev/page-render/
|
!web/apps/web-ele/src/views/online-dev/page-render/
|
||||||
!web/apps/web-ele/src/views/online-dev/page-render/**
|
!web/apps/web-ele/src/views/online-dev/page-render/**
|
||||||
web/apps/web-ele/src/views/dashboard/
|
web/apps/web-ele/src/views/dashboard/
|
||||||
|
!web/apps/web-ele/src/views/dashboard/
|
||||||
|
web/apps/web-ele/src/views/dashboard/*
|
||||||
|
!web/apps/web-ele/src/views/dashboard/index.vue
|
||||||
web/apps/web-ele/src/views/demos/
|
web/apps/web-ele/src/views/demos/
|
||||||
web/apps/web-ele/src/views/zq-smart-table/
|
web/apps/web-ele/src/views/zq-smart-table/
|
||||||
web/apps/web-ele/src/api/online-dev/
|
web/apps/web-ele/src/api/online-dev/
|
||||||
|
|||||||
@@ -34,36 +34,14 @@ __all__ = [
|
|||||||
# 循环节点
|
# 循环节点
|
||||||
'LoopNode',
|
'LoopNode',
|
||||||
# 表单节点
|
# 表单节点
|
||||||
'FormBasicInfoNode',
|
|
||||||
'FormDatabaseDesignNode',
|
|
||||||
'FormDatabaseCreateNode',
|
|
||||||
'FormUIDesignNode',
|
|
||||||
'FormListDesignNode',
|
|
||||||
'FormCreateNode',
|
|
||||||
'FormPublishNode',
|
|
||||||
# 应用节点
|
# 应用节点
|
||||||
'AppCreateNode',
|
|
||||||
'AppDesignNode',
|
|
||||||
'AppSettingsNode',
|
|
||||||
'AppUpdateNode',
|
|
||||||
# 仪表盘节点
|
# 仪表盘节点
|
||||||
'DashboardBasicInfoNode',
|
|
||||||
'DashboardDesignNode',
|
|
||||||
'DashboardCreateNode',
|
|
||||||
'DashboardPublishNode',
|
|
||||||
# 系统总结节点
|
# 系统总结节点
|
||||||
'SystemSummaryNode',
|
|
||||||
# 子流程节点
|
# 子流程节点
|
||||||
'SubflowNode',
|
'SubflowNode',
|
||||||
# Text-to-SQL 节点
|
# Text-to-SQL 节点
|
||||||
'TextToSqlNode',
|
'TextToSqlNode',
|
||||||
# 表单数据节点
|
# 表单数据节点
|
||||||
'FormDataCreateNode',
|
|
||||||
'FormDataReadNode',
|
|
||||||
'FormDataUpdateNode',
|
|
||||||
'FormDataDeleteNode',
|
|
||||||
'FormDataListNode',
|
|
||||||
'FormSchemaToLLMNode',
|
|
||||||
# 知识库节点
|
# 知识库节点
|
||||||
'KnowledgeRetrievalNode',
|
'KnowledgeRetrievalNode',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -122,13 +122,11 @@ def _load_builtin_nodes():
|
|||||||
'code_node',
|
'code_node',
|
||||||
'http_node',
|
'http_node',
|
||||||
'variable_node',
|
'variable_node',
|
||||||
'database_node',
|
|
||||||
'dialog_nodes',
|
'dialog_nodes',
|
||||||
'intent_node',
|
'intent_node',
|
||||||
'loop_node',
|
'loop_node',
|
||||||
'subflow_node',
|
'subflow_node',
|
||||||
'text_to_sql_node',
|
'text_to_sql_node',
|
||||||
'snowflake_cortex_node',
|
|
||||||
'knowledge_retrieval_node',
|
'knowledge_retrieval_node',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from core.menu.schema import MenuCreate, MenuUpdate
|
|||||||
menu_cache = CacheManager(prefix="menu:")
|
menu_cache = CacheManager(prefix="menu:")
|
||||||
|
|
||||||
# 缓存key
|
# 缓存key
|
||||||
AI_AGENT_ADMIN_MENU_CACHE_VERSION = "v6"
|
AI_AGENT_ADMIN_MENU_CACHE_VERSION = "v7"
|
||||||
MENU_TREE_CACHE_KEY = f"tree:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}"
|
MENU_TREE_CACHE_KEY = f"tree:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}"
|
||||||
USER_ROUTE_CACHE_PREFIX = f"user_route:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}:"
|
USER_ROUTE_CACHE_PREFIX = f"user_route:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}:"
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ AI_AGENT_ADMIN_MENU_NAMES = {
|
|||||||
"ControlCenter",
|
"ControlCenter",
|
||||||
"AIPlatform",
|
"AIPlatform",
|
||||||
"AIAgent",
|
"AIAgent",
|
||||||
|
"AIModelConfig",
|
||||||
"AIWorkflow",
|
"AIWorkflow",
|
||||||
"AIWorkflowRuns",
|
"AIWorkflowRuns",
|
||||||
"KnowledgeBase",
|
"KnowledgeBase",
|
||||||
@@ -51,10 +52,10 @@ def _filter_ai_agent_admin_menus(menus: List[Menu]) -> List[Menu]:
|
|||||||
|
|
||||||
|
|
||||||
def _normalize_ai_agent_admin_menu(menu: Menu) -> None:
|
def _normalize_ai_agent_admin_menu(menu: Menu) -> None:
|
||||||
"""Route the retained control center to the original rendered dashboard."""
|
"""Route the retained control center to the lightweight native dashboard."""
|
||||||
if menu.name == "ControlCenter":
|
if menu.name == "ControlCenter":
|
||||||
menu.path = "/page-render/main_home"
|
menu.path = "/dashboard"
|
||||||
menu.component = "online-dev/page-render/index"
|
menu.component = "dashboard/index"
|
||||||
menu.query = None
|
menu.query = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ export type DesignPreviewType =
|
|||||||
| 'knowledge_retrieval'
|
| 'knowledge_retrieval'
|
||||||
| 'list_config'
|
| 'list_config'
|
||||||
| 'llm_config'
|
| 'llm_config'
|
||||||
| 'system_summary'
|
|
||||||
| 'workflow_result';
|
| 'workflow_result';
|
||||||
|
|
||||||
// 设计预览配置
|
// 设计预览配置
|
||||||
|
|||||||
@@ -57,19 +57,11 @@ export interface AgentInfo {
|
|||||||
/** 设计预览类型 */
|
/** 设计预览类型 */
|
||||||
export type DesignPreviewType =
|
export type DesignPreviewType =
|
||||||
| 'agent_capability'
|
| 'agent_capability'
|
||||||
| 'app_design'
|
|
||||||
| 'app_settings'
|
|
||||||
| 'dashboard_basic_info'
|
|
||||||
| 'dashboard_design'
|
|
||||||
| 'dashboard_publish'
|
|
||||||
| 'database_design'
|
| 'database_design'
|
||||||
| 'form_basic_info'
|
|
||||||
| 'form_ui_design'
|
|
||||||
| 'knowledge_retrieval'
|
| 'knowledge_retrieval'
|
||||||
| 'list_config'
|
| 'list_config'
|
||||||
| 'llm_config'
|
| 'llm_config'
|
||||||
| 'workflow_result'
|
| 'workflow_result';
|
||||||
| 'system_summary';
|
|
||||||
|
|
||||||
/** 设计预览数据 */
|
/** 设计预览数据 */
|
||||||
export interface DesignPreviewData {
|
export interface DesignPreviewData {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ const baseModules = import.meta.glob([
|
|||||||
const businessModules = import.meta.glob([
|
const businessModules = import.meta.glob([
|
||||||
'./langs/zh-CN/ai-platform.json',
|
'./langs/zh-CN/ai-platform.json',
|
||||||
'./langs/zh-CN/announcement.json',
|
'./langs/zh-CN/announcement.json',
|
||||||
'./langs/zh-CN/dashboard-design.json',
|
|
||||||
'./langs/zh-CN/menu.json',
|
'./langs/zh-CN/menu.json',
|
||||||
'./langs/zh-CN/message.json',
|
'./langs/zh-CN/message.json',
|
||||||
'./langs/zh-CN/permission.json',
|
'./langs/zh-CN/permission.json',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const overridesPreferences = defineOverridesPreferences({
|
|||||||
enablePreferences: false,
|
enablePreferences: false,
|
||||||
accessMode: 'mixed',
|
accessMode: 'mixed',
|
||||||
authPageLayout: 'panel-center',
|
authPageLayout: 'panel-center',
|
||||||
defaultHomePath: '/page-render/main_home',
|
defaultHomePath: '/dashboard',
|
||||||
layout: 'header-sidebar-nav',
|
layout: 'header-sidebar-nav',
|
||||||
},
|
},
|
||||||
shortcutKeys: {
|
shortcutKeys: {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
|||||||
const lightMenuNames = new Set([
|
const lightMenuNames = new Set([
|
||||||
'AIAgent',
|
'AIAgent',
|
||||||
'AIKnowledgeDetail',
|
'AIKnowledgeDetail',
|
||||||
|
'AIModelConfig',
|
||||||
'AIPlatform',
|
'AIPlatform',
|
||||||
'AIWorkflow',
|
'AIWorkflow',
|
||||||
'AIWorkflowRuns',
|
'AIWorkflowRuns',
|
||||||
@@ -82,8 +83,8 @@ function normalizeBackendRoute<
|
|||||||
if (route.name !== 'ControlCenter') return route;
|
if (route.name !== 'ControlCenter') return route;
|
||||||
|
|
||||||
const normalized = { ...route };
|
const normalized = { ...route };
|
||||||
normalized.path = '/page-render/main_home';
|
normalized.path = '/dashboard';
|
||||||
normalized.component = 'online-dev/page-render/index';
|
normalized.component = 'dashboard/index';
|
||||||
normalized.query = undefined;
|
normalized.query = undefined;
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
@@ -158,7 +159,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',
|
'../views/dashboard/index.vue',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const layoutMap: ComponentRecordType = {
|
const layoutMap: ComponentRecordType = {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ function setupAccessGuard(router: Router) {
|
|||||||
|
|
||||||
if (to.path === subAppRootPath) {
|
if (to.path === subAppRootPath) {
|
||||||
const defaultHome =
|
const defaultHome =
|
||||||
preferences.app.defaultHomePath || '/page-render/main_home';
|
preferences.app.defaultHomePath || '/dashboard';
|
||||||
// 确保路径包含子应用前缀
|
// 确保路径包含子应用前缀
|
||||||
const subAppTargetPath = defaultHome.startsWith(subAppRootPath)
|
const subAppTargetPath = defaultHome.startsWith(subAppRootPath)
|
||||||
? defaultHome
|
? defaultHome
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
import type { RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
meta: { hideInMenu: true },
|
||||||
|
name: 'LegacyMainHomeRedirect',
|
||||||
|
path: '/page-render/main_home',
|
||||||
|
redirect: '/dashboard',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
meta: { hideInMenu: true },
|
meta: { hideInMenu: true },
|
||||||
name: 'AIKnowledgeLegacyRedirect',
|
name: 'AIKnowledgeLegacyRedirect',
|
||||||
path: '/ai-platform/knowledge',
|
path: '/ai-platform/knowledge',
|
||||||
redirect: '/ai-platform/knowledge-base',
|
redirect: '/ai-platform/knowledge-base',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
hideInMenu: true,
|
||||||
|
title: '模型配置',
|
||||||
|
},
|
||||||
|
name: 'AIModelConfig',
|
||||||
|
path: '/ai-platform/model',
|
||||||
|
component: () => import('#/views/ai-platform/model/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
meta: { hideInMenu: true },
|
meta: { hideInMenu: true },
|
||||||
name: 'AIKnowledgeLegacyDetailRedirect',
|
name: 'AIKnowledgeLegacyDetailRedirect',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
const LEGACY_HOME_PATTERNS = [
|
const LEGACY_HOME_PATTERNS = [
|
||||||
|
/^\/page-render\/main_home$/,
|
||||||
/^\/app\/[^/]+\/form-render\//,
|
/^\/app\/[^/]+\/form-render\//,
|
||||||
/^\/form-render\//,
|
/^\/form-render\//,
|
||||||
/^\/online-dev(?:\/|$)/,
|
/^\/online-dev(?:\/|$)/,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ 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',
|
'../../../dashboard/index.vue',
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.filter((item) => !item.includes('/modules/'))
|
.filter((item) => !item.includes('/modules/'))
|
||||||
@@ -120,7 +120,7 @@ const componentKeys: string[] = Object.keys(
|
|||||||
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/');
|
.replace('../../../dashboard/', '/dashboard/');
|
||||||
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('/analytics');
|
const appDefaultHomePath = ref('/dashboard');
|
||||||
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 ||
|
||||||
'/page-render/main_home',
|
'/dashboard',
|
||||||
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 || '/analytics';
|
const path = appDefaultHomePath.value || '/dashboard';
|
||||||
const appCode = appContextStore.appCode;
|
const appCode = appContextStore.appCode;
|
||||||
// 如果是子应用模式且路径不包含子应用前缀,自动添加
|
// 如果是子应用模式且路径不包含子应用前缀,自动添加
|
||||||
if (appCode && !path.startsWith(`/app/${appCode}`)) {
|
if (appCode && !path.startsWith(`/app/${appCode}`)) {
|
||||||
|
|||||||
+26
-549
@@ -2,20 +2,15 @@
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppWindow,
|
|
||||||
BookOpen,
|
BookOpen,
|
||||||
Bot,
|
Bot,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
CircleHelp,
|
CircleHelp,
|
||||||
ClipboardCheck,
|
|
||||||
Code,
|
Code,
|
||||||
Combine,
|
Combine,
|
||||||
Database,
|
|
||||||
FileText,
|
|
||||||
Globe,
|
Globe,
|
||||||
HelpCircle,
|
HelpCircle,
|
||||||
LayoutDashboard,
|
|
||||||
LayoutTemplate,
|
LayoutTemplate,
|
||||||
ListChecks,
|
ListChecks,
|
||||||
MessageSquareText,
|
MessageSquareText,
|
||||||
@@ -38,6 +33,26 @@ const { nodes, edges } = useVueFlow();
|
|||||||
const searchQuery = ref('');
|
const searchQuery = ref('');
|
||||||
const collapsedNodes = ref<Set<string>>(new Set());
|
const collapsedNodes = ref<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const lightWorkflowNodeTypes = new Set([
|
||||||
|
'start',
|
||||||
|
'llm',
|
||||||
|
'http',
|
||||||
|
'code',
|
||||||
|
'condition',
|
||||||
|
'template',
|
||||||
|
'parallel',
|
||||||
|
'merge',
|
||||||
|
'question',
|
||||||
|
'choice',
|
||||||
|
'message',
|
||||||
|
'confirm',
|
||||||
|
'intent',
|
||||||
|
'loop',
|
||||||
|
'subflow',
|
||||||
|
'knowledge_retrieval',
|
||||||
|
'end',
|
||||||
|
]);
|
||||||
|
|
||||||
// 定义不同节点的图标
|
// 定义不同节点的图标
|
||||||
const icons: any = {
|
const icons: any = {
|
||||||
start: Play,
|
start: Play,
|
||||||
@@ -57,21 +72,9 @@ const icons: any = {
|
|||||||
// 合并节点
|
// 合并节点
|
||||||
merge: Combine,
|
merge: Combine,
|
||||||
// 表单节点
|
// 表单节点
|
||||||
form_basic_info: FileText,
|
|
||||||
form_database_design: Database,
|
|
||||||
form_database_create: Database,
|
|
||||||
// 应用节点
|
// 应用节点
|
||||||
app_create: AppWindow,
|
|
||||||
app_design: LayoutDashboard,
|
|
||||||
app_settings: LayoutDashboard,
|
|
||||||
app_update: LayoutDashboard,
|
|
||||||
// 仪表盘节点
|
// 仪表盘节点
|
||||||
dashboard_basic_info: LayoutDashboard,
|
|
||||||
dashboard_design: LayoutDashboard,
|
|
||||||
dashboard_create: LayoutDashboard,
|
|
||||||
dashboard_publish: LayoutDashboard,
|
|
||||||
// 系统总结节点
|
// 系统总结节点
|
||||||
system_summary: ClipboardCheck,
|
|
||||||
// 子流程节点
|
// 子流程节点
|
||||||
subflow: Play,
|
subflow: Play,
|
||||||
// 知识库检索节点
|
// 知识库检索节点
|
||||||
@@ -125,98 +128,17 @@ const availableNodes = computed(() => {
|
|||||||
|
|
||||||
// 只保留上游节点
|
// 只保留上游节点
|
||||||
return nodes.value
|
return nodes.value
|
||||||
.filter((n) => upstreamIds.has(n.id) && n.type !== 'end')
|
.filter(
|
||||||
|
(n) =>
|
||||||
|
upstreamIds.has(n.id) &&
|
||||||
|
lightWorkflowNodeTypes.has(n.type) &&
|
||||||
|
n.type !== 'end',
|
||||||
|
)
|
||||||
.map((node) => {
|
.map((node) => {
|
||||||
let variables: any[] = [];
|
let variables: any[] = [];
|
||||||
|
|
||||||
// 根据节点类型获取输出变量 schema
|
// 根据节点类型获取输出变量 schema
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 'app_create': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'app_id',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.appId'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'app_code',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.appCode'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'app_name',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.appName'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'success',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.createSuccess',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'app_design': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'design_content',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.designContent',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'design_title',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.designTitle',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'confirmed',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.isConfirmed',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'app_settings': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'settings',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.appSettings',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'confirmed',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.isConfirmed',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'app_update': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'update_success',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.updateSuccess',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'config_id',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.configId',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'update_message',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.updateMessage',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'choice': {
|
case 'choice': {
|
||||||
const choiceVar = node.data.variable_name || 'user_choice';
|
const choiceVar = node.data.variable_name || 'user_choice';
|
||||||
variables = [
|
variables = [
|
||||||
@@ -272,385 +194,6 @@ const availableNodes = computed(() => {
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'dashboard_basic_info': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'dashboard_basic_info',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dashboardBasicInfo',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'dashboard_name',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dashboardName',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'dashboard_code',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dashboardCode',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'dashboard_create': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'dashboard_id',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dashboardId',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'dashboard_code',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dashboardCode',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'page_meta',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.pageMeta',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'dashboard_design': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'page_config',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.pageConfig',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'design_title',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.designTitle',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'confirmed',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.isConfirmed',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'dashboard_publish': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'menu_id',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.menuId'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'route_path',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.routePath',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'publish_result',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.publishResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'db_delete': {
|
|
||||||
const deleteOutputVar = node.data.output_variable || 'delete_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: deleteOutputVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.deleteResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${deleteOutputVar}_count`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.affectedRows',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'db_insert': {
|
|
||||||
const insertOutputVar = node.data.output_variable || 'insert_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: insertOutputVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.insertResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${insertOutputVar}_id`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.insertId',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'db_query': {
|
|
||||||
const queryOutputVar = node.data.output_variable || 'query_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: queryOutputVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.queryResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${queryOutputVar}_count`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.resultCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'db_sql': {
|
|
||||||
const sqlOutputVar = node.data.output_variable || 'sql_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: sqlOutputVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.sqlResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${sqlOutputVar}_count`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.resultCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'db_update': {
|
|
||||||
const updateOutputVar = node.data.output_variable || 'update_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: updateOutputVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.updateResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${updateOutputVar}_count`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.affectedRows',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_basic_info': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'form_basic_info',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formBasicInfoObj',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_name',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formName',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_code',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formCode',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_type',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formType',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_description',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formDescription',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_sort',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formSort',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_create': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'form_id',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.formId'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_code',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formCode',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_meta',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formMeta',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_database_create': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'creation_result',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.creationResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'all_tables_ready',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.allTablesReady',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'created_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.createdCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'error_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.errorCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'schema_name',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.schemaName',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'table_name',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.tableName',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_database_design': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'database_design',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.dbDesignObj',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'table_name',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.tableName',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'field_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.fieldCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_list_design': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'list_config',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.listConfig',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'query_field_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.queryFieldCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'column_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.columnCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_publish': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'menu_id',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.menuId'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'route_path',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.routePath',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'publish_result',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.publishResult',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'form_ui_design': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'form_ui_design',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formUiDesign',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'form_code',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.formCode',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'field_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.fieldCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'group_count',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.groupCount',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'http': {
|
case 'http': {
|
||||||
variables = [
|
variables = [
|
||||||
{
|
{
|
||||||
@@ -828,45 +371,6 @@ const availableNodes = computed(() => {
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'snowflake_cortex_analyst': {
|
|
||||||
const analystResultVar =
|
|
||||||
node.data.output_variable || 'analyst_result';
|
|
||||||
const analystSqlVar = node.data.sql_variable || 'analyst_sql';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: analystResultVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.queryResultData',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: analystSqlVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.generatedSql',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${analystResultVar}_raw`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.rawResponse',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Snowflake Cortex 节点
|
|
||||||
case 'snowflake_cortex_llm': {
|
|
||||||
const cortexLlmVar = node.data.output_variable || 'cortex_response';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: cortexLlmVar,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.cortexLlmResponse',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'start': {
|
case 'start': {
|
||||||
// 默认的系统变量
|
// 默认的系统变量
|
||||||
variables = [
|
variables = [
|
||||||
@@ -957,33 +461,6 @@ const availableNodes = computed(() => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'system_summary': {
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: 'summary',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.summaryData',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'total_forms',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.totalForms',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'has_app',
|
|
||||||
label: $t('ai-platform.workflow.editor.variableSelector.hasApp'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'has_dashboard',
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.hasDashboard',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'template': {
|
case 'template': {
|
||||||
const templateOutputVar =
|
const templateOutputVar =
|
||||||
node.data.output_variable || 'template_result';
|
node.data.output_variable || 'template_result';
|
||||||
|
|||||||
@@ -0,0 +1,802 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
AgentListItem,
|
||||||
|
KnowledgeBaseListItem,
|
||||||
|
ModelListItem,
|
||||||
|
WorkflowListItem,
|
||||||
|
WorkflowRunListItem,
|
||||||
|
} from '#/api/ai-platform/ai-platform';
|
||||||
|
import type { UserAnnouncement } from '#/api/core/announcement';
|
||||||
|
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
import {
|
||||||
|
Bot,
|
||||||
|
BrainCircuit,
|
||||||
|
CheckCircle2,
|
||||||
|
Cpu,
|
||||||
|
Database,
|
||||||
|
GitMerge,
|
||||||
|
HardDrive,
|
||||||
|
LayoutDashboard,
|
||||||
|
Mail,
|
||||||
|
Megaphone,
|
||||||
|
MessageSquare,
|
||||||
|
Network,
|
||||||
|
Plus,
|
||||||
|
RefreshCw,
|
||||||
|
Server,
|
||||||
|
ShieldCheck,
|
||||||
|
Sparkles,
|
||||||
|
Settings,
|
||||||
|
Workflow,
|
||||||
|
} from '@vben/icons';
|
||||||
|
import { useUserStore } from '@vben/stores';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ElButton,
|
||||||
|
ElCard,
|
||||||
|
ElEmpty,
|
||||||
|
ElProgress,
|
||||||
|
ElSkeleton,
|
||||||
|
ElTag,
|
||||||
|
} from 'element-plus';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAgentListApi,
|
||||||
|
getAllWorkflowRunsApi,
|
||||||
|
getKnowledgeBaseListApi,
|
||||||
|
getModelListApi,
|
||||||
|
getWorkflowListApi,
|
||||||
|
} from '#/api/ai-platform/ai-platform';
|
||||||
|
import { getUserAnnouncementListApi } from '#/api/core/announcement';
|
||||||
|
import { getUnreadCountApi } from '#/api/core/message';
|
||||||
|
import { getServerHealthRealtimeApi } from '#/api/core/server-health';
|
||||||
|
|
||||||
|
defineOptions({ name: 'ControlCenterDashboard' });
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
const refreshing = ref(false);
|
||||||
|
const now = ref(new Date());
|
||||||
|
|
||||||
|
const agents = ref<AgentListItem[]>([]);
|
||||||
|
const workflows = ref<WorkflowListItem[]>([]);
|
||||||
|
const knowledgeBases = ref<KnowledgeBaseListItem[]>([]);
|
||||||
|
const models = ref<ModelListItem[]>([]);
|
||||||
|
const waitingRuns = ref<WorkflowRunListItem[]>([]);
|
||||||
|
const announcements = ref<UserAnnouncement[]>([]);
|
||||||
|
const unreadMessages = ref(0);
|
||||||
|
const realtime = ref<null | {
|
||||||
|
cpu_percent: number;
|
||||||
|
disk_io?: { read_speed?: number; write_speed?: number };
|
||||||
|
memory_percent: number;
|
||||||
|
network_io?: { download_speed?: number; upload_speed?: number };
|
||||||
|
}>(null);
|
||||||
|
|
||||||
|
let clockTimer: ReturnType<typeof setInterval> | undefined;
|
||||||
|
|
||||||
|
const userName = computed(
|
||||||
|
() =>
|
||||||
|
userStore.userInfo?.realName ||
|
||||||
|
userStore.userInfo?.username ||
|
||||||
|
'管理员',
|
||||||
|
);
|
||||||
|
|
||||||
|
const greeting = computed(() => {
|
||||||
|
const hour = now.value.getHours();
|
||||||
|
if (hour < 6) return '夜深了';
|
||||||
|
if (hour < 11) return '早上好';
|
||||||
|
if (hour < 14) return '中午好';
|
||||||
|
if (hour < 18) return '下午好';
|
||||||
|
return '晚上好';
|
||||||
|
});
|
||||||
|
|
||||||
|
const todayText = computed(() =>
|
||||||
|
new Intl.DateTimeFormat('zh-CN', {
|
||||||
|
dateStyle: 'full',
|
||||||
|
}).format(now.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
const timeText = computed(() =>
|
||||||
|
new Intl.DateTimeFormat('zh-CN', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
}).format(now.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
const aiCards = computed(() => [
|
||||||
|
{
|
||||||
|
action: '创建智能体',
|
||||||
|
count: agents.value.length,
|
||||||
|
desc: '角色、工具、知识与协作策略',
|
||||||
|
icon: Bot,
|
||||||
|
path: '/ai-platform/agent',
|
||||||
|
title: '智能体',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: '设计流程',
|
||||||
|
count: workflows.value.length,
|
||||||
|
desc: 'LLM 节点、条件分支和人机确认',
|
||||||
|
icon: Workflow,
|
||||||
|
path: '/ai-platform/workflow',
|
||||||
|
title: '流程编排',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: '维护知识',
|
||||||
|
count: knowledgeBases.value.length,
|
||||||
|
desc: '文档索引、切片检索和问答标注',
|
||||||
|
icon: Database,
|
||||||
|
path: '/ai-platform/knowledge-base',
|
||||||
|
title: '知识库',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: '开始协作',
|
||||||
|
count: waitingRuns.value.length,
|
||||||
|
desc: 'Codex 与多角色智能体协同处理',
|
||||||
|
icon: BrainCircuit,
|
||||||
|
path: '/agent-chat/codex',
|
||||||
|
title: 'Codex 协作',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: '配置 LLM',
|
||||||
|
count: models.value.length,
|
||||||
|
desc: 'Provider、聊天模型、向量模型与 Rerank',
|
||||||
|
icon: Settings,
|
||||||
|
path: '/ai-platform/model',
|
||||||
|
title: '模型配置',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const opsCards = computed(() => [
|
||||||
|
{
|
||||||
|
label: '未读消息',
|
||||||
|
path: '/message/list',
|
||||||
|
value: unreadMessages.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '待确认流程',
|
||||||
|
path: '/ai-confirmation-center',
|
||||||
|
value: waitingRuns.value.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已发布智能体',
|
||||||
|
path: '/ai-platform/agent',
|
||||||
|
value: agents.value.filter((item) => item.status === 'published').length,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function goto(path: string) {
|
||||||
|
router.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBytes(value?: number) {
|
||||||
|
if (!value) return '0 B/s';
|
||||||
|
const units = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
|
||||||
|
let size = value;
|
||||||
|
let index = 0;
|
||||||
|
while (size >= 1024 && index < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
return `${size.toFixed(size >= 10 || index === 0 ? 0 : 1)} ${units[index]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshData() {
|
||||||
|
refreshing.value = true;
|
||||||
|
try {
|
||||||
|
const [
|
||||||
|
agentResult,
|
||||||
|
workflowResult,
|
||||||
|
knowledgeResult,
|
||||||
|
modelResult,
|
||||||
|
waitingResult,
|
||||||
|
announcementResult,
|
||||||
|
unreadResult,
|
||||||
|
realtimeResult,
|
||||||
|
] = await Promise.allSettled([
|
||||||
|
getAgentListApi({ page: 1, pageSize: 12 }),
|
||||||
|
getWorkflowListApi({ page: 1, pageSize: 12 }),
|
||||||
|
getKnowledgeBaseListApi({ page: 1, pageSize: 12 }),
|
||||||
|
getModelListApi({ page: 1, pageSize: 12 }),
|
||||||
|
getAllWorkflowRunsApi({ page: 1, pageSize: 8, status: 'waiting' }),
|
||||||
|
getUserAnnouncementListApi({ page: 1, pageSize: 5 }),
|
||||||
|
getUnreadCountApi(),
|
||||||
|
getServerHealthRealtimeApi(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (agentResult.status === 'fulfilled') {
|
||||||
|
agents.value = agentResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (workflowResult.status === 'fulfilled') {
|
||||||
|
workflows.value = workflowResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (knowledgeResult.status === 'fulfilled') {
|
||||||
|
knowledgeBases.value = knowledgeResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (modelResult.status === 'fulfilled') {
|
||||||
|
models.value = modelResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (waitingResult.status === 'fulfilled') {
|
||||||
|
waitingRuns.value = waitingResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (announcementResult.status === 'fulfilled') {
|
||||||
|
announcements.value = announcementResult.value.items || [];
|
||||||
|
}
|
||||||
|
if (unreadResult.status === 'fulfilled') {
|
||||||
|
unreadMessages.value = unreadResult.value.total || 0;
|
||||||
|
}
|
||||||
|
if (realtimeResult.status === 'fulfilled') {
|
||||||
|
realtime.value = realtimeResult.value;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refreshData();
|
||||||
|
clockTimer = setInterval(() => {
|
||||||
|
now.value = new Date();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (clockTimer) clearInterval(clockTimer);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height class="control-center">
|
||||||
|
<template #title>
|
||||||
|
<div class="flex items-center justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-2 text-base font-semibold">
|
||||||
|
<LayoutDashboard class="h-5 w-5 text-[var(--el-color-primary)]" />
|
||||||
|
控制中心
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 text-xs text-[var(--el-text-color-secondary)]">
|
||||||
|
AI Agent Admin 轻量工作台
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ElButton :loading="refreshing" :icon="RefreshCw" @click="refreshData">
|
||||||
|
刷新
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<ElSkeleton v-if="loading" animated :rows="8" />
|
||||||
|
|
||||||
|
<div v-else class="dashboard-grid">
|
||||||
|
<section class="welcome-panel">
|
||||||
|
<div class="welcome-copy">
|
||||||
|
<div class="avatar">{{ userName.slice(0, 1) }}</div>
|
||||||
|
<div>
|
||||||
|
<h2>{{ greeting }},{{ userName }}</h2>
|
||||||
|
<p>聚焦智能体、流程编排、知识检索和 Codex 协作。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-panel">
|
||||||
|
<strong>{{ timeText }}</strong>
|
||||||
|
<span>{{ todayText }}</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="ai-entry-grid">
|
||||||
|
<ElCard
|
||||||
|
v-for="item in aiCards"
|
||||||
|
:key="item.title"
|
||||||
|
shadow="hover"
|
||||||
|
class="entry-card"
|
||||||
|
:body-style="{ padding: '16px' }"
|
||||||
|
@click="goto(item.path)"
|
||||||
|
>
|
||||||
|
<div class="entry-head">
|
||||||
|
<div class="entry-icon">
|
||||||
|
<component :is="item.icon" class="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<ElButton link type="primary" @click.stop="goto(item.path)">
|
||||||
|
{{ item.action }}
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<div class="entry-title">{{ item.title }}</div>
|
||||||
|
<div class="entry-desc">{{ item.desc }}</div>
|
||||||
|
<div class="entry-count">{{ item.count }}</div>
|
||||||
|
</ElCard>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="main-column">
|
||||||
|
<ElCard shadow="never" class="panel-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="panel-title">
|
||||||
|
<Sparkles class="h-4 w-4" />
|
||||||
|
AI 协作状态
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="ops-grid">
|
||||||
|
<button
|
||||||
|
v-for="item in opsCards"
|
||||||
|
:key="item.label"
|
||||||
|
class="ops-item"
|
||||||
|
type="button"
|
||||||
|
@click="goto(item.path)"
|
||||||
|
>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
<strong>{{ item.value }}</strong>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="agent-list">
|
||||||
|
<div
|
||||||
|
v-for="agent in agents.slice(0, 6)"
|
||||||
|
:key="agent.id"
|
||||||
|
class="agent-row"
|
||||||
|
@click="goto('/ai-platform/agent')"
|
||||||
|
>
|
||||||
|
<Bot class="h-4 w-4" />
|
||||||
|
<div>
|
||||||
|
<strong>{{ agent.name }}</strong>
|
||||||
|
<span>{{ agent.description || agent.code }}</span>
|
||||||
|
</div>
|
||||||
|
<ElTag
|
||||||
|
size="small"
|
||||||
|
:type="agent.status === 'published' ? 'success' : 'info'"
|
||||||
|
>
|
||||||
|
{{ agent.status === 'published' ? '已发布' : '草稿' }}
|
||||||
|
</ElTag>
|
||||||
|
</div>
|
||||||
|
<ElEmpty
|
||||||
|
v-if="agents.length === 0"
|
||||||
|
description="暂无智能体"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
|
<ElCard shadow="never" class="panel-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="panel-title">
|
||||||
|
<GitMerge class="h-4 w-4" />
|
||||||
|
待处理流程
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
v-for="run in waitingRuns"
|
||||||
|
:key="run.id"
|
||||||
|
class="run-row"
|
||||||
|
@click="goto('/ai-confirmation-center')"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<strong>{{ run.workflow_name }}</strong>
|
||||||
|
<span>{{ run.trigger_type || 'workflow' }}</span>
|
||||||
|
</div>
|
||||||
|
<ElTag type="warning" size="small">等待确认</ElTag>
|
||||||
|
</div>
|
||||||
|
<ElEmpty
|
||||||
|
v-if="waitingRuns.length === 0"
|
||||||
|
description="暂无待确认流程"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
</ElCard>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside class="side-column">
|
||||||
|
<ElCard shadow="never" class="panel-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="panel-title">
|
||||||
|
<Server class="h-4 w-4" />
|
||||||
|
服务器信息
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="health-list">
|
||||||
|
<div class="health-item">
|
||||||
|
<div>
|
||||||
|
<Cpu class="h-4 w-4" />
|
||||||
|
<span>CPU</span>
|
||||||
|
</div>
|
||||||
|
<ElProgress
|
||||||
|
:percentage="Math.round(realtime?.cpu_percent || 0)"
|
||||||
|
:stroke-width="8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="health-item">
|
||||||
|
<div>
|
||||||
|
<ShieldCheck class="h-4 w-4" />
|
||||||
|
<span>内存</span>
|
||||||
|
</div>
|
||||||
|
<ElProgress
|
||||||
|
:percentage="Math.round(realtime?.memory_percent || 0)"
|
||||||
|
:stroke-width="8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="io-row">
|
||||||
|
<HardDrive class="h-4 w-4" />
|
||||||
|
<span>磁盘写入</span>
|
||||||
|
<strong>{{ formatBytes(realtime?.disk_io?.write_speed) }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="io-row">
|
||||||
|
<Network class="h-4 w-4" />
|
||||||
|
<span>网络下载</span>
|
||||||
|
<strong>{{
|
||||||
|
formatBytes(realtime?.network_io?.download_speed)
|
||||||
|
}}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
|
<ElCard shadow="never" class="panel-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="panel-title">
|
||||||
|
<Megaphone class="h-4 w-4" />
|
||||||
|
最新公告
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
v-for="item in announcements"
|
||||||
|
:key="item.id"
|
||||||
|
class="notice-row"
|
||||||
|
@click="goto('/message/announcement-list')"
|
||||||
|
>
|
||||||
|
<Mail class="h-4 w-4" />
|
||||||
|
<div>
|
||||||
|
<strong>{{ item.title }}</strong>
|
||||||
|
<span>{{ item.summary || item.publisher_name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ElEmpty
|
||||||
|
v-if="announcements.length === 0"
|
||||||
|
description="暂无公告"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
|
<ElCard shadow="never" class="panel-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="panel-title">
|
||||||
|
<MessageSquare class="h-4 w-4" />
|
||||||
|
快捷入口
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="quick-actions">
|
||||||
|
<ElButton type="primary" :icon="Plus" @click="goto('/ai-platform/agent')">
|
||||||
|
创建智能体
|
||||||
|
</ElButton>
|
||||||
|
<ElButton :icon="Workflow" @click="goto('/ai-platform/workflow')">
|
||||||
|
流程编排
|
||||||
|
</ElButton>
|
||||||
|
<ElButton :icon="Settings" @click="goto('/ai-platform/model')">
|
||||||
|
模型配置
|
||||||
|
</ElButton>
|
||||||
|
<ElButton :icon="Database" @click="goto('/ai-platform/knowledge-base')">
|
||||||
|
知识库
|
||||||
|
</ElButton>
|
||||||
|
<ElButton @click="goto('/system/user')">用户管理</ElButton>
|
||||||
|
<ElButton @click="goto('/system/role')">角色权限</ElButton>
|
||||||
|
</div>
|
||||||
|
</ElCard>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section class="knowledge-strip">
|
||||||
|
<CheckCircle2 class="h-4 w-4" />
|
||||||
|
<span>
|
||||||
|
当前只保留基础管理与 AI 协作主线,在线开发、应用搭建、组织扩展等重型入口已从主流程移除。
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.control-center {
|
||||||
|
--dashboard-border: var(--el-border-color-lighter);
|
||||||
|
--dashboard-soft: var(--el-fill-color-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 360px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-panel,
|
||||||
|
.ai-entry-grid,
|
||||||
|
.knowledge-strip {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-panel {
|
||||||
|
display: flex;
|
||||||
|
min-height: 112px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
border: 1px solid var(--dashboard-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(64, 158, 255, 0.1),
|
||||||
|
rgba(103, 194, 58, 0.08)
|
||||||
|
);
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-copy {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
display: flex;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-copy h2 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-copy p,
|
||||||
|
.time-panel span {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-panel {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-panel strong {
|
||||||
|
display: block;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-entry-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-card {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-head,
|
||||||
|
.panel-title,
|
||||||
|
.io-row,
|
||||||
|
.notice-row,
|
||||||
|
.run-row,
|
||||||
|
.agent-row,
|
||||||
|
.health-item > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-head {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-icon {
|
||||||
|
display: flex;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(64, 158, 255, 0.12);
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-title {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-desc {
|
||||||
|
min-height: 36px;
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-count {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-column,
|
||||||
|
.side-column {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
gap: 8px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-item {
|
||||||
|
display: flex;
|
||||||
|
min-height: 72px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--dashboard-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--dashboard-soft);
|
||||||
|
text-align: left;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-item span,
|
||||||
|
.agent-row span,
|
||||||
|
.run-row span,
|
||||||
|
.notice-row span {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-item strong {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-list,
|
||||||
|
.health-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-row,
|
||||||
|
.run-row,
|
||||||
|
.notice-row {
|
||||||
|
gap: 10px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
transition: background-color 0.16s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-row:hover,
|
||||||
|
.run-row:hover,
|
||||||
|
.notice-row:hover {
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-row > div,
|
||||||
|
.notice-row > div,
|
||||||
|
.run-row > div {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-row strong,
|
||||||
|
.notice-row strong,
|
||||||
|
.run-row strong {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.health-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 72px minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.health-item > div,
|
||||||
|
.io-row {
|
||||||
|
gap: 8px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.io-row {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.io-row span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.io-row strong {
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-actions :deep(.el-button) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-strip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border: 1px solid var(--dashboard-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--el-fill-color-blank);
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-entry-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.welcome-panel,
|
||||||
|
.welcome-copy {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-panel {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-entry-grid,
|
||||||
|
.ops-grid,
|
||||||
|
.quick-actions {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+2
-1
@@ -25,7 +25,8 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
|
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 pnpm -F @vben/web-ele run build",
|
||||||
|
"build:all": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
|
||||||
"build:analyze": "turbo build:analyze",
|
"build:analyze": "turbo build:analyze",
|
||||||
"build:docker": "./scripts/deploy/build-local-docker-image.sh",
|
"build:docker": "./scripts/deploy/build-local-docker-image.sh",
|
||||||
"build:ele": "pnpm run build --filter=@vben/web-ele",
|
"build:ele": "pnpm run build --filter=@vben/web-ele",
|
||||||
|
|||||||
Reference in New Issue
Block a user