perf: 调整 WebUI sidebar 顺序
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve">
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="93.7287" y1="106.6446" x2="52.9011" y2="81.6944">
|
||||
<stop offset="0.0969" style="stop-color:#FFB300"/>
|
||||
<stop offset="1" style="stop-color:#FFB300;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_1_);" d="M123.04,107.67c-4.08-4.12-9.38-9.48-14.92-15.06c-0.34,1.29-0.93,2.39-1.79,3.26
|
||||
c-6.43,6.43-25.6-1.99-45.31-19.1c-2.46-2.13-16.74,20.28-14.1,22.87c3.27,3.2,26,17.86,33.78,20.73
|
||||
c22.66,8.35,34.3,0.22,38.24-3.59C121.16,114.61,122.51,111.5,123.04,107.67z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="115.2813" y1="82.3624" x2="14.863" y2="0.8196">
|
||||
<stop offset="0" style="stop-color:#FFB300"/>
|
||||
<stop offset="0.7062" style="stop-color:#FDD835"/>
|
||||
<stop offset="0.8408" style="stop-color:#FDDC36"/>
|
||||
<stop offset="0.9842" style="stop-color:#FFE93A"/>
|
||||
<stop offset="1" style="stop-color:#FFEB3B"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_2_);" d="M25.05,27.7c-1.54-4.81-2.88-11.1-0.4-13.5c7.51-7.3,31.69,4.88,54.25,27.43
|
||||
c22.55,22.55,34.84,46.84,27.43,54.25c-0.07,0.07-0.16,0.13-0.23,0.2c6.13,5.82,12.2,11.6,16.1,15.31
|
||||
c4.87-14.43-6.45-44.11-31.5-69.96c-4.07-4.2-16.12-16.56-26.55-23.56C54.61,11.47,44.19,5.59,32.57,4.2
|
||||
C25,3.29,11.45,5.24,14.25,15.98c0.55,2.12,2.31,7.22,8.15,13.3C23.56,30.49,25.56,29.3,25.05,27.7z"/>
|
||||
<g>
|
||||
<path style="fill:#FDD835;" d="M55.98,42.1l-0.75,20c-0.06,1.53,0.72,2.98,2.04,3.77l16.86,10.11c1.85,1.25,1.46,4.09-0.66,4.79
|
||||
L54.79,85.5c-1.51,0.38-2.69,1.57-3.06,3.08l-4.89,19.93c-0.62,2.15-3.43,2.65-4.76,0.85L31.06,92.91
|
||||
c-0.85-1.26-2.31-1.97-3.83-1.85L7.49,92.61c-2.23,0.07-3.58-2.45-2.28-4.27l12.6-16.19c0.96-1.23,1.16-2.89,0.52-4.31
|
||||
l-7.88-17.57c-0.76-2.1,1.22-4.17,3.35-3.49l18.39,6.95c1.44,0.54,3.05,0.26,4.22-0.74l15.22-13
|
||||
C53.39,38.62,55.96,39.87,55.98,42.1z"/>
|
||||
<g>
|
||||
<path style="fill:#FFFF8D;" d="M46.99,59.33l4.66-12.75c0.28-0.7,0.7-1.93,1.79-1.4c0.86,0.42,0.46,2.43,0.46,2.43l-1.05,11.54
|
||||
c-0.41,4.39-1.6,5.38-3.3,5.49C47.6,64.75,45.65,62.98,46.99,59.33z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#F4B400;" d="M53.89,83.73l14.53-3.13c0.73-0.18,2.01-0.42,1.64-1.58c-0.29-0.91-2.34-0.8-2.34-0.8l-10.97-0.86
|
||||
c-3.21-0.38-5.72,0.14-6.74,1.84C48.65,81.48,49.89,84.32,53.89,83.73z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
@@ -6,12 +6,12 @@
|
||||
"toolUse": "MCP Tools",
|
||||
"config": "Config",
|
||||
"extension": "Extensions",
|
||||
"extensionMarketplace": "Extension Market",
|
||||
"chat": "Chat",
|
||||
"conversation": "Conversations",
|
||||
"sessionManagement": "Session Management",
|
||||
"console": "Console",
|
||||
"alkaid": "Alkaid Lab",
|
||||
"knowledgeBase": "Knowledge Base",
|
||||
"about": "About",
|
||||
"settings": "Settings",
|
||||
"documentation": "Documentation",
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
"dashboard": "统计",
|
||||
"platforms": "消息平台",
|
||||
"providers": "服务提供商",
|
||||
"persona": "人格管理",
|
||||
"persona": "人格设定",
|
||||
"toolUse": "MCP",
|
||||
"extension": "插件",
|
||||
"config": "配置文件",
|
||||
"extension": "插件管理",
|
||||
"extensionMarketplace": "插件市场",
|
||||
"chat": "聊天",
|
||||
"conversation": "对话数据库",
|
||||
"conversation": "对话数据",
|
||||
"sessionManagement": "会话管理",
|
||||
"console": "控制台",
|
||||
"alkaid": "Alkaid",
|
||||
"knowledgeBase": "知识库",
|
||||
"about": "关于",
|
||||
"settings": "设置",
|
||||
"documentation": "官方文档",
|
||||
|
||||
@@ -9,6 +9,7 @@ import {useAuthStore} from '@/stores/auth';
|
||||
import {useCommonStore} from '@/stores/common';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { useI18n } from '@/i18n/composables';
|
||||
import { router } from '@/router';
|
||||
|
||||
// 配置markdown-it,默认安全设置
|
||||
const md = new MarkdownIt({
|
||||
@@ -277,7 +278,7 @@ commonStore.getStartTime();
|
||||
<v-icon>mdi-menu</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<div class="logo-container" :class="{'mobile-logo': $vuetify.display.xs}">
|
||||
<div class="logo-container" :class="{'mobile-logo': $vuetify.display.xs}" @click="$router.push('/about')">
|
||||
<span class="logo-text">Astr<span class="logo-text-light">Bot</span></span>
|
||||
<span class="version-text hidden-xs">{{ botCurrVersion }}</span>
|
||||
</div>
|
||||
@@ -610,6 +611,7 @@ commonStore.getStartTime();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mobile-logo {
|
||||
|
||||
@@ -39,15 +39,25 @@ const sidebarItem: menu[] = [
|
||||
to: '/tool-use'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.config',
|
||||
icon: 'mdi-cog',
|
||||
to: '/config',
|
||||
title: 'core.navigation.persona',
|
||||
icon: 'mdi-heart',
|
||||
to: '/persona'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.extension',
|
||||
icon: 'mdi-puzzle',
|
||||
to: '/extension'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.knowledgeBase',
|
||||
icon: 'mdi-text-box-search',
|
||||
to: '/alkaid/knowledge-base',
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.config',
|
||||
icon: 'mdi-cog',
|
||||
to: '/config',
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.chat',
|
||||
icon: 'mdi-chat',
|
||||
@@ -63,26 +73,11 @@ const sidebarItem: menu[] = [
|
||||
icon: 'mdi-account-group',
|
||||
to: '/session-management'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.persona',
|
||||
icon: 'mdi-heart',
|
||||
to: '/persona'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.console',
|
||||
icon: 'mdi-console',
|
||||
to: '/console'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.alkaid',
|
||||
icon: 'mdi-test-tube',
|
||||
to: '/alkaid'
|
||||
},
|
||||
{
|
||||
title: 'core.navigation.about',
|
||||
icon: 'mdi-information',
|
||||
to: '/about'
|
||||
},
|
||||
// {
|
||||
// title: 'Project ATRI',
|
||||
// icon: 'mdi-grain',
|
||||
|
||||
@@ -66,27 +66,32 @@ const MainRoutes = {
|
||||
path: '/console',
|
||||
component: () => import('@/views/ConsolePage.vue')
|
||||
},
|
||||
// {
|
||||
// name: 'Alkaid',
|
||||
// path: '/alkaid',
|
||||
// component: () => import('@/views/AlkaidPage.vue'),
|
||||
// children: [
|
||||
// {
|
||||
// path: 'knowledge-base',
|
||||
// name: 'KnowledgeBase',
|
||||
// component: () => import('@/views/alkaid/KnowledgeBase.vue')
|
||||
// },
|
||||
// {
|
||||
// path: 'long-term-memory',
|
||||
// name: 'LongTermMemory',
|
||||
// component: () => import('@/views/alkaid/LongTermMemory.vue')
|
||||
// },
|
||||
// {
|
||||
// path: 'other',
|
||||
// name: 'OtherFeatures',
|
||||
// component: () => import('@/views/alkaid/Other.vue')
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
name: 'Alkaid',
|
||||
path: '/alkaid',
|
||||
component: () => import('@/views/AlkaidPage.vue'),
|
||||
children: [
|
||||
{
|
||||
path: 'knowledge-base',
|
||||
name: 'KnowledgeBase',
|
||||
component: () => import('@/views/alkaid/KnowledgeBase.vue')
|
||||
},
|
||||
{
|
||||
path: 'long-term-memory',
|
||||
name: 'LongTermMemory',
|
||||
component: () => import('@/views/alkaid/LongTermMemory.vue')
|
||||
},
|
||||
{
|
||||
path: 'other',
|
||||
name: 'OtherFeatures',
|
||||
component: () => import('@/views/alkaid/Other.vue')
|
||||
}
|
||||
]
|
||||
name: 'KnowledgeBase',
|
||||
path: '/alkaid/knowledge-base',
|
||||
component: () => import('@/views/alkaid/KnowledgeBase.vue')
|
||||
},
|
||||
{
|
||||
name: 'Chat',
|
||||
|
||||
@@ -1,94 +1,27 @@
|
||||
<template>
|
||||
<v-card style="height: 100%;" elevation="0" class="bg-surface">
|
||||
<v-card-text style="padding: 0; height: 100%; overflow-y: hidden;">
|
||||
<div class="about-wrapper">
|
||||
<!-- Hero Section -->
|
||||
<section class="hero-section">
|
||||
<div class="logo-title-container">
|
||||
<div @click="selectedLogo = selectedLogo == 0 ? 1 : 0" class="logo-container">
|
||||
<img v-if="selectedLogo == 0" width="280" src="@/assets/images/logo-waifu.png" alt="AstrBot Logo" class="fade-in">
|
||||
<img v-if="selectedLogo == 1" width="280" src="@/assets/images/logo-normal.svg" alt="AstrBot Logo" class="fade-in">
|
||||
</div>
|
||||
<div class="title-container">
|
||||
<h1 class="text-h2 font-weight-bold">{{ tm('hero.title') }}</h1>
|
||||
<p class="text-subtitle-1" style="color: var(--v-theme-secondaryText);">{{ tm('hero.subtitle') }}</p>
|
||||
<div class="action-buttons">
|
||||
<v-btn @click="open('https://github.com/Soulter/AstrBot')"
|
||||
color="primary" variant="elevated" prepend-icon="mdi-star">
|
||||
{{ tm('hero.starButton') }}
|
||||
</v-btn>
|
||||
<v-btn class="ml-4" @click="open('https://github.com/Soulter/AstrBot/issues')"
|
||||
color="secondary" variant="elevated" prepend-icon="mdi-comment-question">
|
||||
{{ tm('hero.issueButton') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contributors Section -->
|
||||
<section class="contributors-section">
|
||||
<v-container>
|
||||
<v-row justify="center" align="center">
|
||||
<v-col cols="12" md="6" class="pr-md-8 contributors-info">
|
||||
<h2 class="text-h4 font-weight-medium">{{ tm('contributors.title') }}</h2>
|
||||
<p class="mb-4 text-body-1" style="color: var(--v-theme-secondaryText);">
|
||||
{{ tm('contributors.description') }}
|
||||
</p>
|
||||
<p class="text-body-1" style="color: var(--v-theme-secondaryText);">
|
||||
<a href="https://github.com/Soulter/AstrBot/graphs/contributors" class="text-decoration-none custom-link">{{ tm('contributors.viewLink') }}</a>
|
||||
</p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card variant="outlined" class="overflow-hidden" elevation="2">
|
||||
<v-img v-if="useCustomizerStore().uiTheme==='PurpleThemeDark'"
|
||||
alt="Active Contributors of Soulter/AstrBot"
|
||||
src="https://next.ossinsight.io/widgets/official/compose-recent-active-contributors/thumbnail.png?repo_id=575865240&limit=365&image_size=auto&color_scheme=dark">
|
||||
</v-img>
|
||||
<v-img v-else
|
||||
alt="Active Contributors of Soulter/AstrBot"
|
||||
src="https://next.ossinsight.io/widgets/official/compose-recent-active-contributors/thumbnail.png?repo_id=575865240&limit=365&image_size=auto&color_scheme=light">
|
||||
</v-img>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</section>
|
||||
|
||||
<!-- Stats Section -->
|
||||
<section class="stats-section">
|
||||
<v-container>
|
||||
<v-row justify="center" align="center" class="flex-md-row-reverse">
|
||||
<v-col cols="12" md="6" class="pl-md-8 stats-info">
|
||||
<h2 class="text-h4 font-weight-medium">{{ tm('stats.title') }}</h2>
|
||||
|
||||
<div class="license-container mt-8">
|
||||
<img v-bind="props" src="https://www.gnu.org/graphics/agplv3-with-text-100x42.png" style="cursor: pointer;"/>
|
||||
<p class="text-caption mt-2" style="color: var(--v-theme-secondaryText);">{{ tm('stats.license') }}</p>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card variant="outlined" class="overflow-hidden" elevation="2">
|
||||
<v-img v-if="useCustomizerStore().uiTheme==='PurpleThemeDark'"
|
||||
alt="Stars Map of Soulter/AstrBot"
|
||||
src="https://next.ossinsight.io/widgets/official/analyze-repo-stars-map/thumbnail.png?activity=stars&repo_id=575865240&image_size=auto&color_scheme=dark">
|
||||
</v-img>
|
||||
<v-img v-else
|
||||
alt="Stars Map of Soulter/AstrBot"
|
||||
src="https://next.ossinsight.io/widgets/official/analyze-repo-stars-map/thumbnail.png?activity=stars&repo_id=575865240&image_size=auto&color_scheme=light">
|
||||
</v-img>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</section>
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div style="flex-grow: 1; display: flex; align-items: center; justify-content: center; flex-direction: column;">
|
||||
<div style="text-align: center; max-width: 600px;">
|
||||
<h1 class="font-weight-bold">{{ tm('hero.title') }}</h1>
|
||||
<p class="text-subtitle-1" style="color: var(--v-theme-secondaryText);">{{ tm('hero.subtitle') }}</p>
|
||||
<div style="margin-top: 20px; display: flex; justify-content: center;">
|
||||
<v-btn @click="open('https://github.com/Soulter/AstrBot')" color="primary" variant="tonal"
|
||||
prepend-icon="mdi-star">
|
||||
{{ tm('hero.starButton') }}
|
||||
</v-btn>
|
||||
<v-btn class="ml-4" @click="open('https://github.com/Soulter/AstrBot/issues')" color="secondary"
|
||||
variant="tonal" prepend-icon="mdi-comment-question">
|
||||
{{ tm('hero.issueButton') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {useCustomizerStore} from "@/stores/customizer";
|
||||
import { useCustomizerStore } from "@/stores/customizer";
|
||||
import { useModuleI18n } from '@/i18n/composables';
|
||||
|
||||
export default {
|
||||
@@ -97,148 +30,11 @@ export default {
|
||||
const { tm } = useModuleI18n('features/about');
|
||||
return { tm };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedLogo: 0
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
useCustomizerStore,
|
||||
useCustomizerStore,
|
||||
open(url) {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.about-wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
padding: 40px 20px;
|
||||
background: linear-gradient(to right bottom, rgba(255,255,255,0.7), rgba(240,240,250,0.3));
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
max-width: 900px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo-container:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.title-container {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.contributors-section, .stats-section {
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.contributors-section {
|
||||
background-color: var(--v-theme-containerBg, #f9f9fb);
|
||||
}
|
||||
|
||||
.contributors-info, .stats-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.custom-link {
|
||||
display: inline-block;
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
color: var(--v-primary-base);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.custom-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
transform: scaleX(0);
|
||||
height: 2px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: var(--v-primary-base);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.25s ease-out;
|
||||
}
|
||||
|
||||
.custom-link:hover::after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.license-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.logo-title-container {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.license-container {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contributors-section, .stats-section {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-buttons .v-btn + .v-btn {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</script>
|
||||
@@ -1,438 +0,0 @@
|
||||
<script setup>
|
||||
import Graph from "graphology";
|
||||
import Sigma from "sigma";
|
||||
import ForceSupervisor from "graphology-layout-force/worker";
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<v-card style="height: 100%; width: 100%;">
|
||||
<v-card-text class="pa-4" style="height: 100%;">
|
||||
<v-container fluid class="d-flex flex-column" style="height: 100%;">
|
||||
<div style="margin-bottom: 32px;">
|
||||
<h1 class="gradient-text">The Alkaid Project.</h1>
|
||||
<small style="color: #a3a3a3;">{{ tm('features.alkaid.index.sigma.subtitle') }}</small>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
|
||||
<v-btn size="large" :variant="activeTab === 'long-term-memory' ? 'flat' : 'tonal'"
|
||||
:color="activeTab === 'long-term-memory' ? '#9b72cb' : ''" rounded="lg"
|
||||
@click="activeTab = 'long-term-memory'">
|
||||
<v-icon start>mdi-dots-hexagon</v-icon>
|
||||
长期记忆层
|
||||
</v-btn>
|
||||
<v-btn size="large" :variant="activeTab === 'other' ? 'flat' : 'tonal'"
|
||||
:color="activeTab === 'other' ? '#9b72cb' : ''" rounded="lg" @click="activeTab = 'other'">
|
||||
<v-icon start>mdi-dots-horizontal</v-icon>
|
||||
其他
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'long-term-memory'" id="long-term-memory" class="flex-grow-1"
|
||||
style="display: flex; flex-direction: row;">
|
||||
<div id="graph-container" style="flex-grow: 1; width: 100%; border: 1px solid #eee; border-radius: 8px;">
|
||||
</div>
|
||||
<div id="graph-control-panel"
|
||||
style="min-width: 450px; border: 1px solid #eee; border-radius: 8px; padding: 16px; margin-left: 16px;">
|
||||
<div>
|
||||
<span style="color: #333333;">{{ tm('features.alkaid.index.sigma.visualization') }}</span>
|
||||
<div style="margin-top: 8px;">
|
||||
<v-autocomplete v-model="searchUserId" :items="userIdList" variant="outlined"
|
||||
:label="tm('features.alkaid.index.sigma.filterUserId')"></v-autocomplete>
|
||||
<v-btn color="primary" @click="onNodeSelect" variant="tonal" style="margin-top: 8px;">
|
||||
<v-icon start>mdi-magnify</v-icon>
|
||||
{{ tm('features.alkaid.index.sigma.filter') }}
|
||||
</v-btn>
|
||||
<v-btn color="secondary" @click="resetFilter" variant="tonal"
|
||||
style="margin-top: 8px; margin-left: 8px;">
|
||||
<v-icon start>mdi-filter-remove</v-icon>
|
||||
{{ tm('features.alkaid.index.sigma.resetFilter') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
<div style="margin-top: 16px;">
|
||||
<v-btn color="primary" @click="refreshGraph" variant="tonal">
|
||||
<v-icon start>mdi-refresh</v-icon>
|
||||
{{ tm('features.alkaid.index.sigma.refreshGraph') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-divider class="my-4"></v-divider>
|
||||
|
||||
<div v-if="selectedNode" class="mt-4">
|
||||
<h3>{{ tm('features.alkaid.index.sigma.nodeDetails') }}</h3>
|
||||
<v-card variant="outlined" class="mt-2 pa-3">
|
||||
<div v-if="selectedNode.id">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.id') }}:</span>
|
||||
<span>{{ selectedNode.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedNode._label">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.type') }}:</span>
|
||||
<span>{{ selectedNode._label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedNode.name">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.name') }}:</span>
|
||||
<span>{{ selectedNode.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedNode.user_id">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.userId') }}:</span>
|
||||
<span>{{ selectedNode.user_id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedNode.ts">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.timestamp') }}:</span>
|
||||
<span>{{ selectedNode.ts }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedNode.type">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.type') }}:</span>
|
||||
<span>{{ selectedNode.type }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
|
||||
<div v-if="graphStats" class="mt-4">
|
||||
<h3>{{ tm('features.alkaid.index.sigma.graphStats') }}</h3>
|
||||
<v-card variant="outlined" class="mt-2 pa-3">
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.nodeCount') }}:</span>
|
||||
<span>{{ graphStats.nodeCount }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2">{{ tm('features.alkaid.index.sigma.edgeCount') }}:</span>
|
||||
<span>{{ graphStats.edgeCount }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'other'" class="flex-grow-1" style="display: flex; flex-direction: column;">
|
||||
<div class="d-flex align-center justify-center"
|
||||
style="flex-grow: 1; width: 100%; border: 1px solid #eee; border-radius: 8px;">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-tools</v-icon>
|
||||
<p class="text-h6 text-grey ml-4">{{ tm('features.alkaid.index.sigma.inDevelopment') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import AstrBotConfig from '@/components/shared/AstrBotConfig.vue';
|
||||
import WaitingForRestart from '@/components/shared/WaitingForRestart.vue';
|
||||
import { useModuleI18n } from '@/i18n/composables';
|
||||
|
||||
export default {
|
||||
name: 'AlkaidPage',
|
||||
components: {
|
||||
AstrBotConfig,
|
||||
WaitingForRestart
|
||||
},
|
||||
setup() {
|
||||
const { tm } = useModuleI18n('features/alkaid/index');
|
||||
return { tm };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
renderer: null,
|
||||
graph: null,
|
||||
layout: null,
|
||||
activeTab: 'long-term-memory',
|
||||
node_data: [],
|
||||
edge_data: [],
|
||||
searchUserId: null,
|
||||
userIdList: [],
|
||||
selectedNode: null,
|
||||
graphStats: null,
|
||||
nodeColors: {
|
||||
'PhaseNode': '#4CAF50', // 绿色
|
||||
'PassageNode': '#2196F3', // 蓝色
|
||||
'FactNode': '#FF9800', // 橙色
|
||||
'default': '#9C27B0' // 紫色作为默认
|
||||
},
|
||||
edgeColors: {
|
||||
'_include_': '#607D8B',
|
||||
'_related_': '#9E9E9E',
|
||||
'default': '#BDBDBD'
|
||||
},
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initSigma();
|
||||
this.ltmGetGraph();
|
||||
this.ltmGetUserIds();
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.renderer) {
|
||||
this.renderer.kill();
|
||||
}
|
||||
if (this.layout) {
|
||||
this.layout.stop();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeTab(newVal) {
|
||||
if (newVal === 'long-term-memory') {
|
||||
this.$nextTick(() => {
|
||||
if (!this.renderer) {
|
||||
this.initSigma();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.renderer) {
|
||||
this.renderer.kill();
|
||||
this.renderer = null;
|
||||
}
|
||||
if (this.layout) {
|
||||
this.layout.stop();
|
||||
this.layout = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
ltmGetGraph(userId = null) {
|
||||
this.isLoading = true;
|
||||
const params = userId ? { user_id: userId } : {};
|
||||
|
||||
axios.get('/api/plug/alkaid/ltm/graph', { params })
|
||||
.then(response => {
|
||||
let nodes = response.data.data.nodes;
|
||||
let edges = response.data.data.edges;
|
||||
|
||||
this.node_data = nodes;
|
||||
this.edge_data = edges;
|
||||
|
||||
if (this.graph) {
|
||||
this.graph.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
nodes.forEach(node => {
|
||||
const nodeId = node[0];
|
||||
const nodeData = node[1];
|
||||
|
||||
if (!this.graph.hasNode(nodeId)) {
|
||||
const nodeType = nodeData._label || 'default';
|
||||
const color = this.nodeColors[nodeType] || this.nodeColors['default'];
|
||||
|
||||
this.graph.addNode(nodeId, {
|
||||
x: Math.random(),
|
||||
y: Math.random(),
|
||||
size: 5,
|
||||
label: nodeData.name || nodeId.split('_')[0],
|
||||
color: color,
|
||||
originalData: nodeData
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 添加边
|
||||
edges.forEach(edge => {
|
||||
const sourceId = edge[0];
|
||||
const targetId = edge[1];
|
||||
const edgeData = edge[2];
|
||||
|
||||
if (this.graph.hasNode(sourceId) && this.graph.hasNode(targetId)) {
|
||||
const edgeId = `${sourceId}->${targetId}`;
|
||||
const relationType = edgeData.relation_type || 'default';
|
||||
const color = this.edgeColors[relationType] || this.edgeColors['default'];
|
||||
this.graph.addEdge(sourceId, targetId, {
|
||||
size: 1,
|
||||
color: color,
|
||||
originalData: edgeData,
|
||||
label: relationType,
|
||||
type: "line"
|
||||
});
|
||||
} else {
|
||||
console.warn(`Edge ${sourceId} -> ${targetId} has missing nodes.`);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateGraphStats();
|
||||
|
||||
console.log('Graph initialized with', nodes.length, 'nodes and', edges.length, 'edges');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching graph data:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
|
||||
if (this.layout) {
|
||||
this.layout.start();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
ltmGetUserIds() {
|
||||
axios.get('/api/plug/alkaid/ltm/user_ids')
|
||||
.then(response => {
|
||||
this.userIdList = response.data.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching user IDs:', error);
|
||||
});
|
||||
},
|
||||
|
||||
updateGraphStats() {
|
||||
if (this.graph) {
|
||||
this.graphStats = {
|
||||
nodeCount: this.graph.order,
|
||||
edgeCount: this.graph.size
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
refreshGraph() {
|
||||
this.ltmGetGraph(this.searchUserId);
|
||||
},
|
||||
|
||||
onNodeSelect() {
|
||||
console.log('Selected user ID:', this.searchUserId);
|
||||
if (!this.searchUserId || !this.graph) return;
|
||||
|
||||
// 使用API的user_id参数筛选数据
|
||||
this.ltmGetGraph(this.searchUserId);
|
||||
},
|
||||
|
||||
resetFilter() {
|
||||
this.searchUserId = null;
|
||||
this.ltmGetGraph();
|
||||
},
|
||||
|
||||
initSigma() {
|
||||
const container = document.getElementById("graph-container");
|
||||
if (!container) return;
|
||||
|
||||
if (this.renderer) {
|
||||
this.renderer.kill();
|
||||
this.renderer = null;
|
||||
}
|
||||
if (this.layout) {
|
||||
this.layout.stop();
|
||||
this.layout = null;
|
||||
}
|
||||
|
||||
const graph = new Graph({
|
||||
multi: true,
|
||||
});
|
||||
|
||||
const layout = new ForceSupervisor(graph, {
|
||||
isNodeFixed: (_, attr) => attr.highlighted, settings: {
|
||||
gravity: 0.0001,
|
||||
repulsion: 0.001
|
||||
}
|
||||
});
|
||||
layout.start();
|
||||
|
||||
this.layout = layout;
|
||||
this.graph = graph;
|
||||
const renderer = new Sigma(graph, container, {
|
||||
minCameraRatio: 0.01,
|
||||
maxCameraRatio: 2,
|
||||
labelRenderedSizeThreshold: 1,
|
||||
renderLabels: true,
|
||||
renderEdgeLabels: true,
|
||||
labelSize: 14,
|
||||
labelColor: "#333333",
|
||||
});
|
||||
this.renderer = renderer;
|
||||
|
||||
let draggedNode = null;
|
||||
let isDragging = false;
|
||||
|
||||
renderer.on("downNode", (e) => {
|
||||
isDragging = true;
|
||||
draggedNode = e.node;
|
||||
graph.setNodeAttribute(draggedNode, "highlighted", true);
|
||||
if (!renderer.getCustomBBox()) renderer.setCustomBBox(renderer.getBBox());
|
||||
});
|
||||
|
||||
renderer.on("moveBody", ({ event }) => {
|
||||
if (!isDragging || !draggedNode) return;
|
||||
const pos = renderer.viewportToGraph(event);
|
||||
|
||||
graph.setNodeAttribute(draggedNode, "x", pos.x);
|
||||
graph.setNodeAttribute(draggedNode, "y", pos.y);
|
||||
event.preventSigmaDefault();
|
||||
event.original.preventDefault();
|
||||
event.original.stopPropagation();
|
||||
});
|
||||
const handleUp = () => {
|
||||
if (draggedNode) {
|
||||
graph.removeNodeAttribute(draggedNode, "highlighted");
|
||||
}
|
||||
isDragging = false;
|
||||
draggedNode = null;
|
||||
};
|
||||
renderer.on("upNode", handleUp);
|
||||
renderer.on("upStage", handleUp);
|
||||
|
||||
renderer.on("clickNode", (e) => {
|
||||
const nodeId = e.node;
|
||||
const nodeAttributes = graph.getNodeAttributes(nodeId);
|
||||
this.selectedNode = nodeAttributes.originalData;
|
||||
});
|
||||
|
||||
renderer.on("clickStage", () => {
|
||||
this.selectedNode = null;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
getRandomColor() {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gradient-text {
|
||||
background: linear-gradient(74deg, #2abfe1 0, #9b72cb 25%, #b55908 50%, #d93025 100%);
|
||||
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#graph-container {
|
||||
position: relative;
|
||||
background-color: #f2f6f9;
|
||||
overflow: hidden;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
#graph-container:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.memory-header {
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user