feat: add phone frequency controls and modularize codebase
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { Copy, Eye, FileText, Pencil, Power, Send } from 'lucide-react';
|
||||
import { DeleteRiskAction, Pagination, Tag } from '@/components/ui';
|
||||
import { formatCents } from '@/utils/currency';
|
||||
import { carrierLabelMap, carrierToneMap, statusLabelMap, statusToneMap } from './channelModel';
|
||||
import type { ChannelConfirmAction, ChannelModalState, SmsChannel } from './channelTypes';
|
||||
|
||||
function RateBlock({ label, rate, count, tone = 'neutral' }: { label: string; rate: number; count: number; tone?: 'success' | 'warning' | 'danger' | 'neutral' }) {
|
||||
return (
|
||||
<div className={`sms-channel-rate sms-channel-rate--${tone}`}>
|
||||
<small>{label}</small>
|
||||
<strong>{rate}%</strong>
|
||||
<span>{count.toLocaleString('zh-CN')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChannelTable({
|
||||
channels,
|
||||
currentPage,
|
||||
total,
|
||||
totalPages,
|
||||
onPageChange,
|
||||
onOpenLogs,
|
||||
onOpenReports,
|
||||
onEdit,
|
||||
onConfirm,
|
||||
onTest,
|
||||
onDeleted,
|
||||
}: {
|
||||
channels: SmsChannel[];
|
||||
currentPage: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
onPageChange: (page: number | ((value: number) => number)) => void;
|
||||
onOpenLogs: (channel: SmsChannel) => void;
|
||||
onOpenReports: (channel: SmsChannel) => void;
|
||||
onEdit: (modal: ChannelModalState) => void;
|
||||
onConfirm: (action: ChannelConfirmAction) => void;
|
||||
onTest: (channel: SmsChannel) => void;
|
||||
onDeleted: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="surface sms-channel-table">
|
||||
<div className="sms-channel-table__head">
|
||||
<span>通道信息</span>
|
||||
<span>运营商 / 成本</span>
|
||||
<span>状态</span>
|
||||
<span>今日提交</span>
|
||||
<span>今日提交 / 送达质量</span>
|
||||
<span>操作</span>
|
||||
</div>
|
||||
{channels.map((channel) => (
|
||||
<article className="sms-channel-table__row" key={channel.id}>
|
||||
<div className="sms-channel-identity">
|
||||
<strong>{channel.name}</strong>
|
||||
<span>通道 ID:{channel.id}</span>
|
||||
</div>
|
||||
<div className="sms-channel-carrier-price">
|
||||
<Tag tone={carrierToneMap[channel.carrier]}>{carrierLabelMap[channel.carrier]}</Tag>
|
||||
<strong>{formatCents(channel.unitPrice)} 元</strong>
|
||||
</div>
|
||||
<div className="sms-channel-status-cell">
|
||||
<Tag tone={statusToneMap[channel.status]}>{statusLabelMap[channel.status]}</Tag>
|
||||
<button onClick={() => onOpenLogs(channel)} type="button">
|
||||
<FileText size={14} />连接日志
|
||||
</button>
|
||||
</div>
|
||||
<strong className="sms-channel-total">{channel.total.toLocaleString('zh-CN')}</strong>
|
||||
<div className="sms-channel-quality">
|
||||
<RateBlock count={channel.submitFailureCount} label="提交失败" rate={channel.submitFailureRate} tone={channel.submitFailureCount > 0 ? 'danger' : 'neutral'} />
|
||||
<RateBlock count={channel.successCount} label="送达成功" rate={channel.successRate} tone={channel.successRate >= 80 ? 'success' : 'warning'} />
|
||||
<RateBlock count={channel.unknownCount} label="回执未知" rate={channel.unknownRate} />
|
||||
<RateBlock count={channel.failureCount} label="送达失败" rate={channel.failureRate} tone={channel.failureRate >= 50 ? 'danger' : 'neutral'} />
|
||||
</div>
|
||||
<div className="sms-channel-actions">
|
||||
<button className="sms-channel-report-entry" onClick={() => onOpenReports(channel)} type="button">
|
||||
<Eye size={15} />报备详情
|
||||
</button>
|
||||
<button onClick={() => onEdit({ mode: 'edit', channel })} type="button"><Pencil size={15} />编辑</button>
|
||||
<button onClick={() => onConfirm({ type: 'copy', channel })} type="button"><Copy size={15} />复制通道</button>
|
||||
<button onClick={() => onTest(channel)} type="button"><Send size={15} />发送测试</button>
|
||||
<button className={channel.status === 'stopped' ? 'is-success' : 'is-warning'} onClick={() => onConfirm({ type: 'toggle', channel })} type="button">
|
||||
<Power size={15} />{channel.status === 'stopped' ? '启用' : '停用'}
|
||||
</button>
|
||||
<DeleteRiskAction onCompleted={onDeleted} portal="admin" targetId={channel.id} targetType="channel" />
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
<Pagination
|
||||
nextDisabled={currentPage >= totalPages}
|
||||
onNext={() => onPageChange((value) => Math.min(totalPages, value + 1))}
|
||||
onPrevious={() => onPageChange((value) => Math.max(1, value - 1))}
|
||||
page={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={onPageChange}
|
||||
previousDisabled={currentPage <= 1}
|
||||
total={total}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user