|
- {% extends "../layout.html" %}
-
- {% block title %}用户管理{% endblock %}
-
- {% block content %}
- <div id="userApp">
- <!-- 搜索栏 -->
- <el-card shadow="never" class="mb-4">
- <el-form :inline="true" @submit.prevent="loadList()" class="flex items-center flex-wrap gap-2">
- <el-form-item label="关键词" class="!mb-0">
- <el-input v-model="keyword" placeholder="用户名/姓名..." clearable style="width:180px;" />
- </el-form-item>
- <el-form-item label="角色" class="!mb-0">
- <el-select v-model="roleFilter" placeholder="全部" clearable style="width:120px;">
- <el-option v-for="role in roles" :key="role.id" :label="role.name" :value="role.id" />
- </el-select>
- </el-form-item>
- <el-form-item label="状态" class="!mb-0">
- <el-select v-model="statusFilter" placeholder="全部" clearable style="width:100px;">
- <el-option label="启用" :value="1" />
- <el-option label="禁用" :value="0" />
- </el-select>
- </el-form-item>
- <el-form-item class="!mb-0">
- <el-button type="primary" @click="loadList()">搜索</el-button>
- <el-button @click="resetFilter">重置</el-button>
- </el-form-item>
- </el-form>
- </el-card>
-
- <!-- 用户列表 -->
- <el-card shadow="never">
- <template #header>
- <el-button type="primary" @click="showAddModal">+ 新增用户</el-button>
- </template>
-
- <el-table :data="tableData" v-loading="loading" stripe border>
- <el-table-column prop="id" label="ID" width="60" ></el-table-column>
- <el-table-column prop="username" label="用户名" ></el-table-column>
- <el-table-column prop="nickname" label="姓名">
- <template #default="{ row }">${ row.nickname || '-' }</template>
- </el-table-column>
- <el-table-column prop="role_name" label="角色" width="120">
- <template #default="{ row }">
- <el-tag type="primary" size="small">${ row.role_name || '-' }</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="email" label="邮箱" width="180">
- <template #default="{ row }">${ row.email || '-' }</template>
- </el-table-column>
- <el-table-column prop="status" label="状态" width="80" align="center">
- <template #default="{ row }">
- <el-tag v-if="row.status === 1" type="success" size="small">启用</el-tag>
- <el-tag v-else type="info" size="small">禁用</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="last_login_time" label="最后登录" width="160">
- <template #default="{ row }">${ row.last_login_time || '-' }</template>
- </el-table-column>
- <el-table-column label="操作" width="200" align="center">
- <template #default="{ row }">
- <el-button type="primary" link @click="editUser(row)">编辑</el-button>
- <el-button type="primary" link @click="showResetModal(row)">重置密码</el-button>
- <el-button :type="row.status === 1 ? 'danger' : 'success'" link @click="toggleStatus(row)">
- ${ row.status === 1 ? '禁用' : '启用' }
- </el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <div class="flex justify-end mt-4">
- <el-pagination
- v-model:current-page="pagination.page"
- :page-size="pagination.pageSize"
- :total="pagination.total"
- layout="total, prev, pager, next"
- @current-change="loadList"
- />
- </div>
- </el-card>
-
- <!-- 新增/编辑弹窗 -->
- <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" destroy-on-close>
- <el-form :model="form" label-width="80px">
- <el-form-item label="用户名" required>
- <el-input v-model="form.username" placeholder="请输入用户名" :disabled="!!form.id" />
- </el-form-item>
- <el-form-item v-if="!form.id" label="密码" required>
- <el-input v-model="form.password" type="password" placeholder="请输入密码" show-password />
- </el-form-item>
- <el-form-item label="姓名">
- <el-input v-model="form.nickname" placeholder="请输入姓名" />
- </el-form-item>
- <el-form-item label="邮箱">
- <el-input v-model="form.email" type="email" placeholder="请输入邮箱" />
- </el-form-item>
- <el-form-item label="手机号">
- <el-input v-model="form.phone" placeholder="请输入手机号" />
- </el-form-item>
- <el-form-item label="角色">
- <el-select v-model="form.role_id" placeholder="请选择角色" style="width:100%;">
- <el-option v-for="role in roles" :key="role.id" :label="role.name" :value="role.id" />
- </el-select>
- </el-form-item>
- <el-form-item label="状态">
- <el-switch v-model="form.status" :active-value="1" :inactive-value="0" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="saveUser" :loading="saving">确定</el-button>
- </template>
- </el-dialog>
-
- <!-- 重置密码弹窗 -->
- <el-dialog v-model="resetPwdVisible" title="重置密码" width="400px" destroy-on-close>
- <el-form label-width="80px">
- <el-form-item label="新密码" required>
- <el-input v-model="newPassword" type="password" placeholder="请输入新密码" show-password />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="resetPwdVisible = false">取消</el-button>
- <el-button type="primary" @click="resetPassword" :loading="resetPwdSaving">确定</el-button>
- </template>
- </el-dialog>
- </div>
- {% endblock %}
-
- {% block js %}
- <script>
- const rolesData = {{ roles | dump | safe }};
-
- const { createApp, ref, reactive, onMounted } = Vue;
-
- const app = createApp({
- delimiters: ['${', '}'],
- setup() {
- const keyword = ref('');
- const roleFilter = ref('');
- const statusFilter = ref('');
- const loading = ref(false);
- const tableData = ref([]);
- const pagination = reactive({ page: 1, pageSize: 10, total: 0 });
- const roles = ref(rolesData || []);
-
- // 弹窗
- const dialogVisible = ref(false);
- const dialogTitle = ref('新增用户');
- const saving = ref(false);
- const form = reactive({ id: null, username: '', password: '', nickname: '', email: '', phone: '', role_id: '', status: 1 });
-
- // 重置密码弹窗
- const resetPwdVisible = ref(false);
- const resetPwdSaving = ref(false);
- const newPassword = ref('');
- const resetUserId = ref(null);
-
- // 加载列表
- async function loadList(page) {
- if (typeof page === 'number') pagination.page = page;
- loading.value = true;
- try {
- const params = new URLSearchParams({
- page: pagination.page,
- pageSize: pagination.pageSize,
- keyword: keyword.value,
- role_id: roleFilter.value,
- status: statusFilter.value
- });
- const res = await fetch('/admin/system/user/list?' + params).then(r => r.json());
- if (res.code === 0) {
- tableData.value = res.data.data || [];
- pagination.total = res.data.count || 0;
- } else {
- ElementPlus.ElMessage.error(res.msg || '加载失败');
- }
- } finally {
- loading.value = false;
- }
- }
-
- // 重置筛选
- function resetFilter() {
- keyword.value = '';
- roleFilter.value = '';
- statusFilter.value = '';
- pagination.page = 1;
- loadList();
- }
-
- // 显示新增弹窗
- function showAddModal() {
- dialogTitle.value = '新增用户';
- Object.assign(form, { id: null, username: '', password: '', nickname: '', email: '', phone: '', role_id: '', status: 1 });
- dialogVisible.value = true;
- }
-
- // 编辑用户
- async function editUser(row) {
- const res = await fetch('/admin/system/user/detail?id=' + row.id).then(r => r.json());
- if (res.code !== 0) {
- ElementPlus.ElMessage.error(res.msg || '获取失败');
- return;
- }
- const user = res.data;
- dialogTitle.value = '编辑用户';
- Object.assign(form, {
- id: user.id,
- username: user.username,
- password: '',
- nickname: user.nickname || '',
- email: user.email || '',
- phone: user.phone || '',
- role_id: user.role_id || '',
- status: user.status
- });
- dialogVisible.value = true;
- }
-
- // 保存用户
- async function saveUser() {
- if (!form.username.trim()) {
- ElementPlus.ElMessage.warning('用户名不能为空');
- return;
- }
- if (!form.id && !form.password) {
- ElementPlus.ElMessage.warning('密码不能为空');
- return;
- }
- saving.value = true;
- try {
- const url = form.id ? '/admin/system/user/edit' : '/admin/system/user/add';
- const body = {
- username: form.username,
- password: form.password,
- nickname: form.nickname,
- email: form.email,
- phone: form.phone,
- role_id: form.role_id,
- status: form.status
- };
- if (form.id) body.id = form.id;
-
- const res = await fetch(url, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(body)
- }).then(r => r.json());
-
- if (res.code === 0) {
- ElementPlus.ElMessage.success('保存成功');
- dialogVisible.value = false;
- loadList();
- } else {
- ElementPlus.ElMessage.error(res.msg || '保存失败');
- }
- } finally {
- saving.value = false;
- }
- }
-
- // 显示重置密码弹窗
- function showResetModal(row) {
- resetUserId.value = row.id;
- newPassword.value = '';
- resetPwdVisible.value = true;
- }
-
- // 重置密码
- async function resetPassword() {
- if (!newPassword.value) {
- ElementPlus.ElMessage.warning('请输入新密码');
- return;
- }
- resetPwdSaving.value = true;
- try {
- const res = await fetch('/admin/system/user/resetPassword', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ id: resetUserId.value, password: newPassword.value })
- }).then(r => r.json());
-
- if (res.code === 0) {
- ElementPlus.ElMessage.success('密码重置成功');
- resetPwdVisible.value = false;
- } else {
- ElementPlus.ElMessage.error(res.msg || '重置失败');
- }
- } finally {
- resetPwdSaving.value = false;
- }
- }
-
- // 切换状态
- async function toggleStatus(row) {
- try {
- await ElementPlus.ElMessageBox.confirm('确定要切换该用户状态吗?', '提示', { type: 'warning' });
- const res = await fetch('/admin/system/user/toggleStatus', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ id: row.id })
- }).then(r => r.json());
-
- if (res.code === 0) {
- ElementPlus.ElMessage.success('操作成功');
- loadList();
- } else {
- ElementPlus.ElMessage.error(res.msg || '操作失败');
- }
- } catch {}
- }
-
- onMounted(() => loadList());
-
- return {
- keyword, roleFilter, statusFilter, loading, tableData, pagination, roles,
- dialogVisible, dialogTitle, saving, form,
- resetPwdVisible, resetPwdSaving, newPassword,
- loadList, resetFilter, showAddModal, editUser, saveUser,
- showResetModal, resetPassword, toggleStatus
- };
- }
- });
-
- app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
- app.mount('#userApp');
- </script>
- {% endblock %}
|