import { Alert, Button, Card, Descriptions, Form, Input, Modal, Select, Space, Switch, Table, Tabs, Tag, AutoComplete, message, } from 'antd'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { api } from '../api/client'; import { PageHeader } from './ResourcePage'; import { StatusTag } from '../StatusTag'; type SignRow = { signName?: string; SignName?: string; auditStatus?: string | number; AuditStatus?: string | number; createDate?: string; CreateDate?: string; reason?: string; Reason?: string; signSource?: number; SignSource?: number; signType?: number; SignType?: number; businessType?: string; BusinessType?: string; }; type TplRow = { templateCode?: string; TemplateCode?: string; templateName?: string; TemplateName?: string; auditStatus?: string | number; AuditStatus?: string | number; templateContent?: string; TemplateContent?: string; templateType?: number; TemplateType?: number; createDate?: string; CreateDate?: string; reason?: string; Reason?: string; }; const SIGN_SOURCE: Record = { 0: '企事业单位全称或简称', 1: '工信部备案网站全称或简称', 2: 'App 全称或简称', 3: '公众号或小程序全称或简称', 4: '电商平台店铺名', 5: '商标全称或简称', }; const SIGN_TYPE: Record = { 0: '验证码', 1: '通用' }; const TPL_TYPE: Record = { 0: '验证码', 1: '通知短信', 2: '推广短信', 3: '国际/港澳台', 7: '数字短信', }; function auditText(v: unknown) { const s = String(v ?? ''); if (s === '1' || s === 'AUDIT_STATE_PASS' || s === 'PASS') return { color: 'success', text: '已通过' }; if (s === '0' || s === 'AUDIT_STATE_INIT' || s === 'AUDITING') return { color: 'processing', text: '审核中' }; if (s === '2' || s === 'AUDIT_STATE_NOT_PASS' || s === 'NOT_PASS') return { color: 'error', text: '未通过' }; if (s === '3' || s === 'CANCEL' || s === 'AUDIT_STATE_CANCEL') return { color: 'default', text: '已取消' }; return { color: 'default', text: s ? zh(s) : '—' }; } function unwrapList(data: unknown, keys: string[]): T[] { const body = (data as { data?: unknown })?.data ?? data; if (!body || typeof body !== 'object') return []; const obj = body as Record; for (const k of keys) { const v = obj[k]; if (Array.isArray(v)) return v as T[]; } if (Array.isArray(body)) return body as T[]; return []; } function zh(s: string) { return s; } function dash(v?: string | null) { return v && String(v).trim() ? v : '未绑定'; } export default function SmsPage() { const qc = useQueryClient(); const cfg = useQuery({ queryKey: ['/sms/config'], queryFn: () => api.get('/sms/config') }); const signs = useQuery({ queryKey: ['/sms/signs'], queryFn: () => api.get('/sms/signs'), retry: false, }); const tpls = useQuery({ queryKey: ['/sms/templates'], queryFn: () => api.get('/sms/templates'), retry: false, }); const quals = useQuery({ queryKey: ['/sms/qualifications'], queryFn: () => api.get('/sms/qualifications'), retry: false, }); const logs = useQuery({ queryKey: ['/sms/logs'], queryFn: () => api.get('/sms/logs') }); const events = useQuery({ queryKey: ['/sms/events'], queryFn: () => api.get('/sms/events') }); const [signForm] = Form.useForm(); const [tplForm] = Form.useForm(); const info = (cfg.data?.data ?? {}) as { configured?: boolean; defaultSign?: string; note?: string; region?: string; endpoint?: string; ramLogin?: string; accessKeyIdMasked?: string; secretConfigured?: boolean; securityPhone?: string; securityEmail?: string; presets?: { code: string; name: string }[]; }; const signRows = unwrapList(signs.data, ['smsSignList', 'SmsSignList']); const tplRows = unwrapList(tpls.data, ['smsTemplateList', 'SmsTemplateList']); const qualRows = unwrapList<{ qualificationId?: string; QualificationId?: string; qualificationName?: string; QualificationName?: string; companyName?: string; CompanyName?: string; state?: string; State?: string; }>(quals.data, ['qualificationInfos', 'QualificationInfos', 'records']); const logRows = unwrapList<{ id: string; phones: string; signName: string; templateCode: string; eventKey?: string; code: string; message?: string; createdAt: string; }>(logs.data, ['items']); const eventPack = (events.data?.data ?? {}) as { configured?: boolean; defaultSign?: string; presets?: { code: string; name: string }[]; items?: { key: string; group: string; name: string; when: string; to: string; enabled: boolean; automatic?: boolean; templateCode: string; signName: string; sample: Record; vars?: string; }[]; }; const eventRows = eventPack.items || []; const eventMut = useMutation({ mutationFn: (body: Record) => api.post('/sms/events', body), onSuccess: () => { message.success('已保存步骤关联'); qc.invalidateQueries({ queryKey: ['/sms/events'] }); }, onError: (e: Error) => message.error(e.message), }); const aliyunErr = (signs.error as Error | undefined)?.message || (tpls.error as Error | undefined)?.message; const signMut = useMutation({ mutationFn: (body: Record) => api.post('/sms/signs', body), onSuccess: () => { message.success('已提交签名申请,阿里云审核中'); qc.invalidateQueries({ queryKey: ['/sms/signs'] }); }, onError: (e: Error) => message.error(e.message), }); const tplMut = useMutation({ mutationFn: (body: Record) => api.post('/sms/templates', body), onSuccess: () => { message.success('已提交模板申请,阿里云审核中'); qc.invalidateQueries({ queryKey: ['/sms/templates'] }); }, onError: (e: Error) => message.error(e.message), }); const passedQuals = qualRows .map((q) => ({ value: String(q.qualificationId || q.QualificationId || ''), label: String(q.qualificationName || q.QualificationName || q.companyName || q.CompanyName || q.qualificationId || ''), })) .filter((q) => q.value); return ( <> {info.configured ? '已配置密钥' : '未配置密钥'}} /> {!info.configured ? : null} {aliyunErr ? ( ) : null} r.vars || Object.keys(r.sample || {}).join('、') || '—', }, { title: '模板', dataIndex: 'templateCode', width: 220, render: (_: unknown, r) => ( ({ value: p.code, label: `${p.name}(${p.code})`, }))} onBlur={(e) => { const v = (e.target as HTMLInputElement).value?.trim(); if (v && v !== r.templateCode) eventMut.mutate({ eventKey: r.key, templateCode: v }); }} onSelect={(v) => eventMut.mutate({ eventKey: r.key, templateCode: String(v) })} disabled={r.automatic === false} /> ), }, { title: '启用', dataIndex: 'enabled', width: 80, render: (v: boolean, r) => ( eventMut.mutate({ eventKey: r.key, enabled })} /> ), }, ]} /> ), }, { key: 'account', label: '账号设置', children: ( {info.ramLogin || '—'} 不写入本系统,仅控制台登录使用 {info.accessKeyIdMasked || '—'} {info.secretConfigured ? '已配置(密文,不在页面展示)' : '未配置'} {info.region || 'cn-hangzhou'} {info.endpoint || 'dysmsapi.aliyuncs.com'} {info.defaultSign || '—'} {dash(info.securityPhone)} {dash(info.securityEmail)}

{info.note}

), }, { key: 'signs', label: '签名', children: ( <>
String(r.signName || r.SignName)} loading={signs.isLoading} dataSource={signRows} pagination={false} locale={{ emptyText: '还没有签名,点申请签名。' }} columns={[ { title: '签名', render: (_: unknown, r: SignRow) => r.signName || r.SignName }, { title: '来源', width: 180, render: (_: unknown, r: SignRow) => SIGN_SOURCE[Number(r.signSource ?? r.SignSource)] || r.businessType || r.BusinessType || '—', }, { title: '类型', width: 90, render: (_: unknown, r: SignRow) => SIGN_TYPE[Number(r.signType ?? r.SignType)] || '—', }, { title: '审核', width: 110, render: (_: unknown, r: SignRow) => { const s = auditText(r.auditStatus ?? r.AuditStatus); return {s.text}; }, }, { title: '申请时间', width: 170, render: (_: unknown, r: SignRow) => r.createDate || r.CreateDate || '—' }, { title: '原因', render: (_: unknown, r: SignRow) => r.reason || r.Reason || '—' }, ]} /> ), }, { key: 'templates', label: '模板', children: ( <>
String(r.templateCode || r.TemplateCode)} loading={tpls.isLoading} dataSource={tplRows} pagination={false} locale={{ emptyText: '还没有模板,点申请模板,或先用已有编号发送。' }} columns={[ { title: '模板编号', width: 160, render: (_: unknown, r: TplRow) => r.templateCode || r.TemplateCode }, { title: '名称', render: (_: unknown, r: TplRow) => r.templateName || r.TemplateName }, { title: '类型', width: 110, render: (_: unknown, r: TplRow) => TPL_TYPE[Number(r.templateType ?? r.TemplateType)] || '—', }, { title: '审核', width: 110, render: (_: unknown, r: TplRow) => { const s = auditText(r.auditStatus ?? r.AuditStatus); return {s.text}; }, }, { title: '内容', ellipsis: true, render: (_: unknown, r: TplRow) => r.templateContent || r.TemplateContent || '—' }, { title: '原因', ellipsis: true, render: (_: unknown, r: TplRow) => r.reason || r.Reason || '—' }, ]} /> ), }, { key: 'logs', label: '发送记录', children: (
v || '手工发送' }, { title: '结果', dataIndex: 'code', width: 90, render: (v: string) => , }, { title: '说明', dataIndex: 'message', render: (v: string) => v || '—' }, { title: '时间', dataIndex: 'createdAt', width: 170, render: (v: string) => (v ? String(v).replace('T', ' ').slice(0, 19) : '—'), }, ]} /> ), }, ]} /> ); }