feat(工单管理): 添加工单导出功能

实现工单数据的导出功能,支持CSV、JSON和Excel格式。前端添加导出模态框和选择逻辑,后端实现数据处理和文件生成。用户可选择导出选中项、当前页或全部工单。

- 前端添加TicketExportModal组件处理导出选项
- 后端添加/export接口处理不同格式的导出请求
- 支持导出工单基础信息和自定义字段
- 添加导出按钮到工单管理页面
This commit is contained in:
zhang1106
2026-03-31 21:58:09 +08:00
parent 44137bb851
commit 773efdb9a4
3 changed files with 406 additions and 3 deletions
@@ -0,0 +1,159 @@
import React, { useState } from 'react';
import { Modal, Form, Select, Button, Space, message } from 'antd';
import { ExportOutlined, FileTextOutlined, BranchesOutlined, TableOutlined } from '@ant-design/icons';
const { Option } = Select;
const modalHeaderStyle = {
display: 'flex',
alignItems: 'center',
gap: '8px',
fontSize: '18px',
fontWeight: 600,
};
const TicketExportModal = ({
visible,
onExport,
onCancel,
selectedCount = 0,
currentPageCount = 0,
totalCount = 0,
}) => {
const [exportFormat, setExportFormat] = useState('csv');
const [exportScope, setExportScope] = useState('selected');
const [exportLoading, setExportLoading] = useState(false);
const handleExport = async () => {
if (exportScope === 'selected' && selectedCount === 0) {
message.warning('请先选择要导出的工单');
return;
}
setExportLoading(true);
try {
await onExport({
format: exportFormat,
scope: exportScope,
});
onCancel();
} catch (error) {
message.error('导出失败');
} finally {
setExportLoading(false);
}
};
return (
<Modal
title={
<div style={{ ...modalHeaderStyle, paddingRight: '32px' }}>
<ExportOutlined style={{ color: '#fa8c16' }} />
导出工单数据
</div>
}
open={visible}
onCancel={onCancel}
footer={[
<Button
key="cancel"
onClick={onCancel}
style={{
height: '40px',
borderRadius: '6px',
}}
>
取消
</Button>,
<Button
key="submit"
type="primary"
loading={exportLoading}
onClick={handleExport}
style={{
height: '40px',
borderRadius: '6px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
border: 'none',
color: '#ffffff',
fontWeight: '500',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
导出
</Button>,
]}
destroyOnHidden
styles={{
header: {
borderBottom: '1px solid #f0f0f0',
padding: '16px 24px',
position: 'relative',
},
body: { padding: '24px' },
}}
width={480}
>
<Form layout="vertical">
<Form.Item
label={<span style={{ fontWeight: 500 }}>导出格式</span>}
>
<Select
value={exportFormat}
onChange={setExportFormat}
style={{ width: '100%' }}
>
<Option value="csv">
<Space>
<FileTextOutlined />
CSV 格式适合Excel打开
</Space>
</Option>
<Option value="json">
<Space>
<BranchesOutlined />
JSON 格式适合程序处理
</Space>
</Option>
<Option value="xlsx">
<Space>
<TableOutlined />
Excel 格式.xlsx
</Space>
</Option>
</Select>
</Form.Item>
<Form.Item
label={<span style={{ fontWeight: 500 }}>导出范围</span>}
>
<Select
value={exportScope}
onChange={setExportScope}
style={{ width: '100%' }}
>
<Option value="selected">选中工单 ({selectedCount} )</Option>
<Option value="currentPage">当前页 ({currentPageCount} )</Option>
<Option value="all">全部工单 ({totalCount} )</Option>
</Select>
</Form.Item>
<div
style={{
background: '#f6f7ff',
border: '1px solid #e8eaff',
borderRadius: '8px',
padding: '12px 16px',
color: '#666',
fontSize: '13px',
}}
>
将导出工单的所有字段包括基础信息和自定义字段
</div>
</Form>
</Modal>
);
};
export default React.memo(TicketExportModal);