diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json index 5a2f778b9..6bec70b7c 100644 --- a/apps/vben5/apps/app-antd/package.json +++ b/apps/vben5/apps/app-antd/package.json @@ -31,6 +31,7 @@ "@abp/core": "workspace:*", "@abp/identity": "workspace:*", "@abp/openiddict": "workspace:*", + "@abp/permission": "workspace:*", "@abp/request": "workspace:*", "@abp/ui": "workspace:*", "@vben/access": "workspace:*", diff --git a/apps/vben5/apps/app-antd/src/adapter/request/index.ts b/apps/vben5/apps/app-antd/src/adapter/request/index.ts index 06284043b..cb10c655d 100644 --- a/apps/vben5/apps/app-antd/src/adapter/request/index.ts +++ b/apps/vben5/apps/app-antd/src/adapter/request/index.ts @@ -47,6 +47,7 @@ export function initRequestClient() { config.headers.Authorization = `${accessStore.accessToken}`; } config.headers['Accept-Language'] = preferences.app.locale; + config.headers['X-Request-From'] = 'vben'; return config; }, }); diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json index b042b148c..bdea2d667 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json @@ -10,6 +10,11 @@ "securityLogs": "Security Logs", "organizationUnits": "Organization Units", "auditLogs": "Audit Logs" + }, + "permissions": { + "title": "Permissions", + "groups": "Groups", + "definitions": "Definitions" } }, "openiddict": { diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/component.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/component.json new file mode 100644 index 000000000..51e47b151 --- /dev/null +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/component.json @@ -0,0 +1,120 @@ +{ + "localizable_input": { + "placeholder": "Select localized resources", + "resources": { + "fiexed": { + "group": "Define", + "label": "Fiexed", + "placeholder": "Please enter custom content" + }, + "localization": { + "group": "Localization", + "placeholder": "Please select a name" + } + } + }, + "extra_property_dictionary": { + "title": "Extra properties", + "key": "Key", + "value": "Value", + "actions": { + "title": "Actions", + "create": "Add", + "update": "Edit", + "delete": "Delete", + "clean": "Clean" + }, + "itemWillBeDeleted": "{key} will be deleted!", + "validator": { + "duplicateKey": "A key of the same name has been added" + } + }, + "value_type_nput": { + "type": { + "name": "Type", + "FREE_TEXT": { + "name": "Free Text" + }, + "TOGGLE": { + "name": "Toggle" + }, + "SELECTION": { + "name": "Selection", + "displayText": "Display Text", + "displayTextNotBeEmpty": "Display text cannot be empty", + "value": "value", + "duplicateKeyOrValue": "The name or value of the selection is not allowed to be repeated", + "itemsNotBeEmpty": "Selectable items cannot be empty", + "itemsNotFound": "The selection is not included in the optional list", + "actions": { + "title": "Actions", + "create": "Add", + "update": "Edit", + "delete": "Delete", + "clean": "Clean" + }, + "modal": { + "title": "Selection" + } + } + }, + "validator": { + "name": "Validator", + "isInvalidValue": "The value failed to pass {0}; check the validator options.", + "NULL": { + "name": "None" + }, + "BOOLEAN": { + "name": "Boolean" + }, + "NUMERIC": { + "name": "Numeric", + "minValue": "Min Value", + "maxValue": "Max Value" + }, + "STRING": { + "name": "String", + "allowNull": "Allow Null", + "minLength": "Min Length", + "maxLength": "Max Length", + "regularExpression": "Regular Expression" + } + } + }, + "simple_state_checking": { + "title": "State checking", + "actions": { + "create": "Add", + "update": "Edit", + "delete": "Delete", + "clean": "Clean" + }, + "table": { + "name": "Name", + "properties": "Properties", + "actions": "Actions" + }, + "form": { + "name": "State checking" + }, + "requireAuthenticated": { + "title": "Require Authenticated" + }, + "requireFeatures": { + "title": "Require Features", + "requiresAll": "Requires All", + "requiresAllDesc": "If checked, all selected features need to be enabled.", + "featureNames": "Required features" + }, + "requireGlobalFeatures": { + "title": "Require Global Features", + "featureNames": "Required Global Features" + }, + "requirePermissions": { + "title": "Require Permissions", + "requiresAll": "Requires All", + "requiresAllDesc": "If checked, you need to have all the selected permissions.", + "permissions": "Required Permissions" + } + } +} diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json index 8159d12af..103d0ab79 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json @@ -10,6 +10,11 @@ "securityLogs": "安全日志", "organizationUnits": "组织机构", "auditLogs": "审计日志" + }, + "permissions": { + "title": "权限管理", + "groups": "权限分组", + "definitions": "权限定义" } }, "openiddict": { diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/component.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/component.json new file mode 100644 index 000000000..b3eaaf65c --- /dev/null +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/component.json @@ -0,0 +1,120 @@ +{ + "localizable_input": { + "placeholder": "请选择本地化资源", + "resources": { + "fiexed": { + "group": "自定义", + "label": "固定内容", + "placeholder": "请输入自定义内容" + }, + "localization": { + "group": "本地化", + "placeholder": "请选择名称" + } + } + }, + "extra_property_dictionary": { + "title": "扩展属性", + "key": "名称", + "value": "键值", + "actions": { + "title": "操作", + "create": "新增", + "update": "编辑", + "delete": "删除", + "clean": "清除" + }, + "validator": { + "duplicateKey": "已经添加了一个相同名称的键" + }, + "itemWillBeDeleted": "{key} 将被删除!" + }, + "value_type_nput": { + "type": { + "name": "类型", + "FREE_TEXT": { + "name": "自由文本" + }, + "TOGGLE": { + "name": "切换" + }, + "SELECTION": { + "name": "选择", + "displayText": "显示名称", + "displayTextNotBeEmpty": "显示名称不可为空", + "value": "选择项", + "duplicateKeyOrValue": "选择项的名称或值不允许重复", + "itemsNotBeEmpty": "可选择项列表不能为空", + "itemsNotFound": "选择项不包含在可选列表中", + "actions": { + "title": "操作", + "create": "新增", + "update": "编辑", + "delete": "删除", + "clean": "清除" + }, + "modal": { + "title": "选择项" + } + } + }, + "validator": { + "name": "验证器", + "isInvalidValue": "值未能通过 {0} 校验, 请检查验证器选项.", + "NULL": { + "name": "未定义" + }, + "BOOLEAN": { + "name": "布尔类型" + }, + "NUMERIC": { + "name": "数值类型", + "minValue": "最小值", + "maxValue": "最大值" + }, + "STRING": { + "name": "字符类型", + "allowNull": "允许空值", + "minLength": "最小长度", + "maxLength": "最大长度", + "regularExpression": "正则表达式" + } + } + }, + "simple_state_checking": { + "title": "状态检查", + "actions": { + "create": "新增", + "update": "编辑", + "delete": "删除", + "clean": "清除" + }, + "table": { + "name": "名称", + "properties": "属性", + "actions": "操作" + }, + "form": { + "name": "状态检查器" + }, + "requireAuthenticated": { + "title": "需要用户认证" + }, + "requireFeatures": { + "title": "检查所需功能", + "requiresAll": "要求所有", + "requiresAllDesc": "如果勾选,则需要启用所有选择的功能.", + "featureNames": "需要的功能" + }, + "requireGlobalFeatures": { + "title": "检查全局功能", + "featureNames": "需要的全局功能" + }, + "requirePermissions": { + "title": "检查所需权限", + "requiresAll": "要求所有", + "requiresAllDesc": "如果勾选,则需要拥有所有选择的权限.", + "permissions": "需要的权限" + } + } +} diff --git a/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts b/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts index 680cfeeb4..7d71a20c2 100644 --- a/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts +++ b/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts @@ -81,6 +81,35 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + meta: { + title: $t('abp.manage.permissions.title'), + icon: 'arcticons:permissionsmanager', + }, + name: 'PermissionManagement', + path: '/manage/permissions', + children: [ + { + meta: { + title: $t('abp.manage.permissions.groups'), + icon: 'lucide:group', + }, + name: 'PermissionGroupDefinitions', + path: '/manage/permissions/groups', + component: () => import('#/views/permissions/groups/index.vue'), + }, + { + meta: { + title: $t('abp.manage.permissions.definitions'), + icon: 'icon-park-outline:permissions', + }, + name: 'PermissionDefinitions', + path: '/manage/permissions/definitions', + component: () => + import('#/views/permissions/definitions/index.vue'), + }, + ], + }, { meta: { title: $t('abp.manage.identity.auditLogs'), diff --git a/apps/vben5/apps/app-antd/src/views/permissions/definitions/index.vue b/apps/vben5/apps/app-antd/src/views/permissions/definitions/index.vue new file mode 100644 index 000000000..f8b349598 --- /dev/null +++ b/apps/vben5/apps/app-antd/src/views/permissions/definitions/index.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/apps/vben5/apps/app-antd/src/views/permissions/groups/index.vue b/apps/vben5/apps/app-antd/src/views/permissions/groups/index.vue new file mode 100644 index 000000000..0ec016692 --- /dev/null +++ b/apps/vben5/apps/app-antd/src/views/permissions/groups/index.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireAuthenticatedSimpleStateChecker.ts b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireAuthenticatedSimpleStateChecker.ts new file mode 100644 index 000000000..83c370e56 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireAuthenticatedSimpleStateChecker.ts @@ -0,0 +1,42 @@ +import type { + CurrentUser, + IHasSimpleStateCheckers, + ISimpleStateChecker, + SimpleStateCheckerContext, +} from '../../types/global'; + +import { useAbpStore } from '../../store/abp'; + +export interface RequireAuthenticatedStateChecker { + name: string; +} + +export class RequireAuthenticatedSimpleStateChecker< + TState extends IHasSimpleStateCheckers, + > + implements RequireAuthenticatedStateChecker, ISimpleStateChecker +{ + _currentUser?: CurrentUser; + name = 'A'; + constructor(currentUser?: CurrentUser) { + this._currentUser = currentUser; + } + isEnabled(_context: SimpleStateCheckerContext): boolean { + return this._currentUser?.isAuthenticated ?? false; + } + + serialize(): string { + return JSON.stringify({ + T: this.name, + }); + } +} + +export function useRequireAuthenticatedSimpleStateChecker< + TState extends IHasSimpleStateCheckers, +>(): ISimpleStateChecker { + const abpStore = useAbpStore(); + return new RequireAuthenticatedSimpleStateChecker( + abpStore.application?.currentUser, + ); +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireFeaturesSimpleStateChecker.ts b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireFeaturesSimpleStateChecker.ts new file mode 100644 index 000000000..4ec027687 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireFeaturesSimpleStateChecker.ts @@ -0,0 +1,59 @@ +import type { IFeatureChecker } from '../../types/features'; +import type { + IHasSimpleStateCheckers, + ISimpleStateChecker, + SimpleStateCheckerContext, +} from '../../types/global'; + +import { useFeatures } from '../useFeatures'; + +export interface RequireFeaturesStateChecker { + featureNames: string[]; + name: string; + requiresAll: boolean; +} + +export class RequireFeaturesSimpleStateChecker< + TState extends IHasSimpleStateCheckers, + > + implements RequireFeaturesStateChecker, ISimpleStateChecker +{ + _featureChecker: IFeatureChecker; + featureNames: string[]; + name: string = 'F'; + requiresAll: boolean; + constructor( + featureChecker: IFeatureChecker, + featureNames: string[], + requiresAll: boolean = false, + ) { + this._featureChecker = featureChecker; + this.featureNames = featureNames; + this.requiresAll = requiresAll; + } + isEnabled(_context: SimpleStateCheckerContext): boolean { + return this._featureChecker.isEnabled(this.featureNames, this.requiresAll); + } + + serialize(): string { + return JSON.stringify({ + A: this.requiresAll, + N: this.featureNames, + T: this.name, + }); + } +} + +export function useRequireFeaturesSimpleStateChecker< + TState extends IHasSimpleStateCheckers, +>( + featureNames: string[], + requiresAll: boolean = false, +): ISimpleStateChecker { + const { featureChecker } = useFeatures(); + return new RequireFeaturesSimpleStateChecker( + featureChecker, + featureNames, + requiresAll, + ); +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireGlobalFeaturesSimpleStateChecker.ts b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireGlobalFeaturesSimpleStateChecker.ts new file mode 100644 index 000000000..700ea5ee6 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequireGlobalFeaturesSimpleStateChecker.ts @@ -0,0 +1,62 @@ +import type { IGlobalFeatureChecker } from '../../types/features'; +import type { + IHasSimpleStateCheckers, + ISimpleStateChecker, + SimpleStateCheckerContext, +} from '../../types/global'; + +import { useGlobalFeatures } from '../useGlobalFeatures'; + +export interface RequireGlobalFeaturesStateChecker { + featureNames: string[]; + name: string; + requiresAll: boolean; +} + +export class RequireGlobalFeaturesSimpleStateChecker< + TState extends IHasSimpleStateCheckers, + > + implements RequireGlobalFeaturesStateChecker, ISimpleStateChecker +{ + _globalFeatureChecker: IGlobalFeatureChecker; + featureNames: string[]; + name: string = 'G'; + requiresAll: boolean; + constructor( + globalFeatureChecker: IGlobalFeatureChecker, + featureNames: string[], + requiresAll: boolean = false, + ) { + this._globalFeatureChecker = globalFeatureChecker; + this.featureNames = featureNames; + this.requiresAll = requiresAll; + } + isEnabled(_context: SimpleStateCheckerContext): boolean { + return this._globalFeatureChecker.isEnabled( + this.featureNames, + this.requiresAll, + ); + } + + serialize(): string { + return JSON.stringify({ + A: this.requiresAll, + N: this.featureNames, + T: this.name, + }); + } +} + +export function useRequireGlobalFeaturesSimpleStateChecker< + TState extends IHasSimpleStateCheckers, +>( + featureNames: string[], + requiresAll: boolean = false, +): ISimpleStateChecker { + const globalFeatureChecker = useGlobalFeatures(); + return new RequireGlobalFeaturesSimpleStateChecker( + globalFeatureChecker, + featureNames, + requiresAll, + ); +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequirePermissionsSimpleStateChecker.ts b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequirePermissionsSimpleStateChecker.ts new file mode 100644 index 000000000..5040cbb1b --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/SimpleStateChecking/useRequirePermissionsSimpleStateChecker.ts @@ -0,0 +1,123 @@ +import type { + IHasSimpleStateCheckers, + ISimpleBatchStateChecker, + ISimpleStateChecker, + SimpleBatchStateCheckerContext, + SimpleStateCheckerContext, + SimpleStateCheckerResult, +} from '../../types/global'; +import type { IPermissionChecker } from '../../types/permissions'; + +import { useAuthorization } from '../useAuthorization'; + +export class RequirePermissionsSimpleBatchStateCheckerModel< + TState extends IHasSimpleStateCheckers, +> { + permissions: string[]; + requiresAll: boolean; + state: TState; + constructor( + state: TState, + permissions: string[], + requiresAll: boolean = true, + ) { + this.state = state; + this.permissions = permissions; + this.requiresAll = requiresAll; + } +} + +export interface RequirePermissionsStateChecker< + TState extends IHasSimpleStateCheckers, +> { + model: RequirePermissionsSimpleBatchStateCheckerModel; + name: string; +} + +export class RequirePermissionsSimpleStateChecker< + TState extends IHasSimpleStateCheckers, + > + implements RequirePermissionsStateChecker, ISimpleStateChecker +{ + _permissionChecker: IPermissionChecker; + model: RequirePermissionsSimpleBatchStateCheckerModel; + name: string = 'P'; + constructor( + permissionChecker: IPermissionChecker, + model: RequirePermissionsSimpleBatchStateCheckerModel, + ) { + this.model = model; + this._permissionChecker = permissionChecker; + } + isEnabled(_context: SimpleStateCheckerContext): boolean { + return this._permissionChecker.isGranted( + this.model.permissions, + this.model.requiresAll, + ); + } + + serialize(): string { + return JSON.stringify({ + A: this.model.requiresAll, + N: this.model.permissions, + T: this.name, + }); + } +} + +export class RequirePermissionsSimpleBatchStateChecker< + TState extends IHasSimpleStateCheckers, +> implements ISimpleBatchStateChecker +{ + _permissionChecker: IPermissionChecker; + models: RequirePermissionsSimpleBatchStateCheckerModel[]; + name: string = 'P'; + constructor( + permissionChecker: IPermissionChecker, + models: RequirePermissionsSimpleBatchStateCheckerModel[], + ) { + this.models = models; + this._permissionChecker = permissionChecker; + } + isEnabled(context: SimpleBatchStateCheckerContext) { + const result = {} as SimpleStateCheckerResult; + context.states.forEach((state) => { + const model = this.models.find((x) => x.state === state); + if (model) { + result[model.state] = this._permissionChecker.isGranted( + model.permissions, + model.requiresAll, + ); + } + }); + return result; + } + + serialize(): string | undefined { + return undefined; + } +} + +export function useRequirePermissionsSimpleStateChecker< + TState extends IHasSimpleStateCheckers, +>( + model: RequirePermissionsSimpleBatchStateCheckerModel, +): ISimpleStateChecker { + const permissionChecker = useAuthorization(); + return new RequirePermissionsSimpleStateChecker( + permissionChecker, + model, + ); +} + +export function useRequirePermissionsSimpleBatchStateChecker< + TState extends IHasSimpleStateCheckers, +>( + models: RequirePermissionsSimpleBatchStateCheckerModel[], +): ISimpleBatchStateChecker { + const permissionChecker = useAuthorization(); + return new RequirePermissionsSimpleBatchStateChecker( + permissionChecker, + models, + ); +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/index.ts b/apps/vben5/packages/@abp/core/src/hooks/index.ts index 2d4c33225..84b8a1dc5 100644 --- a/apps/vben5/packages/@abp/core/src/hooks/index.ts +++ b/apps/vben5/packages/@abp/core/src/hooks/index.ts @@ -1,4 +1,9 @@ +export * from './useAuthorization'; +export * from './useFeatures'; +export * from './useGlobalFeatures'; export * from './useLocalization'; +export * from './useLocalizationSerializer'; export * from './useSettings'; +export * from './useSimpleStateCheck'; export * from './useValidation'; export * from './useWindowSizeFn'; diff --git a/apps/vben5/packages/@abp/core/src/hooks/useAuthorization.ts b/apps/vben5/packages/@abp/core/src/hooks/useAuthorization.ts new file mode 100644 index 000000000..af25aa55a --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useAuthorization.ts @@ -0,0 +1,36 @@ +import type { IPermissionChecker } from '../types/permissions'; + +import { computed } from 'vue'; + +import { useAbpStore } from '../store/abp'; + +export function useAuthorization(): IPermissionChecker { + const abpStore = useAbpStore(); + const getGrantedPolicies = computed(() => { + return abpStore.application?.auth.grantedPolicies ?? {}; + }); + + function isGranted(name: string | string[], requiresAll?: boolean): boolean { + const grantedPolicies = getGrantedPolicies.value; + if (Array.isArray(name)) { + if (requiresAll === undefined || requiresAll === true) { + return name.every((name) => grantedPolicies[name]); + } + return name.some((name) => grantedPolicies[name]); + } + return grantedPolicies[name] ?? false; + } + + function authorize(name: string | string[]): void { + if (!isGranted(name)) { + throw new Error( + `Authorization failed! Given policy has not granted: ${name}`, + ); + } + } + + return { + authorize, + isGranted, + }; +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/useFeatures.ts b/apps/vben5/packages/@abp/core/src/hooks/useFeatures.ts new file mode 100644 index 000000000..3ba7bdacf --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useFeatures.ts @@ -0,0 +1,56 @@ +import type { FeatureValue, IFeatureChecker } from '../types/features'; + +import { computed } from 'vue'; + +import { useAbpStore } from '../store/abp'; + +export function useFeatures() { + const abpStore = useAbpStore(); + const getFeatures = computed(() => { + const fetures = abpStore.application?.features.values ?? {}; + const fetureValues = Object.keys(fetures).map((key): FeatureValue => { + return { + name: key, + value: fetures[key] ?? '', + }; + }); + return fetureValues; + }); + + function get(name: string): FeatureValue | undefined { + return getFeatures.value.find((feature) => name === feature.name); + } + + function _isEnabled(name: string): boolean { + const setting = get(name); + return setting?.value.toLowerCase() === 'true'; + } + + const featureChecker: IFeatureChecker = { + getOrEmpty(name: string) { + return get(name)?.value ?? ''; + }, + + isEnabled(featureNames: string | string[], requiresAll?: boolean) { + if (Array.isArray(featureNames)) { + if (featureNames.length === 0) return true; + if (requiresAll === undefined || requiresAll === true) { + for (const featureName of featureNames) { + if (!_isEnabled(featureName)) return false; + } + return true; + } + + for (const featureName of featureNames) { + if (_isEnabled(featureName)) return true; + } + } else { + return _isEnabled(featureNames); + } + + return false; + }, + }; + + return { featureChecker }; +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/useGlobalFeatures.ts b/apps/vben5/packages/@abp/core/src/hooks/useGlobalFeatures.ts new file mode 100644 index 000000000..0e7da84f3 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useGlobalFeatures.ts @@ -0,0 +1,49 @@ +import { computed } from 'vue'; + +import { useAbpStore } from '../store/abp'; +import { isNullOrWhiteSpace } from '../utils/string'; + +export function useGlobalFeatures() { + const getGlobalFeatures = computed(() => { + const abpStore = useAbpStore(); + const enabledFeatures = + abpStore.application?.globalFeatures.enabledFeatures ?? []; + return enabledFeatures; + }); + + function get(name: string): string | undefined { + return getGlobalFeatures.value.find((feature) => name === feature); + } + + function _isEnabled(name: string): boolean { + const 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 (const featureName of featureNames) { + if (!_isEnabled(featureName)) return false; + } + return true; + } + + for (const featureName of featureNames) { + if (_isEnabled(featureName)) return true; + } + } else { + return _isEnabled(featureNames); + } + + return false; + } + + return { + isEnabled, + }; +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/useLocalization.ts b/apps/vben5/packages/@abp/core/src/hooks/useLocalization.ts index 0067107c5..a9e21b08d 100644 --- a/apps/vben5/packages/@abp/core/src/hooks/useLocalization.ts +++ b/apps/vben5/packages/@abp/core/src/hooks/useLocalization.ts @@ -1,6 +1,6 @@ import type { Dictionary, StringLocalizer } from '../types'; -import { computed } from 'vue'; +import { computed, ref, watch } from 'vue'; import merge from 'lodash.merge'; @@ -9,24 +9,47 @@ import { format } from '../utils/string'; export function useLocalization(resourceNames?: string | string[]) { const abpStore = useAbpStore(); - const getResource = computed(() => { - if (!abpStore.application) { - return {}; - } - const { values } = abpStore.application.localization; + const localizations = ref>>({}); + + watch( + () => abpStore.localization, + (localization) => { + if (!localization?.resources) { + localizations.value = {}; + return; + } + const localizationResource: Dictionary< + string, + Dictionary + > = {}; + Object.keys(localization.resources).forEach((resourceName) => { + if (localization.resources[resourceName]) { + localizationResource[resourceName] = + localization.resources[resourceName].texts; + } + }); + localizations.value = localizationResource; + }, + { + deep: true, + immediate: true, + }, + ); + + const getResource = computed(() => { let resource: { [key: string]: string } = {}; if (resourceNames) { if (Array.isArray(resourceNames)) { resourceNames.forEach((name) => { - resource = merge(resource, values[name]); + resource = merge(resource, localizations.value[name]); }); } else { - resource = merge(resource, values[resourceNames]); + resource = merge(resource, localizations.value[resourceNames]); } } else { - Object.keys(values).forEach((rs) => { - resource = merge(resource, values[rs]); + Object.keys(localizations.value).forEach((rs) => { + resource = merge(resource, localizations.value[rs]); }); } @@ -34,11 +57,7 @@ export function useLocalization(resourceNames?: string | string[]) { }); const getResourceByName = computed(() => { return (resource: string): Dictionary => { - if (!abpStore.application) { - return {}; - } - const { values } = abpStore.application.localization; - return values[resource] ?? {}; + return localizations.value[resource] ?? {}; }; }); diff --git a/apps/vben5/packages/@abp/core/src/hooks/useLocalizationSerializer.ts b/apps/vben5/packages/@abp/core/src/hooks/useLocalizationSerializer.ts new file mode 100644 index 000000000..7b37a5008 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useLocalizationSerializer.ts @@ -0,0 +1,109 @@ +import type { LocalizableStringInfo } from '../types'; + +import { isNullOrWhiteSpace } from '../utils/string'; + +interface ValidateOptions { + required?: boolean; +} + +interface ILocalizableStringSerializer { + deserialize(value?: string): LocalizableStringInfo; + serialize(value?: LocalizableStringInfo): string; + validate(value?: string, opt?: ValidateOptions): boolean; +} + +export function useLocalizationSerializer(): ILocalizableStringSerializer { + function Validate(value?: string, opt?: ValidateOptions): boolean { + if (!value || isNullOrWhiteSpace(value)) { + if (!opt || opt.required === undefined || opt.required === true) { + return false; + } + return true; + } + if (value.length < 3 || value[1] !== ':') { + return false; + } + const type = value[0]; + switch (type) { + case 'F': { + return !isNullOrWhiteSpace(value.slice(2).trim()); + } + case 'L': { + const commaPosition = value.indexOf(',', 2); + if (commaPosition === -1) { + return false; + } + const name = value.slice(Math.max(0, commaPosition + 1)); + if (isNullOrWhiteSpace(name)) { + return false; + } + return true; + } + default: { + return false; + } + } + } + + function Serialize(value?: LocalizableStringInfo): string { + if (!value) return ''; + return `L:${value.resourceName},${value.name}`; + } + + function Deserialize(value?: string): LocalizableStringInfo { + if (!value || isNullOrWhiteSpace(value)) { + return { + name: '', + resourceName: '', + }; + } + if (value.length < 2 || value[1] !== ':') { + return { + name: value, + resourceName: '', + }; + } + const type = value[0]; + switch (type) { + case 'F': { + return { + name: value.slice(2), + resourceName: 'Fixed', + }; + } + case 'L': { + const commaPosition = value.indexOf(',', 2); + if (commaPosition === -1) { + return { + name: value, + resourceName: 'Default', + }; + } + const resourceName = value.slice(2, commaPosition); + const name = value.slice(Math.max(0, commaPosition + 1)); + if (isNullOrWhiteSpace(resourceName)) { + return { + name: value, + resourceName: 'Default', + }; + } + return { + name, + resourceName, + }; + } + default: { + return { + name: value, + resourceName: 'Default', + }; + } + } + } + + return { + deserialize: Deserialize, + serialize: Serialize, + validate: Validate, + }; +} diff --git a/apps/vben5/packages/@abp/core/src/hooks/useSimpleStateCheck.ts b/apps/vben5/packages/@abp/core/src/hooks/useSimpleStateCheck.ts new file mode 100644 index 000000000..df2a94c70 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useSimpleStateCheck.ts @@ -0,0 +1,135 @@ +import type { + IHasSimpleStateCheckers, + ISimpleStateChecker, + ISimpleStateCheckerSerializer, +} from '../types/global'; + +import { isNullOrUnDef } from '../utils/is'; +import { isNullOrWhiteSpace } from '../utils/string'; +import { useRequireAuthenticatedSimpleStateChecker } from './SimpleStateChecking/useRequireAuthenticatedSimpleStateChecker'; +import { useRequireFeaturesSimpleStateChecker } from './SimpleStateChecking/useRequireFeaturesSimpleStateChecker'; +import { useRequireGlobalFeaturesSimpleStateChecker } from './SimpleStateChecking/useRequireGlobalFeaturesSimpleStateChecker'; +import { useRequirePermissionsSimpleStateChecker } from './SimpleStateChecking/useRequirePermissionsSimpleStateChecker'; + +class SimpleStateCheckerSerializer implements ISimpleStateCheckerSerializer { + deserialize>( + jsonObject: any, + state: TState, + ): ISimpleStateChecker | undefined { + if (isNullOrUnDef(jsonObject) || !Reflect.has(jsonObject, 'T')) { + return undefined; + } + switch (String(jsonObject.T)) { + case 'A': { + return useRequireAuthenticatedSimpleStateChecker(); + } + case 'F': { + const features = jsonObject.N as string[]; + if (features === undefined) { + throw new 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 new Error( + `'N' is not an array in the serialized state checker! JsonObject: ${ + jsonObject + }`, + ); + } + return useRequireGlobalFeaturesSimpleStateChecker( + globalFeatures, + jsonObject.A === true, + ); + } + case 'P': { + const permissions = jsonObject.N as string[]; + if (permissions === undefined) { + throw new Error( + `'N' is not an array in the serialized state checker! JsonObject: ${ + jsonObject + }`, + ); + } + return useRequirePermissionsSimpleStateChecker({ + permissions, + requiresAll: jsonObject.A === true, + state, + }); + } + default: { + return undefined; + } + } + } + + deserializeArray>( + value: string, + state: TState, + ): ISimpleStateChecker[] { + 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]; + } + + serialize>( + checker: ISimpleStateChecker, + ): string | undefined { + return checker.serialize(); + } + + serializeArray>( + stateCheckers: ISimpleStateChecker[], + ): string | undefined { + if (stateCheckers.length === 0) return undefined; + if (stateCheckers.length === 1) { + const stateChecker = stateCheckers[0]; + const single = stateChecker?.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.slice( + 0, + Math.max(0, serializedCheckers.length - 1), + ); + } + return serializedCheckers.length > 0 + ? `[${serializedCheckers}]` + : undefined; + } +} + +export function useSimpleStateCheck< + TState extends IHasSimpleStateCheckers, +>() { + return { + serializer: new SimpleStateCheckerSerializer(), + }; +} diff --git a/apps/vben5/packages/@abp/core/src/store/abp.ts b/apps/vben5/packages/@abp/core/src/store/abp.ts index 8d035181d..f17801928 100644 --- a/apps/vben5/packages/@abp/core/src/store/abp.ts +++ b/apps/vben5/packages/@abp/core/src/store/abp.ts @@ -52,7 +52,12 @@ export const useAbpStore = defineStore( localization.value = val; } + function $reset() { + application.value = undefined; + } + return { + $reset, application, getI18nLocales, localization, diff --git a/apps/vben5/packages/@abp/core/src/types/features.ts b/apps/vben5/packages/@abp/core/src/types/features.ts new file mode 100644 index 000000000..d46b26a58 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/types/features.ts @@ -0,0 +1,27 @@ +import type { NameValue } from './global'; + +type FeatureValue = NameValue; + +/** + * 特性检查接口 + */ +interface IFeatureChecker { + /** + * 获取特性值 + * @param name 特性名称 + * @returns 返回设定的特性值,不存在则为空字符串 + */ + getOrEmpty(name: string): string; + /** + * 是否启用特性 + * @param featureNames 特性名称 + * @param requiresAll 是否全部符合 + */ + isEnabled(featureNames: string | string[], requiresAll?: boolean): boolean; +} + +interface IGlobalFeatureChecker { + isEnabled(featureNames: string | string[], requiresAll?: boolean): boolean; +} + +export type { FeatureValue, IFeatureChecker, IGlobalFeatureChecker }; diff --git a/apps/vben5/packages/@abp/core/src/types/global.ts b/apps/vben5/packages/@abp/core/src/types/global.ts index b0ab7d8b1..8da06034a 100644 --- a/apps/vben5/packages/@abp/core/src/types/global.ts +++ b/apps/vben5/packages/@abp/core/src/types/global.ts @@ -206,31 +206,35 @@ interface CurrentUser { userName: string; } -// eslint-disable-next-line no-use-before-define +interface IHasSimpleStateCheckers< + TState extends IHasSimpleStateCheckers, +> { + // eslint-disable-next-line no-use-before-define + stateCheckers: ISimpleStateChecker[]; +} + +type SimpleStateRecord< + TState extends IHasSimpleStateCheckers, + TValue, +> = { + [P in TState]: TValue; +}; + type SimpleStateCheckerResult> = - Map; + SimpleStateRecord; interface SimpleStateCheckerContext< - // eslint-disable-next-line no-use-before-define TState extends IHasSimpleStateCheckers, > { state: TState; } interface SimpleBatchStateCheckerContext< - // eslint-disable-next-line no-use-before-define TState extends IHasSimpleStateCheckers, > { states: TState[]; } -interface IHasSimpleStateCheckers< - TState extends IHasSimpleStateCheckers, -> { - // eslint-disable-next-line no-use-before-define - stateCheckers: ISimpleStateChecker[]; -} - interface ISimpleStateChecker> { isEnabled(context: SimpleStateCheckerContext): boolean; serialize(): string | undefined; diff --git a/apps/vben5/packages/@abp/core/src/types/index.ts b/apps/vben5/packages/@abp/core/src/types/index.ts index b34d7c5f1..efa5c216b 100644 --- a/apps/vben5/packages/@abp/core/src/types/index.ts +++ b/apps/vben5/packages/@abp/core/src/types/index.ts @@ -1,7 +1,9 @@ export * from './dto'; +export * from './features'; export * from './global'; export * from './localization'; export * from './openid'; +export * from './permissions'; export * from './rules'; export * from './settings'; export * from './table'; diff --git a/apps/vben5/packages/@abp/core/src/types/permissions.ts b/apps/vben5/packages/@abp/core/src/types/permissions.ts new file mode 100644 index 000000000..cfc062872 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/types/permissions.ts @@ -0,0 +1,6 @@ +interface IPermissionChecker { + authorize(name: string | string[]): void; + isGranted(name: string | string[], requiresAll?: boolean): boolean; +} + +export type { IPermissionChecker }; diff --git a/apps/vben5/packages/@abp/core/src/utils/is.ts b/apps/vben5/packages/@abp/core/src/utils/is.ts new file mode 100644 index 000000000..c760f4afa --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/utils/is.ts @@ -0,0 +1,19 @@ +export function isNullAndUnDef(val: unknown): val is null | undefined { + return isUnDef(val) && isNull(val); +} + +export function isNullOrUnDef(val: unknown): val is null | undefined { + return isUnDef(val) || isNull(val); +} + +export function isDef(val?: T): val is T { + return val !== undefined; +} + +export function isUnDef(val?: T): val is T { + return !isDef(val); +} + +export function isNull(value: any): value is null { + return value === null; +} diff --git a/apps/vben5/packages/@abp/permission/package.json b/apps/vben5/packages/@abp/permission/package.json index b5adfecf3..88cc3d330 100644 --- a/apps/vben5/packages/@abp/permission/package.json +++ b/apps/vben5/packages/@abp/permission/package.json @@ -31,6 +31,11 @@ "@vben/layouts": "workspace:*", "@vben/locales": "workspace:*", "ant-design-vue": "catalog:", - "vue": "catalog:*" + "lodash.clonedeep": "catalog:", + "vue": "catalog:*", + "vxe-table": "catalog:" + }, + "devDependencies": { + "@types/lodash.clonedeep": "catalog:" } } diff --git a/apps/vben5/packages/@abp/permission/src/api/definitions.ts b/apps/vben5/packages/@abp/permission/src/api/definitions.ts new file mode 100644 index 000000000..2862de492 --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/api/definitions.ts @@ -0,0 +1,75 @@ +import type { ListResultDto } from '@abp/core'; + +import type { + PermissionDefinitionCreateDto, + PermissionDefinitionDto, + PermissionDefinitionGetListInput, + PermissionDefinitionUpdateDto, +} from '../types/definitions'; + +import { requestClient } from '@abp/request'; + +/** + * 删除权限定义 + * @param name 权限名称 + */ +export function deleteApi(name: string): Promise { + return requestClient.delete(`/api/permission-management/definitions/${name}`); +} + +/** + * 查询权限定义 + * @param name 权限名称 + * @returns 权限定义数据传输对象 + */ +export function getApi(name: string): Promise { + return requestClient.get( + `/api/permission-management/definitions/${name}`, + ); +} + +/** + * 查询权限定义列表 + * @param input 权限过滤条件 + * @returns 权限定义数据传输对象列表 + */ +export function getListApi( + input?: PermissionDefinitionGetListInput, +): Promise> { + return requestClient.get>( + `/api/permission-management/definitions`, + { + params: input, + }, + ); +} + +/** + * 创建权限定义 + * @param input 权限定义参数 + * @returns 权限定义数据传输对象 + */ +export function createApi( + input: PermissionDefinitionCreateDto, +): Promise { + return requestClient.post( + '/api/permission-management/definitions', + input, + ); +} + +/** + * 更新权限定义 + * @param name 权限名称 + * @param input 权限定义参数 + * @returns 权限定义数据传输对象 + */ +export function updateApi( + name: string, + input: PermissionDefinitionUpdateDto, +): Promise { + return requestClient.put( + `/api/permission-management/definitions/${name}`, + input, + ); +} diff --git a/apps/vben5/packages/@abp/permission/src/api/groups.ts b/apps/vben5/packages/@abp/permission/src/api/groups.ts new file mode 100644 index 000000000..0161ab552 --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/api/groups.ts @@ -0,0 +1,77 @@ +import type { ListResultDto } from '@abp/core'; + +import type { + PermissionGroupDefinitionCreateDto, + PermissionGroupDefinitionDto, + PermissionGroupDefinitionGetListInput, + PermissionGroupDefinitionUpdateDto, +} from '../types/groups'; + +import { requestClient } from '@abp/request'; + +/** + * 删除权限定义 + * @param name 权限名称 + */ +export function deleteApi(name: string): Promise { + return requestClient.delete( + `/api/permission-management/definitions/groups/${name}`, + ); +} + +/** + * 查询权限定义 + * @param name 权限名称 + * @returns 权限定义数据传输对象 + */ +export function getApi(name: string): Promise { + return requestClient.get( + `/api/permission-management/definitions/groups/${name}`, + ); +} + +/** + * 查询权限定义列表 + * @param input 权限过滤条件 + * @returns 权限定义数据传输对象列表 + */ +export function getListApi( + input?: PermissionGroupDefinitionGetListInput, +): Promise> { + return requestClient.get>( + `/api/permission-management/definitions/groups`, + { + params: input, + }, + ); +} + +/** + * 创建权限定义 + * @param input 权限定义参数 + * @returns 权限定义数据传输对象 + */ +export function createApi( + input: PermissionGroupDefinitionCreateDto, +): Promise { + return requestClient.post( + '/api/permission-management/definitions/groups', + input, + ); +} + +/** + * 更新权限定义 + * @param name 权限名称 + * @param input 权限定义参数 + * @returns 权限定义数据传输对象 + */ +export function updateApi( + name: string, + input: PermissionGroupDefinitionUpdateDto, +): Promise { + return requestClient.put( + `/api/permission-management/definitions/groups/${name}`, + input, + ); +} diff --git a/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue b/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue index 1017376d1..6d9012c17 100644 --- a/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue +++ b/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue @@ -1,7 +1,145 @@ - + - + + + + + + + + + + + + + + + + + + + diff --git a/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue b/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue index 1017376d1..044f43ec0 100644 --- a/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue +++ b/apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue @@ -1,7 +1,273 @@ - + - + + + + {{ $t('AbpPermissionManagement.GroupDefinitions:AddNew') }} + + + + + + + {{ $t('AbpUi.Edit') }} + + + + + + {{ $t('AbpUi.Delete') }} + + + + + + onMenuClick(row, info)"> + + {{ + $t('AbpPermissionManagement.PermissionDefinitions:AddNew') + }} + + + + + + + + + + + onGet()" /> + diff --git a/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue index 1017376d1..0b5e88925 100644 --- a/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue +++ b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue @@ -1,7 +1,285 @@ - + - + + + + + + + + {{ $t('AbpPermissionManagement.DisplayName:IsEnabled') }} + + + + onGroupChange(e?.toString())" + /> + + + + + + + + + + + + + + + + + + + + + + + + +import { useLocalization, useLocalizationSerializer } from '@abp/core'; diff --git a/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue index 1017376d1..c55312b5a 100644 --- a/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue +++ b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue @@ -1,7 +1,336 @@ - + - + + + + {{ $t('AbpPermissionManagement.PermissionDefinitions:AddNew') }} + + + + + + + {{ multiTenancySidesMap[permission.multiTenancySide] }} + + + + + + {{ providersMap[provider] }} + + + + + + + {{ $t('AbpUi.Edit') }} + + + {{ $t('AbpUi.Delete') }} + + + + + + + onGet()" /> diff --git a/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/types.ts b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/types.ts new file mode 100644 index 000000000..7a4bcdef0 --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/components/definitions/permissions/types.ts @@ -0,0 +1,53 @@ +import { $t } from '@vben/locales'; + +import { MultiTenancySides } from '../../../types/definitions'; + +export function useTypesMap() { + const multiTenancySidesMap: { [key: number]: string } = { + [MultiTenancySides.Both]: $t( + 'AbpPermissionManagement.MultiTenancySides:Both', + ), + [MultiTenancySides.Host]: $t( + 'AbpPermissionManagement.MultiTenancySides:Host', + ), + [MultiTenancySides.Tenant]: $t( + 'AbpPermissionManagement.MultiTenancySides:Tenant', + ), + }; + + const multiTenancySideOptions = [ + { + label: multiTenancySidesMap[MultiTenancySides.Tenant], + value: MultiTenancySides.Tenant, + }, + { + label: multiTenancySidesMap[MultiTenancySides.Host], + value: MultiTenancySides.Host, + }, + { + label: multiTenancySidesMap[MultiTenancySides.Both], + value: MultiTenancySides.Both, + }, + ]; + + const providersMap: { [key: string]: string } = { + C: $t('AbpPermissionManagement.Providers:Client'), + O: $t('AbpPermissionManagement.Providers:OrganizationUnit'), + R: $t('AbpPermissionManagement.Providers:Role'), + U: $t('AbpPermissionManagement.Providers:User'), + }; + + const providerOptions = [ + { label: providersMap.R, value: 'R' }, + { label: providersMap.U, value: 'U' }, + { label: providersMap.O, value: 'O' }, + { label: providersMap.C, value: 'C' }, + ]; + + return { + multiTenancySideOptions, + multiTenancySidesMap, + providerOptions, + providersMap, + }; +} diff --git a/apps/vben5/packages/@abp/permission/src/constants/index.ts b/apps/vben5/packages/@abp/permission/src/constants/index.ts new file mode 100644 index 000000000..c85954d3e --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/constants/index.ts @@ -0,0 +1 @@ +export * from './permissions'; diff --git a/apps/vben5/packages/@abp/permission/src/constants/permissions.ts b/apps/vben5/packages/@abp/permission/src/constants/permissions.ts new file mode 100644 index 000000000..07e7cf5b4 --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/constants/permissions.ts @@ -0,0 +1,20 @@ +/** 分组权限 */ +export const GroupDefinitionsPermissions = { + /** 新增 */ + Create: 'PermissionManagement.GroupDefinitions.Create', + Default: 'PermissionManagement.GroupDefinitions', + /** 删除 */ + Delete: 'PermissionManagement.GroupDefinitions.Delete', + /** 更新 */ + Update: 'PermissionManagement.GroupDefinitions.Update', +}; +/** 权限定义权限 */ +export const PermissionDefinitionsPermissions = { + /** 新增 */ + Create: 'PermissionManagement.Definitions.Create', + Default: 'PermissionManagement.Definitions', + /** 删除 */ + Delete: 'PermissionManagement.Definitions.Delete', + /** 更新 */ + Update: 'PermissionManagement.Definitions.Update', +}; diff --git a/apps/vben5/packages/@abp/permission/src/types/definitions.ts b/apps/vben5/packages/@abp/permission/src/types/definitions.ts new file mode 100644 index 000000000..699929135 --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/types/definitions.ts @@ -0,0 +1,46 @@ +import type { IHasExtraProperties } from '@abp/core'; + +export enum MultiTenancySides { + Both = 0x3, + Host = 0x2, + Tenant = 0x1, +} +interface PermissionDefinitionCreateOrUpdateDto extends IHasExtraProperties { + displayName: string; + isEnabled: boolean; + parentName?: string; + providers: string[]; + stateCheckers: string; +} + +interface PermissionDefinitionCreateDto + extends PermissionDefinitionCreateOrUpdateDto { + groupName: string; + name: string; +} + +interface PermissionDefinitionDto extends IHasExtraProperties { + displayName: string; + groupName: string; + isEnabled: boolean; + isStatic: boolean; + multiTenancySide: MultiTenancySides; + name: string; + parentName?: string; + providers: string[]; + stateCheckers: string; +} + +interface PermissionDefinitionGetListInput { + filter?: string; + groupName?: string; +} + +type PermissionDefinitionUpdateDto = PermissionDefinitionCreateOrUpdateDto; + +export type { + PermissionDefinitionCreateDto, + PermissionDefinitionDto, + PermissionDefinitionGetListInput, + PermissionDefinitionUpdateDto, +}; diff --git a/apps/vben5/packages/@abp/permission/src/types/groups.ts b/apps/vben5/packages/@abp/permission/src/types/groups.ts new file mode 100644 index 000000000..ed1c5245e --- /dev/null +++ b/apps/vben5/packages/@abp/permission/src/types/groups.ts @@ -0,0 +1,27 @@ +import type { IHasExtraProperties } from '@abp/core'; + +interface PermissionGroupDefinitionCreateOrUpdateDto + extends IHasExtraProperties { + displayName: string; +} +interface PermissionGroupDefinitionCreateDto + extends PermissionGroupDefinitionCreateOrUpdateDto { + name: string; +} +interface PermissionGroupDefinitionDto extends IHasExtraProperties { + displayName: string; + isStatic: boolean; + name: string; +} +interface PermissionGroupDefinitionGetListInput { + filter?: string; +} +type PermissionGroupDefinitionUpdateDto = + PermissionGroupDefinitionCreateOrUpdateDto; + +export type { + PermissionGroupDefinitionCreateDto, + PermissionGroupDefinitionDto, + PermissionGroupDefinitionGetListInput, + PermissionGroupDefinitionUpdateDto, +}; diff --git a/apps/vben5/packages/@abp/ui/package.json b/apps/vben5/packages/@abp/ui/package.json index e039de782..5d4e72c8a 100644 --- a/apps/vben5/packages/@abp/ui/package.json +++ b/apps/vben5/packages/@abp/ui/package.json @@ -40,6 +40,7 @@ "@vben-core/shared": "workspace:*", "@vben-core/typings": "workspace:*", "@vben/common-ui": "workspace:*", + "@vben/icons": "workspace:*", "@vben/locales": "workspace:*", "@vben/plugins": "workspace:*", "@vueuse/core": "catalog:", diff --git a/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts b/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts index 8f959d53a..48dd893bb 100644 --- a/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts +++ b/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts @@ -22,7 +22,7 @@ setupVbenVxeTable({ minHeight: 180, pagerConfig: { pageSize: 10, - pageSizes: [10, 15, 25, 50, 100], + pageSizes: [10, 25, 50, 100], }, proxyConfig: { autoLoad: true, diff --git a/apps/vben5/packages/@abp/ui/src/components/CodeEditor.vue b/apps/vben5/packages/@abp/ui/src/components/codeeditor/index.vue similarity index 93% rename from apps/vben5/packages/@abp/ui/src/components/CodeEditor.vue rename to apps/vben5/packages/@abp/ui/src/components/codeeditor/index.vue index 829ccc7da..d4968d939 100644 --- a/apps/vben5/packages/@abp/ui/src/components/CodeEditor.vue +++ b/apps/vben5/packages/@abp/ui/src/components/codeeditor/index.vue @@ -4,8 +4,8 @@ import { computed } from 'vue'; import { isString } from '@vben-core/shared/utils'; -import CodeMirror from './codemirror/CodeMirror.vue'; -import { MODE } from './codemirror/types'; +import CodeMirror from '../codemirror/CodeMirror.vue'; +import { MODE } from '../codemirror/types'; const props = defineProps({ autoFormat: { default: true, type: Boolean }, diff --git a/apps/vben5/packages/@abp/ui/src/components/index.ts b/apps/vben5/packages/@abp/ui/src/components/index.ts index a1891146b..367fce72a 100644 --- a/apps/vben5/packages/@abp/ui/src/components/index.ts +++ b/apps/vben5/packages/@abp/ui/src/components/index.ts @@ -1,2 +1,5 @@ -export { default as CodeEditor } from './CodeEditor.vue'; +export { default as CodeEditor } from './codeeditor/index.vue'; export * from './codemirror'; +export { default as LocalizableInput } from './localizable-input/LocalizableInput.vue'; +export { default as PropertyTable } from './properties/PropertyTable.vue'; +export * from './properties/types'; diff --git a/apps/vben5/packages/@abp/ui/src/components/localizable-input/LocalizableInput.vue b/apps/vben5/packages/@abp/ui/src/components/localizable-input/LocalizableInput.vue new file mode 100644 index 000000000..556f6063c --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/localizable-input/LocalizableInput.vue @@ -0,0 +1,177 @@ + + + + + + + + + handleResourceChange(value?.toString(), true)" + /> + + + handleDisplayNameChange(e.target.value?.toString()) + " + /> + handleDisplayNameChange(e?.toString())" + /> + + + + + + diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyModal.vue b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyModal.vue new file mode 100644 index 000000000..f20397c04 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyModal.vue @@ -0,0 +1,66 @@ + + + + + + + + + diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue new file mode 100644 index 000000000..8eb920245 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue @@ -0,0 +1,133 @@ + + + + + + + + + + {{ $t('component.extra_property_dictionary.actions.create') }} + + + + + + + + + + + + {{ $t('component.extra_property_dictionary.actions.delete') }} + + + + + + + + + + + diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/types.ts b/apps/vben5/packages/@abp/ui/src/components/properties/types.ts new file mode 100644 index 000000000..45dadb6d1 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/properties/types.ts @@ -0,0 +1,14 @@ +import type { Dictionary } from '@abp/core'; + +interface PropertyInfo { + key: string; + value: string; +} +interface PropertyProps { + allowDelete?: boolean; + allowEdit?: boolean; + data?: Dictionary; + disabled?: boolean; +} + +export type { PropertyInfo, PropertyProps };