f7c3be1d30
- Created ImageForm.vue as standalone page for add/edit image functionality - Removed dialog-based image form from VmImages.vue - Implemented tagsViewStore for global tab state management - Added automatic tab closing on form cancel/back - Fixed data persistence issue when switching between image edits - Removed quick actions section from ImageForm - Updated router configuration for new image form route
561 lines
14 KiB
Vue
561 lines
14 KiB
Vue
<template>
|
|
<div class="users-container">
|
|
<!-- 主容器 -->
|
|
<el-card class="main-container" shadow="never">
|
|
<!-- 搜索和操作栏 -->
|
|
<div class="filter-section">
|
|
<div class="filter-content">
|
|
<el-form :inline="true" :model="queryParams" class="search-form">
|
|
<el-form-item label="用户名">
|
|
<el-input v-model="queryParams.username" placeholder="请输入用户名" clearable />
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 150px">
|
|
<el-option label="启用" value="1" />
|
|
<el-option label="禁用" value="0" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="创建时间">
|
|
<el-date-picker
|
|
v-model="queryParams.dateRange"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
value-format="YYYY-MM-DD"
|
|
style="width: 240px"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleQuery">
|
|
<el-icon><Search /></el-icon>查询
|
|
</el-button>
|
|
<el-button @click="resetQuery">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<div class="action-bar">
|
|
<el-button type="primary" @click="handleAdd">
|
|
<el-icon><Plus /></el-icon>新增用户
|
|
</el-button>
|
|
<el-button type="success">
|
|
<el-icon><Upload /></el-icon>导入
|
|
</el-button>
|
|
<el-button type="primary" plain>
|
|
<el-icon><Download /></el-icon>导出
|
|
</el-button>
|
|
<el-button type="danger" :disabled="!selectedRows.length" @click="handleBatchDelete">
|
|
<el-icon><Delete /></el-icon>批量删除
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 用户列表 -->
|
|
<div class="table-section">
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="userList"
|
|
@selection-change="handleSelectionChange"
|
|
style="width: 100%"
|
|
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
|
|
>
|
|
<el-table-column type="selection" width="55" />
|
|
<el-table-column prop="id" label="ID" width="80" />
|
|
<el-table-column label="用户信息" min-width="250">
|
|
<template #default="{ row }">
|
|
<div class="user-info">
|
|
<el-avatar :size="40" :src="row.avatar"></el-avatar>
|
|
<div class="user-detail">
|
|
<div class="username">{{ row.username }}</div>
|
|
<div class="email">{{ row.email }}</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="role" label="角色" />
|
|
<el-table-column prop="phone" label="手机号码" />
|
|
<el-table-column label="状态" width="100">
|
|
<template #default="{ row }">
|
|
<el-switch
|
|
v-model="row.status"
|
|
:active-value="1"
|
|
:inactive-value="0"
|
|
@change="(val) => handleStatusChange(row, val)"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createTime" label="创建时间" width="180" />
|
|
<el-table-column label="操作" width="220" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
|
|
<el-button type="primary" link @click="handleRoleAssign(row)">分配角色</el-button>
|
|
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<!-- 分页 -->
|
|
<el-pagination
|
|
v-model:current-page="queryParams.pageNum"
|
|
v-model:page-size="queryParams.pageSize"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
:total="total"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
background
|
|
class="pagination"
|
|
/>
|
|
</div>
|
|
</el-card>
|
|
|
|
<!-- 用户表单对话框 -->
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:title="dialogType === 'add' ? '新增用户' : '编辑用户'"
|
|
width="580px"
|
|
append-to-body
|
|
>
|
|
<el-form
|
|
ref="userFormRef"
|
|
:model="userForm"
|
|
:rules="userRules"
|
|
label-width="100px"
|
|
>
|
|
<el-form-item label="用户名" prop="username">
|
|
<el-input v-model="userForm.username" placeholder="请输入用户名" />
|
|
</el-form-item>
|
|
<el-form-item label="昵称" prop="nickname">
|
|
<el-input v-model="userForm.nickname" placeholder="请输入昵称" />
|
|
</el-form-item>
|
|
<el-form-item label="手机号码" prop="phone">
|
|
<el-input v-model="userForm.phone" placeholder="请输入手机号码" />
|
|
</el-form-item>
|
|
<el-form-item label="邮箱" prop="email">
|
|
<el-input v-model="userForm.email" placeholder="请输入邮箱" />
|
|
</el-form-item>
|
|
<el-form-item label="角色" prop="roleIds">
|
|
<el-select
|
|
v-model="userForm.roleIds"
|
|
placeholder="请选择角色"
|
|
multiple
|
|
style="width: 100%"
|
|
>
|
|
<el-option
|
|
v-for="item in roleOptions"
|
|
:key="item.id"
|
|
:label="item.name"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-radio-group v-model="userForm.status">
|
|
<el-radio :label="1">启用</el-radio>
|
|
<el-radio :label="0">禁用</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label="备注" prop="remark">
|
|
<el-input
|
|
v-model="userForm.remark"
|
|
type="textarea"
|
|
placeholder="请输入备注"
|
|
:rows="3"
|
|
/>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<div class="dialog-footer">
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="submitForm">确定</el-button>
|
|
</div>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { Plus, Delete, Upload, Download, Search } from '@element-plus/icons-vue'
|
|
|
|
// 查询参数
|
|
const queryParams = reactive({
|
|
username: '',
|
|
status: '',
|
|
dateRange: [],
|
|
pageNum: 1,
|
|
pageSize: 10
|
|
})
|
|
|
|
// 用户表单
|
|
const userForm = reactive({
|
|
id: undefined,
|
|
username: '',
|
|
nickname: '',
|
|
phone: '',
|
|
email: '',
|
|
roleIds: [],
|
|
status: 1,
|
|
remark: ''
|
|
})
|
|
|
|
// 表单规则
|
|
const userRules = {
|
|
username: [
|
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
|
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
|
|
],
|
|
nickname: [
|
|
{ required: true, message: '请输入昵称', trigger: 'blur' }
|
|
],
|
|
phone: [
|
|
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
|
],
|
|
email: [
|
|
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
// 角色选项
|
|
const roleOptions = ref([
|
|
{ id: 1, name: '管理员' },
|
|
{ id: 2, name: '测试员' },
|
|
{ id: 3, name: '普通用户' },
|
|
{ id: 4, name: '访客' }
|
|
])
|
|
|
|
// 其他状态数据
|
|
const loading = ref(false)
|
|
const userList = ref([])
|
|
const total = ref(0)
|
|
const selectedRows = ref([])
|
|
const dialogVisible = ref(false)
|
|
const dialogType = ref('add') // 'add' 或 'edit'
|
|
const userFormRef = ref(null)
|
|
|
|
// 模拟获取用户列表
|
|
const getUserList = () => {
|
|
loading.value = true
|
|
// 模拟API请求
|
|
setTimeout(() => {
|
|
userList.value = [
|
|
{
|
|
id: 1,
|
|
username: 'admin',
|
|
nickname: '管理员',
|
|
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
|
email: 'admin@example.com',
|
|
phone: '13800138000',
|
|
role: '超级管理员',
|
|
status: 1,
|
|
createTime: '2024-06-01 10:00:00'
|
|
},
|
|
{
|
|
id: 2,
|
|
username: 'test',
|
|
nickname: '测试用户',
|
|
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
|
email: 'test@example.com',
|
|
phone: '13800138001',
|
|
role: '测试人员',
|
|
status: 1,
|
|
createTime: '2024-06-02 14:23:10'
|
|
},
|
|
{
|
|
id: 3,
|
|
username: 'zhangsan',
|
|
nickname: '张三',
|
|
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
|
email: 'zhangsan@example.com',
|
|
phone: '13800138002',
|
|
role: '普通用户',
|
|
status: 0,
|
|
createTime: '2024-06-03 08:15:30'
|
|
},
|
|
{
|
|
id: 4,
|
|
username: 'lisi',
|
|
nickname: '李四',
|
|
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
|
email: 'lisi@example.com',
|
|
phone: '13800138003',
|
|
role: '普通用户',
|
|
status: 1,
|
|
createTime: '2024-06-03 16:42:50'
|
|
},
|
|
{
|
|
id: 5,
|
|
username: 'wangwu',
|
|
nickname: '王五',
|
|
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
|
email: 'wangwu@example.com',
|
|
phone: '13800138004',
|
|
role: '访客',
|
|
status: 1,
|
|
createTime: '2024-06-04 09:30:15'
|
|
}
|
|
]
|
|
total.value = 5
|
|
loading.value = false
|
|
}, 500)
|
|
}
|
|
|
|
// 查询用户列表
|
|
const handleQuery = () => {
|
|
queryParams.pageNum = 1
|
|
getUserList()
|
|
}
|
|
|
|
// 重置查询
|
|
const resetQuery = () => {
|
|
Object.assign(queryParams, {
|
|
username: '',
|
|
status: '',
|
|
dateRange: [],
|
|
pageNum: 1,
|
|
pageSize: 10
|
|
})
|
|
getUserList()
|
|
}
|
|
|
|
// 选择项变化
|
|
const handleSelectionChange = (selection) => {
|
|
selectedRows.value = selection
|
|
}
|
|
|
|
// 分页大小变化
|
|
const handleSizeChange = (size) => {
|
|
queryParams.pageSize = size
|
|
getUserList()
|
|
}
|
|
|
|
// 分页页码变化
|
|
const handleCurrentChange = (page) => {
|
|
queryParams.pageNum = page
|
|
getUserList()
|
|
}
|
|
|
|
// 新增用户
|
|
const handleAdd = () => {
|
|
dialogType.value = 'add'
|
|
dialogVisible.value = true
|
|
// 重置表单
|
|
Object.assign(userForm, {
|
|
id: undefined,
|
|
username: '',
|
|
nickname: '',
|
|
phone: '',
|
|
email: '',
|
|
roleIds: [],
|
|
status: 1,
|
|
remark: ''
|
|
})
|
|
// 重置表单校验结果
|
|
if (userFormRef.value) {
|
|
userFormRef.value.resetFields()
|
|
}
|
|
}
|
|
|
|
// 编辑用户
|
|
const handleEdit = (row) => {
|
|
dialogType.value = 'edit'
|
|
dialogVisible.value = true
|
|
// 填充表单数据
|
|
Object.assign(userForm, {
|
|
id: row.id,
|
|
username: row.username,
|
|
nickname: row.nickname,
|
|
phone: row.phone,
|
|
email: row.email,
|
|
roleIds: [1], // 假设已有角色
|
|
status: row.status,
|
|
remark: row.remark || ''
|
|
})
|
|
// 重置表单校验结果
|
|
if (userFormRef.value) {
|
|
userFormRef.value.resetFields()
|
|
}
|
|
}
|
|
|
|
// 删除用户
|
|
const handleDelete = (row) => {
|
|
ElMessageBox.confirm(`确认删除用户 ${row.username} 吗?`, '警告', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
// 执行删除操作
|
|
ElMessage.success('删除成功')
|
|
getUserList()
|
|
}).catch(() => {})
|
|
}
|
|
|
|
// 批量删除
|
|
const handleBatchDelete = () => {
|
|
if (selectedRows.value.length === 0) {
|
|
ElMessage.warning('请至少选择一条记录')
|
|
return
|
|
}
|
|
ElMessageBox.confirm(`确认删除选中的 ${selectedRows.value.length} 条用户记录吗?`, '警告', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
// 执行批量删除操作
|
|
ElMessage.success('批量删除成功')
|
|
getUserList()
|
|
}).catch(() => {})
|
|
}
|
|
|
|
// 分配角色
|
|
const handleRoleAssign = (row) => {
|
|
ElMessage.info(`为用户 ${row.username} 分配角色功能待实现`)
|
|
}
|
|
|
|
// 修改用户状态
|
|
const handleStatusChange = (row, status) => {
|
|
ElMessage.success(`修改用户 ${row.username} 状态为 ${status === 1 ? '启用' : '禁用'} 成功`)
|
|
}
|
|
|
|
// 提交表单
|
|
const submitForm = () => {
|
|
userFormRef.value?.validate((valid) => {
|
|
if (valid) {
|
|
if (dialogType.value === 'add') {
|
|
// 新增用户
|
|
ElMessage.success('新增用户成功')
|
|
} else {
|
|
// 修改用户
|
|
ElMessage.success('修改用户成功')
|
|
}
|
|
dialogVisible.value = false
|
|
getUserList()
|
|
}
|
|
})
|
|
}
|
|
|
|
// 初始化
|
|
onMounted(() => {
|
|
getUserList()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.users-container {
|
|
padding: 0;
|
|
}
|
|
|
|
.main-container {
|
|
border: 1px solid #e1e8ed;
|
|
background: #ffffff;
|
|
}
|
|
|
|
.filter-section {
|
|
padding: 0;
|
|
border-bottom: 1px solid #e1e8ed;
|
|
background: #fafbfc;
|
|
}
|
|
|
|
.filter-content {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16px 20px;
|
|
gap: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.search-form {
|
|
margin: 0;
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.search-form :deep(.el-form-item) {
|
|
margin-bottom: 0;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.action-bar {
|
|
display: flex;
|
|
gap: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.table-section {
|
|
padding: 0;
|
|
}
|
|
|
|
.pagination {
|
|
margin-top: 20px;
|
|
padding: 16px 20px;
|
|
border-top: 1px solid #e1e8ed;
|
|
background: #fafbfc;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 4px 0;
|
|
}
|
|
|
|
.user-detail {
|
|
margin-left: 12px;
|
|
}
|
|
|
|
.username {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
margin-bottom: 4px;
|
|
color: #1f2937;
|
|
}
|
|
|
|
.email {
|
|
font-size: 12px;
|
|
color: #64748b;
|
|
}
|
|
|
|
.dialog-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* 表格样式优化 */
|
|
:deep(.el-table) {
|
|
border: none;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
:deep(.el-table__header) {
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
:deep(.el-table th) {
|
|
background: #f8f9fa !important;
|
|
border-bottom: 2px solid #e1e8ed;
|
|
color: #2c3e50;
|
|
font-weight: 600;
|
|
font-size: 13px;
|
|
}
|
|
|
|
:deep(.el-table td) {
|
|
border-bottom: 1px solid #f0f2f5;
|
|
color: #34495e;
|
|
}
|
|
|
|
:deep(.el-table tr:hover > td) {
|
|
background-color: #f8f9fa !important;
|
|
}
|
|
|
|
:deep(.el-card__body) {
|
|
padding: 0;
|
|
}
|
|
</style> |