Browse Source

feat(vben5): Complete text template

pull/1182/head
colin 10 months ago
parent
commit
1e24bee02b
  1. 1
      apps/vben5/packages/@abp/text-templating/package.json
  2. 1
      apps/vben5/packages/@abp/text-templating/src/api/index.ts
  3. 69
      apps/vben5/packages/@abp/text-templating/src/api/useTemplateContentsApi.ts
  4. 216
      apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateContentCurtuleModal.vue
  5. 171
      apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateContentModal.vue
  6. 10
      apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateDefinitionTable.vue
  7. 26
      apps/vben5/packages/@abp/text-templating/src/types/contents.ts
  8. 1
      apps/vben5/packages/@abp/text-templating/src/types/index.ts

1
apps/vben5/packages/@abp/text-templating/package.json

@ -20,6 +20,7 @@
}
},
"dependencies": {
"@abp/components": "workspace:*",
"@abp/core": "workspace:*",
"@abp/localization": "workspace:*",
"@abp/request": "workspace:*",

1
apps/vben5/packages/@abp/text-templating/src/api/index.ts

@ -1 +1,2 @@
export { useTemplateContentsApi } from './useTemplateContentsApi';
export { useTemplateDefinitionsApi } from './useTemplateDefinitionsApi';

69
apps/vben5/packages/@abp/text-templating/src/api/useTemplateContentsApi.ts

@ -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,
};
}

216
apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateContentCurtuleModal.vue

@ -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>

171
apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateContentModal.vue

@ -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>

10
apps/vben5/packages/@abp/text-templating/src/components/definitions/TemplateDefinitionTable.vue

@ -159,6 +159,11 @@ const [TemplateDefinitionModal, modalApi] = useVbenModal({
() => import('./TemplateDefinitionModal.vue'),
),
});
const [TemplateContentModal, contentModalApi] = useVbenModal({
connectedComponent: defineAsyncComponent(
() => import('./TemplateContentModal.vue'),
),
});
async function onGet(input?: Record<string, string>) {
try {
@ -209,9 +214,11 @@ function onUpdate(row: TextTemplateDefinitionDto) {
modalApi.open();
}
function onMenuClick(_row: TextTemplateDefinitionDto, info: MenuInfo) {
function onMenuClick(row: TextTemplateDefinitionDto, info: MenuInfo) {
switch (info.key) {
case 'contents': {
contentModalApi.setData(row);
contentModalApi.open();
break;
}
}
@ -302,6 +309,7 @@ onMounted(onGet);
</template>
</Grid>
<TemplateDefinitionModal @change="() => onGet()" />
<TemplateContentModal />
</template>
<style scoped></style>

26
apps/vben5/packages/@abp/text-templating/src/types/contents.ts

@ -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
apps/vben5/packages/@abp/text-templating/src/types/index.ts

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

Loading…
Cancel
Save