feat: 新增账号管理功能,完善权限控制与用户信息同步
1. 新增用户管理后台接口与前端页面,支持管理员增删改查用户账号 2. 新增RBAC权限控制系统,按角色分配菜单与操作权限 3. 优化登录响应与用户信息接口,返回完整权限码与用户数据 4. 新增密码安全校验,提示纯数字密码风险 5. 移除废弃的注册接口,统一用户管理逻辑 6. 更新前端路由与侧边栏权限过滤逻辑
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
<el-form-item label="新密码">
|
||||
<el-input v-model="form.newPassword" type="password" show-password
|
||||
placeholder="至少 6 位" />
|
||||
<el-alert v-if="pwdAllNum" type="warning" :closable="false" style="margin-top:6px;width:100%"
|
||||
title="密码为纯数字,安全性较低,建议包含字母与数字组合" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码">
|
||||
<el-input v-model="form.confirmPassword" type="password" show-password
|
||||
@@ -25,7 +27,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { logout } from '../utils/auth'
|
||||
@@ -34,6 +36,7 @@ import request from '../utils/request'
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const form = reactive({ oldPassword: '', newPassword: '', confirmPassword: '' })
|
||||
const pwdAllNum = computed(() => form.newPassword.length > 0 && /^[\d]+$/.test(form.newPassword))
|
||||
|
||||
async function submit() {
|
||||
if (!form.oldPassword || !form.newPassword) return ElMessage.warning('请填写旧密码和新密码')
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { fmtTime } from '../utils/format'
|
||||
|
||||
const ROLE_LABELS = { admin: '管理员', operator: '库房操作员', inspector: '质检员' }
|
||||
const roleOptions = [
|
||||
{ value: 'admin', label: '管理员' },
|
||||
{ value: 'operator', label: '库房操作员' },
|
||||
{ value: 'inspector', label: '质检员' }
|
||||
]
|
||||
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const page = reactive({ current: 1, size: 10 })
|
||||
|
||||
async function loadUsers() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await request.get('/user/list')
|
||||
list.value = data?.list || []
|
||||
total.value = list.value.length
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 新增 / 编辑 弹窗 ---------- */
|
||||
const dialogVisible = ref(false)
|
||||
const editingId = ref(0)
|
||||
const saving = ref(false)
|
||||
const form = reactive({ username: '', realName: '', role: 'operator', dept: '', password: '' })
|
||||
|
||||
const pwdAllNum = computed(() => /^[\d]+$/.test(form.password))
|
||||
function resetForm() {
|
||||
editingId.value = 0
|
||||
Object.assign(form, { username: '', realName: '', role: 'operator', dept: '', password: '' })
|
||||
}
|
||||
function openCreate() {
|
||||
resetForm()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
function openEdit(row) {
|
||||
resetForm()
|
||||
editingId.value = row.id
|
||||
form.username = row.username
|
||||
form.realName = row.realName || ''
|
||||
form.role = row.role || 'operator'
|
||||
form.dept = row.dept || ''
|
||||
form.password = '' // 编辑时不填 = 不改密码
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!form.username.trim()) return ElMessage.warning('请输入用户名')
|
||||
if (!editingId.value && !form.password) return ElMessage.warning('请输入初始密码')
|
||||
if (form.password && form.password.length < 6) return ElMessage.warning('密码长度至少 6 位')
|
||||
saving.value = true
|
||||
try {
|
||||
if (editingId.value) {
|
||||
const payload = {
|
||||
id: editingId.value,
|
||||
realName: form.realName.trim() || undefined,
|
||||
role: form.role,
|
||||
dept: form.dept.trim() || undefined,
|
||||
isActive: undefined
|
||||
}
|
||||
if (form.password) payload.password = form.password // 填写则重置密码
|
||||
await request.post('/user/update', payload)
|
||||
ElMessage.success('保存成功')
|
||||
} else {
|
||||
await request.post('/user/create', {
|
||||
username: form.username.trim(),
|
||||
realName: form.realName.trim() || undefined,
|
||||
role: form.role,
|
||||
dept: form.dept.trim() || undefined,
|
||||
password: form.password
|
||||
})
|
||||
ElMessage.success('账号创建成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
await loadUsers()
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleActive(row) {
|
||||
try {
|
||||
await request.post('/user/update', { id: row.id, isActive: !row.isActive })
|
||||
ElMessage.success(row.isActive ? '已停用' : '已启用')
|
||||
await loadUsers()
|
||||
} catch { /* 拦截器已提示 */ }
|
||||
}
|
||||
|
||||
async function removeUser(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除账号「${row.username}」?该操作不可恢复。`, '删除确认', {
|
||||
type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消'
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await request.post('/user/delete', { id: row.id })
|
||||
ElMessage.success('已删除')
|
||||
await loadUsers()
|
||||
} catch { /* 拦截器已提示 */ }
|
||||
}
|
||||
|
||||
onMounted(loadUsers)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="never" class="mb12">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<div>
|
||||
<b>账号管理</b>
|
||||
<span style="margin-left:8px;color:#909399;font-size:12px">仅管理员可访问;角色决定菜单与功能权限</span>
|
||||
</div>
|
||||
<el-button type="primary" :icon="'Plus'" @click="openCreate">新增账号</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table :data="list" border size="small" v-loading="loading" empty-text="暂无账号">
|
||||
<el-table-column prop="id" label="ID" width="64" align="center" />
|
||||
<el-table-column prop="username" label="用户名" min-width="120" />
|
||||
<el-table-column prop="realName" label="姓名" min-width="110">
|
||||
<template #default="{ row }">{{ row.realName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="角色" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.role === 'admin' ? 'danger' : row.role === 'inspector' ? 'warning' : 'primary'" size="small">
|
||||
{{ ROLE_LABELS[row.role] || row.role }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dept" label="部门" min-width="100">
|
||||
<template #default="{ row }">{{ row.dept || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch :model-value="row.isActive" @change="toggleActive(row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最近登录" width="150" align="center">
|
||||
<template #default="{ row }">{{ row.lastLoginAt ? fmtTime(row.lastLoginAt) : '从未' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="removeUser(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="editingId ? '编辑账号' : '新增账号'" width="520px"
|
||||
append-to-body destroy-on-close @closed="resetForm">
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="用户名" required>
|
||||
<el-input v-model="form.username" :disabled="!!editingId" placeholder="登录名(创建后不可改)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="form.realName" placeholder="显示名(可空)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" required>
|
||||
<el-select v-model="form.role" style="width:100%">
|
||||
<el-option v-for="o in roleOptions" :key="o.value" :value="o.value" :label="o.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门">
|
||||
<el-input v-model="form.dept" placeholder="部门(可空)" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="editingId ? '重置密码' : '初始密码'" required>
|
||||
<el-input v-model="form.password" type="password" show-password
|
||||
:placeholder="editingId ? '不填则保持原密码(至少6位)' : '至少 6 位'" />
|
||||
<el-alert v-if="pwdAllNum" type="warning" :closable="false" style="margin-top:6px"
|
||||
title="密码为纯数字,安全性较低,建议包含字母与数字组合" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.mb12 { margin-bottom: 12px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user