feat(buyer): 更新电子卡券(E_COUPON)相关逻辑与展示

- 优化购物车与支付页面,确保电子卡券只能通过立即购买结算
- 增强商品详情页,添加限时折扣与第N件优惠展示
- 更新促销逻辑,支持拼团与积分购买选项
- 规范化商品类型与库存提示,提升用户体验
This commit is contained in:
田香琪
2026-08-02 13:55:19 +08:00
parent 1ae7ab3036
commit 1accdf5826
24 changed files with 920 additions and 311 deletions

View File

@@ -132,35 +132,59 @@
</template>
<div v-else style="color: #999; margin-left: 5px">未开发票</div>
</div>
<!-- 电子卡券卡密信息PC 内嵌 -->
<div class="order-card ecoupon-card-keys" v-if="isECouponOrder">
<!-- 卡密信息E_COUPON 主单或满赠子单,以 cardKeyFulfillStatus 为准 -->
<div class="order-card ecoupon-card-keys" v-if="hasCardKeySection">
<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
v-for="line in cardKeyFulfillLines"
:key="line.key"
class="ecoupon-fulfill-block"
>
<div v-if="cardKeyFulfillLines.length > 1" class="ecoupon-fulfill-title">
{{ line.goodsName || (line.isGift ? "满赠电子卡券" : "电子卡券") }}
</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>
<el-alert
v-if="line.status !== 'DELIVERED'"
:type="cardKeyFulfillAlertType(line.status)"
show-icon
:closable="false"
:title="resolveCardKeyFulfillMessage(line)"
class="mb_10"
/>
<template v-else-if="line.cardKeys.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="lineCardKeyRows(line)" style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column
v-if="cardKeyFulfillLines.length > 1"
prop="goodsName"
label="商品"
min-width="120"
show-overflow-tooltip
/>
<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>
</div>
</template>
</div>
<el-button
v-if="ecouponCardKeyRows.length"
class="mt_10 ecoupon-card-keys-pc"
size="small"
@click="copyAllCardKeys"
>
复制全部卡密
</el-button>
</div>
<!-- 订单商品 -->
@@ -378,7 +402,14 @@ import {
getPackage
} from "@/api/order.js";
import { afterSaleReason, receiptDetail } from "@/api/member";
import { isECouponOrder as checkECouponOrder } from "@/constants/goodsType";
import {
isECouponOrder as checkECouponOrder,
collectCardKeyFulfillLines,
orderHasCardKeySection,
flattenOrderCardKeys,
cardKeyFulfillAlertType,
resolveCardKeyFulfillMessage,
} from "@/constants/goodsType";
/**
* 订单详情E_COUPON 展示 orderItems[].cardKeys屏蔽售后/物流FR-B-03 / FR-B-04
@@ -415,20 +446,37 @@ export default {
isNonPhysicalOrder() {
return this.isVirtualOrder || this.isECouponOrder;
},
ecouponCardKeyDelivered() {
return (this.order.orderItems || []).some((item) => item.cardKeyDelivered);
/** 是否展示卡密区块(含满赠 E_COUPON 子单 giftECouponOrders */
hasCardKeySection() {
return orderHasCardKeySection({
orderItems: this.order.orderItems,
giftSummaries: this.order.giftECouponOrders,
isECouponOrder: this.isECouponOrder,
});
},
cardKeyFulfillLines() {
return collectCardKeyFulfillLines(
this.order.orderItems,
this.order.giftECouponOrders
);
},
ecouponCardKeyRows() {
const rows = [];
(this.order.orderItems || []).forEach((item) => {
(item.cardKeys || []).forEach((ck) => {
rows.push({ ...ck });
});
});
return rows;
return flattenOrderCardKeys(
this.order.orderItems,
false,
this.order.giftECouponOrders
);
},
},
methods: {
cardKeyFulfillAlertType,
resolveCardKeyFulfillMessage,
lineCardKeyRows(line) {
return (line.cardKeys || []).map((ck) => ({
...ck,
goodsName: line.goodsName,
}));
},
copyCardKey(row) {
const text = `卡号:${row.cardNo || ""}\n卡密${row.cardSecret || ""}`;
this.copyText(text);
@@ -767,6 +815,18 @@ table {
display: none;
}
.ecoupon-fulfill-block + .ecoupon-fulfill-block {
margin-top: 16px;
padding-top: 16px;
border-top: 1px dashed #eee;
}
.ecoupon-fulfill-title {
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.ecoupon-card-keys-pc {
display: block;
}