|
|
@@ -364,14 +364,7 @@
|
|
|
@click.stop="handleViewAttachment(scope.row, file)"
|
|
|
>
|
|
|
<!-- 图片缩略图 -->
|
|
|
- <ImagePreview
|
|
|
- v-if="isListImageFile(file)"
|
|
|
- :src="file.ossId"
|
|
|
- class="attachment-thumb-image"
|
|
|
- :isOssId="true"
|
|
|
- width="50"
|
|
|
- height="50"
|
|
|
- />
|
|
|
+ <ImagePreview v-if="isListImageFile(file)" :src="file.ossId" class="attachment-thumb-image" :isOssId="true" width="50" height="50" />
|
|
|
<!-- 非图片文件图标 -->
|
|
|
<div v-else class="attachment-file-icon">
|
|
|
<el-icon><Document /></el-icon>
|
|
|
@@ -1668,14 +1661,14 @@ const loadAllAttachmentInfos = () => {
|
|
|
contractList.value.forEach((row) => {
|
|
|
if (row.attachmentFiles) {
|
|
|
const attachments = parseListAttachments(row.attachmentFiles);
|
|
|
- attachments.forEach(att => {
|
|
|
+ attachments.forEach((att) => {
|
|
|
if (att.ossId && !allOssIds.includes(att.ossId)) {
|
|
|
allOssIds.push(att.ossId);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
if (allOssIds.length > 0) {
|
|
|
loadAttachmentInfos(allOssIds);
|
|
|
}
|
|
|
@@ -1708,7 +1701,7 @@ const parseListAttachments = (fileStr: string): Array<{ ossId: string; category?
|
|
|
if (Array.isArray(parsed)) {
|
|
|
return parsed.map((item: any) => ({
|
|
|
ossId: typeof item === 'string' ? item : item.ossId || item,
|
|
|
- category: typeof item === 'object' ? (item.category || 'other') : 'other'
|
|
|
+ category: typeof item === 'object' ? item.category || 'other' : 'other'
|
|
|
}));
|
|
|
}
|
|
|
} catch (e) {
|
|
|
@@ -1721,7 +1714,7 @@ const parseListAttachments = (fileStr: string): Array<{ ossId: string; category?
|
|
|
.split(',')
|
|
|
.map((id: string) => id.trim())
|
|
|
.filter((id: string) => id)
|
|
|
- .map(ossId => ({ ossId, category: 'other' }));
|
|
|
+ .map((ossId) => ({ ossId, category: 'other' }));
|
|
|
};
|
|
|
|
|
|
/** 获取附件数量 */
|
|
|
@@ -1739,7 +1732,7 @@ const loadAttachmentInfos = async (ossIds: string[]) => {
|
|
|
if (ossIds.length === 0) return;
|
|
|
|
|
|
// 过滤出未缓存的ossId
|
|
|
- const uncachedIds = ossIds.filter(id => !attachmentInfoCache.value.has(id));
|
|
|
+ const uncachedIds = ossIds.filter((id) => !attachmentInfoCache.value.has(id));
|
|
|
if (uncachedIds.length === 0) return;
|
|
|
|
|
|
try {
|
|
|
@@ -1765,7 +1758,7 @@ const loadAttachmentInfos = async (ossIds: string[]) => {
|
|
|
const getAttachmentList = (row: ContractVO): Array<{ ossId: string; name: string; url?: string }> => {
|
|
|
if (!row.attachmentFiles) return [];
|
|
|
const attachments = parseListAttachments(row.attachmentFiles);
|
|
|
-
|
|
|
+
|
|
|
// 返回所有附件,优先使用缓存的文件名
|
|
|
return attachments.map((att, index) => {
|
|
|
const cachedInfo = attachmentInfoCache.value.get(att.ossId);
|
|
|
@@ -1795,19 +1788,19 @@ const getListFileExt = (fileName: string): string => {
|
|
|
/** 获取短文件名(用于显示) */
|
|
|
const getShortFileName = (fileName: string): string => {
|
|
|
if (!fileName) return '';
|
|
|
-
|
|
|
+
|
|
|
const lastDotIndex = fileName.lastIndexOf('.');
|
|
|
if (lastDotIndex === -1) {
|
|
|
return fileName.length > 10 ? fileName.substring(0, 10) + '...' : fileName;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const name = fileName.substring(0, lastDotIndex);
|
|
|
const ext = fileName.substring(lastDotIndex);
|
|
|
-
|
|
|
+
|
|
|
if (name.length <= 8) {
|
|
|
return fileName;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return name.substring(0, 8) + '...' + ext;
|
|
|
};
|
|
|
|
|
|
@@ -2827,6 +2820,13 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
|
|
|
return '/src/assets/images/yz.png';
|
|
|
};
|
|
|
|
|
|
+ // 获取单位字典标签的辅助函数
|
|
|
+ const getUnitLabel = (unitValue: string | undefined): string => {
|
|
|
+ if (!unitValue) return '';
|
|
|
+ const unitDict = crm_product_unit.value?.find((item: any) => item.value === unitValue);
|
|
|
+ return unitDict?.label || unitValue || '';
|
|
|
+ };
|
|
|
+
|
|
|
// 构建产品明细表格行
|
|
|
const productRows =
|
|
|
contractData.products
|
|
|
@@ -2839,6 +2839,7 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
|
|
|
<tr>
|
|
|
<td style="padding: 4px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${product.productName || ''}</td>
|
|
|
<td style="padding: 4px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${product.model || product.productModel || ''}</td>
|
|
|
+ <td style="padding: 4px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${getUnitLabel(product.unit)}</td>
|
|
|
<td style="padding: 4px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${product.quantity || 0}</td>
|
|
|
<td style="padding: 4px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${formatNumber(product.price)}</td>
|
|
|
<td style="padding: 4px 6px; border: 1px solid #000; text-align: right; font-size: 12px;">${formatNumber(beforeTax)}</td>
|
|
|
@@ -2848,7 +2849,7 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
|
|
|
</tr>`;
|
|
|
})
|
|
|
.join('') ||
|
|
|
- '<tr><td colspan="8" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">暂无产品信息</td></tr>';
|
|
|
+ '<tr><td colspan="9" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">暂无产品信息</td></tr>';
|
|
|
|
|
|
return `
|
|
|
<div style="width: 210mm; margin: 0 auto; padding: 0 15mm 20mm 15mm; font-family: '宋体', SimSun, serif; line-height: 1.6; box-sizing: border-box;">
|
|
|
@@ -2890,8 +2891,9 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
|
|
|
<tr style="background-color: #fff;">
|
|
|
<th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 20%;">产品名称</th>
|
|
|
<th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 10%;">规格</th>
|
|
|
- <th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 8%;">用量(吨)</th>
|
|
|
- <th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 12%;">含税单价(元/吨)</th>
|
|
|
+ <th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 8%;">单位</th>
|
|
|
+ <th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 8%;">用量</th>
|
|
|
+ <th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 12%;">含税单价(元)</th>
|
|
|
<th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 12%;">未含税金额(元)</th>
|
|
|
<th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 10%;">税费(元)</th>
|
|
|
<th style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px; width: 13%;">含税金额(元)</th>
|
|
|
@@ -2902,12 +2904,12 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
|
|
|
${productRows}
|
|
|
<!-- 合计行 -->
|
|
|
<tr style="background-color: #fff; font-weight: bold;">
|
|
|
- <td colspan="4" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">合计(人民币)</td>
|
|
|
+ <td colspan="5" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">合计(人民币)</td>
|
|
|
<td colspan="4" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">${productTotalChinese}(¥${formatNumber(productTotal)})</td>
|
|
|
</tr>
|
|
|
<!-- 提示行 -->
|
|
|
<tr>
|
|
|
- <td colspan="8" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
|
|
|
+ <td colspan="9" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
|
|
|
含税、含运费、装卸费(一票制) 结算以实际到货吨位为准
|
|
|
</td>
|
|
|
</tr>
|
|
|
@@ -3690,11 +3692,11 @@ onMounted(() => {
|
|
|
overflow-x: auto;
|
|
|
overflow-y: hidden;
|
|
|
white-space: nowrap;
|
|
|
-
|
|
|
+
|
|
|
/* 隐藏滚动条但保持滚动功能 */
|
|
|
scrollbar-width: none; /* Firefox */
|
|
|
-ms-overflow-style: none; /* IE and Edge */
|
|
|
-
|
|
|
+
|
|
|
&::-webkit-scrollbar {
|
|
|
display: none; /* Chrome, Safari, Opera */
|
|
|
}
|