2026-04-01 10:44:51 +08:00
|
|
|
|
<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;">
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<div style="display: flex; align-items: center; gap: 4px;">
|
2026-04-07 11:47:41 +08:00
|
|
|
|
<el-input v-model="queryParams.keyword" placeholder="请输入关键字" clearable
|
|
|
|
|
|
@keyup.enter.native="handleQuery" />
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<el-button icon="el-icon-sort" size="mini" @click="toggleMoreFilter"
|
|
|
|
|
|
:type="showMoreFilter ? 'primary' : 'default'">
|
|
|
|
|
|
<!-- {{ showMoreFilter ? '收起' : '更多' }} -->
|
2026-04-01 10:44:51 +08:00
|
|
|
|
</el-button>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<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>
|
2026-04-01 10:44:51 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 更多筛选条件 -->
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<div v-show="showMoreFilter" class="more-filter"
|
|
|
|
|
|
style="margin-top: 10px; padding-top: 10px; border-top: 1px dashed #e4e7ed;">
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
|
2026-04-07 11:47:41 +08:00
|
|
|
|
<el-form-item label="合同名称" prop="contractName">
|
|
|
|
|
|
<el-input v-model="queryParams.contractName" placeholder="请输入合同名称" clearable
|
|
|
|
|
|
@keyup.enter.native="handleQuery" />
|
|
|
|
|
|
</el-form-item>
|
2026-04-13 17:48:19 +08:00
|
|
|
|
<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
|
2026-04-02 16:49:07 +08:00
|
|
|
|
@keyup.enter.native="handleQuery" />
|
2026-04-01 10:44:51 +08:00
|
|
|
|
</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">
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<div v-for="row in contractList" :key="row.contractId" class="list-item"
|
2026-04-01 10:44:51 +08:00
|
|
|
|
style="padding: 10px; border-bottom: 2px solid #dddddd; cursor: pointer;"
|
2026-04-02 16:49:07 +08:00
|
|
|
|
:class="{ 'list-item-active': selectedRow === row }" @click="handleRowClick(row)">
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 合同名称和编号 -->
|
|
|
|
|
|
<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>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 供方和需方 -->
|
|
|
|
|
|
<div style="font-size: 12px; color: #909399; margin-bottom: 6px;">
|
|
|
|
|
|
<span>供方: {{ row.supplier }}</span>
|
|
|
|
|
|
<span style="margin-left: 20px;">需方: {{ row.customer }}</span>
|
|
|
|
|
|
</div>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 签订时间和交货日期 -->
|
|
|
|
|
|
<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>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 签订地点和状态 -->
|
|
|
|
|
|
<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>
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<!-- 操作按钮独占一行 -->
|
|
|
|
|
|
<div style="display: flex; gap: 10px; padding-top: 8px; border-top: 1px dashed #f0f0f0;">
|
2026-04-02 16:49:07 +08:00
|
|
|
|
<el-button size="mini" type="text" icon="el-icon-download" @click.stop="handleExport(row)">导出</el-button>
|
2026-04-01 10:44:51 +08:00
|
|
|
|
<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>
|
2026-04-13 17:48:19 +08:00
|
|
|
|
import { listOrder, updateOrder } from "@/api/crm/order";
|
2026-04-02 16:49:07 +08:00
|
|
|
|
import * as ExcelJS from 'exceljs';
|
|
|
|
|
|
import { saveAs } from 'file-saver';
|
2026-04-01 10:44:51 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: "ContractList",
|
2026-04-13 17:48:19 +08:00
|
|
|
|
dicts: ['wip_pack_saleman'],
|
2026-04-01 10:44:51 +08:00
|
|
|
|
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;
|
2026-04-13 17:48:19 +08:00
|
|
|
|
listOrder(this.queryParams).then(response => {
|
2026-04-01 10:44:51 +08:00
|
|
|
|
this.contractList = response.rows;
|
|
|
|
|
|
this.total = response.total;
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 状态变更 */
|
|
|
|
|
|
handleChangeStatus(row) {
|
2026-04-13 17:48:19 +08:00
|
|
|
|
updateOrder(row).then(response => {
|
2026-04-01 10:44:51 +08:00
|
|
|
|
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;
|
2026-04-02 16:49:07 +08:00
|
|
|
|
},
|
|
|
|
|
|
/** 导出合同 */
|
|
|
|
|
|
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字符串(productContent,json结构参考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;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 提取所有p标签内容
|
|
|
|
|
|
const pTagRegex = /<p[^>]*>([\s\S]*?)<\/p>/g;
|
|
|
|
|
|
let match;
|
|
|
|
|
|
const pContents = [];
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
while ((match = pTagRegex.exec(htmlContent)) !== null) {
|
|
|
|
|
|
let content = match[1];
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 移除其他HTML标签
|
|
|
|
|
|
content = content.replace(/<[^>]*>/g, '');
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 处理HTML实体
|
|
|
|
|
|
content = content.replace(/ /g, ' ');
|
|
|
|
|
|
content = content.replace(/</g, '<');
|
|
|
|
|
|
content = content.replace(/>/g, '>');
|
|
|
|
|
|
content = content.replace(/&/g, '&');
|
|
|
|
|
|
content = content.replace(/"/g, '"');
|
|
|
|
|
|
content = content.replace(/'/g, "'");
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 清理空格和换行
|
|
|
|
|
|
content = content.trim();
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-03 11:30:19 +08:00
|
|
|
|
// 如果不是以大写汉字数字开头,添加一个中文字符的缩进
|
2026-04-02 16:49:07 +08:00
|
|
|
|
if (content) {
|
2026-04-03 11:30:19 +08:00
|
|
|
|
// 检查是否以大写汉字数字开头(一、二、三、四、五、六、七、八、九、十)
|
|
|
|
|
|
const chineseNumberRegex = /^[一二三四五六七八九十]+、/;
|
|
|
|
|
|
if (!chineseNumberRegex.test(content)) {
|
|
|
|
|
|
content = ' ' + content; // 使用全角空格作为中文字符缩进
|
|
|
|
|
|
}
|
2026-04-02 16:49:07 +08:00
|
|
|
|
pContents.push(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 如果没有提取到p标签内容,尝试提取所有文本内容
|
|
|
|
|
|
if (pContents.length === 0) {
|
|
|
|
|
|
let textContent = htmlContent.replace(/<[^>]*>/g, '');
|
|
|
|
|
|
textContent = textContent.replace(/ /g, ' ').trim();
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-03 11:30:19 +08:00
|
|
|
|
// 如果不是以大写汉字数字开头,添加一个中文字符的缩进
|
2026-04-02 16:49:07 +08:00
|
|
|
|
if (textContent) {
|
2026-04-03 11:30:19 +08:00
|
|
|
|
// 检查是否以大写汉字数字开头(一、二、三、四、五、六、七、八、九、十)
|
|
|
|
|
|
const chineseNumberRegex = /^[一二三四五六七八九十]+、/;
|
|
|
|
|
|
if (!chineseNumberRegex.test(textContent)) {
|
|
|
|
|
|
textContent = ' ' + textContent; // 使用全角空格作为中文字符缩进
|
|
|
|
|
|
}
|
2026-04-02 16:49:07 +08:00
|
|
|
|
pContents.push(textContent);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 直接合并单元格并设置内容,避免合并已合并的单元格
|
|
|
|
|
|
if (pContents.length > 0) {
|
|
|
|
|
|
// 计算需要的行数
|
|
|
|
|
|
const contentLines = pContents.reduce((total, content) => {
|
|
|
|
|
|
return total + (content.match(/\n/g) || []).length + 1;
|
|
|
|
|
|
}, 0);
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 合并单元格
|
|
|
|
|
|
const endRow = currentRow + Math.ceil(contentLines / 2); // 估算需要的行数
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:H${endRow}`);
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 设置合并后单元格的内容和样式
|
|
|
|
|
|
const mergedContent = pContents.join('\n');
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = mergedContent;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'top', wrapText: true };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 调整合并后单元格的行高
|
|
|
|
|
|
const lineCount = (mergedContent.match(/\n/g) || []).length + 1;
|
|
|
|
|
|
const height = Math.max(60, lineCount * 15);
|
|
|
|
|
|
worksheet.getRow(currentRow).height = height;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 调整currentRow
|
|
|
|
|
|
currentRow = endRow + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置合同尾部信息,每一行的前四列与后四列分别合并
|
|
|
|
|
|
// 内容参考
|
|
|
|
|
|
|
|
|
|
|
|
// 设置合同尾部信息,每一行的前四列与后四列分别合并
|
|
|
|
|
|
// 内容参考ContractPreview.vue
|
|
|
|
|
|
if (currentRow > 0) {
|
|
|
|
|
|
// 空行
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 供方(甲方)信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `供方(甲方):${row.supplier || '嘉祥科伦普重工有限公司'}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).value = `需方(乙方):${row.customer || ''}`;
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 地址信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `地址:${row.supplierAddress || ''}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).value = `地址:${row.customerAddress || ''}`;
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 电话信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `电话:${row.supplierPhone || ''}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).value = `电话:${row.customerPhone || ''}`;
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 开户行信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `开户行:${row.supplierBank || ''}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).value = `开户行:${row.customerBank || ''}`;
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 账号信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `账号:${row.supplierAccount || ''}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
worksheet.mergeCells(`E${currentRow}:H${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).value = `账号:${row.customerAccount || ''}`;
|
|
|
|
|
|
worksheet.getCell(`E${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
|
|
|
|
|
currentRow++;
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
// 税号信息
|
|
|
|
|
|
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).value = `税号:${row.supplierTaxNo || ''}`;
|
|
|
|
|
|
worksheet.getCell(`A${currentRow}`).alignment = { horizontal: 'left', vertical: 'middle' };
|
2026-04-07 11:47:41 +08:00
|
|
|
|
|
2026-04-02 16:49:07 +08:00
|
|
|
|
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`);
|
|
|
|
|
|
},
|
2026-04-01 10:44:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</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;
|
|
|
|
|
|
}
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
.list-item {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
2026-04-02 16:49:07 +08:00
|
|
|
|
|
2026-04-01 10:44:51 +08:00
|
|
|
|
.list-item .el-button {
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|