用户列表新增抽屉效果详细信息

This commit is contained in:
RuoYi
2026-04-09 12:49:06 +08:00
parent 891b2c0200
commit ecb536f101
4 changed files with 249 additions and 6 deletions
+52
View File
@@ -241,6 +241,58 @@
}
/** 详细卡片样式 */
.detail-drawer {
.el-drawer__body {
padding: 0;
}
.el-drawer__header {
margin-bottom: 6px;
padding: 8px 12px 6px;
font-size: 15px;
color: #303133;
background: #f8f8f8;
}
.section-header {
font-size: 15px;
color: #6379bb;
border-bottom: 1px solid #ddd;
margin: 12px 0 16px 0;
padding-bottom: 8px;
}
.drawer-content {
padding: 0 20px 20px 20px;
.info-item {
display: flex;
align-items: flex-start;
padding: 8px 0;
min-height: 40px;
}
.info-label {
flex-shrink: 0;
width: 200px;
color: #606266;
font-size: 13px;
line-height: 1.6;
padding-top: 4px;
text-align: right;
margin-right: 14px;
}
.info-value {
flex: 1;
color: #303133;
font-size: 13px;
font-weight: 500;
line-height: 1.6;
word-break: break-all;
padding-top: 4px;
min-height: 1.6em;
&.plaintext {
border-bottom: 1px dashed #dde1e6;
}
}
}
}
.detail-wrap { padding: 0 4px; }
.detail-card {
+5 -5
View File
@@ -251,14 +251,13 @@ function toggleFullscreen() {
const mainContainer = document.querySelector('.main-container') as HTMLElement | null
const navbar = document.querySelector('.navbar') as HTMLElement | null
const sidebar = document.querySelector('.sidebar-container') as HTMLElement | null
const tagsActionBtn = document.querySelector<HTMLElement>('.tags-action-btn')
if (!mainContainer) return
if (!isFullscreen.value) {
mainContainer.classList.add('fullscreen-mode')
document.body.style.overflow = 'hidden'
const elementsToHide = [{ el: navbar, originalDisplay: navbar?.style.display || '' }, { el: sidebar, originalDisplay: sidebar?.style.display || '' }]
elementsToHide.forEach((item :any) => {
elementsToHide.forEach(item => {
if (item.el && item.el.style.display !== 'none') {
item.originalDisplay = item.el.style.display
item.el.style.display = 'none'
@@ -269,13 +268,14 @@ function toggleFullscreen() {
} else {
mainContainer.classList.remove('fullscreen-mode')
document.body.style.overflow = ''
hiddenElements.value.forEach((item :any) => {
hiddenElements.value.forEach((item: any) => {
if (item.el) {
item.el.style.display = item.originalDisplay
}
})
hiddenElements.value = ref<any[]>([])
tagsActionBtn?.blur()
hiddenElements.value = []
const tagsBtn = document.querySelector('.tags-action-btn') as HTMLElement | null
tagsBtn?.blur()
isFullscreen.value = false
}
}
+13 -1
View File
@@ -46,7 +46,11 @@
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns.userId.visible" />
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns.userName.visible" :show-overflow-tooltip="true" />
<el-table-column label="用户名称" align="center" key="userName" v-if="columns.userName.visible" :show-overflow-tooltip="true">
<template #default="scope">
<a class="link-type" style="cursor:pointer" @click="handleViewData(scope.row)">{{ scope.row.userName }}</a>
</template>
</el-table-column>
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns.nickName.visible" :show-overflow-tooltip="true" />
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns.deptName.visible" :show-overflow-tooltip="true" />
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns.phonenumber.visible" width="120" />
@@ -172,6 +176,8 @@
</template>
</el-dialog>
<!-- 用户详情抽屉 -->
<user-view-drawer ref="userViewRef" />
<!-- 用户导入对话框 -->
<excel-import-dialog ref="importUserRef" title="用户导入" action="/system/user/importData" template-action="/system/user/importTemplate" template-file-name="user_template" update-support-label="是否更新已经存在的用户数据" @success="getList" />
</div>
@@ -182,6 +188,7 @@ import { getToken } from "@/utils/auth"
import useAppStore from '@/store/modules/app'
import TreePanel from "@/components/TreePanel"
import ExcelImportDialog from "@/components/ExcelImportDialog"
import UserViewDrawer from "./view"
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user"
import type { SysUser, UserQueryParams, UserFormDataResult } from '@/types/api/system/user'
import type { SysRole } from '@/types/api/system/role'
@@ -370,6 +377,11 @@ function handleSelectionChange(selection: SysUser[]) {
multiple.value = !selection.length
}
/** 详情按钮操作 */
function handleViewData(row: SysUser) {
proxy.$refs["userViewRef"].open(row.userId)
}
/** 导入按钮操作 */
function handleImport() {
proxy.$refs["importUserRef"].open()
+179
View File
@@ -0,0 +1,179 @@
<template>
<el-drawer title="用户信息详情" v-model="visible" direction="rtl" size="68%" append-to-body :before-close="handleClose" class="detail-drawer">
<div v-loading="loading" class="drawer-content">
<!-- 基本信息 -->
<h4 class="section-header">基本信息</h4>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">用户名称</label>
<span class="info-value plaintext">{{ info.nickName }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">归属部门</label>
<span class="info-value plaintext">{{ (info.dept && info.dept.deptName) }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">手机号码</label>
<span class="info-value plaintext">{{ info.phonenumber }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">邮箱</label>
<span class="info-value plaintext">{{ info.email }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">登录账号</label>
<span class="info-value plaintext">{{ info.userName }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">用户状态</label>
<span class="info-value plaintext">
<el-tag size="small" :type="info.status === '0' ? 'success' : 'danger'">{{ info.status === '0' ? '正常' : '停用' }}</el-tag>
</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">岗位</label>
<span class="info-value plaintext">{{ postNames || '无岗位' }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">用户性别</label>
<span class="info-value plaintext">{{ sexLabel }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="24">
<div class="info-item full-width">
<label class="info-label">角色</label>
<span class="info-value plaintext">{{ roleNames || '无角色' }}</span>
</div>
</el-col>
</el-row>
<!-- 其他信息 -->
<h4 class="section-header">其他信息</h4>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">创建者</label>
<span class="info-value plaintext">{{ info.createBy }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">创建时间</label>
<span class="info-value plaintext">{{ info.createTime }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">更新者</label>
<span class="info-value plaintext">{{ info.updateBy }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">更新时间</label>
<span class="info-value plaintext">{{ info.updateTime }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="12">
<div class="info-item">
<label class="info-label">最后登录IP</label>
<span class="info-value plaintext">{{ info.loginIp }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label class="info-label">最后登录时间</label>
<span class="info-value plaintext">{{ info.loginDate }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mb8">
<el-col :span="24">
<div class="info-item full-width">
<label class="info-label">备注</label>
<span class="info-value plaintext">{{ info.remark }}</span>
</div>
</el-col>
</el-row>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { getUser } from '@/api/system/user'
import type { SysUser } from '@/types/api/system/user'
import type { SysRole } from '@/types/api/system/role'
import type { SysPost } from '@/types/api/system/post'
const visible = ref<boolean>(false)
const loading = ref<boolean>(false)
const info = reactive<SysUser>({})
const postOptions = ref<SysPost[]>([])
const roleOptions = ref<SysRole[]>([])
const { proxy } = getCurrentInstance()
const { sys_user_sex } = proxy.useDict("sys_user_sex")
const sexLabel = computed(() => proxy.selectDictLabel(sys_user_sex.value, info.sex) || '-')
const postNames = computed<string>(() => {
if (!postOptions.value.length || !info.postIds) return ''
return postOptions.value.filter((p: SysPost) => info.postIds?.includes(p.postId)).map((p: SysPost) => p.postName).join('、') || ''
})
const roleNames = computed<string>(() => {
if (!roleOptions.value.length || !info.roleIds) return ''
return roleOptions.value.filter((r: SysRole) => info.roleIds?.includes(r.roleId)).map((r: SysRole) => r.roleName).join('、') || ''
})
const open = async (userId: number): Promise<void> => {
visible.value = true
loading.value = true
try {
const res = await getUser(userId)
Object.assign(info, res.data || {})
postOptions.value = res.posts || []
roleOptions.value = res.roles || []
info.postIds = res.postIds || []
info.roleIds = res.roleIds || []
} catch (error) {
console.error('获取用户信息失败:', error)
} finally {
loading.value = false
}
}
const handleClose = (): void => {
visible.value = false
}
defineExpose({
open
})
</script>