<script lang="ts" setup>
|
import type { DmsAction, DmsGrantBatch, DmsPermissionResource, DmsPrincipalType, DmsResourceScope } from '#/api/x/dms/permission';
|
|
import { computed, ref } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { BasicDrawer, useDrawerInner } from '@jnpf/ui/drawer';
|
import { BasicForm, useForm } from '@jnpf/ui/form';
|
|
import dayjs from 'dayjs';
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { createPermissionGrant } from '#/api/x/dms/permission';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
import PermissionGroupSelect from './PermissionGroupSelect.vue';
|
import { actionsForResource, normalizePrincipalId, permissionDateToIso, permissionResourceDisplayName, scopesForResource } from './permissionRules';
|
|
interface GrantFormState {
|
actions: DmsAction[];
|
grantReason: string;
|
includeChildOrgs: boolean;
|
maxUseCount?: number;
|
principalIds: string[];
|
principalType: DmsPrincipalType;
|
resourceScope: DmsResourceScope;
|
validity?: [number, number];
|
}
|
|
const emit = defineEmits<{
|
reload: [];
|
}>();
|
|
const resource = ref<DmsPermissionResource>();
|
const sourceId = ref('');
|
const { createMessage } = useMessage();
|
const MAX_GRANT_ITEMS = 500;
|
|
const principalOptions: Array<{ fullName: string; id: DmsPrincipalType }> = [
|
{ fullName: '用户', id: 'USER' },
|
{ fullName: '组织', id: 'ORG' },
|
{ fullName: '岗位', id: 'POSITION' },
|
{ fullName: '角色', id: 'ROLE' },
|
{ fullName: '权限组', id: 'GROUP' },
|
];
|
const availableActions = computed(() => (resource.value ? actionsForResource(resource.value.resourceType) : []));
|
const availableScopes = computed(() => (resource.value ? scopesForResource(resource.value.resourceType) : []));
|
const [registerForm, { clearValidate, resetFields, setFieldsValue, validate }] = useForm({
|
baseColProps: { span: 24 },
|
layout: 'vertical',
|
schemas: [
|
{
|
field: 'principalType',
|
label: '授权主体类型',
|
component: 'Radio',
|
componentProps: {
|
buttonStyle: 'solid',
|
onChange: handlePrincipalTypeChange,
|
optionType: 'button',
|
options: principalOptions,
|
},
|
defaultValue: 'USER',
|
rules: [{ required: true, message: '请选择授权主体类型', trigger: 'change' }],
|
},
|
{
|
field: 'principalIds',
|
label: '授权主体',
|
component: 'UserSelect',
|
slot: 'principalIds',
|
rules: [{ required: true, type: 'array', min: 1, message: '请选择授权主体', trigger: 'change' }],
|
},
|
{
|
field: 'actions',
|
label: '允许操作',
|
component: 'Checkbox',
|
componentProps: () => ({ options: availableActions.value.map((item) => ({ fullName: item.label, id: item.value })) }),
|
defaultValue: ['READ'],
|
rules: [{ required: true, type: 'array', min: 1, message: '至少选择一个操作', trigger: 'change' }],
|
},
|
{
|
field: 'resourceScope',
|
label: '资源范围',
|
component: 'Select',
|
componentProps: () => ({ options: availableScopes.value.map((item) => ({ fullName: item.label, id: item.value })) }),
|
defaultValue: 'SELF',
|
rules: [{ required: true, message: '请选择资源范围', trigger: 'change' }],
|
},
|
{
|
field: 'includeChildOrgs',
|
label: '组织范围',
|
component: 'JnpfCheckboxSingle',
|
componentProps: { checkedValue: true, label: '包含子组织成员', unCheckedValue: false },
|
defaultValue: false,
|
ifShow: ({ values }) => values.principalType === 'ORG',
|
},
|
{
|
field: 'validity',
|
label: '有效期',
|
component: 'DateRange',
|
componentProps: () => ({ format: 'YYYY-MM-DD HH:mm', placeholder: ['开始时间', '结束时间'], startTime: dayjs().startOf('day').valueOf() }),
|
},
|
{
|
field: 'maxUseCount',
|
label: '最多使用次数',
|
component: 'InputNumber',
|
componentProps: { min: 1, placeholder: '不限次数', precision: 0 },
|
extra: '不填写表示不限制;每次受限操作只扣减一次。',
|
ifShow: ({ values }) => values.principalType === 'USER',
|
},
|
{
|
field: 'grantReason',
|
label: '授权原因',
|
component: 'Textarea',
|
componentProps: { maxlength: 1000, rows: 3, showCount: true },
|
},
|
],
|
showActionButtonGroup: false,
|
});
|
const [registerDrawer, { changeOkLoading, closeDrawer }] = useDrawerInner(init);
|
|
function defaultForm(): GrantFormState {
|
return {
|
actions: ['READ'],
|
grantReason: '',
|
includeChildOrgs: false,
|
principalIds: [],
|
principalType: 'USER',
|
resourceScope: 'SELF',
|
};
|
}
|
|
function newSourceId() {
|
return `manual-${uuidv4()}`;
|
}
|
|
async function init(data: { resource: DmsPermissionResource }) {
|
resource.value = data.resource;
|
sourceId.value = newSourceId();
|
await resetFields();
|
await setFieldsValue(defaultForm());
|
}
|
|
async function handlePrincipalTypeChange() {
|
await setFieldsValue({
|
includeChildOrgs: false,
|
maxUseCount: undefined,
|
principalIds: [],
|
});
|
await clearValidate(['principalIds']);
|
}
|
|
async function handleSubmit() {
|
if (!resource.value) return;
|
const values = await validate();
|
if (!values) return;
|
const form = values as GrantFormState;
|
const currentResource = resource.value;
|
const validFrom = permissionDateToIso(form.validity?.[0]);
|
const validTo = permissionDateToIso(form.validity?.[1]);
|
const principalIds = [...new Set(form.principalIds.map(normalizePrincipalId).filter(Boolean))];
|
if (!principalIds.length) {
|
createMessage.warning('请选择授权主体');
|
return;
|
}
|
if (principalIds.length * form.actions.length > MAX_GRANT_ITEMS) {
|
createMessage.warning(`一次最多创建 ${MAX_GRANT_ITEMS} 条授权,请减少主体或操作`);
|
return;
|
}
|
const data: DmsGrantBatch = {
|
sourceId: sourceId.value,
|
sourceType: 'MANUAL',
|
items: principalIds.flatMap((principalId) =>
|
form.actions.map((actionCode) => ({
|
actionCode,
|
grantReason: form.grantReason.trim() || undefined,
|
includeChildOrgs: form.principalType === 'ORG' && form.includeChildOrgs,
|
maxUseCount: form.principalType === 'USER' ? form.maxUseCount : undefined,
|
principalId,
|
principalType: form.principalType,
|
resourceId: currentResource.id,
|
resourceScope: form.resourceScope,
|
resourceType: currentResource.resourceType,
|
validFrom,
|
validTo,
|
})),
|
),
|
};
|
onlineUtils.sign({
|
title: '电子签名',
|
isFaceToFace: false,
|
metaData: {
|
biz_button: '授权',
|
biz_data: [data],
|
biz_module: '文件夹权限管理',
|
biz_title: currentResource.name,
|
is_biz_form: false,
|
is_review_button: false,
|
},
|
onCancel: () => {},
|
onSubmit: (signData) => submitGrant(data, signData?.biz_sign),
|
});
|
}
|
|
async function submitGrant(data: DmsGrantBatch, bizSign?: string) {
|
changeOkLoading(true);
|
try {
|
const response = await createPermissionGrant(data, bizSign);
|
createMessage.success(response.msg || `已创建 ${response.data.length} 条授权`);
|
closeDrawer();
|
emit('reload');
|
} finally {
|
changeOkLoading(false);
|
}
|
}
|
</script>
|
|
<template>
|
<BasicDrawer
|
v-bind="$attrs"
|
@register="registerDrawer"
|
title="新增文件权限"
|
width="min(620px, calc(100vw - 16px))"
|
:mask-closable="false"
|
destroy-on-close
|
show-footer
|
@ok="handleSubmit">
|
<div class="grant-drawer-body" v-if="resource">
|
<a-alert class="mb-[20px]" type="info" show-icon>
|
<template #message> 授权资源:{{ permissionResourceDisplayName(resource) }}({{ resource.resourceType }}) </template>
|
</a-alert>
|
<BasicForm @register="registerForm">
|
<template #principalIds="{ model, field }">
|
<JnpfUserSelect v-if="model.principalType === 'USER'" v-model:value="model[field]" multiple />
|
<JnpfOrganizeSelect v-else-if="model.principalType === 'ORG'" v-model:value="model[field]" multiple />
|
<JnpfPosSelect v-else-if="model.principalType === 'POSITION'" v-model:value="model[field]" multiple />
|
<JnpfRoleSelect v-else-if="model.principalType === 'ROLE'" v-model:value="model[field]" multiple />
|
<PermissionGroupSelect v-else v-model:value="model[field]" />
|
</template>
|
</BasicForm>
|
</div>
|
</BasicDrawer>
|
</template>
|
|
<style lang="scss" scoped>
|
.grant-drawer-body {
|
padding: 20px 24px 28px;
|
}
|
|
:deep(.ant-radio-group) {
|
display: flex;
|
flex-wrap: wrap;
|
}
|
</style>
|