Browse Source

🔄 fix(tables): keep current page after edits & auto-fallback when page becomes empty

Includes ChannelsTable, RedemptionsTable and UsersTable:

• Refactor `refresh(page = activePage)` in all three tables so data reloads the requested (or current) page instead of forcing page 1.
• On single-row deletion (and bulk deletion in ChannelsTable):
  – Refresh current page immediately.
  – If the refreshed page has no data and `activePage > 1`, automatically load the previous page to avoid blank views.
• RedemptionsTable: corrected prior bug where `refresh` used `activePage - 1`.
• Misc: removed outdated inline comments and aligned search / reset flows.

Result: smoother UX—users stay on their working page, and pagination gracefully adjusts after deletions.
t0ng7u 6 months ago
parent
commit
4f06a1df50

+ 17 - 6
web/src/components/table/ChannelsTable.js

@@ -547,9 +547,15 @@ const ChannelsTable = () => {
                   title: t('确定是否要删除此渠道?'),
                   content: t('此修改将不可逆'),
                   onOk: () => {
-                    manageChannel(record.id, 'delete', record).then(() => {
-                      removeRecord(record);
-                    });
+                    (async () => {
+                      await manageChannel(record.id, 'delete', record);
+                      await refresh();
+                      setTimeout(() => {
+                        if (channels.length === 0 && activePage > 1) {
+                          refresh(activePage - 1);
+                        }
+                      }, 100);
+                    })();
                   },
                 });
               },
@@ -1004,12 +1010,12 @@ const ChannelsTable = () => {
     }
   };
 
-  const refresh = async () => {
+  const refresh = async (page = activePage) => {
     const { searchKeyword, searchGroup, searchModel } = getFormValues();
     if (searchKeyword === '' && searchGroup === '' && searchModel === '') {
-      await loadChannels(activePage, pageSize, idSort, enableTagMode);
+      await loadChannels(page, pageSize, idSort, enableTagMode);
     } else {
-      await searchChannels(enableTagMode, activeTypeKey, statusFilter, activePage, pageSize, idSort);
+      await searchChannels(enableTagMode, activeTypeKey, statusFilter, page, pageSize, idSort);
     }
   };
 
@@ -1569,6 +1575,11 @@ const ChannelsTable = () => {
     if (success) {
       showSuccess(t('已删除 ${data} 个通道!').replace('${data}', data));
       await refresh();
+      setTimeout(() => {
+        if (channels.length === 0 && activePage > 1) {
+          refresh(activePage - 1);
+        }
+      }, 100);
     } else {
       showError(message);
     }

+ 16 - 6
web/src/components/table/RedemptionsTable.js

@@ -151,9 +151,15 @@ const RedemptionsTable = () => {
                 title: t('确定是否要删除此兑换码?'),
                 content: t('此修改将不可逆'),
                 onOk: () => {
-                  manageRedemption(record.id, 'delete', record).then(() => {
-                    removeRecord(record.key);
-                  });
+                  (async () => {
+                    await manageRedemption(record.id, 'delete', record);
+                    await refresh();
+                    setTimeout(() => {
+                      if (redemptions.length === 0 && activePage > 1) {
+                        refresh(activePage - 1);
+                      }
+                    }, 100);
+                  })();
                 },
               });
             },
@@ -320,8 +326,13 @@ const RedemptionsTable = () => {
       });
   }, [pageSize]);
 
-  const refresh = async () => {
-    await loadRedemptions(activePage - 1, pageSize);
+  const refresh = async (page = activePage) => {
+    const { searchKeyword } = getFormValues();
+    if (searchKeyword === '') {
+      await loadRedemptions(page, pageSize);
+    } else {
+      await searchRedemptions(searchKeyword, page, pageSize);
+    }
   };
 
   const manageRedemption = async (id, action, record) => {
@@ -541,7 +552,6 @@ const RedemptionsTable = () => {
                 onClick={() => {
                   if (formApi) {
                     formApi.reset();
-                    // 重置后立即查询,使用setTimeout确保表单重置完成
                     setTimeout(() => {
                       setActivePage(1);
                       loadRedemptions(1, pageSize);

+ 57 - 51
web/src/components/table/TokensTable.js

@@ -61,6 +61,52 @@ const TokensTable = () => {
       title: t('名称'),
       dataIndex: 'name',
     },
+    {
+      title: t('剩余'),
+      key: 'quota',
+      render: (text, record) => {
+        if (record.unlimited_quota) {
+          return <Tag color='white' shape='circle'>{t('无限制')}</Tag>;
+        }
+
+        const used = parseInt(record.used_quota) || 0;
+        const remain = parseInt(record.remain_quota) || 0;
+        const total = used + remain;
+        // 计算剩余额度百分比,100% 表示额度未使用
+        const percent = total > 0 ? (remain / total) * 100 : 0;
+
+        // 根据剩余百分比动态设置颜色,100% 绿色,<=10% 红色,<=30% 黄色,其余默认
+        const getProgressColor = (pct) => {
+          if (pct === 100) return 'var(--semi-color-success)';
+          if (pct <= 10) return 'var(--semi-color-danger)';
+          if (pct <= 30) return 'var(--semi-color-warning)';
+          return undefined; // 默认颜色
+        };
+
+        return (
+          <Tooltip
+            content={
+              <div className='text-xs'>
+                <div>{t('已用额度')}: {renderQuota(used)}</div>
+                <div>{t('剩余额度')}: {renderQuota(remain)} ({percent.toFixed(0)}%)</div>
+                <div>{t('总额度')}: {renderQuota(total)}</div>
+              </div>
+            }
+          >
+            <div className='w-[30px]'>
+              <Progress
+                percent={percent}
+                stroke={getProgressColor(percent)}
+                showInfo={false}
+                aria-label='quota usage'
+                type="circle"
+                size='small'
+              />
+            </div>
+          </Tooltip>
+        );
+      },
+    },
     {
       title: t('状态'),
       dataIndex: 'status',
@@ -172,52 +218,6 @@ const TokensTable = () => {
         );
       },
     },
-    {
-      title: t('剩余额度'),
-      key: 'quota',
-      render: (text, record) => {
-        if (record.unlimited_quota) {
-          return <Tag color='white' shape='circle'>{t('无限制')}</Tag>;
-        }
-
-        const used = parseInt(record.used_quota) || 0;
-        const remain = parseInt(record.remain_quota) || 0;
-        const total = used + remain;
-        // 计算剩余额度百分比,100% 表示额度未使用
-        const percent = total > 0 ? (remain / total) * 100 : 0;
-
-        // 根据剩余百分比动态设置颜色,100% 绿色,<=10% 红色,<=30% 黄色,其余默认
-        const getProgressColor = (pct) => {
-          if (pct === 100) return 'var(--semi-color-success)';
-          if (pct <= 10) return 'var(--semi-color-danger)';
-          if (pct <= 30) return 'var(--semi-color-warning)';
-          return undefined; // 默认颜色
-        };
-
-        return (
-          <Tooltip
-            content={
-              <div className='text-xs'>
-                <div>{t('已用额度')}: {renderQuota(used)}</div>
-                <div>{t('剩余额度')}: {renderQuota(remain)} ({percent.toFixed(0)}%)</div>
-                <div>{t('总额度')}: {renderQuota(total)}</div>
-              </div>
-            }
-          >
-            <div className='w-[30px]'>
-              <Progress
-                percent={percent}
-                stroke={getProgressColor(percent)}
-                showInfo={false}
-                aria-label='quota usage'
-                type="circle"
-                size='small'
-              />
-            </div>
-          </Tooltip>
-        );
-      },
-    },
     {
       title: t('可用模型'),
       dataIndex: 'model_limits',
@@ -427,9 +427,10 @@ const TokensTable = () => {
                   title: t('确定是否要删除此令牌?'),
                   content: t('此修改将不可逆'),
                   onOk: () => {
-                    manageToken(record.id, 'delete', record).then(() => {
-                      removeRecord(record.key);
-                    });
+                    (async () => {
+                      await manageToken(record.id, 'delete', record);
+                      await refresh();
+                    })();
                   },
                 });
               }}
@@ -503,8 +504,8 @@ const TokensTable = () => {
     setLoading(false);
   };
 
-  const refresh = async () => {
-    await loadTokens(1);
+  const refresh = async (page = activePage) => {
+    await loadTokens(page);
     setSelectedKeys([]);
   };
 
@@ -680,6 +681,11 @@ const TokensTable = () => {
         const count = res.data.data || 0;
         showSuccess(t('已删除 {{count}} 个令牌!', { count }));
         await refresh();
+        setTimeout(() => {
+          if (tokens.length === 0 && activePage > 1) {
+            refresh(activePage - 1);
+          }
+        }, 100);
       } else {
         showError(res?.data?.message || t('删除失败'));
       }

+ 12 - 8
web/src/components/table/UsersTable.js

@@ -247,9 +247,15 @@ const UsersTable = () => {
                 title: t('确定是否要注销此用户?'),
                 content: t('相当于删除用户,此修改将不可逆'),
                 onOk: () => {
-                  manageUser(record.id, 'delete', record).then(() => {
-                    removeRecord(record.id);
-                  });
+                  (async () => {
+                    await manageUser(record.id, 'delete', record);
+                    await refresh();
+                    setTimeout(() => {
+                      if (users.length === 0 && activePage > 1) {
+                        refresh(activePage - 1);
+                      }
+                    }, 100);
+                  })();
                 },
               });
             },
@@ -459,13 +465,12 @@ const UsersTable = () => {
     });
   };
 
-  const refresh = async () => {
-    setActivePage(1);
+  const refresh = async (page = activePage) => {
     const { searchKeyword, searchGroup } = getFormValues();
     if (searchKeyword === '' && searchGroup === '') {
-      await loadUsers(1, pageSize);
+      await loadUsers(page, pageSize);
     } else {
-      await searchUsers(1, pageSize, searchKeyword, searchGroup);
+      await searchUsers(page, pageSize, searchKeyword, searchGroup);
     }
   };
 
@@ -606,7 +611,6 @@ const UsersTable = () => {
                 onClick={() => {
                   if (formApi) {
                     formApi.reset();
-                    // 重置后立即查询,使用setTimeout确保表单重置完成
                     setTimeout(() => {
                       setActivePage(1);
                       loadUsers(1, pageSize);