8 changed files with 494 additions and 1 deletions
@ -1 +1,2 @@ |
|||||
|
export { useTemplateContentsApi } from './useTemplateContentsApi'; |
||||
export { useTemplateDefinitionsApi } from './useTemplateDefinitionsApi'; |
export { useTemplateDefinitionsApi } from './useTemplateDefinitionsApi'; |
||||
|
|||||
@ -0,0 +1,69 @@ |
|||||
|
import type { |
||||
|
TextTemplateContentDto, |
||||
|
TextTemplateContentGetInput, |
||||
|
TextTemplateContentUpdateDto, |
||||
|
TextTemplateRestoreInput, |
||||
|
} from '../types/contents'; |
||||
|
|
||||
|
import { useRequest } from '@abp/request'; |
||||
|
|
||||
|
export function useTemplateContentsApi() { |
||||
|
const { cancel, request } = useRequest(); |
||||
|
/** |
||||
|
* 获取模板内容 |
||||
|
* @param input 参数 |
||||
|
* @returns 模板内容数据传输对象 |
||||
|
*/ |
||||
|
function getApi( |
||||
|
input: TextTemplateContentGetInput, |
||||
|
): Promise<TextTemplateContentDto> { |
||||
|
let url = '/api/text-templating/templates/content'; |
||||
|
url += input.culture ? `/${input.culture}/${input.name}` : `/${input.name}`; |
||||
|
return request<TextTemplateContentDto>(url, { |
||||
|
method: 'GET', |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
* 重置模板内容为默认值 |
||||
|
* @param name 模板名称 |
||||
|
* @param input 参数 |
||||
|
* @returns 模板定义数据传输对象列表 |
||||
|
*/ |
||||
|
function restoreToDefaultApi( |
||||
|
name: string, |
||||
|
input: TextTemplateRestoreInput, |
||||
|
): Promise<void> { |
||||
|
return request( |
||||
|
`/api/text-templating/templates/content/${name}/restore-to-default`, |
||||
|
{ |
||||
|
data: input, |
||||
|
method: 'PUT', |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
/** |
||||
|
* 更新模板内容 |
||||
|
* @param name 模板名称 |
||||
|
* @param input 参数 |
||||
|
* @returns 模板内容数据传输对象 |
||||
|
*/ |
||||
|
function updateApi( |
||||
|
name: string, |
||||
|
input: TextTemplateContentUpdateDto, |
||||
|
): Promise<TextTemplateContentDto> { |
||||
|
return request<TextTemplateContentDto>( |
||||
|
`/api/text-templating/templates/content/${name}`, |
||||
|
{ |
||||
|
data: input, |
||||
|
method: 'PUT', |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
cancel, |
||||
|
getApi, |
||||
|
restoreToDefaultApi, |
||||
|
updateApi, |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,216 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { ExtendedFormApi } from '@vben/common-ui'; |
||||
|
|
||||
|
import type { TextTemplateContentDto } from '../../types'; |
||||
|
import type { TextTemplateDefinitionDto } from '../../types/definitions'; |
||||
|
|
||||
|
import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
||||
|
import { $t } from '@vben/locales'; |
||||
|
|
||||
|
import { useAbpStore } from '@abp/core'; |
||||
|
import { Modal as AntdvModal, Button, Card, message } from 'ant-design-vue'; |
||||
|
|
||||
|
import { useTemplateContentsApi } from '../../api'; |
||||
|
|
||||
|
defineProps<{ |
||||
|
title: string; |
||||
|
}>(); |
||||
|
const emits = defineEmits<{ |
||||
|
(event: 'change', data: TextTemplateContentDto): void; |
||||
|
}>(); |
||||
|
const abpStore = useAbpStore(); |
||||
|
const { getApi, restoreToDefaultApi, updateApi } = useTemplateContentsApi(); |
||||
|
|
||||
|
const [SourceForm, sourceFormApi] = useVbenForm({ |
||||
|
commonConfig: { |
||||
|
colon: true, |
||||
|
componentProps: { |
||||
|
class: 'w-full', |
||||
|
}, |
||||
|
disabled: true, |
||||
|
}, |
||||
|
layout: 'vertical', |
||||
|
schema: [ |
||||
|
{ |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
fieldNames: { |
||||
|
label: 'displayName', |
||||
|
value: 'cultureName', |
||||
|
}, |
||||
|
onChange: (val?: string) => onCultureChange(sourceFormApi, val), |
||||
|
}, |
||||
|
fieldName: 'culture', |
||||
|
label: $t('AbpTextTemplating.BaseCultureName'), |
||||
|
rules: 'required', |
||||
|
}, |
||||
|
{ |
||||
|
component: 'Textarea', |
||||
|
componentProps: { |
||||
|
autoSize: { |
||||
|
minRows: 20, |
||||
|
}, |
||||
|
showCount: true, |
||||
|
}, |
||||
|
fieldName: 'content', |
||||
|
label: $t('AbpTextTemplating.BaseContent'), |
||||
|
rules: 'required', |
||||
|
}, |
||||
|
], |
||||
|
showDefaultActions: false, |
||||
|
}); |
||||
|
const [TargetForm, targetFormApi] = useVbenForm({ |
||||
|
commonConfig: { |
||||
|
colon: true, |
||||
|
componentProps: { |
||||
|
class: 'w-full', |
||||
|
}, |
||||
|
}, |
||||
|
handleSubmit: onSubmit, |
||||
|
layout: 'vertical', |
||||
|
schema: [ |
||||
|
{ |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
fieldNames: { |
||||
|
label: 'displayName', |
||||
|
value: 'cultureName', |
||||
|
}, |
||||
|
onChange: (val?: string) => onCultureChange(targetFormApi, val), |
||||
|
}, |
||||
|
fieldName: 'culture', |
||||
|
label: $t('AbpTextTemplating.TargetCultureName'), |
||||
|
rules: 'required', |
||||
|
}, |
||||
|
{ |
||||
|
component: 'Textarea', |
||||
|
componentProps: { |
||||
|
autoSize: { |
||||
|
minRows: 20, |
||||
|
}, |
||||
|
showCount: true, |
||||
|
}, |
||||
|
fieldName: 'content', |
||||
|
label: $t('AbpTextTemplating.TargetContent'), |
||||
|
rules: 'required', |
||||
|
}, |
||||
|
], |
||||
|
showDefaultActions: false, |
||||
|
}); |
||||
|
const [Modal, modalApi] = useVbenModal({ |
||||
|
confirmText: $t('AbpTextTemplating.SaveContent'), |
||||
|
fullscreen: true, |
||||
|
fullscreenButton: false, |
||||
|
async onConfirm() { |
||||
|
await targetFormApi.validateAndSubmitForm(); |
||||
|
}, |
||||
|
async onOpenChange(isOpen) { |
||||
|
if (isOpen) { |
||||
|
await onInit(); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
async function onInit() { |
||||
|
try { |
||||
|
modalApi.setState({ loading: true }); |
||||
|
const culture = |
||||
|
abpStore.application?.localization.currentCulture.cultureName; |
||||
|
const languages = abpStore.application?.localization.languages ?? []; |
||||
|
sourceFormApi.updateSchema([ |
||||
|
{ |
||||
|
componentProps: { |
||||
|
options: languages, |
||||
|
}, |
||||
|
fieldName: 'culture', |
||||
|
}, |
||||
|
]); |
||||
|
targetFormApi.updateSchema([ |
||||
|
{ |
||||
|
componentProps: { |
||||
|
options: languages, |
||||
|
}, |
||||
|
fieldName: 'culture', |
||||
|
}, |
||||
|
]); |
||||
|
const { name } = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const { content } = await getApi({ |
||||
|
culture, |
||||
|
name, |
||||
|
}); |
||||
|
sourceFormApi.setValues({ |
||||
|
content, |
||||
|
culture, |
||||
|
}); |
||||
|
} finally { |
||||
|
modalApi.setState({ loading: false }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function onCultureChange(formApi: ExtendedFormApi, culture?: string) { |
||||
|
const { name } = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const { content } = await getApi({ |
||||
|
culture, |
||||
|
name, |
||||
|
}); |
||||
|
formApi.setValues({ |
||||
|
content, |
||||
|
culture, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
async function onSubmit(values: Record<string, string>) { |
||||
|
try { |
||||
|
modalApi.setState({ submitting: true }); |
||||
|
const { name } = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const dto = await updateApi(name, { |
||||
|
content: values.content!, |
||||
|
culture: values.culture, |
||||
|
}); |
||||
|
emits('change', dto); |
||||
|
message.success($t('AbpTextTemplating.TemplateContentUpdated')); |
||||
|
modalApi.close(); |
||||
|
} finally { |
||||
|
modalApi.setState({ submitting: false }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onRestoreToDefault() { |
||||
|
AntdvModal.confirm({ |
||||
|
centered: true, |
||||
|
content: $t('AbpTextTemplating.RestoreToDefaultMessage'), |
||||
|
onOk: async () => { |
||||
|
const { name } = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const formValues = await sourceFormApi.getValues(); |
||||
|
await restoreToDefaultApi(name, { |
||||
|
culture: formValues.culture, |
||||
|
}); |
||||
|
message.success($t('AbpTextTemplating.TemplateContentRestoredToDefault')); |
||||
|
await onInit(); |
||||
|
}, |
||||
|
title: $t('AbpTextTemplating.RestoreToDefault'), |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<Modal :title="$t('AbpTextTemplating.EditContents')"> |
||||
|
<Card :title="title"> |
||||
|
<template #extra> |
||||
|
<Button danger type="primary" @click="onRestoreToDefault"> |
||||
|
{{ $t('AbpTextTemplating.RestoreToDefault') }} |
||||
|
</Button> |
||||
|
</template> |
||||
|
<div class="flex flex-row gap-3"> |
||||
|
<div class="w-1/2"> |
||||
|
<SourceForm /> |
||||
|
</div> |
||||
|
<div class="w-1/2"> |
||||
|
<TargetForm /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Card> |
||||
|
</Modal> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1,171 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { |
||||
|
TextTemplateContentDto, |
||||
|
TextTemplateDefinitionDto, |
||||
|
} from '../../types'; |
||||
|
|
||||
|
import { computed, defineAsyncComponent, ref } from 'vue'; |
||||
|
|
||||
|
import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
||||
|
import { $t } from '@vben/locales'; |
||||
|
|
||||
|
import { MarkdownViewer } from '@abp/components/vditor'; |
||||
|
import { useAbpStore } from '@abp/core'; |
||||
|
import { |
||||
|
Alert, |
||||
|
Modal as AntdvModal, |
||||
|
Button, |
||||
|
Card, |
||||
|
message, |
||||
|
} from 'ant-design-vue'; |
||||
|
|
||||
|
import { useTemplateContentsApi } from '../../api/useTemplateContentsApi'; |
||||
|
|
||||
|
const emits = defineEmits<{ |
||||
|
(event: 'change', data: TextTemplateContentDto): void; |
||||
|
}>(); |
||||
|
|
||||
|
const { cancel, getApi, restoreToDefaultApi, updateApi } = |
||||
|
useTemplateContentsApi(); |
||||
|
|
||||
|
const abpStore = useAbpStore(); |
||||
|
const textTemplate = ref<TextTemplateDefinitionDto>(); |
||||
|
|
||||
|
const getCardTitle = computed(() => { |
||||
|
if (!textTemplate.value) { |
||||
|
return ''; |
||||
|
} |
||||
|
return `${$t('AbpTextTemplating.DisplayName:Name')} - ${textTemplate.value.name}`; |
||||
|
}); |
||||
|
|
||||
|
const [Form, formApi] = useVbenForm({ |
||||
|
commonConfig: { |
||||
|
componentProps: { |
||||
|
class: 'w-full', |
||||
|
}, |
||||
|
}, |
||||
|
handleSubmit: onSubmit, |
||||
|
layout: 'vertical', |
||||
|
schema: [ |
||||
|
{ |
||||
|
component: 'Textarea', |
||||
|
componentProps: { |
||||
|
autoSize: { |
||||
|
minRows: 20, |
||||
|
}, |
||||
|
showCount: true, |
||||
|
}, |
||||
|
fieldName: 'content', |
||||
|
label: $t('AbpTextTemplating.DisplayName:Content'), |
||||
|
rules: 'required', |
||||
|
}, |
||||
|
], |
||||
|
showDefaultActions: false, |
||||
|
}); |
||||
|
const [Modal, modalApi] = useVbenModal({ |
||||
|
confirmText: $t('AbpTextTemplating.SaveContent'), |
||||
|
fullscreen: true, |
||||
|
fullscreenButton: false, |
||||
|
onCancel() { |
||||
|
modalApi.close(); |
||||
|
}, |
||||
|
onClosed() { |
||||
|
cancel('TemplateDefinitionModal has closed!'); |
||||
|
}, |
||||
|
onConfirm: async () => { |
||||
|
await formApi.validateAndSubmitForm(); |
||||
|
}, |
||||
|
onOpenChange: async (isOpen: boolean) => { |
||||
|
textTemplate.value = undefined; |
||||
|
if (isOpen) { |
||||
|
const textTemplateDefine = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
textTemplate.value = textTemplateDefine; |
||||
|
await onGet(); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
const [TemplateContentCurtuleModal, curtuleModalApi] = useVbenModal({ |
||||
|
connectedComponent: defineAsyncComponent( |
||||
|
() => import('./TemplateContentCurtuleModal.vue'), |
||||
|
), |
||||
|
}); |
||||
|
|
||||
|
async function onGet() { |
||||
|
try { |
||||
|
modalApi.setState({ loading: true }); |
||||
|
const textTemplateDefine = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const culture = textTemplateDefine.isInlineLocalized |
||||
|
? undefined |
||||
|
: abpStore.application?.localization.currentCulture.cultureName; |
||||
|
const dto = await getApi({ culture, name: textTemplateDefine.name }); |
||||
|
formApi.setFieldValue('content', dto.content); |
||||
|
} finally { |
||||
|
modalApi.setState({ loading: false }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onRestoreToDefault() { |
||||
|
AntdvModal.confirm({ |
||||
|
centered: true, |
||||
|
content: $t('AbpTextTemplating.RestoreToDefaultMessage'), |
||||
|
onOk: async () => { |
||||
|
await restoreToDefaultApi(textTemplate.value!.name, {}); |
||||
|
message.success($t('AbpTextTemplating.TemplateContentRestoredToDefault')); |
||||
|
await onGet(); |
||||
|
}, |
||||
|
title: $t('AbpTextTemplating.RestoreToDefault'), |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function onCustomizePerCulture() { |
||||
|
curtuleModalApi.setData(textTemplate.value); |
||||
|
curtuleModalApi.open(); |
||||
|
} |
||||
|
|
||||
|
async function onSubmit(values: Record<string, string>) { |
||||
|
try { |
||||
|
modalApi.setState({ submitting: true }); |
||||
|
const textTemplateDefine = modalApi.getData<TextTemplateDefinitionDto>(); |
||||
|
const culture = textTemplateDefine.isInlineLocalized |
||||
|
? undefined |
||||
|
: abpStore.application?.localization.currentCulture.cultureName; |
||||
|
const dto = await updateApi(textTemplateDefine.name, { |
||||
|
content: values.content!, |
||||
|
culture, |
||||
|
}); |
||||
|
emits('change', dto); |
||||
|
message.success($t('AbpTextTemplating.TemplateContentUpdated')); |
||||
|
modalApi.close(); |
||||
|
} finally { |
||||
|
modalApi.setState({ submitting: false }); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<Modal :title="$t('AbpTextTemplating.EditContents')"> |
||||
|
<Alert v-if="textTemplate?.isInlineLocalized" type="warning"> |
||||
|
<template #message> |
||||
|
<MarkdownViewer |
||||
|
:value="$t('AbpTextTemplating.InlineContentDescription')" |
||||
|
/> |
||||
|
</template> |
||||
|
</Alert> |
||||
|
<Card :title="getCardTitle"> |
||||
|
<template #extra> |
||||
|
<div class="flex flex-row gap-2"> |
||||
|
<Button danger type="primary" @click="onRestoreToDefault"> |
||||
|
{{ $t('AbpTextTemplating.RestoreToDefault') }} |
||||
|
</Button> |
||||
|
<Button type="dashed" @click="onCustomizePerCulture"> |
||||
|
{{ $t('AbpTextTemplating.CustomizePerCulture') }} |
||||
|
</Button> |
||||
|
</div> |
||||
|
</template> |
||||
|
<Form /> |
||||
|
</Card> |
||||
|
<TemplateContentCurtuleModal :title="getCardTitle" /> |
||||
|
</Modal> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1,26 @@ |
|||||
|
interface TextTemplateContentDto { |
||||
|
content?: string; |
||||
|
culture?: string; |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
interface TextTemplateContentGetInput { |
||||
|
culture?: string; |
||||
|
name: string; |
||||
|
} |
||||
|
|
||||
|
interface TextTemplateRestoreInput { |
||||
|
culture?: string; |
||||
|
} |
||||
|
|
||||
|
interface TextTemplateContentUpdateDto { |
||||
|
content: string; |
||||
|
culture?: string; |
||||
|
} |
||||
|
|
||||
|
export type { |
||||
|
TextTemplateContentDto, |
||||
|
TextTemplateContentGetInput, |
||||
|
TextTemplateContentUpdateDto, |
||||
|
TextTemplateRestoreInput, |
||||
|
}; |
||||
@ -1 +1,2 @@ |
|||||
|
export * from './contents'; |
||||
export * from './definitions'; |
export * from './definitions'; |
||||
|
|||||
Loading…
Reference in new issue