fix: connect remaining sms pages to real backend
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user