fix: 优化前端交互细节

修正消息匹配转义和复制兜底日志,并统一卡片操作入口与主题链接颜色。
This commit is contained in:
Wenjie Zhang 2026-05-26 19:39:08 +08:00
parent 7b1b326523
commit 404fcd55a3
6 changed files with 91 additions and 72 deletions

View File

@ -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] || []

View File

@ -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)

View File

@ -107,6 +107,41 @@
<div class="user-id-row">ID: {{ user.uid || '-' }}</div>
</div>
</div>
<a-dropdown :trigger="['click']" placement="bottomRight">
<template #overlay>
<a-menu>
<a-menu-item key="edit" @click.stop="showEditUserModal(user)">
<span class="user-card-menu-item">
<Pencil :size="14" />
编辑用户
</span>
</a-menu-item>
<a-menu-item
key="delete"
:disabled="isUserDeleteDisabled(user)"
@click.stop="confirmDeleteUser(user)"
>
<span
class="user-card-menu-item"
:class="{ danger: !isUserDeleteDisabled(user) }"
>
<Trash2 :size="14" />
删除用户
</span>
</a-menu-item>
</a-menu>
</template>
<a-button
type="text"
size="small"
class="card-menu-trigger lucide-icon-btn"
aria-label="用户操作"
@click.stop
>
<MoreVertical :size="16" />
</a-button>
</a-dropdown>
</div>
<div class="card-content">
@ -123,36 +158,6 @@
<span class="info-value time-text">{{ formatTime(user.last_login) }}</span>
</div>
</div>
<div class="card-actions">
<a-tooltip title="编辑用户">
<a-button
type="text"
size="small"
@click="showEditUserModal(user)"
class="action-btn lucide-icon-btn"
>
<Pencil :size="14" />
<span>编辑</span>
</a-button>
</a-tooltip>
<a-tooltip title="删除用户">
<a-button
type="text"
size="small"
danger
@click="confirmDeleteUser(user)"
:disabled="
user.id === userStore.userId ||
(user.role === 'superadmin' && userStore.userRole !== 'superadmin')
"
class="action-btn lucide-icon-btn"
>
<Trash2 :size="14" />
<span>删除</span>
</a-button>
</a-tooltip>
</div>
</div>
</div>
</div>
@ -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);

View File

@ -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)
}

View File

@ -554,8 +554,8 @@ defineExpose({
&:hover,
&:focus {
color: var(--main-700);
background: var(--main-50);
color: var(--gray-700);
background: var(--gray-50);
}
}

View File

@ -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
}