You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1216 lines
37 KiB
1216 lines
37 KiB
<template> |
|
<div class="container"> |
|
<!-- 面包屑导航 --> |
|
<basic-crumb></basic-crumb> |
|
|
|
<!-- 页面标题 --> |
|
<div class="page-title">审批详情</div> |
|
|
|
<!-- 加载中 --> |
|
<!-- <el-loading v-if="loading" fullscreen text="加载中..."></el-loading> --> |
|
<!-- 订单信息卡片 --> |
|
<div class="boxcard" v-if="!loading && orderData"> |
|
<div class="card-header">订单信息</div> |
|
<el-row :gutter="40" class="order-info-container"> |
|
<!-- 左侧订单信息:el-col --> |
|
<div style="padding:20px;width:100%"> |
|
<el-col :span="12"> |
|
<div class="info-item"> |
|
<span class="label">订单编号:</span> |
|
<span class="value">{{ orderData.code || "-" }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">订单名称:</span> |
|
<span class="value">{{ orderData.name || "-" }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">客户公司名称:</span> |
|
<span class="value"> |
|
<span |
|
@click="openCustomerDetail" |
|
style="color: #2244f5;cursor: pointer;" |
|
>{{ orderData.customerName || "-" }}</span |
|
> |
|
</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">货币单位:</span> |
|
<span class="value">{{ getMonetaryUnitText(orderData.monetaryUnit) }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">交货时间:</span> |
|
<span class="value">{{ orderData.deliveryDate || "-" }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">下单时间:</span> |
|
<span class="value">{{ orderData.createTime || "-" }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">提交人:</span> |
|
<span class="value">{{ orderData.createUserName || "" }}</span> |
|
</div> |
|
</el-col> |
|
<!-- 右侧金额信息:el-col --> |
|
<el-col :span="12"> |
|
<div class="info-item"> |
|
<span class="label">公司指导价:</span> |
|
<span class="value">{{ formatNumber(orderData.standardPrice) }}<span style="margin-left: 4px;"> |
|
元 |
|
</span></span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">销售价:</span> |
|
<span class="value">{{ formatNumber(orderData.sellingPrice) }}<span style="margin-left: 4px;"> |
|
元 |
|
</span></span> |
|
</div> |
|
<!-- 分成金额 --> |
|
<div class="info-item formula"> |
|
<span class="label">分成金额:</span> |
|
<span class="value"> |
|
<span class="value">{{ formatNumber(orderData.splitPrice) }}<span style="margin-left: 4px;"> |
|
元 |
|
</span></span> |
|
</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">申请优惠金额:</span> |
|
<span class="value"> |
|
<el-input |
|
v-model="orderData.discountAmount" |
|
:disabled="isDisabled" |
|
style="width: 14vw" |
|
placeholder="请输入" |
|
@focus="handleAmountFocus('discountAmount')" |
|
@blur="formatAmountBlur('discountAmount')" |
|
/><span style="margin-left: 4px;"> |
|
元 |
|
</span> |
|
</span> |
|
</div> |
|
<!-- 提成金额 --> |
|
<div class="info-item"> |
|
<span class="label">提成金额:</span> |
|
<span class="value"> |
|
<el-input |
|
v-model="orderData.commissionPrice" |
|
disabled |
|
style="width: 14vw" |
|
placeholder="请输入" |
|
@blur="formatInput('commissionRate')" |
|
|
|
/> |
|
<span style="margin-left: 4px;"> |
|
元 |
|
</span> |
|
</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="label">提成比例:</span> |
|
<span class="value"> |
|
<el-input |
|
v-model="orderData.commissionRate" |
|
placeholder="请输入" |
|
style="width: 14vw" |
|
:disabled="isDisabled" |
|
@blur="formatRateBlur" |
|
@input="calcCommissionPrice" |
|
/> |
|
<span style="margin-left: 10px">%</span> |
|
</span> |
|
</div> |
|
<div class="info-item" v-if="orderData.monetaryUnit !=='CNY'"> |
|
<!-- 中国的话 不存在 --> |
|
<!-- {{orderData.monetaryUnit}} --> |
|
<span class="label">优惠汇率:</span> |
|
<span class="value"> |
|
<el-input |
|
v-model="orderData.exchangeRate" |
|
:disabled="isDisabled" |
|
placeholder="请输入" |
|
style="width: 10vw" |
|
@blur="formatExchangeRateBlur" |
|
@focus="handleExchangeRateFocus" |
|
/> |
|
</span> |
|
</div> |
|
</el-col> |
|
</div> |
|
</el-row> |
|
<!-- 其他费用卡片 --> |
|
<div class="other-fee-card"> |
|
<div class="card-header">其他费用</div> |
|
<div class="other-fee-container"> |
|
<!-- 费用项列表 --> |
|
<div |
|
class="fee-row" |
|
v-for="(fee, index) in orderData.extraChargeList" |
|
:key="index" |
|
> |
|
<div class="fee-item"> |
|
<span class="label">费用类别:</span> |
|
<el-input |
|
v-model="fee.type" |
|
:placeholder="isDisabled ? fee.type : '请输入费用的类目名称'" |
|
style="width: 17.8vw" |
|
:disabled="isDisabled" |
|
/> |
|
</div> |
|
<div class="fee-item"> |
|
<span class="label">费用金额:</span> |
|
<el-input |
|
v-model="fee.price" |
|
:placeholder="isDisabled ? fee.price : '请输入费用金额'" |
|
style="width: 17.8vw" |
|
|
|
:disabled="isDisabled" |
|
@focus="handleFeePriceFocus(index)" |
|
@blur="handleFeePriceBlur(index)" |
|
/> |
|
<!-- 按钮组(仅最后一行显示) --> |
|
<div |
|
class="btn-group" |
|
> |
|
<template v-if="index === orderData.extraChargeList.length - 1 && !isDisabled"> |
|
<el-button |
|
type="success" |
|
icon="el-icon-plus" |
|
circle |
|
@click="addFeeItem" |
|
|
|
/> |
|
<el-button |
|
type="danger" |
|
icon="el-icon-minus" |
|
circle |
|
@click="removeFeeItem" |
|
:disabled="orderData.extraChargeList.length <= 1" |
|
/> |
|
</template> |
|
|
|
</div> |
|
</div> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<!-- 项目信息卡片 --> |
|
<div |
|
class="systemBox" |
|
v-if=" |
|
!loading && |
|
orderData && |
|
orderData.projectList && |
|
orderData.projectList.length > 0 |
|
" |
|
> |
|
<div class="card-header">项目信息</div> |
|
<div class="system-info-container"> |
|
<!-- 项目列表 --> |
|
<div |
|
class="system-item" |
|
v-for="(project, index) in orderData.projectList" |
|
:key="index" |
|
:class="{ expanded: expandProjectIndex === index }" |
|
> |
|
<div class="system-header" @click="toggleProject(index)"> |
|
<div> |
|
<!-- {{project}} --> |
|
<!-- <span class="system-code">{{ project.code }}</span> --> |
|
<span class="system-name">{{ project.name }}</span> |
|
<span class="system-num">{{ project.num }}</span> |
|
|
|
<span class="system-name">{{ project.unit }}</span> |
|
|
|
<span></span> |
|
</div> |
|
<div> |
|
<el-link type="primary" style="margin-left: 10px;color: #3A84FF;"> |
|
{{ expandProjectIndex === index ? "收起" : "展开" }} |
|
</el-link> |
|
</div> |
|
</div> |
|
|
|
<!-- 产品表格(展开时显示) --> |
|
<div v-if="expandProjectIndex === index"> |
|
<el-table |
|
:data="project.productList" |
|
style="width: 100%; margin-top: 10px" |
|
border |
|
max-height="490px" |
|
:header-cell-style="{ 'text-align': 'center' }" |
|
:cell-style="{ 'text-align': 'center' }" |
|
> |
|
<el-table-column prop="code" label="产品编号" /> |
|
<el-table-column prop="name" label="产品名称" /> |
|
<el-table-column prop="spec" label="产品规格" /> |
|
<el-table-column prop="num" label="产品数量" /> |
|
<el-table-column prop="unit" label="单位" width="100"/> |
|
<el-table-column label="公司指导价"> |
|
<template slot-scope="scope"> |
|
{{ formatNumber(scope.row.standardPrice) }} |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="销售价"> |
|
<template slot-scope="scope"> |
|
{{ formatNumber(scope.row.sellingPrice) }} |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="分成金额"> |
|
<template slot-scope="scope"> |
|
{{ formatNumber(scope.row.splitPrice) }} |
|
</template> |
|
</el-table-column> |
|
</el-table> |
|
<div class="rowTotal"> |
|
<div class="rowleft"> |
|
合计条数:{{project.productList.length}} |
|
</div> |
|
<div class="rowright"> |
|
<div class="item"> |
|
公司指导价合计: {{ formatNumber(project.standardPrice) }} |
|
</div> |
|
<div class="item"> |
|
销售价合计: {{ formatNumber(project.sellingPrice) }} |
|
</div> |
|
<div class="item"> |
|
分成金额合计: {{ formatNumber(project.splitPrice) }} |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<!-- 审批操作栏 --> |
|
<div class="approval-action-bar" v-if="!loading && orderData"> |
|
<div v-if="isDisabled" class="approval-action-box"> |
|
<span style="color:#ffff;font-size: 1.458vw;">审批意见: </span> |
|
<el-input |
|
v-model="orderData.approveRemarks" |
|
disabled |
|
style="width: 70vw; margin-right: 20px" |
|
/> |
|
</div> |
|
<template v-if="!isDisabled"> |
|
<el-input |
|
v-model="orderData.approveRemarks" |
|
placeholder="请输入审批意见" |
|
style="width: 70vw; margin-right: 20px" |
|
/> |
|
<el-button |
|
type="danger" |
|
style="margin-right: 10px" |
|
@click="handleApproval('reject')" |
|
v-if="!isDisabled" |
|
class="el-button--big" |
|
>拒绝</el-button |
|
> |
|
<el-button type="success" @click="handleApproval('resolve')" v-if="!isDisabled" class="el-button--big">通过</el-button> |
|
</template> |
|
|
|
</div> |
|
<!-- 客户详情弹窗 --> |
|
|
|
<el-dialog |
|
title="客户详情" |
|
:visible.sync="customerDialogVisible" |
|
width="38vw" |
|
append-to-body |
|
:modal="false" |
|
class="customer-dialog" |
|
> |
|
<div class="customer-detail"> |
|
<div class="detail-item"> |
|
<span class="detail-label">客户公司名称:</span> |
|
<span class="detail-value">{{ customerDetail.companyName || "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">国家:</span> |
|
<span class="detail-value">{{ customerDetail.countryValue || "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">客户联系人1:</span> |
|
|
|
<span class="detail-value">{{ customerDetail.contactMain ? customerDetail.contactMain.name : "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">联系人手机号:</span> |
|
<span class="detail-value">{{ customerDetail.contactMain ? customerDetail.contactMain.phone : "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">联系人邮箱:</span> |
|
<span class="detail-value">{{ customerDetail.contactMain ? customerDetail.contactMain.email : "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">联系人WhatsApp:</span> |
|
<span class="detail-value">{{ customerDetail.contactMain ? customerDetail.contactMain.imWhatsApp : "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">通讯地址1:</span> |
|
<span class="detail-value">{{ customerDetail.address1 || "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">通讯地址2:</span> |
|
<span class="detail-value">{{ customerDetail.address2 || "-" }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">客户来源:</span> |
|
<span class="detail-value">{{ getOriginText(customerDetail.fromType) }}</span> |
|
</div> |
|
<div class="detail-item"> |
|
<span class="detail-label">备注:</span> |
|
<span class="detail-value">{{ customerDetail.remark || "-" }}</span> |
|
</div> |
|
</div> |
|
</el-dialog> |
|
</div> |
|
</template> |
|
<script> |
|
import basicCrumb from "@/components/basic-crumb/main"; |
|
import { |
|
fetchOrderDetail, |
|
submit, |
|
getExchangeRateList |
|
} from "@/api/approval/approvalDetails.js"; |
|
import { searchData } from "@/api/customer/customer.js"; |
|
import { |
|
getCurrencyList, |
|
} from "@/api/order/orderAddEdit"; |
|
import {getOrigin} from '@/api/customer/customer' |
|
export default { |
|
components: { |
|
basicCrumb, |
|
}, |
|
data() { |
|
return { |
|
customerDialogVisible: false, |
|
customerDetail: {}, |
|
loading: false, // 加载状态 |
|
orderData: {}, // |
|
expandProjectIndex: -1, |
|
// approveRemarks: "", // 审批意见 |
|
orderId: "", |
|
isDisabled: "", |
|
originList:[], |
|
|
|
}; |
|
}, |
|
mounted() { |
|
const orderId = this.$route.query.orderId ||this.$route.query.id; |
|
this.isDisabled = this.$route.query.mode == 'view' |
|
// 如果有orderId,调用接口获取订单详情 |
|
if (orderId) { |
|
this.orderId = orderId; |
|
this.getOrderDetail(orderId); |
|
|
|
} |
|
this.loadCurrencyList(); |
|
this.getOriginData() |
|
|
|
}, |
|
methods: { |
|
|
|
// 匹配客户来源文字 |
|
getOriginText(originCode) { |
|
if (!originCode) return "-"; |
|
const originItem = this.originList.find(item => item.dictKey === originCode); |
|
return originItem ? originItem.dictValue : originCode; |
|
}, |
|
formatToFourDecimal(num) { |
|
if (!num || isNaN(Number(num))) return "0.0000"; |
|
return Number(num).toFixed(4); |
|
}, |
|
handleExchangeRateFocus() { |
|
if (!this.orderData.exchangeRate) return; |
|
// 移除所有非数字和小数点的字符,保留原始输入 |
|
const rawValue = String(this.orderData.exchangeRate).replace(/[^\d.]/g, '').replace(/(\.\d*)\./g, '$1'); |
|
this.orderData.exchangeRate = rawValue || ""; |
|
}, |
|
// 申请优惠金额聚焦事件(移除千分位,还原纯数字) |
|
handleAmountFocus(field) { |
|
if (!this.orderData[field]) return; |
|
// 移除千分位逗号,还原为纯数字字符串 |
|
const rawValue = this.restoreNumber(this.orderData[field]); |
|
// 非数字则置空,否则赋值纯数字 |
|
this.orderData[field] = isNaN(Number(rawValue)) ? "" : rawValue; |
|
}, |
|
getOriginData(){ |
|
getOrigin().then(res =>{ |
|
this.originList = res.data.data || [] |
|
}).catch(err => { |
|
console.error('获取客户来源列表失败:', err) |
|
this.originList = [] |
|
}) |
|
}, |
|
// 格式化数值为两位小数(通用方法) |
|
formatToTwoDecimal(num) { |
|
// 空值/非数字处理 |
|
if (!num || isNaN(Number(num))) { |
|
return "0.00"; |
|
} |
|
// 转为数字并保留两位小数 |
|
return Number(num).toFixed(2); |
|
}, |
|
async fetchCustomerDetail() { |
|
try { |
|
const res = await searchData({ |
|
companyName: this.orderData.customerName, |
|
current: 1, |
|
size: 1000, |
|
}); |
|
if (res.data.code === 200) { |
|
const data = res.data.data.records || []; |
|
this.customerDetail = data[0] || {}; // 保存客户详情数据 |
|
} else { |
|
this.$message.error("获取客户详情失败"); |
|
} |
|
} catch (error) { |
|
console.error("获取客户详情异常:", error); |
|
this.$message.error("网络异常,请稍后重试"); |
|
} |
|
}, |
|
|
|
// 金额千分位格式化核心方法(修复0值显示问题) |
|
formatNumber(num) { |
|
// 处理空值、null、undefined、"-"等无效值(排除0) |
|
if (num === "-" || num === "" || num === null || num === undefined) { |
|
return "-"; // 无效值统一显示0.00,而非"-" |
|
} |
|
// 单独处理0值 |
|
if (num === 0 || num === "0") { |
|
return "0.00"; |
|
} |
|
// 移除可能存在的千分位逗号,转为纯数字 |
|
let numStr = String(num).replace(/,/g, ""); |
|
const number = Number(numStr); |
|
// 非数字处理 |
|
if (isNaN(number)) { |
|
return "0.00"; |
|
} |
|
// 千分位 + 强制保留两位小数(金额标准格式) |
|
return number.toLocaleString("zh-CN", { |
|
minimumFractionDigits: 2, |
|
maximumFractionDigits: 2, |
|
useGrouping: true // 强制开启千分位 |
|
}); |
|
}, |
|
// 改动11:新增还原纯数字方法(提交/计算时用) |
|
restoreNumber(formattedNum) { |
|
if (!formattedNum) return ""; |
|
const pureNum = String(formattedNum).replace(/,/g, ''); // 移除所有千分位逗号 |
|
return isNaN(Number(pureNum)) ? "" : pureNum; |
|
}, |
|
// 改动13:金额输入框失焦事件(千分位格式化) |
|
formatAmountBlur(field) { |
|
if (!this.orderData[field]) { |
|
this.orderData[field] = ""; |
|
return; |
|
} |
|
const rawValue = this.restoreNumber(this.orderData[field]); |
|
const numberValue = Number(rawValue); |
|
|
|
if (isNaN(numberValue)) { |
|
this.$message.warning("请输入有效的数字"); |
|
this.orderData[field] = ""; |
|
return; |
|
} |
|
|
|
// 千分位 + 两位小数格式化 |
|
this.orderData[field] = numberValue.toLocaleString("zh-CN", { |
|
minimumFractionDigits: 2, |
|
maximumFractionDigits: 2, |
|
}); |
|
}, |
|
|
|
// 改动14:提成比例失焦事件(仅保留两位小数,无千分位) |
|
formatRateBlur() { |
|
if (!this.orderData.commissionRate) { |
|
this.orderData.commissionRate = ""; |
|
return; |
|
} |
|
// 移除非数字和小数点 |
|
let value = this.orderData.commissionRate.toString().replace(/[^\d.]/g, ''); |
|
// 只保留第一个小数点 |
|
value = value.replace(/(\.\d*)\./g, '$1'); |
|
// 空值/非数字处理 |
|
if (!value || isNaN(Number(value))) { |
|
this.$message.warning("请输入有效的比例数字"); |
|
this.orderData.commissionRate = ""; |
|
return; |
|
} |
|
// 仅保留两位小数,无千分位 |
|
this.orderData.commissionRate = Number(value).toFixed(2); |
|
// 重新计算提成金额 |
|
this.calcCommissionPrice(); |
|
}, |
|
|
|
// 改动15:优惠汇率失焦事件 |
|
formatExchangeRateBlur() { |
|
if (!this.orderData.exchangeRate) { |
|
this.orderData.exchangeRate = ""; |
|
return; |
|
} |
|
// 移除非数字和小数点 |
|
let value = this.orderData.exchangeRate.toString().replace(/[^\d.]/g, ''); |
|
// 只保留第一个小数点 |
|
value = value.replace(/(\.\d*)\./g, '$1'); |
|
// 空值/非数字处理 |
|
if (!value || isNaN(Number(value))) { |
|
this.$message.warning("请输入有效的汇率数字"); |
|
this.orderData.exchangeRate = ""; |
|
return; |
|
} |
|
// 核心改动:保留4位小数(不再限制两位) |
|
this.orderData.exchangeRate = this.formatToFourDecimal(value); |
|
}, |
|
// 改动16:其他费用金额聚焦事件 |
|
handleFeePriceFocus(index) { |
|
const fee = this.orderData.extraChargeList[index]; |
|
if (!fee || !fee.price) return; |
|
const rawValue = this.restoreNumber(fee.price); |
|
fee.price = isNaN(Number(rawValue)) ? "" : rawValue; |
|
}, |
|
|
|
// 改动17:其他费用金额失焦事件(千分位格式化) |
|
handleFeePriceBlur(index) { |
|
const fee = this.orderData.extraChargeList[index]; |
|
if (!fee || !fee.price) { |
|
fee.price = ""; |
|
return; |
|
} |
|
const rawValue = this.restoreNumber(fee.price); |
|
const numberValue = Number(rawValue); |
|
|
|
if (isNaN(numberValue)) { |
|
this.$message.warning("请输入有效的费用金额"); |
|
fee.price = ""; |
|
return; |
|
} |
|
|
|
// 千分位 + 两位小数格式化 |
|
fee.price = numberValue.toLocaleString("zh-CN", { |
|
minimumFractionDigits: 2, |
|
maximumFractionDigits: 2, |
|
}); |
|
}, |
|
|
|
async fetchCustomerDetail() { |
|
try { |
|
const res = await searchData({ |
|
companyName: this.orderData.customerName, |
|
current: 1, |
|
size: 1000, |
|
}); |
|
if (res.data.code === 200) { |
|
const data = res.data.data.records || []; |
|
this.customerDetail = data[0] || {}; // 保存客户详情数据 |
|
} else { |
|
this.$message.error("获取客户详情失败"); |
|
} |
|
} catch (error) { |
|
console.error("获取客户详情异常:", error); |
|
this.$message.error("网络异常,请稍后重试"); |
|
} |
|
}, |
|
|
|
formatInput(field) { |
|
// 空值处理 |
|
if (!this.orderData[field]) { |
|
this.orderData[field] = ""; |
|
return; |
|
} |
|
|
|
// 去除非数字和小数点的字符(保留一个小数点) |
|
let value = this.orderData[field].toString().replace(/[^\d.]/g, ''); |
|
// 只保留第一个小数点 |
|
value = value.replace(/(\.\d*)\./g, '$1'); |
|
|
|
// 空值/非数字处理 |
|
if (!value || isNaN(Number(value))) { |
|
this.orderData[field] = ""; |
|
return; |
|
} |
|
|
|
// 保留两位小数 |
|
const formattedValue = Number(value).toFixed(2); |
|
// 处理类似 1.00 这样的数值,避免显示为 1(可选,根据需求调整) |
|
this.orderData[field] = formattedValue; |
|
|
|
// 如果是提成比例,格式化后重新计算提成金额 |
|
if (field === 'commissionRate') { |
|
this.calcCommissionPrice(); |
|
} |
|
}, |
|
|
|
// ========== 修改:提成比例输入处理(兼容格式化) ========== |
|
handleCommissionRateInput() { |
|
this.formatInput('commissionRate'); |
|
}, |
|
|
|
async getOrderDetail(orderId) { |
|
try { |
|
// 显示加载状态 |
|
this.loading = true; |
|
// 调用后端订单详情接口 |
|
const res = await fetchOrderDetail({ id: orderId }); |
|
// 接口请求成功 |
|
if (res.data.code === 200) { |
|
this.orderData = res.data.data; // 赋值订单数据 |
|
// 如果是审批编辑页 不要把上次审批意见带出来 |
|
if (orderId && this.$route.query.mode == "edit") { |
|
this.orderData.approveRemarks = "" |
|
} |
|
// 改动18:初始化所有金额字段为千分位 |
|
this.initAmountFormat(); |
|
// this.orderData.discountAmount = this.formatToTwoDecimal(this.orderData.discountAmount); |
|
if(this.$route.query.mode !== 'view' && this.orderData.targetCurrency !== 'CNY'){ |
|
this.getExchangeRateList() |
|
} |
|
if (this.orderData.customerName) { |
|
this.fetchCustomerDetail(); // 仅请求数据,不打开弹窗 |
|
} |
|
|
|
|
|
if ( |
|
!this.orderData.extraChargeList || |
|
this.orderData.extraChargeList.length === 0 |
|
) { |
|
this.orderData.extraChargeList = [{ type: "", price: "" }]; |
|
} |
|
this.calcCommissionPrice(); |
|
} else { |
|
this.$message.error("获取订单详情失败"); |
|
} |
|
} catch (error) { |
|
console.error("获取订单详情异常:", error); |
|
} finally { |
|
// 无论成功失败,都关闭加载状态 |
|
this.loading = false; |
|
} |
|
}, |
|
// 改动19:新增初始化金额格式方法 |
|
initAmountFormat() { |
|
// 基础金额字段初始化(千分位) |
|
const amountFields = ['standardPrice', 'sellingPrice', 'splitPrice', 'discountAmount', 'commissionPrice']; |
|
amountFields.forEach(field => { |
|
// 即使字段为空,也初始化为0.00(而非空) |
|
this.orderData[field] = this.formatNumber(this.orderData[field] || 0); |
|
}); |
|
|
|
// 提成比例初始化(仅两位小数,空值则设为0.00) |
|
this.orderData.commissionRate = this.orderData.commissionRate |
|
? Number(this.orderData.commissionRate).toFixed(2) |
|
: "0.00"; |
|
|
|
// 优惠汇率初始化(仅两位小数,空值则设为0.00) |
|
this.orderData.exchangeRate = this.orderData.exchangeRate |
|
? this.formatToFourDecimal(this.orderData.exchangeRate) |
|
: "0.0000"; |
|
|
|
// 其他费用金额初始化(千分位) |
|
if (this.orderData.extraChargeList && this.orderData.extraChargeList.length) { |
|
this.orderData.extraChargeList.forEach((fee, index) => { |
|
fee.price = this.formatNumber(fee.price || 0); |
|
}); |
|
} |
|
}, |
|
|
|
getMonetaryUnitText(code) { |
|
// 1. 空值处理 |
|
if (!code) return "-"; |
|
// 2. 从货币列表中匹配编码 |
|
const unitItem = this.monetaryUnitList.find(item => item.dictKey === code); |
|
// 3. 匹配到返回展示文本,未匹配到返回原编码(避免展示空) |
|
return unitItem ? unitItem.dictValue : code; |
|
}, |
|
// 加载货币单位列表 |
|
async loadCurrencyList() { |
|
try { |
|
const res = await getCurrencyList(); |
|
if (res.data.code === 200) { |
|
this.monetaryUnitList = res.data.data || []; |
|
} |
|
} catch (err) { |
|
this.$message.error("网络异常,无法加载货币单位列表"); |
|
} |
|
}, |
|
// 获取汇率列表 |
|
getExchangeRateList() { |
|
getExchangeRateList({ |
|
baseCurrency: "CNY", // 基础货币 |
|
targetCurrency: this.orderData.monetaryUnit, // 目标货币 |
|
current: 1, |
|
size: 1000, |
|
}) |
|
.then((res) => { |
|
if (res.data.code === 200) { |
|
const tableData = res.data.data.records || []; |
|
this.orderData.exchangeRate = this.formatToFourDecimal(tableData[0].rate); |
|
// this.total = res.data.data.total || 0; |
|
} |
|
}) |
|
.catch(() => {}); |
|
}, |
|
// 新增:计算提成金额核心方法(提成比例变化时触发) |
|
calcCommissionPrice() { |
|
// 还原指导价为纯数字(移除千分位) |
|
const standardPrice = Number(this.restoreNumber(this.orderData.standardPrice) || 0); |
|
let commissionRate = this.orderData.commissionRate ? this.orderData.commissionRate.toString().trim() : ''; |
|
|
|
if (!/^\d+(\.\d+)?$/.test(commissionRate)) { |
|
this.orderData.commissionPrice = this.formatNumber(0); // 提成金额显示千分位 |
|
return; |
|
} |
|
|
|
const rate = Number(commissionRate) / 100; |
|
let commissionPrice = standardPrice * rate; |
|
// 提成金额格式化千分位 |
|
this.orderData.commissionPrice = this.formatNumber(commissionPrice); |
|
}, |
|
getContactValue(key) { |
|
// 多层判断:先判断contacts是否存在且是数组,再判断第一个素是否存在,最后取对应属性 |
|
if ( |
|
this.customerDetail.contacts && |
|
Array.isArray(this.customerDetail.contacts) && |
|
this.customerDetail.contacts[0] |
|
) { |
|
return this.customerDetail.contacts[0][key] || "-"; |
|
} |
|
return "-"; |
|
}, |
|
/** |
|
* 打开客户详情弹窗并加载客户数据 |
|
*/ |
|
openCustomerDetail() { |
|
this.customerDialogVisible = true; // 显示客户详情弹窗 |
|
}, |
|
|
|
// 展开/收起项目信息 |
|
toggleProject(index) { |
|
// 如果点击的是当前展开的项目,收起(设为-1);否则展开(设为对应索引) |
|
this.expandProjectIndex = this.expandProjectIndex === index ? -1 : index; |
|
}, |
|
/** |
|
* 方法:计算前端分页数据 |
|
* @param {Object} system - 项目对象 |
|
*/ |
|
calcPageData(system) { |
|
const { productList, currentPage, pageSize } = system; |
|
// 计算起始索引和结束索引 |
|
const startIndex = (currentPage - 1) * pageSize; |
|
const endIndex = startIndex + pageSize; |
|
// 截取当前页数据(前端分页核心逻辑) |
|
system.pageProductList = productList.slice(startIndex, endIndex); |
|
}, |
|
|
|
/** |
|
* 添加其他费用项 |
|
* @returns {void} |
|
*/ |
|
addFeeItem() { |
|
// 向其他费用数组中添加新的空费用项 |
|
this.orderData.extraChargeList.push({ type: "", price: "" }); |
|
}, |
|
|
|
/** |
|
* 移除最后一个其他费用项 |
|
* @returns {void} |
|
*/ |
|
removeFeeItem() { |
|
// 至少保留一个费用项 |
|
if (this.orderData.extraChargeList.length <= 1) return; |
|
// 删除最后一个费用项 |
|
this.orderData.extraChargeList.pop(); |
|
}, |
|
|
|
// 审批通过操作 |
|
async handleApproval(mode) { |
|
// if (!this.approveRemarks) { |
|
// return this.$message.warning("请输入审批意见"); |
|
// } |
|
if (mode === 'reject' && !this.orderData.approveRemarks.trim()) { |
|
return this.$message.warning("请输入审批意见"); |
|
} |
|
// 3-通过 4-驳回 |
|
let status = 3; |
|
if (mode == "reject") { |
|
status = 4; |
|
} |
|
try { |
|
const requestData = { |
|
...this.orderData, |
|
id: this.orderData.id, |
|
// 1. 基础金额字段:还原千分位 + 转为Number类型(关键!) |
|
standardPrice: Number(this.restoreNumber(this.orderData.standardPrice) || 0), |
|
sellingPrice: Number(this.restoreNumber(this.orderData.sellingPrice) || 0), |
|
splitPrice: Number(this.restoreNumber(this.orderData.splitPrice) || 0), |
|
discountAmount: Number(this.restoreNumber(this.orderData.discountAmount) || 0), |
|
commissionPrice: Number(this.restoreNumber(this.orderData.commissionPrice) || 0), |
|
// 2. 比例/汇率字段:转为Number类型 |
|
commissionRate: Number(this.orderData.commissionRate || 0), |
|
exchangeRate: Number(this.orderData.exchangeRate || 0), |
|
// 3. 审批相关字段 |
|
status: status, |
|
approveRemarks: this.orderData.approveRemarks.trim() || "", |
|
// 4. 其他费用列表:还原每个费用项的金额为Number |
|
extraChargeList: this.orderData.extraChargeList |
|
.filter((item) => item.type.trim() && item.price.trim()) // 过滤空项 |
|
.map((item) => ({ |
|
type: item.type.trim(), |
|
price: Number(this.restoreNumber(item.price) || 0), // 转为Number |
|
})), |
|
}; |
|
console.log("requestData1111",requestData) |
|
// 调用后端接口 |
|
const res = await submit(requestData); |
|
console.log("res提交", res); |
|
if (res.data.code === 200) { |
|
if (mode == 'resolve') { |
|
this.$message.success("订单提交审批成功!"); |
|
} else { |
|
this.$message.success("订单驳回成功!"); |
|
} |
|
|
|
this.$router.push({ |
|
path:"/approval/approvalRecord", |
|
query:{ |
|
|
|
} |
|
}); |
|
} else { |
|
this.$message.error(); |
|
} |
|
} catch (error) { |
|
this.$message.error("网络异常,请稍后重试"); |
|
} |
|
}, |
|
}, |
|
}; |
|
</script> |
|
|
|
<style scoped lang="scss"> |
|
@import '@/styles/variables.scss'; |
|
.container { |
|
background-color: #ffffff; |
|
min-height: 100vh; |
|
box-sizing: border-box; |
|
margin-top: 8px; |
|
padding: 20px !important; |
|
position: relative; |
|
.page-title { |
|
font-size:$font-size-title; |
|
margin-bottom: 10px; |
|
width: 100%; |
|
text-align: center; |
|
} |
|
|
|
.boxcard { |
|
border: 1px solid #bbbbbb; |
|
margin: 20px; |
|
padding: 30px; |
|
border-radius: 20px; |
|
box-shadow: 0 2px 2px 0 #bbbbbb; |
|
} |
|
|
|
// 其他费用卡片样式 |
|
.other-fee-card { |
|
border: 1px solid #bbbbbb; |
|
margin-top: 30px; |
|
padding: 20px; |
|
border-radius: 20px; |
|
box-shadow: 0 2px 2px 0 #bbbbbb; |
|
} |
|
|
|
.card-header { |
|
font-size: 1.354vw; |
|
margin-bottom: 20px; |
|
font-weight: 500; |
|
} |
|
.approval-action-bar { |
|
position: fixed !important; // 固定定位(核心) |
|
bottom: 0; // 贴紧页面底部 |
|
left: 0; // 贴紧左侧 |
|
right: 0; // 贴紧右侧 |
|
z-index: 999; // 提高层级,避免被其他元素遮挡 |
|
::v-deep .el-input .el-input__inner { |
|
height: 3vw !important; |
|
line-height: 3vw !important; |
|
|
|
min-height: 3vw; |
|
} |
|
} |
|
.order-info-container { |
|
display: flex; |
|
justify-content: space-between; |
|
flex-wrap: wrap; |
|
|
|
.info-left, |
|
.info-right { |
|
width: 48%; |
|
} |
|
|
|
.info-item { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 10px; |
|
// line-height: 32px; |
|
font-size: $font-size-26; |
|
height: 2.25vw; |
|
line-height: 2.25vw; |
|
display: flex; |
|
align-items: center; |
|
|
|
.label { |
|
width: 9.478vw !important; |
|
font-size: $font-size-26; |
|
text-align: left; |
|
// margin-right: 15px; |
|
color: #000; |
|
} |
|
|
|
.value { |
|
color: #000; |
|
|
|
} |
|
} |
|
::v-deep .el-input { |
|
display: inline-flex; |
|
align-items: center; |
|
// 让输入框高度和line-height:32px匹配 |
|
// .el-input__inner { |
|
// height: 32px !important; |
|
// line-height: 32px !important; |
|
// padding: 0 15px !important; // 重置内边距,避免过宽 |
|
// box-sizing: border-box; |
|
// } |
|
} |
|
|
|
|
|
.formula { |
|
color: #909399; |
|
} |
|
} |
|
.systemBox { |
|
padding: 0 30px; |
|
margin-bottom: 150px; |
|
} |
|
|
|
// 其他费用容器样式 |
|
.other-fee-container { |
|
width: 100%; |
|
|
|
// 每行费用项 |
|
.fee-row { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 15px; |
|
gap: 20px; // 费用项之间的间距 |
|
} |
|
|
|
// 单个费用项(类别/金额) |
|
.fee-item { |
|
display: flex; |
|
align-items: center; |
|
width: 50%; |
|
.label { |
|
width: 100px; |
|
text-align: left; |
|
margin-right:2.5vw; |
|
// color: #606266; |
|
font-size: 1.146vw; |
|
} |
|
} |
|
|
|
// 按钮组 |
|
.btn-group { |
|
// 关键2:重置按钮组样式,取消挤压 |
|
width: auto !important; |
|
display: flex; |
|
gap: 10px; |
|
margin-left: 5vw !important; |
|
// 强制按钮垂直居中 |
|
align-items: center; |
|
|
|
::v-deep .el-button { |
|
&--circle { |
|
width: 3.6458vw !important; |
|
height:3.6458vw !important; |
|
min-width:3.6458vw !important; |
|
max-width:3.6458vw !important; |
|
max-height: 40px !important; |
|
padding: 0 !important; |
|
border-radius: 50% !important; |
|
display: inline-flex !important; |
|
align-items: center !important; |
|
justify-content: center !important; |
|
box-sizing: border-box !important; |
|
font-size: $font-size-28; |
|
} |
|
|
|
&--success { |
|
background-color: rgba(100,179,113,1); |
|
color: rgba(255,255,255,1); |
|
} |
|
&--danger { |
|
background: rgba(226,45,45,1) !important; |
|
border-color: rgba(226,45,45,1) !important; |
|
} |
|
}} |
|
} |
|
|
|
.system-info-container { |
|
.system-item { |
|
border: 1px solid #e6e6e6; |
|
border-radius: 15px; |
|
padding: 15px; |
|
margin-bottom: 15px; |
|
cursor: pointer; |
|
|
|
.system-header { |
|
display: flex; |
|
align-items: center; |
|
justify-content: space-between; |
|
|
|
.system-code { |
|
margin-right: 12px; |
|
color: #606266; |
|
font-size: $font-size-22; |
|
} |
|
|
|
.system-name { |
|
margin-right: 12px; |
|
font-weight: 500; |
|
font-size: $font-size-22; |
|
} |
|
.system-num { |
|
font-weight: 500; |
|
font-size: $font-size-22; |
|
} |
|
|
|
.system-count { |
|
margin-right: 20px; |
|
color: #606266; |
|
font-size: $font-size-22; |
|
} |
|
|
|
.system-price { |
|
margin-left: auto; |
|
color: #303133; |
|
font-weight: 500; |
|
font-size: $font-size-22; |
|
} |
|
} |
|
|
|
&.expanded { |
|
.system-header { |
|
margin-bottom: 10px; |
|
} |
|
} |
|
} |
|
|
|
.table-pagination { |
|
display: flex; |
|
align-items: center; |
|
justify-content: flex-start; |
|
margin-top: 10px; |
|
font-size: 13px; |
|
color: #606266; |
|
} |
|
} |
|
|
|
.approval-action-bar { |
|
position: relative; |
|
bottom: 0; // 贴紧页面底部 |
|
left: 0; // 贴紧左侧 |
|
right: 0; // 贴紧右侧 |
|
z-index: 99; // |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
margin-top: 30px; |
|
height: 80px; |
|
background-color: #1e3a8a; |
|
.approval-action-box{ |
|
width: 100%; |
|
height: 80px; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
|
|
::v-deep .el-input__inner { |
|
background-color: #1e3a8a; |
|
color: #ffffff; |
|
border-color: #ffffff; |
|
border-radius: 30px; |
|
} |
|
} |
|
|
|
// .customer-detail { |
|
// color: #2244f5; |
|
// font-size: 20px !important; |
|
// } |
|
|
|
|
|
.rowTotal{ |
|
display: flex; |
|
justify-content: space-between; |
|
color: #000; |
|
font-size: $font-size-22; |
|
|
|
.rowright{ |
|
display: flex; |
|
width:80%; |
|
justify-content: flex-end; |
|
.item{ |
|
// width: 200px; |
|
margin: 0 20px; |
|
} |
|
} |
|
} |
|
|
|
} |
|
</style> |
|
|
|
<style lang="scss"> |
|
@import '@/styles/variables.scss'; |
|
.customer-dialog{ |
|
.el-dialog__body{ |
|
padding: 0 30px !important; |
|
} |
|
} |
|
/* 专门处理弹窗 */ |
|
.customer-dialog .customer-detail { |
|
padding: 1vw 1vw; |
|
font-size: 14px; |
|
// color: #2244f5 !important; |
|
} |
|
|
|
.customer-dialog .detail-item { |
|
display: flex; |
|
align-items: flex-start; |
|
// padding: 10px 0; |
|
|
|
} |
|
|
|
.customer-dialog .detail-label { |
|
// width: 110px; |
|
// font-weight: 500; |
|
color: #606266; |
|
text-align: left; |
|
padding-right: 15px; |
|
font-size: $font-size-24; |
|
width: 10vw; |
|
} |
|
|
|
.customer-dialog .detail-value { |
|
font-size: $font-size-24; |
|
flex: 1; |
|
// color: #2244f5 !important; |
|
// word-wrap: break-word; |
|
// word-break: break-all; |
|
} |
|
</style>
|
|
|