|
- {% extends "../layout.html" %}
-
- {% block title %}角色权限{% endblock %}
-
- {% block content %}
- <div id="roleApp">
- <!-- 搜索栏 -->
- <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 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="name" label="角色名称">
- <template #default="{ row }">
- <span class="text-orange-500 font-medium">${ row.name }</span>
- </template>
- </el-table-column>
- <el-table-column prop="is_default" label="默认" width="80" align="center">
- <template #default="{ row }">
- <el-tag v-if="row.is_default" type="success" size="small">是</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="code" label="角色编码">
- <template #default="{ row }">${ row.code || '-' }</template>
- </el-table-column>
- <el-table-column prop="description" label="描述">
- <template #default="{ row }">${ row.description || '-' }</template>
- </el-table-column>
- <el-table-column label="操作" width="200" align="center">
- <template #default="{ row }">
- <el-button type="primary" link @click="openPermDrawer(row)">分配权限</el-button>
- <el-button v-if="!row.is_default" type="primary" link @click="editRole(row)">编辑</el-button>
- <el-button v-if="!row.is_default" type="danger" link @click="deleteRole(row)">删除</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.name" placeholder="请输入角色名称" />
- </el-form-item>
- <el-form-item label="角色编码">
- <el-input v-model="form.code" placeholder="请输入角色编码(如 ADMIN)" />
- </el-form-item>
- <el-form-item label="描述">
- <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入角色描述" />
- </el-form-item>
- <el-form-item label="排序">
- <el-input-number v-model="form.sort" :min="0" />
- </el-form-item>
- <el-form-item label="默认角色">
- <el-switch v-model="form.is_default" :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="saveRole" :loading="saving">确定</el-button>
- </template>
- </el-dialog>
-
- <!-- 权限分配抽屉 -->
- <el-drawer v-model="permDrawerVisible" :title="permTitle" size="420px" destroy-on-close>
- <template #header>
- <span class="font-semibold">${ permTitle }</span>
- </template>
- <div class="mb-3">
- <el-checkbox v-model="expandAll" @change="handleExpandAll">全部展开</el-checkbox>
- </div>
- <el-tree
- ref="permTreeRef"
- :data="permissionTree"
- show-checkbox
- node-key="key"
- :default-expand-all="expandAll"
- :props="{ label: 'name', children: 'children' }"
- />
- <template #footer>
- <el-button @click="permDrawerVisible = false">取消</el-button>
- <el-button type="primary" @click="savePermissions" :loading="permSaving">确定</el-button>
- </template>
- </el-drawer>
- </div>
- {% endblock %}
-
- {% block js %}
- <script>
- const permissionTreeData = {{ permissionTree | dump | safe }};
-
- const { createApp, ref, reactive, onMounted, nextTick } = Vue;
-
- const app = createApp({
- delimiters: ['${', '}'],
- setup() {
- const keyword = ref('');
- const loading = ref(false);
- const tableData = ref([]);
- const pagination = reactive({ page: 1, pageSize: 20, total: 0 });
-
- // 弹窗
- const dialogVisible = ref(false);
- const dialogTitle = ref('新增角色');
- const saving = ref(false);
- const form = reactive({ id: null, name: '', code: '', description: '', sort: 0, is_default: 0 });
-
- // 权限抽屉
- const permDrawerVisible = ref(false);
- const permTitle = ref('权限分配');
- const permTreeRef = ref(null);
- const expandAll = ref(false);
- const permSaving = ref(false);
- const currentPermRoleId = ref(null);
- const permissionTree = ref(permissionTreeData || []);
-
- // 加载列表
- 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 });
- const res = await fetch('/admin/system/role/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 = '';
- pagination.page = 1;
- loadList();
- }
-
- // 显示新增弹窗
- function showAddModal() {
- dialogTitle.value = '新增角色';
- Object.assign(form, { id: null, name: '', code: '', description: '', sort: 0, is_default: 0 });
- dialogVisible.value = true;
- }
-
- // 编辑角色
- function editRole(row) {
- dialogTitle.value = '编辑角色';
- Object.assign(form, { id: row.id, name: row.name, code: row.code || '', description: row.description || '', sort: row.sort || 0, is_default: row.is_default || 0 });
- dialogVisible.value = true;
- }
-
- // 保存角色
- async function saveRole() {
- if (!form.name.trim()) {
- ElementPlus.ElMessage.warning('角色名称不能为空');
- return;
- }
- saving.value = true;
- try {
- const url = form.id ? '/admin/system/role/edit' : '/admin/system/role/add';
- const body = { name: form.name, code: form.code, description: form.description, sort: form.sort, is_default: form.is_default };
- 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;
- }
- }
-
- // 删除角色
- async function deleteRole(row) {
- try {
- await ElementPlus.ElMessageBox.confirm('确定要删除该角色吗?', '提示', { type: 'warning' });
- const res = await fetch('/admin/system/role/delete', {
- 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 {}
- }
-
- // 打开权限抽屉
- async function openPermDrawer(row) {
- currentPermRoleId.value = row.id;
- permTitle.value = `【${row.name}】权限分配`;
- permDrawerVisible.value = true;
-
- await nextTick();
- const res = await fetch('/admin/system/role/detail?id=' + row.id).then(r => r.json());
- const currentPerms = res.code === 0 ? (res.data.permissions || []) : [];
- if (permTreeRef.value) {
- permTreeRef.value.setCheckedKeys(currentPerms);
- }
- }
-
- // 全部展开/折叠
- function handleExpandAll(val) {
- if (permTreeRef.value) {
- const nodes = permTreeRef.value.store.nodesMap;
- for (const key in nodes) {
- nodes[key].expanded = val;
- }
- }
- }
-
- // 保存权限
- async function savePermissions() {
- permSaving.value = true;
- try {
- const permissions = permTreeRef.value ? permTreeRef.value.getCheckedKeys(true) : [];
- const res = await fetch('/admin/system/role/assignPermissions', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ id: currentPermRoleId.value, permissions })
- }).then(r => r.json());
-
- if (res.code === 0) {
- ElementPlus.ElMessage.success('权限保存成功');
- permDrawerVisible.value = false;
- } else {
- ElementPlus.ElMessage.error(res.msg || '保存失败');
- }
- } finally {
- permSaving.value = false;
- }
- }
-
- onMounted(() => loadList());
-
- return {
- keyword, loading, tableData, pagination,
- dialogVisible, dialogTitle, saving, form,
- permDrawerVisible, permTitle, permTreeRef, expandAll, permSaving, permissionTree,
- loadList, resetFilter, showAddModal, editRole, saveRole, deleteRole,
- openPermDrawer, handleExpandAll, savePermissions
- };
- }
- });
-
- app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
- app.mount('#roleApp');
- </script>
- {% endblock %}
|