diff --git a/apps/vue/.env.development b/apps/vue/.env.development index 221f51e7a..323254092 100644 --- a/apps/vue/.env.development +++ b/apps/vue/.env.development @@ -6,7 +6,7 @@ VITE_PUBLIC_PATH=/ # Cross-domain proxy, you can configure multiple # Please note that no line breaks -VITE_PROXY=[["/connect","http://127.0.0.1:44385"],["/api","http://127.0.0.1:30000"],["/signalr-hubs","ws://127.0.0.1:30000"]] +VITE_PROXY=[["/connect","http://127.0.0.1:30000"],["/api","http://127.0.0.1:30000"],["/signalr-hubs","ws://127.0.0.1:30000"]] # VITE_PROXY=[["/api","https://vvbin.cn/test"]] # Delete console @@ -25,6 +25,6 @@ VITE_GLOB_API_URL_PREFIX= VITE_GLOB_MULTITENANCY_KEY='__tenant' # STS Connect -VITE_GLOB_AUTHORITY='http://127.0.0.1:44385' +VITE_GLOB_AUTHORITY='http://127.0.0.1:30000' VITE_GLOB_CLIENT_ID='vue-admin-client' VITE_GLOB_CLIENT_SECRET='1q2w3e*' diff --git a/apps/vue/src/api/feature-management/definitions/features/index.ts b/apps/vue/src/api/feature-management/definitions/features/index.ts new file mode 100644 index 000000000..01bd0f6a4 --- /dev/null +++ b/apps/vue/src/api/feature-management/definitions/features/index.ts @@ -0,0 +1,40 @@ +import { defHttp } from '/@/utils/http/axios'; +import { + FeatureDefinitionDto, + FeatureDefinitionCreateDto, + FeatureDefinitionUpdateDto, + FeatureDefinitionGetListInput, +} from './model'; + +export const CreateAsyncByInput = (input: FeatureDefinitionCreateDto) => { + return defHttp.post({ + url: '/api/feature-management/definitions', + data: input, + }); +}; + +export const DeleteAsyncByName = (name: string) => { + return defHttp.delete({ + url: `/api/feature-management/definitions/${name}`, + }); +}; + +export const GetAsyncByName = (name: string) => { + return defHttp.get({ + url: `/api/feature-management/definitions/${name}`, + }); +}; + +export const GetListAsyncByInput = (input: FeatureDefinitionGetListInput) => { + return defHttp.get>({ + url: '/api/feature-management/definitions', + params: input, + }); +}; + +export const UpdateAsyncByNameAndInput = (name: string, input: FeatureDefinitionUpdateDto) => { + return defHttp.put({ + url: `/api/feature-management/definitions/${name}`, + data: input, + }); +}; diff --git a/apps/vue/src/api/feature-management/definitions/features/model/index.ts b/apps/vue/src/api/feature-management/definitions/features/model/index.ts new file mode 100644 index 000000000..117532ded --- /dev/null +++ b/apps/vue/src/api/feature-management/definitions/features/model/index.ts @@ -0,0 +1,36 @@ +interface FeatureDefinitionCreateOrUpdateDto extends IHasExtraProperties { + displayName: string; + parentName?: string; + description?: string; + defaultValue?: string; + valueType: string; + isVisibleToClients: boolean; + isAvailableToHost: boolean; + allowedProviders: string[]; +} + +export interface FeatureDefinitionCreateDto extends FeatureDefinitionCreateOrUpdateDto { + name: string; + groupName: string; +} + +export interface FeatureDefinitionDto extends IHasExtraProperties { + name: string; + groupName: string; + displayName: string; + parentName?: string; + description?: string; + defaultValue?: string; + valueType: string; + isStatic: boolean; + isVisibleToClients: boolean; + isAvailableToHost: boolean; + allowedProviders: string[]; +} + +export interface FeatureDefinitionGetListInput { + filter?: string; + groupName?: string; +} + +export type FeatureDefinitionUpdateDto = FeatureDefinitionCreateOrUpdateDto; diff --git a/apps/vue/src/api/feature-management/definitions/groups/index.ts b/apps/vue/src/api/feature-management/definitions/groups/index.ts new file mode 100644 index 000000000..e3b038dbc --- /dev/null +++ b/apps/vue/src/api/feature-management/definitions/groups/index.ts @@ -0,0 +1,40 @@ +import { defHttp } from '/@/utils/http/axios'; +import { + FeatureGroupDefinitionDto, + FeatureGroupDefinitionCreateDto, + FeatureGroupDefinitionUpdateDto, + FeatureGroupDefinitionGetListInput, +} from './model'; + +export const CreateAsyncByInput = (input: FeatureGroupDefinitionCreateDto) => { + return defHttp.post({ + url: '/api/feature-management/definitions/groups', + data: input, + }); +}; + +export const DeleteAsyncByName = (name: string) => { + return defHttp.delete({ + url: `/api/feature-management/definitions/groups/${name}`, + }); +}; + +export const GetAsyncByName = (name: string) => { + return defHttp.get({ + url: `/api/feature-management/definitions/groups/${name}`, + }); +}; + +export const GetListAsyncByInput = (input: FeatureGroupDefinitionGetListInput) => { + return defHttp.get>({ + url: '/api/feature-management/definitions/groups', + params: input, + }); +}; + +export const UpdateAsyncByNameAndInput = (name: string, input: FeatureGroupDefinitionUpdateDto) => { + return defHttp.put({ + url: `/api/feature-management/definitions/groups/${name}`, + data: input, + }); +}; diff --git a/apps/vue/src/api/feature-management/definitions/groups/model/index.ts b/apps/vue/src/api/feature-management/definitions/groups/model/index.ts new file mode 100644 index 000000000..031761e2d --- /dev/null +++ b/apps/vue/src/api/feature-management/definitions/groups/model/index.ts @@ -0,0 +1,19 @@ +interface FeatureGroupDefinitionCreateOrUpdateDto extends IHasExtraProperties { + displayName: string; +} + +export interface FeatureGroupDefinitionCreateDto extends FeatureGroupDefinitionCreateOrUpdateDto { + name: string; +} + +export interface FeatureGroupDefinitionDto extends IHasExtraProperties { + name: string; + displayName: string; + isStatic: boolean; +} + +export interface FeatureGroupDefinitionGetListInput { + filter?: string; +} + +export type FeatureGroupDefinitionUpdateDto = FeatureGroupDefinitionCreateOrUpdateDto; diff --git a/apps/vue/src/api/feature-management/features/index.ts b/apps/vue/src/api/feature-management/features/index.ts new file mode 100644 index 000000000..dabbc3178 --- /dev/null +++ b/apps/vue/src/api/feature-management/features/index.ts @@ -0,0 +1,20 @@ +import { defAbpHttp } from '/@/utils/http/abp'; +import { FeatureGroupResult, UpdateFeatures, FeatureUpdateByProvider, FeatureGetByProvider } from './model'; + +export const GetByProvider = (provider: FeatureGetByProvider) => { + return defAbpHttp.get({ + url: '/api/feature-management/features', + params: provider, + }); +}; + +export const UpdateByProvider = ( + provider: FeatureUpdateByProvider, + input: UpdateFeatures +) => { + return defAbpHttp.put({ + url: '/api/feature-management/features', + data: input, + params: provider, + }); +}; diff --git a/apps/vue/src/api/feature-management/features/model/index.ts b/apps/vue/src/api/feature-management/features/model/index.ts new file mode 100644 index 000000000..3e13e5840 --- /dev/null +++ b/apps/vue/src/api/feature-management/features/model/index.ts @@ -0,0 +1,44 @@ +export interface Provider { + name: string; + key: string; +} + +export interface Feature { + name: string; + displayName: string; + value: any; + provider: Provider; + description?: string; + valueType: ValueType; + depth: number; + parentName?: string; +} + +export interface FeatureGroup { + name: string; + displayName: string; + features: Feature[]; +} + +export class FeatureGroupResult { + groups!: FeatureGroup[]; +} + +export interface UpdateFeature { + name: string; + value: string; +} + +export interface UpdateFeatures { + features: UpdateFeature[]; +} + +export interface FeatureGetByProvider { + providerName: string; + providerKey: string | null; +} + +export interface FeatureUpdateByProvider { + providerName: string; + providerKey: string | null; +} diff --git a/apps/vue/src/api/feature/feature.ts b/apps/vue/src/api/feature/feature.ts deleted file mode 100644 index 0c0834614..000000000 --- a/apps/vue/src/api/feature/feature.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { defAbpHttp } from '/@/utils/http/abp'; -import { FeatureGroupResult, UpdateFeatures } from './model/featureModel'; - -/** 与 multi-tenancy中不同,此为管理tenant api */ -enum Api { - Get = '/api/feature-management/features', - Update = '/api/feature-management/features', -} - -export const get = (provider: { providerName: string; providerKey: string | null }) => { - return defAbpHttp.get({ - url: Api.Get, - params: provider, - }); -}; - -export const update = ( - provider: { providerName: string; providerKey: string | null }, - input: UpdateFeatures -) => { - return defAbpHttp.put({ - url: Api.Update, - data: input, - params: provider, - }); -}; diff --git a/apps/vue/src/api/feature/model/featureModel.ts b/apps/vue/src/api/feature/model/featureModel.ts deleted file mode 100644 index 721af6f02..000000000 --- a/apps/vue/src/api/feature/model/featureModel.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface Provider { - name: string; - key: string; -} - -export interface Feature { - name: string; - displayName: string; - value: any; - provider: Provider; - description?: string; - valueType: ValueType; - depth: number; - parentName?: string; -} - -export interface FeatureGroup { - name: string; - displayName: string; - features: Feature[]; -} - -export class FeatureGroupResult { - groups!: FeatureGroup[]; -} - -export interface UpdateFeature { - name: string; - value: string; -} - -export interface UpdateFeatures { - features: UpdateFeature[]; -} diff --git a/apps/vue/src/api/messages/notifications.ts b/apps/vue/src/api/messages/notifications.ts index 23224e65f..a2a78af03 100644 --- a/apps/vue/src/api/messages/notifications.ts +++ b/apps/vue/src/api/messages/notifications.ts @@ -10,7 +10,7 @@ import { format } from '/@/utils/strings'; enum Api { GetById = '/api/notifications/my-notifilers/{id}', GetList = '/api/notifications/my-notifilers', - GetAssignableNotifiers = '/api/notifications/notifilers/assignables', + GetAssignableNotifiers = '/api/notifications/assignables', Read = '/api/notifications/my-notifilers/{id}/read', MarkReadState = '/api/notifications/my-notifilers/mark-read-state', } diff --git a/apps/vue/src/api/oss-management/oss.ts b/apps/vue/src/api/oss-management/oss.ts index 15d6adefa..71e887a9d 100644 --- a/apps/vue/src/api/oss-management/oss.ts +++ b/apps/vue/src/api/oss-management/oss.ts @@ -202,9 +202,6 @@ export const deleteObject = (input: GetOssObjectRequest) => { url: Api.DeleteObject, params: input, }, - { - joinParamsToUrl: true, - }, ); }; diff --git a/apps/vue/src/api/settings-management/definitions/index.ts b/apps/vue/src/api/settings-management/definitions/index.ts new file mode 100644 index 000000000..a448ec73d --- /dev/null +++ b/apps/vue/src/api/settings-management/definitions/index.ts @@ -0,0 +1,41 @@ +import { defHttp } from '/@/utils/http/axios'; +import { + SettingDefinitionDto, + SettingDefinitionCreateDto, + SettingDefinitionUpdateDto, + SettingDefinitionGetListInput, + } from './model'; + + +export const GetAsyncByName = (name: string) => { + return defHttp.get({ + url: `/api/setting-management/settings/definitions/${name}`, + }); +}; + +export const GetListAsyncByInput = (input: SettingDefinitionGetListInput) => { + return defHttp.get>({ + url: `/api/setting-management/settings/definitions`, + params: input, + }); +} + +export const CreateAsyncByInput = (input: SettingDefinitionCreateDto) => { + return defHttp.post({ + url: `/api/setting-management/settings/definitions`, + data: input, + }); +}; + +export const UpdateAsyncByNameAndInput = (name: string, input: SettingDefinitionUpdateDto) => { + return defHttp.put({ + url: `/api/setting-management/settings/definitions/${name}`, + data: input, + }); +}; + +export const DeleteOrRestoreAsyncByName = (name: string) => { + return defHttp.delete({ + url: `/api/setting-management/settings/definitions/${name}`, + }); +}; diff --git a/apps/vue/src/api/settings-management/definitions/model/index.ts b/apps/vue/src/api/settings-management/definitions/model/index.ts new file mode 100644 index 000000000..99f89c0ec --- /dev/null +++ b/apps/vue/src/api/settings-management/definitions/model/index.ts @@ -0,0 +1,32 @@ +export interface SettingDefinitionDto extends ExtensibleObject { + name: string; + displayName: string; + description?: string; + defaultValue?: string; + isVisibleToClients: boolean; + providers: string[]; + isInherited: boolean; + isEncrypted: boolean; + isStatic: boolean; +} + +export interface SettingDefinitionGetListInput { + filter?: string; + providerName?: string; +} + +interface SettingDefinitionCreateOrUpdateDto extends IHasConcurrencyStamp, IHasExtraProperties { + displayName: string; + description?: string; + defaultValue?: string; + isInherited: boolean; + isEncrypted: boolean; + isVisibleToClients: boolean; + providers: string[]; +} + +export interface SettingDefinitionCreateDto extends SettingDefinitionCreateOrUpdateDto { + name: string; +} + +export type SettingDefinitionUpdateDto = SettingDefinitionCreateOrUpdateDto; diff --git a/apps/vue/src/api/settings-management/settings/index.ts b/apps/vue/src/api/settings-management/settings/index.ts new file mode 100644 index 000000000..a5e08b54c --- /dev/null +++ b/apps/vue/src/api/settings-management/settings/index.ts @@ -0,0 +1,60 @@ +import { defHttp } from '/@/utils/http/axios'; +import { SettingGroupResult, SettingsUpdate } from './model'; + +enum Api { + GetGlobalSettings = '/api/setting-management/settings/by-global', + SetGlobalSettings = '/api/setting-management/settings/change-global', + GetCurrentTenantSettings = '/api/setting-management/settings/by-current-tenant', + SetCurrentTenantSettings = '/api/setting-management/settings/change-current-tenant', + GetCurrentUserSettings = '/api/setting-management/settings/by-current-user', + SetCurrentUserSettings = '/api/setting-management/settings/change-current-user', + SendTestEmail = '/api/setting-management/settings/send-test-email' +} + +export const getGlobalSettings = () => { + return defHttp.get({ + url: Api.GetGlobalSettings, + }); +}; + +export const setGlobalSettings = (payload: SettingsUpdate) => { + return defHttp.put({ + data: payload, + url: Api.SetGlobalSettings, + }); +}; + +export const getCurrentTenantSettings = () => { + return defHttp.get({ + url: Api.GetCurrentTenantSettings, + }); +}; + +export const setCurrentTenantSettings = (payload: SettingsUpdate) => { + return defHttp.put({ + data: payload, + url: Api.SetCurrentTenantSettings, + }); +}; + +export const getCurrentUserSettings = () => { + return defHttp.get({ + url: Api.GetCurrentUserSettings, + }); +}; + +export const setCurrentUserSettings = (payload: SettingsUpdate) => { + return defHttp.put({ + data: payload, + url: Api.SetCurrentUserSettings, + }); +}; + +export const sendTestEmail = (emailAddress: string) => { + return defHttp.post({ + data: { + emailAddress: emailAddress + }, + url: Api.SendTestEmail, + }); +} diff --git a/apps/vue/src/api/settings/model/settingModel.ts b/apps/vue/src/api/settings-management/settings/model/index.ts similarity index 100% rename from apps/vue/src/api/settings/model/settingModel.ts rename to apps/vue/src/api/settings-management/settings/model/index.ts diff --git a/apps/vue/src/api/settings/settings.ts b/apps/vue/src/api/settings/settings.ts deleted file mode 100644 index 379365b9b..000000000 --- a/apps/vue/src/api/settings/settings.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { defHttp } from '/@/utils/http/axios'; -import { SettingGroupResult, SettingsUpdate } from './model/settingModel'; - -enum Api { - GetGlobalSettings = '/api/setting-management/settings/by-global', - SetGlobalSettings = '/api/setting-management/settings/change-global', - GetCurrentTenantSettings = '/api/setting-management/settings/by-current-tenant', - SetCurrentTenantSettings = '/api/setting-management/settings/change-current-tenant', - GetCurrentUserSettings = '/api/setting-management/settings/by-current-user', - SetCurrentUserSettings = '/api/setting-management/settings/change-current-user', - SendTestEmail = '/api/setting-management/settings/send-test-email' -} - -export const getGlobalSettings = () => { - return defHttp.get({ - url: Api.GetGlobalSettings, - }); -}; - -export const setGlobalSettings = (payload: SettingsUpdate) => { - return defHttp.put({ - data: payload, - url: Api.SetGlobalSettings, - }); -}; - -export const getCurrentTenantSettings = () => { - return defHttp.get({ - url: Api.GetCurrentTenantSettings, - }); -}; - -export const setCurrentTenantSettings = (payload: SettingsUpdate) => { - return defHttp.put({ - data: payload, - url: Api.SetCurrentTenantSettings, - }); -}; - -export const getCurrentUserSettings = () => { - return defHttp.get({ - url: Api.GetCurrentUserSettings, - }); -}; - -export const setCurrentUserSettings = (payload: SettingsUpdate) => { - return defHttp.put({ - data: payload, - url: Api.SetCurrentUserSettings, - }); -}; - -export const sendTestEmail = (emailAddress: string) => { - return defHttp.post({ - data: { - emailAddress: emailAddress - }, - url: Api.SendTestEmail, - }); -} diff --git a/apps/vue/src/api/text-templating/contents/index.ts b/apps/vue/src/api/text-templating/contents/index.ts index ebd2632fa..431d37a97 100644 --- a/apps/vue/src/api/text-templating/contents/index.ts +++ b/apps/vue/src/api/text-templating/contents/index.ts @@ -7,7 +7,7 @@ import { } from './model'; const remoteServiceName = 'AbpTextTemplating'; -const controllerName = 'TextTemplate'; +const controllerName = 'TextTemplateContent'; export const GetAsyncByInput = (input: TextTemplateContentGetInput) => { return defAbpHttp.request({ @@ -15,7 +15,9 @@ export const GetAsyncByInput = (input: TextTemplateContentGetInput) => { controller: controllerName, action: 'GetAsync', uniqueName: 'GetAsyncByInput', - params: input, + params: { + input: input, + }, }); }; diff --git a/apps/vue/src/api/text-templating/definitions/index.ts b/apps/vue/src/api/text-templating/definitions/index.ts index e50cb98df..e9134a9d7 100644 --- a/apps/vue/src/api/text-templating/definitions/index.ts +++ b/apps/vue/src/api/text-templating/definitions/index.ts @@ -44,7 +44,7 @@ export const GetByNameAsyncByName = (name: string) => { }; export const GetListAsyncByInput = (input: TextTemplateDefinitionGetListInput) => { - return defAbpHttp.request>({ + return defAbpHttp.request>({ service: remoteServiceName, controller: controllerName, action: 'GetListAsync', diff --git a/apps/vue/src/api/text-templating/definitions/model/index.ts b/apps/vue/src/api/text-templating/definitions/model/index.ts index 4f51c5ff8..0fe745d88 100644 --- a/apps/vue/src/api/text-templating/definitions/model/index.ts +++ b/apps/vue/src/api/text-templating/definitions/model/index.ts @@ -1,7 +1,6 @@ export interface TextTemplateDefinitionDto { name: string; displayName: string; - formatedDisplayName?: string; defaultCultureName?: string; isInlineLocalized: boolean; isLayout: boolean; @@ -27,6 +26,8 @@ export interface TextTemplateDefinitionCreateDto extends TextTemplateDefinitionC export interface TextTemplateDefinitionUpdateDto extends TextTemplateDefinitionCreateOrUpdateDto, IHasConcurrencyStamp { } -export interface TextTemplateDefinitionGetListInput extends PagedAndSortedResultRequestDto { +export interface TextTemplateDefinitionGetListInput { filter?: string; + isLayout?: boolean; + isStatic?: boolean; } \ No newline at end of file diff --git a/apps/vue/src/api/webhooks/definitions/groups/index.ts b/apps/vue/src/api/webhooks/definitions/groups/index.ts new file mode 100644 index 000000000..60f5fe1d8 --- /dev/null +++ b/apps/vue/src/api/webhooks/definitions/groups/index.ts @@ -0,0 +1,41 @@ +import { defHttp } from '/@/utils/http/axios'; +import { + WebhookGroupDefinitionDto, + WebhookGroupDefinitionCreateDto, + WebhookGroupDefinitionUpdateDto, + WebhookGroupDefinitionGetListInput, + } from './model'; + + +export const GetAsyncByName = (name: string) => { + return defHttp.get({ + url: `/api/webhooks/definitions/groups/${name}`, + }); +}; + +export const GetListAsyncByInput = (input: WebhookGroupDefinitionGetListInput) => { + return defHttp.get>({ + url: `/api/webhooks/definitions/groups`, + params: input, + }); +} + +export const CreateAsyncByInput = (input: WebhookGroupDefinitionCreateDto) => { + return defHttp.post({ + url: `/api/webhooks/definitions/groups`, + data: input, + }); +}; + +export const UpdateAsyncByNameAndInput = (name: string, input: WebhookGroupDefinitionUpdateDto) => { + return defHttp.put({ + url: `/api/webhooks/definitions/groups/${name}`, + data: input, + }); +}; + +export const DeleteAsyncByName = (name: string) => { + return defHttp.delete({ + url: `/api/webhooks/definitions/groups/${name}`, + }); +}; diff --git a/apps/vue/src/api/webhooks/definitions/groups/model/index.ts b/apps/vue/src/api/webhooks/definitions/groups/model/index.ts new file mode 100644 index 000000000..af6c24d15 --- /dev/null +++ b/apps/vue/src/api/webhooks/definitions/groups/model/index.ts @@ -0,0 +1,19 @@ +interface WebhookGroupDefinitionCreateOrUpdateDto extends IHasExtraProperties { + displayName: string; +} + +export interface WebhookGroupDefinitionCreateDto extends WebhookGroupDefinitionCreateOrUpdateDto { + name: string; +} + +export interface WebhookGroupDefinitionDto extends IHasExtraProperties { + name: string; + displayName: string; + isStatic: boolean; +} + +export interface WebhookGroupDefinitionGetListInput { + filter?: string; +} + +export type WebhookGroupDefinitionUpdateDto = WebhookGroupDefinitionCreateOrUpdateDto; diff --git a/apps/vue/src/api/webhooks/definitions/webhooks/index.ts b/apps/vue/src/api/webhooks/definitions/webhooks/index.ts new file mode 100644 index 000000000..d54a54543 --- /dev/null +++ b/apps/vue/src/api/webhooks/definitions/webhooks/index.ts @@ -0,0 +1,41 @@ +import { defHttp } from '/@/utils/http/axios'; +import { + WebhookDefinitionDto, + WebhookDefinitionCreateDto, + WebhookDefinitionUpdateDto, + WebhookDefinitionGetListInput, + } from './model'; + + +export const GetAsyncByName = (name: string) => { + return defHttp.get({ + url: `/api/webhooks/definitions/${name}`, + }); +}; + +export const GetListAsyncByInput = (input: WebhookDefinitionGetListInput) => { + return defHttp.get>({ + url: `/api/webhooks/definitions`, + params: input, + }); +} + +export const CreateAsyncByInput = (input: WebhookDefinitionCreateDto) => { + return defHttp.post({ + url: `/api/webhooks/definitions`, + data: input, + }); +}; + +export const UpdateAsyncByNameAndInput = (name: string, input: WebhookDefinitionUpdateDto) => { + return defHttp.put({ + url: `/api/webhooks/definitions/${name}`, + data: input, + }); +}; + +export const DeleteAsyncByName = (name: string) => { + return defHttp.delete({ + url: `/api/webhooks/definitions/${name}`, + }); +}; diff --git a/apps/vue/src/api/webhooks/definitions/webhooks/model/index.ts b/apps/vue/src/api/webhooks/definitions/webhooks/model/index.ts new file mode 100644 index 000000000..288b405d0 --- /dev/null +++ b/apps/vue/src/api/webhooks/definitions/webhooks/model/index.ts @@ -0,0 +1,28 @@ +interface WebhookDefinitionCreateOrUpdateDto extends IHasExtraProperties { + displayName: string; + description?: string; + isEnabled: boolean; + requiredFeatures: string[]; +} + +export interface WebhookDefinitionDto extends IHasExtraProperties { + groupName: string; + name: string; + displayName: string; + description?: string; + isEnabled: boolean; + isStatic: boolean; + requiredFeatures: string[]; +} + +export interface WebhookDefinitionCreateDto extends WebhookDefinitionCreateOrUpdateDto { + groupName: string; + name: string; +} + +export interface WebhookDefinitionGetListInput { + filter?: string; + groupName?: string; +} + +export type WebhookDefinitionUpdateDto = WebhookDefinitionCreateOrUpdateDto; \ No newline at end of file diff --git a/apps/vue/src/api/webhooks/model/sendAttemptsModel.ts b/apps/vue/src/api/webhooks/model/sendAttemptsModel.ts deleted file mode 100644 index 43c62d3e2..000000000 --- a/apps/vue/src/api/webhooks/model/sendAttemptsModel.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { HttpStatusCode } from '/@/enums/httpEnum'; - -export interface WebhookEvent { - tenantId?: string; - webhookName: string; - data: string; - creationTime: Date; -} - -export interface WebhookSendAttempt { - id: string; - tenantId?: string; - webhookEventId: string; - webhookSubscriptionId: string; - response: string; - responseStatusCode?: HttpStatusCode; - creationTime: Date; - lastModificationTime?: Date; - sendExactSameData: boolean; - requestHeaders: Record; - responseHeaders: Record; - webhookEvent: WebhookEvent; -} - -export interface WebhookSendAttemptGetListInput extends PagedAndSortedResultRequestDto { - filter?: string; - webhookEventId?: string; - subscriptionId?: string; - responseStatusCode?: HttpStatusCode; - beginCreationTime?: Date; - endCreationTime?: Date; -} diff --git a/apps/vue/src/api/webhooks/model/subscriptionsModel.ts b/apps/vue/src/api/webhooks/model/subscriptionsModel.ts deleted file mode 100644 index 52d81ee9f..000000000 --- a/apps/vue/src/api/webhooks/model/subscriptionsModel.ts +++ /dev/null @@ -1,45 +0,0 @@ -export interface WebhookSubscription extends CreationAuditedEntityDto, IHasConcurrencyStamp { - tenantId?: string; - webhookUri: string; - description?: string; - secret: string; - isActive: boolean; - webhooks: string[]; - headers: Dictionary; -} - -export interface WebhookSubscriptionCreateOrUpdate { - webhookUri: string; - description?: string; - secret: string; - isActive: boolean; - webhooks: string[]; - headers: Dictionary; -} - -export type CreateWebhookSubscription = WebhookSubscriptionCreateOrUpdate; - -export interface UpdateWebhookSubscription extends WebhookSubscriptionCreateOrUpdate , IHasConcurrencyStamp {}; - -export interface WebhookAvailable { - name: string; - displayName: string; - description: string; -} - -export interface WebhookAvailableGroup { - name: string; - displayName: string; - webhooks: WebhookAvailable[]; -} - -export interface WebhookSubscriptionGetListInput extends PagedAndSortedResultRequestDto { - filter?: string; - tenantId?: string; - webhookUri?: string; - secret?: string; - isActive?: boolean; - webhooks?: string; - beginCreationTime?: Date; - endCreationTime?: Date; -} diff --git a/apps/vue/src/api/webhooks/send-attempts.ts b/apps/vue/src/api/webhooks/send-attempts.ts deleted file mode 100644 index 72d2a837c..000000000 --- a/apps/vue/src/api/webhooks/send-attempts.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { defAbpHttp } from '/@/utils/http/abp'; -import { WebhookSendAttempt, WebhookSendAttemptGetListInput } from './model/sendAttemptsModel'; - -const remoteServiceName = 'WebhooksManagement'; -const controllerName = 'WebhookSendRecord'; - -export const getById = (id: string) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'GetAsync', - params: { - id: id, - }, - }); -}; - -export const deleteById = (id: string) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'DeleteAsync', - params: { - id: id, - }, - }); -} - -export const deleteMany = (keys: string[]) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'DeleteManyAsync', - uniqueName: 'DeleteManyAsyncByInput', - data: { - recordIds: keys, - }, - }); -} - -export const getList = (input: WebhookSendAttemptGetListInput) => { - return defAbpHttp.request>({ - service: remoteServiceName, - controller: controllerName, - action: 'GetListAsync', - params: { - input: input, - }, - }); -}; - -export const resend = (id: string) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'ResendAsync', - params: { - id: id, - }, - }); -} - -export const resendMany = (keys: string[]) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'ResendManyAsync', - uniqueName: 'ResendManyAsyncByInput', - data: { - recordIds: keys, - }, - }); -} diff --git a/apps/vue/src/api/webhooks/send-attempts/index.ts b/apps/vue/src/api/webhooks/send-attempts/index.ts new file mode 100644 index 000000000..fcdc98f99 --- /dev/null +++ b/apps/vue/src/api/webhooks/send-attempts/index.ts @@ -0,0 +1,46 @@ +import { defAbpHttp } from '/@/utils/http/abp'; +import { + WebhookSendAttempt, + WebhookSendAttemptGetListInput, + WebhookSendRecordDeleteManyInput, + WebhookSendRecordResendManyInput, +} from './model'; + +export const GetAsyncById = (id: string) => { + return defAbpHttp.get({ + url: `/api/webhooks/send-attempts/${id}`, + }); +}; + +export const DeleteAsyncById = (id: string) => { + return defAbpHttp.delete({ + url: `/api/webhooks/send-attempts/${id}`, + }); +}; + +export const DeleteManyAsyncByInput = (input: WebhookSendRecordDeleteManyInput) => { + return defAbpHttp.delete({ + url: `/api/webhooks/send-attempts/delete-many`, + data: input, + }); +}; + +export const GetListAsyncByInput = (input: WebhookSendAttemptGetListInput) => { + return defAbpHttp.get>({ + url: `/api/webhooks/send-attempts`, + params: input, + }); +}; + +export const ResendAsyncById = (id: string) => { + return defAbpHttp.post({ + url: `/api/webhooks/send-attempts/${id}/resend`, + }); +}; + +export const ResendManyAsyncByInput = (input: WebhookSendRecordResendManyInput) => { + return defAbpHttp.post({ + url: `/api/webhooks/send-attempts/resend-many`, + data: input, + }); +}; diff --git a/apps/vue/src/api/webhooks/send-attempts/model/index.ts b/apps/vue/src/api/webhooks/send-attempts/model/index.ts new file mode 100644 index 000000000..2cdebd406 --- /dev/null +++ b/apps/vue/src/api/webhooks/send-attempts/model/index.ts @@ -0,0 +1,40 @@ +import { HttpStatusCode } from '/@/enums/httpEnum'; + +export interface WebhookEvent { + tenantId?: string; + webhookName: string; + data: string; + creationTime: Date; +} + +export interface WebhookSendAttempt { + id: string; + tenantId?: string; + webhookEventId: string; + webhookSubscriptionId: string; + response: string; + responseStatusCode?: HttpStatusCode; + creationTime: Date; + lastModificationTime?: Date; + sendExactSameData: boolean; + requestHeaders: Record; + responseHeaders: Record; + webhookEvent: WebhookEvent; +} + +export interface WebhookSendAttemptGetListInput extends PagedAndSortedResultRequestDto { + filter?: string; + webhookEventId?: string; + subscriptionId?: string; + responseStatusCode?: HttpStatusCode; + beginCreationTime?: Date; + endCreationTime?: Date; +} + +export interface WebhookSendRecordDeleteManyInput { + recordIds: string[]; +} + +export interface WebhookSendRecordResendManyInput { + recordIds: string[]; +} diff --git a/apps/vue/src/api/webhooks/subscriptions.ts b/apps/vue/src/api/webhooks/subscriptions.ts deleted file mode 100644 index 64a31a03e..000000000 --- a/apps/vue/src/api/webhooks/subscriptions.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { defAbpHttp } from '/@/utils/http/abp'; -import { - WebhookSubscription, - WebhookAvailableGroup, - CreateWebhookSubscription, - UpdateWebhookSubscription, - WebhookSubscriptionGetListInput, -} from './model/subscriptionsModel'; - -const remoteServiceName = 'WebhooksManagement'; -const controllerName = 'WebhookSubscription'; - -export const create = (input: CreateWebhookSubscription) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'CreateAsync', - data: input, - }); -}; - -export const update = (id: string, input: UpdateWebhookSubscription) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'UpdateAsync', - data: input, - params: { - id: id, - }, - }); -}; - -export const getById = (id: string) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'GetAsync', - params: { - id: id, - }, - }); -}; - -export const deleteById = (id: string) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'DeleteAsync', - params: { - id: id, - }, - }); -}; - -export const deleteMany = (keys: string[]) => { - return defAbpHttp.request({ - service: remoteServiceName, - controller: controllerName, - action: 'DeleteManyAsync', - uniqueName: 'DeleteManyAsyncByInput', - data: { - recordIds: keys, - }, - }); -}; - -export const getList = (input: WebhookSubscriptionGetListInput) => { - return defAbpHttp.request>({ - service: remoteServiceName, - controller: controllerName, - action: 'GetListAsync', - params: { - input: input, - }, - }); -}; - -export const getAllAvailableWebhooks = () => { - return defAbpHttp.request>({ - service: remoteServiceName, - controller: controllerName, - action: 'GetAllAvailableWebhooksAsync', - }); -}; diff --git a/apps/vue/src/api/webhooks/subscriptions/index.ts b/apps/vue/src/api/webhooks/subscriptions/index.ts new file mode 100644 index 000000000..e0793e602 --- /dev/null +++ b/apps/vue/src/api/webhooks/subscriptions/index.ts @@ -0,0 +1,55 @@ +import { defAbpHttp } from '/@/utils/http/abp'; +import { + WebhookSubscription, + WebhookAvailableGroup, + CreateWebhookSubscription, + UpdateWebhookSubscription, + WebhookSubscriptionGetListInput, + WebhookSubscriptionDeleteManyInput, +} from './model'; + +export const CreateAsyncByInput = (input: CreateWebhookSubscription) => { + return defAbpHttp.post({ + url: `/api/webhooks/subscriptions`, + data: input, + }); +}; + +export const UpdateAsyncByIdAndInput = (id: string, input: UpdateWebhookSubscription) => { + return defAbpHttp.put({ + url: `/api/webhooks/subscriptions/${id}`, + data: input, + }); +}; + +export const GetAsyncById = (id: string) => { + return defAbpHttp.get({ + url: `/api/webhooks/subscriptions/${id}`, + }); +}; + +export const DeleteAsyncById = (id: string) => { + return defAbpHttp.delete({ + url: `/api/webhooks/subscriptions/${id}`, + }); +}; + +export const DeleteManyAsyncByInput = (input: WebhookSubscriptionDeleteManyInput) => { + return defAbpHttp.delete({ + url: `/api/webhooks/subscriptions/delete-many`, + data: input, + }); +}; + +export const GetListAsyncByInput = (input: WebhookSubscriptionGetListInput) => { + return defAbpHttp.get>({ + url: `/api/webhooks/subscriptions`, + params: input, + }); +}; + +export const GetAllAvailableWebhooksAsync = () => { + return defAbpHttp.get>({ + url: `/api/webhooks/subscriptions/availables`, + }); +}; diff --git a/apps/vue/src/api/webhooks/subscriptions/model/index.ts b/apps/vue/src/api/webhooks/subscriptions/model/index.ts new file mode 100644 index 000000000..15ef8d5fb --- /dev/null +++ b/apps/vue/src/api/webhooks/subscriptions/model/index.ts @@ -0,0 +1,49 @@ +export interface WebhookSubscription extends CreationAuditedEntityDto, IHasConcurrencyStamp { + tenantId?: string; + webhookUri: string; + description?: string; + secret: string; + isActive: boolean; + webhooks: string[]; + headers: Dictionary; +} + +export interface WebhookSubscriptionCreateOrUpdate { + webhookUri: string; + description?: string; + secret: string; + isActive: boolean; + webhooks: string[]; + headers: Dictionary; +} + +export type CreateWebhookSubscription = WebhookSubscriptionCreateOrUpdate; + +export interface UpdateWebhookSubscription extends WebhookSubscriptionCreateOrUpdate , IHasConcurrencyStamp {}; + +export interface WebhookAvailable { + name: string; + displayName: string; + description: string; +} + +export interface WebhookAvailableGroup { + name: string; + displayName: string; + webhooks: WebhookAvailable[]; +} + +export interface WebhookSubscriptionGetListInput extends PagedAndSortedResultRequestDto { + filter?: string; + tenantId?: string; + webhookUri?: string; + secret?: string; + isActive?: boolean; + webhooks?: string; + beginCreationTime?: Date; + endCreationTime?: Date; +} + +export interface WebhookSubscriptionDeleteManyInput { + recordIds: string[]; +} diff --git a/apps/vue/src/components/Abp/ExtraPropertyDictionary/ExtraPropertyDictionary.vue b/apps/vue/src/components/Abp/ExtraPropertyDictionary/ExtraPropertyDictionary.vue new file mode 100644 index 000000000..afcc5fd45 --- /dev/null +++ b/apps/vue/src/components/Abp/ExtraPropertyDictionary/ExtraPropertyDictionary.vue @@ -0,0 +1,271 @@ + + + + + diff --git a/apps/vue/src/components/Abp/ExtraPropertyDictionary/index.ts b/apps/vue/src/components/Abp/ExtraPropertyDictionary/index.ts new file mode 100644 index 000000000..5d0b25b39 --- /dev/null +++ b/apps/vue/src/components/Abp/ExtraPropertyDictionary/index.ts @@ -0,0 +1,3 @@ +import ExtraPropertyDictionary from './ExtraPropertyDictionary.vue'; + +export { ExtraPropertyDictionary }; diff --git a/apps/vue/src/components/Abp/FeatureModal/hooks/useFeatures.ts b/apps/vue/src/components/Abp/FeatureModal/hooks/useFeatures.ts new file mode 100644 index 000000000..e159f7929 --- /dev/null +++ b/apps/vue/src/components/Abp/FeatureModal/hooks/useFeatures.ts @@ -0,0 +1,145 @@ +import type { Ref } from 'vue'; + +import { watch, ref, unref } from 'vue'; +import { message } from 'ant-design-vue'; +import { useLocalization } from '/@/hooks/abp/useLocalization'; +import { useValidation } from '/@/hooks/abp/useValidation'; +import { FeatureGroup } from '/@/api/feature-management/features/model'; +import { GetByProvider, UpdateByProvider } from '/@/api/feature-management/features'; +import { ReturnInnerMethods } from '/@/components/Modal'; + +interface UseFeature { + providerName: Ref; + providerKey: Ref; + formRel: Ref; + modalMethods: ReturnInnerMethods; +} + +export function useFeatures({ providerName, providerKey, formRel, modalMethods }: UseFeature) { + const { L } = useLocalization('AbpFeatureManagement'); + const { ruleCreator } = useValidation(); + const featureGroup = ref<{ groups: FeatureGroup[] }>({ + groups: [], + }); + const featureGroupKey = ref(0); + + watch( + () => unref(providerKey), + (key) => { + if (key !== undefined) { + const form = unref(formRel); + form.resetFields(); + onGroupChange(0); + GetByProvider({ + providerName: unref(providerName), + providerKey: key, + }).then((res) => { + featureGroup.value.groups = mapFeatures(res.groups); + }); + } + }, + ); + + function getFeatures(groups: FeatureGroup[]) { + const features: { name: string; value: string }[] = []; + groups.forEach((g) => { + g.features.forEach((f) => { + if (f.value !== null) { + features.push({ + name: f.name, + value: String(f.value), + }); + } + }); + }); + return features; + } + + function mapFeatures(groups: FeatureGroup[]) { + groups.forEach((g) => { + g.features.forEach((f) => { + switch (f.valueType?.validator.name) { + case 'BOOLEAN': + f.value = String(f.value).toLocaleLowerCase() === 'true'; + break; + case 'NUMERIC': + f.value = Number(f.value); + break; + } + }); + }); + return groups; + } + + function validator(field: string, validator: Validator) { + const featureRules: { [key: string]: any }[] = new Array<{ [key: string]: any }>(); + if (validator.properties) { + switch (validator.name) { + case 'NUMERIC': + featureRules.push( + ...ruleCreator.fieldMustBeetWeen({ + name: field, + start: Number(validator.properties.MinValue), + end: Number(validator.properties.MaxValue), + trigger: 'change', + }), + ); + break; + case 'STRING': + if ( + validator.properties.AllowNull && + validator.properties.AllowNull.toLowerCase() === 'true' + ) { + featureRules.push( + ...ruleCreator.fieldRequired({ + name: field, + trigger: 'blur', + }), + ); + } + featureRules.push( + ...ruleCreator.fieldMustBeStringWithMinimumLengthAndMaximumLength({ + name: field, + minimum: Number(validator.properties.MinValue), + maximum: Number(validator.properties.MaxValue), + trigger: 'blur', + }), + ); + break; + default: + break; + } + } + return featureRules; + } + + function onGroupChange(activeKey) { + featureGroupKey.value = activeKey; + } + + function handleSubmit() { + const form = unref(formRel); + form.validate().then(() => { + UpdateByProvider( + { + providerName: unref(providerName), + providerKey: unref(providerKey), + }, + { + features: getFeatures(unref(featureGroup).groups), + }, + ).then(() => { + modalMethods.closeModal(); + message.success(L('Successful')); + }); + }); + } + + return { + featureGroup, + featureGroupKey, + validator, + handleSubmit, + onGroupChange, + }; +} diff --git a/apps/vue/src/views/feature/index.ts b/apps/vue/src/components/Abp/FeatureModal/index.ts similarity index 100% rename from apps/vue/src/views/feature/index.ts rename to apps/vue/src/components/Abp/FeatureModal/index.ts diff --git a/apps/vue/src/components/Abp/FeatureModal/src/FeatureModal.vue b/apps/vue/src/components/Abp/FeatureModal/src/FeatureModal.vue new file mode 100644 index 000000000..3d1c3b6a1 --- /dev/null +++ b/apps/vue/src/components/Abp/FeatureModal/src/FeatureModal.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/apps/vue/src/components/Abp/LocalizableInput/LocalizableInput.vue b/apps/vue/src/components/Abp/LocalizableInput/LocalizableInput.vue new file mode 100644 index 000000000..64798b8cd --- /dev/null +++ b/apps/vue/src/components/Abp/LocalizableInput/LocalizableInput.vue @@ -0,0 +1,168 @@ + + + + + \ No newline at end of file diff --git a/apps/vue/src/components/Abp/LocalizableInput/index.ts b/apps/vue/src/components/Abp/LocalizableInput/index.ts new file mode 100644 index 000000000..20e36d69d --- /dev/null +++ b/apps/vue/src/components/Abp/LocalizableInput/index.ts @@ -0,0 +1,3 @@ +import LocalizableInput from './LocalizableInput.vue'; + +export { LocalizableInput }; diff --git a/apps/vue/src/components/Abp/LocalizableInput/props.ts b/apps/vue/src/components/Abp/LocalizableInput/props.ts new file mode 100644 index 000000000..e147f5c79 --- /dev/null +++ b/apps/vue/src/components/Abp/LocalizableInput/props.ts @@ -0,0 +1,9 @@ +export interface Props { + resources: string[], + value: string, +} + +export interface Resource { + name: string, + value: string, +} diff --git a/apps/vue/src/components/Abp/StringValueType/StringValueTypeInput.vue b/apps/vue/src/components/Abp/StringValueType/StringValueTypeInput.vue new file mode 100644 index 000000000..8cbbd480e --- /dev/null +++ b/apps/vue/src/components/Abp/StringValueType/StringValueTypeInput.vue @@ -0,0 +1,551 @@ + + + + + \ No newline at end of file diff --git a/apps/vue/src/components/Abp/StringValueType/index.ts b/apps/vue/src/components/Abp/StringValueType/index.ts new file mode 100644 index 000000000..3814e539c --- /dev/null +++ b/apps/vue/src/components/Abp/StringValueType/index.ts @@ -0,0 +1,3 @@ +import StringValueTypeInput from './StringValueTypeInput.vue'; + +export { StringValueTypeInput }; diff --git a/apps/vue/src/components/Abp/StringValueType/validator.ts b/apps/vue/src/components/Abp/StringValueType/validator.ts new file mode 100644 index 000000000..313c1348f --- /dev/null +++ b/apps/vue/src/components/Abp/StringValueType/validator.ts @@ -0,0 +1,134 @@ +import { isBoolean, isNullOrUnDef, isNumber } from "/@/utils/is"; +import { isNullOrWhiteSpace } from "/@/utils/strings"; + +export interface ValueValidator { + name: string; + properties: Dictionary; + + isValid(value?: any): boolean; +} + +export class AlwaysValidValueValidator implements ValueValidator { + name = "NULL"; + properties: Dictionary; + constructor() { + this.properties = {}; + } + isValid(_value?: any): boolean { + return true; + } +} + +export class BooleanValueValidator implements ValueValidator { + name = "BOOLEAN"; + properties: Dictionary; + constructor() { + this.properties = {}; + } + isValid(value?: any): boolean { + if (value === undefined) return true; + if (isBoolean(value)) return true; + const bolString = String(value).toLowerCase(); + if (bolString === 'true' || bolString === 'false') return true; + return false; + } +} + +export class NumericValueValidator implements ValueValidator { + name = "NUMERIC"; + properties: Dictionary; + constructor() { + this.properties = {}; + } + + get minValue(): number | undefined { + return Number(this.properties['MinValue']); + } + + set minValue(value: number) { + this.properties['MinValue'] = value; + } + + get maxValue(): number | undefined { + return Number(this.properties['MaxValue']); + } + + set maxValue(value: number) { + this.properties['MaxValue'] = value; + } + + isValid(value?: any): boolean { + if (!value) return true; + if (isNumber(value)) return this._isValidInternal(value); + const numString = String(value); + if (!isNullOrUnDef(numString)) { + const num = Number(numString); + if (num) return this._isValidInternal(num); + } + return false; + } + + _isValidInternal(value: number): boolean { + if (this.minValue && value < this.minValue) return false; + if (this.maxValue && value > this.maxValue) return false; + return true; + } +} + +export class StringValueValidator implements ValueValidator { + name = "STRING"; + properties: Dictionary; + constructor() { + this.properties = {}; + } + + get allowNull(): boolean { + return String(this.properties['AllowNull'] ?? 'true')?.toLowerCase() === 'true'; + } + + set allowNull(value: boolean) { + this.properties['AllowNull'] = value; + } + + get minLength(): number | undefined { + return Number(this.properties['MinLength']); + } + + set minLength(value: number) { + this.properties['MinLength'] = value; + } + + get maxLength(): number | undefined { + return Number(this.properties['MaxLength']); + } + + set maxLength(value: number) { + this.properties['MaxLength'] = value; + } + + get regularExpression(): string { + return String(this.properties['RegularExpression'] ?? ''); + } + + set regularExpression(value: string) { + this.properties['RegularExpression'] = value + } + + isValid(value?: any): boolean { + console.log(value); + console.log(this.allowNull); + if (!this.allowNull && isNullOrUnDef(value)) return false; + const valueString = String(value); + console.log(valueString); + if (!this.allowNull && isNullOrWhiteSpace(valueString.trim())) return false; + console.log(valueString.length); + console.log(this.maxLength); + console.log(this.maxLength); + if (this.minLength && this.minLength > 0 && valueString.length < this.minLength) return false; + if (this.maxLength && this.maxLength > 0 && valueString.length > this.maxLength) return false; + if (!isNullOrWhiteSpace(this.regularExpression)) { + return RegExp(this.regularExpression).test(valueString); + } + return true; + } +} \ No newline at end of file diff --git a/apps/vue/src/components/Abp/StringValueType/valueType.ts b/apps/vue/src/components/Abp/StringValueType/valueType.ts new file mode 100644 index 000000000..e12b03ef1 --- /dev/null +++ b/apps/vue/src/components/Abp/StringValueType/valueType.ts @@ -0,0 +1,122 @@ +import { + ValueValidator, + AlwaysValidValueValidator, + BooleanValueValidator, + NumericValueValidator, + StringValueValidator, +} from "./validator"; + +export interface StringValueType { + name: string; + properties: Dictionary; + validator: ValueValidator; +} + +export interface SelectionStringValueItem { + value: string; + displayText: LocalizableStringInfo; +} + +export interface SelectionStringValueItemSource { + items: SelectionStringValueItem[]; +} + +export class FreeTextStringValueType implements StringValueType { + name = "FreeTextStringValueType"; + properties: Dictionary; + validator: ValueValidator; + constructor(validator?: ValueValidator) { + this.properties = {}; + this.validator = validator ?? new AlwaysValidValueValidator(); + } +} + +export class ToggleStringValueType implements StringValueType { + name = "ToggleStringValueType"; + properties: Dictionary; + validator: ValueValidator; + constructor(validator?: ValueValidator) { + this.properties = {}; + this.validator = validator ?? new BooleanValueValidator(); + } +} + +export class SelectionStringValueType implements StringValueType { + name = "SelectionStringValueType"; + properties: Dictionary; + validator: ValueValidator; + itemSource: SelectionStringValueItemSource; + constructor(validator?: ValueValidator) { + this.properties = {}; + this.itemSource = { + items: [], + }; + this.validator = validator ?? new AlwaysValidValueValidator(); + } +} + +class StringValueTypeSerializer { + serialize(value: StringValueType): string { + // console.log('serialize', value); + const valueTypeString = JSON.stringify(value); + // console.log('deserialize to obj', valueTypeString); + return valueTypeString; + } + + deserialize(value: string): StringValueType { + let valueType: StringValueType; + const valueTypeObj = JSON.parse(value); + // console.log('deserialize', value); + // console.log('deserialize to obj', valueTypeObj); + switch (valueTypeObj.name) { + case 'TOGGLE': + case 'ToggleStringValueType': + // console.log('deserialize valueType to TOGGLE', valueTypeObj.name); + valueType = new ToggleStringValueType(); + break; + case 'SELECTION': + case 'SelectionStringValueType': + // console.log('deserialize valueType to SELECTION', valueTypeObj.name); + valueType = new SelectionStringValueType(); + (valueType as SelectionStringValueType).itemSource = valueTypeObj.itemSource; + break; + default: + case 'FREE_TEXT': + case 'FreeTextStringValueType': + // console.log('deserialize valueType to FREE_TEXT or default', valueTypeObj.name); + valueType = new FreeTextStringValueType(); + break; + } + valueType.properties = valueTypeObj.properties; + valueType.validator = this._deserializeValidator(valueTypeObj.validator); + return valueType; + } + + _deserializeValidator(validator: any): ValueValidator { + let convertValidator: ValueValidator = new AlwaysValidValueValidator(); + if (validator.name) { + switch (validator.name) { + case 'BOOLEAN': + // console.log('deserialize validator to BOOLEAN', validator.name); + convertValidator = new BooleanValueValidator(); + break; + case 'NUMERIC': + // console.log('deserialize validator to NUMERIC', validator.name); + convertValidator = new NumericValueValidator(); + break; + case 'STRING': + // console.log('deserialize validator to STRING', validator.name); + convertValidator = new StringValueValidator(); + break; + case 'NULL': + // console.log('deserialize validator to NULL', validator.name); + convertValidator = new AlwaysValidValueValidator(); + break; + } + } + convertValidator.properties = validator.properties; + return convertValidator; + } +} + +export const valueTypeSerializer = new StringValueTypeSerializer(); \ No newline at end of file diff --git a/apps/vue/src/components/Abp/index.ts b/apps/vue/src/components/Abp/index.ts new file mode 100644 index 000000000..c63eca4db --- /dev/null +++ b/apps/vue/src/components/Abp/index.ts @@ -0,0 +1,4 @@ +export * from './ExtraPropertyDictionary'; +export * from './FeatureModal'; +export * from './LocalizableInput'; +export * from './StringValueType'; \ No newline at end of file diff --git a/apps/vue/src/components/Form/src/componentMap.ts b/apps/vue/src/components/Form/src/componentMap.ts index 588cfed12..dc399bcce 100644 --- a/apps/vue/src/components/Form/src/componentMap.ts +++ b/apps/vue/src/components/Form/src/componentMap.ts @@ -34,6 +34,10 @@ import { IconPicker } from '/@/components/Icon'; import { CountdownInput } from '/@/components/CountDown'; import { Input as BInput } from '/@/components/Input'; import { CodeEditorX } from '/@/components/CodeEditor'; +import { + ExtraPropertyDictionary, + LocalizableInput +} from '/@/components/Abp'; const componentMap = new Map(); const customComponentMap = new Map(); @@ -76,6 +80,9 @@ componentMap.set('WeekPicker', DatePicker.WeekPicker); componentMap.set('TimePicker', TimePicker); componentMap.set('Divider', Divider); +componentMap.set('ExtraPropertyDictionary', ExtraPropertyDictionary); +componentMap.set('LocalizableInput', LocalizableInput); + customComponentMap.forEach((v, k) => { componentMap.set(k, v); }); diff --git a/apps/vue/src/components/Form/src/types/index.ts b/apps/vue/src/components/Form/src/types/index.ts index 9ea4997bf..8182d7c9a 100644 --- a/apps/vue/src/components/Form/src/types/index.ts +++ b/apps/vue/src/components/Form/src/types/index.ts @@ -115,4 +115,6 @@ export type ComponentType = | 'Rate' | 'Divider' | 'ApiTransfer' - | 'CodeEditorX'; + | 'CodeEditorX' + | 'ExtraPropertyDictionary' + | 'LocalizableInput'; diff --git a/apps/vue/src/components/Permission/src/PermissionModal.vue b/apps/vue/src/components/Permission/src/PermissionModal.vue index a9574966d..bc3e4d1c9 100644 --- a/apps/vue/src/components/Permission/src/PermissionModal.vue +++ b/apps/vue/src/components/Permission/src/PermissionModal.vue @@ -5,63 +5,57 @@ @register="registerModal" :title="getIdentity" :width="800" - :min-height="600" + :min-height="400" :mask-closable="false" + :can-fullscreen="false" @ok="handleSubmit" @visible-change="handleVisibleChange" > - - - {{ L('SelectAllInAllTabs') }} - - - - - {{ L('SelectAllInAllTabs') }} + + + + + {{ L('SelectAllInThisTab') }} - - {{ L('SelectAllInThisTab') }} - - - - - - - + + + + + + + diff --git a/apps/vue/src/components/SettingManagement/src/SettingForm.vue b/apps/vue/src/components/SettingManagement/src/SettingForm.vue index b86422370..d6a87e0c1 100644 --- a/apps/vue/src/components/SettingManagement/src/SettingForm.vue +++ b/apps/vue/src/components/SettingManagement/src/SettingForm.vue @@ -115,7 +115,7 @@ } from 'ant-design-vue'; import { Input as BInput } from '/@/components/Input'; import { formatToDate } from '/@/utils/dateUtil'; - import { SettingGroup, SettingsUpdate } from '/@/api/settings/model/settingModel'; + import { SettingGroup, SettingsUpdate } from '/@/api/settings-management/settings/model'; const CollapsePanel = Collapse.Panel; const FormItem = Form.Item; diff --git a/apps/vue/src/hooks/abp/useDefineSettings.ts b/apps/vue/src/hooks/abp/useDefineSettings.ts index c30827d9e..ff971ded7 100644 --- a/apps/vue/src/hooks/abp/useDefineSettings.ts +++ b/apps/vue/src/hooks/abp/useDefineSettings.ts @@ -1,5 +1,5 @@ import { computed, onMounted } from 'vue'; -import { SettingGroup } from '/@/api/settings/model/settingModel'; +import { SettingGroup } from '/@/api/settings-management/settings/model'; import { useSettingManagementStoreWithOut } from '/@/store/modules/settings'; import { useSettings as useAbpSettings, ISettingProvider } from '/@/hooks/abp/useSettings'; diff --git a/apps/vue/src/hooks/abp/useFeatures.ts b/apps/vue/src/hooks/abp/useFeatures.ts index 4c9ce0d00..1bb774879 100644 --- a/apps/vue/src/hooks/abp/useFeatures.ts +++ b/apps/vue/src/hooks/abp/useFeatures.ts @@ -9,9 +9,10 @@ type FeatureValue = NameValue; interface IFeatureChecker { /** * 是否启用特性 - * @param name 特性名称 + * @param featureNames 特性名称 + * @param requiresAll 是否全部符合 */ - isEnabled(name: string): boolean; + isEnabled(featureNames: string | string[], requiresAll?: boolean): boolean; /** * 获取特性值 * @param name 特性名称 @@ -37,13 +38,34 @@ export function useFeatures() { return getFeatures.value.find((feature) => name === feature.name); } + function _isEnabled(name: string): boolean { + var setting = get(name); + return setting?.value.toLowerCase() === 'true'; + } + const featureChecker: IFeatureChecker = { getOrEmpty(name: string) { return get(name)?.value ?? ''; }, - isEnabled(name: string) { - var setting = get(name); - return setting?.value.toLowerCase() === 'true'; + + isEnabled(featureNames: string | string[], requiresAll?: boolean) { + if (Array.isArray(featureNames)) { + if (featureNames.length === 0) return true; + if (!requiresAll || 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; }, }; diff --git a/apps/vue/src/hooks/abp/useLocalization.ts b/apps/vue/src/hooks/abp/useLocalization.ts index 1a1915af3..37995c05f 100644 --- a/apps/vue/src/hooks/abp/useLocalization.ts +++ b/apps/vue/src/hooks/abp/useLocalization.ts @@ -1,10 +1,11 @@ import { computed } from 'vue'; import { merge } from 'lodash-es'; -import { useAbpStoreWithOut } from '/@/store/modules/abp'; import { format } from '/@/utils/strings'; +import { useAbpStoreWithOut } from '/@/store/modules/abp'; interface IStringLocalizer { L(key: string, args?: Recordable | any[] | undefined): string; + Lr(resource: string, key: string, args?: Recordable | any[] | undefined): string; } export function useLocalization(resourceNames?: string | string[]) { @@ -29,6 +30,16 @@ export function useLocalization(resourceNames?: string | string[]) { return resource; }); + const getResourceByName = computed(() => { + return (resource: string): Dictionary => { + const abpStore = useAbpStoreWithOut(); + const { values } = abpStore.getApplication.localization; + if (Reflect.has(values, resource)) { + return values[resource]; + } + return {}; + }; + }); function L(key: string, args?: Recordable | any[] | undefined) { if (!key) return ''; @@ -37,9 +48,18 @@ export function useLocalization(resourceNames?: string | string[]) { return format(getResource.value[key], args ?? []); } + function Lr(resource: string, key: string, args?: Recordable | any[] | undefined) { + if (!key) return ''; + const findResource = getResourceByName.value(resource); + if (!findResource) return key; + if (!Reflect.has(findResource, key)) return key; + return format(findResource[key], args ?? []); + } + const localizer: IStringLocalizer = { L: L, + Lr: Lr, }; - return { L, localizer }; + return { L, Lr, localizer }; } diff --git a/apps/vue/src/hooks/abp/useLocalizationSerializer.ts b/apps/vue/src/hooks/abp/useLocalizationSerializer.ts new file mode 100644 index 000000000..e3a638456 --- /dev/null +++ b/apps/vue/src/hooks/abp/useLocalizationSerializer.ts @@ -0,0 +1,101 @@ +import { isNullOrWhiteSpace } from "/@/utils/strings"; + +interface ValidateOptions { + required?: boolean; +} + +interface ILocalizableStringSerializer { + serialize(value?: LocalizableStringInfo): string; + deserialize(value?: string): LocalizableStringInfo; + 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.substring(2).trim()); + case 'L': + const commaPosition = value.indexOf(',', 2); + if (commaPosition == -1) { + return false; + } + const name = value.substring(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 { + resourceName: '', + name: '', + }; + } + if (value.length < 2 || value[1] !== ':') { + return { + resourceName: '', + name: value, + }; + } + const type = value[0]; + switch (type) { + case 'F': + return { + resourceName: 'Fixed', + name: value.substring(2), + }; + case 'L': + const commaPosition = value.indexOf(',', 2); + if (commaPosition == -1) { + return { + resourceName: 'Default', + name: value, + }; + } + const resourceName = value.substring(2, commaPosition); + const name = value.substring(commaPosition + 1); + if (isNullOrWhiteSpace(resourceName)) { + return { + resourceName: 'Default', + name: value, + }; + } + return { + resourceName: resourceName, + name: name, + }; + default: + return { + resourceName: 'Default', + name: value, + }; + } + } + + return { + validate: Validate, + serialize: Serialize, + deserialize: Deserialize, + } +} diff --git a/apps/vue/src/locales/lang/en/component.ts b/apps/vue/src/locales/lang/en/component.ts index bac3fd3e1..d1381ca0d 100644 --- a/apps/vue/src/locales/lang/en/component.ts +++ b/apps/vue/src/locales/lang/en/component.ts @@ -150,10 +150,91 @@ export default { reUploadFailed: 'Re-upload failed files', }, verify: { - error: 'verification failed!', - time: 'The verification is successful and it takes {time} seconds!', + error: 'verification failed!', + time: 'The verification is successful and it takes {time} seconds!', redoTip: 'Click the picture to refresh', dragText: 'Hold down the slider and drag', successText: 'Verified', }, + 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', + }, + 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', + }, + }, + } }; diff --git a/apps/vue/src/locales/lang/zh-CN/component.ts b/apps/vue/src/locales/lang/zh-CN/component.ts index 483aaf82c..fa1ed3ee2 100644 --- a/apps/vue/src/locales/lang/zh-CN/component.ts +++ b/apps/vue/src/locales/lang/zh-CN/component.ts @@ -159,4 +159,85 @@ export default { dragText: '请按住滑块拖动', successText: '验证通过', }, + 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: '已经添加了一个相同名称的键', + }, + }, + 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: '正则表达式', + }, + }, + } }; diff --git a/apps/vue/src/store/modules/settings.ts b/apps/vue/src/store/modules/settings.ts index 8548d380b..bd94dfdeb 100644 --- a/apps/vue/src/store/modules/settings.ts +++ b/apps/vue/src/store/modules/settings.ts @@ -1,7 +1,7 @@ import { defineStore } from 'pinia'; import { store } from '/@/store'; import { createLocalStorage } from '/@/utils/cache'; -import { SettingGroup } from '/@/api/settings/model/settingModel'; +import { SettingGroup } from '/@/api/settings-management/settings/model'; const ls = createLocalStorage(); const SETTING_ID = 'setting-management'; diff --git a/apps/vue/src/utils/table.ts b/apps/vue/src/utils/table.ts new file mode 100644 index 000000000..9412a9ec6 --- /dev/null +++ b/apps/vue/src/utils/table.ts @@ -0,0 +1,23 @@ +import { isDate, isNumber } from "./is"; + +export function sorter(a: Recordable, b: Recordable, field: string) : number { + if (!a[field] && !b[field]) { + return 0; + } + if (a[field] && !b[field]) { + return 1; + } + if (b[field] && !a[field]) { + return -1; + } + if (isDate(a[field])) { + return a[field] - b[field]; + } + if (isNumber(a[field])) { + return a[field] - b[field]; + } + if (Array.isArray(a[field])) { + return a[field].length - b[field].length; + } + return String(a[field]).localeCompare(String(b[field])); +} diff --git a/apps/vue/src/views/account/center/Setting.vue b/apps/vue/src/views/account/center/Setting.vue index 05ff6680a..56a77f74d 100644 --- a/apps/vue/src/views/account/center/Setting.vue +++ b/apps/vue/src/views/account/center/Setting.vue @@ -6,8 +6,8 @@ import { useAbpStoreWithOut } from '/@/store/modules/abp'; import { SettingForm } from '/@/components/SettingManagement'; - import { SettingGroup } from '/@/api/settings/model/settingModel'; - import { getCurrentUserSettings, setCurrentUserSettings } from '/@/api/settings/settings'; + import { SettingGroup } from '/@/api/settings-management/settings/model'; + import { getCurrentUserSettings, setCurrentUserSettings } from '/@/api/settings-management/settings'; interface ISettingForm { providerName: string; diff --git a/apps/vue/src/views/feature-management/definitions/features/components/FeatureDefinitionModal.vue b/apps/vue/src/views/feature-management/definitions/features/components/FeatureDefinitionModal.vue new file mode 100644 index 000000000..960e32375 --- /dev/null +++ b/apps/vue/src/views/feature-management/definitions/features/components/FeatureDefinitionModal.vue @@ -0,0 +1,433 @@ +