新增用户注册功能

This commit is contained in:
Cheng Zhou
2025-12-22 21:19:48 +08:00
parent 03fddba406
commit 6ddf9901a0
33 changed files with 1389 additions and 138 deletions
@@ -0,0 +1,135 @@
<template>
<div class="page">
<el-card>
<div class="header">
<div>
<h3>注册审核</h3>
<p>仅显示待审核账号审核通过后即可登录</p>
</div>
<el-select v-model="status" size="small" style="width: 180px" @change="loadUsers">
<el-option label="待审核" value="PENDING" />
<el-option label="已通过" value="ACTIVE" />
<el-option label="已拒绝" value="REJECTED" />
<el-option label="已禁用" value="DISABLED" />
</el-select>
</div>
<el-table :data="users" v-loading="loading" style="width: 100%">
<el-table-column prop="email" label="邮箱" min-width="200" />
<el-table-column prop="full_name" label="姓名" min-width="140" />
<el-table-column prop="role" label="角色" width="120" />
<el-table-column prop="department" label="部门" min-width="160" />
<el-table-column prop="created_at" label="注册时间" width="200">
<template #default="scope">{{ displayDateTime(scope.row.created_at) }}</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template #default="scope">
<el-tag :type="statusType(scope.row.status)">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template #default="scope">
<el-button
v-if="scope.row.status === 'PENDING'"
type="success"
link
size="small"
@click="onApprove(scope.row.id)"
>
审核通过
</el-button>
<el-button
v-if="scope.row.status === 'PENDING'"
type="danger"
link
size="small"
@click="onReject(scope.row.id)"
>
拒绝
</el-button>
<span v-else></span>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { ElMessage } from "element-plus";
import { approveUser, listPendingUsers, rejectUser } from "../../api/admin";
import type { UserInfo, UserStatus } from "../../types/api";
import { displayDateTime } from "../../utils/display";
const users = ref<UserInfo[]>([]);
const loading = ref(false);
const status = ref<UserStatus>("PENDING");
const statusType = (s: UserStatus) => {
switch (s) {
case "ACTIVE":
return "success";
case "PENDING":
return "warning";
case "REJECTED":
return "danger";
default:
return "info";
}
};
const loadUsers = async () => {
loading.value = true;
try {
const { data } = await listPendingUsers(status.value);
users.value = data.items || [];
} catch (error: any) {
ElMessage.error(error?.response?.data?.message || "加载待审核用户失败");
} finally {
loading.value = false;
}
};
const onApprove = async (id: string) => {
try {
await approveUser(id);
ElMessage.success("已通过审核");
loadUsers();
} catch (error: any) {
ElMessage.error(error?.response?.data?.message || "审核失败");
}
};
const onReject = async (id: string) => {
try {
await rejectUser(id);
ElMessage.success("已拒绝");
loadUsers();
} catch (error: any) {
ElMessage.error(error?.response?.data?.message || "拒绝失败");
}
};
onMounted(() => {
loadUsers();
});
</script>
<style scoped>
.page {
padding: 16px;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.header h3 {
margin: 0;
}
.header p {
margin: 4px 0 0;
color: #6c7a89;
}
</style>
+31 -8
View File
@@ -1,8 +1,14 @@
<template>
<el-dialog :title="user ? '编辑用户' : '新建用户'" width="520px" v-model="visibleProxy" :close-on-click-modal="false">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" :disabled="!!user" placeholder="请输入用户名" />
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" :disabled="!!user" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="姓名" prop="full_name">
<el-input v-model="form.full_name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="部门" prop="department">
<el-input v-model="form.department" placeholder="请输入部门" />
</el-form-item>
<el-form-item :label="user ? '新密码(可选)' : '初始密码'" prop="password">
<el-input v-model="form.password" type="password" show-password :placeholder="user ? '留空则不修改密码' : '请输入初始密码'" />
@@ -42,13 +48,22 @@ const visibleProxy = computed({
const formRef = ref<FormInstance>();
const submitting = ref(false);
const form = reactive({
username: "",
email: "",
full_name: "",
department: "",
password: "",
is_active: true,
});
const defaultRole = "PV";
const rules = reactive<FormRules>({
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
email: [
{ required: true, message: "请输入邮箱", trigger: "blur" },
{ type: "email", message: "邮箱格式不正确", trigger: "blur" },
],
full_name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
department: [{ required: true, message: "请输入部门", trigger: "blur" }],
password: [
{
validator: (_rule, value, callback) => {
@@ -64,7 +79,9 @@ const rules = reactive<FormRules>({
});
const resetForm = () => {
form.username = "";
form.email = "";
form.full_name = "";
form.department = "";
form.password = "";
form.is_active = true;
};
@@ -75,7 +92,9 @@ watch(
if (val) {
resetForm();
if (props.user) {
form.username = props.user.username;
form.email = props.user.email;
form.full_name = props.user.full_name;
form.department = props.user.department;
form.is_active = props.user.is_active;
}
}
@@ -90,13 +109,17 @@ const onSubmit = async () => {
if (props.user) {
await updateUser(props.user.id, {
is_active: form.is_active,
full_name: form.full_name,
department: form.department,
password: form.password || undefined,
});
ElMessage.success("用户已更新");
} else {
await createUser({
username: form.username,
role: "PV",
email: form.email,
full_name: form.full_name,
department: form.department,
role: defaultRole,
password: form.password,
});
ElMessage.success("用户已创建");
+39 -8
View File
@@ -9,11 +9,14 @@
<el-button type="primary" @click="openCreate">新建用户</el-button>
</div>
<el-table :data="users" stripe v-loading="loading" style="width: 100%">
<el-table-column prop="username" label="用户名" min-width="140" />
<el-table-column label="状态" width="120">
<el-table-column prop="email" label="邮箱" min-width="200" />
<el-table-column prop="full_name" label="姓名" min-width="140" />
<el-table-column prop="department" label="部门" min-width="140" />
<el-table-column label="状态" width="140">
<template #default="scope">
<el-tag type="success" v-if="scope.row.is_active">启用</el-tag>
<el-tag type="danger" v-else>已禁用</el-tag>
<el-tag :type="statusType(scope.row.status)">
{{ statusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="created_at" label="创建时间" width="200">
@@ -24,11 +27,11 @@
<el-button link type="primary" size="small" @click="openEdit(scope.row)">编辑</el-button>
<el-button
link
:type="scope.row.is_active ? 'danger' : 'primary'"
:type="scope.row.status === 'ACTIVE' ? 'danger' : 'primary'"
size="small"
@click="toggleStatus(scope.row)"
>
{{ scope.row.is_active ? "禁用" : "启用" }}
{{ scope.row.status === 'ACTIVE' ? "禁用" : "启用" }}
</el-button>
<el-button link type="danger" size="small" @click="openReset(scope.row)">重置密码</el-button>
<el-button
@@ -105,7 +108,7 @@ const openEdit = (row: UserInfo) => {
};
const toggleStatus = async (row: UserInfo) => {
const disable = row.is_active;
const disable = row.status === "ACTIVE";
const ok = await ElMessageBox.confirm(
disable ? "该操作将影响用户账号,请确认是否继续" : "确认启用该用户账号?",
disable ? "禁用账号" : "启用账号",
@@ -117,7 +120,7 @@ const toggleStatus = async (row: UserInfo) => {
).catch(() => null);
if (!ok) return;
try {
await updateUser(row.id, { is_active: !row.is_active });
await updateUser(row.id, { status: disable ? "DISABLED" : "ACTIVE", is_active: !disable });
ElMessage.success(disable ? "已禁用" : "已启用");
loadUsers();
} catch (e: any) {
@@ -161,6 +164,34 @@ const onDelete = async (row: UserInfo) => {
onMounted(() => {
loadUsers();
});
const statusType = (status: string) => {
switch (status) {
case "ACTIVE":
return "success";
case "PENDING":
return "warning";
case "REJECTED":
return "danger";
default:
return "info";
}
};
const statusLabel = (status: string) => {
switch (status) {
case "ACTIVE":
return "启用";
case "PENDING":
return "待审核";
case "REJECTED":
return "已拒绝";
case "DISABLED":
return "已停用";
default:
return status || "未知";
}
};
</script>
<style scoped>