2026-07-21 09:23:43 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-08-17 09:15:36 +08:00
|
|
|
|
import { onMounted, reactive, ref } from 'vue'
|
2026-08-03 09:34:08 +08:00
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
|
|
import {
|
2026-08-10 11:41:16 +08:00
|
|
|
|
changeMyPassword,
|
2026-08-03 09:34:08 +08:00
|
|
|
|
deleteUser,
|
|
|
|
|
|
getUsers,
|
|
|
|
|
|
resetUserPassword,
|
|
|
|
|
|
updateUserAccess,
|
|
|
|
|
|
} from '@/api/modules/system'
|
2026-08-17 09:15:36 +08:00
|
|
|
|
import type { SystemUser, UserStatus } from '@/types'
|
2026-07-23 19:32:42 +08:00
|
|
|
|
import { statusLabel, statusTagType } from '@/utils/status'
|
2026-07-21 09:23:43 +08:00
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const users = ref<SystemUser[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
async function loadUsers() {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
users.value = await getUsers()
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(loadUsers)
|
2026-08-03 09:34:08 +08:00
|
|
|
|
|
|
|
|
|
|
// 当前登录用户,用于禁止操作自身(避免误锁自己)
|
|
|
|
|
|
const currentUsername = ref<string>('')
|
|
|
|
|
|
try {
|
|
|
|
|
|
currentUsername.value = JSON.parse(localStorage.getItem('currentUser') || '{}').username || ''
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
currentUsername.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isSelf(row: SystemUser) {
|
|
|
|
|
|
return row.username === currentUsername.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-03 11:02:41 +08:00
|
|
|
|
function asSystemUser(row: unknown): SystemUser {
|
|
|
|
|
|
return row as SystemUser
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-03 09:34:08 +08:00
|
|
|
|
// ---------- 启停 ----------
|
|
|
|
|
|
async function toggleStatus(row: SystemUser, next: boolean) {
|
|
|
|
|
|
const nextStatus: UserStatus = next ? 'active' : 'disabled'
|
|
|
|
|
|
const prev = row.status
|
|
|
|
|
|
row.status = nextStatus
|
|
|
|
|
|
try {
|
|
|
|
|
|
await updateUserAccess(row.id, { status: nextStatus })
|
|
|
|
|
|
ElMessage.success(`${row.display_name} 已${next ? '启用' : '停用'}`)
|
|
|
|
|
|
await loadUsers()
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
row.status = prev
|
|
|
|
|
|
ElMessage.error('状态更新失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- 重置密码 ----------
|
|
|
|
|
|
const pwdDialog = reactive({ visible: false, id: '', name: '', password: '', saving: false })
|
|
|
|
|
|
function openResetPwd(row: SystemUser) {
|
|
|
|
|
|
pwdDialog.id = row.id
|
|
|
|
|
|
pwdDialog.name = row.display_name
|
|
|
|
|
|
pwdDialog.password = 'Platform@123'
|
|
|
|
|
|
pwdDialog.visible = true
|
|
|
|
|
|
}
|
|
|
|
|
|
async function confirmResetPwd() {
|
|
|
|
|
|
if (!pwdDialog.password.trim()) {
|
|
|
|
|
|
ElMessage.warning('请输入新密码')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
pwdDialog.saving = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
await resetUserPassword(pwdDialog.id, pwdDialog.password.trim())
|
|
|
|
|
|
ElMessage.success(`已重置 ${pwdDialog.name} 的密码`)
|
|
|
|
|
|
pwdDialog.visible = false
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
ElMessage.error('重置密码失败')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
pwdDialog.saving = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-10 11:41:16 +08:00
|
|
|
|
// ---------- 用户自改密码 ----------
|
|
|
|
|
|
const myPwdDialog = reactive({ visible: false, oldPassword: '', newPassword: '', saving: false })
|
|
|
|
|
|
function openChangeMyPwd() {
|
|
|
|
|
|
myPwdDialog.oldPassword = ''
|
|
|
|
|
|
myPwdDialog.newPassword = ''
|
|
|
|
|
|
myPwdDialog.visible = true
|
|
|
|
|
|
}
|
|
|
|
|
|
async function confirmChangeMyPwd() {
|
|
|
|
|
|
if (!myPwdDialog.oldPassword.trim() || !myPwdDialog.newPassword.trim()) {
|
|
|
|
|
|
ElMessage.warning('请填写旧密码和新密码')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (myPwdDialog.newPassword.length < 6) {
|
|
|
|
|
|
ElMessage.warning('新密码至少 6 位')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
myPwdDialog.saving = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
await changeMyPassword(myPwdDialog.oldPassword.trim(), myPwdDialog.newPassword.trim())
|
|
|
|
|
|
ElMessage.success('密码修改成功')
|
|
|
|
|
|
myPwdDialog.visible = false
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
ElMessage.error('密码修改失败,请检查旧密码是否正确')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
myPwdDialog.saving = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-03 09:34:08 +08:00
|
|
|
|
// ---------- 删除 ----------
|
|
|
|
|
|
async function removeUser(row: SystemUser) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await ElMessageBox.confirm(
|
|
|
|
|
|
`确定删除用户 “${row.display_name}(${row.username})” 吗?该操作不可恢复。`,
|
|
|
|
|
|
'删除用户',
|
|
|
|
|
|
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' },
|
|
|
|
|
|
)
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
await deleteUser(row.id)
|
|
|
|
|
|
ElMessage.success(`已删除 ${row.display_name}`)
|
|
|
|
|
|
await loadUsers()
|
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
|
const msg = err?.response?.data?.message || '删除失败'
|
|
|
|
|
|
ElMessage.error(msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<section class="user-settings" v-loading="loading">
|
|
|
|
|
|
<header class="page-header">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h1>用户设置</h1>
|
2026-08-17 09:15:36 +08:00
|
|
|
|
<p>管理平台账号、角色状态与登录密码。</p>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</div>
|
2026-08-10 11:41:16 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<el-button @click="openChangeMyPwd">修改密码</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="$router.push('/user-settings/create')">创建用户</el-button>
|
|
|
|
|
|
</div>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</header>
|
|
|
|
|
|
|
2026-08-03 09:34:08 +08:00
|
|
|
|
<el-table :data="users" border>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
<el-table-column prop="username" label="账号" min-width="140" />
|
|
|
|
|
|
<el-table-column prop="display_name" label="显示名称" min-width="160" />
|
|
|
|
|
|
<el-table-column prop="role" label="角色" width="120" />
|
2026-08-03 09:34:08 +08:00
|
|
|
|
<el-table-column label="状态" width="130">
|
2026-07-23 19:32:42 +08:00
|
|
|
|
<template #default="{ row }">
|
2026-08-03 11:02:41 +08:00
|
|
|
|
<el-tag :type="statusTagType(asSystemUser(row).status)" size="small">{{ statusLabel(asSystemUser(row).status) }}</el-tag>
|
2026-07-23 19:32:42 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
<el-table-column prop="create_time" label="创建时间" min-width="180" />
|
2026-08-03 09:34:08 +08:00
|
|
|
|
<el-table-column label="操作" width="260" fixed="right">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-switch
|
2026-08-03 11:02:41 +08:00
|
|
|
|
:model-value="asSystemUser(row).status === 'active'"
|
|
|
|
|
|
:disabled="asSystemUser(row).protected || isSelf(asSystemUser(row))"
|
|
|
|
|
|
@change="(v: any) => toggleStatus(asSystemUser(row), v)"
|
2026-08-03 09:34:08 +08:00
|
|
|
|
inline-prompt
|
|
|
|
|
|
active-text="启用"
|
|
|
|
|
|
inactive-text="停用"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
link
|
|
|
|
|
|
type="primary"
|
2026-08-03 11:02:41 +08:00
|
|
|
|
:disabled="asSystemUser(row).protected"
|
|
|
|
|
|
@click="openResetPwd(asSystemUser(row))"
|
2026-08-03 09:34:08 +08:00
|
|
|
|
>重置密码</el-button>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
link
|
|
|
|
|
|
type="danger"
|
2026-08-03 11:02:41 +08:00
|
|
|
|
:disabled="asSystemUser(row).protected || isSelf(asSystemUser(row))"
|
|
|
|
|
|
@click="removeUser(asSystemUser(row))"
|
2026-08-03 09:34:08 +08:00
|
|
|
|
>删除</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</el-table>
|
2026-08-03 09:34:08 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 重置密码 -->
|
|
|
|
|
|
<el-dialog v-model="pwdDialog.visible" title="重置密码" width="420px">
|
|
|
|
|
|
<p class="dlg-tip">为 <b>{{ pwdDialog.name }}</b> 设置新密码:</p>
|
|
|
|
|
|
<el-input v-model="pwdDialog.password" placeholder="请输入新密码" show-password />
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="pwdDialog.visible = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" :loading="pwdDialog.saving" @click="confirmResetPwd">确定重置</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
2026-08-10 11:41:16 +08:00
|
|
|
|
<!-- 修改自己的密码 -->
|
|
|
|
|
|
<el-dialog v-model="myPwdDialog.visible" title="修改密码" width="420px">
|
|
|
|
|
|
<el-form label-width="80px">
|
|
|
|
|
|
<el-form-item label="旧密码">
|
|
|
|
|
|
<el-input v-model="myPwdDialog.oldPassword" placeholder="请输入当前密码" show-password />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="新密码">
|
|
|
|
|
|
<el-input v-model="myPwdDialog.newPassword" placeholder="至少 6 位" show-password />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="myPwdDialog.visible = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" :loading="myPwdDialog.saving" @click="confirmChangeMyPwd">确认修改</el-button>
|
2026-08-03 09:34:08 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</section>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.user-settings {
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
margin-bottom: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header h1 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header p {
|
|
|
|
|
|
margin: 8px 0 0;
|
|
|
|
|
|
color: #64748b;
|
|
|
|
|
|
}
|
2026-08-03 09:34:08 +08:00
|
|
|
|
|
|
|
|
|
|
.dlg-tip {
|
|
|
|
|
|
margin: 0 0 12px;
|
|
|
|
|
|
color: #475569;
|
|
|
|
|
|
}
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</style>
|