feat(contract): 启用产品内容组件并优化合同相关功能
- 在合同页面启用ProductContent组件替代注释代码 - 优化ProductContent组件数值计算和空值处理 - 修改ContractList组件从productContent字段获取数据 - 在OrderDetail组件添加"写入合同"功能 - 优化ReceiveTable组件未收款金额计算逻辑
This commit is contained in:
@@ -42,6 +42,8 @@
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="editable">
|
||||
<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-delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
@@ -77,17 +79,17 @@
|
||||
<el-input v-model="form.material" placeholder="请输入材质" />
|
||||
</el-form-item>
|
||||
<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 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 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 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 label="表面处理" prop="surfaceTreatment">
|
||||
<el-input v-model="form.surfaceTreatment" placeholder="请输入表面处理" />
|
||||
@@ -129,7 +131,7 @@
|
||||
<script>
|
||||
import { listOrderItem, getOrderItem } from "@/api/crm/orderItem";
|
||||
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";
|
||||
|
||||
export default {
|
||||
@@ -186,41 +188,9 @@ export default {
|
||||
productType: [
|
||||
{ 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: [
|
||||
{ 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: {},
|
||||
@@ -234,6 +204,9 @@ export default {
|
||||
// if (newVal !== oldVal) {
|
||||
this.queryParams.orderId = newVal;
|
||||
this.getList();
|
||||
getOrder(this.orderId).then(response => {
|
||||
this.orderContent = response.data;
|
||||
});
|
||||
// }
|
||||
},
|
||||
immediate: true
|
||||
@@ -301,6 +274,127 @@ export default {
|
||||
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() {
|
||||
this.$refs["printer"].print();
|
||||
this.orderOpen = false;
|
||||
|
||||
@@ -162,7 +162,6 @@ export default {
|
||||
return this.receivableList.reduce((total, item) => total + parseFloat(item.amount), 0);
|
||||
},
|
||||
unreceivedAmount() {
|
||||
// return this.order ? this.order.totalAmount - this.order.receivedAmount : 0;
|
||||
return this.order ? this.order.orderAmount - this.receivedAmount : 0;
|
||||
},
|
||||
},
|
||||
@@ -232,14 +231,6 @@ export default {
|
||||
this.receivableList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
if (this.isFirst) {
|
||||
this.isFirst = false;
|
||||
return;
|
||||
}
|
||||
updateOrder({
|
||||
orderId: this.orderId,
|
||||
unpaidAmount: this.unreceivedAmount,
|
||||
})
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
|
||||
@@ -261,10 +261,10 @@ export default {
|
||||
// 1. 创建excel
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('产品销售合同');
|
||||
let orderItems = [];
|
||||
// 2. 查询合同详情
|
||||
const res = await listOrderItem({ orderId: row.orderId, pageNum: 1, pageSize: 1000 });
|
||||
orderItems = res.rows || [];
|
||||
// let orderItems = [];
|
||||
// // 2. 查询合同详情
|
||||
// const res = await listOrderItem({ orderId: row.orderId, pageNum: 1, pageSize: 1000 });
|
||||
// orderItems = res.rows || [];
|
||||
|
||||
// 2. 设置列宽
|
||||
worksheet.columns = [
|
||||
@@ -408,38 +408,46 @@ export default {
|
||||
totalAmountInWords: '零元整'
|
||||
};
|
||||
|
||||
if (orderItems) {
|
||||
if (row.productContent) {
|
||||
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
|
||||
};
|
||||
productData = JSON.parse(row.productContent);
|
||||
} catch (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}`;
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<h4 style="margin-bottom: 10px; color: #606266;">一、产品内容</h4>
|
||||
<OrderDetail :orderId="contract.orderId" :remark="contract.remark" readonly />
|
||||
<!-- <ProductContent v-model="contract.productContent" readonly /> -->
|
||||
<!-- <OrderDetail :orderId="contract.orderId" :remark="contract.remark" readonly /> -->
|
||||
<ProductContent v-model="contract.productContent" readonly />
|
||||
<!-- <div v-html="contract.productContent" style="border: 1px solid #e4e7ed; padding: 10px; border-radius: 4px;"></div> -->
|
||||
</div>
|
||||
|
||||
|
||||
@@ -182,13 +182,13 @@
|
||||
// 计算总数量
|
||||
totalQuantity() {
|
||||
return this.products.reduce((total, item) => {
|
||||
return total + (item.quantity || 0);
|
||||
return total + (parseFloat(item.quantity) || 0);
|
||||
}, 0);
|
||||
},
|
||||
// 计算总含税总额
|
||||
totalTaxTotal() {
|
||||
return this.products.reduce((total, item) => {
|
||||
return total + (item.taxTotal || 0);
|
||||
return total + (parseFloat(item.taxTotal) || 0);
|
||||
}, 0);
|
||||
},
|
||||
// 计算大写金额
|
||||
@@ -283,6 +283,12 @@
|
||||
// 监听value变化,更新内部数据
|
||||
value: {
|
||||
handler(newValue) {
|
||||
if (!newValue) {
|
||||
this.products = [{}];
|
||||
this.remark = '';
|
||||
this.productName = '';
|
||||
return;
|
||||
}
|
||||
this.parseContent(newValue);
|
||||
},
|
||||
immediate: true
|
||||
|
||||
@@ -81,9 +81,9 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- <el-form-item label="产品内容">
|
||||
<el-form-item label="产品内容">
|
||||
<ProductContent v-model="form.productContent" :readonly="false" />
|
||||
</el-form-item> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="合同内容">
|
||||
<ContractTemplateManager @select="handleTemplateSelect" />
|
||||
<editor v-model="form.contractContent" :min-height="192" />
|
||||
@@ -326,8 +326,6 @@ export default {
|
||||
console.log(customer);
|
||||
},
|
||||
|
||||
|
||||
|
||||
/** 处理合同模板选择 */
|
||||
handleTemplateSelect(template) {
|
||||
this.form.contractContent = template.dictValue;
|
||||
|
||||
Reference in New Issue
Block a user