feature(user): 优化用户体验

- 替换创建新对话按钮图标,提升视觉一致性
- 移动撤销源预览 URL 的逻辑,避免重复定义
- 移除知识库卡片组件中的多余间距,简化样式
This commit is contained in:
Wenjie Zhang 2026-04-21 00:18:59 +08:00
parent cd47227888
commit 4e0046beb1
4 changed files with 44 additions and 54 deletions

View File

@ -36,7 +36,7 @@
:disabled="chatUIStore.creatingNewChat"
>
<LoaderCircle v-if="chatUIStore.creatingNewChat" size="18" class="loading-icon" />
<MessageSquarePlus v-else size="18" />
<MessageCirclePlus v-else size="18" />
创建新对话
</button>
</div>
@ -112,7 +112,7 @@ import { computed, h } from 'vue'
import { message, Modal } from 'ant-design-vue'
import {
PanelLeftClose,
MessageSquarePlus,
MessageCirclePlus,
LoaderCircle,
Pin,
PinOff,
@ -318,15 +318,15 @@ const togglePin = (chatId) => {
height: 100%;
&.sidebar-open {
box-shadow: 4px 0 16px rgba(0, 0, 0, 0.08);
border-right: none;
box-shadow: 4px 0 16px var(--dark-5);
border-right: 1px solid var(--gray-50);
}
}
&.sidebar-open {
width: 280px;
max-width: 300px;
border-right: 1px solid var(--gray-200);
border-right: 1px solid var(--gray-150);
}
.sidebar-header {

View File

@ -156,6 +156,13 @@ const downloadingMarkdown = ref(false)
const sourcePreviewLoading = ref(false)
const sourcePreviewUrl = ref('')
const revokeSourcePreviewUrl = () => {
if (sourcePreviewUrl.value) {
window.URL.revokeObjectURL(sourcePreviewUrl.value)
sourcePreviewUrl.value = ''
}
}
//
const theme = computed(() => (themeStore.isDark ? 'dark' : 'light'))
@ -239,13 +246,6 @@ const afterOpenChange = (open) => {
}
}
const revokeSourcePreviewUrl = () => {
if (sourcePreviewUrl.value) {
window.URL.revokeObjectURL(sourcePreviewUrl.value)
sourcePreviewUrl.value = ''
}
}
const loadSourcePreview = async () => {
if (!file.value?.file_id || !store.databaseId || !hasSourcePreview.value) return
if (sourcePreviewUrl.value) return

View File

@ -501,7 +501,6 @@ const deleteDatabase = () => {
.tags-section {
display: flex;
gap: 6px;
align-items: center;
flex-wrap: wrap;
}

View File

@ -132,7 +132,6 @@
<a-input
v-model:value="userManagement.form.username"
placeholder="请输入用户名2-20个字符"
size="large"
@blur="validateAndGenerateUserId"
:maxlength="20"
/>
@ -141,23 +140,19 @@
</div>
</a-form-item>
<!-- 显示自动生成的用户ID -->
<!-- 显示自动生成的用户ID仅添加用户时显示 -->
<a-form-item
v-if="userManagement.form.generatedUserId || userManagement.editMode"
v-if="userManagement.form.generatedUserId && !userManagement.editMode"
label="用户ID"
class="form-item"
>
<a-input
:value="userManagement.form.generatedUserId"
placeholder="自动生成"
size="large"
disabled
:addon-before="userManagement.editMode ? '已存在ID' : '登录ID'"
:addon-before="'登录ID'"
/>
<div v-if="!userManagement.editMode" class="help-text">
此ID将用于登录根据用户名自动生成
</div>
<div v-else class="help-text">编辑模式下不能修改用户ID</div>
<div class="help-text">此ID将用于登录根据用户名自动生成</div>
</a-form-item>
<!-- 手机号字段 -->
@ -165,7 +160,6 @@
<a-input
v-model:value="userManagement.form.phoneNumber"
placeholder="请输入手机号(可选,可用于登录)"
size="large"
:maxlength="11"
/>
<div v-if="userManagement.form.phoneError" class="error-text">
@ -186,7 +180,6 @@
<a-input-password
v-model:value="userManagement.form.password"
placeholder="请输入密码"
size="large"
/>
</a-form-item>
@ -194,7 +187,6 @@
<a-input-password
v-model:value="userManagement.form.confirmPassword"
placeholder="请再次输入密码"
size="large"
/>
</a-form-item>
</template>
@ -204,11 +196,11 @@
label="角色"
class="form-item"
>
<a-input value="超级管理员" size="large" disabled />
<a-input value="超级管理员" disabled />
<div class="help-text">超级管理员账户无法修改角色</div>
</a-form-item>
<a-form-item v-else label="角色" class="form-item">
<a-select v-model:value="userManagement.form.role" size="large">
<a-select v-model:value="userManagement.form.role">
<a-select-option value="user">普通用户</a-select-option>
<a-select-option value="admin" v-if="userStore.isSuperAdmin">管理员</a-select-option>
</a-select>
@ -218,7 +210,6 @@
<a-form-item v-if="userStore.isSuperAdmin" label="部门" class="form-item">
<a-select
v-model:value="userManagement.form.departmentId"
size="large"
placeholder="请选择部门"
>
<a-select-option
@ -237,7 +228,7 @@
<script setup>
import { reactive, onMounted, watch } from 'vue'
import { notification, Modal } from 'ant-design-vue'
import { message, Modal } from 'ant-design-vue'
import { useUserStore } from '@/stores/user'
import { departmentApi } from '@/apis'
import { Plus, Pencil, Trash2, User, UserLock, UserStar } from 'lucide-vue-next'
@ -407,7 +398,7 @@ const handleUserFormSubmit = async () => {
try {
//
if (!userManagement.form.username.trim()) {
notification.error({ message: '用户名不能为空' })
message.error('用户名不能为空')
return
}
@ -416,24 +407,24 @@ const handleUserFormSubmit = async () => {
userManagement.form.username.trim().length < 2 ||
userManagement.form.username.trim().length > 20
) {
notification.error({ message: '用户名长度必须在 2-20 个字符之间' })
message.error('用户名长度必须在 2-20 个字符之间')
return
}
//
if (userManagement.form.phoneNumber && !validatePhoneNumber(userManagement.form.phoneNumber)) {
notification.error({ message: '请输入正确的手机号格式' })
message.error('请输入正确的手机号格式')
return
}
if (userManagement.displayPasswordFields) {
if (!userManagement.form.password) {
notification.error({ message: '密码不能为空' })
message.error('密码不能为空')
return
}
if (userManagement.form.password !== userManagement.form.confirmPassword) {
notification.error({ message: '两次输入的密码不一致' })
message.error('两次输入的密码不一致')
return
}
}
@ -464,7 +455,7 @@ const handleUserFormSubmit = async () => {
}
await userStore.updateUser(userManagement.editUserId, updateData)
notification.success({ message: '用户更新成功' })
message.success('用户更新成功')
} else {
//
const createData = {
@ -484,7 +475,7 @@ const handleUserFormSubmit = async () => {
}
await userStore.createUser(createData)
notification.success({ message: '用户创建成功' })
message.success('用户创建成功')
}
//
@ -492,10 +483,7 @@ const handleUserFormSubmit = async () => {
userManagement.modalVisible = false
} catch (error) {
console.error('用户操作失败:', error)
notification.error({
message: '操作失败',
description: error.message || '请稍后重试'
})
message.error(error.message || '操作失败,请稍后重试')
} finally {
userManagement.loading = false
}
@ -505,7 +493,7 @@ const handleUserFormSubmit = async () => {
const confirmDeleteUser = (user) => {
//
if (user.id === userStore.userId) {
notification.error({ message: '不能删除自己的账户' })
message.error('不能删除自己的账户')
return
}
@ -520,15 +508,12 @@ const confirmDeleteUser = (user) => {
try {
userManagement.loading = true
await userStore.deleteUser(user.id)
notification.success({ message: '用户删除成功' })
message.success('用户删除成功')
//
await fetchUsers()
} catch (error) {
console.error('删除用户失败:', error)
notification.error({
message: '删除失败',
description: error.message || '请稍后重试'
})
message.error(error.message || '删除失败,请稍后重试')
} finally {
userManagement.loading = false
}
@ -776,30 +761,31 @@ onMounted(async () => {
.user-modal {
:deep(.ant-modal-header) {
padding: 20px 24px;
padding: 20px 24px 16px;
border-bottom: 1px solid var(--gray-150);
.ant-modal-title {
font-size: 16px;
font-size: 17px;
font-weight: 600;
color: var(--gray-900);
}
}
:deep(.ant-modal-body) {
padding: 24px;
padding: 20px 24px 24px;
}
.user-form {
.form-item {
margin-bottom: 20px;
margin-bottom: 16px;
:deep(.ant-form-item-label) {
padding-bottom: 4px;
padding-bottom: 6px;
label {
font-weight: 500;
color: var(--gray-900);
font-weight: 600;
font-size: 13px;
color: var(--gray-800);
}
}
}
@ -820,10 +806,15 @@ onMounted(async () => {
.password-toggle {
margin-bottom: 16px;
padding: 12px 16px;
background: var(--gray-25);
border-radius: 8px;
border: 1px solid var(--gray-100);
:deep(.ant-checkbox-wrapper) {
font-weight: 500;
color: var(--gray-600);
color: var(--gray-700);
font-size: 13px;
}
}
}