admin.xpcool.com/apps/web-tdesign/src/views/system/admin/index.vue
夏犀麟 a44244c7ad
Some checks failed
CI / Test (ubuntu-latest) (push) Has been skipped
CI / Lint (ubuntu-latest) (push) Has been skipped
CI / Check (ubuntu-latest) (push) Has been skipped
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been skipped
Deploy Website on push / Deploy Push Playground Ftp (push) Has been skipped
Deploy Website on push / Deploy Push Docs Ftp (push) Has been skipped
Deploy Website on push / Deploy Push Antd Ftp (push) Has been skipped
Deploy Website on push / Deploy Push Element Ftp (push) Has been skipped
Deploy Website on push / Deploy Push Naive Ftp (push) Has been skipped
Release Drafter / update_release_draft (push) Has been skipped
Deploy Website on push / Rerun on failure (push) Has been skipped
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
chore: init admin.xpcool.com (vben5 + tdesign)
2026-08-24 23:56:57 +08:00

236 lines
6.1 KiB
Vue

<script lang="ts" setup>
import type { AdminItem, 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 {
createAdmin,
deleteAdmin,
getAdminList,
getRoleList,
resetAdminPassword,
updateAdmin,
} from '#/api/system';
const { hasAccessByCodes } = useAccess();
const loading = ref(false);
const list = ref<AdminItem[]>([]);
const roleOptions = ref<{ label: string; value: number }[]>([]);
const dialogOpen = ref(false);
const editing = ref<null | AdminItem>(null);
const query = reactive({ page: 1, size: 10, keyword: '' });
const pagination = reactive({ current: 1, pageSize: 10, total: 0 });
const form = reactive({
username: '',
password: '',
nickname: '',
status: 1,
roleIds: [] as number[],
});
async function load() {
loading.value = true;
try {
const data = await getAdminList(query);
list.value = data.list ?? [];
pagination.total = data.total ?? 0;
} finally {
loading.value = false;
}
}
async function loadRoles() {
const data = await getRoleList({ page: 1, size: 100 });
roleOptions.value = (data.list ?? []).map((r: RoleItem) => ({
label: r.name,
value: r.id,
}));
}
function openCreate() {
editing.value = null;
Object.assign(form, { username: '', password: '', nickname: '', status: 1, roleIds: [] });
dialogOpen.value = true;
}
function openEdit(row: AdminItem) {
editing.value = row;
Object.assign(form, {
username: row.username,
password: '',
nickname: row.nickname,
status: row.status,
roleIds: row.roleIds ?? [],
});
dialogOpen.value = true;
}
async function submit() {
if (editing.value) {
await updateAdmin(editing.value.id, {
nickname: form.nickname,
status: form.status,
roleIds: form.roleIds,
});
} else {
await createAdmin({
username: form.username,
password: form.password,
nickname: form.nickname,
roleIds: form.roleIds,
});
}
MessagePlugin.success('保存成功');
dialogOpen.value = false;
load();
}
function onDelete(row: AdminItem) {
const d = DialogPlugin.confirm({
header: '确认删除该管理员?',
onConfirm: async () => {
await deleteAdmin(row.id);
MessagePlugin.success('删除成功');
load();
d.destroy();
},
});
}
function onResetPwd(row: AdminItem) {
let password = '';
const d = DialogPlugin.confirm({
header: `重置「${row.username}」的密码`,
body: '请输入新密码(至少 6 位)',
onConfirm: async () => {
if (!password || password.length < 6) {
MessagePlugin.warning('密码至少 6 位');
return;
}
await resetAdminPassword(row.id, password);
MessagePlugin.success('密码已重置');
d.destroy();
},
});
}
const columns = [
{ colKey: 'id', title: 'ID', width: 70 },
{ colKey: 'username', title: '用户名' },
{ colKey: 'nickname', title: '昵称' },
{ colKey: 'statusText', title: '状态', width: 90 },
{ colKey: 'roleNamesText', title: '角色' },
{ colKey: 'createdAt', title: '创建时间', width: 170 },
{ colKey: 'op', title: '操作', width: 200 },
];
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();
loadRoles();
});
</script>
<template>
<Page auto-content-height>
<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:admin:create'])"
theme="primary"
@click="openCreate"
>
新增管理员
</t-button>
</div>
<t-table
row-key="id"
:data="list"
:columns="columns"
:loading="loading"
:pagination="pagination"
@page-change="onPageChange"
>
<template #statusText="{ row }">{{ row.status === 1 ? '启用' : '禁用' }}</template>
<template #roleNamesText="{ row }">{{ (row.roleNames ?? []).join(', ') }}</template>
<template #op="{ row }">
<t-button
v-if="hasAccessByCodes(['system:admin:update'])"
size="small"
theme="primary"
variant="text"
@click="openEdit(row)"
>
编辑
</t-button>
<t-button
v-if="hasAccessByCodes(['system:admin:resetPwd'])"
size="small"
theme="primary"
variant="text"
@click="onResetPwd(row)"
>
重置密码
</t-button>
<t-button
v-if="hasAccessByCodes(['system:admin:delete'])"
size="small"
theme="danger"
variant="text"
@click="onDelete(row)"
>
删除
</t-button>
</template>
</t-table>
<t-dialog
v-model:visible="dialogOpen"
:header="editing ? '编辑管理员' : '新增管理员'"
:on-confirm="submit"
>
<t-form label-align="right" label-width="80px">
<t-form-item label="用户名" required>
<t-input v-model="form.username" :disabled="!!editing" placeholder="登录用户名" />
</t-form-item>
<t-form-item v-if="!editing" label="密码" required>
<t-input v-model="form.password" type="password" placeholder="至少 6 位" />
</t-form-item>
<t-form-item label="昵称">
<t-input v-model="form.nickname" 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-select v-model="form.roleIds" multiple :options="roleOptions" placeholder="选择角色" />
</t-form-item>
</t-form>
</t-dialog>
</Page>
</template>