2025-07-12 15:48:30 +08:00

341 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="main__container table">
<header class="table-header">
<div class="table-header__search">
<n-input placeholder="请输入用户名称" v-model:value="queryInfo.userName" size="small" style="width: 240px;"
clearable />
<n-select placeholder="请选择角色" v-model:value="queryInfo.roleName" :options="options" size="small"
style="width: 240px;" clearable />
</div>
<ls-list-button @search="init" @reset="resetList"></ls-list-button>
</header>
<main class="table-main" ref="tableMainRef">
<n-data-table v-if="height" :columns="columns" :data="data" :max-height="`${height}px`"
:min-height="`${height}px`">
<template #empty>
<LsEmpty type="no-data" title="暂无数据" description="Not Found 404"></LsEmpty>
</template>
</n-data-table>
<div class="table-main__pagination">
<n-pagination v-model:page="pagination.page" v-model:page-size="pagination.pageSize" :item-count="data.length"
show-size-picker :page-sizes="[10, 20, 30, 40]">
<template #prefix="{ itemCount }">
共 {{ itemCount }} 项
</template>
</n-pagination>
</div>
</main>
</div>
<!-- 弹窗 -->
<n-modal v-model:show="addRoleDialog">
<n-card style="width: 450px" title="授权角色" :bordered="false" size="huge" role="dialog" aria-modal="true">
<n-input-group>
<n-input v-model:value="roleName" readonly :style="{ width: '70%' }" placeholder="请从角色库选择角色" />
<n-button type="warning" strong secondary :style="{ width: '30%' }" @click="openRoleDialog">
角色库
<template #icon>
<n-icon>
<Server />
</n-icon>
</template>
</n-button>
</n-input-group>
<template #footer>
<div class="flex-content right">
<n-button type="primary" size="small" @click="handleEnableRole"> </n-button>
<n-button @click="addRoleDialog = false" size="small"> </n-button>
</div>
</template>
</n-card>
</n-modal>
<!-- 角色库 -->
<LsRoleBase ref="roleRef" @choose="chooseRole"></LsRoleBase>
</template>
<script setup lang='ts'>
import { Server } from '@vicons/ionicons5';
import { getUserList, deleteUser, enableRole } from '@/api/roleApi';
import { DataTableColumns, NButton, NTag, useDialog, useMessage } from 'naive-ui';
import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
import LsRoleBase from '@/components/Ls-UI/LsRoleBase.vue';
const message = useMessage();
const tableMainRef = ref<HTMLElement | null>(null);
const roleRef = ref<InstanceType<typeof LsRoleBase> | null>(null);
const queryInfo = reactive({
roleName: void 0,
userName: ''
});
const total = ref(0);
const pagination = reactive<PageController>({
pageSize: 10,
page: 1,
});
const options = ref([]);
const data = ref([]);
const height = ref(null);
const dialog = useDialog();
const addRoleDialog = ref(false);
const operationUuid = ref('');
const roleName = ref(''); //授权角色
// 类似render - 函数式组件渲染
function createColumns({
handleDelete,
handleAuthorize,
handleMenuAssign,
handleAssignJob
}: {
handleDelete: (rowData: UserRaw) => void,
handleAuthorize: (rowData: UserRaw) => void,
handleMenuAssign: (rowData: UserRaw) => void,
handleAssignJob: (rowData: UserRaw) => void,
}): DataTableColumns<UserRaw> {
return [
{
title: '用户名',
key: 'userName',
align: 'center'
},
{
title: '性别',
key: 'sex',
align: 'center',
render(row: UserRaw) {
return h(NTag, {
type: row.sex === '男' ? 'primary' : row.sex === '女' ? 'error' : 'default',
bordered: false,
class: 'user-table__gender-tag'
}, {
default: () => `${row.sex || '未知'}`
})
}
},
{
title: '职位',
key: 'jobCode',
align: 'center',
render(row: UserRaw) {
return h('span', { class: 'user-table__job' }, {
default: () => `${row.jobCode || '无职位'}`
})
}
},
{
title: '菜单权限',
key: 'menuName',
align: 'center',
render(row: UserRaw) {
return h('span', { class: 'user-table__menu' }, {
default: () => `${row.menuName || '无权限'}`
})
}
},
{
title: '电话',
key: 'phoneNumber',
align: 'center',
render(row: UserRaw) {
return h('span', { class: 'user-table__phone' }, {
default: () => `${row.phoneNumber || '无号码'}`
})
}
},
{
title: '操作',
key: 'actions',
align: 'center',
render(row: UserRaw) {
return h('div', { class: 'user-table__actions' }, [
h(NButton, {
size: 'small',
type: 'primary',
strong: true,
secondary: true,
class: 'user-table__action-btn',
onClick: () => handleAuthorize(row)
}, { default: () => '授权用户' }),
h(NButton, {
size: 'small',
type: 'error',
strong: true,
secondary: true,
class: 'user-table__action-btn',
onClick: () => handleDelete(row)
}, { default: () => '删除用户' }),
h(NButton, {
size: 'small',
type: 'info',
strong: true,
secondary: true,
class: 'user-table__action-btn',
onClick: () => handleMenuAssign(row)
}, { default: () => '菜单分配' }),
h(NButton, {
size: 'small',
type: 'warning',
strong: true,
secondary: true,
class: 'user-table__action-btn',
onClick: () => handleAssignJob(row)
}, { default: () => '赋予职位' })
])
}
}
]
}
// 生成表格列
const columns = createColumns({
handleAuthorize(rowData) {
addRoleDialog.value = true;
operationUuid.value = rowData.id;
},
handleDelete(rowData) {
dialog.warning({
title: '警告',
content: `你确定删除用户${rowData.userName}`,
positiveText: '确定',
negativeText: '不确定',
draggable: true,
onPositiveClick: () => {
deleteUser(rowData.id).then(() => {
message.success('删除成功!');
init();
}).catch((error) => {
message.error(error.message);
})
},
onNegativeClick: () => {
message.info('取消')
}
})
},
handleMenuAssign(rowData) {
message.error(`我点击了${rowData.userName}`);
},
handleAssignJob(rowData) {
message.error(`我点击了${rowData.userName}`);
}
})
const init = async () => {
try {
const { users, totalCount } = await getUserList({
...pagination,
...queryInfo
});
total.value = totalCount;
data.value = users;
} catch (error) {
message.error(error.message);
}
};
const resetList = () => {
pagination.page = 1;
pagination.pageSize = 10;
queryInfo.roleName = void 0;
queryInfo.userName = '';
init();
}
onMounted(() => {
if (tableMainRef.value) {
height.value = tableMainRef.value.clientHeight - 64 - 50; //减去两次内边距 - 分页的大小
};
initRoleList();
init();
});
const openRoleDialog = () => {
roleRef.value?.openDialog();
}
const handleEnableRole = async () => {
try {
const query = {
userId: operationUuid.value,
roleName: roleName.value
}
await enableRole(query);
init();
message.success('授权成功!');
} catch (error) {
message.error(error.message);
}
}
// 获取角色
const initRoleList = async () => {
try {
const result = await getRoleList();
options.value = result.map(item => {
return {
label:item.normalizedName,
value:item.name
}
});
} catch (error) {
message.error(error instanceof Error ? error.message : error);
}
};
// 赋予角色
const chooseRole = (row) => {
console.log(row, 'row');
roleName.value = row.name;
}
</script>
<style scoped lang='scss'>
.table-header {
background: #fff;
border-radius: 4px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 $normolGap;
&__search {
display: flex;
align-items: center;
gap: $miniGap;
}
}
.table-main {
background: #fff;
padding: $normolGap;
border-radius: 4px;
height: calc(100% - 2 * $normolGap);
&__pagination {
height: 50px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}
.user-table {
// 文本样式
:deep() &__job,
:deep() &__menu,
:deep() &__phone {
font-size: 14px;
color: var(--n-text-color);
}
// 操作按钮容器
:deep() &__actions {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 6px;
width: 100%;
max-width: 200px;
margin: 0 auto;
}
// 操作按钮
:deep() &__action-btn.n-button {
width: 100%;
white-space: nowrap;
}
}
</style>