diff --git a/web/frontend/src/pages/DouyuCollabPage.tsx b/web/frontend/src/pages/DouyuCollabPage.tsx index 90cfcfa..5cbc79c 100644 --- a/web/frontend/src/pages/DouyuCollabPage.tsx +++ b/web/frontend/src/pages/DouyuCollabPage.tsx @@ -6,6 +6,7 @@ import { Badge, Button, Card, + Checkbox, Col, Descriptions, Drawer, @@ -253,6 +254,7 @@ export default function DouyuCollabPage() { const [batchStatusOpen, setBatchStatusOpen] = useState(false); const [statusValue, setStatusValue] = useState('to_recharge'); const [statusReason, setStatusReason] = useState(''); + const [batchStatusRollback, setBatchStatusRollback] = useState(false); const [statusBusy, setStatusBusy] = useState(false); // 勾选导出 CK:明文只在弹窗 state 里,关闭即清空 @@ -291,6 +293,8 @@ export default function DouyuCollabPage() { }); setOrders(data.items); setTotal(data.total); + // 与虎牙一致:勾选只对「当前页当前筛选结果」有效,刷新后剔除已不在列表里的单。 + setSelectedRowKeys((keys) => keys.filter((key) => data.items.some((row) => row.id === Number(key)))); } catch (error) { message.error(getErrorMessage(error)); } finally { @@ -441,6 +445,17 @@ export default function DouyuCollabPage() { selectedOrders.length === selectedIds.length && selectedOrders.every((row) => row.status === 'done' && !row.is_archived); + /** 批量改状态时,所选订单里是否有必须显式勾「回退」的单(与后端 set_status 规则一致)。 */ + const batchRollbackRequired = useMemo(() => { + const targetIndex = FLOW_ORDER.indexOf(statusValue); + if (targetIndex < 0) return false; + return selectedOrders.some((row) => { + if (row.status === 'failed' || row.status === 'void') return true; + const currentIndex = FLOW_ORDER.indexOf(row.status); + return currentIndex >= 0 && targetIndex < currentIndex; + }); + }, [selectedOrders, statusValue]); + /** 与「CK 管理」保持一致:账号----密码----CK,每行一条。 */ const ckExportText = useMemo(() => { if (!ckExport) return ''; @@ -697,6 +712,7 @@ export default function DouyuCollabPage() { setBatchStatusOpen(false); setStatusValue(order.next_status || order.status); setStatusReason(''); + setBatchStatusRollback(false); }; const openBatchStatus = () => { @@ -708,9 +724,14 @@ export default function DouyuCollabPage() { setBatchStatusOpen(true); setStatusValue('recharged'); setStatusReason(''); + setBatchStatusRollback(false); }; const submitStatus = async () => { + if (batchStatusOpen && batchRollbackRequired && !batchStatusRollback) { + message.warning('所选订单需要回退状态,请先勾选「回退」再提交'); + return; + } setStatusBusy(true); try { if (batchStatusOpen) { @@ -718,8 +739,29 @@ export default function DouyuCollabPage() { ids: selectedIds, status: statusValue, reason: statusReason, + rollback: batchStatusRollback, }); - message.success(`批量改状态完成:${result.results.filter((item) => item.success).length}/${result.results.length}`); + const failures = result.results.filter((item) => !item.success); + const orderNoById = new Map(selectedOrders.map((row) => [row.id, row.order_no])); + if (failures.length === 0) { + message.success( + `批量改状态完成:${result.results.length} 单 →「${STATUS_META[statusValue].label}」`, + ); + } else { + modal.info({ + title: `改状态完成:成功 ${result.results.length - failures.length} / 未通过 ${failures.length}`, + width: 640, + content: ( + + {failures.map((item) => ( + + {orderNoById.get(item.id) || `#${item.id}`}:{item.message || '未通过'} + + ))} + + ), + }); + } setBatchStatusOpen(false); setSelectedRowKeys([]); } else if (statusTarget) { @@ -732,6 +774,7 @@ export default function DouyuCollabPage() { expected_version: statusTarget.version, }); message.success('状态已更新'); + setSelectedRowKeys((keys) => keys.filter((key) => Number(key) !== statusTarget.id)); setStatusTarget(null); } refreshAll(); @@ -1619,7 +1662,6 @@ export default function DouyuCollabPage() { rowSelection={{ selectedRowKeys, onChange: (keys) => setSelectedRowKeys(keys), - preserveSelectedRowKeys: true, }} pagination={{ current: page, @@ -1835,7 +1877,10 @@ export default function DouyuCollabPage() { }} onOk={() => void submitStatus()} confirmLoading={statusBusy} - okText="确定" + okText={batchStatusOpen ? `改成「${STATUS_META[statusValue].label}」` : '确定'} + okButtonProps={{ + disabled: batchStatusOpen && batchRollbackRequired && !batchStatusRollback, + }} >