|
|
@@ -272,6 +272,17 @@ Echo 回调收到的对象:
|
|
|
|
|
|
字段含义与各列表接口文档一致,这里不重复。
|
|
|
|
|
|
+## 操作弹窗与列表更新
|
|
|
+
|
|
|
+**打开审核、编辑、打款等操作弹窗时,暂停当前列表的自动增删、替换和排序。** 收到推送后只标记「列表有更新」;弹窗全部关闭后再按原筛选、分页刷新一次。断线补发要求 `resync` 时也遵守同一个暂停规则,不能绕过它直接刷新列表。
|
|
|
+
|
|
|
+- 从点击操作按钮、开始读取详情时就暂停;详情请求失败或弹窗实际关闭后再释放。嵌套弹窗全部关闭后才恢复,不能只在点击提交按钮时提前恢复。
|
|
|
+- 弹窗草稿使用独立数据副本,不直接绑定表格行对象;弹窗开关由独立状态控制,表格使用记录 `id` 等稳定主键,避免刷新时重建整个页面。
|
|
|
+- 当前记录被其他人处理或删除时,弹窗提示状态已变化,不自动关闭或覆盖输入;提交时后端仍须校验最新状态,避免重复审核或打款。
|
|
|
+- 暂停范围只覆盖正在操作的列表。平台在线人数和待处理角标继续更新,不用冻结整个后台。
|
|
|
+
|
|
|
+下方参考示例提供 `holdOrderListUpdates()`:操作开始时调用一次,把返回的释放函数绑定到实际弹窗关闭和详情读取失败的路径。前端需接入真实弹窗生命周期,示例不会自动识别业务弹窗。
|
|
|
+
|
|
|
## 平台在线人数和待处理角标
|
|
|
|
|
|
两个统计频道均监听 **`.stats.updated`**,`action` 为 `updated`、`key` 为 `{"scope":"all"}`,`data` 是对应 HTTP 接口的完整 `data` 对象。收到后直接替换统计对象,不逐项累加。
|
|
|
@@ -508,11 +519,44 @@ export class AdminRealtime {
|
|
|
}
|
|
|
|
|
|
// 体彩订单列表页
|
|
|
+let activeOrderDialogs = 0;
|
|
|
+let orderListNeedsSync = false;
|
|
|
+
|
|
|
+// 点击审核/编辑等操作时先调用;返回函数在弹窗实际关闭或详情加载失败时调用。
|
|
|
+// 每次操作各持有一个释放函数,可嵌套,重复调用同一释放函数不会提前解锁。
|
|
|
+function holdOrderListUpdates() {
|
|
|
+ activeOrderDialogs++;
|
|
|
+ let released = false;
|
|
|
+ return async () => {
|
|
|
+ if (released) return;
|
|
|
+ released = true;
|
|
|
+ activeOrderDialogs--;
|
|
|
+ if (activeOrderDialogs === 0 && orderListNeedsSync) {
|
|
|
+ orderListNeedsSync = false;
|
|
|
+ try { await fetchList(); }
|
|
|
+ catch (error) { orderListNeedsSync = true; throw error; }
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function resyncOrderList() {
|
|
|
+ if (activeOrderDialogs > 0) {
|
|
|
+ orderListNeedsSync = true;
|
|
|
+ return; // 延后至弹窗关闭;关闭后必须执行上面的完整列表同步
|
|
|
+ }
|
|
|
+ await fetchList();
|
|
|
+}
|
|
|
+
|
|
|
realtime.watch('admin.orders.sport', ['order.created', 'order.updated', 'order.deleted'], (e) => {
|
|
|
+ if (activeOrderDialogs > 0) {
|
|
|
+ orderListNeedsSync = true;
|
|
|
+ // 可显示「列表有更新」;当前操作记录的状态变化提示由弹窗自己处理。
|
|
|
+ return;
|
|
|
+ }
|
|
|
const index = rows.findIndex((r) => r.id === e.key.id);
|
|
|
if (e.action === 'deleted') return index >= 0 && rows.splice(index, 1);
|
|
|
if (!matchesFilters(e.data)) return index >= 0 && rows.splice(index, 1);
|
|
|
if (index >= 0) rows.splice(index, 1, e.data);
|
|
|
else if (e.action === 'created' && page === 1) rows.unshift(e.data);
|
|
|
-}, () => fetchList());
|
|
|
+}, resyncOrderList);
|
|
|
```
|