Files
klp-oa/klp-ui/src/views/crm/contract/components/ContractList.vue
砂糖 9f3d402174 refactor(crm): 重构合同模块为订单模块并优化相关功能
重构合同模块为订单模块,包括以下主要变更:
1. 将合同编号字段从contractNo统一改为contractCode
2. 在CrmOrderBo中添加日期格式化注解
3. 重构ContractTabs组件为订单详情页
4. 添加销售员字段和相关选择器
5. 优化订单列表查询条件和展示
6. 调整订单附件管理功能
2026-04-13 17:48:19 +08:00

735 lines
29 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

<template>
<div class="contract-list">
<!-- 筛选区和按钮操作区合并 -->
<div class="filter-section" style="padding: 10px; border-bottom: 1px solid #e4e7ed;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
<div style="display: flex; align-items: center; gap: 4px;">
<el-input v-model="queryParams.keyword" placeholder="请输入关键字" clearable
@keyup.enter.native="handleQuery" />
<el-button icon="el-icon-sort" size="mini" @click="toggleMoreFilter"
:type="showMoreFilter ? 'primary' : 'default'">
<!-- {{ showMoreFilter ? '收起' : '更多' }} -->
</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="$emit('add')"></el-button>
</div>
</div>
<!-- 更多筛选条件 -->
<div v-show="showMoreFilter" class="more-filter"
style="margin-top: 10px; padding-top: 10px; border-top: 1px dashed #e4e7ed;">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
<el-form-item label="合同名称" prop="contractName">
<el-input v-model="queryParams.contractName" placeholder="请输入合同名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="销售员" prop="salesman">
<el-input v-model="queryParams.salesman" placeholder="请输入销售员" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="合同编号" prop="contractCode">
<el-input v-model="queryParams.contractCode" placeholder="请输入合同编号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="供方" prop="supplier">
<el-input v-model="queryParams.supplier" placeholder="请输入供方" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="需方" prop="customer">
<el-input v-model="queryParams.customer" placeholder="请输入需方" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="签订时间" prop="signTime">
<el-date-picker clearable v-model="queryParams.signTime" type="date" value-format="yyyy-MM-dd"
placeholder="请选择签订时间">
</el-date-picker>
</el-form-item>
<el-form-item label="交货日期" prop="deliveryDate">
<el-date-picker clearable v-model="queryParams.deliveryDate" type="date" value-format="yyyy-MM-dd"
placeholder="请选择交货日期">
</el-date-picker>
</el-form-item>
<el-form-item label="签订地点" prop="signLocation">
<el-input v-model="queryParams.signLocation" placeholder="请输入签订地点" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="合同状态" prop="contractStatus">
<el-select v-model="queryParams.contractStatus" placeholder="请选择合同状态">
<el-option label="草稿" value="0" />
<el-option label="已生效" value="1" />
<el-option label="已作废" value="2" />
<el-option label="已完成" value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="custom-list" v-loading="loading">
<div class="list-body">
<div v-for="row in contractList" :key="row.contractId" class="list-item"
style="padding: 10px; border-bottom: 2px solid #dddddd; cursor: pointer;"
:class="{ 'list-item-active': selectedRow === row }" @click="handleRowClick(row)">
<!-- 合同名称和编号 -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<div style="font-weight: bold;">{{ row.contractName }}</div>
<div style="font-size: 12px; color: #606266;">{{ row.contractNo }}</div>
</div>
<!-- 供方和需方 -->
<div style="font-size: 12px; color: #909399; margin-bottom: 6px;">
<span>供方: {{ row.supplier }}</span>
<span style="margin-left: 20px;">需方: {{ row.customer }}</span>
</div>
<!-- 签订时间和交货日期 -->
<div style="font-size: 12px; color: #909399; margin-bottom: 6px;">
<span>签订时间: {{ parseTime(row.signTime, '{y}-{m}-{d}') }}</span>
<span style="margin-left: 20px;">交货日期: {{ parseTime(row.deliveryDate, '{y}-{m}-{d}') }}</span>
</div>
<!-- 签订地点和状态 -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<div style="font-size: 12px; color: #909399;">
签订地点: {{ row.signLocation }}
</div>
<el-tag
:type="row.status == 0 ? 'info' : row.status == 1 ? 'success' : row.status == 2 ? 'danger' : 'primary'"
size="small">
{{ row.status == 0 ? '草稿' : row.status == 1 ? '已生效' : row.status == 2 ? '已作废' : '已完成' }}
</el-tag>
</div>
<!-- 操作按钮独占一行 -->
<div style="display: flex; gap: 10px; padding-top: 8px; border-top: 1px dashed #f0f0f0;">
<el-button size="mini" type="text" icon="el-icon-download" @click.stop="handleExport(row)">导出</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="$emit('update', row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="$emit('delete', row)">删除</el-button>
</div>
</div>
<div v-if="contractList.length === 0" style="padding: 40px; text-align: center; color: #909399;">
暂无合同数据
</div>
</div>
</div>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" style="padding: 10px; margin-bottom: 10px !important;" />
</div>
</template>
<script>
import { listOrder, updateOrder } from "@/api/crm/order";
import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
export default {
name: "ContractList",
dicts: ['wip_pack_saleman'],
data() {
return {
// 合同信息表格数据
contractList: [],
// 遮罩层
loading: false,
// 总条数
total: 0,
// 选中的行
selectedRow: null,
// 是否显示更多筛选
showMoreFilter: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
contractName: undefined,
contractNo: undefined,
supplier: undefined,
customer: undefined,
signTime: undefined,
deliveryDate: undefined,
signLocation: undefined,
status: undefined,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询合同信息列表 */
getList() {
this.loading = true;
listOrder(this.queryParams).then(response => {
this.contractList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 状态变更 */
handleChangeStatus(row) {
updateOrder(row).then(response => {
this.$message({
message: "状态变更成功",
type: "success"
});
this.getList();
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.$refs["queryForm"].resetFields();
this.handleQuery();
},
// 行点击事件
handleRowClick(row) {
this.selectedRow = row;
this.$emit('rowClick', row);
},
// 切换更多筛选显示/隐藏
toggleMoreFilter() {
this.showMoreFilter = !this.showMoreFilter;
},
/** 导出合同 */
async handleExport(row) {
// 1. 创建excel
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('产品销售合同');
// 2. 设置列宽
worksheet.columns = [
{ width: 10 },
{ width: 20 },
{ width: 15 },
{ width: 15 },
{ width: 15 },
{ width: 15 },
{ width: 15 },
{ width: 20 }
];
// 3. 合并单元格并设置内容
// 公司信息
worksheet.mergeCells('A1:H1');
worksheet.getCell('A1').value = '嘉祥科伦普重工有限公司';
worksheet.getCell('A1').font = { size: 16, bold: true };
worksheet.getCell('A1').alignment = { horizontal: 'center', vertical: 'middle' };
// 合同标题
worksheet.mergeCells('A2:F2');
worksheet.getCell('A2').value = '产品销售合同';
worksheet.getCell('A2').font = { size: 18, bold: true };
worksheet.getCell('A2').alignment = { horizontal: 'center', vertical: 'middle' };
// 合同编号
worksheet.mergeCells('G2:H2');
worksheet.getCell('G2').value = `合同编号:${row.contractNo || ''}`;
worksheet.getCell('G2').alignment = { horizontal: 'right', vertical: 'middle' };
// 供方信息
worksheet.getCell('A3').value = `供方(甲方):${row.supplier || '嘉祥科伦普重工有限公司'}`;
worksheet.getCell('A3').alignment = { horizontal: 'left', vertical: 'middle' };
// 签订时间
worksheet.getCell('E3').value = `签订时间:${row.signTime ? this.parseTime(row.signTime, '{y}年{m}月{d}日') : '2026年 月 日'}`;
worksheet.getCell('E3').alignment = { horizontal: 'left', vertical: 'middle' };
// 需方信息
worksheet.getCell('A4').value = `需方(乙方):${row.customer || ''}`;
worksheet.getCell('A4').alignment = { horizontal: 'left', vertical: 'middle' };
// 签订地点
worksheet.getCell('E4').value = `签订地点:${row.signLocation || '山东省济宁市嘉祥县'}`;
worksheet.getCell('E4').alignment = { horizontal: 'left', vertical: 'middle' };
// 产品内容标题
worksheet.getCell('A5').value = '一、产品内容';
worksheet.getCell('A5').font = { bold: true };
// 产品名称和生产厂家
worksheet.mergeCells('A6:C6');
worksheet.getCell('A6').value = `产品名称:${row.productName || '冷硬钢卷'}`;
worksheet.getCell('A6').alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.getCell('A6').border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.mergeCells('D6:H6');
worksheet.getCell('D6').value = `生产厂家:${row.manufacturer || '嘉祥科伦普重工有限公司'}`;
worksheet.getCell('D6').alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.getCell('D6').border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
// 产品表格标题(分为两行)
// 合并序号、规格、材质、备注列的单元格(两行)
const mergedHeaders = ['序号', '规格mm', '材质', '备注'];
const mergedColumns = [1, 2, 3, 8];
mergedColumns.forEach((col, index) => {
// 合并两行
worksheet.mergeCells(7, col, 8, col);
const cell = worksheet.getCell(7, col);
cell.value = mergedHeaders[index];
cell.font = { bold: true };
cell.fill = { type: 'pattern', pattern: 'solid' };
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
cell.alignment = { horizontal: 'center', vertical: 'middle' };
});
// 数量、含税单价、不含税单价、含税总额的标题和单位各占一行
const quantityHeaders = ['数量', '(吨)'];
const taxPriceHeaders = ['含税单价', '(元/吨)'];
const noTaxPriceHeaders = ['不含税单价', '(元/吨)'];
const taxTotalHeaders = ['含税总额', '(元)'];
// 数量列
worksheet.getCell(7, 4).value = quantityHeaders[0];
worksheet.getCell(8, 4).value = quantityHeaders[1];
// 含税单价列
worksheet.getCell(7, 5).value = taxPriceHeaders[0];
worksheet.getCell(8, 5).value = taxPriceHeaders[1];
// 不含税单价列
worksheet.getCell(7, 6).value = noTaxPriceHeaders[0];
worksheet.getCell(8, 6).value = noTaxPriceHeaders[1];
// 含税总额列
worksheet.getCell(7, 7).value = taxTotalHeaders[0];
worksheet.getCell(8, 7).value = taxTotalHeaders[1];
// 设置样式
for (let row = 7; row <= 8; row++) {
for (let col = 4; col <= 7; col++) {
const cell = worksheet.getCell(row, col);
cell.font = { bold: true };
cell.fill = { type: 'pattern', pattern: 'solid' };
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
cell.alignment = { horizontal: 'center', vertical: 'middle' };
}
}
// 产品表格数据, 来源于一个json字符串productContentjson结构参考ProductContent.vue
// 解析产品内容
let productData = {
products: [],
productName: row.productName || '冷硬钢卷',
remark: '',
totalQuantity: 0,
totalTaxTotal: 0,
totalAmountInWords: '零元整'
};
if (row.productContent) {
try {
productData = JSON.parse(row.productContent);
} catch (error) {
console.error('解析产品内容失败:', error);
}
}
// 更新产品名称
worksheet.getCell('A6').value = `产品名称:${productData.productName}`;
// 产品表格数据
let currentRow = 9;
let totalQuantity = 0;
let totalTaxTotal = 0;
if (productData.products && productData.products.length > 0) {
productData.products.forEach((product, index) => {
const rowNum = currentRow + index;
worksheet.getCell(`A${rowNum}`).value = index + 1;
worksheet.getCell(`B${rowNum}`).value = product.spec || '';
worksheet.getCell(`C${rowNum}`).value = product.material || '';
worksheet.getCell(`D${rowNum}`).value = product.quantity || 0;
worksheet.getCell(`E${rowNum}`).value = product.taxPrice || 0;
worksheet.getCell(`F${rowNum}`).value = product.noTaxPrice || 0;
worksheet.getCell(`G${rowNum}`).value = product.taxTotal || 0;
worksheet.getCell(`H${rowNum}`).value = product.remark || '';
// 设置数据行边框
for (let i = 1; i <= 8; i++) {
const cell = worksheet.getCell(rowNum, i);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
cell.alignment = { horizontal: 'center', vertical: 'middle' };
}
// 累加合计
totalQuantity += parseFloat(product.quantity || 0);
totalTaxTotal += parseFloat(product.taxTotal || 0);
});
currentRow += productData.products.length;
} else {
// 至少一行空数据
worksheet.getCell(`A${currentRow}`).value = 1;
worksheet.getCell(`B${currentRow}`).value = '';
worksheet.getCell(`C${currentRow}`).value = 'SPCC';
worksheet.getCell(`D${currentRow}`).value = '';
worksheet.getCell(`E${currentRow}`).value = '';
worksheet.getCell(`F${currentRow}`).value = 0.00;
worksheet.getCell(`G${currentRow}`).value = 0;
worksheet.getCell(`H${currentRow}`).value = '';
// 设置数据行边框
for (let i = 1; i <= 8; i++) {
const cell = worksheet.getCell(currentRow, i);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
cell.alignment = { horizontal: 'center', vertical: 'middle' };
}
currentRow++;
}
// 合计行
worksheet.getCell(`A${currentRow}`).value = '合 计';
worksheet.getCell(`A${currentRow}`).font = { bold: true };
worksheet.getCell(`A${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'center', vertical: 'middle' };
worksheet.getCell(`D${currentRow}`).value = productData.totalQuantity || totalQuantity;
worksheet.getCell(`D${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`D${currentRow}`).alignment = { horizontal: 'center', vertical: 'middle' };
worksheet.getCell(`G${currentRow}`).value = productData.totalTaxTotal || totalTaxTotal;
worksheet.getCell(`G${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`G${currentRow}`).alignment = { horizontal: 'center', vertical: 'middle' };
worksheet.getCell(`H${currentRow}`).value = '';
worksheet.getCell(`H${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`H${currentRow}`).alignment = { horizontal: 'center', vertical: 'middle' };
currentRow++;
// 大写金额
worksheet.mergeCells(`A${currentRow}:B${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `合计人民币(大写)`;
worksheet.getCell(`A${currentRow}`).font = { bold: true };
worksheet.getCell(`A${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`C${currentRow}:H${currentRow}`);
worksheet.getCell(`C${currentRow}`).value = `${productData.totalAmountInWords || row.totalAmountUpper || '零元整'}`;
worksheet.getCell(`C${currentRow}`).font = { bold: true };
worksheet.getCell(`C${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`C${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 备注行
worksheet.mergeCells(`B${currentRow}:H${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = '备注:';
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'top' };
worksheet.getCell(`A${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`B${currentRow}`).alignment = { horizontal: 'left', vertical: 'top' };
worksheet.getCell(`B${currentRow}`).value = productData.remark || '';
worksheet.getCell(`B${currentRow}`).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
worksheet.getCell(`B${currentRow}`).alignment = { horizontal: 'left', vertical: 'top', wrapText: true };
currentRow++;
// 空白行保持至少5行空白
// const emptyRows = Math.max(5, 15 - currentRow + 1);
// for (let i = 0; i < emptyRows; i++) {
// const rowNum = currentRow + i;
// for (let j = 1; j <= 8; j++) {
// const cell = worksheet.getCell(rowNum, j);
// cell.border = {
// top: { style: 'thin' },
// left: { style: 'thin' },
// bottom: { style: 'thin' },
// right: { style: 'thin' }
// };
// }
// }
// currentRow += emptyRows;
// 产品表格之后是其他合同内容contractContent字段存储的是HTML格式的富文本
if (row.contractContent) {
// 优化HTML处理保留p标签作为换行符将多个p标签单元格合并
let htmlContent = row.contractContent;
// 提取所有p标签内容
const pTagRegex = /<p[^>]*>([\s\S]*?)<\/p>/g;
let match;
const pContents = [];
while ((match = pTagRegex.exec(htmlContent)) !== null) {
let content = match[1];
// 移除其他HTML标签
content = content.replace(/<[^>]*>/g, '');
// 处理HTML实体
content = content.replace(/&nbsp;/g, ' ');
content = content.replace(/&lt;/g, '<');
content = content.replace(/&gt;/g, '>');
content = content.replace(/&amp;/g, '&');
content = content.replace(/&quot;/g, '"');
content = content.replace(/&#39;/g, "'");
// 清理空格和换行
content = content.trim();
// 如果不是以大写汉字数字开头,添加一个中文字符的缩进
if (content) {
// 检查是否以大写汉字数字开头(一、二、三、四、五、六、七、八、九、十)
const chineseNumberRegex = /^[一二三四五六七八九十]+、/;
if (!chineseNumberRegex.test(content)) {
content = ' ' + content; // 使用全角空格作为中文字符缩进
}
pContents.push(content);
}
}
// 如果没有提取到p标签内容尝试提取所有文本内容
if (pContents.length === 0) {
let textContent = htmlContent.replace(/<[^>]*>/g, '');
textContent = textContent.replace(/&nbsp;/g, ' ').trim();
// 如果不是以大写汉字数字开头,添加一个中文字符的缩进
if (textContent) {
// 检查是否以大写汉字数字开头(一、二、三、四、五、六、七、八、九、十)
const chineseNumberRegex = /^[一二三四五六七八九十]+、/;
if (!chineseNumberRegex.test(textContent)) {
textContent = ' ' + textContent; // 使用全角空格作为中文字符缩进
}
pContents.push(textContent);
}
}
// 直接合并单元格并设置内容,避免合并已合并的单元格
if (pContents.length > 0) {
// 计算需要的行数
const contentLines = pContents.reduce((total, content) => {
return total + (content.match(/\n/g) || []).length + 1;
}, 0);
// 合并单元格
const endRow = currentRow + Math.ceil(contentLines / 2); // 估算需要的行数
worksheet.mergeCells(`A${currentRow}:H${endRow}`);
// 设置合并后单元格的内容和样式
const mergedContent = pContents.join('\n');
worksheet.getCell(`A${currentRow}`).value = mergedContent;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'top', wrapText: true };
// 调整合并后单元格的行高
const lineCount = (mergedContent.match(/\n/g) || []).length + 1;
const height = Math.max(60, lineCount * 15);
worksheet.getRow(currentRow).height = height;
// 调整currentRow
currentRow = endRow + 1;
}
}
// 设置合同尾部信息,每一行的前四列与后四列分别合并
// 内容参考
// 设置合同尾部信息,每一行的前四列与后四列分别合并
// 内容参考ContractPreview.vue
if (currentRow > 0) {
// 空行
currentRow++;
// 供方(甲方)信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `供方(甲方):${row.supplier || '嘉祥科伦普重工有限公司'}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `需方(乙方):${row.customer || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 地址信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `地址:${row.supplierAddress || ''}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `地址:${row.customerAddress || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 电话信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `电话:${row.supplierPhone || ''}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `电话:${row.customerPhone || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 开户行信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `开户行:${row.supplierBank || ''}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `开户行:${row.customerBank || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 账号信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `账号:${row.supplierAccount || ''}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `账号:${row.customerAccount || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
// 税号信息
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
worksheet.getCell(`A${currentRow}`).value = `税号:${row.supplierTaxNo || ''}`;
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
worksheet.getCell(`E${currentRow}`).value = `税号:${row.customerTaxNo || ''}`;
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
currentRow++;
}
// 4. 设置行高
worksheet.getRow(1).height = 40;
worksheet.getRow(2).height = 30;
worksheet.getRow(3).height = 20;
worksheet.getRow(4).height = 20;
worksheet.getRow(5).height = 20;
worksheet.getRow(6).height = 20;
worksheet.getRow(7).height = 25;
// 自动调整数据行高
for (let i = 8; i <= currentRow; i++) {
if (!worksheet.getRow(i).height) {
worksheet.getRow(i).height = 20;
}
}
// 5. 导出文件
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
saveAs(blob, `合同_${row.contractNo || row.contractName || '未命名'}.xlsx`);
},
}
};
</script>
<style scoped>
.contract-list {
height: 100%;
overflow-y: auto;
}
.custom-list {
border: 1px solid #e4e7ed;
border-radius: 4px;
overflow: hidden;
}
.list-item:hover {
background-color: #f5f7fa;
}
.list-item-active {
background-color: #ecf5ff;
border-left: 3px solid #409eff;
}
@media screen and (max-width: 1200px) {
.list-header {
font-size: 12px;
}
.list-item {
font-size: 12px;
}
.list-item .el-button {
font-size: 10px;
}
}
</style>