feat: 添加权限配置页面
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
export * from './device';
|
||||
export * from './global';
|
||||
export * from './permission';
|
||||
export * from './station';
|
||||
|
||||
1
src/components/permission/index.ts
Normal file
1
src/components/permission/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './permission-config-modal';
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { ComponentInstance } from 'vue';
|
||||
import PermissionConfigModal from './permission-config-modal.vue';
|
||||
|
||||
export type PermissionConfigModalProps = ComponentInstance<typeof PermissionConfigModal>['$props'];
|
||||
|
||||
export { PermissionConfigModal };
|
||||
@@ -0,0 +1,263 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
deletePermissionApi,
|
||||
detailBaseEmployeeApi,
|
||||
pagePermissionApi,
|
||||
savePermissionApi,
|
||||
type BaseEmployeeResultVO,
|
||||
type NdmPermissionResultVO,
|
||||
type NdmPermissionSaveVO,
|
||||
type Station,
|
||||
} from '@/apis';
|
||||
import { PERMISSION_TYPE_NAMES, type PermissionType } from '@/enums';
|
||||
import { useStationStore } from '@/stores';
|
||||
import { parseErrorFeedback } from '@/utils';
|
||||
import { useMutation } from '@tanstack/vue-query';
|
||||
import { objectEntries } from '@vueuse/core';
|
||||
import { isCancel } from 'axios';
|
||||
import { cloneDeep, groupBy } from 'es-toolkit';
|
||||
import { NButton, NCheckbox, NDataTable, NFlex, NModal, NText, type DataTableColumn, type DataTableColumns } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, h, ref, toRefs } from 'vue';
|
||||
|
||||
/**
|
||||
* 为避免修改权限记录而带来复杂的状态管理,引入 `action` 字段
|
||||
*
|
||||
* - `save`:新增权限
|
||||
* - `delete`:删除权限
|
||||
*
|
||||
* 修改权限的过程中会对本地的权限列表进行修改:
|
||||
* - 当勾选权限时,在本地新增一条权限,`action` 为 `save`
|
||||
* - 当取消勾选权限时,如果该权限来自服务器,则将`action` 改为 `delete`,否则直接删除权限记录
|
||||
*
|
||||
* 最后,保存权限时,只需要遍历本地权限列表:
|
||||
* - 如果 `action` 为 `save`,则调用 `savePermissionApi` 新增权限
|
||||
* - 如果 `action` 为 `delete`,则调用 `deletePermissionApi` 删除权限
|
||||
*/
|
||||
type PermissionAction = 'save' | 'delete';
|
||||
|
||||
type NdmPermissionSaveOrResultVO = NdmPermissionSaveVO | NdmPermissionResultVO;
|
||||
|
||||
type NdmPermissionExtendedSaveVO = NdmPermissionSaveVO & { action?: PermissionAction };
|
||||
|
||||
type NdmPermissionExtendedResultVO = NdmPermissionResultVO & { action?: PermissionAction };
|
||||
|
||||
type NdmPermissionExtendedSaveOrResultVO = NdmPermissionExtendedSaveVO | NdmPermissionExtendedResultVO;
|
||||
|
||||
const omitActionField = (extendedSaveOrResultVO: NdmPermissionExtendedSaveOrResultVO) => {
|
||||
const copy = cloneDeep(extendedSaveOrResultVO);
|
||||
delete copy.action;
|
||||
const saveOrResultVO = copy as NdmPermissionSaveOrResultVO;
|
||||
return saveOrResultVO;
|
||||
};
|
||||
|
||||
const isSaveVO = (saveOrResultVO: NdmPermissionSaveOrResultVO): saveOrResultVO is NdmPermissionSaveVO => {
|
||||
return !('id' in saveOrResultVO);
|
||||
};
|
||||
|
||||
const isResultVO = (saveOrResultVO: NdmPermissionSaveOrResultVO): saveOrResultVO is NdmPermissionResultVO => {
|
||||
return 'id' in saveOrResultVO;
|
||||
};
|
||||
|
||||
const props = defineProps<{
|
||||
employeeId?: string;
|
||||
}>();
|
||||
|
||||
const stationStore = useStationStore();
|
||||
const { stations } = storeToRefs(stationStore);
|
||||
|
||||
const show = defineModel<boolean>('show', { default: false });
|
||||
|
||||
const { employeeId } = toRefs(props);
|
||||
|
||||
const abortController = ref<AbortController>(new AbortController());
|
||||
|
||||
const employee = ref<BaseEmployeeResultVO>();
|
||||
const { mutateAsync: getEmployeeAsync } = useMutation({
|
||||
mutationFn: async () => {
|
||||
abortController.value.abort();
|
||||
abortController.value = new AbortController();
|
||||
|
||||
if (!employeeId.value) return;
|
||||
|
||||
const signal = abortController.value.signal;
|
||||
|
||||
const data = await detailBaseEmployeeApi(employeeId.value, { signal });
|
||||
return data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
if (!data) return;
|
||||
employee.value = data;
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isCancel(error)) return;
|
||||
console.error(error);
|
||||
const errorFeedback = parseErrorFeedback(error);
|
||||
window.$message.error(errorFeedback);
|
||||
},
|
||||
});
|
||||
|
||||
const permissions = ref<Record<Station['code'], NdmPermissionExtendedSaveOrResultVO[]>>({});
|
||||
const { mutate: getPermissions, isPending: permissionsLoading } = useMutation({
|
||||
mutationFn: async () => {
|
||||
abortController.value.abort();
|
||||
abortController.value = new AbortController();
|
||||
|
||||
if (!employeeId.value) return;
|
||||
|
||||
const signal = abortController.value.signal;
|
||||
|
||||
const data = await pagePermissionApi(
|
||||
{
|
||||
model: {
|
||||
employeeId: employeeId.value,
|
||||
},
|
||||
extra: {},
|
||||
current: 1,
|
||||
size: Object.keys(PERMISSION_TYPE_NAMES).length * stations.value.length,
|
||||
sort: 'id',
|
||||
order: 'ascending',
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
if (!data) return;
|
||||
const { records } = data;
|
||||
permissions.value = groupBy(records, (record) => record.stationCode ?? '');
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isCancel(error)) return;
|
||||
console.error(error);
|
||||
const errorFeedback = parseErrorFeedback(error);
|
||||
window.$message.error(errorFeedback);
|
||||
},
|
||||
});
|
||||
|
||||
const onUpdatePermissionChecked = (checked: boolean, stationCode: Station['code'], permissionType: PermissionType) => {
|
||||
if (!employeeId.value) return;
|
||||
if (checked) {
|
||||
// 勾选时,新增一条权限记录,`action` 为 `save`
|
||||
if (!permissions.value[stationCode]) permissions.value[stationCode] = [];
|
||||
permissions.value[stationCode].push({ employeeId: employeeId.value, stationCode, type: permissionType, action: 'save' });
|
||||
} else {
|
||||
if (!permissions.value[stationCode]) return;
|
||||
// 取消勾选时,先找到所有该类型的权限并遍历,
|
||||
// 如果权限来自数据库(即有 `id` 字段),则将其 `action` 设为 `delete`,
|
||||
// 否则直接从权限记录中移除
|
||||
const targets = permissions.value[stationCode].filter((permission) => permission.type === permissionType);
|
||||
for (const permission of targets) {
|
||||
if (isResultVO(permission)) {
|
||||
permission.action = 'delete';
|
||||
} else {
|
||||
permissions.value[stationCode].splice(permissions.value[stationCode].indexOf(permission), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tableColumns = computed<DataTableColumns<Station>>(() => {
|
||||
return [
|
||||
{ title: '车站编号', key: 'code', align: 'center', width: 120 },
|
||||
{ title: '车站名称', key: 'name', align: 'center', width: 360 },
|
||||
// 「权限」列
|
||||
...objectEntries(PERMISSION_TYPE_NAMES).map<DataTableColumn<Station>>(([permissionType, title]) => ({
|
||||
title: title,
|
||||
key: permissionType,
|
||||
align: 'center',
|
||||
render: (rowData) => {
|
||||
const { code: stationCode } = rowData;
|
||||
return h(NCheckbox, {
|
||||
// 如果权限记录中存在该权限且 `action` 不为 `delete`,则处于勾选状态
|
||||
checked: !!(permissions.value[stationCode] ?? []).find((permission) => permission.type === permissionType && permission.action !== 'delete'),
|
||||
// 改变勾选状态
|
||||
onUpdateChecked: (checked) => onUpdatePermissionChecked(checked, stationCode, permissionType),
|
||||
});
|
||||
},
|
||||
})),
|
||||
];
|
||||
});
|
||||
|
||||
const { mutate: savePermissions, isPending: permissionsSaving } = useMutation({
|
||||
mutationFn: async () => {
|
||||
abortController.value.abort();
|
||||
abortController.value = new AbortController();
|
||||
|
||||
const signal = abortController.value.signal;
|
||||
|
||||
// 遍历所有的权限记录,根据 `action` 进行保存或删除
|
||||
for (const stationPermissions of Object.values(permissions.value)) {
|
||||
for (const permission of stationPermissions) {
|
||||
const saveOrResultVO = omitActionField(permission);
|
||||
if (permission.action === 'save' && isSaveVO(saveOrResultVO)) {
|
||||
await savePermissionApi(saveOrResultVO, { signal });
|
||||
}
|
||||
if (permission.action === 'delete' && isResultVO(saveOrResultVO)) {
|
||||
const id = saveOrResultVO.id;
|
||||
if (!id) continue;
|
||||
await deletePermissionApi([id], { signal });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
window.$message.success('权限配置保存成功');
|
||||
getPermissions();
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isCancel(error)) return;
|
||||
console.error(error);
|
||||
const errorFeedback = parseErrorFeedback(error);
|
||||
window.$message.error(errorFeedback);
|
||||
},
|
||||
});
|
||||
|
||||
const onAfterEnter = () => {
|
||||
getEmployeeAsync().then(() => getPermissions());
|
||||
};
|
||||
|
||||
const onAfterLeave = () => {
|
||||
employee.value = undefined;
|
||||
permissions.value = {};
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
abortController.value.abort();
|
||||
show.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal
|
||||
v-model:show="show"
|
||||
preset="card"
|
||||
style="width: 100vw; height: 100vh"
|
||||
:content-style="{ height: '100%', overflow: 'hidden' }"
|
||||
:close-on-esc="false"
|
||||
:mask-closable="false"
|
||||
:auto-focus="false"
|
||||
@after-enter="onAfterEnter"
|
||||
@after-leave="onAfterLeave"
|
||||
@close="onClose"
|
||||
>
|
||||
<template #header>
|
||||
<span>{{ `配置权限 - ${employee?.realName ?? ''}` }}</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<NDataTable flex-height style="height: 100%" :columns="tableColumns" :data="stations" :loading="permissionsLoading" :single-line="false" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<NText depth="3" style="font-size: smaller">*未勾选任何权限的用户将被认为拥有所有权限</NText>
|
||||
</template>
|
||||
<template #action>
|
||||
<NFlex justify="end">
|
||||
<NButton size="small" @click="onClose">取消</NButton>
|
||||
<NButton type="primary" size="small" :loading="permissionsSaving" @click="() => savePermissions()">保存</NButton>
|
||||
</NFlex>
|
||||
</template>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Reference in New Issue
Block a user