From 26587ac09aa5be743bfdeabd4781f1b3db208bf6 Mon Sep 17 00:00:00 2001 From: dap <15891557205@163.com> Date: Sat, 5 Apr 2025 13:33:15 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E5=BC=B9=E7=AA=97=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=94=B9=E5=85=B3=E9=97=AD=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=8F=90=E7=A4=BA=E6=A1=86(=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E6=9C=80=E7=BB=88=E4=B8=8D=E4=BC=9A=E5=8A=A0=E5=85=A5)=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=A1=B5=E9=9D=A2:=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 +- apps/web-antd/src/utils/popup.ts | 117 ++++++++++++++++++ .../src/views/system/config/config-modal.vue | 18 ++- 3 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 apps/web-antd/src/utils/popup.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 94198281..df3796a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,11 +26,15 @@ - TableSwitch组件重构 - 管理员租户切换不再返回首页 直接刷新当前页(除特殊页面外会回到首页) - 租户切换Select增加loading -- modalLoading/drawerLoading改为调用内部的lock/unlock方法 +- ~~modalLoading/drawerLoading改为调用内部的lock/unlock方法~~ 有待商榷暂时按老版本逻辑不变 - 登录验证码 增加loading - DictEnum使用const代替enum - TinyMCE组件重构 移除冗余代码/功能 增加loading +**ALPHA功能** + +- 弹窗表单数据更改关闭时的提示框(可能最终不会加入) 测试页面: 参数管理 + **BUG FIX** - 重新登录 字典会unknown的情况[详细分析](https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IBY27D) diff --git a/apps/web-antd/src/utils/popup.ts b/apps/web-antd/src/utils/popup.ts new file mode 100644 index 00000000..0fa2e2e4 --- /dev/null +++ b/apps/web-antd/src/utils/popup.ts @@ -0,0 +1,117 @@ +import type { ExtendedFormApi } from '@vben/common-ui'; +import type { MaybePromise } from '@vben/types'; + +import { ref } from 'vue'; + +import { $t } from '@vben/locales'; + +import { Modal } from 'ant-design-vue'; +import { isFunction } from 'lodash-es'; + +interface BeforeCloseDiffProps { + /** + * 初始化值如何获取 + * @returns Promise + */ + initializedGetter: () => MaybePromise; + /** + * 当前值如何获取 + * @returns Promise + */ + currentGetter: () => MaybePromise; + /** + * 自定义比较函数 + * @param init 初始值 + * @param current 当前值 + * @returns boolean + */ + compare?: (init: string, current: string) => boolean; +} + +/** + * @deprecated 注意为实验性功能 可能有api变动/被移除 + * @param props props + * @returns hook + * + * 待解决问题: 网速慢情况直接关闭 会导致数据不一致问题 + * 但是使用api.lock会导致在报错情况无法关闭(因为目前代码没有finally) + */ +export function useBeforeCloseDiff(props: BeforeCloseDiffProps) { + const { initializedGetter, currentGetter, compare } = props; + const initialized = ref(''); + const isInitialized = ref(false); + const isSubmitted = ref(false); + + async function updateInitialized(data?: string) { + initialized.value = data || (await initializedGetter()); + isInitialized.value = true; + } + + function setSubmitted() { + isSubmitted.value = true; + } + + async function onBeforeClose(): Promise { + // 如果还未初始化,直接允许关闭 + if (!isInitialized.value) { + return true; + } + // 如果已经提交过,直接允许关闭 + if (isSubmitted.value) { + // 重置状态 + isSubmitted.value = false; + return true; + } + + try { + const current = await currentGetter(); + + if (isFunction(compare) && compare(initialized.value, current)) { + return true; + } else { + // 如果数据没有变化,直接允许关闭 + if (current === initialized.value) { + return true; + } + } + + // 数据有变化,显示确认对话框 + return new Promise((resolve) => { + Modal.confirm({ + title: $t('pages.common.tip'), + content: $t('您有未保存的更改,确认要退出吗?'), + centered: true, + okButtonProps: { danger: true }, + cancelText: $t('common.cancel'), + okText: $t('common.confirm'), + onOk: () => { + resolve(true); + isInitialized.value = false; + }, + onCancel: () => resolve(false), + }); + }); + } catch (error) { + console.error('Failed to compare data:', error); + return true; + } + } + + return { + onBeforeClose, + updateInitialized, + setSubmitted, + }; +} + +/** + * 给useVbenForm使用的 封装函数 + * @param formApi 表单实例 + * @returns getter + */ +export function defaultFormValueGetter(formApi: ExtendedFormApi) { + return async () => { + const v = await formApi.getValues(); + return JSON.stringify(v); + }; +} diff --git a/apps/web-antd/src/views/system/config/config-modal.vue b/apps/web-antd/src/views/system/config/config-modal.vue index 9569f0c5..1094c231 100644 --- a/apps/web-antd/src/views/system/config/config-modal.vue +++ b/apps/web-antd/src/views/system/config/config-modal.vue @@ -7,6 +7,7 @@ import { cloneDeep } from '@vben/utils'; import { useVbenForm } from '#/adapter/form'; import { configAdd, configInfo, configUpdate } from '#/api/system/config'; +import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup'; import { modalSchema } from './data'; @@ -25,9 +26,15 @@ const [BasicForm, formApi] = useVbenForm({ showDefaultActions: false, }); +const { onBeforeClose, updateInitialized, setSubmitted } = useBeforeCloseDiff({ + initializedGetter: defaultFormValueGetter(formApi), + currentGetter: defaultFormValueGetter(formApi), +}); + const [BasicModal, modalApi] = useVbenModal({ fullscreenButton: false, - onCancel: handleCancel, + onBeforeClose, + onClosed: handleClosed, onConfirm: handleConfirm, onOpenChange: async (isOpen) => { if (!isOpen) { @@ -42,6 +49,7 @@ const [BasicModal, modalApi] = useVbenModal({ const record = await configInfo(id); await formApi.setValues(record); } + await updateInitialized(); modalApi.modalLoading(false); }, @@ -56,8 +64,9 @@ async function handleConfirm() { } const data = cloneDeep(await formApi.getValues()); await (isUpdate.value ? configUpdate(data) : configAdd(data)); + setSubmitted(); emit('reload'); - await handleCancel(); + modalApi.close(); } catch (error) { console.error(error); } finally { @@ -65,14 +74,13 @@ async function handleConfirm() { } } -async function handleCancel() { - modalApi.close(); +async function handleClosed() { await formApi.resetForm(); }