|
|
@@ -1550,6 +1550,7 @@ import {
|
|
|
type ContractQuery,
|
|
|
type ContractVO
|
|
|
} from '@/api/crm/contract';
|
|
|
+import { getQuote } from '@/api/crm/quote';
|
|
|
import { useDict } from '@/utils/dict';
|
|
|
import { parseTime } from '@/utils/ruoyi';
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
@@ -2369,8 +2370,9 @@ const handleGeneratePdf = async (row: ContractVO) => {
|
|
|
const contract = res.data;
|
|
|
|
|
|
// 2. 生成HTML内容(先生成带占位符的模板,再替换占位符为最新数据)
|
|
|
+ const quoteFreight = await fetchQuoteFreight(contract.quoteId);
|
|
|
let htmlContent = generateContractTemplate(contract);
|
|
|
- htmlContent = replacePlaceholders(htmlContent, contract);
|
|
|
+ htmlContent = replacePlaceholders(htmlContent, contract, quoteFreight);
|
|
|
|
|
|
// 3. 创建临时容器
|
|
|
const tempDiv = document.createElement('div');
|
|
|
@@ -2729,6 +2731,8 @@ const handleDetailPrint = async () => {
|
|
|
console.warn('⚠️ 获取最新合同数据失败,使用详情抽屉中的数据:', fetchError);
|
|
|
}
|
|
|
|
|
|
+ const quoteFreight = await fetchQuoteFreight(contractData.quoteId);
|
|
|
+
|
|
|
// 先尝试从数据库获取已保存的模板
|
|
|
let htmlContent = '';
|
|
|
try {
|
|
|
@@ -2753,7 +2757,7 @@ const handleDetailPrint = async () => {
|
|
|
|
|
|
// 统一用数据库最新数据替换占位符(无论模板来自数据库还是新生成的,都可能含占位符)
|
|
|
try {
|
|
|
- htmlContent = replacePlaceholders(htmlContent, contractData);
|
|
|
+ htmlContent = replacePlaceholders(htmlContent, contractData, quoteFreight);
|
|
|
console.log('占位符替换完成');
|
|
|
} catch (replaceError) {
|
|
|
console.error('替换占位符失败,使用原始内容:', replaceError);
|
|
|
@@ -3017,8 +3021,21 @@ watch(detailActiveTab, (newTab) => {
|
|
|
// 目前回款和开票记录在详情中已存在
|
|
|
});
|
|
|
|
|
|
+/** 获取合同关联报价单的运费(无关联或获取失败时返回 0) */
|
|
|
+const fetchQuoteFreight = async (quoteId?: number): Promise<number> => {
|
|
|
+ if (!quoteId) return 0;
|
|
|
+ try {
|
|
|
+ const quoteRes = await getQuote(quoteId);
|
|
|
+ const freight = Number(quoteRes?.data?.freight);
|
|
|
+ return Number.isFinite(freight) && freight > 0 ? freight : 0;
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('获取报价单运费失败:', error);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
/** 替换模板中的占位符为数据库最新数据 */
|
|
|
-const replacePlaceholders = (htmlContent: string, contractData: ContractVO | undefined): string => {
|
|
|
+const replacePlaceholders = (htmlContent: string, contractData: ContractVO | undefined, quoteFreight = 0): string => {
|
|
|
if (!htmlContent || !contractData) return htmlContent;
|
|
|
|
|
|
let result = htmlContent;
|
|
|
@@ -3260,12 +3277,18 @@ const replacePlaceholders = (htmlContent: string, contractData: ContractVO | und
|
|
|
// 匹配 {{productTable}} 占位符
|
|
|
result = result.replace(/\{\{productTable\}\}/gi, productRows);
|
|
|
|
|
|
- // 替换产品合计行
|
|
|
- const totalAmountText = isFrameworkContract ? '随行就市' : `${productTotalChinese}(¥${formatNumber(productTotal)})`;
|
|
|
+ // 替换产品合计行(左侧运费,右侧合计=产品金额+运费)
|
|
|
+ const freightAmount = Number(quoteFreight) || 0;
|
|
|
+ const totalWithFreight = productTotal + freightAmount;
|
|
|
+ const totalWithFreightChinese = numberToChinese(totalWithFreight);
|
|
|
+ const freightText = freightAmount > 0 ? `¥${formatNumber(freightAmount)}` : '-';
|
|
|
+ const totalWithFreightText = isFrameworkContract ? '随行就市' : `${totalWithFreightChinese}(¥${formatNumber(totalWithFreight)})`;
|
|
|
const productTotalRow = `
|
|
|
<tr style="background-color: #fff; font-weight: bold;">
|
|
|
- <td colspan="4" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">合计(人民币)</td>
|
|
|
- <td colspan="6" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">${totalAmountText}</td>
|
|
|
+ <td colspan="2" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">运费</td>
|
|
|
+ <td colspan="2" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">${freightText}</td>
|
|
|
+ <td colspan="2" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">合计(人民币)</td>
|
|
|
+ <td colspan="4" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">${totalWithFreightText}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td colspan="10" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
|
|
|
@@ -3785,8 +3808,7 @@ const handleSaveTemplate = async () => {
|
|
|
|
|
|
// 替换合计行(如果存在)
|
|
|
if (totalRowHtml) {
|
|
|
- // 查找并替换合计行(通过合计人民币文本定位)
|
|
|
- const totalRowPattern = /<tr style="background-color: #fff; font-weight: bold;">[\s\S]*?<td colspan="4"[\s\S]*?>合计[\s\S]*?<\/tr>/;
|
|
|
+ const totalRowPattern = /<tr style="background-color: #fff; font-weight: bold;">[\s\S]*?<\/tr>/;
|
|
|
if (totalRowPattern.test(finalTemplate)) {
|
|
|
finalTemplate = finalTemplate.replace(totalRowPattern, totalRowHtml);
|
|
|
}
|
|
|
@@ -4087,7 +4109,8 @@ const handleGeneratePdfFromDialog = async () => {
|
|
|
if (htmlContent.includes('{{') || htmlContent.includes('SETTLEMENT_METHOD_CONTENT')) {
|
|
|
// 重新获取最新合同数据来替换占位符
|
|
|
const res = await getContractDetail(detailContract.value.id);
|
|
|
- htmlContent = replacePlaceholders(htmlContent, res.data);
|
|
|
+ const quoteFreight = await fetchQuoteFreight(res.data?.quoteId);
|
|
|
+ htmlContent = replacePlaceholders(htmlContent, res.data, quoteFreight);
|
|
|
}
|
|
|
|
|
|
// 1. 创建临时容器
|