refactor: frontend 重命名为 backend-adminend,新增 shccd159/shjjy153 配置

- frontend 目录迁移至 backend-adminend(管理后台前端)
- 新增 application-shccd159.yml、application-shjjy153.yml
- 更新 deploy.conf、DEPLOY.md、application.yml

Made-with: Cursor
This commit is contained in:
apple
2026-03-16 09:33:54 +08:00
parent 0cd7ebe202
commit 4b0afb3951
738 changed files with 292 additions and 89 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
<template>
<div>
<el-dialog title="订单信息" :visible.sync="dialogVisible" width="700px" v-if="orderDatalist">
<div class="description" v-loading="loading">
<div class="title">用户信息</div>
<div class="acea-row">
<div class="description-term">用户昵称{{ orderDatalist.nikeName }}</div>
<div class="description-term">绑定电话{{ orderDatalist.phone ? orderDatalist.phone : '无' }}</div>
</div>
<el-divider></el-divider>
<div class="title">{{ orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货信息' : '收货信息' }}</div>
<div class="acea-row">
<div class="description-term">
{{ orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货人' : '收货人' }}{{ orderDatalist.realName }}
</div>
<div class="description-term">
{{ orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货电话' : '收货电话' }}{{
orderDatalist.userPhone
}}
</div>
<div class="description-term" v-if="orderDatalist.statusStr.key !== 'toBeWrittenOff'">
{{ orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货地址' : '收货地址' }}{{
orderDatalist.userAddress
}}
</div>
</div>
<el-divider></el-divider>
<div class="title">订单信息</div>
<div class="acea-row">
<div class="description-term">订单编号{{ orderDatalist.orderId }}</div>
<div class="description-term" style="color: red">订单状态{{ orderDatalist.statusStr.value }}</div>
<div class="description-term">商品总数{{ orderDatalist.totalNum }}</div>
<div class="description-term">商品总价{{ orderDatalist.proTotalPrice }}</div>
<div class="description-term">支付邮费{{ orderDatalist.payPostage }}</div>
<div class="description-term">优惠券金额{{ orderDatalist.couponPrice }}</div>
<div class="description-term">实际支付{{ orderDatalist.payPrice }}</div>
<div class="description-term">抵扣金额{{ orderDatalist.deductionPrice }}</div>
<div class="description-term fontColor3" v-if="orderDatalist.refundPrice">
退款金额{{ orderDatalist.refundPrice }}
</div>
<div class="description-term" v-if="orderDatalist.useIntegral">使用积分{{ orderDatalist.useIntegral }}</div>
<div class="description-term" v-if="orderDatalist.backIntegral">
退回积分{{ orderDatalist.backIntegral }}
</div>
<div class="description-term">创建时间{{ orderDatalist.createTime }}</div>
<div class="description-term" v-if="orderDatalist.refundReasonTime">
退款时间{{ orderDatalist.refundReasonTime }}
</div>
<div class="description-term">支付方式{{ orderDatalist.payTypeStr }}</div>
<div class="description-term">推广人{{ orderDatalist.spreadName | filterEmpty }}</div>
<div class="description-term"
v-if="orderDatalist.shippingType === 2 && orderDatalist.statusStr.key === 'notShipped'">
门店名称{{ orderDatalist.storeName }}
</div>
<div class="description-term"
v-if="orderDatalist.shippingType === 2 && orderDatalist.statusStr.key === 'notShipped'">
核销码{{ orderDatalist.user_phone }}
</div>
<div class="description-term">商家备注{{ orderDatalist.remark }}</div>
<template v-if="orderDatalist.statusStr.key === 'toBeWrittenOff' && orderDatalist.systemStore">
<div class="description-term">提货码{{ orderDatalist.verifyCode }}</div>
<div class="description-term">门店名称{{ orderDatalist.systemStore.name }}</div>
<div class="description-term">门店电话{{ orderDatalist.systemStore.phone }}</div>
<div class="description-term">
门店地址{{ orderDatalist.systemStore.address + orderDatalist.systemStore.detailedAddress }}
</div>
</template>
<div class="description-term acea-row pz-box" v-if="refundReasonWapImg.length">
<div>退款凭证</div>
<div>
<el-image class="pz-img" :src="item" alt="" v-for="(item, index) in refundReasonWapImg" :key="index"
:preview-src-list="refundReasonWapImg"></el-image>
</div>
</div>
</div>
<template v-if="orderDatalist.deliveryType === 'express'">
<el-divider></el-divider>
<div class="title">物流信息</div>
<div class="acea-row">
<div class="description-term">快递公司{{ orderDatalist.deliveryName }}</div>
<div class="description-term">
快递单号{{ orderDatalist.deliveryId }}
<el-button type="primary" @click="openLogistics" style="margin-left: 5px"
v-hasPermi="['admin:order:logistics:info']">物流查询</el-button>
</div>
</div>
</template>
<template v-if="orderDatalist.deliveryType === 'send'">
<el-divider></el-divider>
<div class="title">配送信息</div>
<div class="acea-row">
<div class="description-term">送货人姓名{{ orderDatalist.deliveryName }}</div>
<div class="description-term">送货人电话{{ orderDatalist.deliveryId }}</div>
</div>
</template>
<template v-if="orderDatalist.mark">
<el-divider></el-divider>
<div class="title">用户备注</div>
<div class="acea-row">
<div class="description-term">{{ orderDatalist.mark }}</div>
</div>
</template>
</div>
</el-dialog>
<el-dialog v-if="orderDatalist" title="提示" :visible.sync="modal2" width="30%">
<div class="logistics acea-row row-top">
<div class="logistics_img"><img src="@/assets/imgs/expressi.jpg" /></div>
<div class="logistics_cent">
<span class="mb10">物流公司{{ orderDatalist.deliveryName }}</span>
<span>物流单号{{ orderDatalist.deliveryId }}</span>
</div>
</div>
<div class="acea-row row-column-around trees-coadd">
<div class="scollhide">
<el-timeline :reverse="reverse">
<el-timeline-item v-for="(item, i) in result" :key="i">
<p class="time" v-text="item.time"></p>
<p class="content" v-text="item.status"></p>
</el-timeline-item>
</el-timeline>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="modal2 = false">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
0;
import { orderDetailApi, getLogisticsInfoApi } from '@/api/order';
export default {
name: 'OrderDetail',
props: {
orderId: {
type: String,
default: 0,
},
},
data() {
return {
reverse: true,
dialogVisible: false,
orderDatalist: null,
loading: false,
modal2: false,
result: [],
refundReasonWapImg: [],
};
},
mounted() { },
methods: {
openLogistics() {
this.getOrderData();
this.modal2 = true;
},
// 获取订单物流信息
getOrderData() {
getLogisticsInfoApi({ orderNo: this.orderId }).then(async (res) => {
this.result = res.list;
});
},
getDetail(id) {
this.loading = true;
orderDetailApi({ orderNo: id })
.then((res) => {
this.orderDatalist = res;
res.refundReasonWapImg ?
this.refundReasonWapImg = res.refundReasonWapImg.split(',')
:
this.refundReasonWapImg = []
this.loading = false;
})
.catch(() => {
this.orderDatalist = null;
this.loading = false;
});
},
},
};
</script>
<style scoped lang="scss">
.logistics {
align-items: center;
padding: 10px 0px;
.logistics_img {
width: 45px;
height: 45px;
margin-right: 12px;
img {
width: 100%;
height: 100%;
}
}
.logistics_cent {
span {
display: block;
font-size: 12px;
}
}
}
.trees-coadd {
width: 100%;
height: 400px;
border-radius: 4px;
overflow: hidden;
.scollhide {
width: 100%;
height: 100%;
overflow: auto;
margin-left: 18px;
padding: 10px 0 10px 0;
box-sizing: border-box;
.content {
font-size: 12px;
}
.time {
font-size: 12px;
color: #2d8cf0;
}
}
}
.title {
margin-bottom: 14px;
color: #303133;
font-weight: 500;
font-size: 14px;
}
.description {
&-term {
display: table-cell;
padding-bottom: 5px;
line-height: 20px;
width: 50%;
font-size: 12px;
color: #606266;
}
::v-deep .el-divider--horizontal {
margin: 12px 0 !important;
}
}
.pz-box {
align-items: center;
}
.pz-img {
width: 50px;
height: 50px;
}
</style>

View File

@@ -0,0 +1,629 @@
<template>
<div class="print-container">
<div class="print-content" v-loading="loading">
<div class="print-header">
<h1>订单详情</h1>
<div class="print-actions no-print">
<el-button type="primary" icon="el-icon-printer" @click="handlePrint">打印</el-button>
<el-button icon="el-icon-document-copy" @click="handleCopy">复制订单信息</el-button>
</div>
</div>
<template v-if="orderDatalist">
<!-- 用户信息 -->
<div class="section">
<div class="section-title">用户信息</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">用户昵称</span>
<span class="value">{{ orderDatalist.nikeName }}</span>
</div>
<div class="info-item">
<span class="label">绑定电话</span>
<span class="value">{{ orderDatalist.phone || '无' }}</span>
</div>
</div>
</div>
</div>
<!-- 收货信息 -->
<div class="section">
<div class="section-title">{{ orderDatalist.statusStr && orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货信息' : '收货信息' }}</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">{{ orderDatalist.statusStr && orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货人:' : '收货人:' }}</span>
<span class="value">{{ orderDatalist.realName }}</span>
</div>
<div class="info-item">
<span class="label">{{ orderDatalist.statusStr && orderDatalist.statusStr.key === 'toBeWrittenOff' ? '提货电话:' : '收货电话:' }}</span>
<span class="value">{{ orderDatalist.userPhone }}</span>
</div>
</div>
<div class="info-row" v-if="orderDatalist.statusStr && orderDatalist.statusStr.key !== 'toBeWrittenOff'">
<div class="info-item full-width">
<span class="label">收货地址</span>
<span class="value">{{ orderDatalist.userAddress }}</span>
</div>
</div>
</div>
</div>
<!-- 订单信息 -->
<div class="section">
<div class="section-title">订单信息</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">订单编号</span>
<span class="value">{{ orderDatalist.orderId }}</span>
</div>
<div class="info-item">
<span class="label">订单状态</span>
<span class="value status">{{ orderDatalist.statusStr ? orderDatalist.statusStr.value : '' }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">商品总数</span>
<span class="value">{{ orderDatalist.totalNum }}</span>
</div>
<div class="info-item">
<span class="label">商品总价</span>
<span class="value">{{ orderDatalist.proTotalPrice }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">支付邮费</span>
<span class="value">{{ orderDatalist.payPostage }}</span>
</div>
<div class="info-item">
<span class="label">优惠券金额</span>
<span class="value">{{ orderDatalist.couponPrice }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">实际支付</span>
<span class="value highlight">{{ orderDatalist.payPrice }}</span>
</div>
<div class="info-item">
<span class="label">抵扣金额</span>
<span class="value">{{ orderDatalist.deductionPrice }}</span>
</div>
</div>
<div class="info-row" v-if="orderDatalist.refundPrice">
<div class="info-item">
<span class="label">退款金额</span>
<span class="value refund">{{ orderDatalist.refundPrice }}</span>
</div>
</div>
<div class="info-row" v-if="orderDatalist.useIntegral">
<div class="info-item">
<span class="label">使用积分</span>
<span class="value">{{ orderDatalist.useIntegral }}</span>
</div>
<div class="info-item" v-if="orderDatalist.backIntegral">
<span class="label">退回积分</span>
<span class="value">{{ orderDatalist.backIntegral }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">创建时间</span>
<span class="value">{{ orderDatalist.createTime }}</span>
</div>
<div class="info-item">
<span class="label">支付方式</span>
<span class="value">{{ orderDatalist.payTypeStr }}</span>
</div>
</div>
<div class="info-row" v-if="orderDatalist.refundReasonTime">
<div class="info-item">
<span class="label">退款时间</span>
<span class="value">{{ orderDatalist.refundReasonTime }}</span>
</div>
</div>
<div class="info-row" v-if="orderDatalist.remark">
<div class="info-item full-width">
<span class="label">商家备注</span>
<span class="value">{{ orderDatalist.remark }}</span>
</div>
</div>
</div>
</div>
<!-- 商品列表 -->
<div class="section" v-if="orderDatalist.orderInfoList && orderDatalist.orderInfoList.length">
<div class="section-title">商品列表</div>
<div class="section-content">
<table class="product-table">
<thead>
<tr>
<th class="col-image">商品图片</th>
<th>商品名称</th>
<th>规格</th>
<th>单价</th>
<th>数量</th>
<th v-if="hasGiveIntegral">赠送积分</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in orderDatalist.orderInfoList" :key="index">
<td class="col-image">
<img v-if="item.info && item.info.image" :src="item.info.image" class="product-thumb" alt="" />
<span v-else>-</span>
</td>
<td>
<div>{{ item.info ? item.info.productName : '-' }}</div>
<div class="product-id" v-if="item.productId">编号{{ item.productId }}</div>
</td>
<td>{{ item.info && item.info.sku ? item.info.sku : '-' }}</td>
<td>{{ item.info ? item.info.price : '-' }}</td>
<td>{{ item.info ? item.info.payNum : '-' }}</td>
<td v-if="hasGiveIntegral">{{ item.info && item.info.giveIntegral != null ? item.info.giveIntegral : '-' }}</td>
<td>{{ item.info ? (item.info.price * item.info.payNum).toFixed(2) : '-' }}</td>
</tr>
</tbody>
<tfoot>
<tr class="summary-row">
<td class="col-image"></td>
<td colspan="2">合计</td>
<td></td>
<td>{{ totalPayNum }}</td>
<td v-if="hasGiveIntegral"></td>
<td>{{ orderDatalist.proTotalPrice }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- 物流信息 -->
<div class="section" v-if="orderDatalist.deliveryType === 'express'">
<div class="section-title">物流信息</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">快递公司</span>
<span class="value">{{ orderDatalist.deliveryName }}</span>
</div>
<div class="info-item">
<span class="label">快递单号</span>
<span class="value">{{ orderDatalist.deliveryId }}</span>
</div>
</div>
</div>
</div>
<!-- 配送信息 -->
<div class="section" v-if="orderDatalist.deliveryType === 'send'">
<div class="section-title">配送信息</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">送货人姓名</span>
<span class="value">{{ orderDatalist.deliveryName }}</span>
</div>
<div class="info-item">
<span class="label">送货人电话</span>
<span class="value">{{ orderDatalist.deliveryId }}</span>
</div>
</div>
</div>
</div>
<!-- 用户备注 -->
<div class="section" v-if="orderDatalist.mark">
<div class="section-title">用户备注</div>
<div class="section-content">
<div class="info-row">
<div class="info-item full-width">
<span class="value">{{ orderDatalist.mark }}</span>
</div>
</div>
</div>
</div>
<!-- 提货信息 -->
<div class="section" v-if="orderDatalist.statusStr && orderDatalist.statusStr.key === 'toBeWrittenOff' && orderDatalist.systemStore">
<div class="section-title">门店信息</div>
<div class="section-content">
<div class="info-row">
<div class="info-item">
<span class="label">提货码</span>
<span class="value highlight">{{ orderDatalist.verifyCode }}</span>
</div>
<div class="info-item">
<span class="label">门店名称</span>
<span class="value">{{ orderDatalist.systemStore.name }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">门店电话</span>
<span class="value">{{ orderDatalist.systemStore.phone }}</span>
</div>
<div class="info-item full-width">
<span class="label">门店地址</span>
<span class="value">{{ orderDatalist.systemStore.address + orderDatalist.systemStore.detailedAddress }}</span>
</div>
</div>
</div>
</div>
</template>
<div v-else-if="!loading" class="empty-tip">
<p>未找到订单信息</p>
</div>
</div>
</div>
</template>
<script>
import { orderDetailApi } from '@/api/order';
export default {
name: 'OrderDetailPrint',
data() {
return {
loading: false,
orderDatalist: null,
orderId: '',
};
},
computed: {
hasGiveIntegral() {
const list = this.orderDatalist && this.orderDatalist.orderInfoList;
if (!list || !list.length) return false;
return list.some((item) => item.info && (item.info.giveIntegral != null && item.info.giveIntegral !== ''));
},
totalPayNum() {
const list = this.orderDatalist && this.orderDatalist.orderInfoList;
if (!list || !list.length) return 0;
return list.reduce((sum, item) => sum + (item.info && item.info.payNum ? Number(item.info.payNum) : 0), 0);
},
},
created() {
this.orderId = this.$route.query.orderId || this.$route.params.orderId;
if (this.orderId) {
this.getDetail();
}
},
methods: {
// 获取订单详情
getDetail() {
this.loading = true;
orderDetailApi({ orderNo: this.orderId })
.then((res) => {
// 后端返回 orderInfo组件使用 orderInfoList统一为 orderInfoList
const list = Array.isArray(res.orderInfo) ? res.orderInfo : (res.orderInfoList || []);
this.orderDatalist = { ...res, orderInfoList: list };
this.loading = false;
})
.catch(() => {
this.orderDatalist = null;
this.loading = false;
});
},
// 打印
handlePrint() {
window.print();
},
// 复制订单信息
handleCopy() {
const orderInfo = this.formatOrderInfo();
this.copyToClipboard(orderInfo);
},
// 格式化订单信息为文本
formatOrderInfo() {
if (!this.orderDatalist) return '';
const data = this.orderDatalist;
const isPickup = data.statusStr && data.statusStr.key === 'toBeWrittenOff';
let text = `【订单详情】\n`;
text += `=====================================\n`;
text += `\n【用户信息】\n`;
text += `用户昵称:${data.nikeName || ''}\n`;
text += `绑定电话:${data.phone || '无'}\n`;
text += `\n【${isPickup ? '提货' : '收货'}信息】\n`;
text += `${isPickup ? '提货人' : '收货人'}${data.realName || ''}\n`;
text += `${isPickup ? '提货电话' : '收货电话'}${data.userPhone || ''}\n`;
if (!isPickup) {
text += `收货地址:${data.userAddress || ''}\n`;
}
text += `\n【订单信息】\n`;
text += `订单编号:${data.orderId || ''}\n`;
text += `订单状态:${data.statusStr ? data.statusStr.value : ''}\n`;
text += `商品总数:${data.totalNum || 0}\n`;
text += `商品总价:¥${data.proTotalPrice || 0}\n`;
text += `支付邮费:¥${data.payPostage || 0}\n`;
text += `优惠券金额:¥${data.couponPrice || 0}\n`;
text += `实际支付:¥${data.payPrice || 0}\n`;
text += `抵扣金额:¥${data.deductionPrice || 0}\n`;
if (data.refundPrice) {
text += `退款金额:¥${data.refundPrice}\n`;
}
if (data.useIntegral) {
text += `使用积分:${data.useIntegral}\n`;
}
if (data.backIntegral) {
text += `退回积分:${data.backIntegral}\n`;
}
text += `创建时间:${data.createTime || ''}\n`;
if (data.refundReasonTime) {
text += `退款时间:${data.refundReasonTime}\n`;
}
text += `支付方式:${data.payTypeStr || ''}\n`;
if (data.remark) {
text += `商家备注:${data.remark}\n`;
}
// 商品列表
if (data.orderInfoList && data.orderInfoList.length) {
text += `\n【商品列表】\n`;
data.orderInfoList.forEach((item, index) => {
if (item.info) {
let line = `${index + 1}. ${item.info.productName || ''}`;
if (item.productId) line += ` 编号:${item.productId}`;
line += ` | ${item.info.sku || '-'} | ¥${item.info.price || 0} x ${item.info.payNum || 0}`;
if (item.info.giveIntegral != null && item.info.giveIntegral !== '') {
line += ` | 赠送积分:${item.info.giveIntegral}`;
}
text += line + '\n';
}
});
}
// 物流信息
if (data.deliveryType === 'express') {
text += `\n【物流信息】\n`;
text += `快递公司:${data.deliveryName || ''}\n`;
text += `快递单号:${data.deliveryId || ''}\n`;
}
// 配送信息
if (data.deliveryType === 'send') {
text += `\n【配送信息】\n`;
text += `送货人姓名:${data.deliveryName || ''}\n`;
text += `送货人电话:${data.deliveryId || ''}\n`;
}
// 用户备注
if (data.mark) {
text += `\n【用户备注】\n`;
text += `${data.mark}\n`;
}
// 门店信息
if (isPickup && data.systemStore) {
text += `\n【门店信息】\n`;
text += `提货码:${data.verifyCode || ''}\n`;
text += `门店名称:${data.systemStore.name || ''}\n`;
text += `门店电话:${data.systemStore.phone || ''}\n`;
text += `门店地址:${(data.systemStore.address || '') + (data.systemStore.detailedAddress || '')}\n`;
}
text += `\n=====================================`;
return text;
},
// 复制到剪贴板
copyToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(() => {
this.$message.success('订单信息已复制到剪贴板');
}).catch(() => {
this.fallbackCopy(text);
});
} else {
this.fallbackCopy(text);
}
},
// 兼容复制方法
fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
this.$message.success('订单信息已复制到剪贴板');
} catch (err) {
this.$message.error('复制失败,请手动复制');
}
document.body.removeChild(textarea);
},
},
};
</script>
<style scoped lang="scss">
.print-container {
min-height: 100vh;
background: #f5f5f5;
padding: 20px;
box-sizing: border-box;
}
.print-content {
max-width: 800px;
margin: 0 auto;
background: #fff;
padding: 30px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.print-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #409eff;
h1 {
margin: 0;
font-size: 24px;
color: #303133;
}
}
.section {
margin-bottom: 24px;
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-left: 10px;
border-left: 3px solid #409eff;
}
.section-content {
padding: 15px;
background: #fafafa;
border-radius: 4px;
}
}
.info-row {
display: flex;
flex-wrap: wrap;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
.info-item {
width: 50%;
padding: 4px 0;
font-size: 14px;
color: #606266;
&.full-width {
width: 100%;
}
.label {
color: #909399;
}
.value {
color: #303133;
&.status {
color: #e6a23c;
font-weight: 500;
}
&.highlight {
color: #409eff;
font-weight: 600;
}
&.refund {
color: #f56c6c;
font-weight: 500;
}
}
}
.product-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
th, td {
padding: 10px 8px;
text-align: left;
border-bottom: 1px solid #ebeef5;
}
th {
background: #f5f7fa;
color: #909399;
font-weight: 500;
}
td {
color: #606266;
}
tr:last-child td {
border-bottom: none;
}
.col-image {
width: 70px;
text-align: center;
}
.product-thumb {
width: 50px;
height: 50px;
object-fit: cover;
vertical-align: middle;
}
.product-id {
font-size: 12px;
color: #909399;
margin-top: 2px;
}
.summary-row td {
font-weight: 600;
background: #f5f7fa;
border-top: 2px solid #ebeef5;
}
}
.empty-tip {
text-align: center;
padding: 60px 0;
color: #909399;
font-size: 14px;
}
/* 打印样式 */
@media print {
.no-print {
display: none !important;
}
.print-container {
background: #fff;
padding: 0;
}
.print-content {
box-shadow: none;
max-width: 100%;
padding: 0;
}
.section-content {
background: #fff !important;
border: 1px solid #eee;
}
.print-header {
border-bottom: 2px solid #333;
}
.section .section-title {
border-left-color: #333;
}
}
</style>

View File

@@ -0,0 +1,518 @@
<template>
<el-dialog
:close-on-click-modal="false"
:visible.sync="modals"
title="发送货"
class="order_box"
:before-close="handleClose"
width="600px"
>
<el-form
ref="formItem"
v-loading="loading"
:model="formItem"
label-width="130px"
@submit.native.prevent
:rules="rules"
>
<el-form-item label="选择类型:">
<el-radio-group
v-model="formItem.deliveryType"
:disabled="isEdit"
@change="changeRadioType(formItem.deliveryType)"
required
>
<el-radio label="express">发货</el-radio>
<el-radio label="send">送货</el-radio>
<el-radio label="fictitious">虚拟</el-radio>
</el-radio-group>
</el-form-item>
<!--发货-->
<div v-if="formItem.deliveryType === 'express'">
<el-form-item label="发货类型:">
<el-radio-group
:disabled="isEdit"
v-model="formItem.expressRecordType"
@change="changeRadio(formItem.expressRecordType)"
>
<el-radio label="3">商家寄件</el-radio>
<el-radio label="1">手动填写</el-radio>
<el-radio label="2" v-if="checkPermi(['admin:order:sheet:info'])">电子面单打印</el-radio>
</el-radio-group>
</el-form-item>
<!--商家寄件-->
<template v-if="formItem.expressRecordType === '3'">
<el-form-item label="寄件人姓名:" prop="shipment.sendRealName">
<el-input
v-model="formItem.shipment.sendRealName"
placeholder="请输入寄件人姓名"
style="width: 80%"
></el-input>
</el-form-item>
<el-form-item label="寄件人电话:" prop="shipment.sendPhone">
<el-input
v-model="formItem.shipment.sendPhone"
placeholder="请输入寄件人电话"
style="width: 80%"
></el-input>
</el-form-item>
<el-form-item label="寄件人地址:" prop="shipment.sendAddress">
<el-input
v-model="formItem.shipment.sendAddress"
placeholder="请输入寄件人地址"
style="width: 80%"
></el-input>
</el-form-item>
<el-form-item label="快递公司:" prop="shipment.kuaidicom">
<el-select
value-key="id"
v-model="formItem.shipment.kuaidicom"
filterable
style="width: 80%"
@change="onChangeShipmentExpress"
>
<el-option
v-for="(item, i) in shipmentExpress"
:value="item.value"
:key="item.id"
:label="item.label"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="快递业务类型:" prop="shipment.serviceType">
<el-select
v-model="formItem.shipment.serviceType"
filterable
placeholder="请选择业务类型"
style="width: 60%"
>
<el-option v-for="item in serviceTypeList" :value="item" :key="item">{{ item }}</el-option>
</el-select>
</el-form-item>
<el-form-item label="电子面单:" class="express_temp_id" prop="shipment.tempid">
<div class="acea-row">
<el-select
v-model="formItem.shipment.tempid"
placeholder="请选择电子面单"
:class="[formItem.shipment.tempid ? 'width9' : 'width8']"
@change="onChangeExpressTempId"
>
<el-option
v-for="(item, i) in expressTempIdList"
:value="item.temp_id"
:key="i"
:label="item.temp_id"
></el-option>
</el-select>
<div v-if="formItem.shipment.tempid" style="position: relative">
<div class="tempImgList ml10">
<div class="demo-image__preview">
<el-image
style="width: 36px; height: 36px"
:src="expressTempIdImg"
:preview-src-list="[expressTempIdImg]"
/>
</div>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="取件日期:">
<el-radio-group v-model="formItem.shipment.dayType" type="button">
<el-radio :label="0">今天</el-radio>
<el-radio :label="1">明天</el-radio>
<el-radio :label="2">后天</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="取件时间:">
<el-time-picker
is-range
format="HH:mm"
v-model="pickupTime"
value-format="HH:mm"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
@change="onchangeTime"
/>
</el-form-item>
</template>
<!--手动填写电子面单打印-->
<template v-else>
<el-form-item v-model="nowCompany" label="快递公司:" prop="company">
<el-select
value-key="code"
v-model="formItem.company"
filterable
style="width: 80%"
@change="onChangeExport"
>
<el-option v-for="(item, i) in express" :value="item" :key="item.code" :label="item.name"></el-option>
</el-select>
</el-form-item>
</template>
<!--手动填写-->
<el-form-item v-if="formItem.expressRecordType === '1'" label="快递单号:" prop="expressNumber">
<el-input v-model="formItem.expressNumber" placeholder="请输入快递单号" style="width: 80%"></el-input>
</el-form-item>
<!--电子面单打印-->
<template v-if="formItem.expressRecordType === '2'">
<el-form-item label="电子面单:" class="express_temp_id" prop="expressTempId">
<div class="acea-row">
<el-select
v-model="formItem.expressTempId"
placeholder="请选择电子面单"
:class="[formItem.expressTempId ? 'width9' : 'width8']"
@change="onChangeImg"
>
<el-option
v-for="(item, i) in exportTempList"
:value="item.temp_id"
:key="i"
:label="item.title"
></el-option>
</el-select>
<div v-if="formItem.expressTempId" style="position: relative">
<div class="tempImgList ml10">
<div class="demo-image__preview">
<el-image style="width: 36px; height: 36px" :src="tempImg" :preview-src-list="[tempImg]" />
</div>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="寄件人姓名:" prop="toName">
<el-input v-model="formItem.toName" placeholder="请输入寄件人姓名" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="寄件人电话:" prop="toTel">
<el-input v-model="formItem.toTel" placeholder="请输入寄件人电话" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="寄件人地址:" prop="toAddr">
<el-input v-model="formItem.toAddr" placeholder="请输入寄件人地址" style="width: 80%"></el-input>
</el-form-item>
</template>
</div>
<!--送货-->
<div v-if="formItem.deliveryType === 'send'">
<el-form-item label="送货人姓名:" prop="deliveryName">
<el-input v-model="formItem.deliveryName" placeholder="请输入送货人姓名" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="送货人电话:" prop="deliveryTel">
<el-input v-model="formItem.deliveryTel" placeholder="请输入送货人电话" style="width: 80%"></el-input>
</el-form-item>
</div>
<div>
<el-form-item label="">
<div style="color: #cecece">顺丰请输入单号收件人或寄件人手机号后四位</div>
<div style="color: #cecece">例如SF000000000000:3941</div>
</el-form-item>
</div>
</el-form>
<div slot="footer">
<el-button type="primary" @click="putSend('formItem')">提交</el-button>
<el-button @click="cancel('formItem')">取消</el-button>
</div>
</el-dialog>
</template>
<script>
import { orderSendApi, sheetInfoApi, updateTrackingNumberApi } from '@/api/order';
import { expressAllApi, exportTempApi, shipmentExpressApi } from '@/api/sms';
import { checkPermi } from '@/utils/permission'; // 权限判断函数
import { Debounce } from '@/utils/validate';
const validatePhone = (rule, value, callback) => {
if (!value) {
return callback(new Error('请填写手机号'));
} else if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error('手机号格式不正确!'));
} else {
callback();
}
};
//修改引入打印扩展
import printJS from 'print-js';
export default {
name: 'orderSend',
props: {
orderId: {
type: String,
default: '',
},
expressListNormal: {
type: Array,
default: [],
},
expressListElec: {
type: Array,
default: [],
},
orderDetail: {
type: Object,
default: null,
},
},
data() {
return {
formItem: {
deliveryType: 'express',
expressRecordType: '3',
expressCode: '',
company: '', //快递公司
deliveryName: '',
deliveryTel: '',
expressName: '',
expressNumber: '',
expressTempId: '',
toAddr: '',
toName: '',
toTel: '',
orderNo: '',
shipment: {
sendRealName: '',
sendPhone: '',
sendAddress: '',
kuaidicom: '', //快递公司编码
serviceType: '', //快递业务类型
pickupStartTime: '', // 取件开始时间
pickupEndTime: '', // 取件结束时间
tempid: '', //电子面单模板id
dayType: 1, //时间
},
},
modals: false,
exportTempList: [],
tempImg: '',
rules: {
'shipment.sendRealName': [{ required: true, message: '请输入寄件人姓名', trigger: 'blur' }],
'shipment.sendPhone': [{ required: true, validator: validatePhone, trigger: 'blur' }],
'shipment.sendAddress': [{ required: true, message: '请输入寄件人地址', trigger: 'blur' }],
'shipment.kuaidicom': [{ required: true, message: '请选择快递公司', trigger: 'change' }],
'shipment.serviceType': [{ required: true, message: '请选择业务类型', trigger: 'change' }],
'shipment.tempid': [{ required: true, message: '请选择电子面单模板', trigger: 'change' }],
toName: [{ required: true, message: '请输寄件人姓名', trigger: 'blur' }],
toTel: [{ required: true, validator: validatePhone, trigger: 'blur' }],
toAddr: [{ required: true, message: '请输入寄件人地址', trigger: 'blur' }],
company: [{ required: true, message: '请选择快递公司', trigger: 'change' }],
expressNumber: [{ required: true, message: '请输入快递单号', trigger: 'blur' }],
expressTempId: [{ required: true, message: '请选择电子面单', trigger: 'change' }],
deliveryName: [{ required: true, message: '请输入送货人姓名', trigger: 'blur' }],
deliveryTel: [{ required: true, validator: validatePhone, trigger: 'blur' }],
},
expressType: 'normal',
loading: false,
express: [], //物流公司
isEdit: false, //是否是编辑
shipmentExpress: [], //一号通物流公司
serviceTypeList: [], //业务类型
expressTempIdList: [], //商家发货电子面单
expressTempIdImg: '', // 商家发货电子面单图片
pickupTime: ['', ''], // 取件时间
nowCompany: '',
};
},
watch: {
orderDetail: {
handler: function (val) {
if (val) {
this.loading = true;
this.isEdit = true;
this.getExpressDetail(val);
} else {
this.isEdit = false;
this.loading = false;
}
},
immediate: false,
deep: true,
},
},
mounted() {
this.express = this.expressListNormal;
if (checkPermi(['admin:pass:shipment:express'])) this.getShipmentExpress();
},
methods: {
checkPermi,
//一号通 商家寄件 快递列表
getShipmentExpress() {
shipmentExpressApi().then((res) => {
this.shipmentExpress = res.data;
});
},
// 取件时间选择
onchangeTime(e) {
this.formItem.shipment.pickupStartTime = e[0];
this.formItem.shipment.pickupEndTime = e[1];
},
//商家寄件选择快递公司获取电子面单列表
onChangeShipmentExpress(value) {
this.formItem.shipment.serviceType = '';
let expressItem = this.shipmentExpress.find((item) => {
return item.value === value;
});
if (expressItem === undefined) {
return;
}
this.serviceTypeList = expressItem.types; //业务类型
this.expressTempIdList = expressItem.list; //商家发货电子面单
},
//选择电子面单
onChangeExpressTempId(item) {
this.expressTempIdList.map((i) => {
if (i.temp_id === item) this.expressTempIdImg = i.pic;
});
},
//物流信息详情, 快递单号快递公司快递公司code
getExpressDetail(val) {
if (val.deliveryType === 'send') {
this.formItem.deliveryTel = val.deliveryId;
this.formItem.deliveryName = val.deliveryName;
} else {
this.formItem.expressName = val.deliveryName;
this.formItem.expressNumber = val.deliveryId;
}
this.formItem.deliveryType = val.deliveryType;
this.formItem.expressCode = val.deliveryCode;
this.formItem.expressRecordType = val.expressRecordType;
this.formItem.company = { code: val.deliveryCode, name: val.deliveryName };
this.loading = false;
},
// 默认信息
sheetInfo() {
sheetInfoApi().then(async (res) => {
this.formItem.toAddr = res.exportToAddress || '';
this.formItem.toName = res.exportToName || '';
this.formItem.toTel = res.exportToTel || '';
});
},
// 快递公司选择
onChangeExport(val) {
this.formItem.expressCode = val.code;
this.formItem.expressName = val.name;
this.formItem.expressTempId = '';
if (this.formItem.expressRecordType === '2') this.exportTemp(val.code);
},
// 电子面单模板
exportTemp(code) {
exportTempApi({ com: code }).then(async (res) => {
this.exportTempList = res.data.data || [];
});
},
onChangeImg(item) {
this.exportTempList.map((i) => {
if (i.temp_id === item) this.tempImg = i.pic;
});
},
//选择类型
changeRadioType() {
if (this.formItem.deliveryType === 'fictitious') {
this.formItem.expressId = '';
this.formItem.expressCode = '';
}
},
//选择发货类型
changeRadio(o) {
if (o !== '3') {
if (o === '2') {
this.express = this.expressListElec;
} else {
this.express = this.expressListNormal;
}
//其他数据置空
this.formItem.shipment = {
sendRealName: '',
sendPhone: '',
sendAddress: '',
kuaidicom: '', //快递公司编码
serviceType: '', //快递业务类型
pickupStartTime: '', // 取件开始时间
pickupEndTime: '', // 取件结束时间
tempid: '', //电子面单模板id
dayType: 1, //时间
};
} else {
//其他数据置空
this.formItem.deliveryName = '';
this.formItem.expressCode = '';
this.formItem.expressName = '';
this.formItem.expressTempId = '';
this.formItem.expressNumber = '';
}
},
// 提交
putSend: Debounce(function (name) {
// 打印测试
//this.printImg("http://api.kuaidi100.com/label/getImage/20230505/FBA3DFCE5C684CB9A13DADA8EE8357FB");
// 正常业务中使用;
this.formItem.orderNo = this.orderId;
this.$refs[name].validate((valid) => {
if (valid) {
!this.isEdit
? orderSendApi(this.formItem).then((data) => {
// data -》 label是一个网络图片地址直接打印即可
if (this.formItem.expressRecordType === '2') this.printImg(data.label);
this.$message.success('发送货成功');
this.modals = false;
this.$refs[name].resetFields();
this.$emit('submitFail');
})
: updateTrackingNumberApi(this.formItem).then((data) => {
this.$message.success('修改快递单号成功');
this.modals = false;
this.$refs[name].resetFields();
this.$emit('submitFail');
});
} else {
this.$message.error('请填写信息');
}
});
}),
handleClose() {
this.cancel('formItem');
},
cancel(name) {
this.modals = false;
this.$refs[name].resetFields();
this.formItem.deliveryType = 'express';
this.formItem.expressRecordType = '3';
},
//修改增加打印方法
printImg(url) {
printJS({
printable: url,
type: 'image',
documentTitle: '快递信息',
style: `img{
width: 100%;
height: 476px;
}`,
});
},
},
};
</script>
<style scoped lang="scss">
.width8 {
width: 80%;
}
.width9 {
width: 70%;
}
.tempImgList {
// opacity: 1;
width: 38px !important;
height: 30px !important;
// margin-top: -30px;
cursor: pointer;
position: absolute;
z-index: 11;
img {
width: 38px !important;
height: 30px !important;
}
}
</style>

View File

@@ -0,0 +1,119 @@
<template>
<el-dialog :visible.sync="modals" title="发送货" class="order_box" :before-close="handleClose" width="600px">
<el-form ref="formItem" :model="formItem" label-width="110px" @submit.native.prevent :rules="rules">
<el-form-item label="快递公司:" prop="expressCode">
<el-select v-model="formItem.deliveryId" filterable style="width: 80%">
<el-option v-for="(item, i) in express" :value="item.deliveryId" :key="i"
:label="item.deliveryName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="快递单号:" prop="waybillId">
<el-input v-model="formItem.waybillId" placeholder="请输入快递单号" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button type="primary" @click="putSend('formItem')" v-hasPermi="['admin:order:video:send']">提交</el-button>
<el-button @click="cancel('formItem')">取消</el-button>
</div>
</el-dialog>
</template>
<script>
import { videoSendApi, sheetInfoApi, companyGetListApi } from '@/api/order';
import { Debounce } from '@/utils/validate';
const validatePhone = (rule, value, callback) => {
if (!value) {
return callback(new Error('请填写手机号'));
} else if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error('手机号格式不正确!'));
} else {
callback();
}
};
export default {
name: 'orderSend',
props: {
orderId: String,
},
data() {
return {
formItem: {
deliveryId: '',
orderNo: '',
waybillId: '',
},
modals: false,
express: [],
exportTempList: [],
tempImg: '',
rules: {
deliveryId: [{ required: true, message: '请选择快递公司', trigger: 'change' }],
waybillId: [{ required: true, message: '请输入快递单号', trigger: 'blur' }],
},
expressType: 'normal',
};
},
mounted() {
this.express = JSON.parse(sessionStorage.getItem('videoExpress'));
},
methods: {
// 视频号快递公司
companyGetList() {
companyGetListApi().then(async (res) => {
this.express = res;
sessionStorage.setItem('videoExpress', JSON.stringify(res));
});
},
// 提交
putSend: Debounce(function (name) {
this.formItem.orderNo = this.orderId;
this.$refs[name].validate((valid) => {
if (valid) {
videoSendApi(this.formItem).then((async) => {
this.$message.success('发送货成功');
this.modals = false;
this.$refs[name].resetFields();
this.$emit('submitFail');
});
} else {
this.$message.error('请填写信息');
}
});
}),
handleClose() {
this.cancel('formItem');
},
cancel(name) {
this.modals = false;
this.$refs[name].resetFields();
this.formItem.type = '1';
this.formItem.expressRecordType = '1';
},
},
};
</script>
<style scoped lang="scss">
.width8 {
width: 80%;
}
.width9 {
width: 70%;
}
.tempImgList {
// opacity: 1;
width: 38px !important;
height: 30px !important;
// margin-top: -30px;
cursor: pointer;
position: absolute;
z-index: 11;
img {
width: 38px !important;
height: 30px !important;
}
}
</style>