165 lines
6.2 KiB
TypeScript
165 lines
6.2 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { Edit3, Settings2, Trash2 } from 'lucide-react';
|
|
import { Button, Pagination, Table, Tabs, Tag, type TableColumn } from '@/components/ui';
|
|
import { formatAmount } from '@/utils/currency';
|
|
import { formatDateTime } from '@/utils/dateTime';
|
|
import type { ConfirmAction, SmsApp } from './applicationTypes';
|
|
|
|
function applicationStatusTag(app: SmsApp) {
|
|
if (app.status === 'disabling') {
|
|
const detail = app.deactivation;
|
|
const title = [
|
|
detail?.reason || '等待未完成回执清算',
|
|
`等待供应商回执:${detail?.awaitingSupplierReceipt ?? 0}条`,
|
|
`等待推送:${detail?.waitingToSend ?? 0}条`,
|
|
`等待客户端确认:${detail?.awaitingClientAck ?? 0}条`,
|
|
`可重试失败:${detail?.retryableFailures ?? 0}条`,
|
|
`待推送上行:${detail?.pendingUplinks ?? 0}条`,
|
|
`进入停用中:${formatDateTime(detail?.disablingAt)}`,
|
|
`自动停用时间:${formatDateTime(detail?.autoDisableAt)}`,
|
|
].join('\n');
|
|
return <span aria-label={title} className="application-status-detail" tabIndex={0} title={title}><Tag tone="warning">停用中</Tag></span>;
|
|
}
|
|
return <Tag tone={app.status === 'active' ? 'success' : 'neutral'}>{app.status === 'active' ? '启用' : '停用'}</Tag>;
|
|
}
|
|
|
|
type EnterpriseApplicationTableProps = {
|
|
apps: SmsApp[];
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
onConfirmAction: (action: ConfirmAction) => void;
|
|
onEdit: (app: SmsApp) => void;
|
|
onOpenCmppParams: (app: SmsApp) => void;
|
|
onOpenConnection: (app: SmsApp) => void;
|
|
onOpenDeactivate: (app: SmsApp) => void;
|
|
onOpenHttpParams: (app: SmsApp) => void;
|
|
onPageChange: (page: number) => void;
|
|
};
|
|
|
|
export function EnterpriseApplicationTable({
|
|
apps,
|
|
page,
|
|
pageSize,
|
|
total,
|
|
onConfirmAction,
|
|
onEdit,
|
|
onOpenCmppParams,
|
|
onOpenConnection,
|
|
onOpenDeactivate,
|
|
onOpenHttpParams,
|
|
onPageChange,
|
|
}: EnterpriseApplicationTableProps) {
|
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
const columns = useMemo<Array<TableColumn<SmsApp>>>(() => [
|
|
{ key: 'name', title: '应用名称', width: '180px', render: (record) => <strong>{record.name}</strong> },
|
|
{ key: 'enterprise', title: '企业名称', width: '220px', render: (record) => record.enterprise },
|
|
{ key: 'sentToday', title: '今日发送', width: '120px', render: (record) => `${record.sentToday.toLocaleString('zh-CN')} 条` },
|
|
{ key: 'deliveryRate', title: '到达率', width: '130px', render: (record) => `${record.deliveryRate}%` },
|
|
{ key: 'unitPrice', title: '单价', width: '130px', render: (record) => `${formatAmount(record.unitPrice)} 元` },
|
|
{
|
|
key: 'cmppStatus',
|
|
title: '客户连接状态',
|
|
width: '250px',
|
|
render: (record) => (
|
|
<div className="cmpp-status-cell">
|
|
<Tag tone={record.cmppStatus === 'connected' ? 'success' : record.cmppStatus === 'disconnected' ? 'danger' : 'neutral'}>
|
|
{record.cmppStatus === 'connected' ? '已连接' : record.cmppStatus === 'disconnected' ? '已断开' : '未开通'}
|
|
</Tag>
|
|
<button disabled={!record.cmppParams.interfaceEnabled} onClick={() => onOpenConnection(record)} type="button">
|
|
{record.cmppConnections.filter((item) => item.state === 'open').length}
|
|
</button>
|
|
<button
|
|
className={`cmpp-status-cell__params ${record.cmppParams.interfaceEnabled ? 'is-enabled' : 'is-disabled'}`}
|
|
disabled={!record.cmppParams.interfaceEnabled}
|
|
onClick={() => onOpenCmppParams(record)}
|
|
type="button"
|
|
>
|
|
<Settings2 size={13} />
|
|
CMPP参数
|
|
</button>
|
|
<button
|
|
className={`cmpp-status-cell__params ${record.httpEnabled ? 'is-enabled' : 'is-disabled'}`}
|
|
disabled={!record.httpEnabled}
|
|
onClick={() => onOpenHttpParams(record)}
|
|
type="button"
|
|
>
|
|
HTTP参数
|
|
</button>
|
|
</div>
|
|
),
|
|
},
|
|
{ key: 'enabled', title: '状态', width: '130px', render: applicationStatusTag },
|
|
{
|
|
key: 'actions',
|
|
title: '操作',
|
|
align: 'right',
|
|
width: '190px',
|
|
render: (record) => (
|
|
<div className="table-actions enterprise-app-actions">
|
|
<Button icon={<Edit3 size={15} />} onClick={() => onEdit(record)} size="sm" variant="ghost">编辑</Button>
|
|
<Button
|
|
onClick={() => {
|
|
if (record.status === 'active') onOpenDeactivate(record);
|
|
else onConfirmAction({ action: 'enable', id: record.id, name: record.name });
|
|
}}
|
|
size="sm"
|
|
variant={record.status === 'active' ? 'warning' : 'success'}
|
|
>
|
|
{record.status === 'active' ? '停用' : '启用'}
|
|
</Button>
|
|
<Button
|
|
icon={<Trash2 size={15} />}
|
|
onClick={() => onConfirmAction({ action: 'delete', id: record.id, name: record.name })}
|
|
size="sm"
|
|
variant="danger"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
),
|
|
},
|
|
], [
|
|
onConfirmAction,
|
|
onEdit,
|
|
onOpenCmppParams,
|
|
onOpenConnection,
|
|
onOpenDeactivate,
|
|
onOpenHttpParams,
|
|
]);
|
|
|
|
return (
|
|
<div className="surface section-stack">
|
|
<Tabs
|
|
items={[
|
|
{
|
|
label: '短信应用',
|
|
value: 'sms',
|
|
content: (
|
|
<>
|
|
<Table columns={columns} data={apps} pagination={false} rowKey="id" />
|
|
<Pagination
|
|
nextDisabled={page >= totalPages}
|
|
onNext={() => onPageChange(Math.min(totalPages, page + 1))}
|
|
onPageChange={onPageChange}
|
|
onPrevious={() => onPageChange(Math.max(1, page - 1))}
|
|
page={page}
|
|
previousDisabled={page <= 1}
|
|
total={total}
|
|
totalPages={totalPages}
|
|
/>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
label: '彩信应用',
|
|
value: 'mms',
|
|
pending: true,
|
|
content: <div className="ui-table__empty">彩信应用待后端能力确认,本页不展示演示数据。</div>,
|
|
},
|
|
]}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|