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
+42 -175
View File
@@ -1,76 +1,45 @@
import { useMemo, useState } from 'react';
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { ArrowLeft, Info, RefreshCw } from 'lucide-react';
import { ArrowLeft } from 'lucide-react';
import { adminApi } from '@/api/adminApi';
import { Breadcrumb, Button, Input, Select } from '@/components/ui';
import { getEnterpriseRecords } from './adminEnterpriseMock';
type QueueType = 'priority' | 'normal';
type ReportRule = 'any' | 'one';
type InterfaceType = 'cmpp' | 'http';
const channelGroupOptions = {
mobile: [
{ label: '移动通道组A', value: 'mobile-a' },
{ label: '移动通道组B', value: 'mobile-b' },
],
unicom: [
{ label: '联通通道组B', value: 'unicom-b' },
{ label: '联通通道组C', value: 'unicom-c' },
],
telecom: [
{ label: '电信通道组C', value: 'telecom-c' },
{ label: '电信通道组D', value: 'telecom-d' },
],
};
const mismatchPolicyOptions = [
{ label: '拒绝发送', value: 'reject' },
{ label: '跳人工审核', value: 'manual-review' },
{ label: '直接发送', value: 'direct-send' },
];
function generateCode(prefix: string) {
return `${prefix}${Math.random().toString(36).slice(2, 8).toUpperCase()}`;
}
export function AdminSmsApplicationFormPage() {
const navigate = useNavigate();
const { enterpriseId, appId } = useParams();
const enterprise = useMemo(
() => getEnterpriseRecords().find((item) => item.id === enterpriseId),
[enterpriseId],
);
const isEdit = Boolean(appId);
const [appName, setAppName] = useState(isEdit ? '示例应用' : '');
const [unitPrice, setUnitPrice] = useState(isEdit ? '0.0300' : '');
const [queueType, setQueueType] = useState<QueueType>('priority');
const [mobileGroup, setMobileGroup] = useState('mobile-a');
const [unicomGroup, setUnicomGroup] = useState('unicom-b');
const [telecomGroup, setTelecomGroup] = useState('telecom-c');
const [reportRule, setReportRule] = useState<ReportRule>('any');
const [dailyLimit, setDailyLimit] = useState(isEdit ? '100000' : '');
const [phoneDailyLimit, setPhoneDailyLimit] = useState(isEdit ? '10' : '');
const [mismatchPolicy, setMismatchPolicy] = useState('manual-review');
const [smsEnabled, setSmsEnabled] = useState(true);
const [interfaceType, setInterfaceType] = useState<InterfaceType>('cmpp');
const [ipAddress, setIpAddress] = useState(isEdit ? '192.168.1.100' : '');
const [connectionCount, setConnectionCount] = useState(isEdit ? '2' : '');
const [enterpriseCode, setEnterpriseCode] = useState(isEdit ? 'ABC123' : generateCode('EC'));
const [interfaceAccount, setInterfaceAccount] = useState(isEdit ? 'ABC123' : generateCode('AC'));
const [interfacePassword, setInterfacePassword] = useState(isEdit ? '************' : generateCode('PW'));
const [accessNumber, setAccessNumber] = useState(isEdit ? '1069' : '');
const [nameError, setNameError] = useState('');
const [appName, setAppName] = useState('');
const [scene, setScene] = useState('行业通知');
const [dailyLimit, setDailyLimit] = useState('100000');
const [phoneDailyLimit, setPhoneDailyLimit] = useState('10');
const [mismatchPolicy, setMismatchPolicy] = useState('manual_review');
const [ipAddress, setIpAddress] = useState('');
const [error, setError] = useState('');
function goBack() {
navigate('/admin/enterprise-applications');
}
function submit() {
if (!appName.trim()) {
setNameError('请填写应用名称');
if (!enterpriseId) {
setError('缺少企业 ID');
return;
}
goBack();
if (isEdit) {
setError('短信应用编辑接口待补,当前不做本地模拟保存');
return;
}
adminApi.createEnterpriseApplication({
tenantId: enterpriseId,
name: appName,
scene,
dailyLimit: Number(dailyLimit) || undefined,
maxPhonesPerTask: Number(phoneDailyLimit) || undefined,
templateMismatchMode: mismatchPolicy,
ipAllowlist: ipAddress ? [ipAddress] : [],
})
.then(goBack)
.catch((failure: Error) => setError(failure.message || '短信应用保存失败'));
}
return (
@@ -78,140 +47,38 @@ export function AdminSmsApplicationFormPage() {
<div className="page-heading">
<div>
<Breadcrumb items={[isEdit ? '编辑短信应用' : '添加短信应用']} />
<p>{enterprise?.name ?? '当前企业'} </p>
<p></p>
</div>
<Button icon={<ArrowLeft size={16} />} onClick={goBack} variant="ghost">
</Button>
<Button icon={<ArrowLeft size={16} />} onClick={goBack} variant="ghost"></Button>
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="surface admin-app-form-card">
<section className="ui-detail-section">
<div className="ui-detail-section__header">
<h3></h3>
</div>
<div className="admin-app-form-grid">
<Input
error={nameError}
label="应用名称"
onChange={(event) => {
setAppName(event.target.value);
setNameError('');
}}
placeholder="请输入应用名称"
required
value={appName}
/>
<Input
label="编ID(元)"
onChange={(event) => setUnitPrice(event.target.value)}
placeholder="0.0300"
required
suffix={<span className="admin-app-form-price-note">(3.0000)</span>}
value={unitPrice}
/>
<div className="admin-app-form-row admin-app-form-row--wide">
<span></span>
<div className="radio-row">
<label>
<input checked={queueType === 'priority'} onChange={() => setQueueType('priority')} type="radio" />
</label>
<label>
<input checked={queueType === 'normal'} onChange={() => setQueueType('normal')} type="radio" />
</label>
</div>
<div className="admin-app-form-tip">
<Info size={17} />
<span></span>
</div>
</div>
<Select label="发送通道组-移动" onChange={(event) => setMobileGroup(event.target.value)} options={channelGroupOptions.mobile} required value={mobileGroup} />
<Select label="发送通道组-联通" onChange={(event) => setUnicomGroup(event.target.value)} options={channelGroupOptions.unicom} required value={unicomGroup} />
<Select label="发送通道组-电信" onChange={(event) => setTelecomGroup(event.target.value)} options={channelGroupOptions.telecom} required value={telecomGroup} />
<div className="admin-app-form-row admin-app-form-row--wide">
<span></span>
<div className="radio-row">
<label>
<input checked={reportRule === 'any'} onChange={() => setReportRule('any')} type="radio" />
</label>
<label>
<input checked={reportRule === 'one'} onChange={() => setReportRule('one')} type="radio" />
</label>
</div>
</div>
</div>
</section>
<section className="ui-detail-section">
<div className="ui-detail-section__header">
<h3></h3>
</div>
<div className="ui-detail-section__header"><h3></h3></div>
<div className="admin-app-form-grid">
<Input label="应用名称" onChange={(event) => setAppName(event.target.value)} placeholder="请输入应用名称" required value={appName} />
<Input label="应用场景" onChange={(event) => setScene(event.target.value)} placeholder="行业通知/营销推广/验证码" value={scene} />
<Input label="日发送数量限制" onChange={(event) => setDailyLimit(event.target.value)} placeholder="100000" required value={dailyLimit} />
<Input label="每号码日发送频次限制" onChange={(event) => setPhoneDailyLimit(event.target.value)} placeholder="10" required value={phoneDailyLimit} />
<Input label="每任务最大号码数" onChange={(event) => setPhoneDailyLimit(event.target.value)} placeholder="10" required value={phoneDailyLimit} />
<Select
label="不符合模板的短信"
onChange={(event) => setMismatchPolicy(event.target.value)}
options={mismatchPolicyOptions}
options={[
{ label: '拒绝发送', value: 'reject' },
{ label: '跳人工审核', value: 'manual_review' },
{ label: '直接发送', value: 'direct_send' },
]}
required
value={mismatchPolicy}
/>
</div>
</section>
<section className="ui-detail-section">
<div className="ui-detail-section__header">
<h3></h3>
</div>
<div className="admin-app-form-grid">
<div className="admin-app-form-row admin-app-form-row--wide">
<span></span>
<button className={smsEnabled ? 'admin-switch is-on' : 'admin-switch'} onClick={() => setSmsEnabled((current) => !current)} type="button">
<span />
{smsEnabled ? '开通' : '关闭'}
</button>
</div>
<div className="admin-app-form-row admin-app-form-row--wide">
<span></span>
<div className="radio-row">
<label>
<input checked={interfaceType === 'cmpp'} onChange={() => setInterfaceType('cmpp')} type="radio" />
CMPP接口
</label>
<label>
<input checked={interfaceType === 'http'} onChange={() => setInterfaceType('http')} type="radio" />
HTTP接口
</label>
</div>
</div>
<Input label="IP地址" onChange={(event) => setIpAddress(event.target.value)} placeholder="请输入 IP 地址" required value={ipAddress} />
<Input label="连接数" onChange={(event) => setConnectionCount(event.target.value)} placeholder="请输入连接数" required value={connectionCount} />
<Input
label="企业代码"
onChange={(event) => setEnterpriseCode(event.target.value)}
required
suffix={<Button icon={<RefreshCw size={14} />} onClick={() => setEnterpriseCode(generateCode('EC'))} size="sm" variant="ghost"></Button>}
value={enterpriseCode}
/>
<Input label="接口账号" onChange={(event) => setInterfaceAccount(event.target.value)} required value={interfaceAccount} />
<Input
label="接口密码"
onChange={(event) => setInterfacePassword(event.target.value)}
required
suffix={<Button icon={<RefreshCw size={14} />} onClick={() => setInterfacePassword(generateCode('PW'))} size="sm" variant="ghost"></Button>}
value={interfacePassword}
/>
<Input label="接入号" onChange={(event) => setAccessNumber(event.target.value)} placeholder="请输入接入号" required value={accessNumber} />
<Input label="IP 白名单" onChange={(event) => setIpAddress(event.target.value)} placeholder="例如 192.168.1.100/32" value={ipAddress} />
</div>
</section>
<div className="enterprise-form-footer">
<Button onClick={submit}></Button>
<Button onClick={goBack} variant="ghost"></Button>
<Button disabled={!appName || isEdit} onClick={submit}>{isEdit ? '编辑待补接口' : '创建应用'}</Button>
<Button onClick={goBack} variant="ghost"></Button>
</div>
</div>
</section>