297 lines
8.3 KiB
Vue
297 lines
8.3 KiB
Vue
<template>
|
|
<div class="userManageWrap">
|
|
<div class="userManageCard">
|
|
<div class="headerRow">
|
|
<h2>用户管理</h2>
|
|
<el-button type="primary" @click="handleAdd">新增用户</el-button>
|
|
</div>
|
|
|
|
<el-table :data="tableData" v-loading="loading" stripe style="width: 100%">
|
|
<el-table-column prop="id" label="ID" width="70" />
|
|
<el-table-column prop="tel" label="手机号" width="140" />
|
|
<el-table-column prop="name" label="姓名" width="140" />
|
|
<el-table-column prop="nickName" label="昵称" width="140" />
|
|
<el-table-column prop="status" label="状态" width="80">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small">
|
|
{{ row.status === 1 ? '正常' : '禁用' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createTime" label="创建时间" width="170" />
|
|
<el-table-column label="操作" min-width="200" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button link type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
|
<el-button link type="warning" size="small" @click="handleResetPwd(row)">重置密码</el-button>
|
|
<el-popconfirm title="确定删除该用户?" @confirm="handleDelete(row)">
|
|
<template #reference>
|
|
<el-button link type="danger" size="small">删除</el-button>
|
|
</template>
|
|
</el-popconfirm>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div class="paginationRow">
|
|
<el-pagination
|
|
v-model:current-page="currentPage"
|
|
v-model:page-size="pageSize"
|
|
:total="total"
|
|
:page-sizes="[10, 20, 50]"
|
|
layout="total, sizes, prev, pager, next"
|
|
@size-change="loadUsers"
|
|
@current-change="loadUsers"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 新增/编辑用户弹窗 -->
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:title="dialogType === 'add' ? '新增用户' : '编辑用户'"
|
|
width="460px"
|
|
destroy-on-close
|
|
>
|
|
<el-form
|
|
ref="userFormRef"
|
|
:model="userForm"
|
|
:rules="userRules"
|
|
label-width="80px"
|
|
>
|
|
<el-form-item label="手机号" prop="tel">
|
|
<el-input v-model="userForm.tel" :disabled="dialogType === 'edit'" />
|
|
</el-form-item>
|
|
<el-form-item label="姓名" prop="name">
|
|
<el-input v-model="userForm.name" />
|
|
</el-form-item>
|
|
<el-form-item label="昵称">
|
|
<el-input v-model="userForm.nickName" />
|
|
</el-form-item>
|
|
<el-form-item v-if="dialogType === 'add'" label="密码" prop="password">
|
|
<el-input v-model="userForm.password" type="password" show-password />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleSubmit" :loading="submitting">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 重置密码弹窗 -->
|
|
<el-dialog v-model="resetPwdVisible" title="重置密码" width="400px" destroy-on-close>
|
|
<el-form ref="resetPwdFormRef" :model="resetPwdForm" :rules="resetPwdRules" label-width="80px">
|
|
<el-form-item label="用户">
|
|
<el-input :value="resetPwdForm.tel" disabled />
|
|
</el-form-item>
|
|
<el-form-item label="新密码" prop="newPassword">
|
|
<el-input v-model="resetPwdForm.newPassword" type="password" show-password />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="resetPwdVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleResetPwdSubmit" :loading="resettingPwd">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import { fetchUserPage, createUser, editUser, deleteUser, resetUserPassword } from '@/api'
|
|
|
|
const tableData = ref<any[]>([])
|
|
const loading = ref(false)
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(10)
|
|
const total = ref(0)
|
|
|
|
// 新增/编辑
|
|
const dialogVisible = ref(false)
|
|
const dialogType = ref<'add' | 'edit'>('add')
|
|
const userFormRef = ref<FormInstance>()
|
|
const submitting = ref(false)
|
|
const userForm = reactive({
|
|
id: 0,
|
|
tel: '',
|
|
name: '',
|
|
nickName: '',
|
|
password: '',
|
|
})
|
|
|
|
const userRules = reactive<FormRules>({
|
|
tel: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
|
|
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
|
password: [
|
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
|
{ min: 6, message: '密码长度至少6位', trigger: 'blur' },
|
|
],
|
|
})
|
|
|
|
// 重置密码
|
|
const resetPwdVisible = ref(false)
|
|
const resetPwdFormRef = ref<FormInstance>()
|
|
const resettingPwd = ref(false)
|
|
const resetPwdForm = reactive({
|
|
id: 0,
|
|
tel: '',
|
|
newPassword: '',
|
|
})
|
|
|
|
const resetPwdRules = reactive<FormRules>({
|
|
newPassword: [
|
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
|
{ min: 6, message: '密码长度至少6位', trigger: 'blur' },
|
|
],
|
|
})
|
|
|
|
const loadUsers = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await fetchUserPage<any>({
|
|
current: currentPage.value,
|
|
size: pageSize.value,
|
|
})
|
|
if (res.code === 200) {
|
|
tableData.value = res.data.records || []
|
|
total.value = res.data.total || 0
|
|
}
|
|
} catch (e: any) {
|
|
console.error('加载用户列表失败:', e)
|
|
ElMessage.error('加载用户列表失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const handleAdd = () => {
|
|
dialogType.value = 'add'
|
|
userForm.id = 0
|
|
userForm.tel = ''
|
|
userForm.name = ''
|
|
userForm.nickName = ''
|
|
userForm.password = ''
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
const handleEdit = (row: any) => {
|
|
dialogType.value = 'edit'
|
|
userForm.id = row.id
|
|
userForm.tel = row.tel
|
|
userForm.name = row.name || ''
|
|
userForm.nickName = row.nickName || ''
|
|
userForm.password = ''
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
const handleSubmit = async () => {
|
|
const valid = await userFormRef.value?.validate().catch(() => false)
|
|
if (!valid) return
|
|
|
|
submitting.value = true
|
|
try {
|
|
let res: any
|
|
if (dialogType.value === 'add') {
|
|
res = await createUser<any>({
|
|
tel: userForm.tel,
|
|
name: userForm.name,
|
|
nickName: userForm.nickName,
|
|
password: userForm.password,
|
|
})
|
|
} else {
|
|
res = await editUser<any>({
|
|
id: userForm.id,
|
|
name: userForm.name,
|
|
nickName: userForm.nickName,
|
|
})
|
|
}
|
|
if (res.code === 200) {
|
|
ElMessage.success(dialogType.value === 'add' ? '新增成功' : '编辑成功')
|
|
dialogVisible.value = false
|
|
loadUsers()
|
|
} else {
|
|
ElMessage.error(res.msg || '操作失败')
|
|
}
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
const handleResetPwd = (row: any) => {
|
|
resetPwdForm.id = row.id
|
|
resetPwdForm.tel = row.tel
|
|
resetPwdForm.newPassword = ''
|
|
resetPwdVisible.value = true
|
|
}
|
|
|
|
const handleResetPwdSubmit = async () => {
|
|
const valid = await resetPwdFormRef.value?.validate().catch(() => false)
|
|
if (!valid) return
|
|
|
|
resettingPwd.value = true
|
|
try {
|
|
const res = await resetUserPassword<any>({
|
|
id: resetPwdForm.id,
|
|
newPassword: resetPwdForm.newPassword,
|
|
})
|
|
if (res.code === 200) {
|
|
ElMessage.success('密码重置成功')
|
|
resetPwdVisible.value = false
|
|
} else {
|
|
ElMessage.error(res.msg || '重置失败')
|
|
}
|
|
} finally {
|
|
resettingPwd.value = false
|
|
}
|
|
}
|
|
|
|
const handleDelete = async (row: any) => {
|
|
const res = await deleteUser<any>(row.id)
|
|
if (res.code === 200) {
|
|
ElMessage.success('删除成功')
|
|
loadUsers()
|
|
} else {
|
|
ElMessage.error(res.msg || '删除失败')
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadUsers()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.userManageWrap {
|
|
flex: 1;
|
|
padding: 30px;
|
|
overflow-y: auto;
|
|
z-index: 1;
|
|
}
|
|
|
|
.userManageCard {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
padding: 24px 30px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
.headerRow {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
|
|
h2 {
|
|
font-size: 20px;
|
|
color: #333;
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
.paginationRow {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 16px;
|
|
}
|
|
</style>
|