mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-08-06 02:47:29 +08:00
feat(buyer): 添加电子卡券(E_COUPON)支持
- 新增 E_COUPON 商品类型,支持电子卡券的展示与购买逻辑 - 优化商品详情页,调整促销、库存和购买按钮逻辑以适应电子卡券 - 增加卡密信息展示与管理功能,支持商家端卡池管理 - 更新相关页面以处理电子卡券的特殊逻辑,如免地址、隐藏优惠券等
This commit is contained in:
@@ -44,7 +44,14 @@
|
||||
<div class="item-detail-title-row">
|
||||
<span class="item-detail-name">{{ skuDetail.goodsName }}</span>
|
||||
<el-tag
|
||||
v-if="skuDetail.goodsType !== 'VIRTUAL_GOODS'"
|
||||
v-if="isECouponGoods"
|
||||
class="goods-type-tag"
|
||||
size="small"
|
||||
>
|
||||
电子卡券
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else-if="skuDetail.goodsType !== 'VIRTUAL_GOODS'"
|
||||
class="goods-type-tag"
|
||||
size="small"
|
||||
>
|
||||
@@ -64,7 +71,7 @@
|
||||
</div>
|
||||
<!-- 限时秒杀 -->
|
||||
<Promotion
|
||||
v-if="promotionMap['SECKILL']"
|
||||
v-if="!isECouponGoods && promotionMap['SECKILL']"
|
||||
:time="promotionMap['SECKILL'].endTime"
|
||||
></Promotion>
|
||||
<!-- 商品详细 价格、优惠券、促销 -->
|
||||
@@ -73,7 +80,7 @@
|
||||
<!-- 秒杀价格 -->
|
||||
<div
|
||||
class="item-price-row"
|
||||
v-if="skuDetail.promotionPrice && promotionMap['SECKILL']"
|
||||
v-if="skuDetail.promotionPrice && !isECouponGoods && promotionMap['SECKILL']"
|
||||
>
|
||||
<p>
|
||||
<span class="item-price-title" v-if="promotionMap['SECKILL']"
|
||||
@@ -85,7 +92,7 @@
|
||||
<!-- 商品原价 -->
|
||||
<div class="item-price-row" v-else>
|
||||
<!-- 批发价格 -->
|
||||
<div v-if="wholesaleNum && wholesaleNum.length">
|
||||
<div v-if="!isECouponGoods && wholesaleNum && wholesaleNum.length">
|
||||
<div class="flex">
|
||||
<div class="item-price-title">
|
||||
价 格
|
||||
@@ -119,7 +126,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 优惠券展示 -->
|
||||
<div class="item-price-coupon-row" v-if="promotionMap['COUPON'].length">
|
||||
<div class="item-price-coupon-row" v-if="!isECouponGoods && promotionMap['COUPON'].length">
|
||||
<div class="Ellipsis">
|
||||
<span class="item-price-title">优 惠 券</span>
|
||||
<span>
|
||||
@@ -159,7 +166,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 满减展示 -->
|
||||
<div class="item-price-row" v-if="promotionMap['FULL_DISCOUNT']">
|
||||
<div class="item-price-row" v-if="!isECouponGoods && promotionMap['FULL_DISCOUNT']">
|
||||
<p>
|
||||
<span class="item-price-title"
|
||||
>促 销</span>
|
||||
@@ -238,14 +245,16 @@
|
||||
:precision="0"
|
||||
@blur="changeCount"
|
||||
></el-input-number>
|
||||
<span class="inventory"> 库存{{ skuDetail.quantity }}</span>
|
||||
<span class="inventory"> 库存{{ displayStock }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="item-select"
|
||||
v-if="
|
||||
skuDetail.goodsType !== 'VIRTUAL_GOODS' && skuDetail.weight !== 0
|
||||
!isECouponGoods &&
|
||||
skuDetail.goodsType !== 'VIRTUAL_GOODS' &&
|
||||
skuDetail.weight !== 0
|
||||
"
|
||||
>
|
||||
<div class="item-select-title">
|
||||
@@ -279,16 +288,16 @@
|
||||
<div class="add-buy-car">
|
||||
<el-button
|
||||
class="goods-action-btn"
|
||||
v-if="skuDetail.goodsType !== 'VIRTUAL_GOODS'"
|
||||
v-if="!isECouponGoods && skuDetail.goodsType !== 'VIRTUAL_GOODS'"
|
||||
:loading="loading"
|
||||
:disabled="skuDetail.quantity === 0"
|
||||
:disabled="isOutOfStock"
|
||||
@click="addShoppingCartBtn"
|
||||
>加入购物车</el-button
|
||||
>
|
||||
<el-button
|
||||
class="goods-action-btn"
|
||||
:loading="loading1"
|
||||
:disabled="skuDetail.quantity === 0"
|
||||
:disabled="isOutOfStock"
|
||||
@click="buyNow"
|
||||
>立即购买</el-button
|
||||
>
|
||||
@@ -320,6 +329,12 @@ import {
|
||||
} from "@/api/member.js";
|
||||
import { addCartGoods } from "@/api/cart.js";
|
||||
import playIcon from "@/assets/iconfont/play.svg";
|
||||
/** 卡密商品(E_COUPON)判定与库存工具,见 @/constants/goodsType */
|
||||
import {
|
||||
isECoupon,
|
||||
getECouponStock,
|
||||
getECouponMaxBuyNum,
|
||||
} from "@/constants/goodsType";
|
||||
|
||||
export default {
|
||||
name: "ShowGoods",
|
||||
@@ -397,11 +412,27 @@ export default {
|
||||
})
|
||||
: [];
|
||||
},
|
||||
/** 卡密商品:禁加购/禁促销,仅立即购买,库存用 poolStock(FR-B-01 / P-06) */
|
||||
isECouponGoods() {
|
||||
return isECoupon(this.skuDetail.goodsType);
|
||||
},
|
||||
displayStock() {
|
||||
if (this.isECouponGoods) {
|
||||
return getECouponStock(this.skuDetail);
|
||||
}
|
||||
return Number(this.skuDetail.quantity) || 0;
|
||||
},
|
||||
quantityMax() {
|
||||
if (this.isECouponGoods) {
|
||||
return getECouponMaxBuyNum(this.skuDetail);
|
||||
}
|
||||
const qty = Number(this.skuDetail.quantity) || 0;
|
||||
return qty > 0 ? qty : 1;
|
||||
},
|
||||
isOutOfStock() {
|
||||
if (this.isECouponGoods) {
|
||||
return getECouponStock(this.skuDetail) <= 0;
|
||||
}
|
||||
return (Number(this.skuDetail.quantity) || 0) <= 0;
|
||||
},
|
||||
},
|
||||
@@ -425,17 +456,20 @@ export default {
|
||||
|
||||
|
||||
syncCountWithQuantity() {
|
||||
const qty = Number(this.skuDetail.quantity) || 0;
|
||||
const qty = this.isECouponGoods
|
||||
? getECouponStock(this.skuDetail)
|
||||
: Number(this.skuDetail.quantity) || 0;
|
||||
if (qty <= 0) {
|
||||
this.count = 1;
|
||||
return;
|
||||
}
|
||||
if (this.wholesaleList && this.wholesaleList.length > 0) {
|
||||
if (!this.isECouponGoods && this.wholesaleList && this.wholesaleList.length > 0) {
|
||||
this.count = Math.min(Math.max(this.wholesaleList[0].num, 1), qty);
|
||||
return;
|
||||
}
|
||||
if (this.count > qty) {
|
||||
this.count = qty;
|
||||
const max = this.isECouponGoods ? getECouponMaxBuyNum(this.skuDetail) : qty;
|
||||
if (this.count > max) {
|
||||
this.count = max;
|
||||
} else if (this.count < 1) {
|
||||
this.count = 1;
|
||||
}
|
||||
@@ -471,6 +505,11 @@ export default {
|
||||
},
|
||||
|
||||
addShoppingCartBtn() {
|
||||
// FR-B-01:E_COUPON 禁止加购物车(后端亦返回 CARD_KEY_E_COUPON_CART_FORBIDDEN)
|
||||
if (this.isECouponGoods) {
|
||||
Message.warning("电子卡券请使用立即购买");
|
||||
return;
|
||||
}
|
||||
// 添加购物车
|
||||
const params = {
|
||||
num: this.count,
|
||||
@@ -506,10 +545,11 @@ export default {
|
||||
skuId: this.skuDetail.id,
|
||||
cartType: "BUY_NOW",
|
||||
};
|
||||
// 虚拟商品购买
|
||||
// 虚拟商品:cartType=VIRTUAL(核销码流程)
|
||||
if (this.skuDetail.goodsType === "VIRTUAL_GOODS") {
|
||||
params.cartType = "VIRTUAL";
|
||||
}
|
||||
// E_COUPON 卡密商品:cartType 须保持 BUY_NOW,勿用 VIRTUAL(FR-B-01 / EC-24)
|
||||
this.loading1 = true;
|
||||
addCartGoods(params)
|
||||
.then((res) => {
|
||||
|
||||
39
buyer/src/constants/goodsType.js
Normal file
39
buyer/src/constants/goodsType.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 卡密商品(E_COUPON / 电子卡券)— 买家端公共常量与工具
|
||||
*
|
||||
* 与 VIRTUAL_GOODS(核销型虚拟)区分:E_COUPON 仅立即购买(BUY_NOW),
|
||||
* 支付后自动发卡,订单终态 COMPLETED;可售库存优先读 poolStock。
|
||||
* 需求:lilishop/docs/requirements/card-key-goods-v4.md
|
||||
*
|
||||
* @author Mike
|
||||
* @date 2026-07-31
|
||||
*/
|
||||
export const E_COUPON_GOODS_TYPE = "E_COUPON";
|
||||
export const E_COUPON_ORDER_TYPE = "E_COUPON";
|
||||
|
||||
/** 单次购买数量上限(O-09 待拍板默认值:min(poolStock, 999)) */
|
||||
export const E_COUPON_MAX_BUY_NUM = 999;
|
||||
|
||||
export function isECoupon(goodsType) {
|
||||
return goodsType === E_COUPON_GOODS_TYPE;
|
||||
}
|
||||
|
||||
export function isECouponOrder(orderType) {
|
||||
return orderType === E_COUPON_ORDER_TYPE;
|
||||
}
|
||||
|
||||
/** 可售库存:优先 poolStock */
|
||||
export function getECouponStock(sku) {
|
||||
if (!sku) return 0;
|
||||
const pool = sku.poolStock;
|
||||
if (pool !== undefined && pool !== null) {
|
||||
return Number(pool) || 0;
|
||||
}
|
||||
return Number(sku.quantity) || 0;
|
||||
}
|
||||
|
||||
export function getECouponMaxBuyNum(sku) {
|
||||
const stock = getECouponStock(sku);
|
||||
if (stock <= 0) return 1;
|
||||
return Math.min(stock, E_COUPON_MAX_BUY_NUM);
|
||||
}
|
||||
@@ -80,9 +80,16 @@
|
||||
>
|
||||
自营
|
||||
</el-tag>
|
||||
<!-- E_COUPON 卡密商品类型标识 -->
|
||||
<el-tag
|
||||
class="goods-show-tag goods-show-tag-physical goods-show-tag-ecoupon"
|
||||
v-if="item.goodsType === 'E_COUPON'"
|
||||
>
|
||||
电子卡券
|
||||
</el-tag>
|
||||
<el-tag
|
||||
class="goods-show-tag goods-show-tag-physical"
|
||||
v-if="item.goodsType === 'VIRTUAL_GOODS'"
|
||||
v-else-if="item.goodsType === 'VIRTUAL_GOODS'"
|
||||
>
|
||||
虚拟
|
||||
</el-tag>
|
||||
@@ -281,13 +288,24 @@ export default {
|
||||
}
|
||||
|
||||
.goods-show-tag {
|
||||
height: 18px;
|
||||
width: 32px;
|
||||
line-height: 14px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 3px;
|
||||
justify-content: center;
|
||||
height: 18px;
|
||||
min-width: 32px;
|
||||
width: auto;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(.el-tag__content) {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-show-tag-ecoupon {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.goods-show-tag-self {
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
<el-button @click="handleCancelOrder(order.sn)" type="danger" v-if="order.allowOperationVO.cancel" size="small">取消订单</el-button>
|
||||
<el-button @click="goPay(order.sn)" size="small" type="success" v-if="order.allowOperationVO.pay">去支付</el-button>
|
||||
<el-button @click="received(order.sn)" size="small" type="primary" v-if="order.allowOperationVO.rog">确认收货</el-button>
|
||||
<!-- 售后 -->
|
||||
<el-button v-if="order.groupAfterSaleStatus && (order.groupAfterSaleStatus.includes('NOT_APPLIED')|| order.groupAfterSaleStatus.includes('PART_AFTER_SALE'))"
|
||||
<!-- 售后(E_COUPON 卡密已交付不支持售后 FR-B-04) -->
|
||||
<el-button v-if="order.orderType !== 'E_COUPON' && order.groupAfterSaleStatus && (order.groupAfterSaleStatus.includes('NOT_APPLIED')|| order.groupAfterSaleStatus.includes('PART_AFTER_SALE'))"
|
||||
@click="applyAfterSale(order.orderItems)" size="small">申请售后</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,8 +141,8 @@
|
||||
<el-button @click="handleCancelOrder(order.sn)" type="danger" v-if="order.allowOperationVO.cancel" size="small">取消订单</el-button>
|
||||
<el-button @click="goPay(order.sn)" size="small" type="success" v-if="order.allowOperationVO.pay">去支付</el-button>
|
||||
<el-button @click="received(order.sn)" size="small" type="primary" v-if="order.allowOperationVO.rog">确认收货</el-button>
|
||||
<!-- 售后 -->
|
||||
<el-button v-if="order.groupAfterSaleStatus && (order.groupAfterSaleStatus.includes('NOT_APPLIED')|| order.groupAfterSaleStatus.includes('PART_AFTER_SALE'))"
|
||||
<!-- 售后(E_COUPON 不支持买家售后 FR-B-04) -->
|
||||
<el-button v-if="order.orderType !== 'E_COUPON' && order.groupAfterSaleStatus && (order.groupAfterSaleStatus.includes('NOT_APPLIED')|| order.groupAfterSaleStatus.includes('PART_AFTER_SALE'))"
|
||||
@click="applyAfterSale(order.orderItems)" size="small">申请售后</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
>取消订单</el-button>
|
||||
<el-button v-if="order.allowOperationVO.showLogistics || orderPackage.length > 0 || logistics" type="info" @click="logisticsList()" size="small">查看物流</el-button>
|
||||
</el-card>
|
||||
<p class="verificationCode" v-if="order.order.verificationCode">
|
||||
<p class="verificationCode" v-if="order.order.verificationCode && !isECouponOrder">
|
||||
核验码:<span>{{ order.order.verificationCode }}</span>
|
||||
</p>
|
||||
<div class="order-card">
|
||||
@@ -51,7 +51,7 @@
|
||||
></el-step>
|
||||
</el-steps>
|
||||
</div>
|
||||
<div class="order-card" v-if="order.order.deliveryMethod === 'LOGISTICS' && order.order.orderType !== 'VIRTUAL'">
|
||||
<div class="order-card" v-if="order.order.deliveryMethod === 'LOGISTICS' && !isNonPhysicalOrder">
|
||||
<h3>收货人信息</h3>
|
||||
<p>收货人:{{ order.order.consigneeName }}</p>
|
||||
<p>手机号码:{{ $filters.secrecyMobile( order.order.consigneeMobile ) }}</p>
|
||||
@@ -70,7 +70,7 @@
|
||||
<p>支付方式:{{ order.paymentMethodValue }}</p>
|
||||
<p>付款状态:{{ order.payStatusValue }}</p>
|
||||
</div>
|
||||
<div class="order-card" v-if="!order.order.verificationCode && order.order.orderType !== 'VIRTUAL'">
|
||||
<div class="order-card" v-if="!order.order.verificationCode && !isNonPhysicalOrder">
|
||||
<h3>配送信息</h3>
|
||||
<p>配送方式:{{ order.deliveryMethodValue }}</p>
|
||||
<p v-if="order.order.deliveryMethod === 'LOGISTICS'">配送状态:{{ order.deliverStatusValue }}</p>
|
||||
@@ -132,6 +132,37 @@
|
||||
</template>
|
||||
<div v-else style="color: #999; margin-left: 5px">未开发票</div>
|
||||
</div>
|
||||
<!-- 电子卡券:卡密信息(PC 内嵌) -->
|
||||
<div class="order-card ecoupon-card-keys" v-if="isECouponOrder">
|
||||
<h3>卡密信息</h3>
|
||||
<el-alert
|
||||
v-if="!ecouponCardKeyDelivered"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
title="卡密尚未发放,请稍后刷新或联系商家"
|
||||
/>
|
||||
<template v-else-if="ecouponCardKeyRows.length">
|
||||
<div class="ecoupon-card-keys-mobile">
|
||||
<el-button type="primary" @click="cardKeyDialogVisible = true">查看卡密</el-button>
|
||||
</div>
|
||||
<div class="ecoupon-card-keys-pc">
|
||||
<el-table border :data="ecouponCardKeyRows" style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="cardNo" label="卡号" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="cardSecret" label="卡密" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="allocatedTime" label="发卡时间" width="170" />
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row" link type="primary" @click="copyCardKey(row)">复制</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-button class="mt_10" size="small" @click="copyAllCardKeys">复制全部卡密</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 订单商品 -->
|
||||
<div class="goods">
|
||||
<div class="shop-name">
|
||||
@@ -144,8 +175,8 @@
|
||||
<th width="15%">货号</th>
|
||||
<th width="10%">单价</th>
|
||||
<th width="5%">数量</th>
|
||||
<th width="10%">退款状态</th>
|
||||
<th width="10%">实际退款金额</th>
|
||||
<th width="10%" v-if="!isECouponOrder">退款状态</th>
|
||||
<th width="10%" v-if="!isECouponOrder">实际退款金额</th>
|
||||
<th width="10%">小计</th>
|
||||
<th width="10%">操作</th>
|
||||
</tr>
|
||||
@@ -170,8 +201,8 @@
|
||||
<td>{{ goods.id }}</td>
|
||||
<td>{{ $filters.unitPrice(goods.goodsPrice, "¥") }}</td>
|
||||
<td>{{ goods.num }}</td>
|
||||
<td>{{refundPriceList(goods.isRefund)}}</td>
|
||||
<td>{{ $filters.unitPrice(goods.refundPrice, "¥") }}</td>
|
||||
<td v-if="!isECouponOrder">{{refundPriceList(goods.isRefund)}}</td>
|
||||
<td v-if="!isECouponOrder">{{ $filters.unitPrice(goods.refundPrice, "¥") }}</td>
|
||||
<td>{{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }}</td>
|
||||
<td class="order-item-actions">
|
||||
<el-button
|
||||
@@ -192,8 +223,9 @@
|
||||
>
|
||||
<el-button
|
||||
v-if="
|
||||
goods.afterSaleStatus.includes('NOT_APPLIED') ||
|
||||
goods.afterSaleStatus.includes('PART_AFTER_SALE')
|
||||
!isECouponOrder &&
|
||||
(goods.afterSaleStatus.includes('NOT_APPLIED') ||
|
||||
goods.afterSaleStatus.includes('PART_AFTER_SALE'))
|
||||
"
|
||||
@click="applyAfterSale(goods.sn)"
|
||||
type="default"
|
||||
@@ -213,7 +245,7 @@
|
||||
<div>
|
||||
<span>商品总价:</span><span>{{ $filters.unitPrice(order.order.goodsPrice, "¥") }}</span><br />
|
||||
</div>
|
||||
<div v-if="order.order.orderType !== 'VIRTUAL'">
|
||||
<div v-if="!isECouponOrder">
|
||||
<span>运费:</span><span>+{{ $filters.unitPrice(order.order.freightPrice, "¥") }}</span><br />
|
||||
</div>
|
||||
<div v-if="order.order.priceDetailDTO.couponPrice">
|
||||
@@ -319,6 +351,19 @@
|
||||
<el-button @click="logisticsModal = false">取消</el-button>
|
||||
</div></template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 移动端查看卡密 -->
|
||||
<el-dialog v-model="cardKeyDialogVisible" title="卡密信息" width="92%" class="card-key-mobile-dialog">
|
||||
<div v-for="(row, idx) in ecouponCardKeyRows" :key="idx" class="card-key-mobile-item">
|
||||
<p><strong>卡号:</strong>{{ row.cardNo }}</p>
|
||||
<p><strong>卡密:</strong>{{ row.cardSecret }}</p>
|
||||
<el-button size="small" link type="primary" @click="copyCardKey(row)">复制</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="copyAllCardKeys">复制全部</el-button>
|
||||
<el-button type="primary" @click="cardKeyDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</UserCenterLayout>
|
||||
|
||||
</div>
|
||||
@@ -333,6 +378,12 @@ import {
|
||||
getPackage
|
||||
} from "@/api/order.js";
|
||||
import { afterSaleReason, receiptDetail } from "@/api/member";
|
||||
import { isECouponOrder as checkECouponOrder } from "@/constants/goodsType";
|
||||
|
||||
/**
|
||||
* 订单详情:E_COUPON 展示 orderItems[].cardKeys,屏蔽售后/物流(FR-B-03 / FR-B-04)。
|
||||
* 移动端已完成订单用 Dialog 查看卡密(原型 P-09 / D-01)。
|
||||
*/
|
||||
export default {
|
||||
name: "order-detail",
|
||||
data() {
|
||||
@@ -340,6 +391,7 @@ export default {
|
||||
order: {}, // 订单详情数据
|
||||
progressList: [], // 订单流程
|
||||
logistics: "", // 物流数据
|
||||
cardKeyDialogVisible: false,
|
||||
cancelParams: {
|
||||
// 取消售后参数
|
||||
orderSn: "",
|
||||
@@ -352,7 +404,57 @@ export default {
|
||||
logisticsModal: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/** 电子卡券订单:无物流/核验码,卡密数据来自 API-B-01 orderItems[].cardKeys */
|
||||
isECouponOrder() {
|
||||
return checkECouponOrder(this.order?.order?.orderType);
|
||||
},
|
||||
isVirtualOrder() {
|
||||
return this.order?.order?.orderType === "VIRTUAL";
|
||||
},
|
||||
isNonPhysicalOrder() {
|
||||
return this.isVirtualOrder || this.isECouponOrder;
|
||||
},
|
||||
ecouponCardKeyDelivered() {
|
||||
return (this.order.orderItems || []).some((item) => item.cardKeyDelivered);
|
||||
},
|
||||
ecouponCardKeyRows() {
|
||||
const rows = [];
|
||||
(this.order.orderItems || []).forEach((item) => {
|
||||
(item.cardKeys || []).forEach((ck) => {
|
||||
rows.push({ ...ck });
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
copyCardKey(row) {
|
||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||
this.copyText(text);
|
||||
},
|
||||
copyAllCardKeys() {
|
||||
const text = this.ecouponCardKeyRows
|
||||
.map((row, i) => `${i + 1}. 卡号:${row.cardNo || ""} 卡密:${row.cardSecret || ""}`)
|
||||
.join("\n");
|
||||
this.copyText(text || "");
|
||||
},
|
||||
copyText(text) {
|
||||
if (!text) return;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
Message.success("已复制到剪贴板");
|
||||
});
|
||||
} else {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
Message.success("已复制到剪贴板");
|
||||
}
|
||||
},
|
||||
isVatSpecialReceipt (receipt) {
|
||||
if (!receipt) return false;
|
||||
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
|
||||
@@ -660,6 +762,32 @@ table {
|
||||
color: $theme_color;
|
||||
}
|
||||
}
|
||||
|
||||
.ecoupon-card-keys-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ecoupon-card-keys-pc {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-key-mobile-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
p {
|
||||
margin: 4px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ecoupon-card-keys-mobile {
|
||||
display: block;
|
||||
}
|
||||
.ecoupon-card-keys-pc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/** 订单进度条 */
|
||||
.progress {
|
||||
margin: 15px 0;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<el-divider />
|
||||
<div class="content width_1200_auto">
|
||||
<!-- 收货地址 -->
|
||||
<div class="address" v-if="selectedDeliverMethod === 'LOGISTICS' && goodsType !== 'VIRTUAL_GOODS'">
|
||||
<!-- 收货地址(E_COUPON / VIRTUAL 免地址,见 isVirtualLikeCheckout) -->
|
||||
<div class="address" v-if="selectedDeliverMethod === 'LOGISTICS' && !isVirtualLikeCheckout">
|
||||
<div class="card-head">
|
||||
<span>收货人信息</span>
|
||||
<span @click="goAddressManage">管理收货人地址</span>
|
||||
@@ -94,7 +95,7 @@
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
<div class="goods-content" v-if="goodsType !== 'VIRTUAL_GOODS'">
|
||||
<div class="goods-content" v-if="!isVirtualLikeCheckout">
|
||||
<div class="card-head mt_20 mb_20">
|
||||
<span>配送方式</span>
|
||||
</div>
|
||||
@@ -166,8 +167,8 @@
|
||||
<span @click="editInvoice">编辑</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 优惠券 -->
|
||||
<div class="invoice">
|
||||
<!-- 优惠券(E_COUPON 不参与促销 M-01) -->
|
||||
<div class="invoice" v-if="!isECouponCheckout">
|
||||
<div class="card-head mt_20 mb_20">
|
||||
<span class="relative">优惠券</span>
|
||||
</div>
|
||||
@@ -203,7 +204,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 礼品卡 -->
|
||||
<div class="invoice pay-gcc-module">
|
||||
<div class="invoice pay-gcc-module" v-if="!isECouponCheckout">
|
||||
<div class="pay-gcc-head">
|
||||
<div class="pay-gcc-title">
|
||||
使用礼品卡
|
||||
@@ -328,6 +329,7 @@ import {
|
||||
} from "@/api/cart";
|
||||
import { getStoreAddress } from "@/api/shopentry.js"
|
||||
import { canUseCouponList } from "@/api/member.js";
|
||||
import { isECoupon } from "@/constants/goodsType";
|
||||
|
||||
export default {
|
||||
name: "Pay",
|
||||
@@ -377,6 +379,14 @@ export default {
|
||||
);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
},
|
||||
/** 电子卡券结算:免地址、隐藏优惠券/礼品卡/发票(M-01 / O-11) */
|
||||
isECouponCheckout() {
|
||||
return isECoupon(this.goodsType);
|
||||
},
|
||||
/** 虚拟型结算(核销虚拟 + 电子卡券):均不需要收货地址 */
|
||||
isVirtualLikeCheckout() {
|
||||
return this.goodsType === "VIRTUAL_GOODS" || this.isECouponCheckout;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -854,10 +864,15 @@ export default {
|
||||
query: { orderType: "TRADE", sn: res.result.sn },
|
||||
});
|
||||
}
|
||||
} else if (res.message) {
|
||||
Message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((err) => {
|
||||
Spin.hide();
|
||||
if (err?.message && !err?.data?.message) {
|
||||
Message.error(err.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 优惠券可用范围
|
||||
|
||||
@@ -102,14 +102,21 @@ export default {
|
||||
title: '支付确认',
|
||||
content: '确认使用余额支付吗?',
|
||||
onOk: () => {
|
||||
pay(params).then(res => {
|
||||
if (res.success) {
|
||||
Message.success(res.message)
|
||||
this.$router.push('/payDone');
|
||||
} else {
|
||||
Message.warning(res.message)
|
||||
}
|
||||
})
|
||||
return pay(params)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success(res.message || '支付成功');
|
||||
this.$router.push('/payDone');
|
||||
} else {
|
||||
Message.warning(res.message || '支付失败');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
// 支付失败兜底:业务错误(如 CARD_KEY_STORE_SELL_FORBIDDEN)通常已由 axios 拦截器提示
|
||||
if (err?.message && !err?.data?.message) {
|
||||
Message.error(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user