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