Skip to content

Commit 10b6c98

Browse files
committed
feat(ui): enhance resource management in ListSpaceSidebar and KnowledgeBaseList
- Added support for displaying shared knowledge bases in the ListSpaceSidebar, allowing users to see resources shared with them. - Updated the KnowledgeBaseList to include a section for shared knowledge bases, improving visibility and access to collaborative resources. - Enhanced internationalization support by adding relevant translations for shared knowledge bases in both English and Chinese. - Refactored the agent editor modal to categorize knowledge bases into 'My Knowledge Bases' and 'Collaborative Knowledge Bases', streamlining user selection and interaction. This update improves the user experience by providing clearer access to shared resources and enhancing the overall functionality of the knowledge management system.
1 parent 29c790b commit 10b6c98

File tree

5 files changed

+193
-35
lines changed

5 files changed

+193
-35
lines changed

frontend/src/components/ListSpaceSidebar.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</div>
2121
<span v-if="countAll !== undefined" class="item-count">{{ countAll }}</span>
2222
</div>
23-
<!-- 资源列表模式:我的 + 空间列表 -->
23+
<!-- 资源列表模式:我的 + 共享给我 + 空间列表 -->
2424
<template v-if="mode === 'resource'">
2525
<div
2626
class="sidebar-item"
@@ -33,6 +33,18 @@
3333
</div>
3434
<span v-if="countMine !== undefined" class="item-count">{{ countMine }}</span>
3535
</div>
36+
<div
37+
v-if="countShared !== undefined && countShared > 0"
38+
class="sidebar-item"
39+
:class="{ active: selected === 'shared' }"
40+
@click="select('shared')"
41+
>
42+
<div class="item-left">
43+
<t-icon name="share" class="item-icon" />
44+
<span class="item-label">{{ $t('listSpaceSidebar.sharedToMe') }}</span>
45+
</div>
46+
<span class="item-count">{{ countShared }}</span>
47+
</div>
3648
<template v-if="organizationsWithCount.length">
3749
<div class="sidebar-section">
3850
<span class="section-title">{{ $t('listSpaceSidebar.spaces') }}</span>
@@ -89,21 +101,23 @@ import { useOrganizationStore } from '@/stores/organization'
89101
90102
const props = withDefaults(
91103
defineProps<{
92-
/** resource = 知识库/智能体(全部+我的+空间列表);organization = 共享空间(全部+我创建的+我加入的) */
104+
/** resource = 知识库/智能体(全部+我的+共享给我+空间列表);organization = 共享空间(全部+我创建的+我加入的) */
93105
mode?: 'resource' | 'organization'
94106
modelValue: string
95107
/** 全部数量(可选) */
96108
countAll?: number
97109
/** 我的数量(resource 模式) */
98110
countMine?: number
111+
/** 共享给我的数量(resource 模式) */
112+
countShared?: number
99113
/** 各空间下的数量(resource 模式),key 为 organization_id */
100114
countByOrg?: Record<string, number>
101115
/** 我创建的数量(organization 模式) */
102116
countCreated?: number
103117
/** 我加入的数量(organization 模式) */
104118
countJoined?: number
105119
}>(),
106-
{ mode: 'resource', countAll: undefined, countMine: undefined, countByOrg: () => ({}), countCreated: undefined, countJoined: undefined }
120+
{ mode: 'resource', countAll: undefined, countMine: undefined, countShared: undefined, countByOrg: () => ({}), countCreated: undefined, countJoined: undefined }
107121
)
108122
109123
const emit = defineEmits<{

frontend/src/i18n/locales/en-US.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default {
1818
title: 'Filter',
1919
all: 'All',
2020
mine: 'Mine',
21+
sharedToMe: 'Collaborative',
2122
spaces: 'Spaces'
2223
},
2324
knowledgeBase: {
@@ -387,6 +388,9 @@ export default {
387388
noKnowledgeBase: 'No Knowledge Base',
388389
noKnowledgeBaseDesc: 'Pure model conversation, no knowledge retrieval',
389390
selectKnowledgeBases: 'Select Knowledge Bases',
391+
selectKnowledgeBasesDesc: 'Select knowledge bases to associate (including collaborative ones)',
392+
myKnowledgeBases: 'My Knowledge Bases',
393+
sharedKnowledgeBases: 'Collaborative Knowledge Bases',
390394
retrieveKBOnlyWhenMentioned: 'Retrieve Only When Mentioned',
391395
retrieveKBOnlyWhenMentionedDesc: "Off: auto-retrieve configured KBs; On: retrieve only when user {'@'} mentions",
392396
rerankModel: 'ReRank Model',
@@ -1124,6 +1128,7 @@ export default {
11241128
sharedToMe: 'Shared with me',
11251129
},
11261130
uninitializedBanner: 'Some knowledge bases are not initialized. Configure model information in settings before adding documents.',
1131+
emptyShared: 'No collaborative knowledge bases yet. Join a shared space to access knowledge bases from others.',
11271132
empty: {
11281133
title: 'No knowledge bases yet',
11291134
description: 'Click "Create Knowledge Base" in the top-right corner to add your first one.',

frontend/src/i18n/locales/zh-CN.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default {
1818
title: "筛选",
1919
all: "全部",
2020
mine: "我的",
21+
sharedToMe: "协作",
2122
spaces: "空间",
2223
},
2324
knowledgeBase: {
@@ -926,6 +927,9 @@ export default {
926927
noKnowledgeBase: "不使用知识库",
927928
noKnowledgeBaseDesc: "纯模型对话,不检索知识库",
928929
selectKnowledgeBases: "选择知识库",
930+
selectKnowledgeBasesDesc: "选择要关联的知识库(包括协作知识库)",
931+
myKnowledgeBases: "我的知识库",
932+
sharedKnowledgeBases: "协作知识库",
929933
retrieveKBOnlyWhenMentioned: "仅在 {'@'} 提及时检索",
930934
retrieveKBOnlyWhenMentionedDesc: "关闭:自动检索已配置的知识库,开启:仅当用户 {'@'} 提及时才检索",
931935
rerankModel: "ReRank 模型",
@@ -1517,6 +1521,7 @@ export default {
15171521
},
15181522
uninitializedBanner:
15191523
"部分知识库尚未初始化,需要先在设置中配置模型信息才能添加知识文档",
1524+
emptyShared: "暂无协作知识库,可以加入共享空间获取他人共享的知识库",
15201525
empty: {
15211526
title: "暂无知识库",
15221527
description: '点击左侧快捷操作"新建知识库"按钮创建第一个知识库',

frontend/src/views/agent/AgentEditorModal.vue

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -665,29 +665,49 @@
665665
<div v-if="kbSelectionMode === 'selected'" class="setting-row">
666666
<div class="setting-info">
667667
<label>{{ $t('agent.editor.selectKnowledgeBases') }}</label>
668-
<p class="desc">选择要关联的知识库</p>
668+
<p class="desc">{{ $t('agent.editor.selectKnowledgeBasesDesc') }}</p>
669669
</div>
670670
<div class="setting-control">
671671
<t-select
672672
v-model="formData.config.knowledge_bases"
673673
multiple
674674
:placeholder="$t('agent.editor.selectKnowledgeBases')"
675675
filterable
676+
:min-collapsed-num="3"
676677
>
677-
<t-option
678-
v-for="kb in kbOptions"
679-
:key="kb.value"
680-
:value="kb.value"
681-
:label="kb.label"
682-
>
683-
<div class="kb-option-item">
684-
<span class="kb-option-icon" :class="kb.type === 'faq' ? 'faq-icon' : 'doc-icon'">
685-
<t-icon :name="kb.type === 'faq' ? 'chat-bubble-help' : 'folder'" />
686-
</span>
687-
<span class="kb-option-label">{{ kb.label }}</span>
688-
<span class="kb-option-count">({{ kb.count || 0 }})</span>
689-
</div>
690-
</t-option>
678+
<t-option-group v-if="myKbOptions.length" :label="$t('agent.editor.myKnowledgeBases')">
679+
<t-option
680+
v-for="kb in myKbOptions"
681+
:key="kb.value"
682+
:value="kb.value"
683+
:label="kb.label"
684+
>
685+
<div class="kb-option-item">
686+
<span class="kb-option-icon" :class="kb.type === 'faq' ? 'faq-icon' : 'doc-icon'">
687+
<t-icon :name="kb.type === 'faq' ? 'chat-bubble-help' : 'folder'" />
688+
</span>
689+
<span class="kb-option-label">{{ kb.label }}</span>
690+
<span class="kb-option-count">{{ kb.count || 0 }}</span>
691+
</div>
692+
</t-option>
693+
</t-option-group>
694+
<t-option-group v-if="sharedKbOptions.length" :label="$t('agent.editor.sharedKnowledgeBases')">
695+
<t-option
696+
v-for="kb in sharedKbOptions"
697+
:key="kb.value"
698+
:value="kb.value"
699+
:label="kb.label"
700+
>
701+
<div class="kb-option-item">
702+
<span class="kb-option-icon" :class="kb.type === 'faq' ? 'faq-icon' : 'doc-icon'">
703+
<t-icon :name="kb.type === 'faq' ? 'chat-bubble-help' : 'folder'" />
704+
</span>
705+
<span class="kb-option-label">{{ kb.label }}</span>
706+
<span v-if="kb.orgName" class="kb-option-org">{{ kb.orgName }}</span>
707+
<span class="kb-option-count">{{ kb.count || 0 }}</span>
708+
</div>
709+
</t-option>
710+
</t-option-group>
691711
</t-select>
692712
</div>
693713
</div>
@@ -1048,12 +1068,14 @@ import { listMCPServices, type MCPService } from '@/api/mcp-service';
10481068
import { listSkills, type SkillInfo } from '@/api/skill';
10491069
import { getAgentConfig, getConversationConfig } from '@/api/system';
10501070
import { useUIStore } from '@/stores/ui';
1071+
import { useOrganizationStore } from '@/stores/organization';
10511072
import AgentAvatar from '@/components/AgentAvatar.vue';
10521073
import PromptTemplateSelector from '@/components/PromptTemplateSelector.vue';
10531074
import ModelSelector from '@/components/ModelSelector.vue';
10541075
import AgentShareSettings from '@/components/AgentShareSettings.vue';
10551076
10561077
const uiStore = useUIStore();
1078+
const orgStore = useOrganizationStore();
10571079
10581080
const { t } = useI18n();
10591081
@@ -1072,7 +1094,7 @@ const emit = defineEmits<{
10721094
const currentSection = ref(props.initialSection || 'basic');
10731095
const saving = ref(false);
10741096
const allModels = ref<ModelConfig[]>([]);
1075-
const kbOptions = ref<{ label: string; value: string; type?: 'document' | 'faq'; count?: number }[]>([]);
1097+
const kbOptions = ref<{ label: string; value: string; type?: 'document' | 'faq'; count?: number; shared?: boolean; orgName?: string }[]>([]);
10761098
const mcpOptions = ref<{ label: string; value: string }[]>([]);
10771099
const skillOptions = ref<{ name: string; description: string }[]>([]);
10781100
// 是否允许启用 Skills(取决于后端沙箱是否启用,disabled 时为 false;未请求前为 false 避免闪显)
@@ -1124,6 +1146,10 @@ const allTools = [
11241146
{ value: 'data_schema', label: '查看数据元信息', description: '获取表格文件的元信息', requiresKB: true },
11251147
];
11261148
1149+
// 知识库分组:我的 vs 共享的
1150+
const myKbOptions = computed(() => kbOptions.value.filter(kb => !kb.shared));
1151+
const sharedKbOptions = computed(() => kbOptions.value.filter(kb => kb.shared));
1152+
11271153
// 根据知识库配置动态计算是否有知识库能力
11281154
const hasKnowledgeBase = computed(() => {
11291155
return kbSelectionMode.value !== 'none';
@@ -1635,17 +1661,46 @@ const loadDependencies = async () => {
16351661
allModels.value = models;
16361662
}
16371663
1638-
// 加载知识库列表
1664+
// 加载知识库列表(我的 + 共享的)
16391665
const kbRes: any = await listKnowledgeBases();
1666+
const myKbs: typeof kbOptions.value = [];
16401667
if (kbRes.data) {
1641-
kbOptions.value = kbRes.data.map((kb: any) => ({
1642-
label: kb.name,
1643-
value: kb.id,
1644-
type: kb.type || 'document',
1645-
count: kb.type === 'faq' ? (kb.chunk_count || 0) : (kb.knowledge_count || 0)
1646-
}));
1668+
kbRes.data.forEach((kb: any) => {
1669+
myKbs.push({
1670+
label: kb.name,
1671+
value: kb.id,
1672+
type: kb.type || 'document',
1673+
count: kb.type === 'faq' ? (kb.chunk_count || 0) : (kb.knowledge_count || 0),
1674+
shared: false,
1675+
});
1676+
});
1677+
}
1678+
1679+
// 加载共享给我的知识库
1680+
const sharedKbs: typeof kbOptions.value = [];
1681+
try {
1682+
const sharedList = await orgStore.fetchSharedKnowledgeBases();
1683+
if (sharedList && sharedList.length > 0) {
1684+
const myKbIds = new Set(myKbs.map(kb => kb.value));
1685+
sharedList.forEach((shared: any) => {
1686+
const kb = shared.knowledge_base;
1687+
if (!kb || myKbIds.has(kb.id)) return;
1688+
sharedKbs.push({
1689+
label: kb.name,
1690+
value: kb.id,
1691+
type: kb.type || 'document',
1692+
count: kb.type === 'faq' ? (kb.chunk_count || 0) : (kb.knowledge_count || 0),
1693+
shared: true,
1694+
orgName: shared.org_name,
1695+
});
1696+
});
1697+
}
1698+
} catch (e) {
1699+
console.warn('Failed to load shared knowledge bases', e);
16471700
}
16481701
1702+
kbOptions.value = [...myKbs, ...sharedKbs];
1703+
16491704
// 加载 MCP 服务列表(只加载启用的)
16501705
try {
16511706
const mcpList = await listMCPServices();
@@ -3404,23 +3459,29 @@ const handleSave = async () => {
34043459
.kb-option-item {
34053460
display: flex;
34063461
align-items: center;
3407-
gap: 6px;
3462+
gap: 8px;
3463+
padding: 2px 0;
34083464
}
34093465
34103466
.kb-option-icon {
34113467
display: flex;
34123468
align-items: center;
34133469
justify-content: center;
34143470
flex-shrink: 0;
3415-
font-size: 16px;
3471+
width: 24px;
3472+
height: 24px;
3473+
border-radius: 6px;
3474+
font-size: 14px;
34163475
3417-
// Document KB - Greenish
3476+
// Document KB
34183477
&.doc-icon {
3478+
background: rgba(16, 185, 129, 0.1);
34193479
color: #10b981;
34203480
}
34213481
3422-
// FAQ KB - Blueish
3482+
// FAQ KB
34233483
&.faq-icon {
3484+
background: rgba(0, 82, 217, 0.1);
34243485
color: #0052d9;
34253486
}
34263487
}
@@ -3430,12 +3491,30 @@ const handleSave = async () => {
34303491
overflow: hidden;
34313492
text-overflow: ellipsis;
34323493
white-space: nowrap;
3494+
font-size: 13px;
3495+
color: #1d2129;
3496+
}
3497+
3498+
.kb-option-org {
3499+
flex-shrink: 0;
3500+
font-size: 11px;
3501+
color: #8c8c8c;
3502+
background: #f2f3f5;
3503+
padding: 1px 6px;
3504+
border-radius: 4px;
3505+
max-width: 100px;
3506+
overflow: hidden;
3507+
text-overflow: ellipsis;
3508+
white-space: nowrap;
34333509
}
34343510
34353511
.kb-option-count {
34363512
flex-shrink: 0;
3437-
font-size: 12px;
3438-
color: #999;
3513+
font-size: 11px;
3514+
color: #8c8c8c;
3515+
background: #f2f3f5;
3516+
padding: 1px 6px;
3517+
border-radius: 4px;
34393518
}
34403519
34413520
// FAQ 策略区域样式

0 commit comments

Comments
 (0)