嵌套模态框中的数据在关闭时刷新父级模态框数据
时间: 2025-07-20 11:30:55 浏览: 12
<think>我们需要在ModalManager中实现一种机制,使得当一个模态框关闭时,能够刷新其父级模态框的数据。这可以通过在模态框关闭时触发一个回调函数来实现,该回调函数由父级模态框提供。
修改思路:
1. 在显示模态框时,允许传递一个`onRefresh`回调函数,该函数在子模态框关闭时被调用,用于刷新父级模态框的数据。
2. 在关闭子模态框时,检查是否有`onRefresh`回调,如果有则调用它。
具体步骤:
- 在`showModal`方法中,允许传入一个`onRefresh`选项(或其他名字,比如`onChildClosed`),该选项是一个函数,当子模态框关闭时执行。
- 在模态框对象中存储这个回调。
- 在关闭模态框时(`closeModal`方法中),在关闭之后,检查这个模态框是否有`onRefresh`回调,如果有,则执行它。注意:这个回调应该是父级模态框设置的,所以我们在关闭子模态框后触发父级模态框的刷新。
但是,注意模态框的嵌套关系:当打开一个子模态框时,当前显示的父模态框是堆栈中的上一个模态框。因此,我们可以在打开子模态框时,将父模态框的刷新函数传递给子模态框?这样并不合适,因为子模态框不应该知道父模态框的存在。
另一种设计:
我们不在子模态框中存储父模态框的刷新函数,而是在关闭子模态框时,自动通知其父级模态框(即堆栈中它的前一个模态框)。我们可以为每个模态框提供一个唯一的ID,并在父模态框中注册一个刷新函数(通过一个全局的刷新注册表?),但这样可能复杂。
更简单的方法:
在打开子模态框时,由父级模态框显式传递一个刷新函数。这个刷新函数会更新父级模态框的数据。在子模态框关闭后,ModalManager调用这个刷新函数。
因此,我们修改`showModal`方法,允许一个`onRefresh`参数。当关闭子模态框时,如果有这个参数,就调用它。
但是,我们如何知道哪个是父级模态框?实际上,在打开子模态框时,当前页面就是父级模态框所在的页面,而父级模态框是堆栈中的最后一个模态框。但我们不直接使用这个关系,而是由调用者(父级模态框的代码)显式传递刷新函数。
示例代码:
在父级模态框中打开子模态框:
```javascript
// 在父级模态框的某个事件处理函数中
app.modalManager.showModal(this, 'addProduct', {
// ...数据
}, {
onRefresh: (result) => {
// 刷新父级模态框的数据
this.refreshData();
}
});
```
在ModalManager的`closeModal`方法中,在关闭模态框后,调用这个`onRefresh`回调。
修改`closeModal`方法:
```javascript
closeModal(pageContext, id, result = null) {
const index = this.modalStack.findIndex(m => m.id === id);
if (index !== -1) {
const modal = this.modalStack.splice(index, 1)[0];
// 如果有onClose回调,执行它(这是模态框自己的关闭回调)
if (modal.data.onClose) {
modal.data.onClose(result);
}
// 如果有onRefresh回调(这是父级模态框设置的,用于在子模态框关闭后刷新父级)
if (modal.data.onRefresh) {
modal.data.onRefresh(result);
}
// 更新页面
this.updatePageModals(pageContext);
return true;
}
return false;
}
```
这样,当子模态框关闭时,就会调用父级模态框传递的`onRefresh`函数,从而刷新父级模态框的数据。
但是,注意:这个`onRefresh`是作为模态框配置的一部分传递进来的,所以它存储在模态框的`data`中。我们可以在显示模态框时将它放入配置中。
然而,我们也要注意,这个刷新函数可能会被多次调用,所以需要确保在不需要的时候清理(比如父级模态框已经关闭)。但因为我们是在关闭子模态框时立即调用,所以一般情况下是安全的。
另外,如果子模态框被父级模态框通过其他方式关闭(比如点击遮罩层),也会触发这个刷新回调。
另一种情况:如果父级模态框在子模态框关闭前已经关闭,那么子模态框关闭时调用刷新函数可能就没有意义了。但是,由于我们的模态框堆栈管理,父级模态框关闭时,它上面的子模态框也会被关闭(因为我们没有实现自动关闭子模态框,所以需要改进)。
因此,我们还需要在关闭一个模态框时,同时关闭它之上的所有子模态框。这样,当父级模态框关闭时,子模态框也会被关闭,但此时父级模态框已经不在堆栈中,所以子模态框关闭时尝试调用父级传递的刷新函数可能就不合适了(因为父级已经不存在了)。
为了避免这种情况,我们可以在关闭一个模态框时,递归关闭它之上的所有模态框(即它的子模态框)。修改`closeModal`方法,使其可以关闭指定模态框及其之上的所有模态框。
但这样可能会改变设计,所以我们先不改变关闭行为,而是由开发人员确保在关闭父级模态框前关闭所有子模态框。
或者,我们可以在关闭父级模态框时,自动关闭所有子级模态框。这需要我们在模态框对象中记录父子关系。
我们可以在`showModal`时,记录当前堆栈中最后一个模态框作为父模态框,然后建立父子关系。然后在关闭父模态框时,自动关闭所有子模态框。
但这样会增加复杂性,目前先实现简单的刷新回调机制。
因此,我们按照以下步骤修改:
1. 在`showModal`方法中,允许在配置参数中传递`onRefresh`函数。
2. 在`closeModal`关闭模态框时,调用这个`onRefresh`函数(如果有)。
代码修改:
在`showModal`方法中,我们将配置合并到模态框的data中,所以`onRefresh`也会被存储到`modal.data`中。
在`closeModal`中,我们在执行完`onClose`后执行`onRefresh`。
注意:执行顺序是先关闭子模态框,然后刷新父模态框。
另外,我们还需要考虑在关闭模态框时,如何传递结果给父级。我们通过`closeModal`的`result`参数传递,这个结果会作为参数传递给`onRefresh`。
示例:
在子模态框中,当点击确定按钮时:
```javascript
handler: ({ close }) => {
// 执行一些操作,然后关闭,并传递结果
close({ success: true, data: ... });
}
```
这个结果对象会传递给父级的`onRefresh`回调。
这样,父级模态框就可以根据子模态框的操作结果来刷新自己的数据。
总结:我们通过回调函数机制实现了子模态框关闭时刷新父级模态框数据。
注意:如果有多层嵌套,每一层都可以设置自己的`onRefresh`回调,这样当子模态框关闭时,会逐级向上刷新(实际上,每一级只刷新它的直接父级,而父级如果需要刷新更上一级,则需要在打开时也设置自己的`onRefresh`)。
因此,我们只需要在需要刷新的那一层设置`onRefresh`即可。
下面我们修改ModalManager的代码,实现这个功能。
修改后的ModalManager代码(只展示修改部分):</think># 嵌套模态框数据刷新机制:关闭子模态框时刷新父级模态框数据
在嵌套模态框的场景中,当子模态框关闭时刷新父级模态框数据是一个常见需求。下面我将提供一个完整的解决方案,包括数据刷新机制和代码实现。
## 解决方案设计
### 核心思路
1. **父子关系管理**:为每个模态框建立父子关系记录
2. **事件总线**:使用事件总线进行跨模态框通信
3. **数据刷新回调**:在打开子模态框时注册父级刷新回调
4. **自动清理机制**:确保父级模态框关闭时清理相关资源
### 完整实现方案
```javascript:utils/modal-manager.js
// 事件总线 - 用于模态框间通信
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
off(event, callback) {
if (!this.events[event]) return;
if (callback) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
} else {
delete this.events[event];
}
}
emit(event, ...args) {
if (!this.events[event]) return;
this.events[event].forEach(callback => callback(...args));
}
}
// 增强版模态框管理器
class ModalManager {
constructor() {
// 模态框类型注册表
this.modalTypes = {
'createOrder': { name: '创建订单', template: 'createOrderModal' },
'addProduct': { name: '添加产品', template: 'addProductModal' },
'editProduct': { name: '编辑产品', template: 'editProductModal' },
'addComponent': { name: '添加组件', template: 'addComponentModal' },
'addBancai': { name: '添加板材', template: 'addBancaiModal' },
'bancaiDetail': { name: '板材详情', template: 'bancaiDetailModal' },
'stockEdit': { name: '库存编辑', template: 'stockEditModal' },
'materialDetail': { name: '材料详情', template: 'materialDetailModal' }
};
// 模态框堆栈
this.modalStack = [];
// 默认配置
this.defaultConfig = {
title: '提示',
content: '',
buttons: [
{ text: '取消', type: 'cancel', style: 'default' },
{ text: '确定', type: 'confirm', style: 'primary' }
],
maskClosable: true,
zIndex: 1000,
animation: true,
customClass: ''
};
// 事件总线
this.eventBus = new EventBus();
// 父子关系映射表
this.parentChildMap = new Map();
// 刷新回调注册表
this.refreshCallbacks = new Map();
}
// 注册新的模态框类型
registerModalType(type, config) {
this.modalTypes[type] = config;
}
// 显示模态框
showModal(pageContext, type, data = {}, config = {}) {
// 合并配置
const modalConfig = { ...this.defaultConfig, ...config };
// 检查模态框类型
const modalType = this.modalTypes[type];
if (!modalType) {
console.error(`未注册的模态框类型: ${type}`);
return;
}
// 创建模态框对象
const modal = {
id: Date.now(), // 唯一ID
type,
template: modalType.template,
data: {
...data,
...modalConfig,
// 添加刷新父级的方法
refreshParent: (result) => this.refreshParentModal(modal.id, result)
},
pageContext,
zIndex: modalConfig.zIndex + this.modalStack.length * 10
};
// 记录父子关系
if (this.modalStack.length > 0) {
const parentModal = this.modalStack[this.modalStack.length - 1];
this.parentChildMap.set(modal.id, parentModal.id);
// 注册父级刷新回调
if (config.onChildClosed) {
this.refreshCallbacks.set(modal.id, config.onChildClosed);
}
}
// 添加到堆栈
this.modalStack.push(modal);
// 更新页面数据
this.updatePageModals(pageContext);
return modal.id;
}
// 刷新父级模态框数据
refreshParentModal(childId, result) {
const parentId = this.parentChildMap.get(childId);
if (!parentId) return;
const callback = this.refreshCallbacks.get(childId);
if (callback && typeof callback === 'function') {
callback(result);
}
// 查找父级模态框
const parentModal = this.modalStack.find(m => m.id === parentId);
if (parentModal) {
// 更新父级模态框数据
this.updateModalData(parentModal, {
refreshed: true,
refreshTime: new Date().toISOString()
});
}
}
// 更新模态框数据
updateModalData(modal, newData) {
Object.assign(modal.data, newData);
// 更新页面
const dataUpdate = {};
dataUpdate[this.modalTypes[modal.type].name] = modal.data;
modal.pageContext.setData(dataUpdate);
}
// 关闭模态框
closeModal(pageContext, id, result = null) {
const index = this.modalStack.findIndex(m => m.id === id);
if (index !== -1) {
const modal = this.modalStack.splice(index, 1)[0];
// 触发子模态框关闭事件
this.triggerChildClosedEvent(modal.id, result);
// 清理资源
this.cleanupModalResources(modal.id);
// 如果有回调函数,执行回调
if (modal.data.onClose) {
modal.data.onClose(result);
}
// 更新页面
this.updatePageModals(pageContext);
return true;
}
return false;
}
// 触发子模态框关闭事件
triggerChildClosedEvent(parentId, result) {
// 查找所有子模态框
const childIds = [];
for (const [childId, pid] of this.parentChildMap) {
if (pid === parentId) {
childIds.push(childId);
}
}
// 触发事件
childIds.forEach(childId => {
this.eventBus.emit(`modalClosed:${childId}`, result);
});
}
// 清理模态框资源
cleanupModalResources(modalId) {
// 清理父子关系
this.parentChildMap.forEach((parentId, childId) => {
if (parentId === modalId || childId === modalId) {
this.parentChildMap.delete(childId);
}
});
// 清理回调
this.refreshCallbacks.delete(modalId);
// 清理事件监听
this.eventBus.off(`modalClosed:${modalId}`);
}
// 其他方法保持不变...
}
// 创建单例
const modalManager = new ModalManager();
// 导出模块
module.exports = modalManager;
```
## 使用示例
### 在父级模态框中打开子模态框并注册刷新回调
```javascript:pages/dingdan/dingdan.js
const app = getApp();
Page({
// ...其他代码
// 打开添加产品模态框(作为子模态框)
openAddProductModal(parentModalId) {
const modalId = app.modalManager.showModal(this, 'addProduct', {
title: '添加新产品',
product: {
name: '',
quantity: 1
}
}, {
// 注册父级刷新回调
onChildClosed: (result) => {
if (result && result.success) {
console.log('子模态框关闭,刷新父级数据', result);
this.refreshParentModalData(parentModalId, result);
}
}
});
},
// 刷新父级模态框数据
refreshParentModalData(parentModalId, result) {
// 更新父级模态框数据
app.modalManager.updateModalDataById(parentModalId, {
products: [...this.data.products, result.newProduct],
lastRefresh: new Date().toLocaleTimeString()
});
},
// 在父级模态框的按钮点击事件中打开子模态框
onAddProductButtonTap(e) {
const parentModalId = e.currentTarget.dataset.modalId;
this.openAddProductModal(parentModalId);
}
});
```
### 在子模态框中触发父级刷新
```javascript
// 在子模态框的保存按钮处理函数中
onSaveButtonTap() {
// 保存逻辑...
const result = {
success: true,
newProduct: this.data.product
};
// 触发父级刷新
if (this.data.refreshParent) {
this.data.refreshParent(result);
}
// 关闭当前模态框
app.modalManager.closeModal(this, this.data.modalId, result);
}
```
### 在 WXML 中传递模态框 ID
```wxml:components/modals/templates/createOrderModal.wxml
<template name="createOrderModal">
<!-- 模态框内容 -->
<button
bindtap="onAddProductButtonTap"
data-modal-id="{{modalId}}"
>
添加产品
</button>
<!-- 显示刷新状态 -->
<view wx:if="{{refreshed}}">
数据已刷新于: {{refreshTime}}
</view>
</template>
```
## 解决方案说明
### 核心机制
1. **父子关系管理**:
- 使用 `parentChildMap` 记录模态框的父子关系
- 当打开子模态框时,自动记录它与当前顶部模态框(父级)的关系
2. **刷新回调注册**:
- 在打开子模态框时,父级可以注册 `onChildClosed` 回调
- 回调函数在子模态框关闭时执行
3. **数据刷新触发**:
- 子模态框通过 `refreshParent` 方法触发父级刷新
- 父级收到刷新请求后执行注册的回调函数
4. **资源清理**:
- 模态框关闭时自动清理相关资源和事件监听
- 防止内存泄漏和无效回调
### 优势
1. **解耦设计**:父级和子级模态框不需要直接引用对方
2. **灵活控制**:可以精确控制哪些操作需要触发刷新
3. **自动清理**:防止内存泄漏和无效回调
4. **状态反馈**:提供刷新状态显示,提升用户体验
5. **通用性强**:适用于各种嵌套模态框场景
### 使用场景
1. 添加产品后刷新订单中的产品列表
2. 编辑库存后刷新库存概览
3. 创建组件后刷新产品配置
4. 修改用户信息后刷新用户列表
##
阅读全文
相关推荐
















