Files
manage/src/pages/company/quotation/view/index.jsx
‘Liammcl’ 9b4a7f5fd8 报价单魔魁
2024-12-18 02:01:29 +08:00

264 lines
7.5 KiB
JavaScript
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 React, { useEffect, useState } from 'react';
import { Card, Typography, Descriptions, Table, Button, Space, Spin } from 'antd';
import { ArrowLeftOutlined, PrinterOutlined, EditOutlined } from '@ant-design/icons';
import { useNavigate, useParams } from 'react-router-dom';
import { supabase } from '@/config/supabase';
import ReactDOMServer from 'react-dom/server';
import PrintView from '@/components/PrintView';
const { Title, Text } = Typography;
const printStyles = `
@media print {
/* 隐藏所有按钮和不需要的元素 */
button, .no-print {
display: none !important;
}
/* 只打印卡片内容 */
.ant-card {
box-shadow: none !important;
margin: 0 !important;
padding: 0 !important;
}
/* 移除背景色 */
body, .bg-gray-50 {
background: white !important;
min-height: auto !important;
padding: 0 !important;
}
/* 确保内容完整打印 */
.ant-card-body {
padding: 24px !important;
page-break-inside: avoid;
}
/* 优化表格打印样式 */
.ant-table {
page-break-inside: auto !important;
}
.ant-table-row {
page-break-inside: avoid !important;
}
}
`;
const QuotationView = () => {
const navigate = useNavigate();
const { id } = useParams();
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const fetchQuotationDetail = async () => {
try {
const { data: quotation, error } = await supabase
.from('resources')
.select('*')
.eq('id', id)
.single();
if (error) throw error;
setData(quotation);
} catch (error) {
console.error('获取报价单<E4BBB7><E58D95>情失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (id) {
fetchQuotationDetail();
}
}, [id]);
const columns = [
{
title: '序号',
dataIndex: 'index',
width: 100,
render: (_, __, index) => index + 1,
},
{
title: '名称',
dataIndex: 'productName',
width: '40%',
},
{
title: '数量',
dataIndex: 'quantity',
width: '20%',
align: 'right',
render: (value) => value?.toLocaleString('zh-CN', { minimumFractionDigits: 2 }),
},
{
title: '单价',
dataIndex: 'price',
width: '20%',
align: 'right',
render: (value) => value?.toLocaleString('zh-CN', { minimumFractionDigits: 2 }),
},
{
title: '小计',
width: '20%',
align: 'right',
render: (_, record) => (
(record.quantity * record.price)?.toLocaleString('zh-CN', { minimumFractionDigits: 2 })
),
},
];
const handlePrint = () => {
const printWindow = window.open('', '_blank');
const printContent = ReactDOMServer.renderToString(<PrintView data={data} />);
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>打印报价单</title>
<link rel="stylesheet" href="${window.location.origin}/antd.min.css">
<style>
@media print {
body {
padding: 0;
margin: 0;
}
}
</style>
</head>
<body>
${printContent}
<script>
window.onload = function() {
window.print();
window.onafterprint = function() {
window.close();
}
}
</script>
</body>
</html>
`);
printWindow.document.close();
};
if (loading) {
return (
<div className="flex justify-center items-center min-h-screen">
<Spin size="large" />
</div>
);
}
if (!data) {
return null;
}
return (
<>
<style>{printStyles}</style>
<div className="bg-gray-50 min-h-screen ">
<Card className="max-w-5xl mx-auto shadow-lg">
<div className="flex justify-between items-center mb-6 no-print">
<Space>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/company/quotation')}
>
返回列表
</Button>
</Space>
<Space>
<Button
icon={<EditOutlined />}
type="primary"
onClick={() => navigate(`/company/quotaInfo/${data.id}?edit=true`)}
>
编辑报价单
</Button>
<Button
icon={<PrinterOutlined />}
onClick={handlePrint}
>
打印报价单
</Button>
</Space>
</div>
<div className="text-center mb-8">
<Title level={2} className="!mb-2">{data.attributes.quataName}</Title>
<Text type="secondary">报价单号{data.id}</Text>
</div>
<div className="bg-gray-50 p-6 rounded-lg mb-8">
<Descriptions column={2} bordered>
<Descriptions.Item label="客户公司" span={1}>
{data.attributes.companyName}
</Descriptions.Item>
<Descriptions.Item label="供应商" span={1}>
{data.attributes.supplierName}
</Descriptions.Item>
<Descriptions.Item label="报价日期" span={1}>
{new Date(data.created_at).toLocaleDateString('zh-CN')}
</Descriptions.Item>
<Descriptions.Item label="报价有效期" span={1}>
{new Date(Date.now() + 30*24*60*60*1000).toLocaleDateString('zh-CN')}
</Descriptions.Item>
</Descriptions>
</div>
<div className="mb-8">
<Title level={4} className="mb-4">报价明细</Title>
<Table
columns={columns}
dataSource={data.attributes.items}
pagination={false}
rowKey={(record, index) => index}
className="border rounded-lg"
summary={() => (
<Table.Summary fixed>
<Table.Summary.Row className="bg-gray-50 font-bold">
<Table.Summary.Cell index={0} colSpan={4} align="right">
总计{data.attributes.currency}
</Table.Summary.Cell>
<Table.Summary.Cell index={1} align="right">
{data.attributes.totalAmount?.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
style: 'currency',
currency: data.attributes.currency
})}
</Table.Summary.Cell>
</Table.Summary.Row>
</Table.Summary>
)}
/>
</div>
{data.attributes.description && (
<div className="bg-gray-50 p-6 rounded-lg">
<Title level={4} className="mb-4">补充说明</Title>
<Text>{data.attributes.description}</Text>
</div>
)}
<div className="mt-8 pt-8 border-t border-gray-200">
<Text type="secondary" className="block mb-2">
注意事项
</Text>
<ul className="text-gray-500 text-sm">
<li>1. 本报价单有效期为30天</li>
<li>2. 最终解释权归本公司所有</li>
<li>3. 如有疑问请及时联系我们</li>
</ul>
</div>
</Card>
</div>
</>
);
};
export default QuotationView;