admin.xpcool.com/apps/web-tdesign/src/views/system/role/index.vue
夏犀麟 f02a514e44 feat(house): 增加房屋数据管理功能
- 新增全局主题美化样式文件 theme.css 并在 bootstrap.ts 中引入
- 实现工作台页面重构,包含渐变英雄头、统计卡片和快捷入口宫格
- 开发楼盘地图功能,在 dashboard 中集成腾讯地图 GL JS 散点显示
- 添加成交记录列表页面,支持关键词和区域筛选查询
- 新增新房预售许可证管理页面,包含预售证号、楼盘名等信息展示
- 扩展 house API 接口,增加交易记录和预售许可相关数据模型
- 优化数据明细页面,将占位内容替换为实际的成交记录表格
- 添加招聘数据看板和爬虫管理页面,支持数据源管理和抓取触发功能
2026-08-27 01:48:52 +08:00

232 lines
5.7 KiB
Vue

<script lang="ts" setup>
import type { MenuNode, RoleItem } from '#/api/system';
import { onMounted, reactive, ref } from 'vue';
import { useAccess } from '@vben/access';
import { Page } from '@vben/common-ui';
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
import {
createRole,
deleteRole,
getMenuTree,
getRoleList,
updateRole,
} from '#/api/system';
const { hasAccessByCodes } = useAccess();
const loading = ref(false);
const list = ref<RoleItem[]>([]);
const menuTree = ref<any[]>([]);
const dialogOpen = ref(false);
const editing = ref<null | RoleItem>(null);
const query = reactive({ page: 1, size: 10, keyword: '' });
const pagination = reactive({ current: 1, pageSize: 10, total: 0 });
const form = reactive({
code: '',
name: '',
status: 1,
menuIds: [] as number[],
});
function toTree(nodes: MenuNode[]): any[] {
return (nodes ?? []).map((n) => ({
label: n.name,
value: n.id,
children: n.children?.length ? toTree(n.children) : undefined,
}));
}
async function load() {
loading.value = true;
try {
const data = await getRoleList(query);
list.value = data.list ?? [];
pagination.total = data.total ?? 0;
} finally {
loading.value = false;
}
}
async function loadMenus() {
const data = await getMenuTree();
menuTree.value = toTree(data.tree ?? []);
}
function openCreate() {
editing.value = null;
Object.assign(form, { code: '', name: '', status: 1, menuIds: [] });
dialogOpen.value = true;
}
function openEdit(row: RoleItem) {
editing.value = row;
Object.assign(form, {
code: row.code,
name: row.name,
status: row.status,
menuIds: row.menuIds ?? [],
});
dialogOpen.value = true;
}
async function submit() {
if (editing.value) {
await updateRole(editing.value.id, {
name: form.name,
status: form.status,
menuIds: form.menuIds,
});
} else {
await createRole({
code: form.code,
name: form.name,
status: form.status,
menuIds: form.menuIds,
});
}
MessagePlugin.success('保存成功');
dialogOpen.value = false;
load();
}
function onDelete(row: RoleItem) {
const d = DialogPlugin.confirm({
header: `确认删除角色「${row.name}」?`,
onConfirm: async () => {
await deleteRole(row.id);
MessagePlugin.success('删除成功');
load();
d.destroy();
},
});
}
const columns = [
{ colKey: 'id', title: 'ID', width: 70 },
{ colKey: 'code', title: '角色编码' },
{ colKey: 'name', title: '角色名称' },
{ colKey: 'statusText', title: '状态', width: 90 },
{ colKey: 'createdAt', title: '创建时间', width: 170 },
{ colKey: 'op', title: '操作', width: 150 },
];
function onPageChange(p: { current: number; pageSize: number }) {
query.page = p.current;
query.size = p.pageSize;
pagination.current = p.current;
pagination.pageSize = p.pageSize;
load();
}
onMounted(() => {
load();
loadMenus();
});
</script>
<template>
<Page auto-content-height>
<div class="wb-page-head">
<h2 class="wb-page-title">角色管理</h2>
<p class="wb-page-sub">维护角色及其菜单权限</p>
</div>
<t-card :bordered="false" class="wb-filter-card">
<div class="mb-3 flex items-center justify-between">
<div class="flex items-center gap-2">
<t-input
v-model="query.keyword"
clearable
placeholder="角色编码/名称"
style="width: 220px"
@enter="load"
/>
<t-button theme="primary" @click="load">查询</t-button>
</div>
<t-button
v-if="hasAccessByCodes(['system:role:create'])"
theme="primary"
@click="openCreate"
>
新增角色
</t-button>
</div>
</t-card>
<t-card :bordered="false" class="wb-table-card">
<t-table
row-key="id"
:data="list"
:columns="columns"
:loading="loading"
:pagination="pagination"
@page-change="onPageChange"
>
<template #statusText="{ row }">
{{
row.status === 1 ? '启用' : '禁用'
}}
</template>
<template #op="{ row }">
<t-button
v-if="hasAccessByCodes(['system:role:update'])"
size="small"
theme="primary"
variant="text"
@click="openEdit(row)"
>
编辑
</t-button>
<t-button
v-if="hasAccessByCodes(['system:role:delete'])"
size="small"
theme="danger"
variant="text"
@click="onDelete(row)"
>
删除
</t-button>
</template>
</t-table>
</t-card>
<t-dialog
v-model:visible="dialogOpen"
:header="editing ? '编辑角色' : '新增角色'"
width="560px"
:on-confirm="submit"
>
<t-form label-align="right" label-width="80px">
<t-form-item label="角色编码" required>
<t-input
v-model="form.code"
:disabled="!!editing"
placeholder="如 super_admin"
/>
</t-form-item>
<t-form-item label="角色名称" required>
<t-input v-model="form.name" placeholder="显示名称" />
</t-form-item>
<t-form-item label="状态">
<t-switch v-model="form.status" :custom-value="[1, 0]" />
</t-form-item>
<t-form-item label="菜单权限">
<t-tree
v-model="form.menuIds"
:data="menuTree"
checkable
:expand-all="true"
value-mode="all"
:keys="{ value: 'value', label: 'label', children: 'children' }"
/>
</t-form-item>
</t-form>
</t-dialog>
</Page>
</template>