fix: connect remaining sms pages to real backend

This commit is contained in:
hectorzhao
2026-07-02 19:30:04 +08:00
parent 06562e42dd
commit 131f344ac4
50 changed files with 2074 additions and 6400 deletions
+57 -39
View File
@@ -1,7 +1,7 @@
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Check, FileText, Plus, Search, Send, Trash2 } from 'lucide-react';
import { Button, DateTimeInput, Input, Modal, Select, Tag } from '@/components/ui';
import { clientService, type RecentMessage } from '@/mock';
import { Button, DateTimeInput, Input, Modal, Select, Tag, Textarea } from '@/components/ui';
import { clientApi, type ClientSmsApplication, type ClientSmsSignature, type ClientSmsTemplate, type SmsBatchTask } from '@/api/adminApi';
type Recipient = {
id: string;
@@ -11,47 +11,52 @@ type Recipient = {
type SendMode = 'now' | 'scheduled';
type ReceiverMode = 'manual' | 'import';
const smsApplications = [
{ label: '会员营销平台', value: 'member' },
{ label: '订单通知系统', value: 'order' },
{ label: '登录认证服务', value: 'auth' },
];
export function ClientSendPage() {
const templates = clientService.getTemplates();
const signatures = clientService.getSignatures();
const approvedTemplates = templates.filter((item) => item.status === 'approved');
const approvedSignatures = signatures.filter((item) => item.status === 'approved');
const [applications, setApplications] = useState<ClientSmsApplication[]>([]);
const [templates, setTemplates] = useState<ClientSmsTemplate[]>([]);
const [signatures, setSignatures] = useState<ClientSmsSignature[]>([]);
const [error, setError] = useState('');
const [taskName, setTaskName] = useState('');
const [applicationId, setApplicationId] = useState('');
const [signatureId, setSignatureId] = useState('');
const [templateId, setTemplateId] = useState('');
const [templatePickerOpen, setTemplatePickerOpen] = useState(false);
const [templateKeyword, setTemplateKeyword] = useState('');
const [messageContent, setMessageContent] = useState('');
const [sendMode, setSendMode] = useState<SendMode>('now');
const [scheduledAt, setScheduledAt] = useState('');
const [receiverMode, setReceiverMode] = useState<ReceiverMode>('manual');
const [recipients, setRecipients] = useState<Recipient[]>([{ id: '1', phone: '' }]);
const [submittedRecord, setSubmittedRecord] = useState<RecentMessage | null>(null);
const [submittedRecord, setSubmittedRecord] = useState<SmsBatchTask | null>(null);
useEffect(() => {
Promise.all([clientApi.listApplications(), clientApi.listTemplates({ status: 'approved' }), clientApi.listSignatures()])
.then(([applicationItems, templateItems, signatureItems]) => {
setApplications(applicationItems.filter((item) => item.status === 'active'));
setTemplates(templateItems);
setSignatures(signatureItems.filter((item) => item.auditStatus === 'approved'));
setError('');
})
.catch((reason: Error) => setError(reason.message || '短信发送基础数据加载失败'));
}, []);
const selectedSignature = useMemo(
() => approvedSignatures.find((item) => item.id === signatureId),
[approvedSignatures, signatureId],
() => signatures.find((item) => item.id === signatureId),
[signatures, signatureId],
);
const selectedTemplate = useMemo(
() => approvedTemplates.find((item) => item.id === templateId),
[approvedTemplates, templateId],
() => templates.find((item) => item.id === templateId),
[templates, templateId],
);
const filteredTemplates = approvedTemplates.filter((item) => (
const filteredTemplates = templates.filter((item) => (
item.name.includes(templateKeyword) || item.content.includes(templateKeyword)
));
const validRecipients = recipients.filter((item) => item.phone.trim());
const previewText = selectedSignature && selectedTemplate
? `${selectedSignature.name}${selectedTemplate.content}`
: '请选择签名和模板';
const previewText = selectedSignature && messageContent
? `${selectedSignature.name}${messageContent}`
: messageContent;
const wordCount = previewText.length;
const smsParts = Math.max(1, Math.ceil(wordCount / 70));
const smsParts = wordCount > 0 ? Math.max(1, Math.ceil(wordCount / 70)) : 0;
const estimatedCount = validRecipients.length * smsParts;
const canSubmit = Boolean(taskName && applicationId && signatureId && templateId && validRecipients.length > 0 && (sendMode === 'now' || scheduledAt));
@@ -68,7 +73,9 @@ export function ClientSendPage() {
}
function chooseTemplate(id: string) {
const template = templates.find((item) => item.id === id);
setTemplateId(id);
setMessageContent(template?.content ?? '');
setTemplatePickerOpen(false);
}
@@ -77,17 +84,20 @@ export function ClientSendPage() {
return;
}
const nextRecord: RecentMessage = {
id: `MSG-${Date.now().toString().slice(-6)}`,
scene: selectedTemplate?.name ?? taskName,
count: estimatedCount,
channel: '华东主通道',
status: sendMode === 'now' ? 'info' : 'warning',
createdAt: new Date().toLocaleString('zh-CN', { hour12: false }),
};
clientService.addRecentMessage(nextRecord);
setSubmittedRecord(nextRecord);
clientApi.createBatchTask({
applicationId,
templateId,
content: previewText,
category: selectedTemplate?.category ?? taskName,
phones: validRecipients.map((item) => item.phone.trim()),
sendMode: sendMode === 'now' ? 'immediate' : 'scheduled',
scheduledAt: sendMode === 'scheduled' ? scheduledAt : undefined,
})
.then((task) => {
setSubmittedRecord(task);
setError('');
})
.catch((reason: Error) => setError(reason.message || '发送任务提交失败'));
}
return (
@@ -97,8 +107,9 @@ export function ClientSendPage() {
<Send size={22} />
</span>
<h1></h1>
{submittedRecord ? <Tag tone="success"> {submittedRecord.id}</Tag> : null}
{submittedRecord ? <Tag tone="success"> {submittedRecord.taskNo}</Tag> : null}
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="sms-send-layout">
<div className="sms-send-main">
@@ -117,7 +128,7 @@ export function ClientSendPage() {
<Select
label="短信应用"
onChange={(event) => setApplicationId(event.target.value)}
options={[{ label: '选择应用', value: '' }, ...smsApplications]}
options={[{ label: '选择应用', value: '' }, ...applications.map((item) => ({ label: item.name, value: item.id }))]}
value={applicationId}
/>
<Select
@@ -125,7 +136,7 @@ export function ClientSendPage() {
onChange={(event) => setSignatureId(event.target.value)}
options={[
{ label: '选择签名', value: '' },
...approvedSignatures.map((item) => ({ label: item.name, value: item.id })),
...signatures.map((item) => ({ label: item.name, value: item.id })),
]}
value={signatureId}
/>
@@ -245,6 +256,13 @@ export function ClientSendPage() {
<div className="phone-preview">
<div>{previewText}</div>
</div>
<Textarea
label="模板内容"
onChange={(event) => setMessageContent(event.target.value)}
placeholder="选择模板后可在这里编辑模板内容和变量"
rows={7}
value={messageContent}
/>
<div className="preview-stats">
<div>
<span></span>
@@ -252,7 +270,7 @@ export function ClientSendPage() {
</div>
<div>
<span></span>
<strong>{estimatedCount || 1} /</strong>
<strong>{estimatedCount} </strong>
</div>
<div>
<span></span>