feat(web): Home page ui upgrade
This commit is contained in:
@@ -135,4 +135,8 @@ export const getExperienceConfig = (share_token: string) => {
|
||||
'Authorization': `Bearer ${localStorage.getItem(`shareToken_${share_token}`)}`
|
||||
}
|
||||
})
|
||||
}
|
||||
// Get workspace API call statistics
|
||||
export const getWorkspaceApiStatistics = (data: { start_date: number; end_date: number; }) => {
|
||||
return request.get(`/apps/workspace/api-statistics`, data)
|
||||
}
|
||||
@@ -33,92 +33,72 @@ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage')
|
||||
const currentPath = location.pathname;
|
||||
const menus = allMenus[source] || [];
|
||||
|
||||
/** Find matching menu item and build key path */
|
||||
const findMenuKeyPath = (menuList: any[], parentKeys: string[] = []): string[] | null => {
|
||||
let bestMatch: { path: string; parentId?: string; score: number } | null = null;
|
||||
|
||||
for (const menu of menuList) {
|
||||
/** Check submenus */
|
||||
if (menu.subs && menu.subs.length > 0) {
|
||||
const menuPath = menu.path ? (menu.path[0] !== '/' ? '/' + menu.path : menu.path) : '';
|
||||
for (const sub of menu.subs) {
|
||||
if (sub.path) {
|
||||
const subPath = sub.path[0] !== '/' ? '/' + sub.path : sub.path;
|
||||
|
||||
/** Exact match has priority */
|
||||
if (subPath === currentPath) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
console.log('menuPath', menuPath)
|
||||
/** Dynamic route matching */
|
||||
if (subPath.includes(':')) {
|
||||
/** Check if under parent menu */
|
||||
if (menuPath && currentPath.startsWith(menuPath + '/')) {
|
||||
const relativePath = currentPath.replace(menuPath, '');
|
||||
const pathSegments = subPath.split('/');
|
||||
const relativeSegments = relativePath.split('/');
|
||||
if (pathSegments.length === relativeSegments.length) {
|
||||
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(relativePath)) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Direct match submenu path */
|
||||
const pathSegments = subPath.split('/');
|
||||
const currentSegments = currentPath.split('/');
|
||||
if (pathSegments.length === currentSegments.length) {
|
||||
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(currentPath)) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check main menu */
|
||||
if (menu.path) {
|
||||
const menuPath = menu.path[0] !== '/' ? '/' + menu.path : menu.path;
|
||||
/** Exact match has priority */
|
||||
if (menuPath === currentPath) {
|
||||
return [menu.path, ...parentKeys].reverse();
|
||||
}
|
||||
/** Dynamic route matching */
|
||||
if (menuPath.includes(':')) {
|
||||
const pathSegments = menuPath.split('/');
|
||||
const currentSegments = currentPath.split('/');
|
||||
if (pathSegments.length === currentSegments.length) {
|
||||
const pathPattern = menuPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(currentPath)) {
|
||||
const score = menuPath.split('/').length;
|
||||
if (!bestMatch || score > bestMatch.score) {
|
||||
bestMatch = { path: menu.path, score };
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (currentPath.startsWith(menuPath + '/')) {
|
||||
const score = menuPath.split('/').length;
|
||||
if (!bestMatch || score > bestMatch.score) {
|
||||
bestMatch = { path: menu.path, score };
|
||||
}
|
||||
}
|
||||
}
|
||||
const pathMatches = (pattern: string, path: string): boolean => {
|
||||
const normalized = pattern[0] !== '/' ? '/' + pattern : pattern;
|
||||
if (normalized === path) return true;
|
||||
if (normalized.includes(':')) {
|
||||
const regex = new RegExp('^' + normalized.replace(/:[\\w-]+/g, '[^/]+') + '$');
|
||||
return regex.test(path);
|
||||
}
|
||||
|
||||
if (bestMatch) {
|
||||
return bestMatch.parentId ? [bestMatch.path, bestMatch.parentId] : [bestMatch.path];
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively search menu tree, returns keyPath or null.
|
||||
* keyPath format:
|
||||
* - 1-level: [path]
|
||||
* - 2-level: [subPath, parentId]
|
||||
* - 3-level: [subSubPath, subId, parentId]
|
||||
*/
|
||||
/**
|
||||
* parentId: the group's id when recursing into group subs
|
||||
* keyPath format:
|
||||
* - 1-level: [path]
|
||||
* - 2-level: [subPath, parentId]
|
||||
* - 3-level: [subSubPath, subId, parentId]
|
||||
*/
|
||||
const findKeyPath = (menuList: any[], groupId?: string): string[] | null => {
|
||||
for (const menu of menuList) {
|
||||
/** Group menus: recurse into subs, passing group id */
|
||||
if (menu.type === 'group' && menu.subs?.length) {
|
||||
const result = findKeyPath(menu.subs, `${menu.id}`);
|
||||
if (result) return result;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (menu.subs?.length) {
|
||||
for (const sub of menu.subs) {
|
||||
/** Check third-level subs */
|
||||
if (sub.subs?.length) {
|
||||
for (const subSub of sub.subs) {
|
||||
if (subSub.path && pathMatches(subSub.path, currentPath)) {
|
||||
return [subSub.path, `${sub.id}`, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Second-level match: sub is a leaf under menu */
|
||||
if (sub.path && pathMatches(sub.path, currentPath)) {
|
||||
/** If menu is inside a group, return 3-level: [subPath, menuId, groupId] */
|
||||
return groupId
|
||||
? [sub.path, `${menu.id}`, groupId]
|
||||
: [sub.path, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** First-level / group-child match */
|
||||
if (menu.path && pathMatches(menu.path, currentPath)) {
|
||||
return groupId ? [menu.path, groupId] : [menu.path];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const keyPath = findMenuKeyPath(menus);
|
||||
const keyPath = findKeyPath(menus);
|
||||
|
||||
console.log('keyPath', keyPath)
|
||||
if (keyPath) {
|
||||
updateBreadcrumbs(keyPath, source);
|
||||
}
|
||||
}, [location.pathname, allMenus, source, updateBreadcrumbs]);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -115,6 +115,10 @@ export const en = {
|
||||
ontology: 'Ontology Engineering',
|
||||
prompt: 'Prompt Engineering',
|
||||
skills: 'Skill Library',
|
||||
workbench: 'Workbench',
|
||||
memoryRelated: 'Memory-Related',
|
||||
advancedSettings: 'Advanced Settings',
|
||||
promptHistory: 'My history',
|
||||
},
|
||||
dashboard: {
|
||||
total_models: 'Available Models',
|
||||
@@ -185,19 +189,14 @@ export const en = {
|
||||
|
||||
extractMemoryContent: 'Extract Memory Content',
|
||||
createNewMemorySummary: 'Create New Memory Entry',
|
||||
|
||||
createNewApplication: 'Create New Application',
|
||||
createNewApplicationDesc: 'Build an app in just 3 minutes with zero-code drag-and-drop.',
|
||||
|
||||
createNewApplicationDesc: 'Build an app in 3 minutes, no code.',
|
||||
createNewKnowledge: 'Create New Knowledge',
|
||||
createNewKnowledgeDesc: 'Transform your data into a fully searchable, dedicated knowledge base in seconds.',
|
||||
|
||||
createNewKnowledgeDesc: 'Create a searchable knowledge base instantly.',
|
||||
memoryConversation: 'Memory Conversation',
|
||||
memoryConversationDesc: 'The more you use it, the better AI understands you.',
|
||||
memoryConversationDesc: 'The more you use it, the better AI knows you.',
|
||||
helpCenter: 'Help Center',
|
||||
helpCenterDesc: 'One-stop support to answer your questions and get you started fast.',
|
||||
memorySummary: 'View Memory Summary',
|
||||
memorySummaryDesc: 'View Memory Summary Report',
|
||||
helpCenterDesc: 'One place to get help and start fast.',
|
||||
|
||||
activityEmpty: 'There is currently no memory activity',
|
||||
tagEmpty: 'There are no tag records at the moment~',
|
||||
@@ -214,7 +213,12 @@ export const en = {
|
||||
dialogue: 'Dialogue',
|
||||
chunk: 'Chunk',
|
||||
statement: 'Statement',
|
||||
entity: 'Entity'
|
||||
entity: 'Entity',
|
||||
|
||||
apiCallTrends: 'Api Call Trends',
|
||||
total_calls: 'Total API Calls',
|
||||
app_calls: 'Application API Calls',
|
||||
service_calls: 'Service API Calls',
|
||||
},
|
||||
table: {
|
||||
totalRecords: 'Total {{total}} Articles'
|
||||
@@ -1041,7 +1045,7 @@ export const en = {
|
||||
forgetting_interval_hours: 'Forgetting Interval Hours'
|
||||
},
|
||||
application: {
|
||||
searchPlaceholder: 'Search for applications or clusters',
|
||||
searchPlaceholder: 'Search for applications',
|
||||
createApplication: 'Create Application',
|
||||
type: 'Type',
|
||||
source: 'Source',
|
||||
@@ -1052,6 +1056,7 @@ export const en = {
|
||||
applicationName: 'Application Name',
|
||||
applicationIcon: 'Application Icon',
|
||||
applicationType: 'Application Type',
|
||||
allType: 'All types',
|
||||
|
||||
agent: 'Agent',
|
||||
agentDesc: 'Create a single intelligent agent',
|
||||
@@ -1117,6 +1122,7 @@ export const en = {
|
||||
dialogueHistoricalMemory: 'Conversation History Memory',
|
||||
dialogueHistoricalMemoryDesc: 'Enable this to select memory content from memory management.',
|
||||
toolConfiguration: 'Tool Configuration',
|
||||
toolManagement: 'Tool Management',
|
||||
webSearch: 'Web Search',
|
||||
webSearchDesc: 'Allow the Agent to access the Internet for real-time search',
|
||||
codeExecutor: 'Code Executor',
|
||||
@@ -1126,8 +1132,8 @@ export const en = {
|
||||
variableConfiguration: 'Variable Configuration',
|
||||
selectMemoryContent: 'Select Memory Content',
|
||||
selectMemoryContentDesc: 'From Memory Management Select the memory content to be used in the conversation',
|
||||
VariableManagement: 'Variable Management',
|
||||
VariableManagementDesc: 'Configure the available variables for the Agent',
|
||||
variableManagement: 'Variable Management',
|
||||
variableManagementDesc: 'Configure the available variables for the Agent',
|
||||
addVariables: 'Add Variables',
|
||||
variablesEmpty: 'There are currently no variables available',
|
||||
debuggingEmpty: 'No models available for debugging.',
|
||||
@@ -1172,7 +1178,7 @@ export const en = {
|
||||
VersionInformation: 'Version Information',
|
||||
publishedOn: 'Published on',
|
||||
publisher: 'Published by',
|
||||
DetailsOfVersion: 'Version Details: {{version}}',
|
||||
detailsOfVersion: 'Version Details: {{version}}',
|
||||
exportDSLFile: 'Export DSL file',
|
||||
willRollToThisVersion: 'Will roll to this version',
|
||||
share: 'Share',
|
||||
@@ -1370,7 +1376,8 @@ export const en = {
|
||||
gotoList: 'Return to Application List',
|
||||
gotoDetail: 'View Details',
|
||||
dify: 'Dify',
|
||||
pleaseUploadFile: 'Please upload workflow file',
|
||||
pleaseUploadFile: 'Please upload workflow file',
|
||||
promptOptimizationEmpty: 'The prompt words for conversation optimization will be displayed here',
|
||||
},
|
||||
userMemory: {
|
||||
userMemory: 'User Memory',
|
||||
@@ -1970,10 +1977,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
answer: 'Answer',
|
||||
aiAndCognitiveProcessing: 'AI & Cognitive Processing',
|
||||
llm: 'Large Language Model (LLM)',
|
||||
model_selection: 'Multi-Model Selection',
|
||||
model_voting: 'Multi-Model Voting',
|
||||
'knowledge-retrieval': 'Knowledge Retrieval (RAG)',
|
||||
classification: 'Intelligent Classification',
|
||||
'parameter-extractor': 'Parameter Extraction',
|
||||
flowControl: 'Flow Control',
|
||||
'if-else': 'Conditional Branch',
|
||||
@@ -1983,7 +1987,6 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
'cycle-start': '',
|
||||
break: 'Break Loop',
|
||||
assigner: 'Variable Assignment',
|
||||
parallel: 'Parallel Execution',
|
||||
'var-aggregator': 'Variable Aggregator',
|
||||
externalInteraction: 'External Interaction',
|
||||
"http-request": 'HTTP Request',
|
||||
@@ -1993,20 +1996,6 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
cognitiveUpgrading: 'Cognitive Upgrading (Innovation)',
|
||||
'memory-read': 'Memory Retrieval',
|
||||
'memory-write': 'Memory Storage',
|
||||
task_planning: 'Task Planning',
|
||||
reasoning_control: 'Reasoning Control',
|
||||
self_reflection: 'Self Reflection',
|
||||
memory_enhancement: 'Memory Enhancement',
|
||||
agentCollaborationNode: 'Agent Collaboration Nodes',
|
||||
agent_scheduling: 'Agent Scheduling',
|
||||
agent_collaboration: 'Agent Collaboration',
|
||||
agent_arbitration: 'Agent Arbitration',
|
||||
safetyAndCompliance: 'Safety & Compliance',
|
||||
sensitive_detection: 'Sensitive Detection',
|
||||
output_audit: 'Output Audit',
|
||||
evolutionAndGovernance: 'Evolution & Governance',
|
||||
self_optimization: 'Self Optimization',
|
||||
process_evolution: 'Process Evolution',
|
||||
unknown: 'Unknown Node',
|
||||
|
||||
clickToConfigure: 'Click to configure node parameters',
|
||||
@@ -2029,6 +2018,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
inner: 'Built-in',
|
||||
messagesPlaceholder: 'Write prompts here, type "{" to insert variables, type "insert" to insert',
|
||||
vision: 'Vision',
|
||||
parameterSettings: 'Parameter Settings',
|
||||
},
|
||||
start: {
|
||||
variables: 'Input Fields',
|
||||
@@ -2115,7 +2105,9 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
"eq": 'Is',
|
||||
"ne": 'Is Not',
|
||||
},
|
||||
else_desc: 'Used to define the logic that should be executed when the if condition is not met.'
|
||||
else_desc: 'Used to define the logic that should be executed when the if condition is not met.',
|
||||
unset: 'Condition Not Set',
|
||||
set: 'Set',
|
||||
},
|
||||
'http-request': {
|
||||
auth: 'Authentication',
|
||||
@@ -2151,7 +2143,9 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
categories: 'Categories',
|
||||
user_supplement_prompt: 'Instruction',
|
||||
class_name: 'Classification',
|
||||
addClassName: 'Add Classification'
|
||||
addClassName: 'Add Classification',
|
||||
unset: 'Classification Not Set',
|
||||
set: 'Set',
|
||||
},
|
||||
loop: {
|
||||
cycle_vars: 'Loop Variables',
|
||||
@@ -2640,8 +2634,11 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
file: 'Import File',
|
||||
},
|
||||
prompt: {
|
||||
promptDesc: 'Input your original prompt, and AI will help you refine it into a more professional version',
|
||||
chatTitle: 'Multi-turn dialogue',
|
||||
editor: 'Prompt Generator',
|
||||
history: 'My History',
|
||||
historyDesc: 'View and manage your prompt optimization history',
|
||||
historySearchPlaceholder: 'Search by name',
|
||||
model: 'Model',
|
||||
you: 'You',
|
||||
|
||||
@@ -115,6 +115,10 @@ export const zh = {
|
||||
ontology: '本体工程',
|
||||
prompt: '提示词工程',
|
||||
skills: '技能库',
|
||||
workbench: '工作台',
|
||||
memoryRelated: '记忆相关',
|
||||
advancedSettings: '高级设置',
|
||||
promptHistory: '我的历史',
|
||||
},
|
||||
knowledgeBase: {
|
||||
home: '首页',
|
||||
@@ -427,7 +431,7 @@ export const zh = {
|
||||
}
|
||||
},
|
||||
application: {
|
||||
searchPlaceholder: '搜索应用或集群',
|
||||
searchPlaceholder: '搜索应用',
|
||||
createApplication: '创建应用',
|
||||
type: '类型',
|
||||
source: '来源',
|
||||
@@ -438,6 +442,7 @@ export const zh = {
|
||||
applicationName: '应用名称',
|
||||
applicationIcon: '应用图标',
|
||||
applicationType: '应用类型',
|
||||
allType: '全部类型',
|
||||
|
||||
agent: 'Agent',
|
||||
agentDesc: '创建单个智能代理',
|
||||
@@ -502,6 +507,7 @@ export const zh = {
|
||||
dialogueHistoricalMemory: '对话历史记忆',
|
||||
dialogueHistoricalMemoryDesc: '激活后,可以选择记忆管理中的记忆内容',
|
||||
toolConfiguration: '工具配置',
|
||||
toolManagement: '工具管理',
|
||||
webSearch: '网络搜索',
|
||||
webSearchDesc: '允许Agent访问互联网进行实时搜索',
|
||||
codeExecutor: '代码执行器',
|
||||
@@ -511,8 +517,8 @@ export const zh = {
|
||||
variableConfiguration: '变量配置',
|
||||
selectMemoryContent: '选择记忆内容',
|
||||
selectMemoryContentDesc: '从记忆管理中选择要在对话中使用的记忆内容',
|
||||
VariableManagement: '变量管理',
|
||||
VariableManagementDesc: '配置Agent可用的变量',
|
||||
variableManagement: '变量管理',
|
||||
variableManagementDesc: '配置Agent可用的变量',
|
||||
addVariables: '添加变量',
|
||||
variablesEmpty: '目前没有可用的变量',
|
||||
debuggingEmpty: '目前没有可用的调试模型',
|
||||
@@ -557,7 +563,7 @@ export const zh = {
|
||||
VersionInformation: '版本信息',
|
||||
publishedOn: '发布于',
|
||||
publisher: '发布者',
|
||||
DetailsOfVersion: '{{version}}版本详情',
|
||||
detailsOfVersion: '{{version}} 版本详情',
|
||||
exportDSLFile: '导出DSL文件',
|
||||
willRollToThisVersion: '将回滚到此版本',
|
||||
share: '分享',
|
||||
@@ -755,6 +761,7 @@ export const zh = {
|
||||
gotoDetail: '查看详情',
|
||||
dify: 'Dify',
|
||||
pleaseUploadFile: '请上传工作流文件',
|
||||
promptOptimizationEmpty: '对话优化提示词将显示在这里',
|
||||
},
|
||||
table: {
|
||||
totalRecords: '共 {{total}} 条记录'
|
||||
@@ -828,19 +835,14 @@ export const zh = {
|
||||
|
||||
extractMemoryContent: '提取记忆内容',
|
||||
createNewMemorySummary: '创建新记忆条目',
|
||||
|
||||
createNewApplication: '创建新应用',
|
||||
createNewApplicationDesc: '零代码拖拽3分钟创应用',
|
||||
|
||||
createNewApplicationDesc: '零代码,3 分钟搭建应用',
|
||||
createNewKnowledge: '创建知识库',
|
||||
createNewKnowledgeDesc: '秒变可搜索的专属知识库',
|
||||
|
||||
createNewKnowledgeDesc: '秒级生成可搜索知识库',
|
||||
memoryConversation: '记忆对话',
|
||||
memoryConversationDesc: '让AI越用越懂你',
|
||||
memoryConversationDesc: '越用越懂你的 AI',
|
||||
helpCenter: '帮助中心',
|
||||
helpCenterDesc: '一站式解决疑问快速上手',
|
||||
memorySummary: '查看记忆摘要',
|
||||
memorySummaryDesc: '查看记忆摘要报告',
|
||||
helpCenterDesc: '一站式上手与支持',
|
||||
|
||||
activityEmpty: '目前没有记忆活动',
|
||||
tagEmpty: '目前没有标签记录~',
|
||||
@@ -857,7 +859,12 @@ export const zh = {
|
||||
dialogue: '对话',
|
||||
chunk: '分块',
|
||||
statement: '语句',
|
||||
entity: '实体'
|
||||
entity: '实体',
|
||||
|
||||
apiCallTrends: 'Api调用趋势',
|
||||
total_calls: 'Api 总调用量',
|
||||
app_calls: '应用Api调用量',
|
||||
service_calls: '服务Api调用量',
|
||||
},
|
||||
header: {
|
||||
userInfo: '用户信息',
|
||||
@@ -1966,10 +1973,7 @@ export const zh = {
|
||||
answer: '回复(Answer)',
|
||||
aiAndCognitiveProcessing: 'AI与认知处理',
|
||||
llm: '大语言模型 (LLM)',
|
||||
model_selection: '多模型选择',
|
||||
model_voting: '多模型投票',
|
||||
'knowledge-retrieval': '知识检索 (RAG)',
|
||||
classification: '智能分类',
|
||||
'parameter-extractor': '参数提取',
|
||||
flowControl: '流程控制',
|
||||
'if-else': '条件分支',
|
||||
@@ -1979,7 +1983,6 @@ export const zh = {
|
||||
'cycle-start': '',
|
||||
break: '退出循环',
|
||||
assigner: '变量赋值',
|
||||
parallel: '并行执行',
|
||||
'var-aggregator': '变量聚合器',
|
||||
externalInteraction: '外部交互',
|
||||
"http-request": 'HTTP请求',
|
||||
@@ -1989,20 +1992,6 @@ export const zh = {
|
||||
cognitiveUpgrading: '认知升级(创新)',
|
||||
'memory-read': '记忆提取',
|
||||
'memory-write': '记忆储存',
|
||||
task_planning: '任务规划',
|
||||
reasoning_control: '推理控制',
|
||||
self_reflection: '自我反思',
|
||||
memory_enhancement: '记忆增强',
|
||||
agentCollaborationNode: 'Agent 协作节点',
|
||||
agent_scheduling: 'Agent 调度',
|
||||
agent_collaboration: 'Agent 协同',
|
||||
agent_arbitration: 'Agent 仲裁',
|
||||
safetyAndCompliance: '安全与合规',
|
||||
sensitive_detection: '敏感识别',
|
||||
output_audit: '输出审计',
|
||||
evolutionAndGovernance: '演化与治理',
|
||||
self_optimization: '自我优化',
|
||||
process_evolution: '流程演化',
|
||||
unknown: '未知节点',
|
||||
|
||||
clickToConfigure: '点击配置节点参数',
|
||||
@@ -2025,6 +2014,7 @@ export const zh = {
|
||||
inner: '内置',
|
||||
messagesPlaceholder: '在此处编写提示,输入“{”插入变量,输入“insert”插入',
|
||||
vision: '视觉',
|
||||
parameterSettings: '参数设置',
|
||||
},
|
||||
start: {
|
||||
variables: '输入字段',
|
||||
@@ -2111,7 +2101,9 @@ export const zh = {
|
||||
"eq": '是',
|
||||
"ne": '不是',
|
||||
},
|
||||
else_desc: '用于定义当 if 条件不满足时应执行的逻辑。'
|
||||
else_desc: '用于定义当 if 条件不满足时应执行的逻辑。',
|
||||
unset: '条件未设置',
|
||||
set: '已设置',
|
||||
},
|
||||
'http-request': {
|
||||
auth: '鉴权',
|
||||
@@ -2147,7 +2139,9 @@ export const zh = {
|
||||
categories: '分类',
|
||||
user_supplement_prompt: '指令',
|
||||
class_name: '分类',
|
||||
addClassName: '添加分类'
|
||||
addClassName: '添加分类',
|
||||
unset: '分类未设置',
|
||||
set: '已设置',
|
||||
},
|
||||
loop: {
|
||||
cycle_vars: '循环变量',
|
||||
@@ -2640,8 +2634,10 @@ export const zh = {
|
||||
file: '导入文件',
|
||||
},
|
||||
prompt: {
|
||||
promptDesc: 'Input your original prompt, and AI will help you refine it into a more professional version',
|
||||
editor: '提示词生成器',
|
||||
history: '我的历史',
|
||||
historyDesc: 'View and manage your prompt optimization history',
|
||||
historySearchPlaceholder: '按名称搜索',
|
||||
model: '模型',
|
||||
you: '你',
|
||||
|
||||
@@ -88,6 +88,7 @@ const componentMap: Record<string, LazyExoticComponent<ComponentType<object>>> =
|
||||
Ontology: lazy(() => import('@/views/Ontology')),
|
||||
OntologyDetail: lazy(() => import('@/views/Ontology/pages/Detail')),
|
||||
Prompt: lazy(() => import('@/views/Prompt')),
|
||||
PromptHistory: lazy(() => import('@/views/Prompt/pages/History')),
|
||||
Skills: lazy(() => import('@/views/Skills')),
|
||||
SkillConfig: lazy(() => import('@/views/Skills/pages/SkillConfig')),
|
||||
Jump: lazy(() => import('@/views/JumpPage')),
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
{ "path": "/space-config", "element": "SpaceConfig" },
|
||||
{ "path": "/ontology", "element": "Ontology" },
|
||||
{ "path": "/prompt", "element": "Prompt" },
|
||||
{ "path": "/prompt/history", "element": "PromptHistory" },
|
||||
{ "path": "/no-permission", "element": "NoPermission" },
|
||||
{ "path": "/*", "element": "NotFound" }
|
||||
]
|
||||
|
||||
@@ -124,56 +124,40 @@
|
||||
],
|
||||
"space": [
|
||||
{
|
||||
"id": 4,
|
||||
"id": 1,
|
||||
"parent": 0,
|
||||
"code": "dashboard",
|
||||
"label": "记忆看板",
|
||||
"i18nKey": "menu.home",
|
||||
"code": "workbench",
|
||||
"label": "workbench",
|
||||
"i18nKey": "menu.workbench",
|
||||
"path": "/",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"parent": 0,
|
||||
"code": "application",
|
||||
"label": "应用管理",
|
||||
"i18nKey": "menu.applicationManagement",
|
||||
"path": "/application",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"parent": 0,
|
||||
"code": "knowledge",
|
||||
"label": "知识库",
|
||||
"i18nKey": "menu.knowledgeManagement",
|
||||
"path": "/knowledge-base",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"type": "group",
|
||||
"subs": [
|
||||
{
|
||||
"id": 61,
|
||||
"parent": 6,
|
||||
"code": "knowledgePrivate",
|
||||
"label": "Private",
|
||||
"i18nKey": "menu.knowledgePrivate",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/private",
|
||||
"id": 11,
|
||||
"parent": 1,
|
||||
"code": "dashboard",
|
||||
"label": "记忆看板",
|
||||
"i18nKey": "menu.home",
|
||||
"path": "/",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"parent": 1,
|
||||
"code": "application",
|
||||
"label": "应用管理",
|
||||
"i18nKey": "menu.applicationManagement",
|
||||
"path": "/application",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
@@ -181,261 +165,338 @@
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
"parent": 6,
|
||||
"code": "knowledgeShare",
|
||||
"label": "Share",
|
||||
"i18nKey": "menu.knowledgeShare",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/share",
|
||||
"id": 13,
|
||||
"parent": 1,
|
||||
"code": "knowledge",
|
||||
"label": "知识库",
|
||||
"i18nKey": "menu.knowledgeManagement",
|
||||
"path": "/knowledge-base",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 63,
|
||||
"parent": 6,
|
||||
"code": "knowledgeCreateDataset",
|
||||
"label": "CreateDataset",
|
||||
"i18nKey": "menu.knowledgeCreateDataset",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/create-dataset",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 64,
|
||||
"parent": 6,
|
||||
"code": "knowledgeDocumentDetails",
|
||||
"label": "DocumentDetails",
|
||||
"i18nKey": "menu.knowledgeDocumentDetails",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/DocumentDetails",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"parent": 0,
|
||||
"code": "memory",
|
||||
"label": "记忆管理",
|
||||
"i18nKey": "menu.memoryManagement",
|
||||
"path": "/memory",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": [
|
||||
{
|
||||
"id": 71,
|
||||
"parent": 7,
|
||||
"code": "forgettingEngine",
|
||||
"label": "遗忘引擎",
|
||||
"i18nKey": "menu.forgettingEngine",
|
||||
"path": "/forgetting-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"parent": 7,
|
||||
"code": "memoryExtractionEngine",
|
||||
"label": "记忆萃取引擎",
|
||||
"i18nKey": "menu.memoryExtractionEngine",
|
||||
"path": "/memory-extraction-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"parent": 7,
|
||||
"code": "emotionEngine",
|
||||
"label": "情感引擎",
|
||||
"i18nKey": "menu.emotionEngine",
|
||||
"path": "/emotion-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"parent": 7,
|
||||
"code": "selfReflectionEngine",
|
||||
"label": "反思引擎",
|
||||
"i18nKey": "menu.selfReflectionEngine",
|
||||
"path": "/reflection-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"parent": 0,
|
||||
"code": "userMemory",
|
||||
"label": "",
|
||||
"i18nKey": "menu.userMemory",
|
||||
"path": "/user-memory",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 1,
|
||||
"menuDesc": "管理用户记忆",
|
||||
"subs": [
|
||||
{
|
||||
"id": 81,
|
||||
"parent": 8,
|
||||
"code": "userMemoryDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.userMemoryDetail",
|
||||
"path": "/user-memory/neo4j/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 2,
|
||||
"sort": 0,
|
||||
"subs": [
|
||||
{
|
||||
"id": 811,
|
||||
"parent": 81,
|
||||
"code": "statementDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.statementDetail",
|
||||
"path": "/statement/:id",
|
||||
"id": 131,
|
||||
"parent": 13,
|
||||
"code": "knowledgePrivate",
|
||||
"label": "Private",
|
||||
"i18nKey": "menu.knowledgePrivate",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/private",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 132,
|
||||
"parent": 13,
|
||||
"code": "knowledgeShare",
|
||||
"label": "Share",
|
||||
"i18nKey": "menu.knowledgeShare",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/share",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 133,
|
||||
"parent": 13,
|
||||
"code": "knowledgeCreateDataset",
|
||||
"label": "CreateDataset",
|
||||
"i18nKey": "menu.knowledgeCreateDataset",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/create-dataset",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"parent": 13,
|
||||
"code": "knowledgeDocumentDetails",
|
||||
"label": "DocumentDetails",
|
||||
"i18nKey": "menu.knowledgeDocumentDetails",
|
||||
"path": "/knowledge-base/:knowledgeBaseId/DocumentDetails",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"parent": 1,
|
||||
"code": "prompt",
|
||||
"label": "提示词",
|
||||
"i18nKey": "menu.prompt",
|
||||
"path": "/prompt",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": [
|
||||
{
|
||||
"id": 141,
|
||||
"parent": 14,
|
||||
"code": "promptHistory",
|
||||
"label": "promptHistory",
|
||||
"i18nKey": "menu.promptHistory",
|
||||
"path": "/prompt/history",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 3,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"parent": 0,
|
||||
"code": "memoryRelated",
|
||||
"label": "memoryRelated",
|
||||
"i18nKey": "menu.memoryRelated",
|
||||
"path": "/",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"type": "group",
|
||||
"subs": [
|
||||
{
|
||||
"id": 21,
|
||||
"parent": 2,
|
||||
"code": "memory",
|
||||
"label": "记忆管理",
|
||||
"i18nKey": "menu.memoryManagement",
|
||||
"path": "/memory",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": [
|
||||
{
|
||||
"id": 211,
|
||||
"parent": 21,
|
||||
"code": "forgettingEngine",
|
||||
"label": "遗忘引擎",
|
||||
"i18nKey": "menu.forgettingEngine",
|
||||
"path": "/forgetting-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 212,
|
||||
"parent": 21,
|
||||
"code": "memoryExtractionEngine",
|
||||
"label": "记忆萃取引擎",
|
||||
"i18nKey": "menu.memoryExtractionEngine",
|
||||
"path": "/memory-extraction-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 213,
|
||||
"parent": 21,
|
||||
"code": "emotionEngine",
|
||||
"label": "情感引擎",
|
||||
"i18nKey": "menu.emotionEngine",
|
||||
"path": "/emotion-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 214,
|
||||
"parent": 21,
|
||||
"code": "selfReflectionEngine",
|
||||
"label": "反思引擎",
|
||||
"i18nKey": "menu.selfReflectionEngine",
|
||||
"path": "/reflection-engine/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"parent": 8,
|
||||
"code": "userMemoryDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.userMemoryDetail",
|
||||
"path": "/user-memory/:id",
|
||||
"id": 22,
|
||||
"parent": 2,
|
||||
"code": "userMemory",
|
||||
"label": "",
|
||||
"i18nKey": "menu.userMemory",
|
||||
"path": "/user-memory",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 2,
|
||||
"sort": 0
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 1,
|
||||
"menuDesc": "管理用户记忆",
|
||||
"subs": [
|
||||
{
|
||||
"id": 221,
|
||||
"parent": 22,
|
||||
"code": "userMemoryDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.userMemoryDetail",
|
||||
"path": "/user-memory/neo4j/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 2,
|
||||
"sort": 0,
|
||||
"subs": [
|
||||
{
|
||||
"id": 2211,
|
||||
"parent": 221,
|
||||
"code": "statementDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.statementDetail",
|
||||
"path": "/statement/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 3,
|
||||
"sort": 0,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 222,
|
||||
"parent": 22,
|
||||
"code": "userMemoryDetail",
|
||||
"label": "记忆详情",
|
||||
"i18nKey": "menu.userMemoryDetail",
|
||||
"path": "/user-memory/:id",
|
||||
"enable": true,
|
||||
"display": false,
|
||||
"level": 2,
|
||||
"sort": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"parent": 2,
|
||||
"code": "ontology",
|
||||
"label": "本体工程",
|
||||
"i18nKey": "menu.ontology",
|
||||
"path": "/ontology",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"parent": 2,
|
||||
"code": "memoryConversation",
|
||||
"label": "记忆验证",
|
||||
"i18nKey": "menu.memoryConversation",
|
||||
"path": "/memory-conversation",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"id": 3,
|
||||
"parent": 0,
|
||||
"code": "ontology",
|
||||
"label": "本体工程",
|
||||
"i18nKey": "menu.ontology",
|
||||
"path": "/ontology",
|
||||
"code": "advancedSettings",
|
||||
"label": "advancedSettings",
|
||||
"i18nKey": "menu.advancedSettings",
|
||||
"path": "/",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"parent": 0,
|
||||
"code": "memoryConversation",
|
||||
"label": "记忆验证",
|
||||
"i18nKey": "menu.memoryConversation",
|
||||
"path": "/memory-conversation",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"parent": 0,
|
||||
"code": "apiKey",
|
||||
"label": "API KEY管理",
|
||||
"i18nKey": "menu.apiKeyManagement",
|
||||
"path": "/api-key",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"parent": 0,
|
||||
"code": "prompt",
|
||||
"label": "提示词",
|
||||
"i18nKey": "menu.prompt",
|
||||
"path": "/prompt",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"parent": 0,
|
||||
"code": "member",
|
||||
"label": "成员管理",
|
||||
"i18nKey": "menu.memberManagement",
|
||||
"path": "/member",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"parent": 0,
|
||||
"code": "spaceConfig",
|
||||
"label": "空间配置",
|
||||
"i18nKey": "menu.spaceConfig",
|
||||
"path": "/space-config",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
"type": "group",
|
||||
"subs": [
|
||||
{
|
||||
"id": 31,
|
||||
"parent": 3,
|
||||
"code": "apiKey",
|
||||
"label": "API KEY管理",
|
||||
"i18nKey": "menu.apiKeyManagement",
|
||||
"path": "/api-key",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"parent": 3,
|
||||
"code": "member",
|
||||
"label": "成员管理",
|
||||
"i18nKey": "menu.memberManagement",
|
||||
"path": "/member",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"parent": 3,
|
||||
"code": "spaceConfig",
|
||||
"label": "空间配置",
|
||||
"i18nKey": "menu.spaceConfig",
|
||||
"path": "/space-config",
|
||||
"enable": true,
|
||||
"display": true,
|
||||
"level": 1,
|
||||
"sort": 0,
|
||||
"icon": null,
|
||||
"iconActive": null,
|
||||
"subs": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 16:33:34
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-02 16:33:34
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-04 10:31:14
|
||||
*/
|
||||
/**
|
||||
* Menu Store
|
||||
@@ -42,6 +42,7 @@ export interface MenuItem {
|
||||
master?: string | null;
|
||||
disposable?: boolean;
|
||||
appSystem?: string | null;
|
||||
type?: 'group' | string;
|
||||
subs?: MenuItem[] | null;
|
||||
onClick?: (e?: React.MouseEvent) => void | boolean;
|
||||
}
|
||||
@@ -89,20 +90,27 @@ export const useMenu = create<MenuState>((set, get) => ({
|
||||
const { allMenus } = get()
|
||||
const menus = allMenus[source] || []
|
||||
let result: MenuItem[] = []
|
||||
|
||||
console.log('updateBreadcrumbs paths:', paths);
|
||||
|
||||
|
||||
/** Flatten group menus so top-level items are always real menu entries */
|
||||
const flatMenus = menus.flatMap(m => m.type === 'group' ? (m.subs || []) : [m]);
|
||||
|
||||
const findById = (list: MenuItem[], id: string) => list.find(m => `${m.id}` === id);
|
||||
/** Find menu by id in both original menus and flatMenus (handles group ids) */
|
||||
const findMenuById = (id: string) => findById(menus, id) || findById(flatMenus, id);
|
||||
const pathMatches = (pattern: string, path: string) => {
|
||||
const n = pattern[0] !== '/' ? '/' + pattern : pattern;
|
||||
if (n === path) return true;
|
||||
if (n.includes(':')) return new RegExp('^' + n.replace(/:[\w-]+/g, '[^/]+') + '$').test(path);
|
||||
return false;
|
||||
};
|
||||
|
||||
if (paths.length === 3) {
|
||||
/** Three-level menu: [subSubPath, subId, menuId] */
|
||||
const menuId = paths[2];
|
||||
const subId = paths[1];
|
||||
const subSubPath = paths[0];
|
||||
|
||||
const matchedMenu = menus.find(menu => `${menu.id}` === menuId);
|
||||
if (matchedMenu && matchedMenu.subs) {
|
||||
const matchedSub = matchedMenu.subs.find(sub => `${sub.id}` === subId);
|
||||
if (matchedSub && matchedSub.subs) {
|
||||
const matchedSubSub = matchedSub.subs.find(subSub => subSub.path === subSubPath);
|
||||
/** Three-level: [subSubPath, subId, menuId] */
|
||||
const matchedMenu = findMenuById(paths[2]);
|
||||
if (matchedMenu?.subs) {
|
||||
const matchedSub = findById(matchedMenu.subs, paths[1]);
|
||||
if (matchedSub?.subs) {
|
||||
const matchedSubSub = matchedSub.subs.find(s => s.path === paths[0] || pathMatches(s.path || '', paths[0]));
|
||||
if (matchedSubSub) {
|
||||
result = [
|
||||
{ ...matchedMenu, subs: null },
|
||||
@@ -113,23 +121,16 @@ export const useMenu = create<MenuState>((set, get) => ({
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** Original logic for one-level and two-level menus */
|
||||
const matchedMenu: MenuItem | undefined = menus.find(menu => menu.path === paths[paths.length - 1] || `${menu.id}` === paths[1]);
|
||||
|
||||
const matchedMenu = flatMenus.find(m => m.path === paths[0] || `${m.id}` === paths[1]);
|
||||
if (matchedMenu) {
|
||||
let matchedSubMenu: MenuItem | undefined = undefined;
|
||||
if (paths.length > 1 && matchedMenu?.subs?.length) {
|
||||
matchedSubMenu = matchedMenu.subs.find(menu => menu.path === paths[0]);
|
||||
let matchedSubMenu: MenuItem | undefined;
|
||||
if (paths.length > 1 && matchedMenu.subs?.length) {
|
||||
matchedSubMenu = matchedMenu.subs.find(m => m.path === paths[0]);
|
||||
}
|
||||
result = [
|
||||
{ ...matchedMenu, subs: null },
|
||||
matchedSubMenu
|
||||
].filter(item => item !== undefined) as MenuItem[]
|
||||
} else {
|
||||
result = [] as MenuItem[]
|
||||
result = [{ ...matchedMenu, subs: null }, matchedSubMenu].filter(Boolean) as MenuItem[];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const allBreadcrumbs = { ...get().allBreadcrumbs, [source]: result }
|
||||
set({ allBreadcrumbs })
|
||||
localStorage.setItem('breadcrumbs', JSON.stringify(allBreadcrumbs))
|
||||
|
||||
@@ -3,87 +3,99 @@ import type { ThemeConfig } from 'antd';
|
||||
// 浅色主题配置
|
||||
export const lightTheme: ThemeConfig = {
|
||||
token: {
|
||||
colorPrimary: '#155EEF',
|
||||
colorPrimary: '#171719',
|
||||
colorBgBase: '#ffffff',
|
||||
colorTextBase: '#212332',
|
||||
colorTextBase: '#171719',
|
||||
colorBorder: '#DFE4ED',
|
||||
colorBgLayout: '#ffffff',
|
||||
colorBgContainer: '#ffffff',
|
||||
colorText: '#212332',
|
||||
colorText: '#171719',
|
||||
colorTextSecondary: '#6b7280',
|
||||
borderRadius: 6,
|
||||
borderRadius: 8,
|
||||
colorSplit: '#DFE4ED',
|
||||
colorBorderBg: '#DFE4ED',
|
||||
colorBgContainerDisabled: '#F6F8FC',
|
||||
colorBgContainerDisabled: '#F6F6F6',
|
||||
colorTextDisabled: '#5B6167',
|
||||
// Card 用到
|
||||
borderRadiusLG: 12,
|
||||
borderRadiusSM: 8,
|
||||
colorBorderSecondary: '#DFE4ED',
|
||||
// colorBgContainer: '#FBFDFF',
|
||||
colorError: '#FF5D34',
|
||||
sizeSM: 12,
|
||||
fontSizeSM: 12,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
components: {
|
||||
Layout: {
|
||||
headerBg: 'transparent',
|
||||
bodyBg: '#FBFDFF',
|
||||
siderBg: 'transparent',
|
||||
headerPadding: '16px 46px 16px 21px',
|
||||
bodyBg: '#EEEFF4',
|
||||
siderBg: '#FAFCFF',
|
||||
headerPadding: '0 24px 0 20px',
|
||||
headerHeight: 64,
|
||||
headerColor: '#212332',
|
||||
},
|
||||
Menu: {
|
||||
itemColor: '#5B6167',
|
||||
itemSelectedColor: '#212332',
|
||||
subMenuItemSelectedColor: '#212332',
|
||||
fontSize: 13,
|
||||
itemColor: '#171719',
|
||||
itemSelectedColor: '#FFFFFF',
|
||||
subMenuItemSelectedColor: '#FFFFFF',
|
||||
|
||||
itemHoverColor: '#212332',
|
||||
itemHoverBg: '#FFFFFF',
|
||||
itemHoverColor: '#171719',
|
||||
itemHoverBg: 'rgba(223,228,237,0.5)',
|
||||
|
||||
itemBg: 'transparent',
|
||||
itemSelectedBg: '#FFFFFF',
|
||||
itemSelectedBg: '#171719',
|
||||
|
||||
subMenuItemBg: 'transparent',
|
||||
|
||||
itemPaddingInline: 12,
|
||||
|
||||
itemHeight: 32,
|
||||
itemPaddingInline: 10,
|
||||
|
||||
itemHeight: 38,
|
||||
itemMarginBlock: 8,
|
||||
horizontalLineHeight: 32,
|
||||
itemMarginInline: 12,
|
||||
itemBorderRadius: 6,
|
||||
itemBorderRadius: 8,
|
||||
|
||||
iconSize: 16,
|
||||
iconMarginInlineEnd: 8,
|
||||
iconMarginInlineEnd: 10,
|
||||
collapsedIconSize: 12,
|
||||
collapsedWidth: '64px',
|
||||
|
||||
popupBg: '#FBFDFF',
|
||||
groupTitleFontSize: 12,
|
||||
groupTitleColor: '#5B6167',
|
||||
groupTitleLineHeight: '17px',
|
||||
},
|
||||
Button: {
|
||||
defaultColor: '#5B6167',
|
||||
defaultColor: '#171719',
|
||||
defaultBorderColor: '#EBEBEB',
|
||||
defaultShadow: 'none',
|
||||
primaryShadow: 'none',
|
||||
dangerShadow: 'none'
|
||||
dangerShadow: 'none',
|
||||
defaultGhostBorderColor: '#EBEBEB',
|
||||
defaultHoverColor: 'rgba(23, 23, 25, 0.7)',
|
||||
defaultHoverBorderColor: 'rgba(23, 23, 25, 0.7)',
|
||||
},
|
||||
Form: {
|
||||
labelColor: '#212332',
|
||||
labelColor: '#171719',
|
||||
itemMarginBottom: 16,
|
||||
},
|
||||
Slider: {
|
||||
// dotSize: 10,
|
||||
controlSize: 8,
|
||||
railSize: 8,
|
||||
controlSize: 6,
|
||||
railSize: 6,
|
||||
handleSize: 10,
|
||||
handleSizeHover: 10,
|
||||
handleColor: '#155EEF',
|
||||
trackBg: '#155EEF',
|
||||
railBg: '#E1E2E7',
|
||||
handleColor: '#171719',
|
||||
handleActiveOutlineColor: '#171719',
|
||||
trackBg: '#171719',
|
||||
railBg: '#EBEBEB',
|
||||
},
|
||||
Table: {
|
||||
borderColor: '#DFE4ED',
|
||||
headerBg: '#FBFDFF',
|
||||
headerColor: '#5B6167',
|
||||
borderColor: '#EBEBEB',
|
||||
headerBg: '#FFFFFF',
|
||||
rowHoverBg: '#F0F3F8',
|
||||
rowSelectedBg: '#E9F1FF',
|
||||
rowSelectedHoverBg: '#F0F3F8',
|
||||
@@ -95,19 +107,36 @@ export const lightTheme: ThemeConfig = {
|
||||
},
|
||||
Breadcrumb: {
|
||||
itemColor: '#5B6167',
|
||||
lastItemColor: '#212332',
|
||||
lastItemColor: '#171719',
|
||||
linkColor: '#5B6167',
|
||||
linkHoverColor: '#212332',
|
||||
linkHoverColor: '#171719',
|
||||
fontSize: 18,
|
||||
},
|
||||
Input: {
|
||||
inputFontSizeSM: 12,
|
||||
controlHeightSM: 26
|
||||
controlHeightSM: 26,
|
||||
activeShadow: 'none',
|
||||
},
|
||||
InputNumber: {
|
||||
activeShadow: 'none',
|
||||
},
|
||||
Select: {
|
||||
lineHeightSM: 26
|
||||
lineHeightSM: 26,
|
||||
},
|
||||
Upload: {
|
||||
pictureCardSize: 96,
|
||||
},
|
||||
Switch: {
|
||||
trackHeight: 24,
|
||||
trackHeightSM: 18,
|
||||
handleSize: 20,
|
||||
handleSizeSM: 14,
|
||||
innerMinMarginSM: 8,
|
||||
innerMaxMarginSM: 27,
|
||||
},
|
||||
Cascader: {
|
||||
optionSelectedBg: '#F6F6F6',
|
||||
optionSelectedColor: '#212332'
|
||||
}
|
||||
}
|
||||
};
|
||||
5
web/src/styles/common.css
Normal file
5
web/src/styles/common.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.rb-border { border: 1px solid #DFE4ED; }
|
||||
.rb-border-t { border-top: 1px solid #DFE4ED; }
|
||||
.rb-border-r { border-right: 1px solid #DFE4ED; }
|
||||
.rb-border-b { border-bottom: 1px solid #DFE4ED; }
|
||||
.rb-border-l { border-left: 1px solid #DFE4ED; }
|
||||
@@ -1,5 +1,65 @@
|
||||
@import "tailwindcss" prefix(rb);
|
||||
@plugin "@tailwindcss/typography";
|
||||
@import "./common.css";
|
||||
|
||||
@font-face {
|
||||
font-family: 'MiSans-Bold';
|
||||
src: url('@/assets/font/MiSans/MiSans-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}@font-face {
|
||||
font-family: 'MiSans-Demibold';
|
||||
src: url('@/assets/font/MiSans/MiSans-Demibold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}@font-face {
|
||||
font-family: 'MiSans-ExtraLight';
|
||||
src: url('@/assets/font/MiSans/MiSans-ExtraLight.woff2') format('woff2');
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Heavy';
|
||||
src: url('@/assets/font/MiSans/MiSans-Heavy.woff2') format('woff2');
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Light';
|
||||
src: url('@/assets/font/MiSans/MiSans-Light.woff2') format('woff2');
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Medium';
|
||||
src: url('@/assets/font/MiSans/MiSans-Medium.woff2') format('woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Normal';
|
||||
src: url('@/assets/font/MiSans/MiSans-Normal.woff2') format('woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Regular';
|
||||
src: url('@/assets/font/MiSans/MiSans-Regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Semibold';
|
||||
src: url('@/assets/font/MiSans/MiSans-Semibold.woff2') format('woff2');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'MiSans-Thin';
|
||||
src: url('@/assets/font/MiSans/MiSans-Thin.woff2') format('woff2');
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
@@ -31,7 +91,7 @@ body {
|
||||
.ant-menu-light>.ant-menu .ant-menu-item-selected,
|
||||
.ant-menu-light:not(.ant-menu-horizontal) .ant-menu-item:not(.ant-menu-item-selected):hover,
|
||||
.ant-menu-light>.ant-menu:not(.ant-menu-horizontal) .ant-menu-item:not(.ant-menu-item-selected):hover {
|
||||
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: none;
|
||||
}
|
||||
.ant-menu-light .ant-menu-item-selected,
|
||||
.ant-menu-light>.ant-menu .ant-menu-item-selected {
|
||||
@@ -47,8 +107,14 @@ body {
|
||||
background: #FFFFFF;
|
||||
color: #212332;
|
||||
}
|
||||
.ant-menu-inline-collapsed>.ant-menu-item,
|
||||
.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item,
|
||||
.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title,
|
||||
.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title {
|
||||
padding-inline: calc(50% - 6px - 14px);
|
||||
}
|
||||
.ant-slider .ant-slider-handle::after {
|
||||
background-color: #155EEF;
|
||||
background-color: #171719;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
@@ -56,6 +122,34 @@ body {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
.ant-slider-horizontal .ant-slider-handle {
|
||||
inset-block-start: 3px;
|
||||
}
|
||||
.ant-slider.small {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
.ant-slider-horizontal.small .ant-slider-handle {
|
||||
inset-block-start: 5px;
|
||||
}
|
||||
.ant-slider.small .ant-slider-handle::after {
|
||||
background-color: #171719;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.ant-slider.small .ant-slider-handle::before {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.ant-slider.small .ant-slider-handle:hover::after,
|
||||
.ant-slider.small .ant-slider-handle:active::after,
|
||||
.ant-slider.small .ant-slider-handle:focus::after {
|
||||
outline: 2px solid #171719;
|
||||
}
|
||||
.ant-slider.small .ant-slider-rail,
|
||||
.ant-slider.small .ant-slider-track {
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ant-table-container {
|
||||
border: 1px solid #DFE4ED;
|
||||
border-radius: 8px;
|
||||
@@ -83,6 +177,16 @@ body {
|
||||
.ant-table-wrapper .ant-table-thead>tr>td {
|
||||
font-weight: 500;
|
||||
}
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-title,
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-footer,
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-cell,
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-thead>tr>th,
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-tbody>tr>th,
|
||||
.ant-table-wrapper .ant-table.ant-table-small .ant-table-tbody>tr>td,
|
||||
.ant-table-wrapper .ant-table.ant-table-small tfoot>tr>th,
|
||||
.ant-table-wrapper .ant-table.ant-table-small tfoot>tr>td {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.ant-table-wrapper .ant-table-pagination.ant-pagination {
|
||||
margin: 24px 0 32px 0;
|
||||
}
|
||||
@@ -165,13 +269,7 @@ body {
|
||||
.infinite-scroll-component {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
.ant-slider-horizontal .ant-slider-handle {
|
||||
inset-block-start: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent !important;
|
||||
}
|
||||
.ant-breadcrumb a:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
@@ -183,6 +281,34 @@ body {
|
||||
}
|
||||
|
||||
.ͼ2 .cm-gutters {
|
||||
background-color: #FFFFFF;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #DFE4ED;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #DFE4ED;
|
||||
}
|
||||
|
||||
.ant-select-focused.ant-select-outlined:not(.ant-select-disabled):not(.ant-select-customize-input):not(.ant-pagination-size-changer) .ant-select-selector {
|
||||
box-shadow: none;
|
||||
}
|
||||
.ant-select-dropdown .ant-select-item {
|
||||
color: #212332;
|
||||
}
|
||||
.ant-select-dropdown .ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
|
||||
color: #212332;
|
||||
background: #F6F6F6;
|
||||
}
|
||||
77
web/src/views/Home/components/ApiLineCard.tsx
Normal file
77
web/src/views/Home/components/ApiLineCard.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:17:05
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-10 11:35:52
|
||||
*/
|
||||
/**
|
||||
* Line Chart Card Component
|
||||
* Displays time-series data with ECharts line chart
|
||||
* Supports multiple series and date range selection
|
||||
*/
|
||||
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Select } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import Card from './Card'
|
||||
import { getWorkspaceApiStatistics } from '@/api/application'
|
||||
import LineChart, { type ChartData } from '@/components/Charts/LineChart'
|
||||
|
||||
const seriesList = ['total_calls', 'app_calls', 'service_calls']
|
||||
const ApiLineCard: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const options = [
|
||||
{ label: t('dashboard.lastDays', { days: 7 }), value: 7 },
|
||||
{ label: t('dashboard.lastDays', { days: 30 }), value: 30 },
|
||||
{ label: t('dashboard.lastDays', { days: 90 }), value: 90 },
|
||||
{ label: t('dashboard.lastHalfYear'), value: 180 },
|
||||
{ label: t('dashboard.lastYear'), value: 365 },
|
||||
]
|
||||
const [chartData, setChartData] = useState<ChartData[]>([])
|
||||
const [query, setQuery] = useState(7)
|
||||
|
||||
useEffect(() => {
|
||||
getWorkspaceApiStatistics({
|
||||
start_date: dayjs().subtract(query - 1, 'd').startOf('d').valueOf(),
|
||||
end_date: dayjs().endOf('d').valueOf(),
|
||||
})
|
||||
.then(res => {
|
||||
setChartData(res as ChartData[])
|
||||
})
|
||||
}, [query])
|
||||
|
||||
/** Format series list for legend */
|
||||
const formatSeriesList = () => {
|
||||
const list: Record<string, string> = {}
|
||||
seriesList.forEach(key => {
|
||||
list[key] = t(`dashboard.${key}`)
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t(`dashboard.apiCallTrends`)}
|
||||
headerOperate={
|
||||
<Select
|
||||
value={query}
|
||||
options={options}
|
||||
onChange={(value) => setQuery(value)}
|
||||
className="rb:w-35!"
|
||||
/>
|
||||
}
|
||||
className={`rb:pb-6`}
|
||||
>
|
||||
<LineChart
|
||||
chartData={chartData}
|
||||
seriesList={formatSeriesList()}
|
||||
height={239}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ApiLineCard
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:27:28
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:27:28
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-26 11:16:09
|
||||
*/
|
||||
/**
|
||||
* Card Component
|
||||
@@ -21,15 +21,19 @@ interface CardProps {
|
||||
title: string;
|
||||
headerOperate?: ReactNode;
|
||||
className?: string;
|
||||
bodyClassName?: string;
|
||||
}
|
||||
|
||||
const Card: FC<CardProps> = ({ children, title, headerOperate, className }) => {
|
||||
const Card: FC<CardProps> = ({ children, title, headerOperate, className, bodyClassName }) => {
|
||||
return (
|
||||
<RbCard
|
||||
headerType="borderless"
|
||||
title={title}
|
||||
extra={headerOperate}
|
||||
className={`rb:h-full! ${className}`}
|
||||
variant="borderless"
|
||||
className={`rb:h-full! rb:bg-[#FFFFFF]! ${className}`}
|
||||
bodyClassName={bodyClassName}
|
||||
headerClassName="rb:min-h-[58px]!"
|
||||
>
|
||||
{children}
|
||||
</RbCard>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:17:05
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:18:32
|
||||
* @Last Modified time: 2026-02-10 11:59:10
|
||||
*/
|
||||
/**
|
||||
* Line Chart Card Component
|
||||
@@ -10,21 +10,18 @@
|
||||
* Supports multiple series and date range selection
|
||||
*/
|
||||
|
||||
import { type FC, useRef } from 'react'
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Select } from 'antd'
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import Empty from '@/components/Empty'
|
||||
import Card from './Card'
|
||||
import AreaLineChart, { type ChartData } from '@/components/Charts/AreaLineChart';
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface LineCardProps {
|
||||
chartData: Array<Record<string, string | number>>;
|
||||
chartData: ChartData[];
|
||||
limit: number;
|
||||
onChange: (value: string, type: string) => void;
|
||||
type: string;
|
||||
@@ -32,30 +29,8 @@ interface LineCardProps {
|
||||
seriesList: string[];
|
||||
}
|
||||
|
||||
/** ECharts series configuration */
|
||||
const SeriesConfig = {
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3
|
||||
},
|
||||
showSymbol: false,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top'
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [220, 302, 181, 234, 210, 290, 150]
|
||||
}
|
||||
/** Chart color palette */
|
||||
const Colors = ['#FFB048', '#4DA8FF', '#155EEF']
|
||||
|
||||
const LineCard: FC<LineCardProps> = ({ chartData, limit, onChange, type, className, seriesList }) => {
|
||||
const { t } = useTranslation()
|
||||
const chartRef = useRef<ReactEcharts>(null);
|
||||
const options = [
|
||||
{ label: t('dashboard.lastDays', { days: 7 }), value: 7 },
|
||||
{ label: t('dashboard.lastDays', { days: 30 }), value: 30 },
|
||||
@@ -63,39 +38,15 @@ const LineCard: FC<LineCardProps> = ({ chartData, limit, onChange, type, classNa
|
||||
{ label: t('dashboard.lastHalfYear'), value: 180 },
|
||||
{ label: t('dashboard.lastYear'), value: 365 },
|
||||
]
|
||||
|
||||
/** Generate series data with gradient colors */
|
||||
const getSeries = () => {
|
||||
const list = seriesList.map((key, index) => {
|
||||
return {
|
||||
...SeriesConfig,
|
||||
name: t(`dashboard.${key}`),
|
||||
data: chartData.map(vo => vo[key]),
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: Colors[index]
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#FFFFFF'
|
||||
}
|
||||
])
|
||||
},
|
||||
}
|
||||
/** Format series list for legend */
|
||||
const formatSeriesList = () => {
|
||||
const list: Record<string, string> = {}
|
||||
seriesList.forEach(key => {
|
||||
list[key] = t(`dashboard.${key}`)
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
/** Format series list for legend */
|
||||
const formatSeriesList = () => {
|
||||
return seriesList.map(key => ({
|
||||
...SeriesConfig,
|
||||
name: t(`dashboard.${key}`),
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -104,79 +55,18 @@ const LineCard: FC<LineCardProps> = ({ chartData, limit, onChange, type, classNa
|
||||
<Select
|
||||
value={limit}
|
||||
options={options}
|
||||
onChange={(value) => onChange(String(value), type)}
|
||||
style={{ width: '150px' }}
|
||||
onChange={(value) => onChange(String(value), type)}
|
||||
className="rb:w-35!"
|
||||
/>
|
||||
}
|
||||
className={`rb:pb-6 ${className}`}
|
||||
>
|
||||
{chartData && chartData.length > 0 ? (
|
||||
<ReactEcharts
|
||||
ref={chartRef}
|
||||
option={{
|
||||
color: Colors,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
extraCssText: 'box-shadow: 0px 2px 6px 0px rgba(33,35,50,0.16); border-radius: 8px;',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
crossStyle: {
|
||||
color: '#5F6266',
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#5F6266',
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: formatSeriesList(),
|
||||
textStyle: {
|
||||
color: '#5B6167',
|
||||
fontFamily: 'PingFangSC, PingFang SC',
|
||||
lineHeight: 16,
|
||||
},
|
||||
itemGap: 32,
|
||||
padding: 0,
|
||||
itemWidth: 26,
|
||||
itemHeight: 10,
|
||||
left: 'center'
|
||||
},
|
||||
grid: {
|
||||
left: 4,
|
||||
right: '3%',
|
||||
bottom: 0,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: chartData.map(item => formatDateTime(item.created_at, 'DD/MM')),
|
||||
boundaryGap: false,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
color: '#A8A9AA',
|
||||
fontFamily: 'PingFangSC, PingFang SC',
|
||||
align: 'right',
|
||||
lineHeight: 17,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#EBEBEB',
|
||||
}
|
||||
},
|
||||
},
|
||||
series: getSeries()
|
||||
}}
|
||||
style={{ height: '265px', width: '100%', minWidth: '100%', boxSizing: 'border-box' }}
|
||||
opts={{ renderer: 'canvas' }}
|
||||
notMerge={true}
|
||||
lazyUpdate={true}
|
||||
/>
|
||||
) : <Empty size={120} className="rb:mt-12 rb:mb-20.25" />}
|
||||
<AreaLineChart
|
||||
xAxisKey="date"
|
||||
chartData={chartData}
|
||||
seriesList={formatSeriesList()}
|
||||
height={239}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,137 +1,39 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:16:45
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:16:45
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-10 11:57:35
|
||||
*/
|
||||
/**
|
||||
* Pie Chart Card Component
|
||||
* Displays knowledge base type distribution with ECharts donut chart
|
||||
*/
|
||||
|
||||
import { type FC, useRef, useEffect } from 'react'
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
|
||||
import Card from './Card'
|
||||
import Loading from '@/components/Empty/Loading'
|
||||
import Empty from '@/components/Empty'
|
||||
import PieChart, { type ChartData } from '@/components/Charts/PieChart'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface PieCardProps {
|
||||
chartData: Array<Record<string, string | number>>;
|
||||
chartData: ChartData[];
|
||||
loading: boolean;
|
||||
}
|
||||
/** Chart color palette */
|
||||
const Colors = ['#155EEF', '#31E8FF', '#AD88FF', '#FFB048', '#4DA8FF', '#03BDFF']
|
||||
|
||||
const PieCard: FC<PieCardProps> = ({ chartData, loading }) => {
|
||||
const { t } = useTranslation()
|
||||
const chartRef = useRef<ReactEcharts>(null);
|
||||
const resizeScheduledRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
if (chartRef.current && !resizeScheduledRef.current) {
|
||||
resizeScheduledRef.current = true
|
||||
requestAnimationFrame(() => {
|
||||
chartRef.current?.getEchartsInstance().resize();
|
||||
resizeScheduledRef.current = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(handleResize)
|
||||
const chartElement = chartRef.current?.getEchartsInstance().getDom().parentElement
|
||||
if (chartElement) {
|
||||
resizeObserver.observe(chartElement)
|
||||
}
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [chartData])
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('dashboard.knowledgeBaseTypeDistribution')}
|
||||
>
|
||||
{loading
|
||||
? <Loading size={249} />
|
||||
: !chartData || chartData.length === 0
|
||||
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
|
||||
: <ReactEcharts
|
||||
option={{
|
||||
color: Colors,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
textStyle: {
|
||||
color: '#5B6167',
|
||||
fontSize: 12,
|
||||
width: 27,
|
||||
height: 16,
|
||||
},
|
||||
formatter: '{d}%',
|
||||
padding: [8, 5],
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderColor: '#DFE4ED',
|
||||
extraCssText: 'width: 36px; height: 36px; box-shadow: 0px 2px 4px 0px rgba(33,35,50,0.12);border-radius: 36px;'
|
||||
},
|
||||
legend: {
|
||||
right: 20 ,
|
||||
top: 'middle',
|
||||
padding: 0,
|
||||
itemWidth: 12,
|
||||
itemHeight: 12,
|
||||
borderRadius: 2,
|
||||
orient: 'vertical',
|
||||
textStyle: {
|
||||
color: '#5B6167',
|
||||
fontFamily: 'PingFangSC, PingFang SC',
|
||||
lineHeight: 16,
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: ['60%', '100%'],
|
||||
avoidLabelOverlap: false,
|
||||
percentPrecision: 0,
|
||||
padAngle: 4,
|
||||
width: 200,
|
||||
height: 200,
|
||||
left: '10%',
|
||||
top: 'middle',
|
||||
itemStyle: {
|
||||
borderRadius: 0
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#212332',
|
||||
formatter: '{d}%\n{b}',
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: chartData
|
||||
}
|
||||
]
|
||||
}}
|
||||
style={{ height: '265px', width: '100%', minWidth: '400px' }}
|
||||
notMerge={true}
|
||||
lazyUpdate={true}
|
||||
/>
|
||||
? <Loading size={249} />
|
||||
: <PieChart chartData={chartData} />
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:16:38
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:16:38
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-11 14:57:35
|
||||
*/
|
||||
/**
|
||||
* Quick Operation Component
|
||||
@@ -11,15 +11,16 @@
|
||||
*/
|
||||
|
||||
import { type FC } from 'react'
|
||||
import clsx from 'clsx';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Flex } from 'antd';
|
||||
|
||||
import Card from './Card';
|
||||
import applicationIcon from '@/assets/images/menu/application_active.svg';
|
||||
import knowledgeIcon from '@/assets/images/menu/knowledge_active.svg';
|
||||
import memoryConversationIcon from '@/assets/images/menu/memoryConversation_active.svg';
|
||||
import applicationIcon from '@/assets/images/home/application.svg';
|
||||
import knowledgeIcon from '@/assets/images/home/knowledge.svg';
|
||||
import memoryConversationIcon from '@/assets/images/home/memoryConversation.svg';
|
||||
import helpCenterIcon from '@/assets/images/menu/helpCenter_active.svg'
|
||||
import arrowTopRight from '@/assets/images/home/arrow_top_right.svg';
|
||||
|
||||
/** Quick operation items configuration */
|
||||
const quickOperations = [
|
||||
@@ -29,6 +30,13 @@ const quickOperations = [
|
||||
{ key: 'helpCenter', url: '' },
|
||||
]
|
||||
|
||||
const bgStyleList = [
|
||||
'rb:bg-[rgba(21,94,239,0.1)]',
|
||||
'rb:bg-[rgba(156,111,255,0.1)]',
|
||||
'rb:bg-[rgba(255,176,72,0.1)]',
|
||||
'rb:bg-[rgba(77,168,255,0.1)]'
|
||||
]
|
||||
|
||||
/** Icon mapping for quick operations */
|
||||
const quickOperationIcons: {[key: string]: string | undefined} = {
|
||||
createNewApplication: applicationIcon,
|
||||
@@ -62,17 +70,19 @@ const QuickOperation:FC = () => {
|
||||
return (
|
||||
<Card
|
||||
title={t('dashboard.quickOperation')}
|
||||
bodyClassName="rb:pt-0! rb:pb-[14px]! rb:px-4!"
|
||||
>
|
||||
<div className="rb:grid rb:grid-cols-4 rb:gap-4">
|
||||
{quickOperations.map(item => (
|
||||
<div key={item.key} className="rb:rounded-lg rb:p-[20px_16px] rb:border rb:border-[#DFE4ED] rb:cursor-pointer rb:hover:border-[#155EEF]" onClick={() => handleJump(item.url)}>
|
||||
<div className="rb:flex rb:justify-between">
|
||||
<img className="rb:w-8 rb:h-8" src={quickOperationIcons[item.key]} />
|
||||
<img className="rb:w-4 rb:h-4" src={arrowTopRight} />
|
||||
<div className="rb:grid rb:grid-cols-1 rb:gap-3">
|
||||
{quickOperations.map((item, index) => (
|
||||
<Flex key={item.key} align="center" gap={20} className={clsx("rb:relative rb:rounded-xl rb:py-2! rb:px-3! rb:cursor-pointer", bgStyleList[index])} onClick={() => handleJump(item.url)}>
|
||||
<div className="rb:size-8 rb:rounded-lg rb:p-1 rb:bg-[#FFFFFF]">
|
||||
<img className="rb:size-6" src={quickOperationIcons[item.key]} />
|
||||
</div>
|
||||
<div className="rb:mt-6 rb:text-[#212332] rb:text-[16px] rb:leading-5 rb:font-medium">{t(`dashboard.${item.key}`)}</div>
|
||||
<div className="rb:mt-2 rb:text-[#5B6167] rb:text-[12px] rb:font-regular">{t(`dashboard.${item.key}Desc`)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="rb:text-[14px] rb:leading-5 rb:font-medium">{t(`dashboard.${item.key}`)}</div>
|
||||
<div className="rb:mt-0.5 rb:text-[#5B6167] rb:text-[12px] rb:font-regular">{t(`dashboard.${item.key}Desc`)}</div>
|
||||
</div>
|
||||
</Flex>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:15:33
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:15:33
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-11 14:48:31
|
||||
*/
|
||||
/**
|
||||
* Recent Activity Component
|
||||
@@ -13,7 +13,7 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Skeleton } from 'antd';
|
||||
import { Skeleton, Flex } from 'antd';
|
||||
|
||||
import chunkCountIcon from '@/assets/images/home/chunk_count.svg';
|
||||
import statementsCountIcon from '@/assets/images/home/statements_count.svg';
|
||||
@@ -79,30 +79,31 @@ const RecentActivity:FC = () => {
|
||||
return (
|
||||
<Card
|
||||
title={t('dashboard.recentMemoryActivities')}
|
||||
bodyClassName="rb:pt-0! rb:pb-[14px]! rb:px-4!"
|
||||
>
|
||||
{loading
|
||||
? <Skeleton />
|
||||
: !recentActivities || Object.keys(recentActivities).length === 0
|
||||
? <Empty url={activityEmpty} subTitle={t('dashboard.activityEmpty')} size={120} className="rb:mt-11.25 rb:mb-20.25" />
|
||||
: activityList.map((item, index) => (
|
||||
<div key={item.key} className={clsx("rb:flex rb:justify-between rb:items-center rb:not-italic", {
|
||||
'rb:mt-6': index !== 0
|
||||
})}>
|
||||
<div className="rb:flex rb:items-center rb:text-[#060419] rb:text-[16px] rb:font-medium">
|
||||
<img className="rb:w-10 rb:h-10 rb:mr-4" src={item.icon} />
|
||||
<div>
|
||||
{t(`dashboard.${item.key}`)}
|
||||
<div className="rb:text-[#5B6167] rb:text-[14px] rb:font-normal">
|
||||
{item.key === 'triplet_count'
|
||||
? t(`dashboard.${item.key}_desc`, { entities_count: recentActivities.triplet_entities_count, relations_count: recentActivities.triplet_relations_count })
|
||||
: t(`dashboard.${item.key}_desc`, { count: recentActivities[item.key as keyof RecentActivities] })
|
||||
}
|
||||
: <Flex vertical gap={24} justify="space-around">
|
||||
{activityList.map((item) => (
|
||||
<Flex key={item.key} align="center" justify="space-between" className={clsx("rb:not-italic")}>
|
||||
<Flex align="center" gap={20}>
|
||||
<img className="rb:size-10" src={item.icon} />
|
||||
<div>
|
||||
<div className="rb:text-[16px] rb:leading-5.5 rb:font-medium">{t(`dashboard.${item.key}`)}</div>
|
||||
<div className="rb:text-[#7B8085] rb:text-[14px] rb:font-regular rb:mt-1 rb:leading-4.5">
|
||||
{item.key === 'triplet_count'
|
||||
? t(`dashboard.${item.key}_desc`, { entities_count: recentActivities.triplet_entities_count, relations_count: recentActivities.triplet_relations_count })
|
||||
: t(`dashboard.${item.key}_desc`, { count: recentActivities[item.key as keyof RecentActivities] })
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rb:text-[#5F6266] rb:text-right rb:whitespace-nowrap">{data?.latest_relative || ''}</div>
|
||||
</div>
|
||||
))
|
||||
</Flex>
|
||||
<div className="rb:text-[#7B8085] rb:text-right rb:whitespace-nowrap">{data?.latest_relative || ''}</div>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:15:04
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:15:04
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-26 11:15:15
|
||||
*/
|
||||
/**
|
||||
* Tag List Component
|
||||
@@ -12,13 +12,24 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Skeleton } from 'antd';
|
||||
import { Skeleton, Flex } from 'antd';
|
||||
|
||||
import tagEmpty from '@/assets/images/home/tagEmpty.svg'
|
||||
import Empty from '@/components/Empty';
|
||||
import Card from './Card';
|
||||
import { getHotMemoryTags } from '@/api/memory';
|
||||
|
||||
const btnStyleList = [
|
||||
'rb:bg-[rgba(21,94,239,0.06)]',
|
||||
'rb:bg-[rgba(33,35,50,0.06)]',
|
||||
'rb:bg-[rgba(156,111,255,0.06)]'
|
||||
]
|
||||
const numStyleList = [
|
||||
'rb:bg-[#155EEF]',
|
||||
'rb:bg-[#212332]',
|
||||
'rb:bg-[#9C6FFF]'
|
||||
]
|
||||
|
||||
const TagList:FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
@@ -38,23 +49,24 @@ const TagList:FC = () => {
|
||||
return (
|
||||
<Card
|
||||
title={t('dashboard.popularMemoryTags')}
|
||||
bodyClassName='rb:overflow-hidden! rb:pt-0! rb:pb-4! rb:pl-4! rb:pr-3.25!'
|
||||
className="rb:min-h-[calc(100vh-744px)]"
|
||||
>
|
||||
{loading
|
||||
? <Skeleton />
|
||||
: !tagList || tagList.length === 0
|
||||
? <Empty url={tagEmpty} title={t('dashboard.activityEmpty')} size={120} className="rb:mt-9 rb:mb-20.25" />
|
||||
: <div className="rb:gap-3 rb:flex rb:flex-wrap">
|
||||
: <Flex wrap className="rb:gap-x-3! rb:gap-y-2.5!">
|
||||
{tagList.map((item, index) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className={clsx("rb:pt-1.5 rb:pb-1.5 rb:pr-5.75 rb:pl-5 rb:border rb:leading-5 rb:bg-white rb:rounded-[17px]", {
|
||||
'rb:border-[rgba(21,94,239,0.4)] rb:text-[#155EEF]': index % 3 === 0,
|
||||
'rb:border-[rgba(255,138,76,0.4)] rb:text-[#FF5D34]': index % 3 === 1,
|
||||
'rb:border-[rgba(54,159,33,0.4)] rb:text-[#369F21]': index % 3 === 2,
|
||||
})}
|
||||
>{item.name} {item.frequency}</div>
|
||||
className={clsx("rb:rounded-[17px] rb:py-1.5 rb:pl-3 rb:pr-2", btnStyleList[index % 3])}
|
||||
>
|
||||
{item.name}
|
||||
<span className={clsx('rb:px-2 rb:py-0.5 rb:rounded-[10px] rb:text-[#FFFFFF] rb:text-[12px] rb:font-bold rb:font-[MiSans-Demibold] rb:ml-2', numStyleList[index % 3])}>{item.frequency}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Flex>
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
|
||||
112
web/src/views/Home/components/TopCardList.tsx
Normal file
112
web/src/views/Home/components/TopCardList.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:28:07
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-11 14:57:55
|
||||
*/
|
||||
/**
|
||||
* Top Card List Component
|
||||
* Displays dashboard summary cards for key metrics
|
||||
* Shows total memory capacity, applications, knowledge bases, and API calls
|
||||
*/
|
||||
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import clsx from 'clsx';
|
||||
import { Flex } from 'antd';
|
||||
|
||||
import totalMemoryCapacity from '@/assets/images/home/totalMemoryCapacity.svg';
|
||||
import userMemory from '@/assets/images/home/userMemory.svg';
|
||||
import knowledgeBaseCount from '@/assets/images/home/knowledgeBaseCount.svg';
|
||||
import apiCallCount from '@/assets/images/home/apiCallCount.svg';
|
||||
import type { DashboardData } from '../index'
|
||||
|
||||
/** Card configuration with styling */
|
||||
const list = [
|
||||
{
|
||||
key: 'totalMemoryCapacity',
|
||||
icon: totalMemoryCapacity,
|
||||
// value: '45,678',
|
||||
// trendValue: '12.5%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
background: 'rb:bg-[url("@/assets/images/home/totalMemoryCapacity.png")] rb:bg-cover rb:bg-no-repeat',
|
||||
},
|
||||
{
|
||||
key: 'application',
|
||||
icon: userMemory,
|
||||
// value: '32,145',
|
||||
// trendValue: '12.5%',
|
||||
// trend: 'down',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
// background: 'linear-gradient( 180deg, #F1FBF5 0%, #F9FDFF 100%)',
|
||||
},
|
||||
{
|
||||
key: 'knowledgeBaseCount',
|
||||
icon: knowledgeBaseCount,
|
||||
// value: '13,533',
|
||||
// trendValue: '15.7%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'thisWeek',
|
||||
// background: 'linear-gradient( 180deg, #E6F5FE 0%, #FBFDFF 100%)',
|
||||
},
|
||||
{
|
||||
key: 'apiCallCount',
|
||||
icon: apiCallCount,
|
||||
// value: '856.2k',
|
||||
// trendValue: '23.1%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
// background: 'linear-gradient( 180deg, #F8F6F5 0%, #FAFDFF 100%)',
|
||||
},
|
||||
]
|
||||
/**
|
||||
* Component props
|
||||
* @param data - Dashboard statistics data
|
||||
*/
|
||||
const TopCardList: FC<{data?: DashboardData}> = ({ data }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-3">
|
||||
{list.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
className={`rb:rounded-2xl rb:bg-[#FFFFFF] rb:py-4 rb:px-3 ${item.background || ''}`}
|
||||
>
|
||||
<div className={clsx("rb:text-[12px] rb:leading-4", {
|
||||
'rb:text-[#FFFFFF]': item.key === 'totalMemoryCapacity',
|
||||
'rb:text-[#5B6167]': item.key !== 'totalMemoryCapacity',
|
||||
})}>{t(`dashboard.${item.key}`)}</div>
|
||||
|
||||
<div className={clsx("rb:text-[20px] rb:font-bold rb:leading-7 rb:mt-1 rb:font-[MiSans-Bold]", {
|
||||
'rb:text-[#FFFFFF]': item.key === 'totalMemoryCapacity',
|
||||
// 'rb:text-[#171719]': item.key !== 'totalMemoryCapacity',
|
||||
})}>
|
||||
{data?.[item.key as keyof DashboardData] || 0}
|
||||
</div>
|
||||
|
||||
<Flex align="center" className={clsx('rb:font-medium rb:mt-7.5!', {
|
||||
'rb:text-[#FFFFFF]': item.key === 'totalMemoryCapacity',
|
||||
'rb:text-[#369F21]': item.key !== 'totalMemoryCapacity',
|
||||
})}>
|
||||
0%
|
||||
<div className={clsx("rb:size-3.5 rb:cursor-pointer rb:bg-cover", {
|
||||
"rb:bg-[url('@/assets/images/home/arrow_up.svg')]": item.key === 'totalMemoryCapacity',
|
||||
"rb:bg-[url('@/assets/images/home/arrow_up_success.svg')]": item.key !== 'totalMemoryCapacity',
|
||||
})}></div>
|
||||
</Flex>
|
||||
<div className={clsx("rb:text-[12px] rb:leading-4 rb:mt-0.5", {
|
||||
'rb:text-[#FFFFFF]': item.key === 'totalMemoryCapacity',
|
||||
'rb:text-[#5B6167]': item.key !== 'totalMemoryCapacity',
|
||||
})}>
|
||||
{t('dashboard.comparedToYesterday')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopCardList
|
||||
@@ -1,97 +0,0 @@
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid #DFE4ED;
|
||||
padding: 0;
|
||||
}
|
||||
.header {
|
||||
padding: 20px;
|
||||
line-height: 44px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #5B6167;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #DFE4ED;
|
||||
}
|
||||
.avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 2px 6px 0px rgba(33, 35, 50, 0.1);
|
||||
border-radius: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.avatar img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-family: Gilroy, Gilroy;
|
||||
font-weight: 800;
|
||||
font-size: 28px;
|
||||
color: #212332;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
.content-right {
|
||||
text-align: right;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: #5F6266;
|
||||
line-height: 16px;
|
||||
font-style: normal;
|
||||
row-gap: 4px;
|
||||
}
|
||||
.trend {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
font-style: normal;
|
||||
padding-left: 15px;
|
||||
position: relative;
|
||||
margin-bottom: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
.trend::before {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
.trend.up {
|
||||
color: #369F21;
|
||||
}
|
||||
.trend.up::before {
|
||||
background-image: url('@/assets/images/home/arrow_up_success.svg');
|
||||
}
|
||||
.trend.down {
|
||||
color: #FF5D34;
|
||||
}
|
||||
.trend.down::before {
|
||||
background-image: url('@/assets/images/home/arrow_down.png');
|
||||
}
|
||||
|
||||
.trend-desc {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #155EEF;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:28:07
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:28:07
|
||||
*/
|
||||
/**
|
||||
* Top Card List Component
|
||||
* Displays dashboard summary cards for key metrics
|
||||
* Shows total memory capacity, applications, knowledge bases, and API calls
|
||||
*/
|
||||
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import totalMemoryCapacity from '@/assets/images/home/totalMemoryCapacity.svg';
|
||||
import userMemory from '@/assets/images/home/userMemory.svg';
|
||||
import knowledgeBaseCount from '@/assets/images/home/knowledgeBaseCount.svg';
|
||||
import apiCallCount from '@/assets/images/home/apiCallCount.svg';
|
||||
import styles from './index.module.css'
|
||||
import type { DashboardData } from '../../index'
|
||||
|
||||
/** Card configuration with styling */
|
||||
const list = [
|
||||
{
|
||||
key: 'totalMemoryCapacity',
|
||||
icon: totalMemoryCapacity,
|
||||
// value: '45,678',
|
||||
// trendValue: '12.5%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
background: 'linear-gradient(180deg, #E6EFFE 0%, #F9FDFF 100%)',
|
||||
},
|
||||
{
|
||||
key: 'application',
|
||||
icon: userMemory,
|
||||
// value: '32,145',
|
||||
// trendValue: '12.5%',
|
||||
// trend: 'down',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
background: 'linear-gradient( 180deg, #F1FBF5 0%, #F9FDFF 100%)',
|
||||
},
|
||||
{
|
||||
key: 'knowledgeBaseCount',
|
||||
icon: knowledgeBaseCount,
|
||||
// value: '13,533',
|
||||
// trendValue: '15.7%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'thisWeek',
|
||||
background: 'linear-gradient( 180deg, #E6F5FE 0%, #FBFDFF 100%)',
|
||||
},
|
||||
{
|
||||
key: 'apiCallCount',
|
||||
icon: apiCallCount,
|
||||
// value: '856.2k',
|
||||
// trendValue: '23.1%',
|
||||
// trend: 'up',
|
||||
// trendDesc: 'comparedToYesterday',
|
||||
background: 'linear-gradient( 180deg, #F8F6F5 0%, #FAFDFF 100%)',
|
||||
},
|
||||
]
|
||||
/**
|
||||
* Component props
|
||||
* @param data - Dashboard statistics data
|
||||
*/
|
||||
const TopCardList: FC<{data?: DashboardData}> = ({ data }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="rb:grid rb:grid-cols-4 rb:gap-4">
|
||||
{list.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
className={styles.card}
|
||||
style={{
|
||||
background: item.background,
|
||||
}}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.avatar}><img src={item.icon} /></div>
|
||||
<div className={styles.headerTitle}>{t(`dashboard.${item.key}`)}</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.content}>
|
||||
{data?.[item.key as keyof DashboardData] || 0}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopCardList
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:12:43
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:26:04
|
||||
* @Last Modified time: 2026-02-10 11:57:58
|
||||
*/
|
||||
/**
|
||||
* Home Dashboard Page
|
||||
@@ -19,6 +19,7 @@ import { getDashboardData, getMemoryIncrement, getKbTypes } from '@/api/memory';
|
||||
import RecentActivity from './components/RecentActivity'
|
||||
import TagList from './components/TagList'
|
||||
import QuickOperation from './components/QuickOperation'
|
||||
import ApiLineCard from './components/ApiLineCard'
|
||||
|
||||
/**
|
||||
* Dashboard statistics data
|
||||
@@ -120,42 +121,39 @@ const Home = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:pb-6">
|
||||
<TopCardList data={dashboardData} />
|
||||
|
||||
<Row className="rb:mt-4" gutter={16}>
|
||||
<Col span={12}>
|
||||
<LineCard
|
||||
chartData={memoryIncrement}
|
||||
limit={limit}
|
||||
onChange={handleRangeChange}
|
||||
type="memoryGrowthTrend"
|
||||
seriesList={['total_num']}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<PieCard
|
||||
loading={loading.knowledgeTypeDistribution}
|
||||
chartData={knowledgeTypeDistribution}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="rb:mt-4" gutter={16}>
|
||||
<Col span={12}>
|
||||
<RecentActivity />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<TagList />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="rb:mt-4" gutter={16}>
|
||||
<Col span={24}>
|
||||
<QuickOperation />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<Row gutter={[12, 12]}>
|
||||
<Col span={8}>
|
||||
<TopCardList data={dashboardData} />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<LineCard
|
||||
chartData={memoryIncrement}
|
||||
limit={limit}
|
||||
onChange={handleRangeChange}
|
||||
type="memoryGrowthTrend"
|
||||
seriesList={['total_num']}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<ApiLineCard
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<PieCard
|
||||
loading={loading.knowledgeTypeDistribution}
|
||||
chartData={knowledgeTypeDistribution}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<RecentActivity />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<QuickOperation />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<TagList />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user