68 changed files with 2909 additions and 326 deletions
@ -0,0 +1,40 @@ |
|||||
|
import { defHttp } from '/@/utils/http/axios'; |
||||
|
import { |
||||
|
PermissionGroupDefinitionDto, |
||||
|
PermissionGroupDefinitionCreateDto, |
||||
|
PermissionGroupDefinitionUpdateDto, |
||||
|
PermissionGroupDefinitionGetListInput, |
||||
|
} from './model'; |
||||
|
|
||||
|
export const CreateAsyncByInput = (input: PermissionGroupDefinitionCreateDto) => { |
||||
|
return defHttp.post<PermissionGroupDefinitionDto>({ |
||||
|
url: '/api/permission-management/definitions/groups', |
||||
|
data: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const DeleteAsyncByName = (name: string) => { |
||||
|
return defHttp.delete<void>({ |
||||
|
url: `/api/permission-management/definitions/groups/${name}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const GetAsyncByName = (name: string) => { |
||||
|
return defHttp.get<PermissionGroupDefinitionDto>({ |
||||
|
url: `/api/permission-management/definitions/groups/${name}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const GetListAsyncByInput = (input: PermissionGroupDefinitionGetListInput) => { |
||||
|
return defHttp.get<ListResultDto<PermissionGroupDefinitionDto>>({ |
||||
|
url: '/api/permission-management/definitions/groups', |
||||
|
params: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const UpdateAsyncByNameAndInput = (name: string, input: PermissionGroupDefinitionUpdateDto) => { |
||||
|
return defHttp.put<PermissionGroupDefinitionDto>({ |
||||
|
url: `/api/permission-management/definitions/groups/${name}`, |
||||
|
data: input, |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,19 @@ |
|||||
|
interface PermissionGroupDefinitionCreateOrUpdateDto extends IHasExtraProperties { |
||||
|
displayName: string; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionGroupDefinitionCreateDto extends PermissionGroupDefinitionCreateOrUpdateDto { |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionGroupDefinitionDto extends IHasExtraProperties { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
isStatic: boolean; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionGroupDefinitionGetListInput { |
||||
|
filter?: string; |
||||
|
} |
||||
|
|
||||
|
export type PermissionGroupDefinitionUpdateDto = PermissionGroupDefinitionCreateOrUpdateDto; |
||||
@ -0,0 +1,40 @@ |
|||||
|
import { defHttp } from '/@/utils/http/axios'; |
||||
|
import { |
||||
|
PermissionDefinitionDto, |
||||
|
PermissionDefinitionCreateDto, |
||||
|
PermissionDefinitionUpdateDto, |
||||
|
PermissionDefinitionGetListInput, |
||||
|
} from './model'; |
||||
|
|
||||
|
export const CreateAsyncByInput = (input: PermissionDefinitionCreateDto) => { |
||||
|
return defHttp.post<PermissionDefinitionDto>({ |
||||
|
url: '/api/permission-management/definitions', |
||||
|
data: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const DeleteAsyncByName = (name: string) => { |
||||
|
return defHttp.delete<void>({ |
||||
|
url: `/api/permission-management/definitions/${name}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const GetAsyncByName = (name: string) => { |
||||
|
return defHttp.get<PermissionDefinitionDto>({ |
||||
|
url: `/api/permission-management/definitions/${name}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const GetListAsyncByInput = (input: PermissionDefinitionGetListInput) => { |
||||
|
return defHttp.get<ListResultDto<PermissionDefinitionDto>>({ |
||||
|
url: '/api/permission-management/definitions', |
||||
|
params: input, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
export const UpdateAsyncByNameAndInput = (name: string, input: PermissionDefinitionUpdateDto) => { |
||||
|
return defHttp.put<PermissionDefinitionDto>({ |
||||
|
url: `/api/permission-management/definitions/${name}`, |
||||
|
data: input, |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,36 @@ |
|||||
|
export enum MultiTenancySides { |
||||
|
Tenant = 0x1, |
||||
|
Host = 0x2, |
||||
|
Both = 0x3, |
||||
|
} |
||||
|
|
||||
|
interface PermissionDefinitionCreateOrUpdateDto extends IHasExtraProperties { |
||||
|
displayName: string; |
||||
|
parentName?: string; |
||||
|
isEnabled: boolean; |
||||
|
providers: string[]; |
||||
|
stateCheckers: string; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionDefinitionCreateDto extends PermissionDefinitionCreateOrUpdateDto { |
||||
|
groupName: string; |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionDefinitionDto extends IHasExtraProperties { |
||||
|
groupName: string; |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
parentName?: string; |
||||
|
isEnabled: boolean; |
||||
|
isStatic: boolean; |
||||
|
providers: string[]; |
||||
|
stateCheckers: string; |
||||
|
} |
||||
|
|
||||
|
export interface PermissionDefinitionGetListInput { |
||||
|
filter?: string; |
||||
|
groupName?: string; |
||||
|
} |
||||
|
|
||||
|
export type PermissionDefinitionUpdateDto = PermissionDefinitionCreateOrUpdateDto; |
||||
@ -1,21 +1,16 @@ |
|||||
import { defAbpHttp } from '/@/utils/http/abp'; |
import { defAbpHttp } from '/@/utils/http/abp'; |
||||
import { PermissionProvider, PermissionResult, UpdatePermissions } from './model/permissionModel'; |
import { PermissionProvider, PermissionResult, UpdatePermissions } from './model'; |
||||
|
|
||||
enum Api { |
|
||||
Get = '/api/permission-management/permissions', |
|
||||
Update = '/api/permission-management/permissions', |
|
||||
} |
|
||||
|
|
||||
export const get = (provider: PermissionProvider) => { |
export const get = (provider: PermissionProvider) => { |
||||
return defAbpHttp.get<PermissionResult>({ |
return defAbpHttp.get<PermissionResult>({ |
||||
url: Api.Get, |
url: '/api/permission-management/permissions', |
||||
params: provider, |
params: provider, |
||||
}); |
}); |
||||
}; |
}; |
||||
|
|
||||
export const update = (provider: PermissionProvider, input: UpdatePermissions) => { |
export const update = (provider: PermissionProvider, input: UpdatePermissions) => { |
||||
return defAbpHttp.put<void>({ |
return defAbpHttp.put<void>({ |
||||
url: Api.Update, |
url: '/api/permission-management/permissions', |
||||
data: input, |
data: input, |
||||
params: provider, |
params: provider, |
||||
}); |
}); |
||||
@ -1,4 +1,4 @@ |
|||||
import { IPermission } from '../../model/baseModel'; |
import { IPermission } from '/@/api/model/baseModel'; |
||||
|
|
||||
export class PermissionProvider { |
export class PermissionProvider { |
||||
providerName!: string; |
providerName!: string; |
||||
@ -0,0 +1,305 @@ |
|||||
|
<template> |
||||
|
<div :class="`${prefixCls}__container`"> |
||||
|
<div class="card"> |
||||
|
<Card> |
||||
|
<template #title> |
||||
|
<Row> |
||||
|
<Col :span="12"> |
||||
|
<span>{{ t('component.simple_state_checking.title') }}</span> |
||||
|
</Col> |
||||
|
<Col :span="12"> |
||||
|
<div class="toolbar" v-if="!props.disabled"> |
||||
|
<Button type="primary" @click="handleAddNew">{{ t('component.simple_state_checking.actions.create') }}</Button> |
||||
|
<Button danger @click="handleClean">{{ t('component.simple_state_checking.actions.clean') }}</Button> |
||||
|
</div> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</template> |
||||
|
<Table :columns="getTableColumns" :data-source="getSimpleCheckers"> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'name'"> |
||||
|
<span>{{ simpleCheckerMap[record.name] }}</span> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'properties'"> |
||||
|
<div v-if="record.name === 'F'"> |
||||
|
<Tag v-for="feature in record.featureNames">{{ feature }}</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'G'"> |
||||
|
<Tag v-for="feature in record.globalFeatureNames">{{ feature }}</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'P'"> |
||||
|
<Tag v-for="permission in record.model.permissions">{{ permission }}</Tag> |
||||
|
</div> |
||||
|
<div v-else-if="record.name === 'A'"> |
||||
|
<span>{{ t('component.simple_state_checking.requireAuthenticated.title') }}</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'action'"> |
||||
|
<div :class="`${prefixCls}__action`"> |
||||
|
<Button v-if="props.allowEdit" type="link" @click="() => handleEdit(record)"> |
||||
|
<template #icon> |
||||
|
<EditOutlined /> |
||||
|
</template> |
||||
|
{{ t('component.simple_state_checking.actions.update') }} |
||||
|
</Button> |
||||
|
<Divider v-if="props.allowEdit && props.allowDelete" type="vertical" /> |
||||
|
<Button v-if="props.allowDelete" type="link" @click="() => handleDelete(record)" class="ant-btn-error"> |
||||
|
<template #icon> |
||||
|
<DeleteOutlined /> |
||||
|
</template> |
||||
|
{{ t('component.simple_state_checking.actions.delete') }} |
||||
|
</Button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</template> |
||||
|
</Table> |
||||
|
</Card> |
||||
|
</div> |
||||
|
<Modal :class="`${prefixCls}__modal`" v-bind="state.modal"> |
||||
|
<Form |
||||
|
ref="formRef" |
||||
|
class="form" |
||||
|
v-bind="state.form" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 16}" |
||||
|
> |
||||
|
<FormItem name="name" required :label="t('component.simple_state_checking.form.name')"> |
||||
|
<Select |
||||
|
:disabled="state.form.editFlag" |
||||
|
:options="getStateCheckerOptions" |
||||
|
v-model:value="state.form.model.name" |
||||
|
@change="handleStateCheckerChange" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
<component :is="componentsRef[state.form.editComponent]" v-model:value="state.form.model.stateChecker" /> |
||||
|
</Form> |
||||
|
</Modal> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { RuleObject } from 'ant-design-vue/lib/form'; |
||||
|
import type { ColumnsType } from 'ant-design-vue/lib/table'; |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, reactive, ref, unref, shallowRef, shallowReactive } from 'vue'; |
||||
|
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue'; |
||||
|
import { Button, Card, Col, Divider, Empty, Form, Modal, Row, Select, Table, Tag } from 'ant-design-vue'; |
||||
|
import { useDesign } from '/@/hooks/web/useDesign'; |
||||
|
import { useI18n } from '/@/hooks/web/useI18n'; |
||||
|
import { isNullOrUnDef } from '/@/utils/is'; |
||||
|
import { SimplaCheckStateBase } from './typing'; |
||||
|
import { useSimpleStateCheck } from '/@/hooks/abp/useSimpleStateCheck'; |
||||
|
import { propTypes } from '/@/utils/propTypes'; |
||||
|
import RequireGlobalFeaturesSimpleStateChecker from './src/globalFeatures/RequireGlobalFeaturesSimpleStateChecker.vue'; |
||||
|
import RequirePermissionsSimpleStateChecker from './src/permissions/RequirePermissionsSimpleStateChecker.vue'; |
||||
|
import RequireFeaturesSimpleStateChecker from './src/features/RequireFeaturesSimpleStateChecker.vue'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
interface State { |
||||
|
modal: { |
||||
|
title?: string, |
||||
|
visible?: boolean, |
||||
|
maskClosable?: boolean, |
||||
|
width?: number, |
||||
|
minHeight?: number, |
||||
|
onOk?: (e: MouseEvent) => void, |
||||
|
onCancel?: (e: MouseEvent) => void, |
||||
|
}, |
||||
|
form: { |
||||
|
model: any, |
||||
|
editFlag: boolean, |
||||
|
editComponent: string, |
||||
|
rules?: Dictionary<string, RuleObject>, |
||||
|
}, |
||||
|
} |
||||
|
const emits = defineEmits(['change', 'update:value']); |
||||
|
const props = defineProps({ |
||||
|
value: propTypes.string, |
||||
|
state: { |
||||
|
type: Object as PropType<SimplaCheckStateBase>, |
||||
|
required: true, |
||||
|
}, |
||||
|
allowEdit: propTypes.bool.def(false), |
||||
|
allowDelete: propTypes.bool.def(false), |
||||
|
disabled: propTypes.bool.def(false), |
||||
|
}); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const { serializer } = useSimpleStateCheck(); |
||||
|
const { prefixCls } = useDesign('simple-state-checking'); |
||||
|
const formRef = ref<any>(); |
||||
|
const componentsRef = shallowRef({ |
||||
|
'Empty': Empty, |
||||
|
'F': RequireFeaturesSimpleStateChecker, |
||||
|
'P': RequirePermissionsSimpleStateChecker, |
||||
|
'G': RequireGlobalFeaturesSimpleStateChecker, |
||||
|
}); |
||||
|
const simpleCheckerMap = shallowReactive({ |
||||
|
'F': t('component.simple_state_checking.requireFeatures.title'), |
||||
|
'G': t('component.simple_state_checking.requireGlobalFeatures.title'), |
||||
|
'P': t('component.simple_state_checking.requirePermissions.title'), |
||||
|
'A': t('component.simple_state_checking.requireAuthenticated.title') |
||||
|
}); |
||||
|
const state = reactive<State>({ |
||||
|
modal: { |
||||
|
width: 600, |
||||
|
minHeight: 400, |
||||
|
visible: false, |
||||
|
maskClosable: false, |
||||
|
onOk: handleSubmit, |
||||
|
onCancel: handleCancel, |
||||
|
}, |
||||
|
form: { |
||||
|
model: {}, |
||||
|
editFlag: false, |
||||
|
editComponent: 'Empty', |
||||
|
}, |
||||
|
}); |
||||
|
const getTableColumns = computed(() => { |
||||
|
const columns: ColumnsType = [{ |
||||
|
title: t('component.simple_state_checking.table.name'), |
||||
|
dataIndex: 'name', |
||||
|
key: 'name', |
||||
|
align: 'left', |
||||
|
fixed: 'left', |
||||
|
width: 130, |
||||
|
}, |
||||
|
{ |
||||
|
title: t('component.simple_state_checking.table.properties'), |
||||
|
dataIndex: 'properties', |
||||
|
key: 'properties', |
||||
|
align: 'left', |
||||
|
fixed: 'left', |
||||
|
width: 200, |
||||
|
}]; |
||||
|
return columns.concat(props.disabled |
||||
|
? [] |
||||
|
: [{ |
||||
|
width: 230, |
||||
|
title: t('component.simple_state_checking.table.actions'), |
||||
|
align: 'center', |
||||
|
dataIndex: 'action', |
||||
|
key: 'action', |
||||
|
fixed: 'right', |
||||
|
}]); |
||||
|
}); |
||||
|
const getSimpleCheckers = computed(() => { |
||||
|
if (isNullOrUnDef(props.value) || props.value.length === 0) { |
||||
|
return []; |
||||
|
} |
||||
|
const simpleCheckers = serializer.deserializeArray(props.value, props.state); |
||||
|
return simpleCheckers; |
||||
|
}); |
||||
|
const getStateCheckerOptions = computed(() => { |
||||
|
const stateCheckers = unref(getSimpleCheckers); |
||||
|
return Object.keys(simpleCheckerMap).map((key) => { |
||||
|
return { |
||||
|
label: simpleCheckerMap[key], |
||||
|
disabled: stateCheckers.some(x => (x as any).name === key), |
||||
|
value: key, |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
state.form.editFlag = false; |
||||
|
state.form.editComponent = 'Empty'; |
||||
|
state.form.model = {}; |
||||
|
state.modal.title = t('component.simple_state_checking.title'); |
||||
|
state.modal.visible = true; |
||||
|
} |
||||
|
|
||||
|
function handleStateCheckerChange(value: string) { |
||||
|
const stateChecker = serializer.deserialize({ |
||||
|
T: value, |
||||
|
A: true, |
||||
|
N: [], |
||||
|
}, props.state); |
||||
|
state.form.model.stateChecker = stateChecker; |
||||
|
state.form.editComponent = value; |
||||
|
} |
||||
|
|
||||
|
function handleEdit(record) { |
||||
|
state.form.editFlag = true; |
||||
|
state.form.editComponent = record.name; |
||||
|
state.form.model = { |
||||
|
name: record.name, |
||||
|
stateChecker: cloneDeep(record), |
||||
|
}; |
||||
|
state.modal.title = simpleCheckerMap[record.name]; |
||||
|
state.modal.visible = true; |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
const stateCheckers = unref(getSimpleCheckers); |
||||
|
const filtedStateCheckers = stateCheckers.filter(x => (x as any).name !== record.name); |
||||
|
if (filtedStateCheckers.length === 0) { |
||||
|
handleClean(); |
||||
|
return; |
||||
|
} |
||||
|
const serializedCheckers = serializer.serializeArray(filtedStateCheckers); |
||||
|
emits('change', serializedCheckers); |
||||
|
emits('update:value', serializedCheckers); |
||||
|
} |
||||
|
|
||||
|
function handleClean() { |
||||
|
emits('change', undefined); |
||||
|
emits('update:value', undefined); |
||||
|
} |
||||
|
|
||||
|
function handleCancel() { |
||||
|
state.form.model = {}; |
||||
|
state.form.editFlag = false; |
||||
|
state.form.editComponent = 'Empty'; |
||||
|
state.modal.visible = false; |
||||
|
} |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
const form = unref(formRef); |
||||
|
form?.validate().then(() => { |
||||
|
const input = cloneDeep(state.form.model.stateChecker); |
||||
|
const stateCheckers = cloneDeep(unref(getSimpleCheckers)); |
||||
|
const inputIndex = stateCheckers.findIndex(x => (x as any).name === input.name); |
||||
|
if (inputIndex >= 0) { |
||||
|
stateCheckers[inputIndex] = input; |
||||
|
} else { |
||||
|
stateCheckers.push(input); |
||||
|
} |
||||
|
const updateValue = serializer.serializeArray(stateCheckers); |
||||
|
emits('change', updateValue); |
||||
|
emits('update:value', updateValue); |
||||
|
form?.resetFields(); |
||||
|
state.modal.visible = false; |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
@prefix-cls: ~'@{namespace}-simple-state-checking'; |
||||
|
|
||||
|
.@{prefix-cls} { |
||||
|
&__container { |
||||
|
width: 100%; |
||||
|
|
||||
|
.card { |
||||
|
.toolbar { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: flex-end; |
||||
|
margin-bottom: 8px; |
||||
|
|
||||
|
> * { |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&__modal { |
||||
|
.form { |
||||
|
margin: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,6 @@ |
|||||
|
import SimpleStateChecking from './SimpleStateChecking.vue'; |
||||
|
import { SimplaCheckStateBase } from './typing'; |
||||
|
import { PermissionState } from './src/permissions'; |
||||
|
|
||||
|
export { SimplaCheckStateBase, PermissionState }; |
||||
|
export { SimpleStateChecking }; |
||||
@ -0,0 +1,151 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<FormItem |
||||
|
:name="['stateChecker', 'requiresAll']" |
||||
|
:label="t('component.simple_state_checking.requireFeatures.requiresAll')" |
||||
|
:extra="t('component.simple_state_checking.requireFeatures.requiresAllDesc')" |
||||
|
> |
||||
|
<Checkbox |
||||
|
:checked="state.stateChecker.requiresAll" |
||||
|
@change="handleChangeRequiresAll" |
||||
|
> |
||||
|
{{ t('component.simple_state_checking.requireFeatures.requiresAll') }} |
||||
|
</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem :name="['stateChecker', 'featureNames']" required :label="t('component.simple_state_checking.requireFeatures.featureNames')"> |
||||
|
<TreeSelect |
||||
|
allow-clear |
||||
|
tree-checkable |
||||
|
tree-check-strictly |
||||
|
:tree-data="state.treeData" |
||||
|
:field-names="{ |
||||
|
label: 'displayName', |
||||
|
value: 'name', |
||||
|
children: 'children', |
||||
|
}" |
||||
|
:value="getRequiredFeatures" |
||||
|
@change="handleChangeFeatures" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'; |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, reactive, onMounted, watchEffect } from 'vue'; |
||||
|
import { Checkbox, Form, TreeSelect } from 'ant-design-vue'; |
||||
|
import { GetListAsyncByInput } from '/@/api/feature-management/definitions/features'; |
||||
|
import { FeatureDefinitionDto } from '/@/api/feature-management/definitions/features/model'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useI18n } from '/@/hooks/web/useI18n'; |
||||
|
import { listToTree } from '/@/utils/helper/treeHelper'; |
||||
|
import { groupBy } from '/@/utils/array'; |
||||
|
import { valueTypeSerializer } from '../../../StringValueType/valueType'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
interface FeatureTreeData { |
||||
|
name: string; |
||||
|
groupName: string; |
||||
|
displayName: string; |
||||
|
children: FeatureTreeData[]; |
||||
|
} |
||||
|
interface StateChecker { |
||||
|
name: string; |
||||
|
requiresAll: boolean; |
||||
|
featureNames: string[]; |
||||
|
} |
||||
|
interface State { |
||||
|
features: FeatureDefinitionDto[]; |
||||
|
treeData: FeatureTreeData[]; |
||||
|
stateChecker: StateChecker; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['change', 'update:value']); |
||||
|
const props = defineProps({ |
||||
|
value: { |
||||
|
type: Object as PropType<StateChecker>, |
||||
|
required: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const { Lr } = useLocalization(); |
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const state = reactive<State>({ |
||||
|
treeData: [], |
||||
|
features: [], |
||||
|
stateChecker: { |
||||
|
name: 'F', |
||||
|
requiresAll: true, |
||||
|
featureNames: [], |
||||
|
} |
||||
|
}); |
||||
|
const getRequiredFeatures = computed(() => { |
||||
|
return state.features |
||||
|
.filter((feature) => state.stateChecker.featureNames.includes(feature.name)) |
||||
|
.map((feature) => { |
||||
|
return { |
||||
|
label: feature.displayName, |
||||
|
value: feature.name, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
state.stateChecker = props.value; |
||||
|
}); |
||||
|
onMounted(fetchFeatures); |
||||
|
|
||||
|
function fetchFeatures() { |
||||
|
GetListAsyncByInput({}).then((res) => { |
||||
|
state.features = res.items; |
||||
|
formatDisplayName(state.features); |
||||
|
const featureGroup = groupBy(cloneDeep(res.items), 'groupName'); |
||||
|
const featureTreeData: FeatureTreeData[] = []; |
||||
|
Object.keys(featureGroup).forEach((gk) => { |
||||
|
const featuresByGroup = featureGroup[gk].filter(feature => { |
||||
|
const valueType = valueTypeSerializer.deserialize(feature.valueType); |
||||
|
return valueType.validator.name === 'BOOLEAN'; |
||||
|
}); |
||||
|
const featureTree = listToTree(featuresByGroup, { |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}); |
||||
|
featureTreeData.push(...featureTree); |
||||
|
}); |
||||
|
state.treeData = featureTreeData; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function formatDisplayName(list: any[]) { |
||||
|
if (list && Array.isArray(list)) { |
||||
|
list.forEach((item) => { |
||||
|
if (Reflect.has(item, 'displayName')) { |
||||
|
const info = deserialize(item.displayName); |
||||
|
item.displayName = Lr(info.resourceName, info.name); |
||||
|
} |
||||
|
if (Reflect.has(item, 'children')) { |
||||
|
formatDisplayName(item.children); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleChangeRequiresAll(e: CheckboxChangeEvent) { |
||||
|
state.stateChecker.requiresAll = e.target.checked; |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
|
||||
|
function handleChangeFeatures(value: any[]) { |
||||
|
state.stateChecker.featureNames = value.map((val) => val.value); |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,3 @@ |
|||||
|
import RequireFeaturesSimpleStateChecker from './RequireFeaturesSimpleStateChecker.vue'; |
||||
|
|
||||
|
export { RequireFeaturesSimpleStateChecker }; |
||||
@ -0,0 +1,90 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<FormItem |
||||
|
:name="['stateChecker', 'requiresAll']" |
||||
|
:label="t('component.simple_state_checking.requireFeatures.requiresAll')" |
||||
|
:extra="t('component.simple_state_checking.requireFeatures.requiresAllDesc')" |
||||
|
> |
||||
|
<Checkbox |
||||
|
:checked="state.stateChecker.requiresAll" |
||||
|
@change="handleChangeRequiresAll" |
||||
|
> |
||||
|
{{ t('component.simple_state_checking.requireFeatures.requiresAll') }} |
||||
|
</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem |
||||
|
:name="['stateChecker', 'featureNames']" |
||||
|
required |
||||
|
:label="t('component.simple_state_checking.requireGlobalFeatures.featureNames')" |
||||
|
> |
||||
|
<TextArea |
||||
|
allow-clear |
||||
|
:value="getRequiredFeatures" |
||||
|
@change="(e) => handleChangeFeatures(e.target.value)" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'; |
||||
|
import { computed, reactive, watchEffect } from 'vue'; |
||||
|
import { Checkbox, Form, Input } from 'ant-design-vue'; |
||||
|
import { useI18n } from '/@/hooks/web/useI18n'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const TextArea = Input.TextArea; |
||||
|
interface StateChecker { |
||||
|
name: string; |
||||
|
requiresAll: boolean; |
||||
|
featureNames: string[]; |
||||
|
} |
||||
|
interface State { |
||||
|
stateChecker: StateChecker; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['change', 'update:value']); |
||||
|
const props = defineProps({ |
||||
|
value: { |
||||
|
type: Object as PropType<StateChecker>, |
||||
|
required: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const state = reactive<State>({ |
||||
|
stateChecker: { |
||||
|
name: 'G', |
||||
|
requiresAll: true, |
||||
|
featureNames: [], |
||||
|
} |
||||
|
}); |
||||
|
const getRequiredFeatures = computed(() => { |
||||
|
let features = state.stateChecker.featureNames.join(','); |
||||
|
return features.length > 0 ? features.substring(0, features.length - 1) : features; |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
state.stateChecker = props.value; |
||||
|
}); |
||||
|
|
||||
|
function handleChangeRequiresAll(e: CheckboxChangeEvent) { |
||||
|
state.stateChecker.requiresAll = e.target.checked; |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
|
||||
|
function handleChangeFeatures(value?: string) { |
||||
|
if (!value) { |
||||
|
state.stateChecker.featureNames = []; |
||||
|
return; |
||||
|
} |
||||
|
state.stateChecker.featureNames = value.split(','); |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,3 @@ |
|||||
|
import RequireGlobalFeaturesSimpleStateChecker from './RequireGlobalFeaturesSimpleStateChecker.vue'; |
||||
|
|
||||
|
export { RequireGlobalFeaturesSimpleStateChecker }; |
||||
@ -0,0 +1,155 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<FormItem |
||||
|
:name="['stateChecker', 'model', 'featureNames']" |
||||
|
:label="t('component.simple_state_checking.requirePermissions.requiresAll')" |
||||
|
:extra="t('component.simple_state_checking.requirePermissions.requiresAllDesc')" |
||||
|
> |
||||
|
<Checkbox |
||||
|
:checked="state.stateChecker.model.requiresAll" |
||||
|
@change="handleChangeRequiresAll" |
||||
|
> |
||||
|
{{ t('component.simple_state_checking.requirePermissions.requiresAll') }} |
||||
|
</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem |
||||
|
:name="['stateChecker', 'model', 'permissions']" |
||||
|
required |
||||
|
:label="t('component.simple_state_checking.requirePermissions.permissions')" |
||||
|
> |
||||
|
<TreeSelect |
||||
|
allow-clear |
||||
|
tree-checkable |
||||
|
tree-check-strictly |
||||
|
:tree-data="state.treeData" |
||||
|
:field-names="{ |
||||
|
label: 'displayName', |
||||
|
value: 'name', |
||||
|
children: 'children', |
||||
|
}" |
||||
|
:value="getRequiredPermissions" |
||||
|
@change="handleChangePermissions" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'; |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, reactive, onMounted, watchEffect } from 'vue'; |
||||
|
import { Checkbox, Form, TreeSelect } from 'ant-design-vue'; |
||||
|
import { GetListAsyncByInput } from '/@/api/permission-management/definitions/permissions'; |
||||
|
import { PermissionDefinitionDto } from '/@/api/permission-management/definitions/permissions/model'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useI18n } from '/@/hooks/web/useI18n'; |
||||
|
import { listToTree } from '/@/utils/helper/treeHelper'; |
||||
|
import { groupBy } from '/@/utils/array'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
interface TreeData { |
||||
|
name: string; |
||||
|
groupName: string; |
||||
|
displayName: string; |
||||
|
children: TreeData[]; |
||||
|
} |
||||
|
interface StateCheckerModel { |
||||
|
requiresAll: boolean; |
||||
|
permissions: string[]; |
||||
|
} |
||||
|
interface StateChecker { |
||||
|
name: string; |
||||
|
model: StateCheckerModel; |
||||
|
} |
||||
|
interface State { |
||||
|
permissions: PermissionDefinitionDto[]; |
||||
|
treeData: TreeData[]; |
||||
|
stateChecker: StateChecker; |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['change', 'update:value']); |
||||
|
const props = defineProps({ |
||||
|
value: { |
||||
|
type: Object as PropType<StateChecker>, |
||||
|
required: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const { Lr } = useLocalization(); |
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const state = reactive<State>({ |
||||
|
treeData: [], |
||||
|
permissions: [], |
||||
|
stateChecker: { |
||||
|
name: 'P', |
||||
|
model: { |
||||
|
requiresAll: true, |
||||
|
permissions: [], |
||||
|
}, |
||||
|
} |
||||
|
}); |
||||
|
const getRequiredPermissions = computed(() => { |
||||
|
return state.permissions |
||||
|
.filter((permission) => state.stateChecker.model.permissions.includes(permission.name)) |
||||
|
.map((permission) => { |
||||
|
return { |
||||
|
label: permission.displayName, |
||||
|
value: permission.name, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
state.stateChecker = props.value; |
||||
|
}); |
||||
|
onMounted(fetchFeatures); |
||||
|
|
||||
|
function fetchFeatures() { |
||||
|
GetListAsyncByInput({}).then((res) => { |
||||
|
state.permissions = res.items; |
||||
|
formatDisplayName(state.permissions); |
||||
|
const permissionGroup = groupBy(cloneDeep(res.items), 'groupName'); |
||||
|
const permissionGroupTreeData: TreeData[] = []; |
||||
|
Object.keys(permissionGroup).forEach((gk) => { |
||||
|
const featureTree = listToTree(permissionGroup[gk], { |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}); |
||||
|
permissionGroupTreeData.push(...featureTree); |
||||
|
}); |
||||
|
state.treeData = permissionGroupTreeData; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function formatDisplayName(list: any[]) { |
||||
|
if (list && Array.isArray(list)) { |
||||
|
list.forEach((item) => { |
||||
|
if (Reflect.has(item, 'displayName')) { |
||||
|
const info = deserialize(item.displayName); |
||||
|
item.displayName = Lr(info.resourceName, info.name); |
||||
|
} |
||||
|
if (Reflect.has(item, 'children')) { |
||||
|
formatDisplayName(item.children); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleChangeRequiresAll(e: CheckboxChangeEvent) { |
||||
|
state.stateChecker.model.requiresAll = e.target.checked; |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
|
||||
|
function handleChangePermissions(value: any[]) { |
||||
|
state.stateChecker.model.permissions = value.map((val) => val.value); |
||||
|
emits('change', state.stateChecker); |
||||
|
emits('update:value', state.stateChecker); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,7 @@ |
|||||
|
import RequirePermissionsSimpleStateChecker from './RequirePermissionsSimpleStateChecker.vue'; |
||||
|
import { PermissionState } from './typing'; |
||||
|
|
||||
|
export { |
||||
|
RequirePermissionsSimpleStateChecker, |
||||
|
PermissionState, |
||||
|
}; |
||||
@ -0,0 +1,3 @@ |
|||||
|
export class PermissionState implements IHasSimpleStateCheckers<PermissionState> { |
||||
|
stateCheckers: ISimpleStateChecker<PermissionState>[] = []; |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
export class SimplaCheckStateBase implements IHasSimpleStateCheckers<SimplaCheckStateBase> { |
||||
|
stateCheckers: ISimpleStateChecker<SimplaCheckStateBase>[] = []; |
||||
|
} |
||||
@ -1,4 +1,5 @@ |
|||||
export * from './ExtraPropertyDictionary'; |
export * from './ExtraPropertyDictionary'; |
||||
export * from './FeatureModal'; |
export * from './FeatureModal'; |
||||
export * from './LocalizableInput'; |
export * from './LocalizableInput'; |
||||
|
export * from './SimpleStateChecking'; |
||||
export * from './StringValueType'; |
export * from './StringValueType'; |
||||
@ -0,0 +1,28 @@ |
|||||
|
import { useAbpStoreWithOut } from '/@/store/modules/abp'; |
||||
|
|
||||
|
export interface RequireAuthenticatedStateChecker { |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
export class RequireAuthenticatedSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>> implements RequireAuthenticatedStateChecker, ISimpleStateChecker<TState> { |
||||
|
name = "A"; |
||||
|
_currentUser: CurrentUser; |
||||
|
constructor(currentUser: CurrentUser) { |
||||
|
this._currentUser = currentUser; |
||||
|
} |
||||
|
isEnabled(_context: SimpleStateCheckerContext<TState>): boolean { |
||||
|
return this._currentUser.isAuthenticated; |
||||
|
} |
||||
|
|
||||
|
serialize(): string { |
||||
|
return JSON.stringify({ |
||||
|
"T": this.name, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useRequireAuthenticatedSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>>(): ISimpleStateChecker<TState> { |
||||
|
const abpStore = useAbpStoreWithOut(); |
||||
|
const { currentUser } = abpStore.getApplication; |
||||
|
return new RequireAuthenticatedSimpleStateChecker<TState>(currentUser); |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
import { IFeatureChecker, useFeatures } from '/@/hooks/abp/useFeatures'; |
||||
|
|
||||
|
export interface RequireFeaturesStateChecker { |
||||
|
name: string; |
||||
|
requiresAll: boolean; |
||||
|
featureNames: string[]; |
||||
|
} |
||||
|
|
||||
|
export class RequireFeaturesSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>> implements RequireFeaturesStateChecker, ISimpleStateChecker<TState> { |
||||
|
name: string = 'F'; |
||||
|
_featureChecker: IFeatureChecker; |
||||
|
featureNames: string[]; |
||||
|
requiresAll: boolean; |
||||
|
constructor( |
||||
|
featureChecker: IFeatureChecker, |
||||
|
featureNames: string[], |
||||
|
requiresAll: boolean = false) { |
||||
|
this._featureChecker = featureChecker; |
||||
|
this.featureNames = featureNames; |
||||
|
this.requiresAll = requiresAll; |
||||
|
} |
||||
|
isEnabled(_context: SimpleStateCheckerContext<TState>): boolean { |
||||
|
return this._featureChecker.isEnabled(this.featureNames, this.requiresAll); |
||||
|
} |
||||
|
|
||||
|
serialize(): string { |
||||
|
return JSON.stringify({ |
||||
|
"T": this.name, |
||||
|
"A": this.requiresAll, |
||||
|
"N": this.featureNames, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useRequireFeaturesSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>>( |
||||
|
featureNames: string[], |
||||
|
requiresAll: boolean = false, |
||||
|
): ISimpleStateChecker<TState> { |
||||
|
const { featureChecker } = useFeatures(); |
||||
|
return new RequireFeaturesSimpleStateChecker(featureChecker, featureNames, requiresAll); |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
import { IGlobalFeatureChecker, useGlobalFeatures } from '/@/hooks/abp/useGlobalFeatures'; |
||||
|
|
||||
|
export interface RequireGlobalFeaturesStateChecker { |
||||
|
name: string; |
||||
|
requiresAll: boolean; |
||||
|
featureNames: string[]; |
||||
|
} |
||||
|
|
||||
|
export class RequireGlobalFeaturesSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>> implements RequireGlobalFeaturesStateChecker, ISimpleStateChecker<TState> { |
||||
|
name: string = 'G'; |
||||
|
_globalFeatureChecker: IGlobalFeatureChecker; |
||||
|
featureNames: string[]; |
||||
|
requiresAll: boolean; |
||||
|
constructor( |
||||
|
globalFeatureChecker: IGlobalFeatureChecker, |
||||
|
featureNames: string[], |
||||
|
requiresAll: boolean = false) { |
||||
|
this._globalFeatureChecker = globalFeatureChecker; |
||||
|
this.featureNames = featureNames; |
||||
|
this.requiresAll = requiresAll; |
||||
|
} |
||||
|
isEnabled(_context: SimpleStateCheckerContext<TState>): boolean { |
||||
|
return this._globalFeatureChecker.isEnabled(this.featureNames, this.requiresAll); |
||||
|
} |
||||
|
|
||||
|
serialize(): string { |
||||
|
return JSON.stringify({ |
||||
|
"T": this.name, |
||||
|
"A": this.requiresAll, |
||||
|
"N": this.featureNames, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useRequireGlobalFeaturesSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>>( |
||||
|
featureNames: string[], |
||||
|
requiresAll: boolean = false, |
||||
|
): ISimpleStateChecker<TState> { |
||||
|
const globalFeatureChecker = useGlobalFeatures(); |
||||
|
return new RequireGlobalFeaturesSimpleStateChecker(globalFeatureChecker, featureNames, requiresAll); |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
import { PermissionChecker, useAuthorization } from '/@/hooks/abp/useAuthorization'; |
||||
|
|
||||
|
export interface RequirePermissionsStateChecker<TState extends IHasSimpleStateCheckers<TState>> { |
||||
|
name: string; |
||||
|
model: RequirePermissionsSimpleBatchStateCheckerModel<TState>; |
||||
|
} |
||||
|
|
||||
|
export class RequirePermissionsSimpleBatchStateCheckerModel<TState extends IHasSimpleStateCheckers<TState>> { |
||||
|
state: TState; |
||||
|
requiresAll: boolean; |
||||
|
permissions: string[]; |
||||
|
constructor( |
||||
|
state: TState, |
||||
|
permissions: string[], |
||||
|
requiresAll: boolean = true, |
||||
|
) { |
||||
|
this.state = state; |
||||
|
this.permissions = permissions; |
||||
|
this.requiresAll = requiresAll; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class RequirePermissionsSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>> implements RequirePermissionsStateChecker<TState>, ISimpleStateChecker<TState> { |
||||
|
name: string = 'P'; |
||||
|
model: RequirePermissionsSimpleBatchStateCheckerModel<TState>; |
||||
|
_permissionChecker: PermissionChecker; |
||||
|
constructor( |
||||
|
permissionChecker: PermissionChecker, |
||||
|
model: RequirePermissionsSimpleBatchStateCheckerModel<TState>) { |
||||
|
this.model = model; |
||||
|
this._permissionChecker = permissionChecker; |
||||
|
} |
||||
|
isEnabled(_context: SimpleStateCheckerContext<TState>): boolean { |
||||
|
return this._permissionChecker.isGranted(this.model.permissions, this.model.requiresAll); |
||||
|
} |
||||
|
|
||||
|
serialize(): string { |
||||
|
return JSON.stringify({ |
||||
|
"T": this.name, |
||||
|
"A": this.model.requiresAll, |
||||
|
"N": this.model.permissions, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class RequirePermissionsSimpleBatchStateChecker<TState extends IHasSimpleStateCheckers<TState>> implements ISimpleBatchStateChecker<TState> { |
||||
|
name: string = 'P'; |
||||
|
models: RequirePermissionsSimpleBatchStateCheckerModel<TState>[]; |
||||
|
_permissionChecker: PermissionChecker; |
||||
|
constructor( |
||||
|
permissionChecker: PermissionChecker, |
||||
|
models: RequirePermissionsSimpleBatchStateCheckerModel<TState>[]) { |
||||
|
this.models = models; |
||||
|
this._permissionChecker = permissionChecker; |
||||
|
} |
||||
|
isEnabled(context: SimpleBatchStateCheckerContext<TState>) { |
||||
|
const result: SimpleStateCheckerResult<TState> = {}; |
||||
|
context.states.forEach((state) => { |
||||
|
const model = this.models.find(x => x.state === state); |
||||
|
if (model) { |
||||
|
result[model.state as TState] = this._permissionChecker.isGranted(model.permissions, model.requiresAll); |
||||
|
} |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
serialize(): string | undefined { |
||||
|
return undefined; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useRequirePermissionsSimpleStateChecker<TState extends IHasSimpleStateCheckers<TState>>( |
||||
|
model: RequirePermissionsSimpleBatchStateCheckerModel<TState>): ISimpleStateChecker<TState> { |
||||
|
const permissionChecker = useAuthorization(); |
||||
|
return new RequirePermissionsSimpleStateChecker<TState>(permissionChecker, model); |
||||
|
} |
||||
|
|
||||
|
export function useRequirePermissionsSimpleBatchStateChecker<TState extends IHasSimpleStateCheckers<TState>>( |
||||
|
models: RequirePermissionsSimpleBatchStateCheckerModel<TState>[]): ISimpleBatchStateChecker<TState> { |
||||
|
const permissionChecker = useAuthorization(); |
||||
|
return new RequirePermissionsSimpleBatchStateChecker<TState>(permissionChecker, models); |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
import { computed } from 'vue'; |
||||
|
import { useAbpStoreWithOut } from '/@/store/modules/abp'; |
||||
|
import { isNullOrWhiteSpace } from '/@/utils/strings'; |
||||
|
|
||||
|
export interface IGlobalFeatureChecker { |
||||
|
isEnabled(featureNames: string | string[], requiresAll?: boolean): boolean; |
||||
|
} |
||||
|
|
||||
|
export function useGlobalFeatures() { |
||||
|
const getGlobalFeatures = computed(() => { |
||||
|
const abpStore = useAbpStoreWithOut(); |
||||
|
const enabledFeatures = abpStore.getApplication.globalFeatures.enabledFeatures ?? []; |
||||
|
return enabledFeatures; |
||||
|
}); |
||||
|
|
||||
|
function get(name: string): string | undefined { |
||||
|
return getGlobalFeatures.value.find((feature) => name === feature); |
||||
|
} |
||||
|
|
||||
|
function _isEnabled(name: string): boolean { |
||||
|
var feature = get(name); |
||||
|
return !isNullOrWhiteSpace(feature); |
||||
|
} |
||||
|
|
||||
|
function isEnabled(featureNames: string | string[], requiresAll?: boolean): boolean { |
||||
|
if (Array.isArray(featureNames)) { |
||||
|
if (featureNames.length === 0) return true; |
||||
|
if (requiresAll === undefined || requiresAll === true) { |
||||
|
for (let index = 0; index < featureNames.length; index++) { |
||||
|
if (!_isEnabled(featureNames[index])) return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
for (let index = 0; index < featureNames.length; index++) { |
||||
|
if (_isEnabled(featureNames[index])) return true; |
||||
|
} |
||||
|
} else { |
||||
|
return _isEnabled(featureNames); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
isEnabled, |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
import { |
||||
|
useRequireAuthenticatedSimpleStateChecker |
||||
|
} from "./SimpleStateChecking/useRequireAuthenticatedSimpleStateChecker"; |
||||
|
import { |
||||
|
useRequirePermissionsSimpleStateChecker |
||||
|
} from "./SimpleStateChecking/useRequirePermissionsSimpleStateChecker"; |
||||
|
import { |
||||
|
useRequireFeaturesSimpleStateChecker |
||||
|
} from "./SimpleStateChecking/useRequireFeaturesSimpleStateChecker"; |
||||
|
import { |
||||
|
useRequireGlobalFeaturesSimpleStateChecker |
||||
|
} from "./SimpleStateChecking/useRequireGlobalFeaturesSimpleStateChecker"; |
||||
|
import { isNullOrUnDef } from "/@/utils/is"; |
||||
|
import { isNullOrWhiteSpace } from "/@/utils/strings"; |
||||
|
|
||||
|
class SimpleStateCheckerSerializer implements ISimpleStateCheckerSerializer { |
||||
|
serialize<TState extends IHasSimpleStateCheckers<TState>>(checker: ISimpleStateChecker<TState>): string | undefined { |
||||
|
return checker.serialize(); |
||||
|
} |
||||
|
|
||||
|
serializeArray<TState extends IHasSimpleStateCheckers<TState>>(stateCheckers: ISimpleStateChecker<TState>[]): string | undefined { |
||||
|
if (stateCheckers.length === 0) return undefined; |
||||
|
if (stateCheckers.length === 1) { |
||||
|
const single = stateCheckers[0].serialize(); |
||||
|
if (isNullOrUnDef(single)) return undefined; |
||||
|
return `[${single}]`; |
||||
|
} |
||||
|
let serializedCheckers: string = ''; |
||||
|
stateCheckers.forEach((checker) => { |
||||
|
const serializedChecker = checker.serialize(); |
||||
|
if (!isNullOrUnDef(serializedChecker)) { |
||||
|
serializedCheckers += serializedChecker + ','; |
||||
|
} |
||||
|
}); |
||||
|
if (serializedCheckers.endsWith(',')) { |
||||
|
serializedCheckers = serializedCheckers.substring(0, serializedCheckers.length - 1); |
||||
|
} |
||||
|
return serializedCheckers.length > 0 ? `[${serializedCheckers}]` : undefined; |
||||
|
} |
||||
|
|
||||
|
deserialize<TState extends IHasSimpleStateCheckers<TState>>(jsonObject: any, state: TState): ISimpleStateChecker<TState> | undefined { |
||||
|
if (isNullOrUnDef(jsonObject) || !Reflect.has(jsonObject, 'T')) { |
||||
|
return undefined; |
||||
|
} |
||||
|
switch (String(jsonObject['T'])) { |
||||
|
case 'A': |
||||
|
return useRequireAuthenticatedSimpleStateChecker(); |
||||
|
case 'P': |
||||
|
const permissions = jsonObject['N'] as string[]; |
||||
|
if (permissions === undefined) { |
||||
|
throw Error(("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject)); |
||||
|
} |
||||
|
return useRequirePermissionsSimpleStateChecker({ |
||||
|
permissions: permissions, |
||||
|
requiresAll: jsonObject['A'] === true, |
||||
|
state, |
||||
|
}); |
||||
|
case 'F': |
||||
|
const features = jsonObject['N'] as string[]; |
||||
|
if (features === undefined) { |
||||
|
throw Error(("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject)); |
||||
|
} |
||||
|
return useRequireFeaturesSimpleStateChecker( |
||||
|
features, |
||||
|
jsonObject['A'] === true, |
||||
|
); |
||||
|
case 'G': |
||||
|
const globalFeatures = jsonObject['N'] as string[]; |
||||
|
if (globalFeatures === undefined) { |
||||
|
throw Error(("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject)); |
||||
|
} |
||||
|
return useRequireGlobalFeaturesSimpleStateChecker( |
||||
|
globalFeatures, |
||||
|
jsonObject['A'] === true, |
||||
|
); |
||||
|
default: return undefined; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
deserializeArray<TState extends IHasSimpleStateCheckers<TState>>(value: string, state: TState): ISimpleStateChecker<TState>[] { |
||||
|
if (isNullOrWhiteSpace(value)) return []; |
||||
|
const jsonObject = JSON.parse(value); |
||||
|
if (isNullOrUnDef(jsonObject)) return []; |
||||
|
if (Array.isArray(jsonObject)) { |
||||
|
if (jsonObject.length === 0) return []; |
||||
|
return jsonObject.map((json) => this.deserialize(json, state)) |
||||
|
.filter(checker => !isNullOrUnDef(checker)) |
||||
|
.map(checker => checker!); |
||||
|
} |
||||
|
const stateChecker = this.deserialize(jsonObject, state); |
||||
|
if (!stateChecker) return []; |
||||
|
return [stateChecker]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useSimpleStateCheck<TState extends IHasSimpleStateCheckers<TState>>() { |
||||
|
return { |
||||
|
serializer: new SimpleStateCheckerSerializer(), |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,199 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
@register="registerModal" |
||||
|
:title="L('GroupDefinitions')" |
||||
|
:can-fullscreen="false" |
||||
|
:width="800" |
||||
|
:height="500" |
||||
|
:close-func="handleBeforeClose" |
||||
|
@ok="handleSubmit" |
||||
|
> |
||||
|
<Form |
||||
|
ref="formRef" |
||||
|
:model="state.entity" |
||||
|
:rules="state.entityRules" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
> |
||||
|
<Tabs v-model:active-key="state.activeTab"> |
||||
|
<TabPane key="basic" :tab="L('BasicInfo')"> |
||||
|
<FormItem name="name" :label="L('DisplayName:Name')"> |
||||
|
<Input :disabled="state.entityEditFlag && !state.allowedChange" :allow-clear="true" v-model:value="state.entity.name" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="displayName" :label="L('DisplayName:DisplayName')"> |
||||
|
<LocalizableInput :disabled="!state.allowedChange" :allow-clear="true" v-model:value="state.entity.displayName" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
<TabPane key="propertites" :tab="L('Properties')"> |
||||
|
<FormItem name="extraProperties" label="" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }"> |
||||
|
<ExtraPropertyDictionary :disabled="!state.allowedChange" :allow-delete="true" :allow-edit="true" v-model:value="state.entity.extraProperties" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
</Tabs> |
||||
|
</Form> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { Rule } from 'ant-design-vue/lib/form'; |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { ref, reactive, unref, nextTick, watch } from 'vue'; |
||||
|
import { Form, Input, Tabs } from 'ant-design-vue'; |
||||
|
import { ExtraPropertyDictionary } from '/@/components/Abp'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { LocalizableInput } from '/@/components/Abp'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { ValidationEnum, useValidation } from '/@/hooks/abp/useValidation'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { |
||||
|
GetAsyncByName, |
||||
|
CreateAsyncByInput, |
||||
|
UpdateAsyncByNameAndInput |
||||
|
} from '/@/api/permission-management/definitions/groups'; |
||||
|
import { |
||||
|
PermissionGroupDefinitionUpdateDto, |
||||
|
PermissionGroupDefinitionCreateDto |
||||
|
} from '/@/api/permission-management/definitions/groups/model'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const TabPane = Tabs.TabPane; |
||||
|
interface State { |
||||
|
activeTab: string, |
||||
|
allowedChange: boolean, |
||||
|
entity: Recordable, |
||||
|
entityRules?: Dictionary<string, Rule>, |
||||
|
entityChanged: boolean, |
||||
|
entityEditFlag: boolean, |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['register', 'change']); |
||||
|
|
||||
|
const { ruleCreator } = useValidation(); |
||||
|
const { validate } = useLocalizationSerializer(); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const { L } = useLocalization(['AbpPermissionManagement', 'AbpUi']); |
||||
|
|
||||
|
const formRef = ref<any>(); |
||||
|
const state = reactive<State>({ |
||||
|
activeTab: 'basic', |
||||
|
entity: {}, |
||||
|
allowedChange: false, |
||||
|
entityChanged: false, |
||||
|
entityEditFlag: false, |
||||
|
entityRules: { |
||||
|
name: ruleCreator.fieldRequired({ |
||||
|
name: 'Name', |
||||
|
prefix: 'DisplayName', |
||||
|
resourceName: 'AbpPermissionManagement', |
||||
|
trigger: 'blur', |
||||
|
}), |
||||
|
displayName: ruleCreator.defineValidator({ |
||||
|
required: true, |
||||
|
trigger: 'blur', |
||||
|
validator(_rule, value) { |
||||
|
if (!validate(value)) { |
||||
|
return Promise.reject(L(ValidationEnum.FieldRequired, [L('DisplayName:DisplayName')])); |
||||
|
} |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
}), |
||||
|
description: ruleCreator.defineValidator({ |
||||
|
trigger: 'blur', |
||||
|
validator(_rule, value) { |
||||
|
if (!validate(value, { required: false })) { |
||||
|
return Promise.reject(L(ValidationEnum.FieldRequired, [L('DisplayName:Description')])); |
||||
|
} |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
}), |
||||
|
}, |
||||
|
}); |
||||
|
watch( |
||||
|
() => state.entity, |
||||
|
() => { |
||||
|
state.entityChanged = true; |
||||
|
}, |
||||
|
{ |
||||
|
deep: true, |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner((record) => { |
||||
|
nextTick(() => fetch(record.name)); |
||||
|
}); |
||||
|
|
||||
|
function fetch(name?: string) { |
||||
|
state.activeTab = 'basic'; |
||||
|
state.entityEditFlag = false; |
||||
|
if (!name) { |
||||
|
state.entity = {}; |
||||
|
state.allowedChange = true; |
||||
|
nextTick(() => state.entityChanged = false); |
||||
|
return; |
||||
|
} |
||||
|
changeLoading(true); |
||||
|
changeOkLoading(true); |
||||
|
GetAsyncByName(name).then((record) => { |
||||
|
state.entity = record; |
||||
|
state.entityEditFlag = true; |
||||
|
state.allowedChange = !record.isStatic; |
||||
|
}).finally(() => { |
||||
|
changeLoading(false); |
||||
|
changeOkLoading(false); |
||||
|
nextTick(() => state.entityChanged = false); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleBeforeClose(): Promise<boolean> { |
||||
|
return new Promise((resolve) => { |
||||
|
if (!state.entityChanged) { |
||||
|
const form = unref(formRef); |
||||
|
form?.resetFields(); |
||||
|
return resolve(true); |
||||
|
} |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('AreYouSureYouWantToCancelEditingWarningMessage'), |
||||
|
onOk: () => { |
||||
|
const form = unref(formRef); |
||||
|
form?.resetFields(); |
||||
|
resolve(true); |
||||
|
}, |
||||
|
onCancel: () => { |
||||
|
resolve(false); |
||||
|
}, |
||||
|
afterClose: () => { |
||||
|
state.allowedChange = false; |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
if (!state.allowedChange) { |
||||
|
closeModal(); |
||||
|
return; |
||||
|
} |
||||
|
const form = unref(formRef); |
||||
|
form?.validate().then(() => { |
||||
|
changeOkLoading(true); |
||||
|
const api = state.entityEditFlag |
||||
|
? UpdateAsyncByNameAndInput(state.entity.name, cloneDeep(state.entity) as PermissionGroupDefinitionUpdateDto) |
||||
|
: CreateAsyncByInput(cloneDeep(state.entity) as PermissionGroupDefinitionCreateDto); |
||||
|
api.then((res) => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
emits('change', res); |
||||
|
form.resetFields(); |
||||
|
closeModal(); |
||||
|
}).finally(() => { |
||||
|
changeOkLoading(false); |
||||
|
}) |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,159 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<BasicTable @register="registerTable"> |
||||
|
<template #toolbar> |
||||
|
<Button |
||||
|
v-auth="['PermissionManagement.GroupDefinitions.Create']" |
||||
|
type="primary" |
||||
|
@click="handleAddNew" |
||||
|
> |
||||
|
{{ L('GroupDefinitions:AddNew') }} |
||||
|
</Button> |
||||
|
</template> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'displayName'"> |
||||
|
<span>{{ getDisplayName(record.displayName) }}</span> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'action'"> |
||||
|
<TableAction |
||||
|
:stop-button-propagation="true" |
||||
|
:actions="[ |
||||
|
{ |
||||
|
auth: 'PermissionManagement.GroupDefinitions.Update', |
||||
|
label: L('Edit'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleEdit.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
auth: 'PermissionManagement.GroupDefinitions.Delete', |
||||
|
label: L('Delete'), |
||||
|
color: 'error', |
||||
|
icon: 'ant-design:delete-outlined', |
||||
|
ifShow: !record.isStatic, |
||||
|
onClick: handleDelete.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
:dropDownActions="[ |
||||
|
{ |
||||
|
auth: 'PermissionManagement.Definitions.Create', |
||||
|
label: L('PermissionDefinitions:AddNew'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleAddFeature.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
/> |
||||
|
</template> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
<GroupDefinitionModal @register="registerModal" @change="fetch" /> |
||||
|
<PermissionDefinitionModal @register="registerPermissionModal" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, reactive, onMounted } from 'vue'; |
||||
|
import { Button } from 'ant-design-vue'; |
||||
|
import { BasicTable, TableAction, useTable } from '/@/components/Table'; |
||||
|
import { getDataColumns } from '../datas/TableData'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { PermissionGroupDefinitionDto } from '/@/api/permission-management/definitions/groups/model'; |
||||
|
import { GetListAsyncByInput, DeleteAsyncByName } from '/@/api/permission-management/definitions/groups'; |
||||
|
import { getSearchFormSchemas } from '../datas/ModalData'; |
||||
|
import GroupDefinitionModal from './GroupDefinitionModal.vue'; |
||||
|
import PermissionDefinitionModal from '../../permissions/components/PermissionDefinitionModal.vue'; |
||||
|
|
||||
|
interface State { |
||||
|
groups: PermissionGroupDefinitionDto[]; |
||||
|
} |
||||
|
|
||||
|
const state = reactive<State>({ |
||||
|
groups: [], |
||||
|
}); |
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const { L, Lr } = useLocalization(['AbpPermissionManagement', 'AbpUi']); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const [registerModal, { openModal }] = useModal(); |
||||
|
const [registerPermissionModal, { openModal: openPermissionModal }] = useModal(); |
||||
|
const [registerTable, { setLoading, getForm, setTableData }] = useTable({ |
||||
|
rowKey: 'name', |
||||
|
title: L('GroupDefinitions'), |
||||
|
columns: getDataColumns(), |
||||
|
pagination: true, |
||||
|
striped: false, |
||||
|
useSearchForm: true, |
||||
|
showIndexColumn: false, |
||||
|
showTableSetting: true, |
||||
|
tableSetting: { |
||||
|
redo: false, |
||||
|
}, |
||||
|
formConfig: { |
||||
|
labelWidth: 100, |
||||
|
submitFunc: fetch, |
||||
|
schemas: getSearchFormSchemas(), |
||||
|
}, |
||||
|
bordered: true, |
||||
|
canResize: true, |
||||
|
immediate: false, |
||||
|
actionColumn: { |
||||
|
width: 120, |
||||
|
title: L('Actions'), |
||||
|
dataIndex: 'action', |
||||
|
}, |
||||
|
}); |
||||
|
const getDisplayName = computed(() => { |
||||
|
return (displayName?: string) => { |
||||
|
if (!displayName) return displayName; |
||||
|
const info = deserialize(displayName); |
||||
|
return Lr(info.resourceName, info.name); |
||||
|
}; |
||||
|
}); |
||||
|
onMounted(fetch); |
||||
|
|
||||
|
function fetch() { |
||||
|
const form = getForm(); |
||||
|
return form.validate().then(() => { |
||||
|
setLoading(true); |
||||
|
state.groups = []; |
||||
|
var input = form.getFieldsValue(); |
||||
|
GetListAsyncByInput(input).then((res) => { |
||||
|
state.groups = res.items; |
||||
|
}).finally(() => { |
||||
|
setTableData(state.groups); |
||||
|
setLoading(false); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
openModal(true, {}); |
||||
|
} |
||||
|
|
||||
|
function handleEdit(record) { |
||||
|
openModal(true, record); |
||||
|
} |
||||
|
|
||||
|
function handleAddFeature(record) { |
||||
|
openPermissionModal(true, { |
||||
|
groupName: record.name, |
||||
|
groups: cloneDeep(state.groups), |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('ItemWillBeDeleteOrRestoreMessage'), |
||||
|
onOk: () => { |
||||
|
return DeleteAsyncByName(record.name).then(() => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
fetch(); |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,15 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { FormSchema } from '/@/components/Form'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpUi']); |
||||
|
|
||||
|
export function getSearchFormSchemas():FormSchema[] { |
||||
|
return [ |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 24 }, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { BasicColumn } from '/@/components/Table'; |
||||
|
import { sorter } from '/@/utils/table'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpPermissionManagement']); |
||||
|
|
||||
|
export function getDataColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
title: L('DisplayName:Name'), |
||||
|
dataIndex: 'name', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'name'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:DisplayName'), |
||||
|
dataIndex: 'displayName', |
||||
|
align: 'left', |
||||
|
width: 350, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'displayName'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:IsStatic'), |
||||
|
dataIndex: 'isStatic', |
||||
|
align: 'center', |
||||
|
width: 150, |
||||
|
resizable: true, |
||||
|
defaultHidden: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'isStatic'), |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<GroupDefinitionTable /> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
|
||||
|
import GroupDefinitionTable from './components/GroupDefinitionTable.vue'; |
||||
|
export default defineComponent({ |
||||
|
name: 'PermissionsGroupDefinitions', |
||||
|
components: { |
||||
|
GroupDefinitionTable, |
||||
|
}, |
||||
|
setup() {}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,346 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
@register="registerModal" |
||||
|
:title="L('PermissionDefinitions')" |
||||
|
:can-fullscreen="false" |
||||
|
:width="800" |
||||
|
:height="500" |
||||
|
:close-func="handleBeforeClose" |
||||
|
@ok="handleSubmit" |
||||
|
> |
||||
|
<Form |
||||
|
ref="formRef" |
||||
|
:model="state.entity" |
||||
|
:rules="state.entityRules" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
> |
||||
|
<Tabs v-model:active-key="state.activeTab"> |
||||
|
<TabPane key="basic" :tab="L('BasicInfo')"> |
||||
|
<FormItem name="isEnabled" :label="L('DisplayName:IsEnabled')"> |
||||
|
<Checkbox |
||||
|
:disabled="!state.allowedChange" |
||||
|
v-model:checked="state.entity.isEnabled" |
||||
|
>{{ L('DisplayName:IsEnabled') }} |
||||
|
</Checkbox> |
||||
|
</FormItem> |
||||
|
<FormItem name="groupName" :label="L('DisplayName:GroupName')"> |
||||
|
<Select |
||||
|
:disabled="!state.allowedChange" |
||||
|
:allow-clear="true" |
||||
|
v-model:value="state.entity.groupName" |
||||
|
:options="getGroupOptions" |
||||
|
@change="handleGroupChange" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
<FormItem name="parentName" :label="L('DisplayName:ParentName')"> |
||||
|
<TreeSelect |
||||
|
:disabled="!state.allowedChange" |
||||
|
:allow-clear="true" |
||||
|
:tree-data="state.availablePermissions" |
||||
|
v-model:value="state.entity.parentName" |
||||
|
:field-names="{ |
||||
|
label: 'displayName', |
||||
|
value: 'name', |
||||
|
children: 'children', |
||||
|
}" |
||||
|
@change="handleParentChange" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
<FormItem name="name" :label="L('DisplayName:Name')"> |
||||
|
<Input :disabled="state.entityEditFlag && !state.allowedChange" :allow-clear="true" v-model:value="state.entity.name" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="displayName" :label="L('DisplayName:DisplayName')"> |
||||
|
<LocalizableInput :disabled="!state.allowedChange" :allow-clear="true" v-model:value="state.entity.displayName" /> |
||||
|
</FormItem> |
||||
|
<FormItem name="multiTenancySide" :label="L('DisplayName:MultiTenancySide')"> |
||||
|
<Select |
||||
|
:disabled="!state.allowedChange" |
||||
|
v-model:value="state.entity.multiTenancySide" |
||||
|
:options="multiTenancySides" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
<FormItem name="providers" :label="L('DisplayName:Providers')"> |
||||
|
<Select |
||||
|
:disabled="!state.allowedChange" |
||||
|
mode="multiple" |
||||
|
:allow-clear="true" |
||||
|
v-model:value="state.entity.providers" |
||||
|
:options="providers" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
<TabPane key="stateCheckers" :tab="L('StateCheckers')" force-render> |
||||
|
<FormItem name="stateCheckers" label="" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }"> |
||||
|
<SimpleStateChecking |
||||
|
:allow-delete="true" |
||||
|
:allow-edit="true" |
||||
|
:disabled="!state.allowedChange" |
||||
|
v-model:value="state.entity.stateCheckers" |
||||
|
:state="state.simpleCheckState" |
||||
|
/> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
<TabPane key="propertites" :tab="L('Properties')" force-render> |
||||
|
<FormItem name="extraProperties" label="" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }"> |
||||
|
<ExtraPropertyDictionary :disabled="!state.allowedChange" :allow-delete="true" :allow-edit="true" v-model:value="state.entity.extraProperties" /> |
||||
|
</FormItem> |
||||
|
</TabPane> |
||||
|
</Tabs> |
||||
|
</Form> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import type { Rule } from 'ant-design-vue/lib/form'; |
||||
|
import { SimplaCheckStateBase, PermissionState } from '/@/components/Abp'; |
||||
|
import { computed, ref, reactive, unref, nextTick, watch } from 'vue'; |
||||
|
import { Checkbox, Form, Input, Select, Tabs, TreeSelect } from 'ant-design-vue'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { LocalizableInput, ExtraPropertyDictionary, SimpleStateChecking } from '/@/components/Abp'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { ValidationEnum, useValidation } from '/@/hooks/abp/useValidation'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { |
||||
|
GetAsyncByName, |
||||
|
CreateAsyncByInput, |
||||
|
UpdateAsyncByNameAndInput, |
||||
|
GetListAsyncByInput, |
||||
|
} from '/@/api/permission-management/definitions/permissions'; |
||||
|
import { |
||||
|
MultiTenancySides, |
||||
|
PermissionDefinitionUpdateDto, |
||||
|
PermissionDefinitionCreateDto, |
||||
|
} from '/@/api/permission-management/definitions/permissions/model'; |
||||
|
import { PermissionGroupDefinitionDto } from '/@/api/permission-management/definitions/groups/model'; |
||||
|
import { multiTenancySides, providers } from '../../typing'; |
||||
|
import { listToTree } from '/@/utils/helper/treeHelper'; |
||||
|
import { groupBy } from '/@/utils/array'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const TabPane = Tabs.TabPane; |
||||
|
interface PermissionSelectData { |
||||
|
label: string; |
||||
|
value: string; |
||||
|
} |
||||
|
interface PermissionTreeData { |
||||
|
name: string; |
||||
|
groupName: string; |
||||
|
displayName: string; |
||||
|
children: PermissionTreeData[]; |
||||
|
} |
||||
|
interface State { |
||||
|
activeTab: string, |
||||
|
allowedChange: boolean, |
||||
|
entity: Recordable, |
||||
|
entityRules?: Dictionary<string, Rule>, |
||||
|
entityChanged: boolean, |
||||
|
entityEditFlag: boolean, |
||||
|
defaultGroup?: string, |
||||
|
availablePermissions: PermissionTreeData[], |
||||
|
availableGroups: PermissionGroupDefinitionDto[], |
||||
|
selectionDataSource: PermissionSelectData[], |
||||
|
simpleCheckState: SimplaCheckStateBase, |
||||
|
} |
||||
|
|
||||
|
const emits = defineEmits(['register', 'change']); |
||||
|
|
||||
|
const { ruleCreator } = useValidation(); |
||||
|
const { deserialize, validate } = useLocalizationSerializer(); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const { L, Lr } = useLocalization(['AbpPermissionManagement', 'AbpUi']); |
||||
|
|
||||
|
const formRef = ref<any>(); |
||||
|
const state = reactive<State>({ |
||||
|
activeTab: 'basic', |
||||
|
entity: {}, |
||||
|
allowedChange: false, |
||||
|
entityChanged: false, |
||||
|
entityEditFlag: false, |
||||
|
availablePermissions: [], |
||||
|
availableGroups: [], |
||||
|
selectionDataSource: [], |
||||
|
simpleCheckState: new PermissionState(), |
||||
|
entityRules: { |
||||
|
groupName: ruleCreator.fieldRequired({ |
||||
|
name: 'GroupName', |
||||
|
prefix: 'DisplayName', |
||||
|
resourceName: 'AbpPermissionManagement', |
||||
|
trigger: 'blur', |
||||
|
}), |
||||
|
name: ruleCreator.fieldRequired({ |
||||
|
name: 'Name', |
||||
|
prefix: 'DisplayName', |
||||
|
resourceName: 'AbpPermissionManagement', |
||||
|
trigger: 'blur', |
||||
|
}), |
||||
|
displayName: ruleCreator.defineValidator({ |
||||
|
required: true, |
||||
|
trigger: 'blur', |
||||
|
validator(_rule, value) { |
||||
|
if (!validate(value)) { |
||||
|
return Promise.reject(L(ValidationEnum.FieldRequired, [L('DisplayName:DisplayName')])); |
||||
|
} |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
}), |
||||
|
}, |
||||
|
}); |
||||
|
const getGroupOptions = computed(() => { |
||||
|
return state.availableGroups.map((group) => { |
||||
|
const info = deserialize(group.displayName); |
||||
|
return { |
||||
|
label: Lr(info.resourceName, info.name), |
||||
|
value: group.name, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
watch( |
||||
|
() => state.entity, |
||||
|
() => { |
||||
|
state.entityChanged = true; |
||||
|
}, |
||||
|
{ |
||||
|
deep: true, |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner((data) => { |
||||
|
state.defaultGroup = data.groupName; |
||||
|
state.availableGroups = data.groups; |
||||
|
nextTick(() => { |
||||
|
fetchFeatures(state.defaultGroup); |
||||
|
fetch(data.record?.name); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function fetch(name?: string) { |
||||
|
state.activeTab = 'basic'; |
||||
|
state.entityEditFlag = false; |
||||
|
if (!name) { |
||||
|
state.entity = { |
||||
|
isEnabled: true, |
||||
|
groupName: state.defaultGroup, |
||||
|
multiTenancySide: MultiTenancySides.Both, |
||||
|
providers: [], |
||||
|
extraProperties: {}, |
||||
|
}; |
||||
|
state.allowedChange = true; |
||||
|
nextTick(() => state.entityChanged = false); |
||||
|
return; |
||||
|
} |
||||
|
changeLoading(true); |
||||
|
changeOkLoading(true); |
||||
|
GetAsyncByName(name).then((record) => { |
||||
|
state.entity = record; |
||||
|
state.entityEditFlag = true; |
||||
|
state.allowedChange = !record.isStatic; |
||||
|
}).finally(() => { |
||||
|
changeLoading(false); |
||||
|
changeOkLoading(false); |
||||
|
nextTick(() => { |
||||
|
state.entityChanged = false; |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function fetchFeatures(groupName?: string) { |
||||
|
GetListAsyncByInput({ |
||||
|
groupName: groupName, |
||||
|
}).then((res) => { |
||||
|
const permissionGroup = groupBy(res.items, 'groupName'); |
||||
|
const permissionTreeData: PermissionTreeData[] = []; |
||||
|
Object.keys(permissionGroup).forEach((gk) => { |
||||
|
const permissionTree = listToTree(permissionGroup[gk], { |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}); |
||||
|
formatDisplayName(permissionTree); |
||||
|
permissionTreeData.push(...permissionTree); |
||||
|
}); |
||||
|
state.availablePermissions = permissionTreeData; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function formatDisplayName(list: any[]) { |
||||
|
if (list && Array.isArray(list)) { |
||||
|
list.forEach((item) => { |
||||
|
if (Reflect.has(item, 'displayName')) { |
||||
|
const info = deserialize(item.displayName); |
||||
|
item.displayName = Lr(info.resourceName, info.name); |
||||
|
} |
||||
|
if (Reflect.has(item, 'children')) { |
||||
|
formatDisplayName(item.children); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleGroupChange(value?: string) { |
||||
|
state.entity.parentName = undefined; |
||||
|
fetchFeatures(value); |
||||
|
} |
||||
|
|
||||
|
function handleParentChange(value?: string) { |
||||
|
if (!value) return; |
||||
|
GetAsyncByName(value).then((res) => { |
||||
|
state.entity.groupName = res.groupName; |
||||
|
const form = unref(formRef); |
||||
|
form?.clearValidate(['groupName']); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleBeforeClose(): Promise<boolean> { |
||||
|
return new Promise((resolve) => { |
||||
|
if (!state.entityChanged) { |
||||
|
const form = unref(formRef); |
||||
|
form?.resetFields(); |
||||
|
return resolve(true); |
||||
|
} |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('AreYouSureYouWantToCancelEditingWarningMessage'), |
||||
|
onOk: () => { |
||||
|
const form = unref(formRef); |
||||
|
form?.resetFields(); |
||||
|
state.allowedChange = false; |
||||
|
resolve(true); |
||||
|
}, |
||||
|
onCancel: () => { |
||||
|
resolve(false); |
||||
|
}, |
||||
|
afterClose: () => { |
||||
|
state.defaultGroup = undefined; |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
if (!state.allowedChange) { |
||||
|
closeModal(); |
||||
|
return; |
||||
|
} |
||||
|
const form = unref(formRef); |
||||
|
form?.validate().then(() => { |
||||
|
changeOkLoading(true); |
||||
|
const api = state.entityEditFlag |
||||
|
? UpdateAsyncByNameAndInput(state.entity.name, state.entity as PermissionDefinitionUpdateDto) |
||||
|
: CreateAsyncByInput(state.entity as PermissionDefinitionCreateDto); |
||||
|
api.then((res) => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
emits('change', res); |
||||
|
form.resetFields(); |
||||
|
closeModal(); |
||||
|
}).finally(() => { |
||||
|
changeOkLoading(false); |
||||
|
}) |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,262 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<BasicTable @register="registerTable"> |
||||
|
<template #form-groupName="{ model, field }"> |
||||
|
<FormItem name="groupName"> |
||||
|
<Select v-model:value="model[field]" :options="getGroupOptions" /> |
||||
|
</FormItem> |
||||
|
</template> |
||||
|
<template #toolbar> |
||||
|
<Button |
||||
|
v-auth="['PermissionManagement.Definitions.Create']" |
||||
|
type="primary" |
||||
|
@click="handleAddNew" |
||||
|
> |
||||
|
{{ L('PermissionDefinitions:AddNew') }} |
||||
|
</Button> |
||||
|
</template> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'displayName'"> |
||||
|
<span>{{ getGroupDisplayName(record.displayName) }}</span> |
||||
|
</template> |
||||
|
</template> |
||||
|
<template #expandedRowRender="{ record }"> |
||||
|
<BasicTable @register="registerSubTable" :data-source="record.permissions"> |
||||
|
<template #bodyCell="{ column, record }"> |
||||
|
<template v-if="column.key === 'groupName'"> |
||||
|
<span>{{ getGroupDisplayName(record.groupName) }}</span> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'displayName'"> |
||||
|
<span>{{ getDisplayName(record.displayName) }}</span> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'multiTenancySide'"> |
||||
|
<Tag color="blue">{{ multiTenancySidesMap[record.multiTenancySide] }}</Tag> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'providers'"> |
||||
|
<Tag v-for="provider in record.providers" color="blue" style="margin: 5px;">{{ providersMap[provider] }}</Tag> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'isEnabled'"> |
||||
|
<CheckOutlined v-if="record.isEnabled" class="enable" /> |
||||
|
<CloseOutlined v-else class="disable" /> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'isStatic'"> |
||||
|
<CheckOutlined v-if="record.isStatic" class="enable" /> |
||||
|
<CloseOutlined v-else class="disable" /> |
||||
|
</template> |
||||
|
<template v-else-if="column.key === 'action'"> |
||||
|
<TableAction |
||||
|
:stop-button-propagation="true" |
||||
|
:actions="[ |
||||
|
{ |
||||
|
auth: 'PermissionManagement.Definitions.Update', |
||||
|
label: L('Edit'), |
||||
|
icon: 'ant-design:edit-outlined', |
||||
|
onClick: handleEdit.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
auth: 'PermissionManagement.Definitions.Delete', |
||||
|
label: L('Delete'), |
||||
|
color: 'error', |
||||
|
icon: 'ant-design:delete-outlined', |
||||
|
ifShow: !record.isStatic, |
||||
|
onClick: handleDelete.bind(null, record), |
||||
|
}, |
||||
|
]" |
||||
|
/> |
||||
|
</template> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
</template> |
||||
|
</BasicTable> |
||||
|
<PermissionDefinitionModal @register="registerModal" @change="fetch" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { cloneDeep } from 'lodash-es'; |
||||
|
import { computed, reactive, onMounted } from 'vue'; |
||||
|
import { Button, Form, Select, Tag } from 'ant-design-vue'; |
||||
|
import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue'; |
||||
|
import { BasicTable, TableAction, useTable } from '/@/components/Table'; |
||||
|
import { getDataColumns } from '../datas/TableData'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useLocalizationSerializer } from '/@/hooks/abp/useLocalizationSerializer'; |
||||
|
import { GetListAsyncByInput as getGroupDefinitions } from '/@/api/permission-management/definitions/groups'; |
||||
|
import { PermissionGroupDefinitionDto } from '/@/api/permission-management/definitions/groups/model'; |
||||
|
import { GetListAsyncByInput, DeleteAsyncByName } from '/@/api/permission-management/definitions/permissions'; |
||||
|
import { multiTenancySidesMap, providersMap } from '../../typing'; |
||||
|
import { getSearchFormSchemas } from '../datas/ModalData'; |
||||
|
import { listToTree } from '/@/utils/helper/treeHelper'; |
||||
|
import { groupBy } from '/@/utils/array'; |
||||
|
import { sorter } from '/@/utils/table'; |
||||
|
import PermissionDefinitionModal from './PermissionDefinitionModal.vue'; |
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
interface Permission { |
||||
|
groupName: string; |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
parentName?: string; |
||||
|
isEnabled: boolean; |
||||
|
isStatic: boolean; |
||||
|
providers: string[]; |
||||
|
stateCheckers: string[]; |
||||
|
children: Permission[]; |
||||
|
} |
||||
|
interface PermissionGroup { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
permissions: Permission[]; |
||||
|
} |
||||
|
interface State { |
||||
|
groups: PermissionGroupDefinitionDto[]; |
||||
|
} |
||||
|
|
||||
|
const { deserialize } = useLocalizationSerializer(); |
||||
|
const { L, Lr } = useLocalization(['AbpPermissionManagement', 'AbpUi']); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const [registerModal, { openModal }] = useModal(); |
||||
|
const [registerTable, { setLoading, getForm, setTableData }] = useTable({ |
||||
|
rowKey: 'name', |
||||
|
title: L('PermissionDefinitions'), |
||||
|
columns: [ |
||||
|
{ |
||||
|
title: L('DisplayName:DisplayName'), |
||||
|
dataIndex: 'displayName', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'displayName'), |
||||
|
}, |
||||
|
], |
||||
|
pagination: true, |
||||
|
striped: false, |
||||
|
useSearchForm: true, |
||||
|
showIndexColumn: false, |
||||
|
showTableSetting: true, |
||||
|
tableSetting: { |
||||
|
redo: false, |
||||
|
}, |
||||
|
formConfig: { |
||||
|
labelWidth: 100, |
||||
|
submitFunc: fetch, |
||||
|
schemas: getSearchFormSchemas(), |
||||
|
}, |
||||
|
bordered: true, |
||||
|
canResize: true, |
||||
|
immediate: false, |
||||
|
}); |
||||
|
const [registerSubTable] = useTable({ |
||||
|
rowKey: 'name', |
||||
|
columns: getDataColumns(), |
||||
|
pagination: false, |
||||
|
striped: false, |
||||
|
useSearchForm: false, |
||||
|
showIndexColumn: true, |
||||
|
immediate: false, |
||||
|
scroll: { x: 2000, y: 900 }, |
||||
|
actionColumn: { |
||||
|
width: 100, |
||||
|
title: L('Actions'), |
||||
|
dataIndex: 'action', |
||||
|
}, |
||||
|
}); |
||||
|
const state = reactive<State>({ |
||||
|
groups: [], |
||||
|
}); |
||||
|
const getGroupOptions = computed(() => { |
||||
|
return state.groups.map((group) => { |
||||
|
const info = deserialize(group.displayName); |
||||
|
return { |
||||
|
label: Lr(info.resourceName, info.name), |
||||
|
value: group.name, |
||||
|
}; |
||||
|
}); |
||||
|
}); |
||||
|
const getGroupDisplayName = computed(() => { |
||||
|
return (groupName: string) => { |
||||
|
const group = state.groups.find(x => x.name === groupName); |
||||
|
if (!group) return groupName; |
||||
|
const info = deserialize(group.displayName); |
||||
|
return Lr(info.resourceName, info.name); |
||||
|
}; |
||||
|
}); |
||||
|
const getDisplayName = computed(() => { |
||||
|
return (displayName?: string) => { |
||||
|
if (!displayName) return displayName; |
||||
|
const info = deserialize(displayName); |
||||
|
return Lr(info.resourceName, info.name); |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
fetch(); |
||||
|
fetchGroups(); |
||||
|
}); |
||||
|
|
||||
|
function fetch() { |
||||
|
const form = getForm(); |
||||
|
return form.validate().then(() => { |
||||
|
setLoading(true); |
||||
|
setTableData([]); |
||||
|
var input = form.getFieldsValue(); |
||||
|
GetListAsyncByInput(input).then((res) => { |
||||
|
const permissionGroup = groupBy(res.items, 'groupName'); |
||||
|
const permissionGroupData: PermissionGroup[] = []; |
||||
|
Object.keys(permissionGroup).forEach((gk) => { |
||||
|
const groupData: PermissionGroup = { |
||||
|
name: gk, |
||||
|
displayName: gk, |
||||
|
permissions: [], |
||||
|
}; |
||||
|
const permissionTree = listToTree(permissionGroup[gk], { |
||||
|
id: 'name', |
||||
|
pid: 'parentName', |
||||
|
}); |
||||
|
permissionTree.forEach((tk) => { |
||||
|
groupData.permissions.push(tk); |
||||
|
}); |
||||
|
permissionGroupData.push(groupData); |
||||
|
}); |
||||
|
setTableData(permissionGroupData); |
||||
|
}).finally(() => { |
||||
|
setLoading(false); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function fetchGroups() { |
||||
|
getGroupDefinitions({}).then((res) => { |
||||
|
state.groups = res.items; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleAddNew() { |
||||
|
openModal(true, { |
||||
|
groups: cloneDeep(state.groups), |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleEdit(record) { |
||||
|
openModal(true, { |
||||
|
record: record, |
||||
|
groups: cloneDeep(state.groups), |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleDelete(record) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('ItemWillBeDeleteOrRestoreMessage'), |
||||
|
onOk: () => { |
||||
|
return DeleteAsyncByName(record.name).then(() => { |
||||
|
createMessage.success(L('Successful')); |
||||
|
fetch(); |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,22 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { FormSchema } from '/@/components/Form'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpPermissionManagement', 'AbpUi']); |
||||
|
|
||||
|
export function getSearchFormSchemas():FormSchema[] { |
||||
|
return [ |
||||
|
{ |
||||
|
field: 'groupName', |
||||
|
component: 'ApiSelect', |
||||
|
label: L('DisplayName:GroupName'), |
||||
|
colProps: { span: 6 }, |
||||
|
slot: 'groupName', |
||||
|
}, |
||||
|
{ |
||||
|
field: 'filter', |
||||
|
component: 'Input', |
||||
|
label: L('Search'), |
||||
|
colProps: { span: 18 }, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { BasicColumn } from '/@/components/Table'; |
||||
|
import { sorter } from '/@/utils/table'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpPermissionManagement']); |
||||
|
|
||||
|
export function getDataColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
title: L('DisplayName:IsEnabled'), |
||||
|
dataIndex: 'isEnabled', |
||||
|
align: 'center', |
||||
|
width: 150, |
||||
|
resizable: true, |
||||
|
defaultHidden: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'isEnabled'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:MultiTenancySide'), |
||||
|
dataIndex: 'multiTenancySide', |
||||
|
align: 'left', |
||||
|
width: 80, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'multiTenancySide'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Providers'), |
||||
|
dataIndex: 'providers', |
||||
|
align: 'left', |
||||
|
width: 80, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'providers'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:Name'), |
||||
|
dataIndex: 'name', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'name'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:DisplayName'), |
||||
|
dataIndex: 'displayName', |
||||
|
align: 'left', |
||||
|
width: 180, |
||||
|
resizable: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'displayName'), |
||||
|
}, |
||||
|
{ |
||||
|
title: L('DisplayName:IsStatic'), |
||||
|
dataIndex: 'isStatic', |
||||
|
align: 'center', |
||||
|
width: 150, |
||||
|
resizable: true, |
||||
|
defaultHidden: true, |
||||
|
sorter: (a, b) => sorter(a, b, 'isStatic'), |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<PermissionDefinitionTable /> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
|
||||
|
import PermissionDefinitionTable from './components/PermissionDefinitionTable.vue'; |
||||
|
export default defineComponent({ |
||||
|
name: 'PermissionsDefinitions', |
||||
|
components: { |
||||
|
PermissionDefinitionTable, |
||||
|
}, |
||||
|
setup() {}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,30 @@ |
|||||
|
import { MultiTenancySides } from '/@/api/permission-management/definitions/permissions/model'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
|
||||
|
const { L } = useLocalization(['AbpPermissionManagement']); |
||||
|
|
||||
|
export const multiTenancySidesMap = { |
||||
|
[MultiTenancySides.Tenant]: L('MultiTenancySides:Tenant'), |
||||
|
[MultiTenancySides.Host]: L('MultiTenancySides:Host'), |
||||
|
[MultiTenancySides.Both]: L('MultiTenancySides:Both'), |
||||
|
}; |
||||
|
|
||||
|
export const multiTenancySides = [ |
||||
|
{ label: multiTenancySidesMap[MultiTenancySides.Tenant], value: MultiTenancySides.Tenant }, |
||||
|
{ label: multiTenancySidesMap[MultiTenancySides.Host], value: MultiTenancySides.Host }, |
||||
|
{ label: multiTenancySidesMap[MultiTenancySides.Both], value: MultiTenancySides.Both }, |
||||
|
]; |
||||
|
|
||||
|
export const providersMap = { |
||||
|
'R': L('Providers:Role'), |
||||
|
'U': L('Providers:User'), |
||||
|
'O': L('Providers:OrganizationUnit'), |
||||
|
'C': L('Providers:Client'), |
||||
|
}; |
||||
|
|
||||
|
export const providers = [ |
||||
|
{ label: providersMap['R'], value: 'R' }, |
||||
|
{ label: providersMap['U'], value: 'U' }, |
||||
|
{ label: providersMap['O'], value: 'O' }, |
||||
|
{ label: providersMap['C'], value: 'C' }, |
||||
|
]; |
||||
@ -1,7 +1,5 @@ |
|||||
using Volo.Abp.Application.Dtos; |
namespace LINGYUN.Abp.PermissionManagement.Definitions; |
||||
|
public class PermissionGroupDefinitionGetListInput |
||||
namespace LINGYUN.Abp.PermissionManagement.Definitions; |
|
||||
public class PermissionGroupDefinitionGetListInput : PagedAndSortedResultRequestDto |
|
||||
{ |
{ |
||||
public string Filter { get; set; } |
public string Filter { get; set; } |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue