refactor: share responsive batch actions across admin pages
This commit is contained in:
@@ -2,9 +2,9 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { api } from '../api.js';
|
||||
import CenteredModal from '../components/CenteredModal.js';
|
||||
import MobileBatchBar from '../components/MobileBatchBar.js';
|
||||
import MobileFilterSheet from '../components/MobileFilterSheet.js';
|
||||
import ResponsiveFormGrid from '../components/ResponsiveFormGrid.js';
|
||||
import ResponsiveBatchActionBar from '../components/ResponsiveBatchActionBar.js';
|
||||
import { useToast } from '../components/Toast.js';
|
||||
import ModernSelect from '../components/ModernSelect.js';
|
||||
import { MobileCard, MobileField } from '../components/MobileCard.js';
|
||||
@@ -1066,9 +1066,12 @@ export default function Accounts() {
|
||||
: <>确定要删除选中的 <strong>{deleteConfirm?.count || 0}</strong> 个连接吗?</>}
|
||||
/>
|
||||
|
||||
{!isMobile && activeSegment !== 'tokens' && selectedAccountIds.length > 0 && (
|
||||
<div className="card" style={{ padding: 12, marginBottom: 12, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600 }}>已选 {selectedAccountIds.length} 项</span>
|
||||
{activeSegment !== 'tokens' && selectedAccountIds.length > 0 && (
|
||||
<ResponsiveBatchActionBar
|
||||
isMobile={isMobile}
|
||||
info={`已选 ${selectedAccountIds.length} 项`}
|
||||
desktopStyle={{ marginBottom: 12 }}
|
||||
>
|
||||
<button data-testid="accounts-batch-refresh-balance" onClick={() => runBatchAccountAction('refreshBalance')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量刷新余额
|
||||
</button>
|
||||
@@ -1081,24 +1084,7 @@ export default function Accounts() {
|
||||
<button onClick={() => runBatchAccountAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isMobile && activeSegment !== 'tokens' && selectedAccountIds.length > 0 && (
|
||||
<MobileBatchBar info={`已选 ${selectedAccountIds.length} 项`}>
|
||||
<button data-testid="accounts-batch-refresh-balance" onClick={() => runBatchAccountAction('refreshBalance')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量刷新余额
|
||||
</button>
|
||||
<button onClick={() => runBatchAccountAction('enable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量启用
|
||||
</button>
|
||||
<button onClick={() => runBatchAccountAction('disable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量禁用
|
||||
</button>
|
||||
<button onClick={() => runBatchAccountAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</MobileBatchBar>
|
||||
</ResponsiveBatchActionBar>
|
||||
)}
|
||||
|
||||
{activeSegment === 'tokens' ? (
|
||||
|
||||
@@ -3,9 +3,9 @@ import { createPortal } from 'react-dom';
|
||||
import { api } from '../api.js';
|
||||
import CenteredModal from '../components/CenteredModal.js';
|
||||
import DeleteConfirmModal from '../components/DeleteConfirmModal.js';
|
||||
import MobileBatchBar from '../components/MobileBatchBar.js';
|
||||
import { MobileCard, MobileField } from '../components/MobileCard.js';
|
||||
import MobileFilterSheet from '../components/MobileFilterSheet.js';
|
||||
import ResponsiveBatchActionBar from '../components/ResponsiveBatchActionBar.js';
|
||||
import { useToast } from '../components/Toast.js';
|
||||
import ModernSelect from '../components/ModernSelect.js';
|
||||
import { useAnimatedVisibility } from '../components/useAnimatedVisibility.js';
|
||||
@@ -1687,24 +1687,17 @@ export default function DownstreamKeys() {
|
||||
</div>
|
||||
|
||||
{selectedIds.length > 0 ? (
|
||||
isMobile ? (
|
||||
<MobileBatchBar info={`已选 ${selectedIds.length} 个密钥`}>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={openBatchMetadata} disabled={batchActionLoading}>归类/标签</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量启用', selectedIds)} disabled={batchActionLoading}>启用</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量禁用', selectedIds)} disabled={batchActionLoading}>禁用</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量清零用量', selectedIds)} disabled={batchActionLoading}>清零</button>
|
||||
<button className="btn btn-link btn-link-danger" onClick={() => setDeleteConfirm({ mode: 'batch', ids: [...selectedIds] })} disabled={batchActionLoading}>删除</button>
|
||||
</MobileBatchBar>
|
||||
) : (
|
||||
<div className="card" style={{ padding: 12, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600, color: 'var(--color-text-primary)' }}>已选 {selectedIds.length} 个密钥</span>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={openBatchMetadata} disabled={batchActionLoading}>批量归类/标签</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量启用', selectedIds)} disabled={batchActionLoading}>批量启用</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量禁用', selectedIds)} disabled={batchActionLoading}>批量禁用</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量清零用量', selectedIds)} disabled={batchActionLoading}>批量清零用量</button>
|
||||
<button className="btn btn-link btn-link-danger" onClick={() => setDeleteConfirm({ mode: 'batch', ids: [...selectedIds] })} disabled={batchActionLoading}>批量删除</button>
|
||||
</div>
|
||||
)
|
||||
<ResponsiveBatchActionBar
|
||||
isMobile={isMobile}
|
||||
info={`已选 ${selectedIds.length} 个密钥`}
|
||||
infoStyle={{ color: 'var(--color-text-primary)' }}
|
||||
>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={openBatchMetadata} disabled={batchActionLoading}>{isMobile ? '归类/标签' : '批量归类/标签'}</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量启用', selectedIds)} disabled={batchActionLoading}>{isMobile ? '启用' : '批量启用'}</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量禁用', selectedIds)} disabled={batchActionLoading}>{isMobile ? '禁用' : '批量禁用'}</button>
|
||||
<button className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }} onClick={() => void batchRun('批量清零用量', selectedIds)} disabled={batchActionLoading}>{isMobile ? '清零' : '批量清零用量'}</button>
|
||||
<button className="btn btn-link btn-link-danger" onClick={() => setDeleteConfirm({ mode: 'batch', ids: [...selectedIds] })} disabled={batchActionLoading}>{isMobile ? '删除' : '批量删除'}</button>
|
||||
</ResponsiveBatchActionBar>
|
||||
) : null}
|
||||
|
||||
<div className="card" style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
|
||||
+8
-36
@@ -2,8 +2,8 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { api } from '../api.js';
|
||||
import CenteredModal from '../components/CenteredModal.js';
|
||||
import MobileBatchBar from '../components/MobileBatchBar.js';
|
||||
import MobileFilterSheet from '../components/MobileFilterSheet.js';
|
||||
import ResponsiveBatchActionBar from '../components/ResponsiveBatchActionBar.js';
|
||||
import { useToast } from '../components/Toast.js';
|
||||
import ModernSelect from '../components/ModernSelect.js';
|
||||
import { MobileCard, MobileField } from '../components/MobileCard.js';
|
||||
@@ -689,9 +689,12 @@ export default function Sites() {
|
||||
</div>
|
||||
</MobileFilterSheet>
|
||||
|
||||
{!isMobile && selectedSiteIds.length > 0 && (
|
||||
<div className="card" style={{ padding: 12, marginBottom: 12, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600 }}>已选 {selectedSiteIds.length} 项</span>
|
||||
{selectedSiteIds.length > 0 && (
|
||||
<ResponsiveBatchActionBar
|
||||
isMobile={isMobile}
|
||||
info={`已选 ${selectedSiteIds.length} 项`}
|
||||
desktopStyle={{ marginBottom: 12 }}
|
||||
>
|
||||
<button
|
||||
data-testid="sites-batch-enable-system-proxy"
|
||||
onClick={() => runBatchAction('enableSystemProxy')}
|
||||
@@ -718,38 +721,7 @@ export default function Sites() {
|
||||
<button onClick={() => runBatchAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isMobile && selectedSiteIds.length > 0 && (
|
||||
<MobileBatchBar info={`已选 ${selectedSiteIds.length} 项`}>
|
||||
<button
|
||||
data-testid="sites-batch-enable-system-proxy"
|
||||
onClick={() => runBatchAction('enableSystemProxy')}
|
||||
disabled={batchActionLoading}
|
||||
className="btn btn-ghost"
|
||||
style={{ border: '1px solid var(--color-border)' }}
|
||||
>
|
||||
批量开启系统代理
|
||||
</button>
|
||||
<button
|
||||
onClick={() => runBatchAction('disableSystemProxy')}
|
||||
disabled={batchActionLoading}
|
||||
className="btn btn-ghost"
|
||||
style={{ border: '1px solid var(--color-border)' }}
|
||||
>
|
||||
批量关闭系统代理
|
||||
</button>
|
||||
<button onClick={() => runBatchAction('enable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量启用
|
||||
</button>
|
||||
<button onClick={() => runBatchAction('disable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量禁用
|
||||
</button>
|
||||
<button onClick={() => runBatchAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</MobileBatchBar>
|
||||
</ResponsiveBatchActionBar>
|
||||
)}
|
||||
|
||||
<div className="info-tip" style={{ marginBottom: 12 }}>
|
||||
|
||||
@@ -2,9 +2,9 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { api } from '../api.js';
|
||||
import CenteredModal from '../components/CenteredModal.js';
|
||||
import MobileBatchBar from '../components/MobileBatchBar.js';
|
||||
import MobileFilterSheet from '../components/MobileFilterSheet.js';
|
||||
import ResponsiveFormGrid from '../components/ResponsiveFormGrid.js';
|
||||
import ResponsiveBatchActionBar from '../components/ResponsiveBatchActionBar.js';
|
||||
import { useToast } from '../components/Toast.js';
|
||||
import { formatDateTimeLocal } from './helpers/checkinLogTime.js';
|
||||
import {
|
||||
@@ -996,9 +996,12 @@ export function TokensPanel({ embedded = false, onEmbeddedActionsChange }: Token
|
||||
) : null}
|
||||
</CenteredModal>
|
||||
|
||||
{!isMobile && selectedTokenIds.length > 0 && (
|
||||
<div className="card" style={{ padding: 12, marginBottom: 12, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600 }}>已选 {selectedTokenIds.length} 项</span>
|
||||
{selectedTokenIds.length > 0 && (
|
||||
<ResponsiveBatchActionBar
|
||||
isMobile={isMobile}
|
||||
info={`已选 ${selectedTokenIds.length} 项`}
|
||||
desktopStyle={{ marginBottom: 12 }}
|
||||
>
|
||||
<button onClick={() => runBatchTokenAction('enable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量启用
|
||||
</button>
|
||||
@@ -1008,21 +1011,7 @@ export function TokensPanel({ embedded = false, onEmbeddedActionsChange }: Token
|
||||
<button data-testid="tokens-batch-delete" onClick={() => runBatchTokenAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isMobile && selectedTokenIds.length > 0 && (
|
||||
<MobileBatchBar info={`已选 ${selectedTokenIds.length} 项`}>
|
||||
<button onClick={() => runBatchTokenAction('enable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量启用
|
||||
</button>
|
||||
<button onClick={() => runBatchTokenAction('disable')} disabled={batchActionLoading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)' }}>
|
||||
批量禁用
|
||||
</button>
|
||||
<button data-testid="tokens-batch-delete" onClick={() => runBatchTokenAction('delete')} disabled={batchActionLoading} className="btn btn-link btn-link-danger">
|
||||
批量删除
|
||||
</button>
|
||||
</MobileBatchBar>
|
||||
</ResponsiveBatchActionBar>
|
||||
)}
|
||||
|
||||
<CenteredModal
|
||||
|
||||
Reference in New Issue
Block a user