fix: 优化前端交互细节
修正消息匹配转义和复制兜底日志,并统一卡片操作入口与主题链接颜色。
This commit is contained in:
parent
7b1b326523
commit
404fcd55a3
@ -451,10 +451,6 @@ const supportsFiles = computed(() => {
|
|||||||
const currentAgentState = computed(() => {
|
const currentAgentState = computed(() => {
|
||||||
return currentChatId.value ? getThreadState(currentChatId.value)?.agentState || null : null
|
return currentChatId.value ? getThreadState(currentChatId.value)?.agentState || null : null
|
||||||
})
|
})
|
||||||
const currentThreadFiles = computed(() => {
|
|
||||||
if (!currentChatId.value) return []
|
|
||||||
return threadFilesMap.value[currentChatId.value] || []
|
|
||||||
})
|
|
||||||
const currentThreadAttachments = computed(() => {
|
const currentThreadAttachments = computed(() => {
|
||||||
if (!currentChatId.value) return []
|
if (!currentChatId.value) return []
|
||||||
return threadAttachmentsMap.value[currentChatId.value] || []
|
return threadAttachmentsMap.value[currentChatId.value] || []
|
||||||
|
|||||||
@ -293,7 +293,7 @@ const splitTextByQuery = (text, query) => {
|
|||||||
if (!text) return []
|
if (!text) return []
|
||||||
if (!query) return [{ text, isMatch: false }]
|
if (!query) return [{ text, isMatch: false }]
|
||||||
|
|
||||||
const escapedQuery = query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||||
const regex = new RegExp(`(${escapedQuery})`, 'gi')
|
const regex = new RegExp(`(${escapedQuery})`, 'gi')
|
||||||
const parts = text.split(regex)
|
const parts = text.split(regex)
|
||||||
|
|
||||||
|
|||||||
@ -107,6 +107,41 @@
|
|||||||
<div class="user-id-row">ID: {{ user.uid || '-' }}</div>
|
<div class="user-id-row">ID: {{ user.uid || '-' }}</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -123,36 +158,6 @@
|
|||||||
<span class="info-value time-text">{{ formatTime(user.last_login) }}</span>
|
<span class="info-value time-text">{{ formatTime(user.last_login) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -262,7 +267,17 @@ import { reactive, onMounted, watch, computed } from 'vue'
|
|||||||
import { message, Modal } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { departmentApi } from '@/apis'
|
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'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@ -422,6 +437,10 @@ watch(
|
|||||||
// 格式化时间显示
|
// 格式化时间显示
|
||||||
const formatTime = (timeStr) => formatDateTime(timeStr)
|
const formatTime = (timeStr) => formatDateTime(timeStr)
|
||||||
|
|
||||||
|
const isUserDeleteDisabled = (user) =>
|
||||||
|
user.id === userStore.userId ||
|
||||||
|
(user.role === 'superadmin' && userStore.userRole !== 'superadmin')
|
||||||
|
|
||||||
// 获取用户列表
|
// 获取用户列表
|
||||||
const fetchUsers = async () => {
|
const fetchUsers = async () => {
|
||||||
try {
|
try {
|
||||||
@ -773,7 +792,6 @@ onMounted(async () => {
|
|||||||
border: 1px solid var(--gray-150);
|
border: 1px solid var(--gray-150);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
padding-bottom: 6px;
|
|
||||||
|
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
box-shadow: 0 1px 3px var(--shadow-1);
|
box-shadow: 0 1px 3px var(--shadow-1);
|
||||||
@ -784,10 +802,16 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
.user-info-main {
|
.user-info-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
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 {
|
.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 {
|
@keyframes spin {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
|||||||
@ -131,7 +131,9 @@ const copySvgAsPng = async (svgEl, btn) => {
|
|||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(svgContent)
|
await navigator.clipboard.writeText(svgContent)
|
||||||
console.log('PNG 复制失败,已回退复制 SVG 源码')
|
console.log('PNG 复制失败,已回退复制 SVG 源码')
|
||||||
} catch {}
|
} catch (fallbackErr) {
|
||||||
|
console.error('复制 SVG 源码失败:', fallbackErr)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -554,8 +554,8 @@ defineExpose({
|
|||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: var(--main-700);
|
color: var(--gray-700);
|
||||||
background: var(--main-50);
|
background: var(--gray-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,9 @@ export const useThemeStore = defineStore('theme', () => {
|
|||||||
fontFamily:
|
fontFamily:
|
||||||
"'HarmonyOS Sans SC', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;",
|
"'HarmonyOS Sans SC', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;",
|
||||||
colorPrimary: '#24839b',
|
colorPrimary: '#24839b',
|
||||||
|
colorLink: 'var(--main-color)',
|
||||||
|
colorLinkHover: 'var(--main-600)',
|
||||||
|
colorLinkActive: 'var(--main-800)',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
wireframe: false
|
wireframe: false
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user