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.

907 lines
26 KiB

3 months ago
<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">
3 months ago
<!-- 左侧订单信息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>
3 months ago
</div>
<div class="info-item">
<span class="label">货币单位:</span>
<span class="value">{{ getMonetaryUnitText(orderData.monetaryUnit) }}</span>
3 months ago
</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>
3 months ago
<span class="value">{{ orderData.standardPrice || "" }}</span>
3 months ago
</div>
<div class="info-item">
<span class="label">销售价:</span>
3 months ago
<span class="value">{{ orderData.sellingPrice || "" }}</span>
3 months ago
</div>
<!-- 分成金额 -->
<div class="info-item formula">
<span class="label">分成金额:</span>
<span class="value">
3 months ago
<span class="value">{{ orderData.splitPrice || "" }}</span>
3 months ago
</span>
</div>
<div class="info-item">
<span class="label">申请优惠金额:</span>
<span class="value">
<el-input
v-model="orderData.discountAmount"
3 months ago
:disabled="isDisabled"
3 months ago
style="width: 10vw"
3 months ago
placeholder="请输入"
3 months ago
/>
3 months ago
</span>
</div>
<!-- 提成金额 -->
<div class="info-item">
<span class="label">提成金额:</span>
<span class="value">
<el-input
v-model="orderData.commissionPrice"
3 months ago
disabled
style="width: 10vw"
3 months ago
placeholder="请输入"
3 months ago
/>
3 months ago
</span>
</div>
<div class="info-item">
<span class="label">提成比例:</span>
<span class="value">
<el-input
v-model="orderData.commissionRate"
placeholder="请输入"
3 months ago
style="width: 10vw"
3 months ago
:disabled="isDisabled"
@input="calcCommissionPrice"
/>%
</span>
</div>
3 months ago
<div class="info-item" v-if="orderData.monetaryUnit !=='CNY'">
3 months ago
<!-- 中国的话 不存在 -->
<!-- {{orderData.monetaryUnit}} -->
3 months ago
<span class="label">优惠汇率:</span>
<span class="value">
<el-input
v-model="orderData.exchangeRate"
:disabled="isDisabled"
placeholder="请输入"
3 months ago
style="width: 10vw"
3 months ago
/>
</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"
3 months ago
:placeholder="isDisabled ? fee.type : '请输入费用的类目名称'"
style="width: 17.8vw"
3 months ago
:disabled="isDisabled"
/>
</div>
<div class="fee-item">
<span class="label">费用金额</span>
<el-input
v-model="fee.price"
3 months ago
:placeholder="isDisabled ? fee.price : '请输入费用金额'"
style="width: 17.8vw"
3 months ago
type="number"
:disabled="isDisabled"
/>
3 months ago
<!-- 按钮组仅最后一行显示 -->
3 months ago
<div
3 months ago
class="btn-group"
3 months ago
>
<template v-if="index === orderData.extraChargeList.length - 1 && !isDisabled">
<el-button
3 months ago
type="success"
icon="el-icon-plus"
circle
@click="addFeeItem"
3 months ago
3 months ago
/>
<el-button
type="danger"
icon="el-icon-minus"
circle
@click="removeFeeItem"
:disabled="orderData.extraChargeList.length <= 1"
/>
</template>
3 months ago
</div>
3 months ago
</div>
3 months ago
3 months ago
</div>
</div>
</div>
</div>
<!-- 项目信息卡片 -->
3 months ago
<div
class="systemBox"
v-if="
!loading &&
orderData &&
orderData.projectList &&
orderData.projectList.length > 0
"
>
<div class="card-header">项目信息</div>
3 months ago
<div class="system-info-container">
<!-- 项目列表 -->
3 months ago
<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>
3 months ago
3 months ago
<span class="system-name">{{ project.unit }}</span>
3 months ago
3 months ago
<span></span>
</div>
<div>
3 months ago
<el-link type="primary" style="margin-left: 10px;color: #3A84FF;">
3 months ago
{{ expandProjectIndex === index ? "收起" : "展开" }}
</el-link>
</div>
</div>
<!-- 产品表格展开时显示 -->
<div v-if="expandProjectIndex === index">
<el-table
:data="project.productList"
style="width: 100%; margin-top: 10px"
border
3 months ago
max-height="490px"
:header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }"
3 months ago
>
3 months ago
<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 prop="standardPrice" label="公司指导价" />
<el-table-column prop="sellingPrice" label="销售价" />
<el-table-column prop="splitPrice" label="分成金额"/>
3 months ago
</el-table>
<div class="rowTotal">
<div class="rowleft">
合计条数:{{project.productList.length}}
</div>
<div class="rowright">
<div class="item">
公司指导价合计 {{project.standardPrice}}
3 months ago
</div>
<div class="item">
销售价合计 {{project.sellingPrice}}
3 months ago
</div>
<div class="item">
分成金额合计 {{project.splitPrice}}
3 months ago
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 审批操作栏 -->
<div class="approval-action-bar" v-if="!loading && orderData">
<div v-if="isDisabled" class="approval-action-box">
3 months ago
<span style="color:#ffff;font-size: 1.458vw;">审批意见 </span>
3 months ago
<el-input
v-model="orderData.approveRemarks"
3 months ago
disabled
style="width: 70vw; margin-right: 20px"
3 months ago
/>
</div>
<template v-if="!isDisabled">
<el-input
v-model="orderData.approveRemarks"
3 months ago
placeholder="请输入审批意见"
style="width: 70vw; margin-right: 20px"
3 months ago
/>
<el-button
type="danger"
style="margin-right: 10px"
@click="handleApproval('reject')"
v-if="!isDisabled"
3 months ago
class="el-button--big"
3 months ago
>拒绝</el-button
>
3 months ago
<el-button type="success" @click="handleApproval('resolve')" v-if="!isDisabled" class="el-button--big">通过</el-button>
3 months ago
</template>
</div>
<!-- 客户详情弹窗 -->
<el-dialog
title="客户详情"
:visible.sync="customerDialogVisible"
3 months ago
width="30vw"
3 months ago
append-to-body
:modal="false"
class="customer-dialog"
3 months ago
>
<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>
3 months ago
3 months ago
<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">
3 months ago
<span class="detail-label">联系人WhatsApp:</span>
3 months ago
<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">{{ 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";
3 months ago
import {
getCurrencyList,
} from "@/api/order/orderAddEdit";
3 months ago
export default {
components: {
basicCrumb,
},
data() {
return {
customerDialogVisible: false,
customerDetail: {},
loading: false, // 加载状态
orderData: {}, //
expandProjectIndex: -1,
// approveRemarks: "", // 审批意见
3 months ago
orderId: "",
isDisabled:""
};
},
mounted() {
const orderId = this.$route.query.orderId ||this.$route.query.id;
3 months ago
this.isDisabled = this.$route.query.mode == 'view'
3 months ago
// 如果有orderId,调用接口获取订单详情
if (orderId) {
this.getOrderDetail(orderId);
this.orderId = orderId;
}
3 months ago
this.loadCurrencyList();
3 months ago
3 months ago
},
methods: {
async getOrderDetail(orderId) {
try {
// 显示加载状态
this.loading = true;
// 调用后端订单详情接口
const res = await fetchOrderDetail({ id: orderId });
// 接口请求成功
if (res.data.code === 200) {
this.orderData = res.data.data; // 赋值订单数据
if(this.$route.query.mode !== 'view' && this.orderData.targetCurrency !== 'CNY'){
this.getExchangeRateList()
}
3 months ago
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;
}
3 months ago
},
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 = tableData[0].rate
// this.total = res.data.data.total || 0;
}
})
.catch(() => {});
3 months ago
},
// 新增:计算提成金额核心方法(提成比例变化时触发)
calcCommissionPrice() {
3 months ago
const standardPrice = Number(this.orderData.standardPrice || 0);
let commissionRate = this.orderData.commissionRate ? this.orderData.commissionRate.toString().trim() : '';
3 months ago
3 months ago
if (!/^\d+(\.\d+)?$/.test(commissionRate)) {
3 months ago
this.orderData.commissionPrice = "0.00"; // 非法值置为0.00
3 months ago
return;
}
const rate = Number(commissionRate) / 100;
let commissionPrice = standardPrice * rate;
3 months ago
this.orderData.commissionPrice = commissionPrice.toFixed(2); // 强制保留两位小数
3 months ago
},
getContactValue(key) {
3 months ago
// 多层判断:先判断contacts是否存在且是数组,再判断第一个素是否存在,最后取对应属性
3 months ago
if (
this.customerDetail.contacts &&
Array.isArray(this.customerDetail.contacts) &&
this.customerDetail.contacts[0]
) {
return this.customerDetail.contacts[0][key] || "-";
}
return "-";
},
/**
* 打开客户详情弹窗并加载客户数据
*/
async openCustomerDetail() {
try {
// 显示加载状态
this.loading = true;
const res = await searchData({
companyName:this.orderData.customerName,
current: 1,
size: 1000,
});
console.log("res11111111",res)
// 接口请求成功
if (res.data.code === 200) {
const data = res.data.data.records || []
this.customerDetail = data[0]
this.customerDialogVisible = true; // 显示客户详情弹窗
} else {
this.$message.error("获取客户详情失败"); // 提示错误信息
}
} catch (error) {
// 捕获异常并打印日志
console.error("获取客户详情异常:", error);
this.$message.error("网络异常,请稍后重试"); // 提示网络异常
} finally {
// 加载状态
this.loading = false;
}
},
// 展开/收起项目信息
3 months ago
toggleProject(index) {
// 如果点击的是当前展开的项目,收起(设为-1);否则展开(设为对应索引)
this.expandProjectIndex = this.expandProjectIndex === index ? -1 : index;
},
/**
* 方法计算前端分页数据
* @param {Object} system - 项目对象
3 months ago
*/
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) {
3 months ago
// return this.$message.warning("请输入审批意见");
// }
if (mode === 'reject' && !this.orderData.approveRemarks.trim()) {
return this.$message.warning("请输入审批意见");
}
3 months ago
// 3-通过 4-驳回
let status = 3;
if (mode == "reject") {
status = 4;
}
try {
const requestData = {
3 months ago
...this.orderData,
3 months ago
id: this.orderData.id,
commissionRate: this.orderData.commissionRate,
exchangeRate: this.orderData.exchangeRate,
commissionPrice: this.orderData.commissionPrice,
status: status,
approveRemarks:this.orderData.approveRemarks,
3 months ago
extraChargeList: this.orderData.extraChargeList
.filter((item) => item.type && item.price) // 只传有值的费用项
.map((item) => ({
type: item.type,
price: item.price,
})),
discountAmount:this.orderData.discountAmount,
3 months ago
};
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:{
3 months ago
3 months ago
}
});
} else {
this.$message.error();
}
} catch (error) {
this.$message.error("网络异常,请稍后重试");
}
},
},
};
</script>
<style scoped lang="scss">
3 months ago
@import '@/styles/variables.scss';
3 months ago
.container {
background-color: #ffffff;
min-height: 100vh;
box-sizing: border-box;
margin-top: 8px;
padding: 20px !important;
position: relative;
.page-title {
3 months ago
font-size:$font-size-title;
margin-bottom: 10px;
width: 100%;
text-align: center;
}
3 months ago
.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 {
3 months ago
font-size: 1.354vw;
3 months ago
margin-bottom: 20px;
font-weight: 500;
}
3 months ago
.approval-action-bar {
::v-deep .el-input .el-input__inner {
height: 3vw !important;
line-height: 3vw !important;
3 months ago
3 months ago
min-height: 3vw;
}
}
3 months ago
.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;
3 months ago
// line-height: 32px;
font-size: $font-size-26;
3 months ago
height: 2vw;
display: flex;
align-items: center;
3 months ago
.label {
3 months ago
width: 9.478vw !important;
font-size: $font-size-26;
3 months ago
text-align: left;
// margin-right: 15px;
3 months ago
color: #000;
3 months ago
}
.value {
3 months ago
color: #000;
3 months ago
}
}
::v-deep .el-input {
display: inline-flex;
align-items: center;
// 让输入框高度和line-height:32px匹配
3 months ago
// .el-input__inner {
// height: 32px !important;
// line-height: 32px !important;
// padding: 0 15px !important; // 重置内边距,避免过宽
// box-sizing: border-box;
// }
}
3 months ago
3 months ago
3 months ago
.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;
3 months ago
width: 50%;
3 months ago
.label {
3 months ago
width: 100px;
3 months ago
text-align: left;
3 months ago
margin-right:2.5vw;
3 months ago
// color: #606266;
3 months ago
font-size: 1.146vw;
3 months ago
}
}
// 按钮组
3 months ago
.btn-group {
// 关键2:重置按钮组样式,取消挤压
width: auto !important;
3 months ago
display: flex;
3 months ago
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;
}
}}
3 months ago
}
.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: 8px;
color: #606266;
3 months ago
font-size: $font-size-22;
3 months ago
}
.system-name {
margin-right: 8px;
font-weight: 500;
3 months ago
font-size: $font-size-22;
3 months ago
}
.system-count {
margin-right: 20px;
color: #606266;
3 months ago
font-size: $font-size-22;
3 months ago
}
.system-price {
margin-left: auto;
color: #303133;
font-weight: 500;
3 months ago
font-size: $font-size-22;
3 months ago
}
}
&.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 {
3 months ago
position: relative;
3 months ago
bottom: 0; // 贴紧页面底部
left: 0; // 贴紧左侧
right: 0; // 贴紧右侧
3 months ago
z-index: 99; //
3 months ago
display: flex;
justify-content: center;
align-items: center;
margin-top: 30px;
3 months ago
height: 80px;
3 months ago
background-color: #1e3a8a;
.approval-action-box{
width: 100%;
3 months ago
height: 80px;
3 months ago
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;
// }
3 months ago
.rowTotal{
display: flex;
justify-content: space-between;
3 months ago
color: #000;
font-size: $font-size-22;
3 months ago
.rowright{
display: flex;
width:80%;
justify-content: flex-end;
.item{
// width: 200px;
margin: 0 20px;
3 months ago
}
}
}
3 months ago
3 months ago
}
</style>
<style lang="scss">
3 months ago
@import '@/styles/variables.scss';
.customer-dialog{
.el-dialog__body{
padding: 0 30px !important;
3 months ago
}
}
/* 专门处理弹窗 */
.customer-dialog .customer-detail {
3 months ago
padding: 1vw 20px;
font-size: 14px;
// color: #2244f5 !important;
}
.customer-dialog .detail-item {
display: flex;
align-items: flex-start;
// padding: 10px 0;
}
.customer-dialog .detail-label {
3 months ago
// width: 110px;
// font-weight: 500;
color: #606266;
3 months ago
text-align: left;
padding-right: 15px;
3 months ago
font-size: $font-size-24;
width: 10vw;
}
.customer-dialog .detail-value {
3 months ago
font-size: $font-size-24;
flex: 1;
// color: #2244f5 !important;
// word-wrap: break-word;
// word-break: break-all;
}
3 months ago
</style>