committed by
GitHub
14 changed files with 1193 additions and 2 deletions
@ -0,0 +1,15 @@ |
|||
<script lang="ts" setup> |
|||
import { Page } from '@vben/common-ui'; |
|||
|
|||
import { DataDictionaryTable } from '@abp/platform'; |
|||
|
|||
defineOptions({ |
|||
name: 'PlatformDataDictionaries', |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<Page> |
|||
<DataDictionaryTable /> |
|||
</Page> |
|||
</template> |
|||
@ -1,2 +1,3 @@ |
|||
export { useDataDictionariesApi } from './useDataDictionariesApi'; |
|||
export { useEmailMessagesApi } from './useEmailMessagesApi'; |
|||
export { useSmsMessagesApi } from './useSmsMessagesApi'; |
|||
|
|||
@ -0,0 +1,110 @@ |
|||
import type { ListResultDto, PagedResultDto } from '@abp/core'; |
|||
|
|||
import type { |
|||
DataCreateDto, |
|||
DataDto, |
|||
DataItemCreateDto, |
|||
DataItemUpdateDto, |
|||
DataMoveDto, |
|||
DataUpdateDto, |
|||
GetDataListInput, |
|||
} from '../types/dataDictionaries'; |
|||
|
|||
import { useRequest } from '@abp/request'; |
|||
|
|||
export function useDataDictionariesApi() { |
|||
const { cancel, request } = useRequest(); |
|||
|
|||
function createApi(input: DataCreateDto): Promise<DataDto> { |
|||
return request<DataDto>('/api/platform/datas', { |
|||
data: input, |
|||
method: 'POST', |
|||
}); |
|||
} |
|||
|
|||
function deleteApi(id: string): Promise<void> { |
|||
return request(`/api/platform/datas/${id}`, { |
|||
method: 'DELETE', |
|||
}); |
|||
} |
|||
|
|||
function createItemApi(id: string, input: DataItemCreateDto): Promise<void> { |
|||
return request(`/api/platform/datas/${id}/items`, { |
|||
data: input, |
|||
method: 'POST', |
|||
}); |
|||
} |
|||
|
|||
function deleteItemApi(id: string, name: string): Promise<void> { |
|||
return request(`/api/platform/datas/${id}/items/${name}`, { |
|||
method: 'DELETE', |
|||
}); |
|||
} |
|||
|
|||
function getApi(id: string): Promise<DataDto> { |
|||
return request<DataDto>(`/api/platform/datas/${id}`, { |
|||
method: 'GET', |
|||
}); |
|||
} |
|||
|
|||
function getByNameApi(name: string): Promise<DataDto> { |
|||
return request<DataDto>(`/api/platform/datas/by-name/${name}`, { |
|||
method: 'GET', |
|||
}); |
|||
} |
|||
|
|||
function getAllApi(): Promise<ListResultDto<DataDto>> { |
|||
return request<ListResultDto<DataDto>>(`/api/platform/datas/all`, { |
|||
method: 'GET', |
|||
}); |
|||
} |
|||
|
|||
function getPagedListApi( |
|||
input?: GetDataListInput, |
|||
): Promise<PagedResultDto<DataDto>> { |
|||
return request<PagedResultDto<DataDto>>(`/api/platform/datas`, { |
|||
method: 'GET', |
|||
params: input, |
|||
}); |
|||
} |
|||
|
|||
function moveApi(id: string, input: DataMoveDto): Promise<DataDto> { |
|||
return request<DataDto>(`/api/platform/datas/${id}/move`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function updateApi(id: string, input: DataUpdateDto): Promise<DataDto> { |
|||
return request<DataDto>(`/api/platform/datas/${id}`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function updateItemApi( |
|||
id: string, |
|||
name: string, |
|||
input: DataItemUpdateDto, |
|||
): Promise<void> { |
|||
return request(`/api/platform/datas/${id}/items/${name}`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
return { |
|||
cancel, |
|||
createApi, |
|||
createItemApi, |
|||
deleteApi, |
|||
deleteItemApi, |
|||
getAllApi, |
|||
getApi, |
|||
getByNameApi, |
|||
getPagedListApi, |
|||
moveApi, |
|||
updateApi, |
|||
updateItemApi, |
|||
}; |
|||
} |
|||
@ -0,0 +1,257 @@ |
|||
<script setup lang="ts"> |
|||
import type { VxeGridListeners, VxeGridProps } from '@abp/ui'; |
|||
|
|||
import type { DataDto, DataItemDto } from '../../types/dataDictionaries'; |
|||
|
|||
import { defineAsyncComponent, h, reactive, ref } from 'vue'; |
|||
|
|||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { isNullOrWhiteSpace } from '@abp/core'; |
|||
import { useVbenVxeGrid } from '@abp/ui'; |
|||
import { |
|||
CheckOutlined, |
|||
CloseOutlined, |
|||
DeleteOutlined, |
|||
EditOutlined, |
|||
PlusOutlined, |
|||
} from '@ant-design/icons-vue'; |
|||
import { Button, message, Modal } from 'ant-design-vue'; |
|||
|
|||
import { useDataDictionariesApi } from '../../api'; |
|||
import { ValueType } from '../../types/dataDictionaries'; |
|||
|
|||
const { deleteItemApi, getApi } = useDataDictionariesApi(); |
|||
|
|||
const dataItems = ref<DataItemDto[]>([]); |
|||
const pageState = reactive({ |
|||
current: 1, |
|||
size: 10, |
|||
total: 0, |
|||
}); |
|||
const valueTypeMaps: { [key: number]: string } = { |
|||
[ValueType.Array]: 'Array', |
|||
[ValueType.Boolean]: 'Boolean', |
|||
[ValueType.Date]: 'Date', |
|||
[ValueType.DateTime]: 'DateTime', |
|||
[ValueType.Numeic]: 'Number', |
|||
[ValueType.Object]: 'Object', |
|||
[ValueType.String]: 'String', |
|||
}; |
|||
|
|||
const [Drawer, drawerApi] = useVbenDrawer({ |
|||
class: 'w-1/2', |
|||
async onOpenChange(isOpen) { |
|||
if (isOpen) { |
|||
const { name } = drawerApi.getData<DataDto>(); |
|||
drawerApi.setState({ |
|||
title: `${$t('AppPlatform.DisplayName:DataDictionary')} - ${name}`, |
|||
}); |
|||
await onGet(); |
|||
} |
|||
}, |
|||
showConfirmButton: false, |
|||
title: $t('AppPlatform.Data:Items'), |
|||
}); |
|||
|
|||
const gridOptions: VxeGridProps<DataItemDto> = { |
|||
columns: [ |
|||
{ |
|||
align: 'center', |
|||
type: 'seq', |
|||
width: 50, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'name', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:Name'), |
|||
treeNode: true, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'displayName', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:DisplayName'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'description', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:Description'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'valueType', |
|||
formatter: ({ row }) => { |
|||
return valueTypeMaps[row.valueType] ?? row.valueType; |
|||
}, |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:ValueType'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'defaultValue', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:DefaultValue'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'allowBeNull', |
|||
minWidth: 150, |
|||
slots: { default: 'allowBeNull' }, |
|||
title: $t('AppPlatform.DisplayName:AllowBeNull'), |
|||
}, |
|||
{ |
|||
field: 'action', |
|||
fixed: 'right', |
|||
slots: { default: 'action' }, |
|||
title: $t('AbpUi.Actions'), |
|||
width: 220, |
|||
}, |
|||
], |
|||
exportConfig: {}, |
|||
keepSource: true, |
|||
toolbarConfig: { |
|||
custom: true, |
|||
export: true, |
|||
refresh: false, |
|||
zoom: true, |
|||
}, |
|||
treeConfig: { |
|||
accordion: true, |
|||
parentField: 'parentId', |
|||
rowField: 'id', |
|||
transform: true, |
|||
}, |
|||
}; |
|||
|
|||
const gridEvents: VxeGridListeners<DataItemDto> = { |
|||
pageChange(params) { |
|||
pageState.current = params.currentPage; |
|||
pageState.size = params.pageSize; |
|||
onPageChange(); |
|||
}, |
|||
}; |
|||
|
|||
const [Grid, gridApi] = useVbenVxeGrid({ |
|||
gridEvents, |
|||
gridOptions, |
|||
}); |
|||
|
|||
const [DataDictionaryItemModal, itemModalApi] = useVbenModal({ |
|||
connectedComponent: defineAsyncComponent( |
|||
() => import('./DataDictionaryItemModal.vue'), |
|||
), |
|||
}); |
|||
|
|||
async function onGet() { |
|||
const { id } = drawerApi.getData<DataDto>(); |
|||
if (isNullOrWhiteSpace(id)) { |
|||
return; |
|||
} |
|||
try { |
|||
drawerApi.setState({ loading: true }); |
|||
const dto = await getApi(id); |
|||
dataItems.value = dto.items; |
|||
pageState.total = dto.items.length; |
|||
onPageChange(); |
|||
} finally { |
|||
drawerApi.setState({ |
|||
loading: false, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function onPageChange() { |
|||
const items = dataItems.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() { |
|||
const data = drawerApi.getData<DataDto>(); |
|||
itemModalApi.setData({ data }); |
|||
itemModalApi.open(); |
|||
} |
|||
|
|||
function onUpdate(row: DataItemDto) { |
|||
const data = drawerApi.getData<DataDto>(); |
|||
itemModalApi.setData({ data, item: row }); |
|||
itemModalApi.open(); |
|||
} |
|||
|
|||
function onDelete(row: DataItemDto) { |
|||
Modal.confirm({ |
|||
afterClose: () => { |
|||
gridApi.setLoading(false); |
|||
}, |
|||
centered: true, |
|||
content: `${$t('AbpUi.ItemWillBeDeletedMessage')}`, |
|||
onOk: async () => { |
|||
try { |
|||
gridApi.setLoading(true); |
|||
const { id } = drawerApi.getData<DataDto>(); |
|||
await deleteItemApi(id, row.name); |
|||
message.success($t('AbpUi.DeletedSuccessfully')); |
|||
onGet(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Drawer> |
|||
<Grid :table-title="$t('AppPlatform.Data:Items')"> |
|||
<template #toolbar-tools> |
|||
<Button :icon="h(PlusOutlined)" type="primary" @click="onCreate"> |
|||
{{ $t('AppPlatform.Data:AppendItem') }} |
|||
</Button> |
|||
</template> |
|||
<template #allowBeNull="{ row }"> |
|||
<div class="flex flex-row justify-center"> |
|||
<CheckOutlined v-if="row.allowBeNull" class="text-green-500" /> |
|||
<CloseOutlined v-else class="text-red-500" /> |
|||
</div> |
|||
</template> |
|||
<template #action="{ row }"> |
|||
<div class="flex flex-row"> |
|||
<Button |
|||
:icon="h(EditOutlined)" |
|||
block |
|||
type="link" |
|||
@click="onUpdate(row)" |
|||
> |
|||
{{ $t('AbpUi.Edit') }} |
|||
</Button> |
|||
<Button |
|||
:icon="h(DeleteOutlined)" |
|||
block |
|||
danger |
|||
type="link" |
|||
@click="onDelete(row)" |
|||
> |
|||
{{ $t('AbpUi.Delete') }} |
|||
</Button> |
|||
</div> |
|||
</template> |
|||
</Grid> |
|||
<DataDictionaryItemModal @change="onGet" /> |
|||
</Drawer> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,293 @@ |
|||
<script setup lang="ts"> |
|||
import type { |
|||
DataDto, |
|||
DataItemCreateDto, |
|||
DataItemDto, |
|||
DataItemUpdateDto, |
|||
} from '../../types'; |
|||
|
|||
import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { formatToDate, formatToDateTime } from '@abp/core'; |
|||
import { message } from 'ant-design-vue'; |
|||
import cloneDeep from 'lodash.clonedeep'; |
|||
|
|||
import { useDataDictionariesApi } from '../../api'; |
|||
import { ValueType } from '../../types'; |
|||
|
|||
interface ModalState { |
|||
data: DataDto; |
|||
item: DataItemDto; |
|||
} |
|||
|
|||
const emits = defineEmits<{ |
|||
(event: 'change'): void; |
|||
}>(); |
|||
|
|||
const { createItemApi, updateItemApi } = useDataDictionariesApi(); |
|||
|
|||
const [Form, formApi] = useVbenForm({ |
|||
commonConfig: { |
|||
componentProps: { |
|||
class: 'w-full', |
|||
}, |
|||
}, |
|||
handleSubmit: onSubmit, |
|||
schema: [ |
|||
{ |
|||
component: 'Input', |
|||
dependencies: { |
|||
show: false, |
|||
triggerFields: ['name'], |
|||
}, |
|||
fieldName: 'id', |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
dependencies: { |
|||
disabled: (values) => { |
|||
return !!values?.id; |
|||
}, |
|||
triggerFields: ['id'], |
|||
}, |
|||
fieldName: 'name', |
|||
label: $t('AppPlatform.DisplayName:Name'), |
|||
rules: 'required', |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
componentProps: { |
|||
autocomplete: 'off', |
|||
}, |
|||
fieldName: 'displayName', |
|||
label: $t('AppPlatform.DisplayName:DisplayName'), |
|||
rules: 'required', |
|||
}, |
|||
{ |
|||
component: 'Select', |
|||
componentProps: { |
|||
onChange(valueType: ValueType) { |
|||
onValueTypeChange(valueType); |
|||
}, |
|||
options: [ |
|||
{ label: 'String', value: ValueType.String }, |
|||
{ label: 'Number', value: ValueType.Numeic }, |
|||
{ label: 'Date', value: ValueType.Date }, |
|||
{ label: 'DateTime', value: ValueType.DateTime }, |
|||
{ label: 'Boolean', value: ValueType.Boolean }, |
|||
{ label: 'Array', value: ValueType.Array }, |
|||
// TODO: Object 暂不支持 |
|||
// { label: 'Object', value: ValueType.Object }, |
|||
], |
|||
}, |
|||
fieldName: 'valueType', |
|||
label: $t('AppPlatform.DisplayName:ValueType'), |
|||
rules: 'selectRequired', |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
fieldName: 'defaultValue', |
|||
label: $t('AppPlatform.DisplayName:DefaultValue'), |
|||
}, |
|||
{ |
|||
component: 'Checkbox', |
|||
fieldName: 'allowBeNull', |
|||
label: $t('AppPlatform.DisplayName:AllowBeNull'), |
|||
}, |
|||
{ |
|||
component: 'Textarea', |
|||
componentProps: { |
|||
autoSize: { |
|||
minRows: 3, |
|||
}, |
|||
}, |
|||
fieldName: 'description', |
|||
label: $t('AppPlatform.DisplayName:Description'), |
|||
}, |
|||
], |
|||
showDefaultActions: false, |
|||
}); |
|||
const [Modal, modalApi] = useVbenModal({ |
|||
async onConfirm() { |
|||
await formApi.validateAndSubmitForm(); |
|||
}, |
|||
onOpenChange(isOpen) { |
|||
if (isOpen) { |
|||
onInit(); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
function onInit() { |
|||
formApi.resetForm(); |
|||
const state = modalApi.getData<ModalState>(); |
|||
let title = $t('AppPlatform.Data:AppendItem'); |
|||
if (state.item?.id) { |
|||
onValueTypeChange(state.item.valueType); |
|||
formApi.setValues({ |
|||
...state.item, |
|||
defaultValue: mapFromDefaultValue(state.item), |
|||
}); |
|||
title = `${$t('AppPlatform.Data:EditItem')} - ${state.item.name}`; |
|||
} else { |
|||
formApi.updateSchema([ |
|||
{ |
|||
component: 'Input', |
|||
componentProps: { |
|||
autocomplete: 'off', |
|||
}, |
|||
dependencies: { |
|||
rules(values) { |
|||
return values.allowBeNull ? null : 'required'; |
|||
}, |
|||
triggerFields: ['valueType', 'allowBeNull'], |
|||
}, |
|||
fieldName: 'defaultValue', |
|||
}, |
|||
]); |
|||
formApi.setValues({ |
|||
allowBeNull: true, |
|||
}); |
|||
} |
|||
modalApi.setState({ title }); |
|||
} |
|||
|
|||
async function onSubmit(values: Record<string, any>) { |
|||
try { |
|||
modalApi.setState({ submitting: true }); |
|||
const state = modalApi.getData<ModalState>(); |
|||
const input = cloneDeep(values); |
|||
input.defaultValue = mapToDefaultValue(input.valueType, input.defaultValue); |
|||
const api = input.id |
|||
? updateItemApi(state.data.id, input.name, input as DataItemUpdateDto) |
|||
: createItemApi(state.data.id, input as DataItemCreateDto); |
|||
await api; |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
emits('change'); |
|||
modalApi.close(); |
|||
} finally { |
|||
modalApi.setState({ submitting: false }); |
|||
} |
|||
} |
|||
|
|||
function onValueTypeChange(valueType: ValueType) { |
|||
let component = 'Input'; |
|||
let componentProps: Record<string, any> | undefined; |
|||
switch (valueType) { |
|||
case ValueType.Array: { |
|||
component = 'Select'; |
|||
componentProps = { |
|||
mode: 'tags', |
|||
}; |
|||
break; |
|||
} |
|||
case ValueType.Boolean: { |
|||
component = 'Checkbox'; |
|||
break; |
|||
} |
|||
case ValueType.Date: |
|||
case ValueType.DateTime: { |
|||
component = 'DatePicker'; |
|||
componentProps = { |
|||
showTime: valueType === ValueType.DateTime, |
|||
valueFormat: |
|||
valueType === ValueType.DateTime |
|||
? 'YYYY-MM-DD HH:mm:ss' |
|||
: 'YYYY-MM-DD', |
|||
}; |
|||
break; |
|||
} |
|||
case ValueType.Numeic: { |
|||
component = 'InputNumber'; |
|||
break; |
|||
} |
|||
case ValueType.String: { |
|||
componentProps = { |
|||
autocomplete: 'off', |
|||
}; |
|||
break; |
|||
} |
|||
} |
|||
formApi.updateSchema([ |
|||
{ |
|||
component, |
|||
componentProps, |
|||
dependencies: { |
|||
rules(values) { |
|||
return values.allowBeNull ? null : 'required'; |
|||
}, |
|||
triggerFields: ['valueType', 'allowBeNull'], |
|||
}, |
|||
fieldName: 'defaultValue', |
|||
}, |
|||
]); |
|||
formApi.setFieldValue('defaultValue', undefined); |
|||
} |
|||
|
|||
function mapToDefaultValue(valueType: ValueType, defaultValue?: any) { |
|||
if (!defaultValue) { |
|||
return undefined; |
|||
} |
|||
switch (valueType) { |
|||
case ValueType.Array: { |
|||
return (defaultValue as string[]).join(','); |
|||
} |
|||
case ValueType.Boolean: { |
|||
return (defaultValue as Boolean).toString(); |
|||
} |
|||
case ValueType.Date: { |
|||
return formatToDate(defaultValue); |
|||
} |
|||
case ValueType.DateTime: { |
|||
return formatToDateTime(defaultValue); |
|||
} |
|||
case ValueType.Numeic: { |
|||
return (defaultValue as Number).toString(); |
|||
} |
|||
} |
|||
return defaultValue; |
|||
} |
|||
|
|||
function mapFromDefaultValue(dataItem: DataItemDto) { |
|||
if (!dataItem.defaultValue) { |
|||
return undefined; |
|||
} |
|||
let defaultValue: any | undefined; |
|||
switch (dataItem.valueType) { |
|||
case ValueType.Array: { |
|||
defaultValue = dataItem.defaultValue.split(','); |
|||
break; |
|||
} |
|||
case ValueType.Boolean: { |
|||
defaultValue = Boolean(dataItem.defaultValue); |
|||
break; |
|||
} |
|||
case ValueType.Date: { |
|||
defaultValue = formatToDate(dataItem.defaultValue); |
|||
break; |
|||
} |
|||
case ValueType.DateTime: { |
|||
defaultValue = formatToDateTime(dataItem.defaultValue); |
|||
break; |
|||
} |
|||
case ValueType.Numeic: { |
|||
defaultValue = Number(dataItem.defaultValue); |
|||
break; |
|||
} |
|||
case ValueType.String: { |
|||
defaultValue = dataItem.defaultValue; |
|||
break; |
|||
} |
|||
} |
|||
return defaultValue; |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Modal> |
|||
<Form /> |
|||
</Modal> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,140 @@ |
|||
<script setup lang="ts"> |
|||
import type { |
|||
DataCreateDto, |
|||
DataDto, |
|||
DataUpdateDto, |
|||
} from '../../types/dataDictionaries'; |
|||
|
|||
import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { listToTree } from '@abp/core'; |
|||
import { message } from 'ant-design-vue'; |
|||
|
|||
import { useDataDictionariesApi } from '../../api'; |
|||
|
|||
const emits = defineEmits<{ |
|||
(event: 'change', data: DataDto): void; |
|||
}>(); |
|||
|
|||
const { createApi, getAllApi, getApi, updateApi } = useDataDictionariesApi(); |
|||
|
|||
const [Form, formApi] = useVbenForm({ |
|||
commonConfig: { |
|||
componentProps: { |
|||
class: 'w-full', |
|||
}, |
|||
}, |
|||
handleSubmit: onSubmit, |
|||
schema: [ |
|||
{ |
|||
component: 'Input', |
|||
dependencies: { |
|||
show: false, |
|||
triggerFields: ['name'], |
|||
}, |
|||
fieldName: 'id', |
|||
}, |
|||
{ |
|||
component: 'ApiTreeSelect', |
|||
componentProps: { |
|||
allowClear: true, |
|||
api: async () => { |
|||
const { items } = await getAllApi(); |
|||
return listToTree(items, { |
|||
id: 'id', |
|||
pid: 'parentId', |
|||
}); |
|||
}, |
|||
labelField: 'displayName', |
|||
valueField: 'id', |
|||
childrenField: 'children', |
|||
}, |
|||
dependencies: { |
|||
disabled: (values) => { |
|||
return !!values?.id; |
|||
}, |
|||
triggerFields: ['id'], |
|||
}, |
|||
fieldName: 'parentId', |
|||
label: $t('AppPlatform.DisplayName:ParentData'), |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
fieldName: 'name', |
|||
label: $t('AppPlatform.DisplayName:Name'), |
|||
rules: 'required', |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
fieldName: 'displayName', |
|||
label: $t('AppPlatform.DisplayName:DisplayName'), |
|||
rules: 'required', |
|||
}, |
|||
{ |
|||
component: 'Textarea', |
|||
componentProps: { |
|||
autoSize: { |
|||
minRows: 3, |
|||
}, |
|||
}, |
|||
fieldName: 'description', |
|||
label: $t('AppPlatform.DisplayName:Description'), |
|||
}, |
|||
], |
|||
showDefaultActions: false, |
|||
}); |
|||
const [Modal, modalApi] = useVbenModal({ |
|||
onConfirm: async () => { |
|||
await formApi.validateAndSubmitForm(); |
|||
}, |
|||
onOpenChange: async (isOpen) => { |
|||
if (isOpen) { |
|||
await onGet(); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
async function onGet() { |
|||
try { |
|||
modalApi.setState({ loading: true }); |
|||
formApi.resetForm(); |
|||
const { displayName, id, parentId } = modalApi.getData<DataDto>(); |
|||
let title = $t('AppPlatform.Data:AddNew'); |
|||
if (id) { |
|||
const dto = await getApi(id); |
|||
formApi.setValues(dto); |
|||
title = `${$t('AppPlatform.Data:Edit')} - ${dto.name}`; |
|||
} else if (parentId) { |
|||
title = `${$t('AppPlatform.Data:AddChildren')} - ${displayName}`; |
|||
formApi.setFieldValue('parentId', parentId); |
|||
} |
|||
modalApi.setState({ title }); |
|||
} finally { |
|||
modalApi.setState({ loading: false }); |
|||
} |
|||
} |
|||
|
|||
async function onSubmit(values: Record<string, any>) { |
|||
try { |
|||
modalApi.setState({ submitting: true }); |
|||
const api = values.id |
|||
? updateApi(values.id, values as DataUpdateDto) |
|||
: createApi(values as DataCreateDto); |
|||
const dto = await api; |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
emits('change', dto); |
|||
modalApi.close(); |
|||
} finally { |
|||
modalApi.setState({ submitting: false }); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Modal> |
|||
<Form /> |
|||
</Modal> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,281 @@ |
|||
<script setup lang="ts"> |
|||
import type { VxeGridListeners, VxeGridProps } from '@abp/ui'; |
|||
import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface'; |
|||
|
|||
import type { DataDto } from '../../types/dataDictionaries'; |
|||
|
|||
import { defineAsyncComponent, h, onMounted, reactive, ref } from 'vue'; |
|||
|
|||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui'; |
|||
import { createIconifyIcon } from '@vben/icons'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { listToTree } from '@abp/core'; |
|||
import { useVbenVxeGrid } from '@abp/ui'; |
|||
import { |
|||
DeleteOutlined, |
|||
EditOutlined, |
|||
EllipsisOutlined, |
|||
PlusOutlined, |
|||
} from '@ant-design/icons-vue'; |
|||
import { Button, Dropdown, Menu, message, Modal } from 'ant-design-vue'; |
|||
|
|||
import { useDataDictionariesApi } from '../../api/useDataDictionariesApi'; |
|||
|
|||
defineOptions({ |
|||
name: 'DataDictionaryTable', |
|||
}); |
|||
|
|||
const MenuItem = Menu.Item; |
|||
const ItemsIcon = createIconifyIcon('material-symbols:align-items-stretch'); |
|||
|
|||
const { deleteApi, getAllApi } = useDataDictionariesApi(); |
|||
|
|||
const expandRowKeys = ref<string[]>([]); |
|||
const dataDictionaries = ref<DataDto[]>([]); |
|||
const pageState = reactive({ |
|||
current: 1, |
|||
size: 10, |
|||
total: 0, |
|||
}); |
|||
|
|||
const gridOptions: VxeGridProps<DataDto> = { |
|||
columns: [ |
|||
{ |
|||
align: 'center', |
|||
type: 'seq', |
|||
width: 50, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'name', |
|||
minWidth: 150, |
|||
slots: { default: 'name' }, |
|||
title: $t('AppPlatform.DisplayName:Name'), |
|||
treeNode: true, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'displayName', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:DisplayName'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'description', |
|||
minWidth: 150, |
|||
title: $t('AppPlatform.DisplayName:Description'), |
|||
}, |
|||
{ |
|||
field: 'action', |
|||
fixed: 'right', |
|||
slots: { default: 'action' }, |
|||
title: $t('AbpUi.Actions'), |
|||
width: 220, |
|||
}, |
|||
], |
|||
exportConfig: {}, |
|||
keepSource: true, |
|||
rowConfig: { |
|||
keyField: 'id', |
|||
}, |
|||
toolbarConfig: { |
|||
custom: true, |
|||
export: true, |
|||
refresh: { |
|||
queryMethod: onGet, |
|||
}, |
|||
zoom: true, |
|||
}, |
|||
treeConfig: { |
|||
accordion: true, |
|||
parentField: 'parentId', |
|||
rowField: 'id', |
|||
transform: false, |
|||
}, |
|||
}; |
|||
const gridEvents: VxeGridListeners<DataDto> = { |
|||
pageChange(params) { |
|||
pageState.current = params.currentPage; |
|||
pageState.size = params.pageSize; |
|||
onPageChange(); |
|||
}, |
|||
toggleTreeExpand(params) { |
|||
if (params.expanded) { |
|||
expandRowKeys.value.push(params.row.id); |
|||
} else { |
|||
expandRowKeys.value = expandRowKeys.value.filter( |
|||
(key) => key !== params.row.id, |
|||
); |
|||
} |
|||
onExpandChange(); |
|||
}, |
|||
}; |
|||
|
|||
const [Grid, gridApi] = useVbenVxeGrid({ |
|||
gridEvents, |
|||
gridOptions, |
|||
}); |
|||
|
|||
const [DataDictionaryModal, modalApi] = useVbenModal({ |
|||
connectedComponent: defineAsyncComponent( |
|||
() => import('./DataDictionaryModal.vue'), |
|||
), |
|||
}); |
|||
const [DataDictionaryItemDrawer, itemDrawerApi] = useVbenDrawer({ |
|||
connectedComponent: defineAsyncComponent( |
|||
() => import('./DataDictionaryItemDrawer.vue'), |
|||
), |
|||
}); |
|||
|
|||
async function onGet() { |
|||
try { |
|||
gridApi.setLoading(true); |
|||
const { items } = await getAllApi(); |
|||
const treeItems = listToTree(items, { |
|||
id: 'id', |
|||
pid: 'parentId', |
|||
}); |
|||
pageState.total = treeItems.length; |
|||
dataDictionaries.value = treeItems; |
|||
onPageChange(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
} |
|||
|
|||
function onExpandChange() { |
|||
gridApi.setGridOptions({ |
|||
treeConfig: { |
|||
expandRowKeys: expandRowKeys.value, |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
function onPageChange() { |
|||
const items = dataDictionaries.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: DataDto) { |
|||
modalApi.setData(row); |
|||
modalApi.open(); |
|||
} |
|||
|
|||
function onDelete(row: DataDto) { |
|||
Modal.confirm({ |
|||
afterClose: () => { |
|||
gridApi.setLoading(false); |
|||
}, |
|||
centered: true, |
|||
content: `${$t('AbpUi.ItemWillBeDeletedMessage')}`, |
|||
onOk: async () => { |
|||
try { |
|||
gridApi.setLoading(true); |
|||
await deleteApi(row.id); |
|||
message.success($t('AbpUi.DeletedSuccessfully')); |
|||
onGet(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onMenuClick(row: DataDto, info: MenuInfo) { |
|||
switch (info.key) { |
|||
case 'children': { |
|||
modalApi.setData({ |
|||
displayName: row.displayName, |
|||
parentId: row.id, |
|||
}); |
|||
modalApi.open(); |
|||
break; |
|||
} |
|||
case 'items': { |
|||
onManageItems(row); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function onManageItems(row: DataDto) { |
|||
itemDrawerApi.setData(row); |
|||
itemDrawerApi.open(); |
|||
} |
|||
|
|||
onMounted(onGet); |
|||
</script> |
|||
|
|||
<template> |
|||
<Grid :table-title="$t('AppPlatform.DisplayName:DataDictionary')"> |
|||
<template #toolbar-tools> |
|||
<Button :icon="h(PlusOutlined)" type="primary" @click="onCreate"> |
|||
{{ $t('AppPlatform.Data:AddNew') }} |
|||
</Button> |
|||
</template> |
|||
<template #name="{ row }"> |
|||
<Button type="link" @click="onManageItems(row)">{{ row.name }}</Button> |
|||
</template> |
|||
<template #action="{ row }"> |
|||
<div class="flex flex-row"> |
|||
<Button |
|||
:icon="h(EditOutlined)" |
|||
block |
|||
type="link" |
|||
@click="onUpdate(row)" |
|||
> |
|||
{{ $t('AbpUi.Edit') }} |
|||
</Button> |
|||
<Button |
|||
:icon="h(DeleteOutlined)" |
|||
block |
|||
danger |
|||
type="link" |
|||
@click="onDelete(row)" |
|||
> |
|||
{{ $t('AbpUi.Delete') }} |
|||
</Button> |
|||
<Dropdown> |
|||
<template #overlay> |
|||
<Menu @click="(info) => onMenuClick(row, info)"> |
|||
<MenuItem key="children"> |
|||
<div class="flex flex-row items-center gap-[4px]"> |
|||
<PlusOutlined /> |
|||
{{ $t('AppPlatform.Data:AddChildren') }} |
|||
</div> |
|||
</MenuItem> |
|||
<MenuItem key="items"> |
|||
<div class="flex flex-row items-center gap-[4px]"> |
|||
<ItemsIcon /> |
|||
{{ $t('AppPlatform.Data:Items') }} |
|||
</div> |
|||
</MenuItem> |
|||
</Menu> |
|||
</template> |
|||
<Button :icon="h(EllipsisOutlined)" type="link" /> |
|||
</Dropdown> |
|||
</div> |
|||
</template> |
|||
</Grid> |
|||
<DataDictionaryModal @change="onGet" /> |
|||
<DataDictionaryItemDrawer /> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -1,2 +1,3 @@ |
|||
export { default as DataDictionaryTable } from './data-dictionaries/DataDictionaryTable.vue'; |
|||
export { default as EmailMessageTable } from './messages/email/EmailMessageTable.vue'; |
|||
export { default as SmsMessageTable } from './messages/sms/SmsMessageTable.vue'; |
|||
|
|||
@ -0,0 +1,76 @@ |
|||
import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/core'; |
|||
|
|||
enum ValueType { |
|||
Array = 5, |
|||
Boolean = 2, |
|||
Date = 3, |
|||
DateTime = 4, |
|||
Numeic = 1, |
|||
Object = 6, |
|||
String = 0, |
|||
} |
|||
|
|||
interface DataItemDto extends EntityDto<string> { |
|||
allowBeNull: boolean; |
|||
defaultValue?: string; |
|||
description?: string; |
|||
displayName: string; |
|||
name: string; |
|||
valueType: ValueType; |
|||
} |
|||
|
|||
interface DataDto extends EntityDto<string> { |
|||
code: string; |
|||
description?: string; |
|||
displayName: string; |
|||
items: DataItemDto[]; |
|||
name: string; |
|||
parentId?: string; |
|||
} |
|||
|
|||
interface DataCreateOrUpdateDto { |
|||
description?: string; |
|||
displayName: string; |
|||
name: string; |
|||
} |
|||
|
|||
interface DataCreateDto extends DataCreateOrUpdateDto { |
|||
parentId?: string; |
|||
} |
|||
|
|||
interface DataItemCreateOrUpdateDto { |
|||
allowBeNull: boolean; |
|||
defaultValue?: string; |
|||
description?: string; |
|||
displayName: string; |
|||
valueType: ValueType; |
|||
} |
|||
|
|||
interface DataItemCreateDto extends DataItemCreateOrUpdateDto { |
|||
name: string; |
|||
} |
|||
|
|||
interface GetDataListInput extends PagedAndSortedResultRequestDto { |
|||
filter?: string; |
|||
} |
|||
|
|||
interface DataMoveDto { |
|||
parentId?: string; |
|||
} |
|||
|
|||
type DataUpdateDto = DataCreateOrUpdateDto; |
|||
|
|||
type DataItemUpdateDto = DataItemCreateOrUpdateDto; |
|||
|
|||
export { ValueType }; |
|||
|
|||
export type { |
|||
DataCreateDto, |
|||
DataDto, |
|||
DataItemCreateDto, |
|||
DataItemDto, |
|||
DataItemUpdateDto, |
|||
DataMoveDto, |
|||
DataUpdateDto, |
|||
GetDataListInput, |
|||
}; |
|||
@ -1 +1,2 @@ |
|||
export * from './dataDictionaries'; |
|||
export * from './messages'; |
|||
|
|||
Loading…
Reference in new issue