feat(金额显示): 统一金额单位为万元并优化产品内容处理
refactor(产品内容): 提取产品内容处理逻辑到独立工具类 style(合同表单): 调整技术附件和商务附件顺序
This commit is contained in:
@@ -18,9 +18,10 @@
|
||||
<div class="table-cell">规格(mm)</div>
|
||||
<div class="table-cell">材质</div>
|
||||
<div class="table-cell">数量(吨)</div>
|
||||
<div class="table-cell">含税单价(元/吨)</div>
|
||||
<div class="table-cell" v-if="false">含税单价(元/吨)</div>
|
||||
<div class="table-cell">不含税单价(元/吨)</div>
|
||||
<div class="table-cell">含税总额(元)</div>
|
||||
<div class="table-cell">不含税总额(元)</div>
|
||||
<div class="table-cell">
|
||||
备注
|
||||
<el-button
|
||||
@@ -82,7 +83,7 @@
|
||||
@change="calculateTotals"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-cell">
|
||||
<div class="table-cell" v-if="false">
|
||||
<el-input
|
||||
v-model.number="item.taxPrice"
|
||||
placeholder="请输入含税单价"
|
||||
@@ -111,6 +112,15 @@
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-cell">
|
||||
<el-input
|
||||
:value="((item.taxTotal || 0) / 1.13).toFixed(2)"
|
||||
placeholder="不含税总额"
|
||||
type="number"
|
||||
:readonly="true"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-cell">
|
||||
<el-input
|
||||
v-model="item.remark"
|
||||
@@ -125,9 +135,10 @@
|
||||
<div class="table-row table-total-row">
|
||||
<div class="table-cell" colspan="3">合计</div>
|
||||
<div class="table-cell">{{ totalQuantity }}</div>
|
||||
<div class="table-cell"></div>
|
||||
<div class="table-cell" v-if="false"></div>
|
||||
<div class="table-cell"></div>
|
||||
<div class="table-cell">{{ totalTaxTotal }}</div>
|
||||
<div class="table-cell">{{ ((totalTaxTotal || 0) / 1.13).toFixed(2) }}</div>
|
||||
<div class="table-cell"></div>
|
||||
</div>
|
||||
|
||||
@@ -157,164 +168,93 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ProductContent',
|
||||
props: {
|
||||
value: {
|
||||
// 一个json字符串
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
import {
|
||||
parseProductContent,
|
||||
calculateTotalQuantity,
|
||||
calculateTotalTaxTotal,
|
||||
calculateProductTaxTotal,
|
||||
convertToChinese
|
||||
} from '@/utils/productContent';
|
||||
|
||||
export default {
|
||||
name: 'ProductContent',
|
||||
props: {
|
||||
value: {
|
||||
// 一个json字符串
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
products: [],
|
||||
remark: '',
|
||||
productName: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算总数量
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
products: [],
|
||||
remark: '',
|
||||
productName: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算总数量
|
||||
totalQuantity() {
|
||||
return this.products.reduce((total, item) => {
|
||||
return total + (parseFloat(item.quantity) || 0);
|
||||
}, 0);
|
||||
return calculateTotalQuantity(this.products);
|
||||
},
|
||||
// 计算总含税总额
|
||||
totalTaxTotal() {
|
||||
return this.products.reduce((total, item) => {
|
||||
return total + (parseFloat(item.taxTotal) || 0);
|
||||
}, 0);
|
||||
return calculateTotalTaxTotal(this.products);
|
||||
},
|
||||
// 计算大写金额
|
||||
totalAmountInWords() {
|
||||
const amount = this.totalTaxTotal;
|
||||
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;
|
||||
},
|
||||
// 生成JSON字符串
|
||||
jsonContent() {
|
||||
// 只存储非空行
|
||||
const nonEmptyProducts = this.products.filter(item => {
|
||||
return item.spec || item.material || item.quantity || item.taxPrice || item.noTaxPrice || item.remark;
|
||||
});
|
||||
const data = {
|
||||
products: nonEmptyProducts,
|
||||
productName: this.productName,
|
||||
remark: this.remark,
|
||||
totalQuantity: this.totalQuantity,
|
||||
totalTaxTotal: this.totalTaxTotal,
|
||||
totalAmountInWords: this.totalAmountInWords
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
return convertToChinese(this.totalTaxTotal);
|
||||
},
|
||||
// 生成JSON字符串
|
||||
jsonContent() {
|
||||
// 只存储非空行
|
||||
const nonEmptyProducts = this.products.filter(item => {
|
||||
return item.spec || item.material || item.quantity || item.taxPrice || item.noTaxPrice || item.remark;
|
||||
});
|
||||
const data = {
|
||||
products: nonEmptyProducts,
|
||||
productName: this.productName,
|
||||
remark: this.remark,
|
||||
totalQuantity: this.totalQuantity,
|
||||
totalTaxTotal: this.totalTaxTotal,
|
||||
totalAmountInWords: this.totalAmountInWords
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
},
|
||||
watch: {
|
||||
// 监听jsonContent变化,触发update事件
|
||||
jsonContent: {
|
||||
handler(newValue) {
|
||||
this.$emit('input', newValue);
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
watch: {
|
||||
// 监听jsonContent变化,触发update事件
|
||||
jsonContent: {
|
||||
handler(newValue) {
|
||||
this.$emit('input', newValue);
|
||||
},
|
||||
// 监听value变化,更新内部数据
|
||||
value: {
|
||||
handler(newValue) {
|
||||
if (!newValue) {
|
||||
this.products = [{}];
|
||||
this.remark = '';
|
||||
this.productName = '';
|
||||
return;
|
||||
}
|
||||
this.parseContent(newValue);
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
deep: true
|
||||
},
|
||||
methods: {
|
||||
// 解析content字符串
|
||||
// 监听value变化,更新内部数据
|
||||
value: {
|
||||
handler(newValue) {
|
||||
if (!newValue) {
|
||||
this.products = [{}];
|
||||
this.remark = '';
|
||||
this.productName = '';
|
||||
return;
|
||||
}
|
||||
this.parseContent(newValue);
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 解析content字符串
|
||||
parseContent(content) {
|
||||
try {
|
||||
if (!content) {
|
||||
return {
|
||||
productName: '',
|
||||
products: [{
|
||||
_isEmpty: true,
|
||||
spec: '',
|
||||
material: '',
|
||||
quantity: '',
|
||||
taxPrice: '',
|
||||
noTaxPrice: '',
|
||||
remark: ''
|
||||
}],
|
||||
remark: ''
|
||||
}
|
||||
}
|
||||
const data = JSON.parse(content);
|
||||
this.products = data.products || [{}];
|
||||
const data = parseProductContent(content);
|
||||
this.products = data.products.length > 0 ? data.products : [{}];
|
||||
this.remark = data.remark || '';
|
||||
this.productName = data.productName || '';
|
||||
} catch (error) {
|
||||
@@ -326,32 +266,28 @@
|
||||
// 计算金额
|
||||
calculateTotals() {
|
||||
this.products.forEach(item => {
|
||||
if (item.quantity && item.taxPrice) {
|
||||
item.taxTotal = item.quantity * item.taxPrice;
|
||||
} else {
|
||||
item.taxTotal = 0;
|
||||
}
|
||||
item.taxTotal = calculateProductTaxTotal(item);
|
||||
});
|
||||
},
|
||||
// 添加产品行
|
||||
addProduct() {
|
||||
this.products.push({});
|
||||
},
|
||||
// 删除产品行
|
||||
removeProduct(index) {
|
||||
if (this.products.length > 1) {
|
||||
this.products.splice(index, 1);
|
||||
this.calculateTotals();
|
||||
}
|
||||
}
|
||||
// 添加产品行
|
||||
addProduct() {
|
||||
this.products.push({});
|
||||
},
|
||||
mounted() {
|
||||
// 确保至少有一个空行
|
||||
if (this.products.length === 0) {
|
||||
this.products.push({});
|
||||
// 删除产品行
|
||||
removeProduct(index) {
|
||||
if (this.products.length > 1) {
|
||||
this.products.splice(index, 1);
|
||||
this.calculateTotals();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 确保至少有一个空行
|
||||
if (this.products.length === 0) {
|
||||
this.products.push({});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -374,7 +310,7 @@
|
||||
|
||||
.table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 80px 150px 120px 100px 140px 150px 120px 1fr;
|
||||
grid-template-columns: 80px 150px 120px 100px 160px 120px 140px 1fr;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
@@ -414,6 +350,9 @@
|
||||
.table-cell[colspan="8"] {
|
||||
grid-column: span 8;
|
||||
}
|
||||
.table-cell[colspan="9"] {
|
||||
grid-column: span 9;
|
||||
}
|
||||
|
||||
.serial-number {
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user