diff --git a/apps/web-ele/src/router/routes/modules/mall.ts b/apps/web-ele/src/router/routes/modules/mall.ts index 6d9303943..1c2f40496 100644 --- a/apps/web-ele/src/router/routes/modules/mall.ts +++ b/apps/web-ele/src/router/routes/modules/mall.ts @@ -98,7 +98,7 @@ const routes: RouteRecordRaw[] = [ activePath: '/mall/promotion/diy-template/diy-template', }, component: () => - import('#/views/mall/promotion/diy/template/modules/decorate.vue'), + import('#/views/mall/promotion/diy/template/decorate/index.vue'), }, { path: 'page/decorate/:id', @@ -110,7 +110,7 @@ const routes: RouteRecordRaw[] = [ activePath: '/mall/promotion/diy-template/diy-page', }, component: () => - import('#/views/mall/promotion/diy/page/modules/decorate.vue'), + import('#/views/mall/promotion/diy/page/decorate/index.vue'), }, ], }, diff --git a/apps/web-ele/src/views/mall/promotion/components/diy-editor/index.vue b/apps/web-ele/src/views/mall/promotion/components/diy-editor/index.vue index b2e5a2556..f9f7d9c89 100644 --- a/apps/web-ele/src/views/mall/promotion/components/diy-editor/index.vue +++ b/apps/web-ele/src/views/mall/promotion/components/diy-editor/index.vue @@ -267,7 +267,7 @@ const handleDeleteComponent = (index: number) => { }; // 注入无感刷新页面函数 -const reload = inject<() => void>('reload'); +const reload = inject<() => void>('reload'); // TODO @芋艿:是 vue3 + element-plus 独有的,可以清理掉。 // 重置 const handleReset = () => { if (reload) reload(); diff --git a/apps/web-ele/src/views/mall/promotion/diy/page/modules/decorate.vue b/apps/web-ele/src/views/mall/promotion/diy/page/decorate/index.vue similarity index 100% rename from apps/web-ele/src/views/mall/promotion/diy/page/modules/decorate.vue rename to apps/web-ele/src/views/mall/promotion/diy/page/decorate/index.vue diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/data.ts b/apps/web-ele/src/views/mall/promotion/diy/template/data.ts index ca09c59fa..e3c8f1421 100644 --- a/apps/web-ele/src/views/mall/promotion/diy/template/data.ts +++ b/apps/web-ele/src/views/mall/promotion/diy/template/data.ts @@ -3,6 +3,8 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import { DICT_TYPE } from '@vben/constants'; +import { getRangePickerDefaultProps } from '#/utils'; + /** 表单配置 */ export function useFormSchema(): VbenFormSchema[] { return [ @@ -61,9 +63,8 @@ export function useGridFormSchema(): VbenFormSchema[] { label: '创建时间', component: 'RangePicker', componentProps: { - placeholder: ['开始时间', '结束时间'], - clearable: true, - valueFormat: 'YYYY-MM-DD HH:mm:ss', + ...getRangePickerDefaultProps(), + allowClear: true, }, }, ]; diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/modules/decorate.vue b/apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue similarity index 68% rename from apps/web-ele/src/views/mall/promotion/diy/template/modules/decorate.vue rename to apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue index 6beff4f2b..566dc0d5e 100644 --- a/apps/web-ele/src/views/mall/promotion/diy/template/modules/decorate.vue +++ b/apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue @@ -3,67 +3,75 @@ import type { MallDiyPageApi } from '#/api/mall/promotion/diy/page'; import type { MallDiyTemplateApi } from '#/api/mall/promotion/diy/template'; import type { DiyComponentLibrary } from '#/views/mall/promotion/components'; // 商城的 DIY 组件,在 DiyEditor 目录下 -import { onMounted, reactive, ref, unref } from 'vue'; -import { useRouter } from 'vue-router'; +import { onMounted, reactive, ref } from 'vue'; +import { useRoute } from 'vue-router'; +import { useTabs } from '@vben/hooks'; import { IconifyIcon } from '@vben/icons'; import { useAccessStore } from '@vben/stores'; import { isEmpty } from '@vben/utils'; -import { ElMessage } from 'element-plus'; +import { + ElLoading, + ElMessage, + ElRadioButton, + ElRadioGroup, + ElTooltip, +} from 'element-plus'; import * as DiyPageApi from '#/api/mall/promotion/diy/page'; -// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找 import * as DiyTemplateApi from '#/api/mall/promotion/diy/template'; import { DiyEditor, PAGE_LIBS } from '#/views/mall/promotion/components'; /** 装修模板表单 */ defineOptions({ name: 'DiyTemplateDecorate' }); -// 左上角工具栏操作按钮 +const route = useRoute(); +const { refreshTab } = useTabs(); + +const DIY_PAGE_INDEX_KEY = 'diy_page_index'; // 特殊:存储 reset 重置时,当前 selectedTemplateItem 值,从而进行恢复 + const selectedTemplateItem = ref(0); const templateItems = reactive([ { name: '基础设置', icon: 'ep:iphone' }, { name: '首页', icon: 'ep:home-filled' }, { name: '我的', icon: 'ep:user-filled' }, -]); +]); // 左上角工具栏操作按钮 -const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref(); -const formRef = ref(); // 表单 Ref -// 当前编辑的属性 const currentFormData = ref< MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty >({ property: '', -} as MallDiyPageApi.DiyPage); -// templateItem 对应的缓存 +} as MallDiyPageApi.DiyPage); // 当前编辑的属性 const currentFormDataMap = ref< Map ->(new Map()); -// 商城 H5 预览地址 -const previewUrl = ref(''); +>(new Map()); // templateItem 对应的缓存 -// 获取详情 -const getPageDetail = async (id: any) => { - formLoading.value = true; +const previewUrl = ref(''); // 商城 H5 预览地址 + +const templateLibs = [] as DiyComponentLibrary[]; // 模板组件库 +const libs = ref(templateLibs); // 当前组件库 + +/** 获取详情 */ +async function getPageDetail(id: any) { + const loadingInstance = ElLoading.service({ + text: '加载中...', + }); try { formData.value = await DiyTemplateApi.getDiyTemplateProperty(id); + // 拼接手机预览链接 const domain = import.meta.env.VITE_MALL_H5_DOMAIN; const accessStore = useAccessStore(); previewUrl.value = `${domain}?templateId=${formData.value.id}&${accessStore.tenantId}`; } finally { - formLoading.value = false; + loadingInstance.close(); } -}; +} -// 模板组件库 -const templateLibs = [] as DiyComponentLibrary[]; -// 当前组件库 -const libs = ref(templateLibs); -// 模板选项切换 -const handleTemplateItemChange = (val: number) => { +/** 模板选项切换 */ +function handleTemplateItemChange(val: any) { // 缓存模版编辑数据 currentFormDataMap.value.set( templateItems[selectedTemplateItem.value]?.name || '', @@ -74,7 +82,8 @@ const handleTemplateItemChange = (val: number) => { // 切换模版 selectedTemplateItem.value = val; - // 编辑模板 + + // 情况一:编辑模板 if (val === 0) { libs.value = templateLibs; currentFormData.value = (isEmpty(data) ? formData.value : data) as @@ -83,7 +92,7 @@ const handleTemplateItemChange = (val: number) => { return; } - // 编辑页面 + // 情况二:编辑页面 libs.value = PAGE_LIBS; currentFormData.value = ( isEmpty(data) @@ -93,14 +102,13 @@ const handleTemplateItemChange = (val: number) => { ) : data ) as MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty; -}; +} -// 提交表单 -const submitForm = async () => { - // 校验表单 - if (!formRef.value) return; - // 提交请求 - formLoading.value = true; +/** 提交表单 */ +async function submitForm() { + const loadingInstance = ElLoading.service({ + text: '保存中...', + }); try { // 对所有的 templateItems 都进行保存,有缓存则保存缓存,解决都有修改时只保存了当前所编辑的 templateItem,导致装修效果存在差异 for (const [i, templateItem] of templateItems.entries()) { @@ -109,14 +117,14 @@ const submitForm = async () => { if (i === 0) { // 提交模板属性 await DiyTemplateApi.updateDiyTemplateProperty( - isEmpty(data) ? unref(formData)! : data, + isEmpty(data) ? formData.value! : data, ); continue; } // 提交页面属性 // 情况二:提交当前正在编辑的页面 if (currentFormData.value?.name.includes(templateItem.name)) { - await DiyPageApi.updateDiyPageProperty(unref(currentFormData)!); + await DiyPageApi.updateDiyPageProperty(currentFormData.value!); continue; } // 情况三:提交页面编辑缓存 @@ -126,36 +134,18 @@ const submitForm = async () => { } ElMessage.success('保存成功'); } finally { - formLoading.value = false; + loadingInstance.close(); } -}; +} -// 重置表单 -const resetForm = () => { - formData.value = { - id: undefined, - name: '', - used: false, - usedTime: undefined, - remark: '', - previewPicUrls: [], - property: '', - pages: [], - } as MallDiyTemplateApi.DiyTemplateProperty; - formRef.value?.resetFields(); -}; - -// 重置时记录当前编辑的页面 -const handleEditorReset = () => storePageIndex(); - -// #region 无感刷新 -// 记录标识 -const DIY_PAGE_INDEX_KEY = 'diy_page_index'; -// 1. 记录 -const storePageIndex = () => +/** 刷新当前 Tab */ +function handleEditorReset() { sessionStorage.setItem(DIY_PAGE_INDEX_KEY, `${selectedTemplateItem.value}`); -// 2. 恢复 -const recoverPageIndex = () => { + refreshTab(); +} + +/** 恢复当前 Tab 之前的 index(selectedTemplateItem) */ +function recoverPageIndex() { // 恢复重置前的页面,默认是第一个页面 const pageIndex = Number(sessionStorage.getItem(DIY_PAGE_INDEX_KEY)) || 0; // 移除标记 @@ -173,26 +163,24 @@ const recoverPageIndex = () => { if (pageIndex !== selectedTemplateItem.value) { handleTemplateItemChange(pageIndex); } -}; -// #endregion +} /** 初始化 */ -const { currentRoute } = useRouter(); onMounted(async () => { - resetForm(); - if (!currentRoute.value.params.id) { + if (!route.params.id) { ElMessage.warning('参数错误,页面编号不能为空!'); return; } // 查询详情 - await getPageDetail(currentRoute.value.params.id); + formData.value = {} as MallDiyTemplateApi.DiyTemplateProperty; + await getPageDetail(route.params.id); // 恢复重置前的页面 recoverPageIndex(); }); diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/index.vue b/apps/web-ele/src/views/mall/promotion/diy/template/index.vue index 74abcb732..456b50d13 100644 --- a/apps/web-ele/src/views/mall/promotion/diy/template/index.vue +++ b/apps/web-ele/src/views/mall/promotion/diy/template/index.vue @@ -33,32 +33,38 @@ function handleRefresh() { gridApi.query(); } -/** 创建DIY模板 */ +/** 创建 DIY 模板 */ function handleCreate() { formModalApi.setData(null).open(); } -/** 编辑DIY模板 */ +/** 编辑 DIY 模板 */ function handleEdit(row: MallDiyTemplateApi.DiyTemplate) { formModalApi.setData(row).open(); } /** 装修模板 */ function handleDecorate(row: MallDiyTemplateApi.DiyTemplate) { - // 跳转到装修页面 router.push({ name: 'DiyTemplateDecorate', params: { id: row.id } }); } /** 使用模板 */ async function handleUse(row: MallDiyTemplateApi.DiyTemplate) { await confirm(`是否使用模板"${row.name}"?`); - // 发起删除 - await useDiyTemplate(row.id as number); - ElMessage.success('使用成功'); - handleRefresh(); + + const loadingInstance = ElLoading.service({ + text: `正在使用模板"${row.name}"...`, + }); + try { + await useDiyTemplate(row.id as number); + ElMessage.success('使用成功'); + handleRefresh(); + } finally { + loadingInstance.close(); + } } -/** 删除DIY模板 */ +/** 删除 DIY 模板 */ async function handleDelete(row: MallDiyTemplateApi.DiyTemplate) { const loadingInstance = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]), diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue b/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue index 886d3946e..8e088d7cf 100644 --- a/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue @@ -16,9 +16,7 @@ import { $t } from '#/locales'; import { useFormSchema } from '../data'; -/** 提交表单 */ const emit = defineEmits(['success']); - const formData = ref(); const getTitle = computed(() => { return formData.value?.id @@ -47,15 +45,6 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); // 提交表单 const data = (await formApi.getValues()) as MallDiyTemplateApi.DiyTemplate; - - // 确保必要的默认值 - if (!data.previewPicUrls) { - data.previewPicUrls = []; - } - if (data.used === undefined) { - data.used = false; - } - try { await (formData.value?.id ? updateDiyTemplate(data) @@ -91,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({