Browse Source

Merge pull request #1061 from colinin/settings

Settings
pull/1067/head
yx lin 1 year ago
committed by GitHub
parent
commit
d6fb443f05
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      apps/vben5/apps/app-antd/package.json
  2. 5
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  3. 5
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  4. 29
      apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts
  5. 15
      apps/vben5/apps/app-antd/src/views/settings/definitions/index.vue
  6. 15
      apps/vben5/apps/app-antd/src/views/settings/system/index.vue
  7. 37
      apps/vben5/packages/@abp/settings/package.json
  8. 77
      apps/vben5/packages/@abp/settings/src/api/definitions.ts
  9. 2
      apps/vben5/packages/@abp/settings/src/api/index.ts
  10. 84
      apps/vben5/packages/@abp/settings/src/api/settings.ts
  11. 236
      apps/vben5/packages/@abp/settings/src/components/definitions/SettingDefinitionModal.vue
  12. 219
      apps/vben5/packages/@abp/settings/src/components/definitions/SettingDefinitionTable.vue
  13. 3
      apps/vben5/packages/@abp/settings/src/components/index.ts
  14. 214
      apps/vben5/packages/@abp/settings/src/components/settings/SettingForm.vue
  15. 91
      apps/vben5/packages/@abp/settings/src/components/settings/SystemSetting.vue
  16. 25
      apps/vben5/packages/@abp/settings/src/components/settings/UserSetting.vue
  17. 1
      apps/vben5/packages/@abp/settings/src/constants/index.ts
  18. 10
      apps/vben5/packages/@abp/settings/src/constants/permissions.ts
  19. 3
      apps/vben5/packages/@abp/settings/src/index.ts
  20. 48
      apps/vben5/packages/@abp/settings/src/types/definitions.ts
  21. 2
      apps/vben5/packages/@abp/settings/src/types/index.ts
  22. 56
      apps/vben5/packages/@abp/settings/src/types/settings.ts
  23. 6
      apps/vben5/packages/@abp/settings/tsconfig.json

1
apps/vben5/apps/app-antd/package.json

@ -33,6 +33,7 @@
"@abp/openiddict": "workspace:*",
"@abp/permission": "workspace:*",
"@abp/request": "workspace:*",
"@abp/settings": "workspace:*",
"@abp/ui": "workspace:*",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",

5
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -15,6 +15,11 @@
"title": "Permissions",
"groups": "Groups",
"definitions": "Definitions"
},
"settings": {
"title": "Settings",
"definitions": "Definitions",
"system": "System Settings"
}
},
"openiddict": {

5
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -15,6 +15,11 @@
"title": "权限管理",
"groups": "权限分组",
"definitions": "权限定义"
},
"settings": {
"title": "设置管理",
"definitions": "设置定义",
"system": "系统设置"
}
},
"openiddict": {

29
apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts

@ -110,6 +110,35 @@ const routes: RouteRecordRaw[] = [
},
],
},
{
meta: {
title: $t('abp.manage.settings.title'),
icon: 'ic:outline-settings',
},
name: 'SettingManagement',
path: '/manage/settings',
children: [
{
meta: {
title: $t('abp.manage.settings.definitions'),
icon: 'codicon:settings',
},
name: 'SettingDefinitions',
path: '/manage/settings/definitions',
component: () =>
import('#/views/settings/definitions/index.vue'),
},
{
meta: {
title: $t('abp.manage.settings.system'),
icon: 'tabler:settings-cog',
},
name: 'SystemSettings',
path: '/manage/settings/system',
component: () => import('#/views/settings/system/index.vue'),
},
],
},
{
meta: {
title: $t('abp.manage.identity.auditLogs'),

15
apps/vben5/apps/app-antd/src/views/settings/definitions/index.vue

@ -0,0 +1,15 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { SettingDefinitionTable } from '@abp/settings';
defineOptions({
name: 'SettingDefinitions',
});
</script>
<template>
<Page>
<SettingDefinitionTable />
</Page>
</template>

15
apps/vben5/apps/app-antd/src/views/settings/system/index.vue

@ -0,0 +1,15 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { SystemSetting } from '@abp/settings';
defineOptions({
name: 'SystemSettings',
});
</script>
<template>
<Page>
<SystemSetting />
</Page>
</template>

37
apps/vben5/packages/@abp/settings/package.json

@ -0,0 +1,37 @@
{
"name": "@abp/settings",
"version": "8.3.2",
"homepage": "https://github.com/colinin/abp-next-admin",
"bugs": "https://github.com/colinin/abp-next-admin/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/colinin/abp-next-admin.git",
"directory": "packages/@abp/settings"
},
"license": "MIT",
"type": "module",
"sideEffects": [
"**/*.css"
],
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
}
},
"dependencies": {
"@abp/core": "workspace:*",
"@abp/request": "workspace:*",
"@abp/ui": "workspace:*",
"@ant-design/icons-vue": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"ant-design-vue": "catalog:",
"dayjs": "catalog:",
"vue": "catalog:*"
}
}

77
apps/vben5/packages/@abp/settings/src/api/definitions.ts

@ -0,0 +1,77 @@
import type { ListResultDto } from '@abp/core';
import type {
SettingDefinitionCreateDto,
SettingDefinitionDto,
SettingDefinitionGetListInput,
SettingDefinitionUpdateDto,
} from '../types/definitions';
import { requestClient } from '@abp/request';
/**
*
* @param name
*/
export function deleteApi(name: string): Promise<void> {
return requestClient.delete(
`/api/setting-management/settings/definitions/${name}`,
);
}
/**
*
* @param name
* @returns
*/
export function getApi(name: string): Promise<SettingDefinitionDto> {
return requestClient.get<SettingDefinitionDto>(
`/api/setting-management/settings/definitions/${name}`,
);
}
/**
*
* @param input
* @returns
*/
export function getListApi(
input?: SettingDefinitionGetListInput,
): Promise<ListResultDto<SettingDefinitionDto>> {
return requestClient.get<ListResultDto<SettingDefinitionDto>>(
`/api/setting-management/settings/definitions`,
{
params: input,
},
);
}
/**
*
* @param input
* @returns
*/
export function createApi(
input: SettingDefinitionCreateDto,
): Promise<SettingDefinitionDto> {
return requestClient.post<SettingDefinitionDto>(
'/api/setting-management/settings/definitions',
input,
);
}
/**
*
* @param name
* @param input
* @returns
*/
export function updateApi(
name: string,
input: SettingDefinitionUpdateDto,
): Promise<SettingDefinitionDto> {
return requestClient.put<SettingDefinitionDto>(
`/api/setting-management/settings/definitions/${name}`,
input,
);
}

2
apps/vben5/packages/@abp/settings/src/api/index.ts

@ -0,0 +1,2 @@
export * as settingDefinitionsApi from './definitions';
export * as settingsApi from './settings';

84
apps/vben5/packages/@abp/settings/src/api/settings.ts

@ -0,0 +1,84 @@
import type { ListResultDto } from '@abp/core';
import type { SettingGroup, SettingsUpdateInput } from '../types/settings';
import { requestClient } from '@abp/request';
/**
*
* @returns
*/
export function getGlobalSettingsApi(): Promise<ListResultDto<SettingGroup>> {
return requestClient.get<ListResultDto<SettingGroup>>(
`/api/setting-management/settings/by-global`,
);
}
/**
*
* @returns
*/
export function setGlobalSettingsApi(
input: SettingsUpdateInput,
): Promise<void> {
return requestClient.put(
`/api/setting-management/settings/change-global`,
input,
);
}
/**
*
* @returns
*/
export function getTenantSettingsApi(): Promise<ListResultDto<SettingGroup>> {
return requestClient.get<ListResultDto<SettingGroup>>(
`/api/setting-management/settings/by-current-tenant`,
);
}
/**
*
* @returns
*/
export function setTenantSettingsApi(
input: SettingsUpdateInput,
): Promise<void> {
return requestClient.put(
`/api/setting-management/settings/change-current-tenant`,
input,
);
}
/**
*
* @returns
*/
export function getUserSettingsApi(): Promise<ListResultDto<SettingGroup>> {
return requestClient.get<ListResultDto<SettingGroup>>(
`/api/setting-management/settings/by-current-user`,
);
}
/**
*
* @returns
*/
export function setUserSettingsApi(input: SettingsUpdateInput): Promise<void> {
return requestClient.put(
`/api/setting-management/settings/change-current-user`,
input,
);
}
/**
*
* @param emailAddress
*/
export const sendTestEmailApi = (emailAddress: string) => {
return requestClient.post(
`/api/setting-management/settings/send-test-email`,
{
emailAddress,
},
);
};

236
apps/vben5/packages/@abp/settings/src/components/definitions/SettingDefinitionModal.vue

@ -0,0 +1,236 @@
<script setup lang="ts">
import type { PropertyInfo } from '@abp/ui';
import type { FormInstance } from 'ant-design-vue';
import type { SettingDefinitionDto } from '../../types/definitions';
import {
defineEmits,
defineOptions,
reactive,
ref,
toValue,
useTemplateRef,
} from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { LocalizableInput, PropertyTable } from '@abp/ui';
import {
Checkbox,
Form,
Input,
message,
Select,
Tabs,
Textarea,
} from 'ant-design-vue';
import { createApi, getApi, updateApi } from '../../api/definitions';
defineOptions({
name: 'SettingDefinitionModal',
});
const emits = defineEmits<{
(event: 'change', data: SettingDefinitionDto): void;
}>();
const FormItem = Form.Item;
const TabPane = Tabs.TabPane;
type TabKeys = 'basic' | 'props';
const defaultModel = {} as SettingDefinitionDto;
const isEditModel = ref(false);
const activeTab = ref<TabKeys>('basic');
const form = useTemplateRef<FormInstance>('form');
const formModel = ref<SettingDefinitionDto>({ ...defaultModel });
const providerOptions = reactive([
{ label: $t('AbpSettingManagement.Providers:Default'), value: 'D' },
{ label: $t('AbpSettingManagement.Providers:Configuration'), value: 'C' },
{ label: $t('AbpSettingManagement.Providers:Global'), value: 'G' },
{ label: $t('AbpSettingManagement.Providers:Tenant'), value: 'T' },
{ label: $t('AbpSettingManagement.Providers:User'), value: 'U' },
]);
const [Modal, modalApi] = useVbenModal({
class: 'w-1/2',
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
await form.value?.validate();
const api = isEditModel.value
? updateApi(formModel.value.name, toValue(formModel))
: createApi(toValue(formModel));
modalApi.setState({ confirmLoading: true, loading: true });
api
.then((res) => {
message.success($t('AbpUi.SavedSuccessfully'));
emits('change', res);
modalApi.close();
})
.finally(() => {
modalApi.setState({ confirmLoading: false, loading: false });
});
},
onOpenChange: async (isOpen: boolean) => {
if (isOpen) {
isEditModel.value = false;
activeTab.value = 'basic';
formModel.value = { ...defaultModel };
modalApi.setState({
showConfirmButton: true,
title: $t('AbpSettingManagement.Definition:AddNew'),
});
try {
modalApi.setState({ loading: true });
const { name } = modalApi.getData<SettingDefinitionDto>();
name && (await onGet(name));
} finally {
modalApi.setState({ loading: false });
}
}
},
title: $t('AbpSettingManagement.Definition:AddNew'),
});
async function onGet(name: string) {
isEditModel.value = true;
const dto = await getApi(name);
formModel.value = dto;
modalApi.setState({
showConfirmButton: !dto.isStatic,
title: `${$t('AbpSettingManagement.Settings')} - ${dto.name}`,
});
}
function onPropChange(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
formModel.value.extraProperties[prop.key] = prop.value;
}
function onPropDelete(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
delete formModel.value.extraProperties[prop.key];
}
</script>
<template>
<Modal>
<Form
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
:wrapper-col="{ span: 18 }"
>
<Tabs v-model:active-key="activeTab">
<!-- 基本信息 -->
<TabPane key="basic" :tab="$t('AbpSettingManagement.BasicInfo')">
<FormItem
:label="$t('AbpSettingManagement.DisplayName:Name')"
name="name"
required
>
<Input
v-model:value="formModel.name"
:disabled="formModel.isStatic"
autocomplete="off"
/>
</FormItem>
<FormItem
:label="$t('AbpSettingManagement.DisplayName:DefaultValue')"
name="defaultValue"
>
<Textarea
v-model:value="formModel.defaultValue"
:allow-clear="true"
:auto-size="{ minRows: 3 }"
:disabled="formModel.isStatic"
/>
</FormItem>
<FormItem
:label="$t('AbpSettingManagement.DisplayName:DisplayName')"
name="displayName"
required
>
<LocalizableInput
v-model:value="formModel.displayName"
:disabled="formModel.isStatic"
/>
</FormItem>
<FormItem
:label="$t('AbpSettingManagement.DisplayName:Description')"
name="description"
>
<LocalizableInput
v-model:value="formModel.description"
:disabled="formModel.isStatic"
/>
</FormItem>
<FormItem
:extra="$t('AbpSettingManagement.Description:Providers')"
:label="$t('AbpSettingManagement.DisplayName:Providers')"
name="providers"
>
<Select
v-model:value="formModel.providers"
:allow-clear="true"
:disabled="formModel.isStatic"
:options="providerOptions"
mode="multiple"
/>
</FormItem>
<FormItem
:extra="$t('AbpSettingManagement.Description:IsInherited')"
:label="$t('AbpSettingManagement.DisplayName:IsInherited')"
name="isInherited"
>
<Checkbox
v-model:checked="formModel.isInherited"
:disabled="formModel.isStatic"
>
{{ $t('AbpSettingManagement.DisplayName:IsInherited') }}
</Checkbox>
</FormItem>
<FormItem
:extra="$t('AbpSettingManagement.Description:IsEncrypted')"
:label="$t('AbpSettingManagement.DisplayName:IsEncrypted')"
name="isEncrypted"
>
<Checkbox
v-model:checked="formModel.isEncrypted"
:disabled="formModel.isStatic"
>
{{ $t('AbpSettingManagement.DisplayName:IsEncrypted') }}
</Checkbox>
</FormItem>
<FormItem
:extra="$t('AbpSettingManagement.Description:IsVisibleToClients')"
:label="$t('AbpSettingManagement.DisplayName:IsVisibleToClients')"
name="isVisibleToClients"
>
<Checkbox
v-model:checked="formModel.isVisibleToClients"
:disabled="formModel.isStatic"
>
{{ $t('AbpSettingManagement.DisplayName:IsVisibleToClients') }}
</Checkbox>
</FormItem>
</TabPane>
<!-- 属性 -->
<TabPane key="props" :tab="$t('AbpPermissionManagement.Properties')">
<PropertyTable
:data="formModel.extraProperties"
:disabled="formModel.isStatic"
@change="onPropChange"
@delete="onPropDelete"
/>
</TabPane>
</Tabs>
</Form>
</Modal>
</template>
<style scoped></style>

219
apps/vben5/packages/@abp/settings/src/components/definitions/SettingDefinitionTable.vue

@ -0,0 +1,219 @@
<script setup lang="ts">
import type { VbenFormProps, VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { SettingDefinitionDto } from '../../types/definitions';
import { defineAsyncComponent, h, onMounted, reactive, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useLocalization, useLocalizationSerializer } from '@abp/core';
import { useVbenVxeGrid } from '@abp/ui';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
} from '@ant-design/icons-vue';
import { Button, message, Modal } from 'ant-design-vue';
import { deleteApi, getListApi } from '../../api/definitions';
import { SettingDefinitionsPermissions } from '../../constants/permissions';
defineOptions({
name: 'SettingDefinitionTable',
});
const permissionGroups = ref<SettingDefinitionDto[]>([]);
const pageState = reactive({
current: 1,
size: 10,
total: 0,
});
const { Lr } = useLocalization();
const { deserialize } = useLocalizationSerializer();
const formOptions: VbenFormProps = {
//
collapsed: false,
handleReset: onReset,
async handleSubmit(params) {
pageState.current = 1;
await onGet(params);
},
schema: [
{
component: 'Input',
fieldName: 'filter',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpUi.Search'),
},
],
//
showCollapseButton: true,
//
submitOnEnter: true,
};
const gridOptions: VxeGridProps<SettingDefinitionDto> = {
columns: [
{
align: 'left',
field: 'name',
minWidth: 150,
title: $t('AbpSettingManagement.DisplayName:Name'),
},
{
align: 'left',
field: 'displayName',
minWidth: 150,
title: $t('AbpSettingManagement.DisplayName:DisplayName'),
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: $t('AbpUi.Actions'),
width: 180,
},
],
exportConfig: {},
keepSource: true,
toolbarConfig: {
custom: true,
export: true,
refresh: false,
zoom: true,
},
};
const gridEvents: VxeGridListeners<SettingDefinitionDto> = {
pageChange(params) {
pageState.current = params.currentPage;
pageState.size = params.pageSize;
onPageChange();
},
};
const [Grid, gridApi] = useVbenVxeGrid({
formOptions,
gridEvents,
gridOptions,
});
const [SettingDefinitionModal, modalApi] = useVbenModal({
connectedComponent: defineAsyncComponent(
() => import('./SettingDefinitionModal.vue'),
),
});
async function onGet(input?: Record<string, string>) {
try {
gridApi.setLoading(true);
const { items } = await getListApi(input);
pageState.total = items.length;
permissionGroups.value = items.map((item) => {
const localizableString = deserialize(item.displayName);
return {
...item,
displayName: Lr(localizableString.resourceName, localizableString.name),
};
});
onPageChange();
} finally {
gridApi.setLoading(false);
}
}
async function onReset() {
await gridApi.formApi.resetForm();
const input = await gridApi.formApi.getValues();
await onGet(input);
}
function onPageChange() {
const items = permissionGroups.value.slice(
(pageState.current - 1) * pageState.size,
pageState.current * pageState.size,
);
gridApi.setGridOptions({
data: items,
pagerConfig: {
currentPage: pageState.current,
pageSize: pageState.size,
total: pageState.total,
},
});
}
function onCreate() {
modalApi.setData({});
modalApi.open();
}
function onUpdate(row: SettingDefinitionDto) {
modalApi.setData(row);
modalApi.open();
}
function onDelete(row: SettingDefinitionDto) {
Modal.confirm({
centered: true,
content: `${$t('AbpUi.ItemWillBeDeletedMessageWithFormat', [row.name])}`,
onOk: async () => {
await deleteApi(row.name);
message.success($t('AbpUi.SuccessfullyDeleted'));
onGet();
},
title: $t('AbpUi.AreYouSure'),
});
}
onMounted(onGet);
</script>
<template>
<Grid :table-title="$t('AbpSettingManagement.Settings')">
<template #toolbar-tools>
<Button
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[SettingDefinitionsPermissions.Create]"
@click="onCreate"
>
{{ $t('AbpSettingManagement.Definition:AddNew') }}
</Button>
</template>
<template #action="{ row }">
<div class="flex flex-row">
<div :class="row.isStatic ? 'w-full' : 'basis-1/2'">
<Button
:icon="h(EditOutlined)"
block
type="link"
v-access:code="[SettingDefinitionsPermissions.Update]"
@click="onUpdate(row)"
>
{{ $t('AbpUi.Edit') }}
</Button>
</div>
<div v-if="!row.isStatic" class="basis-1/2">
<Button
:icon="h(DeleteOutlined)"
block
danger
type="link"
v-access:code="[SettingDefinitionsPermissions.DeleteOrRestore]"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</div>
</div>
</template>
</Grid>
<SettingDefinitionModal @change="() => onGet()" />
</template>
<style scoped></style>

3
apps/vben5/packages/@abp/settings/src/components/index.ts

@ -0,0 +1,3 @@
export { default as SettingDefinitionTable } from './definitions/SettingDefinitionTable.vue';
export { default as SystemSetting } from './settings/SystemSetting.vue';
export { default as UserSetting } from './settings/UserSetting.vue';

214
apps/vben5/packages/@abp/settings/src/components/settings/SettingForm.vue

@ -0,0 +1,214 @@
<script setup lang="ts">
import type {
SettingDetail,
SettingGroup,
SettingsUpdateInput,
} from '../../types';
import { computed, onMounted, ref, toValue } from 'vue';
import { $t } from '@vben/locales';
import { formatToDate } from '@abp/core';
import {
Button,
Card,
Checkbox,
Collapse,
DatePicker,
Form,
Input,
InputNumber,
InputPassword,
message,
Select,
Tabs,
} from 'ant-design-vue';
import dayjs from 'dayjs';
import { ValueType } from '../../types';
defineOptions({
name: 'SettingForm',
});
const props = defineProps<{
getApi: () => Promise<SettingGroup[]>;
submitApi: (input: SettingsUpdateInput) => Promise<void>;
}>();
const emits = defineEmits<{
(event: 'change', data: SettingsUpdateInput): void;
}>();
const FormItem = Form.Item;
const TabPane = Tabs.TabPane;
const CollapsePanel = Collapse.Panel;
const SelectOption = Select.Option;
const defaultModel: SettingsUpdateInput = {
settings: [],
};
const activeTab = ref(0);
const submiting = ref(false);
const settingGroups = ref<SettingGroup[]>([]);
const settingsUpdateInput = ref<SettingsUpdateInput>({ ...defaultModel });
const getExpandedCollapseKeys = computed(() => {
return (group: SettingGroup) => {
const keys = group.settings.map((group) => {
return group.displayName;
});
return keys;
};
});
async function onGet() {
settingGroups.value = await props.getApi();
}
async function onSubmit() {
try {
submiting.value = true;
const input = toValue(settingsUpdateInput);
await props.submitApi(input);
emits('change', input);
message.success($t('AbpSettingManagement.SuccessfullySaved'));
} finally {
submiting.value = false;
}
}
function onCheckChange(setting: SettingDetail) {
setting.value = setting.value === 'true' ? 'false' : 'true';
onValueChange(setting);
}
function onDateChange(e: any, setting: SettingDetail) {
setting.value = dayjs.isDayjs(e) ? formatToDate(e) : '';
onValueChange(setting);
}
function onValueChange(setting: SettingDetail) {
const index = settingsUpdateInput.value.settings.findIndex(
(s) => s.name === setting.name,
);
if (index === -1) {
settingsUpdateInput.value.settings.push({
name: setting.name,
value: String(setting.value),
});
} else {
settingsUpdateInput.value.settings[index]!.value = String(setting.value);
}
}
onMounted(onGet);
</script>
<template>
<Card :title="$t('AbpSettingManagement.Settings')">
<template #extra>
<Button
v-if="settingsUpdateInput.settings.length > 0"
:loading="submiting"
class="w-[100px]"
post-icon="ant-design:setting-outlined"
type="primary"
@click="onSubmit"
>
{{ $t('AbpUi.Submit') }}
</Button>
</template>
<Form :label-col="{ span: 5 }" :wrapper-col="{ span: 15 }">
<Tabs v-model="activeTab">
<TabPane
v-for="(group, index) in settingGroups"
:key="index"
:tab="group.displayName"
>
<Collapse :default-active-key="getExpandedCollapseKeys(group)">
<CollapsePanel
v-for="setting in group.settings"
:key="setting.displayName"
:header="setting.displayName"
>
<template v-for="detail in setting.details" :key="detail.name">
<slot
v-if="detail.slot"
:change="
detail.valueType === ValueType.Boolean
? onCheckChange(detail)
: onValueChange(detail)
"
:detail="detail"
:name="detail.slot"
></slot>
<FormItem
v-else
:extra="detail.description"
:label="detail.displayName"
>
<template v-if="detail.valueType === ValueType.String">
<InputPassword
v-if="detail.isEncrypted"
v-model:value="detail.value"
:placeholder="detail.description"
@change="onValueChange(detail)"
/>
<Input
v-else
v-model:value="detail.value"
:placeholder="detail.description"
type="text"
@change="onValueChange(detail)"
/>
</template>
<InputNumber
v-else-if="
detail.valueType === ValueType.Number &&
!detail.isEncrypted
"
v-model:value="detail.value"
:placeholder="detail.description"
class="w-full"
@change="onValueChange(detail)"
/>
<DatePicker
v-else-if="detail.valueType === ValueType.Date"
:placeholder="detail.description"
:value="
detail.value ? dayjs(detail.value, 'YYYY-MM-DD') : ''
"
style="width: 100%"
@change="onDateChange($event, detail)"
/>
<Select
v-if="detail.valueType === ValueType.Option"
v-model:value="detail.value"
@change="onValueChange(detail)"
>
<SelectOption
v-for="option in detail.options"
:key="option.value"
:disabled="option.value === detail.value"
>
{{ option.name }}
</SelectOption>
</Select>
<Checkbox
v-if="detail.valueType === ValueType.Boolean"
:checked="detail.value === 'true'"
@change="onCheckChange(detail)"
>
{{ detail.displayName }}
</Checkbox>
</FormItem>
</template>
</CollapsePanel>
</Collapse>
</TabPane>
</Tabs>
</Form>
</Card>
</template>
<style scoped></style>

91
apps/vben5/packages/@abp/settings/src/components/settings/SystemSetting.vue

@ -0,0 +1,91 @@
<script setup lang="ts">
import type { SettingsUpdateInput } from '../../types';
import { ref } from 'vue';
import { $t } from '@vben/locales';
import { isEmail, useAbpStore } from '@abp/core';
import { Button, Form, InputSearch, message, Modal } from 'ant-design-vue';
import {
getGlobalSettingsApi,
getTenantSettingsApi,
sendTestEmailApi,
setGlobalSettingsApi,
setTenantSettingsApi,
} from '../../api/settings';
import SettingForm from './SettingForm.vue';
defineOptions({
name: 'SystemSettingForm',
});
const FormItem = Form.Item;
const abpStore = useAbpStore();
const sending = ref(false);
async function onGet() {
const api = abpStore.application?.currentTenant.isAvailable
? getTenantSettingsApi
: getGlobalSettingsApi;
const { items } = await api();
return items;
}
async function onSubmit(input: SettingsUpdateInput) {
const api = abpStore.application?.currentTenant.isAvailable
? setTenantSettingsApi
: setGlobalSettingsApi;
await api(input);
}
async function onSendMail(email: string) {
if (!isEmail(email)) {
Modal.warn({
centered: true,
content: $t('AbpValidation.The {0} field is not a valid e-mail address', [
$t('AbpSettingManagement.TargetEmailAddress'),
]),
title: $t('AbpValidation.ThisFieldIsNotValid'),
});
return;
}
try {
sending.value = true;
await sendTestEmailApi(email);
message.success($t('AbpSettingManagement.SuccessfullySent'));
} finally {
sending.value = false;
}
}
</script>
<template>
<SettingForm :get-api="onGet" :submit-api="onSubmit">
<template #send-test-email="{ detail }">
<FormItem
:extra="detail.description"
:label="detail.displayName"
name="testEmail"
>
<InputSearch
v-model:value="detail.value"
:loading="sending"
:placeholder="$t('AbpSettingManagement.TargetEmailAddress')"
@search="onSendMail"
>
<template #enterButton>
<Button :loading="sending" type="primary">
{{ $t('AbpSettingManagement.Send') }}
</Button>
</template>
</InputSearch>
</FormItem>
</template>
</SettingForm>
</template>
<style scoped></style>

25
apps/vben5/packages/@abp/settings/src/components/settings/UserSetting.vue

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { SettingsUpdateInput } from '../../types';
import { getUserSettingsApi, setUserSettingsApi } from '../../api/settings';
import SettingForm from './SettingForm.vue';
defineOptions({
name: 'UserSettingForm',
});
async function onGet() {
const { items } = await getUserSettingsApi();
return items;
}
async function onSubmit(input: SettingsUpdateInput) {
await setUserSettingsApi(input);
}
</script>
<template>
<SettingForm :get-api="onGet" :submit-api="onSubmit" />
</template>
<style scoped></style>

1
apps/vben5/packages/@abp/settings/src/constants/index.ts

@ -0,0 +1 @@
export * from './permissions';

10
apps/vben5/packages/@abp/settings/src/constants/permissions.ts

@ -0,0 +1,10 @@
/** 设置定义权限 */
export const SettingDefinitionsPermissions = {
/** 新增 */
Create: 'SettingManagement.Definition.Create',
Default: 'SettingManagement.Definition',
/** 还原或删除 */
DeleteOrRestore: 'SettingManagement.Definition.DeleteOrRestore',
/** 更新 */
Update: 'SettingManagement.Definition.Update',
};

3
apps/vben5/packages/@abp/settings/src/index.ts

@ -0,0 +1,3 @@
export * from './api';
export * from './components';
export * from './types';

48
apps/vben5/packages/@abp/settings/src/types/definitions.ts

@ -0,0 +1,48 @@
import type {
ExtensibleObject,
IHasConcurrencyStamp,
IHasExtraProperties,
} from '@abp/core';
interface SettingDefinitionDto extends ExtensibleObject {
defaultValue?: string;
description?: string;
displayName: string;
isEncrypted: boolean;
isInherited: boolean;
isStatic: boolean;
isVisibleToClients: boolean;
name: string;
providers: string[];
}
interface SettingDefinitionGetListInput {
filter?: string;
providerName?: string;
}
interface SettingDefinitionCreateOrUpdateDto
extends IHasConcurrencyStamp,
IHasExtraProperties {
defaultValue?: string;
description?: string;
displayName: string;
isEncrypted: boolean;
isInherited: boolean;
isVisibleToClients: boolean;
providers: string[];
}
interface SettingDefinitionCreateDto
extends SettingDefinitionCreateOrUpdateDto {
name: string;
}
type SettingDefinitionUpdateDto = SettingDefinitionCreateOrUpdateDto;
export type {
SettingDefinitionCreateDto,
SettingDefinitionDto,
SettingDefinitionGetListInput,
SettingDefinitionUpdateDto,
};

2
apps/vben5/packages/@abp/settings/src/types/index.ts

@ -0,0 +1,2 @@
export * from './definitions';
export * from './settings';

56
apps/vben5/packages/@abp/settings/src/types/settings.ts

@ -0,0 +1,56 @@
interface SettingBase {
/** 名称 */
name: string;
/** 当前设置值 */
value: string;
}
/** 配置变更对象 */
type SettingUpdateInput = SettingBase;
/** 配置变更集合对象 */
interface SettingsUpdateInput {
/** 配置集合 */
settings: SettingUpdateInput[];
}
export enum ValueType {
Array = 4,
Boolean = 2,
Date = 3,
Number = 1,
Object = 10,
Option = 5,
String = 0,
}
interface Option {
name: string;
value: string;
}
interface SettingDetail {
defaultValue: string;
description?: string;
displayName: string;
isEncrypted: boolean;
name: string;
options: Option[];
slot?: string;
value?: string;
valueType: ValueType;
}
interface Setting {
description?: string;
details: SettingDetail[];
displayName: string;
}
interface SettingGroup {
description: string;
displayName: string;
settings: Setting[];
}
export type { Setting, SettingDetail, SettingGroup, SettingsUpdateInput };

6
apps/vben5/packages/@abp/settings/tsconfig.json

@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"exclude": ["node_modules"]
}
Loading…
Cancel
Save