Files
MER-2.2_2601/mer_plat_admin/src/views/merchantOrder/orderPrint.vue
AriadenCaseblg 61c5d964a3 feat: 移植商户端订单管理到平台端 & 新增订单打印功能
1. 将商户端订单管理功能完整移植到平台端管理后台,包括:
   - 商户订单列表、退款单、预约管理(服务看板+工单管理)
   - 菜单名称加"商户"前缀,区别于平台端原有订单管理
   - 不影响平台端原有订单管理功能

2. 新增订单打印功能:
   - 前端:独立打印页面(无layout),支持浏览器打印
   - 后端:新增打印专用API,使用eb_sync_order_detail_staging表的product_name和info字段

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:16:27 +08:00

376 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="order-print-page" v-loading="loading">
<div class="print-header no-print">
<el-button type="primary" size="medium" icon="el-icon-printer" @click="handlePrint">打印</el-button>
<el-button size="medium" @click="goBack">返回</el-button>
</div>
<div class="print-content" v-if="orderData">
<div class="print-title">
<h2>订单详情</h2>
<p class="order-no">订单号{{ orderData.orderNo }}</p>
</div>
<!-- 订单基本信息 -->
<div class="print-section">
<div class="section-title">订单信息</div>
<table class="info-table">
<tr>
<td class="label">订单号</td>
<td>{{ orderData.orderNo }}</td>
<td class="label">下单时间</td>
<td>{{ orderData.createTime }}</td>
</tr>
<tr>
<td class="label">订单状态</td>
<td>{{ orderData.status | orderStatusFilter }}</td>
<td class="label">支付方式</td>
<td>{{ orderData.payType | payTypeFilter }}</td>
</tr>
<tr>
<td class="label">实际支付</td>
<td>¥ {{ orderData.payPrice || '0.00' }}</td>
<td class="label">支付时间</td>
<td>{{ orderData.payTime || '-' }}</td>
</tr>
</table>
</div>
<!-- 收货信息 -->
<div class="print-section" v-if="orderData.realName">
<div class="section-title">收货信息</div>
<table class="info-table">
<tr>
<td class="label">收货人</td>
<td>{{ orderData.realName }}</td>
<td class="label">联系电话</td>
<td>{{ orderData.userPhone }}</td>
</tr>
<tr>
<td class="label">收货地址</td>
<td colspan="3">{{ orderData.userAddress }}</td>
</tr>
</table>
</div>
<!-- 商品信息 -->
<div class="print-section">
<div class="section-title">商品信息</div>
<table class="goods-table">
<thead>
<tr>
<th style="width: 50px">序号</th>
<th>商品名称</th>
<th>商品详情</th>
<th style="width: 80px">规格</th>
<th style="width: 80px">单价</th>
<th style="width: 60px">数量</th>
<th style="width: 90px">小计</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in productList" :key="index">
<td class="center">{{ index + 1 }}</td>
<td>{{ item.productName }}</td>
<td>
<div class="product-info" v-if="item.info">
<template v-if="typeof parsedInfo(item.info) === 'object'">
<div v-for="(val, key) in parsedInfo(item.info)" :key="key" class="info-item">
<span class="info-key">{{ key }}:</span> {{ val }}
</div>
</template>
<template v-else>
{{ item.info }}
</template>
</div>
<span v-else>-</span>
</td>
<td class="center">{{ item.sku || '-' }}</td>
<td class="center">¥ {{ item.price }}</td>
<td class="center">{{ item.payNum }}</td>
<td class="center">¥ {{ (item.price * item.payNum).toFixed(2) }}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="right"><strong>合计</strong></td>
<td class="center"><strong>{{ totalNum }}</strong></td>
<td class="center"><strong>¥ {{ totalPrice }}</strong></td>
</tr>
</tfoot>
</table>
</div>
<!-- 买家备注 -->
<div class="print-section" v-if="orderData.userRemark">
<div class="section-title">买家留言</div>
<p class="remark-text">{{ orderData.userRemark }}</p>
</div>
<!-- 商家备注 -->
<div class="print-section" v-if="orderData.merchantRemark">
<div class="section-title">商家备注</div>
<p class="remark-text">{{ orderData.merchantRemark }}</p>
</div>
<!-- 页脚 -->
<div class="print-footer">
<p>打印时间{{ printTime }}</p>
</div>
</div>
<div v-if="!loading && !orderData" class="no-data">
<p>未找到订单数据</p>
<el-button type="primary" @click="goBack">返回</el-button>
</div>
</div>
</template>
<script>
import { merchantOrderPrintDetailApi } from '@/api/merchantOrder';
import { parseTime } from '@/utils';
export default {
name: 'MerchantOrderPrint',
data() {
return {
loading: true,
orderData: null,
productList: [],
printTime: '',
};
},
computed: {
totalNum() {
return this.productList.reduce((sum, item) => sum + (item.payNum || 0), 0);
},
totalPrice() {
return this.productList.reduce((sum, item) => sum + (item.price * item.payNum || 0), 0).toFixed(2);
},
},
filters: {
orderStatusFilter(status) {
const statusMap = {
0: '待支付',
1: '待发货',
2: '部分发货',
3: '待使用',
4: '待收货',
5: '已收货',
6: '已完成',
9: '已取消',
};
return statusMap[status] || '未知';
},
payTypeFilter(type) {
const typeMap = {
weixin: '微信支付',
alipay: '支付宝',
yue: '余额支付',
shoppingCredits: '购物金',
};
return typeMap[type] || type || '-';
},
},
created() {
const orderNo = this.$route.params.orderNo;
if (orderNo) {
this.getPrintDetail(orderNo);
} else {
this.loading = false;
}
this.printTime = parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}');
},
methods: {
getPrintDetail(orderNo) {
this.loading = true;
merchantOrderPrintDetailApi(orderNo)
.then((res) => {
this.orderData = res.orderInfo || {};
this.productList = res.detailList || [];
this.loading = false;
})
.catch(() => {
this.loading = false;
});
},
parsedInfo(info) {
if (!info) return '';
try {
return JSON.parse(info);
} catch (e) {
return info;
}
},
handlePrint() {
window.print();
},
goBack() {
window.close();
},
},
};
</script>
<style lang="scss" scoped>
.order-print-page {
background: #fff;
min-height: 100vh;
padding: 20px;
}
.print-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: #fff;
padding: 15px 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 100;
display: flex;
gap: 10px;
}
.print-content {
max-width: 800px;
margin: 80px auto 20px;
padding: 20px;
}
.print-title {
text-align: center;
margin-bottom: 30px;
h2 {
font-size: 22px;
margin-bottom: 8px;
color: #303133;
}
.order-no {
font-size: 14px;
color: #606266;
}
}
.print-section {
margin-bottom: 25px;
}
.section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
padding-bottom: 8px;
border-bottom: 2px solid #409eff;
margin-bottom: 12px;
}
.info-table {
width: 100%;
border-collapse: collapse;
td {
padding: 8px 12px;
font-size: 13px;
color: #606266;
border: 1px solid #ebeef5;
}
.label {
width: 100px;
font-weight: 500;
color: #303133;
background: #f5f7fa;
white-space: nowrap;
}
}
.goods-table {
width: 100%;
border-collapse: collapse;
th,
td {
padding: 8px 10px;
font-size: 13px;
border: 1px solid #ebeef5;
}
th {
background: #f5f7fa;
color: #303133;
font-weight: 500;
text-align: center;
}
.center {
text-align: center;
}
.right {
text-align: right;
}
tfoot td {
background: #f5f7fa;
}
}
.product-info {
.info-item {
font-size: 12px;
line-height: 1.6;
color: #909399;
}
.info-key {
color: #606266;
font-weight: 500;
}
}
.remark-text {
font-size: 13px;
color: #606266;
line-height: 1.6;
padding: 10px;
background: #f5f7fa;
border-radius: 4px;
}
.print-footer {
text-align: right;
margin-top: 30px;
padding-top: 15px;
border-top: 1px dashed #dcdfe6;
font-size: 12px;
color: #909399;
}
.no-data {
text-align: center;
padding: 100px 0;
p {
font-size: 16px;
color: #909399;
margin-bottom: 20px;
}
}
/* 打印样式 */
@media print {
.no-print {
display: none !important;
}
.print-content {
margin: 0 auto;
padding: 0;
}
.order-print-page {
padding: 10px;
}
.section-title {
border-bottom-color: #000;
}
.info-table .label {
background: #eee;
}
.goods-table th,
.goods-table tfoot td {
background: #eee;
}
}
</style>