fix: complete first version issue remediation

This commit is contained in:
hectorzhao
2026-07-11 10:14:37 +08:00
parent 709ac97764
commit 208a6c23f8
73 changed files with 1549 additions and 245 deletions
+2 -1
View File
@@ -50,7 +50,6 @@ function mapParams(params: ApplicationCmppParams): ParamRow[] {
{ label: '接入号', value: params.srcId || '-' },
{ label: '连接数', value: String(params.maxConnections || '-') },
{ label: '心跳间隔', value: `${params.heartbeatSeconds}` },
{ label: '窗口大小', value: String(params.windowSize) },
{ label: '协议版本', value: params.protocolVersion },
];
}
@@ -169,6 +168,8 @@ export function ClientApplicationsPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={applications.length}
/>
+2
View File
@@ -298,6 +298,8 @@ export function ClientBatchTasksPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={filteredTasks.length}
/>
+2
View File
@@ -64,6 +64,8 @@ export function ClientBillingPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={plans.length}
/>
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from 'lucide-react';
import { Button, FileActions, Input, Select, Textarea } from '@/components/ui';
import { clientApi, type EnterpriseCertification, type FileObject, type FileRef } from '@/api/adminApi';
import { displayFileName } from '@/utils/fileName';
type AuthStep = 'overview' | 'profile' | 'method' | 'recharge' | 'face' | 'faceScan' | 'pending' | 'success' | 'failed';
type AuthMethod = 'face' | 'recharge';
@@ -59,7 +60,7 @@ function UploadPanel({ file, uploading, onFile }: { file: FileObject | null; upl
return (
<label className="enterprise-upload">
<Upload size={38} />
<strong>{uploading ? '上传中...' : file?.fileName ?? '点击上传'}</strong>
<strong>{uploading ? '上传中...' : file ? displayFileName(file.fileName) : '点击上传'}</strong>
<FileActions file={fileRef} />
<input
accept="image/png,image/jpeg,image/webp,application/pdf"
+2 -1
View File
@@ -13,6 +13,7 @@ import { useNavigate } from 'react-router-dom';
import { Button, Chart, Table, Tag, type TableColumn } from '@/components/ui';
import { clientApi, type DashboardResponse } from '@/api/adminApi';
import { createLineOption, createPieOption } from '@/theme/chartOptions';
import { formatDateTime } from '@/utils/dateTime';
type RecentTaskRow = {
id: string;
@@ -59,7 +60,7 @@ export function ClientHome() {
scene: String(task.category ?? task.content ?? '短信发送'),
count: Number(task.phoneTotal ?? task.progressTotal ?? 0),
channel: Array.isArray(task.messages) && task.messages[0]?.channel?.name ? String(task.messages[0].channel.name) : '待路由',
createdAt: task.createdAt ? new Date(String(task.createdAt)).toLocaleString('zh-CN') : '',
createdAt: formatDateTime(task.createdAt ? String(task.createdAt) : null),
status: String(task.status ?? 'unknown'),
})), [dashboard]);
const latestRecharge = dashboard?.recentRecharges[0];
+2
View File
@@ -229,6 +229,8 @@ export function ClientSendDetailPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={filteredRows.length}
/>
+2
View File
@@ -157,6 +157,8 @@ export function ClientSignaturesPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={filteredSignatures.length}
/>
+4 -1
View File
@@ -10,6 +10,7 @@ import {
type TableColumn,
} from '@/components/ui';
import { clientApi, type OperationLogItem } from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
type LogLevel = 'info' | 'success' | 'warning' | 'error';
@@ -61,7 +62,7 @@ export function ClientSystemLogsPage() {
const currentPage = Math.min(page, totalPages);
const columns = useMemo<Array<TableColumn<OperationLogItem>>>(() => [
{ key: 'time', title: '时间', width: '190px', render: (record) => <span className="muted">{new Date(record.time).toLocaleString('zh-CN')}</span> },
{ key: 'time', title: '时间', width: '190px', render: (record) => <span className="muted">{formatDateTime(record.time)}</span> },
{ key: 'level', title: '级别', width: '120px', render: (record) => <Tag tone={levelToneMap[record.level]}>{levelLabelMap[record.level]}</Tag> },
{ key: 'module', title: '模块', width: '150px', render: (record) => <strong>{record.module}</strong> },
{ key: 'operator', title: '操作人', width: '130px', render: (record) => <strong>{record.operator}</strong> },
@@ -127,6 +128,8 @@ export function ClientSystemLogsPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={total}
/>
+4 -1
View File
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import { Edit3, MessageSquare, Plus, Search, Trash2 } from 'lucide-react';
import { Button, Input, Modal, Pagination, Select, Tag, Textarea } from '@/components/ui';
import { clientApi, type ClientSmsApplication, type ClientSmsSignature, type ClientSmsTemplate } from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
type TemplateVariable = {
name: string;
@@ -276,7 +277,7 @@ export function ClientTemplatesPage() {
{variables.length > 0 ? variables.map((item) => <strong key={item}>${`{${item}}`}</strong>) : <span className="muted"></span>}
</div>
<div className="template-card-footer">
<span>{new Date(template.updatedAt).toLocaleString('zh-CN')}</span>
<span>{formatDateTime(template.updatedAt)}</span>
<div>
<button onClick={() => setModalTemplate(template)} type="button">
<Edit3 size={14} />
@@ -297,6 +298,8 @@ export function ClientTemplatesPage() {
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={filteredTemplates.length}
/>
+23 -10
View File
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { Edit3, KeyRound, Plus, Search, Trash2, Users } from 'lucide-react';
import { clientApi, type ManagedUser, type UserPayload } from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
import { readSession } from '@/api/session';
import { Button, Input, Modal, Select, Table, Tag, type TableColumn } from '@/components/ui';
@@ -50,6 +51,7 @@ export function ClientUsersPage() {
const [newPassword, setNewPassword] = useState('');
const [confirmAction, setConfirmAction] = useState<ConfirmAction | null>(null);
const [error, setError] = useState('');
const [saving, setSaving] = useState(false);
async function load() {
if (!tenantId) return;
@@ -79,6 +81,11 @@ export function ClientUsersPage() {
}
async function saveUser() {
if (!form.displayName.trim() || (!form.email.trim() && !form.phone.trim()) || (creating && form.password.length < 6)) {
setError('请填写姓名、邮箱或手机号;新增用户密码至少 6 位');
return;
}
setSaving(true);
const body: UserPayload = {
displayName: form.displayName,
username: form.username || form.email || form.phone,
@@ -88,14 +95,20 @@ export function ClientUsersPage() {
roleCode: 'enterprise_admin',
operatorId: session?.user.id,
};
if (creating) {
await clientApi.createUser({ ...body, password: form.password }, tenantId);
} else if (editingUser) {
await clientApi.updateUser(editingUser.id, body, tenantId);
try {
if (creating) {
await clientApi.createUser({ ...body, password: form.password }, tenantId);
} else if (editingUser) {
await clientApi.updateUser(editingUser.id, body, tenantId);
}
setCreating(false);
setEditingUser(null);
await load();
} catch (failure) {
setError(failure instanceof Error ? failure.message : '用户保存失败');
} finally {
setSaving(false);
}
setCreating(false);
setEditingUser(null);
await load();
}
async function runConfirm() {
@@ -122,7 +135,7 @@ export function ClientUsersPage() {
{ key: 'phone', title: '手机号', width: '160px', render: (record) => <span className="muted">{record.phone ?? '-'}</span> },
{ key: 'role', title: '角色', width: '130px', render: () => <Tag tone="info"></Tag> },
{ key: 'status', title: '状态', width: '120px', render: (record) => <Tag tone={record.status === 'active' ? 'success' : 'neutral'}>{record.status === 'active' ? '正常' : '禁用'}</Tag> },
{ key: 'lastLoginAt', title: '最后登录时间', width: '190px', render: (record) => <span className="muted">{record.lastLoginAt ? new Date(record.lastLoginAt).toLocaleString('zh-CN') : '-'}</span> },
{ key: 'lastLoginAt', title: '最后登录时间', width: '190px', render: (record) => <span className="muted">{formatDateTime(record.lastLoginAt)}</span> },
{
key: 'actions',
title: '操作',
@@ -131,7 +144,7 @@ export function ClientUsersPage() {
<div className="inline-actions">
<Button icon={<Edit3 size={15} />} onClick={() => openEditor(record)} size="sm" variant="ghost"></Button>
<Button icon={<KeyRound size={15} />} onClick={() => { setPasswordUser(record); setNewPassword(''); }} size="sm" variant="ghost"></Button>
<Button onClick={() => setConfirmAction({ type: 'status', user: record })} size="sm" variant="ghost">{record.status === 'active' ? '禁用' : '启用'}</Button>
<Button onClick={() => setConfirmAction({ type: 'status', user: record })} size="sm" variant={record.status === 'active' ? 'warning' : 'success'}>{record.status === 'active' ? '禁用' : '启用'}</Button>
<Button icon={<Trash2 size={15} />} onClick={() => setConfirmAction({ type: 'delete', user: record })} size="sm" variant="danger"></Button>
</div>
),
@@ -158,7 +171,7 @@ export function ClientUsersPage() {
{(creating || editingUser) ? (
<Modal
footer={<><Button onClick={() => { setCreating(false); setEditingUser(null); }} variant="secondary"></Button><Button onClick={() => void saveUser()}></Button></>}
footer={<><Button disabled={saving} onClick={() => { setCreating(false); setEditingUser(null); }} variant="secondary"></Button><Button disabled={saving} onClick={() => void saveUser()}>{saving ? '保存中...' : '保存'}</Button></>}
onClose={() => { setCreating(false); setEditingUser(null); }}
open
size="xl"