import { useEffect, useMemo, useState } from 'react'; import { ClipboardCopy, FileText } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { clientApi, type ApplicationCmppParams, type ClientSmsApplication } from '@/api/adminApi'; import { formatCents } from '@/utils/currency'; import { Button, Modal, Pagination, Tag } from '@/components/ui'; import { copyText } from '@/utils/clipboard'; type LinkStatus = 'connected' | 'degraded' | 'disconnected' | 'inactive'; type ParamRow = { label: string; value: string; highlight?: boolean; }; const statusLabelMap: Record = { connected: '已连接', degraded: '部分连接', disconnected: '已断', inactive: '未开通', }; const statusToneMap: Record = { connected: 'success', degraded: 'warning', disconnected: 'danger', inactive: 'info', }; function normalizeStatus(application: ClientSmsApplication): LinkStatus { if (application.status !== 'active') { return 'inactive'; } return application.cmppStatus ?? 'inactive'; } function formatPrice(cents?: number | null) { return `${formatCents(cents)} 元`; } function mapParams(params: ApplicationCmppParams): ParamRow[] { return [ { label: 'ID', value: params.applicationId }, { label: '企业名', value: params.tenantName }, { label: '应用名称', value: params.applicationName }, { label: 'AppID', value: params.appCode }, { label: '企业代码', value: params.enterpriseCode }, { label: '账号', value: params.account }, { label: '密码', value: params.passwordCipher, highlight: true }, { label: '网关IP', value: params.gatewayHost || '-' }, { label: '网关端口', value: String(params.gatewayPort || '-') }, { label: '接入号', value: params.srcId || '-' }, { label: '连接数', value: String(params.maxConnections || '-') }, { label: '心跳间隔', value: `${params.heartbeatSeconds} 秒` }, { label: '协议版本', value: params.protocolVersion }, ]; } export function ClientApplicationsPage() { const navigate = useNavigate(); const [applications, setApplications] = useState([]); const [selectedApp, setSelectedApp] = useState(null); const [params, setParams] = useState(null); const [loading, setLoading] = useState(true); const [paramsLoading, setParamsLoading] = useState(false); const [page, setPage] = useState(1); const [error, setError] = useState(''); const [paramsError, setParamsError] = useState(''); const [copied, setCopied] = useState(false); const [copyError, setCopyError] = useState(''); function loadApplications() { setLoading(true); clientApi.listApplications() .then((items) => { setApplications(items.filter((item) => item.status !== 'deleted')); setError(''); }) .catch((reason: Error) => setError(reason.message || '短信应用加载失败')) .finally(() => setLoading(false)); } useEffect(() => { loadApplications(); }, []); function openParams(application: ClientSmsApplication) { if (application.interfaceEnabled === false) return; setSelectedApp(application); setParams(null); setParamsError(''); setCopied(false); setParamsLoading(true); clientApi.getApplicationCmppParams(application.id) .then((data) => { setParams(data); setParamsError(''); }) .catch((reason: Error) => setParamsError(reason.message || '接口参数加载失败')) .finally(() => setParamsLoading(false)); } const selectedRows = useMemo(() => params ? mapParams(params) : [], [params]); const pageSize = 10; const totalPages = Math.max(1, Math.ceil(applications.length / pageSize)); const currentPage = Math.min(page, totalPages); const visibleApplications = applications.slice((currentPage - 1) * pageSize, currentPage * pageSize); useEffect(() => { setPage(1); }, [applications.length]); function copyParams() { if (selectedRows.length === 0) { return; } const text = selectedRows.map((item) => `${item.label}: ${item.value}`).join('\n'); void copyText(text) .then(() => { setCopied(true); setCopyError(''); }) .catch((failure: Error) => setCopyError(failure.message || '复制失败')); } return (

短信应用列表

共 {applications.length} 个应用
{loading ?

正在加载短信应用...

: null} {error ?

{error}

: null} {!loading && !error && applications.length === 0 ? (
暂无短信应用
) : null}
{visibleApplications.map((application) => { const linkStatus = normalizeStatus(application); return (

{application.name}

appid
{application.id}
今日发送成功
{(application.sentToday ?? 0).toLocaleString('zh-CN')} 条
到达率
{application.deliveryRate !== undefined ? `${application.deliveryRate}%` : '-'}
单价
{formatPrice(application.customerUnitPrice)}
CMPP连接状态
{statusLabelMap[linkStatus]}
HTTP接口
{application.httpConfig?.enabled ? '已开通' : '未开通'}
); })}
= totalPages} onNext={() => setPage((value) => Math.min(totalPages, value + 1))} onPrevious={() => setPage((value) => Math.max(1, value - 1))} page={currentPage} totalPages={totalPages} onPageChange={setPage} previousDisabled={currentPage <= 1} total={applications.length} /> } onClick={copyParams}>{copied ? '已复制' : '复制参数'}} onClose={() => setSelectedApp(null)} open={Boolean(selectedApp)} size="xl" title="接口参数" > {paramsLoading ?

正在加载接口参数...

: null} {paramsError ?

{paramsError}

: null} {!paramsLoading && !paramsError && selectedRows.length > 0 ? (
{selectedRows.map((item) => (
{item.label} {item.value}
))}
) : null} {copyError ?

{copyError}

: null}
); }