feat(contract): 启用产品内容组件并优化合同相关功能

- 在合同页面启用ProductContent组件替代注释代码
- 优化ProductContent组件数值计算和空值处理
- 修改ContractList组件从productContent字段获取数据
- 在OrderDetail组件添加"写入合同"功能
- 优化ReceiveTable组件未收款金额计算逻辑
This commit is contained in:
2026-04-23 11:47:30 +08:00
parent 8897a2ad9f
commit 84c26a2990
6 changed files with 186 additions and 89 deletions

View File

@@ -42,6 +42,8 @@
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="editable"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="editable">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-plus"
@click="handleWriteToContract(scope.row)">写入合同</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
@@ -77,17 +79,17 @@
<el-input v-model="form.material" placeholder="请输入材质" /> <el-input v-model="form.material" placeholder="请输入材质" />
</el-form-item> </el-form-item>
<el-form-item label="重量" prop="weight"> <el-form-item label="重量" prop="weight">
<el-input v-model="form.weight" placeholder="请输入重量" /> <el-input type="number" v-model="form.weight" placeholder="请输入重量" />
</el-form-item> </el-form-item>
<el-form-item label="含税单价(元/吨)" prop="contractPrice"> <el-form-item label="含税单价(元/吨)" prop="contractPrice">
<el-input v-model="form.contractPrice" placeholder="请输入含税单价" /> <el-input type="number" v-model="form.contractPrice" placeholder="请输入含税单价" />
</el-form-item> </el-form-item>
<el-form-item label="无税单价(元/吨)" prop="itemAmount"> <el-form-item label="无税单价(元/吨)" prop="itemAmount">
<el-input v-model="form.itemAmount" placeholder="请输入无税单价" /> <el-input type="number" v-model="form.itemAmount" placeholder="请输入无税单价" />
</el-form-item> </el-form-item>
<el-form-item label="卷数" prop="productNum"> <el-form-item label="卷数" prop="productNum">
<el-input v-model="form.productNum" placeholder="请输入卷数" /> <el-input type="number" v-model="form.productNum" placeholder="请输入卷数" />
</el-form-item> </el-form-item>
<el-form-item label="表面处理" prop="surfaceTreatment"> <el-form-item label="表面处理" prop="surfaceTreatment">
<el-input v-model="form.surfaceTreatment" placeholder="请输入表面处理" /> <el-input v-model="form.surfaceTreatment" placeholder="请输入表面处理" />
@@ -129,7 +131,7 @@
<script> <script>
import { listOrderItem, getOrderItem } from "@/api/crm/orderItem"; import { listOrderItem, getOrderItem } from "@/api/crm/orderItem";
import { actions, ORDER_ACTIONS } from "../js/actions"; import { actions, ORDER_ACTIONS } from "../js/actions";
import { getOrder } from "@/api/crm/order"; import { getOrder, updateOrder } from "@/api/crm/order";
import OrderPrinter from "./OrderPrinter.vue"; import OrderPrinter from "./OrderPrinter.vue";
export default { export default {
@@ -186,41 +188,9 @@ export default {
productType: [ productType: [
{ required: true, message: "请输入产品类型", trigger: "blur" } { required: true, message: "请输入产品类型", trigger: "blur" }
], ],
// productNum: [
// { required: true, message: "请输入产品数量", trigger: "blur" }
// ],
// rawMaterialSpec: [
// { required: true, message: "请输入原料规格", trigger: "blur" }
// ],
// finishedProductSpec: [
// { required: true, message: "请输入成品规格", trigger: "blur" }
// ],
// 重量和合同定价不能为空,且必须是数字,最多两位小数
// weight: [
// { required: true, message: "请输入重量", trigger: "blur" },
// // 必须是数字,最多两位小数,使用自定义的校验规则
// {
// validator: (rule, value, callback) => {
// if (!/^\d+(\.\d{1,2,3})?$/.test(value)) {
// callback(new Error("请输入最多三位小数的数字"));
// } else {
// callback();
// }
// }, trigger: "change"
// }
// ],
contractPrice: [ contractPrice: [
{ required: true, message: "请输入合同定价", trigger: "blur" }, { required: true, message: "请输入合同定价", trigger: "blur" },
// 必须是数字,最多两位小数,使用自定义的校验规则 // 必须是数字,最多两位小数,使用自定义的校验规则
{
validator: (rule, value, callback) => {
if (!/^\d+(\.\d{1,2,3})?$/.test(value)) {
callback(new Error("请输入最多三位小数的数字"));
} else {
callback();
}
}, trigger: "change"
}
], ],
}, },
orderContent: {}, orderContent: {},
@@ -234,6 +204,9 @@ export default {
// if (newVal !== oldVal) { // if (newVal !== oldVal) {
this.queryParams.orderId = newVal; this.queryParams.orderId = newVal;
this.getList(); this.getList();
getOrder(this.orderId).then(response => {
this.orderContent = response.data;
});
// } // }
}, },
immediate: true immediate: true
@@ -301,6 +274,127 @@ export default {
this.orderLoading = false; this.orderLoading = false;
}); });
}, },
// 计算大写金额
totalAmountInWords(amountStr) {
const amount = parseFloat(amountStr);
if (isNaN(amount)) return '无效的金额格式';
if (amount === 0) return '零元整';
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
const units = ['', '拾', '佰', '仟'];
const bigUnits = ['', '万', '亿'];
let integerPart = Math.floor(amount);
let decimalPart = Math.round((amount - integerPart) * 100);
let result = '';
// 处理整数部分
if (integerPart > 0) {
let unitIndex = 0;
let bigUnitIndex = 0;
while (integerPart > 0) {
let section = integerPart % 10000;
if (section > 0) {
let sectionResult = '';
let temp = section;
let unitIndexInSection = 0;
while (temp > 0) {
let digit = temp % 10;
if (digit > 0) {
sectionResult = digits[digit] + units[unitIndexInSection] + sectionResult;
} else {
// 避免连续的零
if (sectionResult && !sectionResult.startsWith('零')) {
sectionResult = '零' + sectionResult;
}
}
temp = Math.floor(temp / 10);
unitIndexInSection++;
}
result = sectionResult + bigUnits[bigUnitIndex] + result;
}
integerPart = Math.floor(integerPart / 10000);
bigUnitIndex++;
}
result += '元';
}
// 处理小数部分
if (decimalPart === 0) {
result += '整';
} else {
if (decimalPart >= 10) {
result += digits[Math.floor(decimalPart / 10)] + '角';
}
if (decimalPart % 10 > 0) {
result += digits[decimalPart % 10] + '分';
}
}
return result;
},
async handleWriteToContract(row) {
const { data } = await getOrder(this.orderId);
let productContent = data.productContent;
try {
if (!productContent) {
productContent = {
productName: '',
products: [{
spec: row.finishedProductSpec || '',
material: row.material || '',
quantity: parseFloat(row.weight) || 0,
taxPrice: parseFloat(row.contractPrice) || 0,
noTaxPrice: parseFloat(row.itemAmount) || 0,
taxTotal: parseFloat(row.contractPrice) || 0 * (parseFloat(row.weight) || 0),
remark: row.remark || ''
}],
remark: '',
totalQuantity: parseFloat(row.weight) || 0,
totalTaxTotal: parseFloat(row.contractPrice) || 0 * (parseFloat(row.weight) || 0),
totalAmountInWords: this.totalAmountInWords(parseFloat(row.contractPrice) || 0 * (parseFloat(row.weight) || 0)).toString()
};
} else {
productContent = JSON.parse(productContent);
productContent.products.push({
spec: row.finishedProductSpec || '',
material: row.material || '',
quantity: parseFloat(row.weight) || 0,
taxPrice: parseFloat(row.contractPrice) || 0,
noTaxPrice: parseFloat(row.itemAmount) || 0,
taxTotal: parseFloat(row.contractPrice) || 0 * (parseFloat(row.weight) || 0),
remark: row.remark || ''
});
// 将现有的全部加和计算
productContent.totalQuantity = productContent.products.reduce((acc, cur) => acc + (parseFloat(cur.quantity) || 0), 0);
console.log(productContent.totalQuantity);
productContent.totalTaxTotal = productContent.products.reduce((acc, cur) => acc + (parseFloat(cur.taxTotal) || 0), 0);
productContent.totalAmountInWords = this.totalAmountInWords(productContent.totalTaxTotal).toString();
}
console.log(productContent);
const jsonContent = JSON.stringify(productContent, null, 2);
console.log(jsonContent);
updateOrder({
...this.orderContent,
productContent: jsonContent
}).then(response => {
this.$modal.msgSuccess("写入成功,请刷新合同内容查看");
});
} catch (error) {
console.error(error);
this.$modal.msgError("产品内容格式错误");
return;
}
console.log(productContent.products);
},
printOrder() { printOrder() {
this.$refs["printer"].print(); this.$refs["printer"].print();
this.orderOpen = false; this.orderOpen = false;

View File

@@ -162,7 +162,6 @@ export default {
return this.receivableList.reduce((total, item) => total + parseFloat(item.amount), 0); return this.receivableList.reduce((total, item) => total + parseFloat(item.amount), 0);
}, },
unreceivedAmount() { unreceivedAmount() {
// return this.order ? this.order.totalAmount - this.order.receivedAmount : 0;
return this.order ? this.order.orderAmount - this.receivedAmount : 0; return this.order ? this.order.orderAmount - this.receivedAmount : 0;
}, },
}, },
@@ -232,14 +231,6 @@ export default {
this.receivableList = response.rows; this.receivableList = response.rows;
this.total = response.total; this.total = response.total;
this.loading = false; this.loading = false;
if (this.isFirst) {
this.isFirst = false;
return;
}
updateOrder({
orderId: this.orderId,
unpaidAmount: this.unreceivedAmount,
})
}); });
}, },
// 取消按钮 // 取消按钮

View File

@@ -261,10 +261,10 @@ export default {
// 1. 创建excel // 1. 创建excel
const workbook = new ExcelJS.Workbook(); const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('产品销售合同'); const worksheet = workbook.addWorksheet('产品销售合同');
let orderItems = []; // let orderItems = [];
// 2. 查询合同详情 // // 2. 查询合同详情
const res = await listOrderItem({ orderId: row.orderId, pageNum: 1, pageSize: 1000 }); // const res = await listOrderItem({ orderId: row.orderId, pageNum: 1, pageSize: 1000 });
orderItems = res.rows || []; // orderItems = res.rows || [];
// 2. 设置列宽 // 2. 设置列宽
worksheet.columns = [ worksheet.columns = [
@@ -408,38 +408,46 @@ export default {
totalAmountInWords: '零元整' totalAmountInWords: '零元整'
}; };
if (orderItems) { if (row.productContent) {
try { try {
// 改为从orderItems中获取产品内容 productData = JSON.parse(row.productContent);
const productName = orderItems[0].productType || '冷硬钢卷';
const remark = row.remark || '';
const products = orderItems.map(item => ({
spec: item.finishedProductSpec || '',
material: item.material || '',
quantity: parseFloat(item.weight || 0),
taxPrice: parseFloat(item.contractPrice || 0),
noTaxPrice: parseFloat(item.itemAmount || 0),
taxTotal: parseFloat(item.contractPrice) * parseFloat(item.weight || 0),
remark: item.remark || ''
}));
const totalQuantity = products.reduce((acc, product) => acc + parseFloat(product.quantity || 0), 0);
const totalTaxTotal = products.reduce((acc, product) => acc + parseFloat(product.taxTotal || 0), 0);
const totalAmountInWords = this.convertToChinese(totalTaxTotal);
productData = {
products,
productName,
remark,
totalQuantity,
totalTaxTotal,
totalAmountInWords
};
} catch (error) { } catch (error) {
console.error('解析产品内容失败:', error); console.error('解析产品内容失败:', error);
} }
} }
// if (orderItems) {
// try {
// // 改为从orderItems中获取产品内容
// const productName = orderItems[0].productType || '冷硬钢卷';
// const remark = row.remark || '';
// const products = orderItems.map(item => ({
// spec: item.finishedProductSpec || '',
// material: item.material || '',
// quantity: parseFloat(item.weight || 0),
// taxPrice: parseFloat(item.contractPrice || 0),
// noTaxPrice: parseFloat(item.itemAmount || 0),
// taxTotal: parseFloat(item.contractPrice) * parseFloat(item.weight || 0),
// remark: item.remark || ''
// }));
// const totalQuantity = products.reduce((acc, product) => acc + parseFloat(product.quantity || 0), 0);
// const totalTaxTotal = products.reduce((acc, product) => acc + parseFloat(product.taxTotal || 0), 0);
// const totalAmountInWords = this.convertToChinese(totalTaxTotal);
// productData = {
// products,
// productName,
// remark,
// totalQuantity,
// totalTaxTotal,
// totalAmountInWords
// };
// } catch (error) {
// console.error('解析产品内容失败:', error);
// }
// }
// 更新产品名称 // 更新产品名称
worksheet.getCell('A6').value = `产品名称:${productData.productName}`; worksheet.getCell('A6').value = `产品名称:${productData.productName}`;

View File

@@ -27,8 +27,8 @@
<div style="margin-top: 20px;"> <div style="margin-top: 20px;">
<h4 style="margin-bottom: 10px; color: #606266;">产品内容</h4> <h4 style="margin-bottom: 10px; color: #606266;">产品内容</h4>
<OrderDetail :orderId="contract.orderId" :remark="contract.remark" readonly /> <!-- <OrderDetail :orderId="contract.orderId" :remark="contract.remark" readonly /> -->
<!-- <ProductContent v-model="contract.productContent" readonly /> --> <ProductContent v-model="contract.productContent" readonly />
<!-- <div v-html="contract.productContent" style="border: 1px solid #e4e7ed; padding: 10px; border-radius: 4px;"></div> --> <!-- <div v-html="contract.productContent" style="border: 1px solid #e4e7ed; padding: 10px; border-radius: 4px;"></div> -->
</div> </div>

View File

@@ -182,13 +182,13 @@
// 计算总数量 // 计算总数量
totalQuantity() { totalQuantity() {
return this.products.reduce((total, item) => { return this.products.reduce((total, item) => {
return total + (item.quantity || 0); return total + (parseFloat(item.quantity) || 0);
}, 0); }, 0);
}, },
// 计算总含税总额 // 计算总含税总额
totalTaxTotal() { totalTaxTotal() {
return this.products.reduce((total, item) => { return this.products.reduce((total, item) => {
return total + (item.taxTotal || 0); return total + (parseFloat(item.taxTotal) || 0);
}, 0); }, 0);
}, },
// 计算大写金额 // 计算大写金额
@@ -283,6 +283,12 @@
// 监听value变化更新内部数据 // 监听value变化更新内部数据
value: { value: {
handler(newValue) { handler(newValue) {
if (!newValue) {
this.products = [{}];
this.remark = '';
this.productName = '';
return;
}
this.parseContent(newValue); this.parseContent(newValue);
}, },
immediate: true immediate: true

View File

@@ -81,9 +81,9 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- <el-form-item label="产品内容"> <el-form-item label="产品内容">
<ProductContent v-model="form.productContent" :readonly="false" /> <ProductContent v-model="form.productContent" :readonly="false" />
</el-form-item> --> </el-form-item>
<el-form-item label="合同内容"> <el-form-item label="合同内容">
<ContractTemplateManager @select="handleTemplateSelect" /> <ContractTemplateManager @select="handleTemplateSelect" />
<editor v-model="form.contractContent" :min-height="192" /> <editor v-model="form.contractContent" :min-height="192" />
@@ -326,8 +326,6 @@ export default {
console.log(customer); console.log(customer);
}, },
/** 处理合同模板选择 */ /** 处理合同模板选择 */
handleTemplateSelect(template) { handleTemplateSelect(template) {
this.form.contractContent = template.dictValue; this.form.contractContent = template.dictValue;