版本迁移同步合并|管理后台build
This commit is contained in:
@@ -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;
|
||||
@@ -278,6 +278,8 @@ export interface GenerationAIEngineSnapshot {
|
||||
id?: string;
|
||||
name?: string;
|
||||
provider?: string;
|
||||
modelName?: string;
|
||||
model?: string;
|
||||
supportedModels?: string[];
|
||||
defaultSize?: string;
|
||||
selectedSize?: string;
|
||||
|
||||
@@ -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/);
|
||||
|
||||
Reference in New Issue
Block a user