|
| 1 | +<script setup lang="ts"> |
| 2 | +import { computed, onMounted, ref, useTemplateRef } from 'vue' |
| 3 | +import type ResourceAuthorizationApi from '@/api/admin/workspace/resource-authorization' |
| 4 | +import type SystemResourceAuthorizationApi from '@/api/admin/system/resource-management/resource-authorization' |
| 5 | +import { RESOURCE_PERMISSION } from '@/api/enums' |
| 6 | +import type { Dict, ResourcePermission, ResourceUserPermission } from '@/api/types' |
| 7 | +import { MsgSuccess } from '@/utils/message' |
| 8 | +import MkTable from '@/components/global/mk-table/index.vue' |
| 9 | +import PermissionConfigDialog from './PermissionConfigDialog.vue' |
| 10 | +import { useStore } from '@/stores' |
| 11 | +
|
| 12 | +import type { ResourceAuthorizationContentProps, ResourcePermissionOption } from './types' |
| 13 | +
|
| 14 | +interface Props extends ResourceAuthorizationContentProps { |
| 15 | + api: typeof ResourceAuthorizationApi | typeof SystemResourceAuthorizationApi |
| 16 | + permissionOptions: ResourcePermissionOption[] |
| 17 | +} |
| 18 | +
|
| 19 | +const props = defineProps<Props>() |
| 20 | +const emit = defineEmits<{ refresh: [] }>() |
| 21 | +const submitting = defineModel<boolean>('submitting', { required: true }) |
| 22 | +
|
| 23 | +/* 查询与跨页选择 */ |
| 24 | +const { auth } = useStore() |
| 25 | +const searchFields = computed(() => [ |
| 26 | + { label: '姓名', value: 'nick_name' }, |
| 27 | + { label: '用户名', value: 'username' }, |
| 28 | + { label: '权限', value: 'permission', multiple: true, options: props.permissionOptions }, |
| 29 | + ...(auth.isEE || auth.isPE ? [{ label: '角色', value: 'role' }] : []), |
| 30 | +]) |
| 31 | +const loading = ref(false) |
| 32 | +const permissionUsers = ref<ResourceUserPermission[]>([]) |
| 33 | +const selectedUsers = ref<ResourceUserPermission[]>([]) |
| 34 | +const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) |
| 35 | +const searchQuery = ref<Dict<unknown>>() |
| 36 | +const tableRef = useTemplateRef('tableRef') |
| 37 | +
|
| 38 | +function loadPermissions() { |
| 39 | + if (!props.targetId) return Promise.resolve() |
| 40 | + loading.value = true |
| 41 | + return props.api |
| 42 | + .getResourceAuthorization(props.targetId, props.type, paginationConfig.value, searchQuery.value) |
| 43 | + .then(({ records, total }) => { |
| 44 | + permissionUsers.value = records.map((subject) => ({ |
| 45 | + ...subject, |
| 46 | + permission: props.isRootFolder && subject.permission === RESOURCE_PERMISSION.NOT_AUTH ? RESOURCE_PERMISSION.VIEW : subject.permission, |
| 47 | + })) |
| 48 | + paginationConfig.value.total = total |
| 49 | + }) |
| 50 | + .finally(() => { |
| 51 | + loading.value = false |
| 52 | + }) |
| 53 | +} |
| 54 | +
|
| 55 | +function clearSelection() { |
| 56 | + selectedUsers.value = [] |
| 57 | + tableRef.value?.clearSelection() |
| 58 | +} |
| 59 | +
|
| 60 | +function handleSearch(query?: Dict<unknown>) { |
| 61 | + searchQuery.value = query |
| 62 | + paginationConfig.value.currentPage = 1 |
| 63 | + clearSelection() |
| 64 | + loadPermissions() |
| 65 | +} |
| 66 | +
|
| 67 | +function handleSelectionChange(selection: unknown[]) { |
| 68 | + selectedUsers.value = selection as ResourceUserPermission[] |
| 69 | +} |
| 70 | +
|
| 71 | +/* 单项与批量保存:确认前不修改行数据,失败时保留待提交配置。 */ |
| 72 | +const configDialogRef = useTemplateRef<InstanceType<typeof PermissionConfigDialog>>('configDialogRef') |
| 73 | +const pendingUserIds = ref<string[]>([]) |
| 74 | +
|
| 75 | +function handleOpenBatchConfig() { |
| 76 | + if (!selectedUsers.value.length || submitting.value) return |
| 77 | + pendingUserIds.value = selectedUsers.value.map(({ id }) => id) |
| 78 | + configDialogRef.value?.open() |
| 79 | +} |
| 80 | +
|
| 81 | +function handlePermissionChange(value: string | number | boolean | undefined, subject: ResourceUserPermission) { |
| 82 | + const permission = props.editablePermissionOptions.find((option) => option.value === value)?.value |
| 83 | + if (!permission || permission === subject.permission || submitting.value) return |
| 84 | + pendingUserIds.value = [subject.id] |
| 85 | + if (props.isFolder) { |
| 86 | + configDialogRef.value?.open(permission) |
| 87 | + } else { |
| 88 | + submitPermissions(permission, false) |
| 89 | + } |
| 90 | +} |
| 91 | +
|
| 92 | +function submitPermissions(permission: ResourcePermission, includeChildren: boolean) { |
| 93 | + if (submitting.value || !pendingUserIds.value.length || (includeChildren && !props.managedFolderIds.length)) return |
| 94 | + const permissionScope = { |
| 95 | + permission, |
| 96 | + include_children: includeChildren, |
| 97 | + ...(includeChildren ? { folder_ids: [...props.managedFolderIds] } : {}), |
| 98 | + } |
| 99 | + submitting.value = true |
| 100 | + return props.api |
| 101 | + .putResourceAuthorization( |
| 102 | + props.targetId, |
| 103 | + props.type, |
| 104 | + pendingUserIds.value.map((id) => ({ user_id: id, ...permissionScope })), |
| 105 | + ) |
| 106 | + .then(() => { |
| 107 | + MsgSuccess('提交成功') |
| 108 | + configDialogRef.value?.close() |
| 109 | + pendingUserIds.value = [] |
| 110 | + clearSelection() |
| 111 | + emit('refresh') |
| 112 | + return loadPermissions() |
| 113 | + }) |
| 114 | + .finally(() => { |
| 115 | + submitting.value = false |
| 116 | + }) |
| 117 | +} |
| 118 | +
|
| 119 | +/* 挂载当前标签时查询权限。 */ |
| 120 | +onMounted(() => loadPermissions()) |
| 121 | +</script> |
| 122 | + |
| 123 | +<template> |
| 124 | + <div> |
| 125 | + <div class="flex-between mb-4 gap-4"> |
| 126 | + <el-button type="primary" :disabled="!selectedUsers.length || loading || submitting" @click="handleOpenBatchConfig">配置权限</el-button> |
| 127 | + <MkComplexSearch :fields="searchFields" @change="handleSearch" /> |
| 128 | + </div> |
| 129 | + <MkTable |
| 130 | + ref="tableRef" |
| 131 | + v-loading="loading || submitting" |
| 132 | + v-model:pagination-config="paginationConfig" |
| 133 | + :data="permissionUsers" |
| 134 | + :max-table-height="260" |
| 135 | + @current-change="loadPermissions" |
| 136 | + @size-change="loadPermissions" |
| 137 | + @selection-change="handleSelectionChange" |
| 138 | + > |
| 139 | + <el-table-column type="selection" width="40" reserve-selection /> |
| 140 | + <el-table-column prop="nick_name" label="姓名" min-width="120" show-overflow-tooltip /> |
| 141 | + <el-table-column prop="username" label="用户名" min-width="120" show-overflow-tooltip /> |
| 142 | + <el-table-column v-if="auth.isEE || auth.isPE" label="角色" width="160"> |
| 143 | + <template #default="{ row }"> |
| 144 | + <MkTagGroup v-if="row.role_name?.length" :tags="row.role_name" /> |
| 145 | + <span v-else>-</span> |
| 146 | + </template> |
| 147 | + </el-table-column> |
| 148 | + <el-table-column label="权限" min-width="340"> |
| 149 | + <template #default="{ row }"> |
| 150 | + <el-radio-group :model-value="row.permission" :disabled="submitting" @change="handlePermissionChange($event, row)"> |
| 151 | + <el-radio v-for="option in editablePermissionOptions" :key="option.value" :value="option.value" class="mr-4!">{{ |
| 152 | + option.label |
| 153 | + }}</el-radio> |
| 154 | + </el-radio-group> |
| 155 | + </template> |
| 156 | + </el-table-column> |
| 157 | + </MkTable> |
| 158 | + <PermissionConfigDialog |
| 159 | + ref="configDialogRef" |
| 160 | + :options="editablePermissionOptions" |
| 161 | + :is-folder="isFolder" |
| 162 | + :can-include-children="managedFolderIds.length > 0" |
| 163 | + :loading="submitting" |
| 164 | + @submit="submitPermissions" |
| 165 | + /> |
| 166 | + </div> |
| 167 | +</template> |
0 commit comments