版本迁移同步合并|管理后台build

This commit is contained in:
2026-06-25 17:25:35 +08:00
parent 50afcf02d0
commit fa2e56d021
9 changed files with 1629 additions and 445 deletions
+120 -34
View File
@@ -18,6 +18,26 @@ interface OpenType {
updatedAt: string;
}
const normalizeUploadError = (e: unknown): Error => {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new Error(e);
}
return new Error('上传失败');
};
const assertUploadFile = (file: unknown): File => {
if (file instanceof File) {
return file;
}
throw new Error('请选择有效图片文件');
};
const AdminPlatform: React.FC = () => {
const [openTypes, setOpenTypes] = useState<OpenType[]>([]);
const [total, setTotal] = useState(0);
@@ -49,7 +69,9 @@ const AdminPlatform: React.FC = () => {
}
};
useEffect(() => { load(); }, []);
useEffect(() => {
load();
}, []);
const handleCreate = async () => {
try {
@@ -137,53 +159,89 @@ const AdminPlatform: React.FC = () => {
const columns = [
{
title: 'ID', dataIndex: 'id', width: 100,
title: 'ID',
dataIndex: 'id',
width: 100,
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{
title: '开户类型', dataIndex: 'open_type', width: 100,
title: '开户类型',
dataIndex: 'open_type',
width: 100,
render: (v: number) => <Typography.Text>{v}</Typography.Text>,
},
{
title: '类型名称', dataIndex: 'type_name', width: 150,
title: '类型名称',
dataIndex: 'type_name',
width: 150,
render: (v: string) => <Typography.Text strong>{v}</Typography.Text>,
},
{
title: '描述', dataIndex: 'description', width: 250, ellipsis: true,
render: (v: string) => <Typography.Text type="secondary" style={{ fontSize: 12 }}>{v}</Typography.Text>,
title: '描述',
dataIndex: 'description',
width: 250,
ellipsis: true,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{v}
</Typography.Text>
),
},
{
title: '缩略图', dataIndex: 'thumb', width: 120,
render: (v: string) => v ? <img src={v} alt="thumb" style={{ width: 80, height: 60, objectFit: 'cover' }} /> : '-',
title: '缩略图',
dataIndex: 'thumb',
width: 120,
render: (v: string) => (
v ? <img src={v} alt="thumb" style={{ width: 80, height: 60, objectFit: 'cover' }} /> : '-'
),
},
{
title: '创建时间', dataIndex: 'createdAt', width: 160,
render: (v: string) => <Typography.Text type="secondary" style={{ fontSize: 12 }}>{formatDate(v)}</Typography.Text>,
title: '创建时间',
dataIndex: 'createdAt',
width: 160,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{formatDate(v)}
</Typography.Text>
),
},
{
title: '更新时间', dataIndex: 'updatedAt', width: 160,
render: (v: string) => <Typography.Text type="secondary" style={{ fontSize: 12 }}>{formatDate(v)}</Typography.Text>,
title: '更新时间',
dataIndex: 'updatedAt',
width: 160,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{formatDate(v)}
</Typography.Text>
),
},
{
title: '操作', width: 200,
title: '操作',
width: 200,
render: (_: any, record: OpenType) => (
<Space>
<Button
icon={<EyeOutlined />}
size="small"
onClick={() => handleDetail(record.id)}
></Button>
>
</Button>
<Button
icon={<EditOutlined />}
size="small"
onClick={() => handleUpdate(record.id)}
></Button>
>
</Button>
<Button
icon={<DeleteOutlined />}
size="small"
danger
onClick={() => handleDelete(record.id)}
></Button>
>
</Button>
</Space>
),
},
@@ -198,8 +256,12 @@ const AdminPlatform: React.FC = () => {
<Typography.Text strong style={{ fontSize: 16 }}></Typography.Text>
</Space>
<Space>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateModalVisible(true)}></Button>
<Button icon={<ReloadOutlined />} onClick={() => load()}></Button>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateModalVisible(true)}>
</Button>
<Button icon={<ReloadOutlined />} onClick={() => load()}>
</Button>
</Space>
</div>
<Table
@@ -212,7 +274,11 @@ const AdminPlatform: React.FC = () => {
pageSize,
total,
showTotal: (t) => `${t} 条记录`,
onChange: (p, ps) => { setPage(p); setPageSize(ps); load(p, ps); },
onChange: (p, ps) => {
setPage(p);
setPageSize(ps);
load(p, ps);
},
}}
scroll={{ x: 1000 }}
/>
@@ -231,7 +297,6 @@ const AdminPlatform: React.FC = () => {
width={600}
>
<Form form={createForm} layout="vertical">
<div style={{ display: 'flex', gap: 16 }}>
<Form.Item
name="open_type"
@@ -245,12 +310,15 @@ const AdminPlatform: React.FC = () => {
name="type_name"
label="类型名称"
style={{ flex: 1 }}
rules={[{ required: true, message: '请输入类型名称' }, { max: 100, message: '类型名称不能超过100个字符' }]}
rules={[
{ required: true, message: '请输入类型名称' },
{ max: 100, message: '类型名称不能超过100个字符' },
]}
>
<Input style={{ width: '100%' }} placeholder="请输入类型名称" />
</Form.Item>
</div>
<Form.Item
name="description"
label="描述"
@@ -266,12 +334,13 @@ const AdminPlatform: React.FC = () => {
listType="picture-card"
customRequest={async ({ file, onSuccess, onError }) => {
try {
const res = await uploadImage(file as File);
const uploadFile = assertUploadFile(file);
const res = await uploadImage(uploadFile);
console.log(res);
createForm.setFieldsValue({ thumb: res.url });
onSuccess(res);
} catch (e: any) {
onError(e);
onSuccess?.(res);
} catch (e) {
onError?.(normalizeUploadError(e));
}
}}
>
@@ -301,7 +370,16 @@ const AdminPlatform: React.FC = () => {
<p><strong>:</strong> {currentOpenType.open_type}</p>
<p><strong>:</strong> {currentOpenType.type_name}</p>
<p><strong>:</strong> {currentOpenType.description}</p>
<p><strong>:</strong> {currentOpenType.thumb ? <img src={currentOpenType.thumb} alt="thumb" style={{ width: 120, height: 80, objectFit: 'cover' }} /> : '-'}</p>
<p>
<strong>:</strong>{' '}
{currentOpenType.thumb ? (
<img
src={currentOpenType.thumb}
alt="thumb"
style={{ width: 120, height: 80, objectFit: 'cover' }}
/>
) : '-'}
</p>
<p><strong>:</strong> {formatDate(currentOpenType.createdAt)}</p>
<p><strong>:</strong> {formatDate(currentOpenType.updatedAt)}</p>
</div>
@@ -332,7 +410,10 @@ const AdminPlatform: React.FC = () => {
<Form.Item
name="type_name"
label="类型名称"
rules={[{ required: true, message: '请输入类型名称' }, { max: 100, message: '类型名称不能超过100个字符' }]}
rules={[
{ required: true, message: '请输入类型名称' },
{ max: 100, message: '类型名称不能超过100个字符' },
]}
>
<Input placeholder="请输入类型名称" />
</Form.Item>
@@ -349,14 +430,19 @@ const AdminPlatform: React.FC = () => {
>
<Upload
listType="picture-card"
defaultFileList={currentOpenType?.thumb ? [{ uid: '1', name: 'thumb', status: 'done', url: currentOpenType.thumb }] : []}
defaultFileList={
currentOpenType?.thumb
? [{ uid: '1', name: 'thumb', status: 'done', url: currentOpenType.thumb }]
: []
}
customRequest={async ({ file, onSuccess, onError }) => {
try {
const res = await uploadImage(file);
const uploadFile = assertUploadFile(file);
const res = await uploadImage(uploadFile);
updateForm.setFieldsValue({ thumb: res.url });
onSuccess(res);
} catch (e: any) {
onError(e);
onSuccess?.(res);
} catch (e) {
onError?.(normalizeUploadError(e));
}
}}
>
@@ -374,4 +460,4 @@ const AdminPlatform: React.FC = () => {
);
};
export default AdminPlatform;
export default AdminPlatform;
+2
View File
@@ -278,6 +278,8 @@ export interface GenerationAIEngineSnapshot {
id?: string;
name?: string;
provider?: string;
modelName?: string;
model?: string;
supportedModels?: string[];
defaultSize?: string;
selectedSize?: string;
+2 -2
View File
@@ -92,7 +92,7 @@ function estimateColumnWidths(rows: ExcelCellValue[][], columns: StyledExcelColu
const config = columns[index];
if (config?.width) return { wch: config.width };
const maxLength = rows.reduce((max, row) => Math.max(max, visualLength(row[index])), visualLength(config?.title));
const maxLength = rows.reduce<number>((max, row) => Math.max(max, visualLength(row[index])), visualLength(config?.title));
const minWidth = config?.minWidth ?? 10;
const maxWidth = config?.maxWidth ?? 42;
return { wch: clamp(Math.ceil(maxLength * 1.15) + 2, minWidth, maxWidth) };
@@ -100,7 +100,7 @@ function estimateColumnWidths(rows: ExcelCellValue[][], columns: StyledExcelColu
}
function estimateRowHeight(row: ExcelCellValue[], colWidths: { wch: number }[], baseHeight = 20): number {
const maxLines = row.reduce((max, cell, index) => {
const maxLines = row.reduce<number>((max, cell, index) => {
const width = Math.max(8, colWidths[index]?.wch || 12);
const text = cell === null || cell === undefined ? '' : String(cell);
const explicitLines = text.split(/\r?\n/);