@@ -1,133 +1,109 @@
< script lang = "ts" setup >
import { ref , onMounted } from 'vue' ;
import { useRouter } from 'vue-router' ;
import type {
WorkbenchProjectItem ,
WorkbenchQuickNavItem ,
} from '@vben/common-ui' ;
import { useRouter } from 'vue-router' ;
import { WorkbenchHeader , WorkbenchQuickNav } from '@vben/common-ui' ;
import { preferences } from '@vben/preferences' ;
import { useUserStore } from '@vben/stores' ;
import { openWindow } from '@vben/utils' ;
const userStore = useUserStore ( ) ;
import * as api from '#/api' ;
// 同样,这里的 url 也可以使用以 http 开头的外部链接
const cv : WorkbenchQuickNavItem [ ] = [
{
color : '#3fb27f' ,
authority : [ 'iva' ] ,
icon : 'mdi:video' ,
title : '工作视频分析' ,
url : '/cv/iva ' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'iva-sc' ] ,
icon : 'mdi:video-image' ,
title : '蚕茧视频分析' ,
url : '/cv/iva-sc' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'sca' ] ,
icon : 'mdi:ice-cream' ,
title : '蚕茧仪评分析' ,
url : '/cv/sca' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'sca2' ] ,
icon : 'mdi:ice-pop' ,
title : '蚕茧仪评分析V2' ,
url : '/cv/sca2' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'ysa' ] ,
icon : 'mdi:waveform' ,
title : '催青阶段分析' ,
url : '/cv/ysa' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'ticket' ] ,
icon : 'mdi:ticket-confirmation' ,
title : '仪评指标联分析' ,
url : '/cv/ticket' ,
} ,
{
color : '#3fb27f' ,
authority : [ 'license' ] ,
icon : 'mdi:certificate' ,
title : '证件照片分析' ,
url : '/cv/license' ,
} ,
{
color : '#3fb27f' ,
icon : 'ion:bar-chart-outline' ,
title : '数据标注入口' ,
authority : [ 'user' ] ,
url : 'https://ai.ronsunny.cn:8094/' ,
} ,
] ;
const llm : WorkbenchQuickNavItem [ ] = [
{
color : '#1fdaca' ,
authority : [ 'bot' ] ,
icon : 'mdi:face-agent' ,
title : '通用智能体' ,
url : '/llm/bot' ,
} ,
{
color : '#1fdaca' ,
authority : [ 'report' ] ,
icon : 'mdi:set-center' ,
title : '智农观数阁' ,
url : '/llm/report/report-chat' ,
} ,
{
color : '#1fdaca' ,
authority : [ 'service' ] ,
icon : 'mdi:android-head' ,
title : '灵思智服阁' ,
url : '/llm/service/service-chat' ,
} ,
] ;
const common : WorkbenchQuickNavItem [ ] = [
{
color : '#bf0c2c' ,
authority : [ 'remote' ] ,
icon : 'carbon:workspace' ,
title : '设备远程控制' ,
url : '/remote' ,
} ,
{
color : '#bf0c2c' ,
icon : 'ion:grid-outline' ,
title : 'RAGFlow' ,
authority : [ 'user' ] ,
url : '/out/rag' ,
} ,
] ;
/* -----------------------------
* 类型定义
* ----------------------------- */
interface MenuItem {
id : string ;
name : string ;
path : string ;
type : 'catalog' | 'menu' | 'link ';
meta ? : {
title ? : string ;
icon ? : string ;
link ? : string ;
} ;
children ? : MenuItem [ ] ;
}
/* -----------------------------
* 状态
* ----------------------------- */
const userStore = useUserStore ( ) ;
const router = useRouter ( ) ;
// 这是一个示例方法,实际项目中需要根据实际情况进行调整
const quickNavGroups = ref <
{
title : string ;
items : WorkbenchQuickNavItem [ ] ;
} [ ]
> ( [ ] ) ;
/* -----------------------------
* 工具函数
* ----------------------------- */
function resolveColorByPath ( path : string ) {
if ( path . startsWith ( '/cv' ) ) return '#3fb27f' ;
if ( path . startsWith ( '/llm' ) ) return '#1fdaca' ;
if ( path . startsWith ( '/remote' ) ) return '#bf0c2c' ;
if ( path . startsWith ( '/out' ) ) return '#bf0c2c' ;
return '#6366f1' ;
}
// 🌟 核心:拍平所有 menu / link
function flattenMenu (
nodes : MenuItem [ ] = [ ] ,
result : MenuItem [ ] = [ ] ,
) {
for ( const node of nodes ) {
if ( node . type === 'menu' || node . type === 'link' ) {
result . push ( node ) ;
}
if ( node . children ? . length ) {
flattenMenu ( node . children , result ) ;
}
}
return result ;
}
function mapNodesToQuickNavItems (
nodes : MenuItem [ ] ,
color : string ,
) : WorkbenchQuickNavItem [ ] {
return nodes . map ( ( item ) => {
const isLink = item . type === 'link' ;
return {
title : item . meta ? . title || item . name ,
icon : item . meta ? . icon ,
color ,
url : isLink ? item . meta ? . link : item . path ,
authority : item . path
? [ item . path . split ( '/' ) . filter ( Boolean ) . pop ( ) as string ]
: [ 'user' ] ,
} ;
} ) ;
}
function navTo ( nav : WorkbenchProjectItem | WorkbenchQuickNavItem ) {
if ( nav . url ? . startsWith ( 'http' ) ) {
openWindow ( nav . url ) ;
return ;
}
if ( nav . url ? . startsWith ( '/' ) ) {
router . push ( nav . url ) . catch ( ( error ) => {
console . error ( 'Navigation failed:' , error ) ;
} ) ;
} else {
console . warn ( ` Unknown URL for navigation item: ${ nav . title } -> ${ nav . url } ` ) ;
router . push ( nav . url ) . catch ( console . error ) ;
}
}
/* -----------------------------
* 业务逻辑
* ----------------------------- */
function getGreeting ( ) {
const hour = new Date ( ) . getHours ( ) ;
if ( hour < 6 ) return '凌晨好' ;
@@ -135,6 +111,28 @@ function getGreeting() {
if ( hour < 18 ) return '下午好' ;
return '晚上好' ;
}
async function loadList ( ) {
const res : MenuItem [ ] = await api . getMenuListForUser ( ) ;
if ( ! res ) return ;
quickNavGroups . value = res
. filter ( ( catalog ) => catalog . type === 'catalog' )
. map ( ( catalog ) => {
const color = resolveColorByPath ( catalog . path ) ;
const flatNodes = flattenMenu ( catalog . children || [ ] ) ;
return {
title : catalog . meta ? . title || catalog . name ,
items : mapNodesToQuickNavItems ( flatNodes , color ) ,
} ;
} )
. filter ( ( group ) => group . items . length > 0 ) ;
}
onMounted ( ( ) => {
loadList ( ) ;
} ) ;
< / script >
< template >
@@ -143,38 +141,23 @@ function getGreeting() {
: avatar = "userStore.userInfo?.avatar || preferences.app.defaultAvatar"
>
< template # title >
{ { getGreeting ( ) } } , { { userStore . userInfo ? . username } } ,
{ { getGreeting ( ) } } , { { userStore . userInfo ? . username } } ,
开始您一天的工作吧 !
< / template >
< template # description > 欢迎使用AI实验室 < / template >
< template # description >
欢迎使用 AI 实验室
< / template >
< / WorkbenchHeader >
< div class = "mt-5 flex flex-col lg:flex-row" >
< div
v-for = "group in quickNavGroups"
:key = "group.title"
class = "mt-5 flex flex-col lg:flex-row"
>
< div class = "w-full" >
< WorkbenchQuickNav
:items = "cv "
class = "mt-5 lg:mt-0 "
title = "计算机视觉"
@click ="navTo"
/ >
< / div >
< / div >
< div class = "mt-5 flex flex-col lg:flex-row" >
< div class = "w-full" >
< WorkbenchQuickNav
:items = "llm"
class = "mt-5 lg:mt-0"
title = "大语言模型"
@click ="navTo"
/ >
< / div >
< / div >
< div class = "mt-5 flex flex-col lg:flex-row" >
< div class = "w-full" >
< WorkbenchQuickNav
:items = "common"
class = "mt-5 lg:mt-0"
title = "其他应用"
:t itl e = "group.title "
:items = "group.items "
@click ="navTo"
/ >
< / div >