diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue
index 4c5ab302..39502d02 100644
--- a/web/src/components/AgentChatComponent.vue
+++ b/web/src/components/AgentChatComponent.vue
@@ -451,10 +451,6 @@ const supportsFiles = computed(() => {
const currentAgentState = computed(() => {
return currentChatId.value ? getThreadState(currentChatId.value)?.agentState || null : null
})
-const currentThreadFiles = computed(() => {
- if (!currentChatId.value) return []
- return threadFilesMap.value[currentChatId.value] || []
-})
const currentThreadAttachments = computed(() => {
if (!currentChatId.value) return []
return threadAttachmentsMap.value[currentChatId.value] || []
diff --git a/web/src/components/MessageInputComponent.vue b/web/src/components/MessageInputComponent.vue
index f3f02a75..24b1dfd5 100644
--- a/web/src/components/MessageInputComponent.vue
+++ b/web/src/components/MessageInputComponent.vue
@@ -293,7 +293,7 @@ const splitTextByQuery = (text, query) => {
if (!text) return []
if (!query) return [{ text, isMatch: false }]
- const escapedQuery = query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
+ const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const regex = new RegExp(`(${escapedQuery})`, 'gi')
const parts = text.split(regex)
diff --git a/web/src/components/UserManagementComponent.vue b/web/src/components/UserManagementComponent.vue
index 2640fdd1..5a18d088 100644
--- a/web/src/components/UserManagementComponent.vue
+++ b/web/src/components/UserManagementComponent.vue
@@ -107,6 +107,41 @@
ID: {{ user.uid || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -123,36 +158,6 @@
{{ formatTime(user.last_login) }}
-
-
-
-
-
- 编辑
-
-
-
-
-
- 删除
-
-
-
@@ -262,7 +267,17 @@ import { reactive, onMounted, watch, computed } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { useUserStore } from '@/stores/user'
import { departmentApi } from '@/apis'
-import { Plus, Pencil, Trash2, User, UserLock, UserStar, RefreshCw, Search } from 'lucide-vue-next'
+import {
+ Plus,
+ Pencil,
+ Trash2,
+ User,
+ UserLock,
+ UserStar,
+ RefreshCw,
+ Search,
+ MoreVertical
+} from 'lucide-vue-next'
import { formatDateTime } from '@/utils/time'
const userStore = useUserStore()
@@ -422,6 +437,10 @@ watch(
// 格式化时间显示
const formatTime = (timeStr) => formatDateTime(timeStr)
+const isUserDeleteDisabled = (user) =>
+ user.id === userStore.userId ||
+ (user.role === 'superadmin' && userStore.userRole !== 'superadmin')
+
// 获取用户列表
const fetchUsers = async () => {
try {
@@ -773,7 +792,6 @@ onMounted(async () => {
border: 1px solid var(--gray-150);
border-radius: 8px;
padding: 12px;
- padding-bottom: 6px;
transition: all 0.2s ease;
box-shadow: 0 1px 3px var(--shadow-1);
@@ -784,10 +802,16 @@ onMounted(async () => {
}
.card-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 8px;
margin-bottom: 10px;
.user-info-main {
display: flex;
+ flex: 1;
+ min-width: 0;
gap: 12px;
align-items: center;
@@ -878,6 +902,22 @@ onMounted(async () => {
}
}
}
+
+ .card-menu-trigger {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ flex-shrink: 0;
+ color: var(--gray-600);
+
+ &:hover,
+ &:focus {
+ color: var(--gray-700);
+ background: var(--gray-50);
+ }
+ }
}
.card-content {
@@ -915,38 +955,6 @@ onMounted(async () => {
}
}
}
-
- .card-actions {
- display: flex;
- justify-content: flex-end;
- gap: 6px;
- padding-top: 6px;
- border-top: 1px solid var(--gray-25);
-
- .action-btn {
- display: flex;
- align-items: center;
- gap: 4px;
- padding: 4px 8px;
- border-radius: 6px;
- transition: all 0.2s ease;
- font-size: 12px;
-
- span {
- font-size: 12px;
- }
-
- &:hover {
- background: var(--gray-25);
- }
-
- &.ant-btn-dangerous:hover {
- background: var(--gray-25);
- border-color: var(--color-error-500);
- color: var(--color-error-500);
- }
- }
- }
}
}
}
@@ -965,6 +973,16 @@ onMounted(async () => {
}
}
+.user-card-menu-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+
+ &.danger {
+ color: var(--color-error-700);
+ }
+}
+
@keyframes spin {
from {
transform: rotate(0deg);
diff --git a/web/src/components/common/MarkdownPreview.vue b/web/src/components/common/MarkdownPreview.vue
index 6a463b0b..e2ad230e 100644
--- a/web/src/components/common/MarkdownPreview.vue
+++ b/web/src/components/common/MarkdownPreview.vue
@@ -131,7 +131,9 @@ const copySvgAsPng = async (svgEl, btn) => {
try {
await navigator.clipboard.writeText(svgContent)
console.log('PNG 复制失败,已回退复制 SVG 源码')
- } catch {}
+ } catch (fallbackErr) {
+ console.error('复制 SVG 源码失败:', fallbackErr)
+ }
} finally {
URL.revokeObjectURL(url)
}
diff --git a/web/src/components/model-management/AgentManagePanel.vue b/web/src/components/model-management/AgentManagePanel.vue
index 6f994ffb..dae22669 100644
--- a/web/src/components/model-management/AgentManagePanel.vue
+++ b/web/src/components/model-management/AgentManagePanel.vue
@@ -554,8 +554,8 @@ defineExpose({
&:hover,
&:focus {
- color: var(--main-700);
- background: var(--main-50);
+ color: var(--gray-700);
+ background: var(--gray-50);
}
}
diff --git a/web/src/stores/theme.js b/web/src/stores/theme.js
index ee40336e..3fdb6d8c 100644
--- a/web/src/stores/theme.js
+++ b/web/src/stores/theme.js
@@ -12,6 +12,9 @@ export const useThemeStore = defineStore('theme', () => {
fontFamily:
"'HarmonyOS Sans SC', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;",
colorPrimary: '#24839b',
+ colorLink: 'var(--main-color)',
+ colorLinkHover: 'var(--main-600)',
+ colorLinkActive: 'var(--main-800)',
borderRadius: 8,
wireframe: false
}