Skip to content

Commit 1af5b33

Browse files
committed
feat(ui): 实现普通用户和管理员差异化权限显示
- 知识库API区分:管理员调用getDatabases,普通用户调用getAccessibleDatabases - UI权限控制:隐藏普通用户的顶部导航栏、知识库管理、任务中心等管理员功能 - 路由调整:普通用户可访问/agent页面,移除requiresAdmin限制 - 代码优化:删除AgentSingleView.vue,合并到AgentView.vue统一处理 - 错误处理增强:AppLayout中增加配置和知识库加载的try-catch
1 parent 3eac205 commit 1af5b33

File tree

11 files changed

+248
-481
lines changed

11 files changed

+248
-481
lines changed

web/src/apis/knowledge_api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { apiAdminGet, apiAdminPost, apiAdminPut, apiAdminDelete, apiRequest } from './base'
1+
import { apiGet, apiAdminGet, apiAdminPost, apiAdminPut, apiAdminDelete, apiRequest } from './base'
22

33
/**
44
* 知识库管理API模块
@@ -75,7 +75,7 @@ export const databaseApi = {
7575
* @returns {Promise} - 可访问的知识库列表
7676
*/
7777
getAccessibleDatabases: async () => {
78-
return apiAdminGet('/api/knowledge/databases/accessible')
78+
return apiGet('/api/knowledge/databases/accessible')
7979
}
8080
}
8181

web/src/components/AgentChatComponent.vue

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,22 @@
2727
<div class="chat-header">
2828
<div class="header__left">
2929
<slot name="header-left" class="nav-btn"></slot>
30+
<div
31+
v-if="!chatUIStore.isSidebarOpen && !userStore.isAdmin"
32+
type="button"
33+
class="sidebar-logo-toggle"
34+
@click="toggleSidebar"
35+
>
36+
<img v-if="sidebarLogo" :src="sidebarLogo" alt="logo" class="sidebar-logo-image" />
37+
<PanelLeftOpen v-else class="sidebar-logo-fallback" size="18" />
38+
<div class="sidebar-expand-overlay">
39+
<PanelLeftOpen class="nav-btn-icon" size="18" />
40+
</div>
41+
</div>
3042
<div
3143
type="button"
3244
class="agent-nav-btn"
33-
v-if="!chatUIStore.isSidebarOpen"
45+
v-if="!chatUIStore.isSidebarOpen && userStore.isAdmin"
3446
@click="toggleSidebar"
3547
>
3648
<PanelLeftOpen class="nav-btn-icon" size="18" />
@@ -52,6 +64,7 @@
5264
</div>
5365
</div>
5466
<div class="header__right">
67+
<UserInfoComponent v-if="!userStore.isAdmin" />
5568
<!-- AgentState 显示按钮已移动到输入框底部 -->
5669
<slot name="header-right"></slot>
5770
</div>
@@ -219,13 +232,16 @@ import { ScrollController } from '@/utils/scrollController'
219232
import { AgentValidator } from '@/utils/agentValidator'
220233
import { useAgentStore } from '@/stores/agent'
221234
import { useChatUIStore } from '@/stores/chatUI'
235+
import { useInfoStore } from '@/stores/info'
236+
import { useUserStore } from '@/stores/user'
222237
import { storeToRefs } from 'pinia'
223238
import { MessageProcessor } from '@/utils/messageProcessor'
224239
import { agentApi, threadApi } from '@/apis'
225240
import HumanApprovalModal from '@/components/HumanApprovalModal.vue'
226241
import { useApproval } from '@/composables/useApproval'
227242
import { useAgentStreamHandler } from '@/composables/useAgentStreamHandler'
228243
import AgentPanel from '@/components/AgentPanel.vue'
244+
import UserInfoComponent from '@/components/UserInfoComponent.vue'
229245
230246
// ==================== PROPS & EMITS ====================
231247
const props = defineProps({
@@ -236,6 +252,8 @@ const props = defineProps({
236252
// ==================== STORE MANAGEMENT ====================
237253
const agentStore = useAgentStore()
238254
const chatUIStore = useChatUIStore()
255+
const infoStore = useInfoStore()
256+
const userStore = useUserStore()
239257
const {
240258
agents,
241259
selectedAgentId,
@@ -247,9 +265,11 @@ const {
247265
availableMcps,
248266
availableSkills
249267
} = storeToRefs(agentStore)
268+
const { organization } = storeToRefs(infoStore)
250269
251270
// ==================== LOCAL CHAT & UI STATE ====================
252271
const userInput = ref('')
272+
const sidebarLogo = computed(() => organization.value?.logo || organization.value?.avatar || '')
253273
const useRunsApi =
254274
import.meta.env.VITE_USE_RUNS_API === 'true' &&
255275
localStorage.getItem('force_legacy_stream') !== 'true'
@@ -1910,6 +1930,7 @@ watch(
19101930
.header__right {
19111931
display: flex;
19121932
align-items: center;
1933+
gap: 8px;
19131934
}
19141935
19151936
.switch-icon {
@@ -1920,6 +1941,48 @@ watch(
19201941
.agent-nav-btn:hover .switch-icon {
19211942
color: var(--main-500);
19221943
}
1944+
1945+
.sidebar-logo-toggle {
1946+
position: relative;
1947+
width: 32px;
1948+
height: 32px;
1949+
border-radius: 8px;
1950+
border: 1px solid var(--gray-150);
1951+
background: var(--gray-0);
1952+
display: flex;
1953+
align-items: center;
1954+
justify-content: center;
1955+
overflow: hidden;
1956+
cursor: pointer;
1957+
flex-shrink: 0;
1958+
}
1959+
1960+
.sidebar-logo-image {
1961+
width: 24px;
1962+
height: 24px;
1963+
border-radius: 6px;
1964+
object-fit: cover;
1965+
}
1966+
1967+
.sidebar-logo-fallback {
1968+
color: var(--gray-700);
1969+
}
1970+
1971+
.sidebar-expand-overlay {
1972+
position: absolute;
1973+
inset: 0;
1974+
display: flex;
1975+
align-items: center;
1976+
justify-content: center;
1977+
background: var(--gray-100);
1978+
color: var(--gray-900);
1979+
opacity: 0;
1980+
transition: opacity 0.2s ease;
1981+
}
1982+
1983+
.sidebar-logo-toggle:hover .sidebar-expand-overlay {
1984+
opacity: 1;
1985+
}
19231986
}
19241987
}
19251988

web/src/components/ChatSidebarComponent.vue

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@
55
>
66
<div class="sidebar-content">
77
<div class="sidebar-header">
8-
<div class="header-title">{{ branding.name }}</div>
8+
<div class="header-brand">
9+
<img
10+
v-if="!userStore.isAdmin && (organization.logo || organization.avatar)"
11+
:src="organization.logo || organization.avatar"
12+
alt="logo"
13+
class="brand-logo"
14+
/>
15+
<div class="header-title">{{ branding.name || organization.name || 'Yuxi-Know' }}</div>
16+
</div>
917
<div class="header-actions">
1018
<div
1119
class="toggle-sidebar nav-btn"
@@ -96,7 +104,7 @@
96104
</template>
97105

98106
<script setup>
99-
import { computed, h, ref } from 'vue'
107+
import { computed, h } from 'vue'
100108
import { message, Modal } from 'ant-design-vue'
101109
import {
102110
PanelLeftClose,
@@ -111,13 +119,15 @@ import {
111119
import dayjs, { parseToShanghai } from '@/utils/time'
112120
import { useChatUIStore } from '@/stores/chatUI'
113121
import { useInfoStore } from '@/stores/info'
122+
import { useUserStore } from '@/stores/user'
114123
import { storeToRefs } from 'pinia'
115124
116125
// 使用 chatUI store
117126
const chatUIStore = useChatUIStore()
118127
const infoStore = useInfoStore()
128+
const userStore = useUserStore()
119129
120-
const { branding } = storeToRefs(infoStore)
130+
const { branding, organization } = storeToRefs(infoStore)
121131
122132
const props = defineProps({
123133
currentAgentId: {
@@ -300,6 +310,24 @@ const togglePin = (chatId) => {
300310
border-bottom: 1px solid var(--gray-50);
301311
flex-shrink: 0;
302312
313+
.header-brand {
314+
display: flex;
315+
align-items: center;
316+
gap: 8px;
317+
min-width: 0;
318+
flex: 1;
319+
}
320+
321+
.brand-logo {
322+
width: 26px;
323+
height: 26px;
324+
border-radius: 6px;
325+
object-fit: cover;
326+
flex-shrink: 0;
327+
border: 1px solid var(--gray-100);
328+
background: var(--gray-0);
329+
}
330+
303331
.header-title {
304332
font-weight: 600;
305333
font-size: 16px;

web/src/layouts/AppLayout.vue

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@ const handleDebugModalClose = () => {
4747
showDebugModal.value = false
4848
}
4949
50-
const getRemoteConfig = () => {
51-
configStore.refreshConfig()
50+
const getRemoteConfig = async () => {
51+
if (!userStore.isAdmin) return
52+
try {
53+
await configStore.refreshConfig()
54+
} catch (error) {
55+
console.warn('加载系统配置失败:', error)
56+
}
5257
}
5358
54-
const getRemoteDatabase = () => {
55-
databaseStore.loadDatabases()
59+
const getRemoteDatabase = async () => {
60+
try {
61+
await databaseStore.loadDatabases()
62+
} catch (error) {
63+
console.warn('加载知识库列表失败:', error)
64+
}
5665
}
5766
5867
// Fetch GitHub stars count
@@ -73,12 +82,14 @@ const fetchGithubStars = async () => {
7382
onMounted(async () => {
7483
// 加载信息配置
7584
await infoStore.loadInfoConfig()
76-
// 加载其他配置
77-
getRemoteConfig()
78-
getRemoteDatabase()
79-
fetchGithubStars() // Fetch GitHub stars on mount
80-
// 预加载任务数据,确保任务中心打开时有内容
81-
taskerStore.loadTasks()
85+
// 加载知识库数据(普通用户加载可访问知识库)
86+
await getRemoteDatabase()
87+
// 仅管理员加载系统配置和任务中心数据
88+
if (userStore.isAdmin) {
89+
await getRemoteConfig()
90+
taskerStore.loadTasks()
91+
fetchGithubStars() // Fetch GitHub stars on mount
92+
}
8293
})
8394
8495
// 打印当前页面的路由信息,使用 vue3 的 setup composition API
@@ -95,37 +106,42 @@ const mainList = computed(() => {
95106
path: '/agent',
96107
icon: Bot,
97108
activeIcon: Bot
98-
},
99-
{
100-
name: '图谱',
101-
path: '/graph',
102-
icon: Waypoints,
103-
activeIcon: Waypoints
104-
},
105-
{
106-
name: '知识库',
107-
path: '/database',
108-
icon: LibraryBig,
109-
activeIcon: LibraryBig
110109
}
111110
]
112111
113-
if (userStore.isSuperAdmin) {
112+
if (userStore.isAdmin) {
113+
items.push(
114+
{
115+
name: '图谱',
116+
path: '/graph',
117+
icon: Waypoints,
118+
activeIcon: Waypoints
119+
},
120+
{
121+
name: '知识库',
122+
path: '/database',
123+
icon: LibraryBig,
124+
activeIcon: LibraryBig
125+
}
126+
)
127+
128+
if (userStore.isSuperAdmin) {
129+
items.push({
130+
name: '扩展管理',
131+
path: '/extensions',
132+
icon: Blocks,
133+
activeIcon: Blocks
134+
})
135+
}
136+
114137
items.push({
115-
name: '扩展管理',
116-
path: '/extensions',
117-
icon: Blocks,
118-
activeIcon: Blocks
138+
name: 'Dashboard',
139+
path: '/dashboard',
140+
icon: BarChart3,
141+
activeIcon: BarChart3
119142
})
120143
}
121144
122-
items.push({
123-
name: 'Dashboard',
124-
path: '/dashboard',
125-
icon: BarChart3,
126-
activeIcon: BarChart3
127-
})
128-
129145
return items
130146
})
131147
@@ -137,7 +153,7 @@ provide('settingsModal', {
137153

138154
<template>
139155
<div class="app-layout" :class="{ 'use-top-bar': layoutSettings.useTopBar }">
140-
<div class="header" :class="{ 'top-bar': layoutSettings.useTopBar }">
156+
<div v-if="userStore.isAdmin" class="header" :class="{ 'top-bar': layoutSettings.useTopBar }">
141157
<div class="logo circle">
142158
<router-link to="/">
143159
<img :src="infoStore.organization.avatar" />
@@ -163,6 +179,7 @@ provide('settingsModal', {
163179
</a-tooltip>
164180
</RouterLink>
165181
<div
182+
v-if="userStore.isAdmin"
166183
class="nav-item task-center"
167184
:class="{ active: isDrawerOpen }"
168185
@click="taskerStore.openDrawer()"
@@ -217,7 +234,7 @@ provide('settingsModal', {
217234
>
218235
<DebugComponent />
219236
</a-modal>
220-
<TaskCenterDrawer />
237+
<TaskCenterDrawer v-if="userStore.isAdmin" />
221238
<SettingsModal v-model:visible="showSettingsModal" @close="() => (showSettingsModal = false)" />
222239
</div>
223240
</template>

0 commit comments

Comments
 (0)