|
|
@@ -0,0 +1,422 @@
|
|
|
+<template>
|
|
|
+ <div class="admin-page">
|
|
|
+ <div class="admin-header">
|
|
|
+ <div class="admin-search">
|
|
|
+ <el-input
|
|
|
+ placeholder="请输入用户名或姓名搜索"
|
|
|
+ v-model="searchKeyword"
|
|
|
+ clearable
|
|
|
+ @clear="handleSearch"
|
|
|
+ @keyup.enter="handleSearch"
|
|
|
+ >
|
|
|
+ <template #append>
|
|
|
+ <el-button icon="el-icon-search" @click="handleSearch" />
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ <div class="admin-actions">
|
|
|
+ <el-button type="primary" @click="handleAddUser">添加用户</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="admin-main">
|
|
|
+ <el-table
|
|
|
+ :data="userList"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ style="width: 100%"
|
|
|
+ v-loading="loading"
|
|
|
+ >
|
|
|
+ <el-table-column prop="id" label="ID" width="80" align="center" />
|
|
|
+ <el-table-column prop="username" label="用户名" width="150" />
|
|
|
+ <el-table-column prop="name" label="姓名" width="120" />
|
|
|
+ <el-table-column prop="phone" label="手机号" width="150" />
|
|
|
+ <el-table-column prop="email" label="邮箱" width="200" />
|
|
|
+ <el-table-column label="头像" width="120" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-avatar :size="50" :src="row.photo" v-if="row.photo" />
|
|
|
+ <el-avatar :size="50" v-else>{{ row.name || '用户' }}</el-avatar>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="role" label="角色" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="getRoleTagType(row.role)">
|
|
|
+ {{ row.role }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200" fixed="right" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button size="mini" @click="handleEdit(row)">编辑</el-button>
|
|
|
+ <el-button
|
|
|
+ size="mini"
|
|
|
+ type="danger"
|
|
|
+ @click="handleDelete(row.id)"
|
|
|
+ >删除</el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="pagination">
|
|
|
+ <el-pagination
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ :current-page="page"
|
|
|
+ :page-sizes="[5, 10, 20, 50]"
|
|
|
+ :page-size="size"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :total="total"
|
|
|
+ >
|
|
|
+ </el-pagination>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 添加/编辑用户对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ :title="dialogTitle"
|
|
|
+ v-model="dialogVisible"
|
|
|
+ width="50%"
|
|
|
+ @closed="resetForm"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ :model="userForm"
|
|
|
+ :rules="rules"
|
|
|
+ ref="userFormRef"
|
|
|
+ label-width="100px"
|
|
|
+ >
|
|
|
+ <el-form-item label="用户名" prop="username">
|
|
|
+ <el-input v-model="userForm.username" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="姓名" prop="name">
|
|
|
+ <el-input v-model="userForm.name" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="手机号" prop="phone">
|
|
|
+ <el-input v-model="userForm.phone" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="邮箱" prop="email">
|
|
|
+ <el-input v-model="userForm.email" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="密码" prop="password" v-if="isAdd">
|
|
|
+ <el-input v-model="userForm.password" show-password />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="角色" prop="role">
|
|
|
+ <el-select v-model="userForm.role" placeholder="请选择角色">
|
|
|
+ <el-option
|
|
|
+ v-for="role in roleOptions"
|
|
|
+ :key="role.value"
|
|
|
+ :label="role.label"
|
|
|
+ :value="role.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="头像">
|
|
|
+ <el-upload
|
|
|
+ class="avatar-uploader"
|
|
|
+ action="/api/upload"
|
|
|
+ :show-file-list="false"
|
|
|
+ :on-success="handleAvatarSuccess"
|
|
|
+ :before-upload="beforeAvatarUpload"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ v-if="userForm.photo"
|
|
|
+ :src="userForm.photo"
|
|
|
+ class="avatar"
|
|
|
+ />
|
|
|
+ <i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <span class="dialog-footer">
|
|
|
+ <el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ export default {
|
|
|
+ name: "AdminPage",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ // 用户列表数据
|
|
|
+ userList: [],
|
|
|
+ loading: false,
|
|
|
+ page: 1,
|
|
|
+ size: 10,
|
|
|
+ total: 0,
|
|
|
+ searchKeyword: "",
|
|
|
+
|
|
|
+ // 表单相关
|
|
|
+ dialogVisible: false,
|
|
|
+ dialogTitle: "添加用户",
|
|
|
+ isAdd: true,
|
|
|
+ userForm: {
|
|
|
+ id: null,
|
|
|
+ username: "",
|
|
|
+ name: "",
|
|
|
+ phone: "",
|
|
|
+ email: "",
|
|
|
+ password: "",
|
|
|
+ role: "STUDENT",
|
|
|
+ photo: "",
|
|
|
+ },
|
|
|
+ roleOptions: [
|
|
|
+ { value: "ADMIN", label: "管理员" },
|
|
|
+ { value: "TEACHER", label: "教师" },
|
|
|
+ { value: "STUDENT", label: "学生" },
|
|
|
+ ],
|
|
|
+ rules: {
|
|
|
+ username: [
|
|
|
+ { required: true, message: "请输入用户名", trigger: "blur" },
|
|
|
+ { max: 64, message: "长度不能超过64个字符", trigger: "blur" },
|
|
|
+ ],
|
|
|
+ phone: [
|
|
|
+ { required: true, message: "请输入手机号", trigger: "blur" },
|
|
|
+ { max: 64, message: "长度不能超过64个字符", trigger: "blur" },
|
|
|
+ ],
|
|
|
+ password: [
|
|
|
+ { required: true, message: "请输入密码", trigger: "blur" },
|
|
|
+ { max: 512, message: "长度不能超过512个字符", trigger: "blur" },
|
|
|
+ ],
|
|
|
+ name: [{ max: 64, message: "长度不能超过64个字符", trigger: "blur" }],
|
|
|
+ email: [{ max: 512, message: "长度不能超过512个字符", trigger: "blur" }],
|
|
|
+ role: [{ required: true, message: "请选择角色", trigger: "change" }],
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 获取用户列表
|
|
|
+ async fetchUserList() {
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ // 这里替换为实际的API调用
|
|
|
+ // const res = await this.$apis.getUserList({
|
|
|
+ // page: this.page,
|
|
|
+ // size: this.size,
|
|
|
+ // keyword: this.searchKeyword,
|
|
|
+ // });
|
|
|
+ // this.userList = res.data.list;
|
|
|
+ // this.total = res.data.total;
|
|
|
+
|
|
|
+ // 模拟数据
|
|
|
+ this.userList = [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ username: "admin",
|
|
|
+ name: "管理员",
|
|
|
+ phone: "13800138000",
|
|
|
+ email: "admin@example.com",
|
|
|
+ photo: "",
|
|
|
+ role: "ADMIN",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ username: "teacher1",
|
|
|
+ name: "张老师",
|
|
|
+ phone: "13800138001",
|
|
|
+ email: "teacher1@example.com",
|
|
|
+ photo: "",
|
|
|
+ role: "TEACHER",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ username: "student1",
|
|
|
+ name: "李同学",
|
|
|
+ phone: "13800138002",
|
|
|
+ email: "student1@example.com",
|
|
|
+ photo: "",
|
|
|
+ role: "STUDENT",
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ this.total = 3;
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error("获取用户列表失败");
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 分页变化
|
|
|
+ handlePageChange(currentPage) {
|
|
|
+ this.page = currentPage;
|
|
|
+ this.fetchUserList();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 每页条数变化
|
|
|
+ handleSizeChange(pageSize) {
|
|
|
+ this.size = pageSize;
|
|
|
+ this.fetchUserList();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 搜索
|
|
|
+ handleSearch() {
|
|
|
+ this.page = 1;
|
|
|
+ this.fetchUserList();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 添加用户
|
|
|
+ handleAddUser() {
|
|
|
+ this.dialogTitle = "添加用户";
|
|
|
+ this.isAdd = true;
|
|
|
+ this.dialogVisible = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 编辑用户
|
|
|
+ handleEdit(row) {
|
|
|
+ this.dialogTitle = "编辑用户";
|
|
|
+ this.isAdd = false;
|
|
|
+ this.userForm = Object.assign({}, row);
|
|
|
+ this.dialogVisible = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 删除用户
|
|
|
+ handleDelete(id) {
|
|
|
+ this.$confirm("确认删除该用户吗?", "提示", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning",
|
|
|
+ })
|
|
|
+ .then(async () => {
|
|
|
+ // 这里替换为实际的API调用
|
|
|
+ // await this.$apis.deleteUser(id);
|
|
|
+ this.$message.success("删除成功");
|
|
|
+ this.fetchUserList();
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ submitForm() {
|
|
|
+ this.$refs.userFormRef.validate(async (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ try {
|
|
|
+ if (this.isAdd) {
|
|
|
+ // 添加用户
|
|
|
+ // await this.$apis.addUser(this.userForm);
|
|
|
+ } else {
|
|
|
+ // 更新用户
|
|
|
+ // await this.$apis.updateUser(this.userForm);
|
|
|
+ }
|
|
|
+ this.$message.success(`${this.dialogTitle}成功`);
|
|
|
+ this.dialogVisible = false;
|
|
|
+ this.fetchUserList();
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(`${this.dialogTitle}失败`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 重置表单
|
|
|
+ resetForm() {
|
|
|
+ this.$refs.userFormRef?.resetFields();
|
|
|
+ this.userForm = {
|
|
|
+ id: null,
|
|
|
+ username: "",
|
|
|
+ name: "",
|
|
|
+ phone: "",
|
|
|
+ email: "",
|
|
|
+ password: "",
|
|
|
+ role: "STUDENT",
|
|
|
+ photo: "",
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ // 头像上传成功
|
|
|
+ handleAvatarSuccess(res, file) {
|
|
|
+ this.userForm.photo = URL.createObjectURL(file.raw);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 头像上传前校验
|
|
|
+ beforeAvatarUpload(file) {
|
|
|
+ const isJPG = file.type === "image/jpeg";
|
|
|
+ const isPNG = file.type === "image/png";
|
|
|
+ const isLt2M = file.size / 1024 / 1024 < 2;
|
|
|
+
|
|
|
+ if (!isJPG && !isPNG) {
|
|
|
+ this.$message.error("上传头像图片只能是 JPG/PNG 格式!");
|
|
|
+ }
|
|
|
+ if (!isLt2M) {
|
|
|
+ this.$message.error("上传头像图片大小不能超过 2MB!");
|
|
|
+ }
|
|
|
+ return (isJPG || isPNG) && isLt2M;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取角色标签类型
|
|
|
+ getRoleTagType(role) {
|
|
|
+ switch (role) {
|
|
|
+ case "ADMIN":
|
|
|
+ return "danger";
|
|
|
+ case "TEACHER":
|
|
|
+ return "warning";
|
|
|
+ case "STUDENT":
|
|
|
+ return "success";
|
|
|
+ default:
|
|
|
+ return "info";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.fetchUserList();
|
|
|
+ },
|
|
|
+ };
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <style scoped>
|
|
|
+ .admin-page {
|
|
|
+ padding: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .admin-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .admin-search {
|
|
|
+ width: 300px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .admin-main {
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .pagination {
|
|
|
+ margin-top: 20px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-uploader .el-upload {
|
|
|
+ border: 1px dashed #d9d9d9;
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-uploader .el-upload:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-uploader-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #8c939d;
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
+ line-height: 120px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar {
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+ </style>
|