2025-12-29 17:21:32 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="order-detail-container" v-loading="loading">
|
|
|
|
|
|
<div class="table-toolbar">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="success"
|
|
|
|
|
|
icon="el-icon-download"
|
|
|
|
|
|
@click="handleExport"
|
|
|
|
|
|
>
|
|
|
|
|
|
导出订单明细
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 三个Tab页切换 -->
|
|
|
|
|
|
<el-tabs v-model="activeTab" class="order-tabs" tab-position="top">
|
|
|
|
|
|
<!-- Tab1: 订单维度(订单主表信息) -->
|
|
|
|
|
|
<el-tab-pane label="订单维度" name="mainOrder">
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="orderDetailList"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 100%;"
|
|
|
|
|
|
@selection-change="handleSelectionChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="orderCode" label="订单编号" min-width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="customerCode" label="客户编码" min-width="100"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="companyName" label="公司名称" min-width="150"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="contactPerson" label="联系人" min-width="80"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="customerLevel" label="客户等级" min-width="100"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="industry" label="所属行业" min-width="120"></el-table-column>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<el-table-column prop="orderAmount" label="订单金额(万元)">
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.orderAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="salesman" label="销售员" min-width="80"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="deliveryDate" label="交货日期" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ scope.row.deliveryDate ? formatDate(scope.row.deliveryDate) : '-' }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="orderStatus" label="订单状态" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag v-if="scope.row.orderStatus === 1" type="success">已完成</el-tag>
|
|
|
|
|
|
<el-tag v-else-if="scope.row.orderStatus === 0" type="warning">待处理</el-tag>
|
|
|
|
|
|
<el-tag v-else type="danger">已取消</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="financeStatus" label="财务状态" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag v-if="scope.row.financeStatus === 1" type="success">已结款</el-tag>
|
|
|
|
|
|
<el-tag v-else type="danger">未结款</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<el-table-column prop="unpaidAmount" label="未结金额(万元)" min-width="100">
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.unpaidAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="createTime" label="创建时间" min-width="160">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ scope.row.createTime }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="itemCount" label="明细数量" min-width="80"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="objectionCount" label="异议数量" min-width="80"></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Tab2: 明细维度(订单产品明细,扁平展示) -->
|
|
|
|
|
|
<el-tab-pane label="明细维度" name="itemDetail">
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="flatOrderItemList"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 100%;"
|
|
|
|
|
|
stripe
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="orderCode" label="关联订单编号" min-width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="companyName" label="客户公司" min-width="150"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="productCode" label="产品编码" min-width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="productName" label="产品名称" min-width="150"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="specification" label="产品规格" min-width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="productCount" label="产品数量" min-width="80"></el-table-column>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<el-table-column prop="unitPrice" label="单价(元 / 吨)" min-width="100">
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.unitPrice) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="itemAmount" label="明细金额(元)" min-width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.itemAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="remark" label="明细备注" min-width="150"></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Tab3: 客户分组(折叠面板 + 展开后独立订单表格,替代树表) -->
|
|
|
|
|
|
<el-tab-pane label="客户分组" name="customerGroup">
|
|
|
|
|
|
<!-- 无客户数据提示 -->
|
|
|
|
|
|
<div v-if="customerGroupedList.length === 0" class="empty-tip">
|
|
|
|
|
|
暂无客户分组数据
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 折叠面板:每个面板对应一个客户,展开显示该客户的订单表格 -->
|
|
|
|
|
|
<el-collapse v-else class="customer-collapse" accordion>
|
|
|
|
|
|
<!-- 循环渲染客户折叠项 -->
|
|
|
|
|
|
<el-collapse-item
|
|
|
|
|
|
v-for="customer in customerGroupedList"
|
|
|
|
|
|
:key="customer.customerKey"
|
|
|
|
|
|
:name="customer.customerKey"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 折叠面板标题:展示客户核心信息 + 汇总数据 -->
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
<div class="collapse-title">
|
|
|
|
|
|
<span class="company-name">{{ customer.companyName }}</span>
|
|
|
|
|
|
<span class="customer-info">客户编码:{{ customer.customerCode }}</span>
|
|
|
|
|
|
<span class="customer-info">客户等级:{{ customer.customerLevel }}</span>
|
|
|
|
|
|
<span class="customer-info">所属行业:{{ customer.industry }}</span>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<span class="summary-info">总订单金额:{{ formatAmount(customer.totalOrderAmount) }} 万元</span>
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<span class="summary-info">订单数量:{{ customer.orderCount }} 单</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 展开后:该客户的独立订单表格 -->
|
|
|
|
|
|
<div class="customer-order-table">
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="customer.orderList"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 100%;"
|
|
|
|
|
|
stripe
|
|
|
|
|
|
empty-text="该客户暂无订单数据"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="orderCode" label="订单编号" min-width="120"></el-table-column>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<el-table-column prop="orderAmount" label="订单金额(万元)" min-width="120">
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.orderAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="salesman" label="销售员" min-width="80"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="deliveryDate" label="交货日期" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ scope.row.deliveryDate ? formatDate(scope.row.deliveryDate) : '-' }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="orderStatus" label="订单状态" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag v-if="scope.row.orderStatus === 1" type="success">已完成</el-tag>
|
|
|
|
|
|
<el-tag v-else-if="scope.row.orderStatus === 0" type="warning">待处理</el-tag>
|
|
|
|
|
|
<el-tag v-else type="danger">已取消</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="financeStatus" label="财务状态" min-width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag v-if="scope.row.financeStatus === 1" type="success">已结款</el-tag>
|
|
|
|
|
|
<el-tag v-else type="danger">未结款</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-05-11 10:38:29 +08:00
|
|
|
|
<el-table-column prop="unpaidAmount" label="未结金额(万元)" min-width="120">
|
2025-12-29 17:21:32 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.unpaidAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="createTime" label="创建时间" min-width="160">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ scope.row.createTime }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-collapse-item>
|
|
|
|
|
|
</el-collapse>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 分页组件(针对主订单列表,全局生效) -->
|
|
|
|
|
|
<el-pagination
|
|
|
|
|
|
@size-change="handleSizeChange"
|
|
|
|
|
|
@current-change="handleCurrentChange"
|
|
|
|
|
|
:current-page="pageParams.pageNum"
|
|
|
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
|
|
|
:page-size="pageParams.pageSize"
|
|
|
|
|
|
:total="total"
|
|
|
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
|
style="margin-top: 20px; text-align: right;"
|
|
|
|
|
|
></el-pagination>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: "SalesReportOrderDetail",
|
|
|
|
|
|
props: {
|
|
|
|
|
|
// 订单明细列表(主表)
|
|
|
|
|
|
orderDetailList: {
|
|
|
|
|
|
type: Array,
|
|
|
|
|
|
default: () => []
|
|
|
|
|
|
},
|
|
|
|
|
|
// 分页参数
|
|
|
|
|
|
pageParams: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
default: () => ({ pageNum: 1, pageSize: 10 })
|
|
|
|
|
|
},
|
|
|
|
|
|
// 总条数
|
|
|
|
|
|
total: {
|
|
|
|
|
|
type: Number,
|
|
|
|
|
|
default: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
// 加载状态
|
|
|
|
|
|
loading: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
},
|
|
|
|
|
|
// 金额格式化方法
|
|
|
|
|
|
formatAmount: {
|
|
|
|
|
|
type: Function,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
// 日期格式化方法(年月日)
|
|
|
|
|
|
formatDate: {
|
|
|
|
|
|
type: Function,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
// 激活的Tab页
|
|
|
|
|
|
activeTab: "mainOrder"
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
// 扁平后的订单产品明细(Tab2使用)
|
|
|
|
|
|
flatOrderItemList() {
|
|
|
|
|
|
const flatList = [];
|
|
|
|
|
|
if (!this.orderDetailList || this.orderDetailList.length === 0) {
|
|
|
|
|
|
return flatList;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.orderDetailList.forEach(order => {
|
|
|
|
|
|
// 过滤空明细
|
|
|
|
|
|
if (!order.orderItemList || order.orderItemList.length === 0) return;
|
|
|
|
|
|
// 给每个明细添加主表关联信息
|
|
|
|
|
|
order.orderItemList.forEach(item => {
|
|
|
|
|
|
flatList.push({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
orderCode: order.orderCode || '',
|
|
|
|
|
|
companyName: order.companyName || '',
|
|
|
|
|
|
customerCode: order.customerCode || ''
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return flatList;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 按客户分组的数据(复用原有逻辑,仅修改渲染方式)
|
|
|
|
|
|
customerGroupedList() {
|
|
|
|
|
|
if (!this.orderDetailList || this.orderDetailList.length === 0) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const customerMap = new Map();
|
|
|
|
|
|
// 遍历订单,按客户分组
|
|
|
|
|
|
this.orderDetailList.forEach(order => {
|
|
|
|
|
|
// 跳过无效订单
|
|
|
|
|
|
if (!order.customerCode && !order.companyName) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 生成唯一客户Key
|
|
|
|
|
|
const customerKey = `${order.customerCode || 'unknown_code'}_${order.companyName || 'unknown_company'}`;
|
|
|
|
|
|
// 转换订单金额为数字
|
|
|
|
|
|
const orderAmountNum = Number(order.orderAmount) || 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化客户信息
|
|
|
|
|
|
if (!customerMap.has(customerKey)) {
|
|
|
|
|
|
customerMap.set(customerKey, {
|
|
|
|
|
|
customerKey,
|
|
|
|
|
|
companyName: order.companyName || '未知公司',
|
|
|
|
|
|
customerCode: order.customerCode || '未知编码',
|
|
|
|
|
|
customerLevel: order.customerLevel || '未知等级',
|
|
|
|
|
|
industry: order.industry || '未知行业',
|
|
|
|
|
|
totalOrderAmount: 0,
|
|
|
|
|
|
orderCount: 0,
|
|
|
|
|
|
orderList: []
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新客户统计数据和订单列表
|
|
|
|
|
|
const customerItem = customerMap.get(customerKey);
|
|
|
|
|
|
customerItem.orderCount += 1;
|
|
|
|
|
|
customerItem.totalOrderAmount += orderAmountNum;
|
|
|
|
|
|
if (order.orderCode) {
|
|
|
|
|
|
customerItem.orderList.push({ ...order });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为数组返回
|
|
|
|
|
|
return Array.from(customerMap.values());
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
// 分页大小改变,通知父组件
|
|
|
|
|
|
handleSizeChange(val) {
|
|
|
|
|
|
this.$emit("size-change", val);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 当前页改变,通知父组件
|
|
|
|
|
|
handleCurrentChange(val) {
|
|
|
|
|
|
this.$emit("current-change", val);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 表格勾选,通知父组件
|
|
|
|
|
|
handleSelectionChange(val) {
|
|
|
|
|
|
this.$emit("selection-change", val);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 导出订单,通知父组件
|
|
|
|
|
|
handleExport() {
|
|
|
|
|
|
this.$emit("export-order");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.order-detail-container {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
|
|
|
|
|
|
padding-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-toolbar {
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-tabs {
|
|
|
|
|
|
margin: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 无数据提示样式 */
|
|
|
|
|
|
.empty-tip {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 30px 0;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 客户分组折叠面板样式 */
|
|
|
|
|
|
.customer-collapse {
|
|
|
|
|
|
--el-collapse-item-content-padding: 0;
|
|
|
|
|
|
--el-collapse-item-border-width: 1px 0 0 0;
|
|
|
|
|
|
--el-collapse-border-color: #ebeef5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 折叠面板标题样式 */
|
|
|
|
|
|
.collapse-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 15px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.collapse-title .company-name {
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #1f2d3d;
|
|
|
|
|
|
min-width: 150px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.collapse-title .customer-info {
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
min-width: 120px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.collapse-title .summary-info {
|
|
|
|
|
|
color: #e6a23c;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 客户订单表格样式 */
|
|
|
|
|
|
.customer-order-table {
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
padding-bottom: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 优化折叠面板和表格样式 */
|
|
|
|
|
|
:deep(.el-collapse-item__header) {
|
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-collapse-item__content) {
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
border-top: 1px solid #ebeef5 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table__empty-block) {
|
|
|
|
|
|
min-height: 80px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|