diff --git a/src/pages/company/quotation/view/index.jsx b/src/pages/company/quotation/view/index.jsx
index d271452..551b0fb 100644
--- a/src/pages/company/quotation/view/index.jsx
+++ b/src/pages/company/quotation/view/index.jsx
@@ -1,69 +1,37 @@
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 { Card, Button, Typography, Space, Spin, Divider, Tag } from 'antd';
+import { FileTextOutlined, DownloadOutlined } from '@ant-design/icons';
+import { useParams } from 'react-router-dom';
import { supabase } from '@/config/supabase';
-import ReactDOMServer from 'react-dom/server';
-import PrintView from '@/components/PrintView';
+import html2pdf from 'html2pdf.js';
const { Title, Text } = Typography;
-const printStyles = `
- @media print {
- /* 隐藏所有按钮和不需要的元素 */
- button, .no-print {
- display: none !important;
- }
+const CURRENCY_SYMBOLS = {
+ CNY: "¥",
+ TWD: "NT$",
+ USD: "$",
+};
- /* 只打印卡片内容 */
- .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 QuotationPreview = () => {
const { id } = useParams();
- const [data, setData] = useState(null);
- const [loading, setLoading] = useState(true);
+ const [loading, setLoading] = useState(false);
+ const [quotation, setQuotation] = useState(null);
+ // 获取报价单详情
const fetchQuotationDetail = async () => {
try {
- const { data: quotation, error } = await supabase
- .from('resources')
- .select('*')
- .eq('id', id)
+ setLoading(true);
+ const { data, error } = await supabase
+ .from("resources")
+ .select("*")
+ .eq("id", id)
.single();
if (error) throw error;
- setData(quotation);
+ setQuotation(data);
} catch (error) {
- console.error('获取报价单失败:', error);
+ message.error("获取报价单详情失败");
} finally {
setLoading(false);
}
@@ -75,187 +43,165 @@ const QuotationView = () => {
}
}, [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 })
- ),
- },
- ];
+ // 导出PDF
+ const handleExportPDF = () => {
+ const element = document.getElementById('quotation-content');
+ const opt = {
+ margin: [10, 10],
+ filename: `${quotation.attributes.quataName || '报价单'}.pdf`,
+ image: { type: 'jpeg', quality: 0.98 },
+ html2canvas: { scale: 2 },
+ jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
+ };
- const handlePrint = () => {
- const printWindow = window.open('', '_blank');
- const printContent = ReactDOMServer.renderToString(
);
- printWindow.document.write(`
-
-
-
-
打印报价单
-
-
-
-
- ${printContent}
-
-
-
- `);
- printWindow.document.close();
+ html2pdf().set(opt).from(element).save();
};
if (loading) {
return (
-
+
);
}
- if (!data) {
- return null;
- }
+ if (!quotation) return null;
+
+ const { attributes } = quotation;
+ const currencySymbol = CURRENCY_SYMBOLS[attributes.currency] || '¥';
return (
- <>
-
-
-
-
-
+
+
- }
- onClick={() => navigate('/company/quotation')}
- >
- 返回列表
-
-
-
- }
- type="primary"
- onClick={() => navigate(`/company/quotaInfo/${data.id}?edit=true`)}
- >
- 编辑报价单
-
- }
- onClick={handlePrint}
- >
- 打印报价单
-
+
+ 报价单预览
+ }
+ onClick={handleExportPDF}
+ >
+ 导出PDF
+
-
+ }
+ >
+
+ {/* 报价单标题 */}
-
{data.attributes.quataName}
- 报价单号:{data.id}
+ {attributes.quataName}
+ 创建日期:{new Date(quotation.created_at).toLocaleDateString()}
-
-
-
- {data.attributes.customerName}
-
-
- {new Date(data.created_at).toLocaleDateString('zh-CN')}
-
-
- {new Date(Date.now() + 30*24*60*60*1000).toLocaleDateString('zh-CN')}
-
-
+ {/* 基本信息 */}
+
+
基本信息
+
+
+ 客户:
+
+ {attributes.customers?.map(customer => (
+ {customer.name}
+ ))}
+
+
+
+ 货币类型:
+ {attributes.currency}
+
+
-
-
报价明细
-
index}
- className="border rounded-lg"
- summary={() => (
-
-
-
- 总计({data.attributes.currency}):
-
-
- {data.attributes.totalAmount?.toLocaleString('zh-CN', {
- minimumFractionDigits: 2,
- style: 'currency',
- currency: data.attributes.currency
- })}
-
-
-
- )}
- />
+ {/* 报价明细 */}
+ {attributes.sections?.map((section, sIndex) => (
+
+
+
+
{section.sectionName}
+
+
+
+
+
+
+ | 项目明细 |
+ 描述/备注 |
+ 单位 |
+ 数量 |
+ 单价 |
+ 小计 |
+
+
+
+ {section.items.map((item, iIndex) => (
+
+ | {item.name} |
+ {item.description} |
+ {item.unit} |
+ {item.quantity} |
+
+ {currencySymbol}{item.price?.toLocaleString()}
+ |
+
+ {currencySymbol}{(item.quantity * item.price)?.toLocaleString()}
+ |
+
+ ))}
+
+
+
+
+ ))}
+
+ {/* 金额汇总 */}
+
+
+
+
+ 税前总计:
+ {currencySymbol}{attributes.beforeTaxAmount?.toLocaleString()}
+
+
+ 税率:
+ {attributes.taxRate}%
+
+
+ 税后总计:
+ {currencySymbol}{attributes.afterTaxAmount?.toLocaleString()}
+
+ {attributes.discount > 0 && (
+
+ 折扣价:
+ {currencySymbol}{attributes.discount?.toLocaleString()}
+
+ )}
+
+
+ 最终金额:
+
+ {currencySymbol}{(attributes.discount || attributes.afterTaxAmount)?.toLocaleString()}
+
+
+
+
- {data.attributes.description && (
-
-
补充说明
-
{data.attributes.description}
+ {/* 补充说明 */}
+ {attributes.description && (
+
+
补充说明
+
+ {attributes.description}
+
)}
-
-
-
- 注意事项:
-
-
- - 1. 本报价单有效期为30天
- - 2. 最终解释权归本公司所有
- - 3. 如有疑问请及时联系我们
-
-
-
-
- >
+
+
+
);
};
-export default QuotationView;
\ No newline at end of file
+export default QuotationPreview;
\ No newline at end of file