2026-02-24 17:48:14 +08:00

245 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { MyButtons, MyColumns, MyProTableProps } from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { ProTable } from '@ant-design/pro-components';
import { message, Progress, Space } from 'antd';
import { saveAs } from 'file-saver';
import JSZip from 'jszip';
import { useState } from 'react';
import Create from './modals/Create';
import Update from './modals/Update';
interface DataType {
key?: React.Key;
id?: React.Key;
}
export default function Index({ title = '点位配置' }) {
const [count, setCount] = useState(0);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [downloadProgress, setDownloadProgress] = useState(0);
const [downloading, setDownloading] = useState(false);
const [getSelectedRow, setSelectedRow] = useState<any>([]);
const rowSelection: any = {
selectedRowKeys,
onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
console.log(selectedRows, 'selectedRows[0]');
if (selectedRowKeys.length > 10) {
message.warning('最多批量下载10个');
return;
}
setSelectedRowKeys(selectedRowKeys);
setSelectedRow(selectedRows);
},
getCheckboxProps: (record: any) => ({
disabled: !record.is_enabled,
}),
onSelectAll: (selected: boolean, selectedRows: DataType[]) => {
if (selected && selectedRows.length > 10) {
message.warning('全选数量超过10个');
return false;
}
},
defaultSelectedRowKeys: !count
? []
: getSelectedRow?.map((item: any) => item.id),
};
// 模拟获取二维码接口
const getQRCode = async (id?: string) => {
let res = await Apis.Patrol.PatrolLocations.PatrolLocationQrCode({
id: Number(id),
});
return res?.data?.qr_code;
};
// 生成带海报的二维码
const generatePosterQR = async (base64: string, res: any) => {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx: any = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 800;
// 海报样式
ctx.fillStyle = '#1890ff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = 'bold 36px Arial';
ctx.textAlign = 'center';
ctx.fillText(`${res?.name}`, canvas.width / 2, 130);
const qrImage = new Image();
qrImage.onload = () => {
const qrSize = 300;
const qrX = (canvas.width - qrSize) / 2;
const qrY = 210;
ctx.drawImage(qrImage, qrX, qrY, qrSize, qrSize);
ctx.font = '20px Arial';
ctx.fillText('扫描二维码完成签到', canvas.width / 2, 570);
ctx.font = '16px Arial';
ctx.fillText(res?.code, canvas.width / 2, 605);
resolve(canvas.toDataURL('image/png'));
};
qrImage.src = base64;
});
};
const download = async (type: number) => {
if (!getSelectedRow?.length) {
message.error('请选择巡逻位置!');
return;
}
setDownloading(true);
setDownloadProgress(0);
try {
const zip = new JSZip();
const total = getSelectedRow.length;
for (let i = 0; i < total; i++) {
let res = getSelectedRow?.[i];
if (res?.id) {
await new Promise((resolve: any) => {
setTimeout(resolve, 1000);
});
const qrCode = await getQRCode(res?.id);
if (type === 1) {
const base64Data = qrCode.split(',')[1];
zip.file(`${res?.name}_${res?.code}.png`, base64Data, {
base64: true,
});
}
if (type === 2) {
// 带海报的二维码
const posterData: any = await generatePosterQR(qrCode, res);
const fileData = posterData.split(',')[1];
zip.file(`${res?.name}_${res?.code}.png`, fileData, {
base64: true,
});
}
setCount(i + 1);
const progress = Math.round(((i + 1) / total) * 100);
setDownloadProgress(progress);
// 添加延迟
}
}
const content = await zip.generateAsync({ type: 'blob' });
saveAs(content, `扫码签到二维码.zip`);
setTimeout(() => {
setCount(0);
}, 1000);
} catch (error) {
console.error('下载失败:', error);
message.error('下载失败,请重试');
} finally {
setDownloading(false);
setDownloadProgress(0);
}
};
return (
<ProTable
{...MyProTableProps.props}
loading={{
spinning: downloading,
tip: `正在生成二维码... ${downloadProgress}%`,
indicator: (
<Progress type="circle" percent={downloadProgress} size={20} />
),
}}
request={async (params, sort) =>
MyProTableProps.request(params, sort, Apis.Patrol.PatrolLocations.List)
}
rowSelection={{ type: 'checkbox', ...rowSelection }}
toolBarRender={(action) => [
<MyButtons.Default
key="download_qrcode"
size="middle"
title={`下载纯二维码${
getSelectedRow?.length ? `${count}/${getSelectedRow?.length}` : ''
}`}
onClick={() => download(1)}
/>,
<MyButtons.Default
key="download_qrcode"
size="middle"
title={`下载海报二维码${
getSelectedRow?.length ? `${count}/${getSelectedRow?.length}` : ''
}`}
onClick={() => download(2)}
/>,
<Create key="Create" reload={action?.reload} title={title} />,
]}
columns={[
MyColumns.ID({
search: false,
}),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
}),
{
title: '关联项目',
dataIndex: ['asset_project', 'name'],
search: {
transform: (value) => {
return { project_name: value };
},
},
},
{
title: '点位名称',
dataIndex: 'name',
},
{
title: '位置编码',
dataIndex: 'code',
},
MyColumns.Boolean({
dataIndex: 'is_enabled',
title: '启用/禁用',
search: false,
}),
MyColumns.CreatedAt(),
MyColumns.Option({
render: (_, item: any, index, action) => (
<Space key={index}>
<Update item={item} reload={action?.reload} title={title} />
<MyButtons.Default
title={item.is_enabled === 1 ? '禁用' : '启用'}
type={item.is_enabled === 1 ? 'default' : 'primary'}
danger={item.is_enabled === 1}
isConfirm={true}
description={
item.is_enabled === 1
? `确定要禁用点位吗?`
: `确定要启用点位吗?`
}
onConfirm={() =>
Apis.Patrol.PatrolLocations.Update({
id: item.id ?? 0,
is_enabled: item.is_enabled === 1 ? 0 : 1,
name: item.name,
code: item.code,
asset_projects_id: item.asset_projects_id,
}).then(() => action?.reload())
}
/>
<MyButtons.Delete
onConfirm={() =>
Apis.Patrol.PatrolLocations.Delete({ id: item.id }).then(() =>
action?.reload(),
)
}
/>
</Space>
),
}),
]}
/>
);
}