mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-11 03:18:56 +08:00
feat(order,goods): 详情页促销模块独立拆分与实物赠品多端随单履约展示
- 详情页重构:ShowGoods.vue 价格卡片与促销卡片解耦,促销卡片独立分行展示优惠券与满额活动; - 买家端结算页:Pay.vue 清单中直观展示随单 0 元实物赠品明细行与数量; - 买家端订单中心:MyOrder.vue 与 OrderDetail.vue 标注【赠品】红色徽标、原价划线展示并屏蔽单独售后申请; - 管理端与商家端后台:orderDetail.vue 商品表格与待发货弹窗中醒目标注【赠品】及满赠活动类型。
This commit is contained in:
@@ -70,7 +70,7 @@
|
||||
v-if="promotionMap['SECKILL']"
|
||||
:time="promotionMap['SECKILL'].endTime"
|
||||
></Promotion>
|
||||
<!-- 商品详细 价格、优惠券、促销 -->
|
||||
<!-- 商品详细 价格 -->
|
||||
<div class="item-detail-price-row">
|
||||
<div class="item-price-left">
|
||||
<!-- 秒杀价格 -->
|
||||
@@ -89,76 +89,6 @@
|
||||
<div class="item-price-row" v-else>
|
||||
<span class="item-price">{{ $filters.unitPrice(skuDetail.price, "¥") }}</span>
|
||||
</div>
|
||||
<!-- 优惠券展示 -->
|
||||
<div class="item-price-coupon-row" v-if="promotionMap['COUPON'].length">
|
||||
<span class="item-price-title">优惠券</span>
|
||||
<div class="coupon-tags">
|
||||
<span
|
||||
class="item-coupon"
|
||||
v-for="(item, index) in promotionMap['COUPON'].slice(0, 6)"
|
||||
:key="index"
|
||||
@click="handleReceiveCoupon(item.id)"
|
||||
>
|
||||
<span v-if="item.couponType == 'PRICE'"
|
||||
>{{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }}</span>
|
||||
<span v-if="item.couponType == 'DISCOUNT'"
|
||||
>满{{ item.consumeThreshold }}打{{
|
||||
item.couponDiscount
|
||||
}}折</span>
|
||||
</span>
|
||||
|
||||
<div class="dropdown" v-if="promotionMap['COUPON'].length > 6">
|
||||
<span class="dropdown-more">展开更多 ▾</span>
|
||||
<div class="dropdown-content">
|
||||
<span
|
||||
class="item-coupon"
|
||||
v-for="(item, index) in promotionMap['COUPON'].slice(6, promotionMap['COUPON'].length)"
|
||||
:key="index"
|
||||
@click="handleReceiveCoupon(item.id)"
|
||||
>
|
||||
<span v-if="item.couponType == 'PRICE'"
|
||||
>{{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }}</span>
|
||||
<span v-if="item.couponType == 'DISCOUNT'"
|
||||
>满{{ item.consumeThreshold }}打{{
|
||||
item.couponDiscount
|
||||
}}折</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 拼团 -->
|
||||
<div class="item-price-row" v-if="promotionMap['PINTUAN']">
|
||||
<p>
|
||||
<span class="item-price-title">拼团</span>
|
||||
<span class="item-promotion">拼团价</span>
|
||||
<span class="item-desc-pintuan" v-if="promotionMap['PINTUAN'].price">
|
||||
{{ $filters.unitPrice(promotionMap["PINTUAN"].price, "¥") }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- 满减展示 -->
|
||||
<div class="item-price-row" v-if="promotionMap['FULL_DISCOUNT']">
|
||||
<p>
|
||||
<span class="item-price-title">促销</span>
|
||||
<span class="item-promotion">满减</span>
|
||||
<span
|
||||
class="item-desc-pintuan"
|
||||
v-if="promotionMap['FULL_DISCOUNT'].fullMinus"
|
||||
>满{{ promotionMap["FULL_DISCOUNT"].fullMoney }}元,立减现金{{
|
||||
promotionMap["FULL_DISCOUNT"].fullMinus
|
||||
}}元</span>
|
||||
<span
|
||||
class="item-desc-pintuan"
|
||||
v-if="
|
||||
promotionMap['FULL_DISCOUNT'].fullRate &&
|
||||
promotionMap['FULL_DISCOUNT'].fullRateFlag
|
||||
"
|
||||
>满{{ promotionMap["FULL_DISCOUNT"].fullMoney }}元,立享{{
|
||||
promotionMap["FULL_DISCOUNT"].fullRate
|
||||
}}折</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-price-right">
|
||||
<div class="item-remarks-sum">
|
||||
@@ -170,6 +100,69 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 独立促销与优惠模块 -->
|
||||
<div class="goods-promotions-box" v-if="hasPromotions">
|
||||
<!-- 优惠券展示 -->
|
||||
<div class="promo-item-row" v-if="promotionMap['COUPON'] && promotionMap['COUPON'].length">
|
||||
<span class="promo-title">优惠券</span>
|
||||
<div class="coupon-tags">
|
||||
<span
|
||||
class="item-coupon"
|
||||
v-for="(item, index) in promotionMap['COUPON'].slice(0, 6)"
|
||||
:key="index"
|
||||
@click="handleReceiveCoupon(item.id)"
|
||||
>
|
||||
<span v-if="item.couponType == 'PRICE'"
|
||||
>{{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }}</span>
|
||||
<span v-if="item.couponType == 'DISCOUNT'"
|
||||
>满{{ item.consumeThreshold }}打{{
|
||||
item.couponDiscount
|
||||
}}折</span>
|
||||
</span>
|
||||
|
||||
<div class="dropdown" v-if="promotionMap['COUPON'].length > 6">
|
||||
<span class="dropdown-more">展开更多 ▾</span>
|
||||
<div class="dropdown-content">
|
||||
<span
|
||||
class="item-coupon"
|
||||
v-for="(item, index) in promotionMap['COUPON'].slice(6, promotionMap['COUPON'].length)"
|
||||
:key="index"
|
||||
@click="handleReceiveCoupon(item.id)"
|
||||
>
|
||||
<span v-if="item.couponType == 'PRICE'"
|
||||
>{{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }}</span>
|
||||
<span v-if="item.couponType == 'DISCOUNT'"
|
||||
>满{{ item.consumeThreshold }}打{{
|
||||
item.couponDiscount
|
||||
}}折</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 促销活动行(满减/满折/满赠、拼团) -->
|
||||
<div class="promo-item-row" v-if="promotionMap['FULL_DISCOUNT'] || promotionMap['PINTUAN']">
|
||||
<span class="promo-title">促 销</span>
|
||||
<div class="promo-content-column">
|
||||
<!-- 满减/满折/满赠 -->
|
||||
<div class="promo-activity-entry" v-if="promotionMap['FULL_DISCOUNT']">
|
||||
<span class="promo-badge">{{ getFullDiscountTag(promotionMap['FULL_DISCOUNT']) }}</span>
|
||||
<span class="promo-desc">{{ formatFullDiscountDesc(promotionMap['FULL_DISCOUNT']) }}</span>
|
||||
</div>
|
||||
<!-- 拼团 -->
|
||||
<div class="promo-activity-entry" v-if="promotionMap['PINTUAN']">
|
||||
<span class="promo-badge">拼团</span>
|
||||
<span class="promo-desc">
|
||||
拼团价
|
||||
<span class="promo-price-text">{{ $filters.unitPrice(promotionMap["PINTUAN"].price, "¥") }}</span>
|
||||
,多人拼单更实惠
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 选择颜色 -->
|
||||
<div
|
||||
class="item-select item-spec"
|
||||
@@ -364,6 +357,14 @@ const currentImg = computed(() => {
|
||||
return item.type === "video" ? item.poster : item.url || item;
|
||||
});
|
||||
|
||||
/** 是否包含有效促销/优惠(优惠券、满减/满折/满赠、拼团) */
|
||||
const hasPromotions = computed(() => {
|
||||
const hasCoupon = Array.isArray(promotionMap.value?.COUPON) && promotionMap.value.COUPON.length > 0;
|
||||
const hasFullDiscount = !!promotionMap.value?.FULL_DISCOUNT;
|
||||
const hasPintuan = !!promotionMap.value?.PINTUAN;
|
||||
return hasCoupon || hasFullDiscount || hasPintuan;
|
||||
});
|
||||
|
||||
|
||||
/** 卡密商品:禁加购,仅立即购买;库存读 quantity(M-02 起支持促销) */
|
||||
const isECouponGoods = computed(() => {
|
||||
@@ -533,6 +534,53 @@ function pointPay() {
|
||||
startCheckout(E_COUPON_CART_POINTS, "loading");
|
||||
}
|
||||
|
||||
function getFullDiscountTag(fd) {
|
||||
if (!fd) return "满优惠";
|
||||
if (fd.activityType === "FULL_RATE") return "满折";
|
||||
if (fd.activityType === "FULL_GIFT") return "满赠";
|
||||
if (fd.activityType === "FULL_MINUS") return "满减";
|
||||
if (fd.fullRateFlag) return "满折";
|
||||
if (fd.giftFlag) return "满赠";
|
||||
return "满减";
|
||||
}
|
||||
|
||||
function formatFullDiscountDesc(fd) {
|
||||
if (!fd) return "";
|
||||
let rules = fd.ruleList;
|
||||
if (!rules && fd.rulesJson) {
|
||||
try {
|
||||
rules = JSON.parse(fd.rulesJson);
|
||||
} catch (e) {
|
||||
rules = null;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(rules) && rules.length > 0) {
|
||||
return rules
|
||||
.map((rule) => {
|
||||
if (fd.activityType === "FULL_RATE") {
|
||||
return `满${rule.fullMoney}元打${rule.fullRate}折`;
|
||||
}
|
||||
if (fd.activityType === "FULL_GIFT") {
|
||||
const gifts = Array.isArray(rule.giftItems)
|
||||
? rule.giftItems
|
||||
.map((g) => `${g.goodsName || "赠品"}x${g.num || 1}`)
|
||||
.join("、")
|
||||
: "赠品";
|
||||
return `满${rule.fullMoney}元送${gifts}`;
|
||||
}
|
||||
return `满${rule.fullMoney}元减${rule.fullMinus}元`;
|
||||
})
|
||||
.join(";");
|
||||
}
|
||||
if (fd.fullMinusFlag && fd.fullMinus) {
|
||||
return `满${fd.fullMoney}元,立减现金${fd.fullMinus}元`;
|
||||
}
|
||||
if (fd.fullRateFlag && fd.fullRate) {
|
||||
return `满${fd.fullMoney}元,立享${fd.fullRate}折`;
|
||||
}
|
||||
return fd.title || "";
|
||||
}
|
||||
|
||||
function startCheckout(action, loadingKey) {
|
||||
const params = {
|
||||
num: count.value,
|
||||
@@ -906,6 +954,7 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -948,15 +997,70 @@ onBeforeUnmount(() => {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.item-price-coupon-row {
|
||||
/* 促销与优惠独立展示模块 */
|
||||
.goods-promotions-box {
|
||||
margin-top: 12px;
|
||||
padding: 12px 20px;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.promo-item-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin: 8px 0px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.item-price-title {
|
||||
flex-shrink: 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
.promo-title {
|
||||
flex-shrink: 0;
|
||||
width: 56px;
|
||||
margin-right: 12px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.promo-content-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.promo-activity-entry {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.promo-badge {
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: $theme_color;
|
||||
background: rgba(235, 69, 73, 0.08);
|
||||
border: 1px solid $theme_color;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.promo-desc {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
line-height: 22px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.promo-price-text {
|
||||
color: $theme_color;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.coupon-tags {
|
||||
|
||||
@@ -67,7 +67,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="goods-info">
|
||||
<div class="goods-name hover-color" @click="goodsDetail(goods.skuId, goods.goodsId)">{{ goods.name }}</div>
|
||||
<div class="goods-name hover-color" @click="goodsDetail(goods.skuId, goods.goodsId)">
|
||||
<el-tag v-if="isGiftItem(goods)" size="small" type="danger" effect="plain" class="mr_5">赠品</el-tag>
|
||||
{{ goods.name }}
|
||||
</div>
|
||||
<div class="goods-num">共{{ goods.num }}件</div>
|
||||
<div class="goods-extra">
|
||||
<el-tag v-if="refundPriceList(goods.isRefund) && !isUnpaid(order)" type="warning" size="small">{{ refundPriceList(goods.isRefund) }}</el-tag>
|
||||
@@ -151,7 +154,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="goods-info">
|
||||
<div class="goods-name hover-color" @click="goodsDetail(goods.skuId, goods.goodsId)">{{ goods.name }}</div>
|
||||
<div class="goods-name hover-color" @click="goodsDetail(goods.skuId, goods.goodsId)">
|
||||
<el-tag v-if="isGiftItem(goods)" size="small" type="danger" effect="plain" class="mr_5">赠品</el-tag>
|
||||
{{ goods.name }}
|
||||
</div>
|
||||
<div class="goods-num">共{{ goods.num }}件</div>
|
||||
<div class="goods-extra">
|
||||
<el-tag v-if="refundPriceList(goods.isRefund) && !isUnpaid(order)" type="warning" size="small">{{ refundPriceList(goods.isRefund) }}</el-tag>
|
||||
@@ -240,6 +246,10 @@ const cancelAvail = ref(false); // 取消订单modal控制
|
||||
const cancelReason = ref([]); // 取消订单原因
|
||||
|
||||
// 退款状态枚举
|
||||
function isGiftItem(goods) {
|
||||
if (!goods) return false;
|
||||
return goods.promotionType === 'GIFT' || (Number(goods.flowPrice) === 0 && goods.promotionType === 'GIFT');
|
||||
}
|
||||
function refundPriceList(status) {
|
||||
switch (status) {
|
||||
case 'ALL_REFUND':
|
||||
|
||||
@@ -269,40 +269,54 @@
|
||||
@click="goodsDetail(goods.skuId, goods.goodsId)"
|
||||
class="hover-color goods-name"
|
||||
>
|
||||
<el-tag v-if="isGiftItem(goods)" size="small" type="danger" effect="plain" class="mr_5">赠品</el-tag>
|
||||
{{ goods.goodsName }}
|
||||
</p>
|
||||
<p class="goods-sn">货号:{{ goods.id }}</p>
|
||||
</div>
|
||||
<div class="goods-price">
|
||||
<span class="unit-price">
|
||||
{{ $filters.unitPrice(goods.goodsPrice, "¥") }} × {{ goods.num }}
|
||||
</span>
|
||||
<span class="subtotal">{{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }}</span>
|
||||
<template v-if="isGiftItem(goods)">
|
||||
<span class="unit-price gift-original-price">
|
||||
{{ $filters.unitPrice(goods.goodsPrice, "¥") }}
|
||||
</span>
|
||||
<span class="subtotal gift-subtotal">赠品 (¥0.00 × {{ goods.num }})</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="unit-price">
|
||||
{{ $filters.unitPrice(goods.goodsPrice, "¥") }} × {{ goods.num }}
|
||||
</span>
|
||||
<span class="subtotal">{{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="goods-actions">
|
||||
<button
|
||||
v-if="goods.commentStatus == 'UNFINISHED'"
|
||||
type="button"
|
||||
class="comment-text"
|
||||
@click="comment(order.order.sn, goodsIndex)"
|
||||
>评价</button>
|
||||
<el-button
|
||||
v-if="goods.complainStatus == 'NO_APPLY'"
|
||||
@click="complain(order.order.sn, goodsIndex)"
|
||||
type="danger"
|
||||
size="small"
|
||||
>投诉</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="
|
||||
!isECouponOrder &&
|
||||
(goods.afterSaleStatus.includes('NOT_APPLIED') ||
|
||||
goods.afterSaleStatus.includes('PART_AFTER_SALE'))
|
||||
"
|
||||
@click="applyAfterSale(goods.sn)"
|
||||
size="small"
|
||||
>申请售后</el-button
|
||||
>
|
||||
<template v-if="!isGiftItem(goods)">
|
||||
<button
|
||||
v-if="goods.commentStatus == 'UNFINISHED'"
|
||||
type="button"
|
||||
class="comment-text"
|
||||
@click="comment(order.order.sn, goodsIndex)"
|
||||
>评价</button>
|
||||
<el-button
|
||||
v-if="goods.complainStatus == 'NO_APPLY'"
|
||||
@click="complain(order.order.sn, goodsIndex)"
|
||||
type="danger"
|
||||
size="small"
|
||||
>投诉</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="
|
||||
!isECouponOrder &&
|
||||
(goods.afterSaleStatus.includes('NOT_APPLIED') ||
|
||||
goods.afterSaleStatus.includes('PART_AFTER_SALE'))
|
||||
"
|
||||
@click="applyAfterSale(goods.sn)"
|
||||
size="small"
|
||||
>申请售后</el-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="gift-action-tip">随单赠送,不支持售后</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -726,6 +740,11 @@ function goPay(sn) {
|
||||
});
|
||||
}
|
||||
|
||||
function isGiftItem(goods) {
|
||||
if (!goods) return false;
|
||||
return goods.promotionType === 'GIFT' || (Number(goods.flowPrice) === 0 && goods.promotionType === 'GIFT');
|
||||
}
|
||||
|
||||
function applyAfterSale(sn) {
|
||||
// 申请售后
|
||||
router.push({ name: "ApplyAfterSale", query: { sn: sn } });
|
||||
@@ -1178,4 +1197,16 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.gift-original-price {
|
||||
text-decoration: line-through;
|
||||
color: #999;
|
||||
}
|
||||
.gift-subtotal {
|
||||
color: #e4393c;
|
||||
font-weight: bold;
|
||||
}
|
||||
.gift-action-tip {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -147,6 +147,24 @@
|
||||
<span>{{ goods.goodsSku?.quantity > 0 ? "有货" : "无货" }}</span>
|
||||
<span class="goods-price">{{ $filters.unitPrice(goods.subTotal, "¥") }}</span>
|
||||
</div>
|
||||
<!-- 随单实物赠品行 -->
|
||||
<div
|
||||
class="goods-item gift-item"
|
||||
v-for="(gift, giftIndex) in getShopGifts(shop)"
|
||||
:key="'gift-' + (gift.skuId || giftIndex)"
|
||||
>
|
||||
<span class="goods-main hover-color" @click="goGoodsDetail(gift.skuId, gift.goodsId)">
|
||||
<img :src="gift.thumbnail || gift.image" alt="" />
|
||||
<span class="goods-name">
|
||||
<el-tag size="small" type="danger" effect="plain" class="mr_5">赠品</el-tag>
|
||||
{{ gift.goodsName || gift.name || '随单实物赠品' }}
|
||||
</span>
|
||||
</span>
|
||||
<span class="goods-price gift-zero-price">¥0.00</span>
|
||||
<span>{{ gift.num || 1 }}</span>
|
||||
<span>有货</span>
|
||||
<span class="goods-price gift-zero-price">¥0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-mark">
|
||||
<div v-if="isRemarkOpen(shop)" class="mark-field">
|
||||
@@ -876,6 +894,13 @@ function delAddress(item) {
|
||||
onCancel: () => { },
|
||||
});
|
||||
}
|
||||
function getShopGifts(shop) {
|
||||
if (!shop) return [];
|
||||
if (Array.isArray(shop.giftItemList) && shop.giftItemList.length > 0) {
|
||||
return shop.giftItemList;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function goGoodsDetail(skuId, goodsId) {
|
||||
// 跳转商品详情
|
||||
let routeUrl = router.resolve({
|
||||
@@ -1800,4 +1825,8 @@ function useScope(type, storeName) {
|
||||
.pay-gcc-notice-foot {
|
||||
text-align: right;
|
||||
}
|
||||
.gift-zero-price {
|
||||
color: #e4393c !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -368,6 +368,7 @@
|
||||
/>
|
||||
<div class="goods-info">
|
||||
<div class="goods-name">
|
||||
<el-tag v-if="isGiftItem(row)" size="small" type="danger" effect="plain" class="mr_5">赠品</el-tag>
|
||||
<span>{{ row.goodsName }}</span>
|
||||
</div>
|
||||
<div v-if="row.specs" class="goods-specs">
|
||||
@@ -391,7 +392,11 @@
|
||||
|
||||
<el-table-column label="单价" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<span class="font-mono font-bold">¥ {{ Number(row.unitPrice || 0).toFixed(2) }}</span>
|
||||
<template v-if="isGiftItem(row)">
|
||||
<div class="font-mono text-gray" style="text-decoration: line-through;">¥ {{ Number(row.unitPrice || 0).toFixed(2) }}</div>
|
||||
<div class="text-danger font-bold">赠品 ¥ 0.00</div>
|
||||
</template>
|
||||
<span v-else class="font-mono font-bold">¥ {{ Number(row.unitPrice || 0).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -1159,10 +1164,16 @@ function formatOperatorType(type) {
|
||||
return map[type] || type || "系统";
|
||||
}
|
||||
|
||||
function isGiftItem(row) {
|
||||
if (!row) return false;
|
||||
return row.promotionType === 'GIFT' || (Number(row.flowPrice) === 0 && row.promotionType === 'GIFT');
|
||||
}
|
||||
|
||||
function getPromotionText(row) {
|
||||
let resultText = "";
|
||||
if (row && row.promotionType) {
|
||||
const type = row.promotionType.split(",");
|
||||
if (type.indexOf("GIFT") !== -1) resultText += "满赠赠品 ";
|
||||
if (type.indexOf("PINTUAN") !== -1) resultText += "拼团 ";
|
||||
if (type.indexOf("SECKILL") !== -1) resultText += "秒杀 ";
|
||||
if (type.indexOf("COUPON") !== -1) resultText += "优惠券 ";
|
||||
|
||||
@@ -191,32 +191,152 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card v-if="showDelivery && orderPackages.length" class="mt_10 order-goods-card">
|
||||
<el-divider content-position="left">发货记录</el-divider>
|
||||
<el-table :data="orderPackages" border style="width: 100%">
|
||||
<el-table-column label="发货方式" width="120">
|
||||
<template #default="{ row }">{{ getFulfillmentModeLabel(row.fulfillmentMode) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物流信息" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<template v-if="isNoDeliveryPackage(row)">
|
||||
<span>无需配送</span>
|
||||
<span v-if="row.deliveryRemark" class="delivery-remark-text">({{ row.deliveryRemark }})</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ row.logisticsName || "-" }} / {{ row.logisticsNo || "-" }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="logisticsTime" label="发货时间" width="170" />
|
||||
<el-table-column label="商品明细" min-width="260">
|
||||
<template #default="{ row }">
|
||||
<div v-for="(item, idx) in row.orderPackageItemList || []" :key="idx" class="package-item-line">
|
||||
{{ item.goodsName }} × {{ item.deliverNumber }}
|
||||
<el-card v-if="showDelivery && orderPackages.length" class="mt_10 delivery-record-card">
|
||||
<div class="delivery-section-header">
|
||||
<div class="delivery-section-title-wrap">
|
||||
<span class="delivery-title-bar"></span>
|
||||
<span class="delivery-section-title">发货记录</span>
|
||||
<el-tag size="small" type="primary" effect="plain" round class="delivery-count-badge">
|
||||
共 {{ orderPackages.length }} 个包裹
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="delivery-package-list">
|
||||
<div
|
||||
v-for="(pkg, pkgIdx) in orderPackages"
|
||||
:key="pkg.packageNo || pkg.id || pkgIdx"
|
||||
class="delivery-package-item"
|
||||
>
|
||||
<!-- 包裹顶部信息栏 -->
|
||||
<div class="pkg-bar">
|
||||
<div class="pkg-bar-left">
|
||||
<div class="pkg-index-tag">
|
||||
<el-icon class="pkg-icon"><Box /></el-icon>
|
||||
<span>包裹 {{ orderPackages.length > 1 ? `#${pkgIdx + 1}` : '' }}</span>
|
||||
</div>
|
||||
|
||||
<el-tag
|
||||
size="small"
|
||||
:type="getFulfillmentTagType(pkg.fulfillmentMode)"
|
||||
effect="light"
|
||||
class="pkg-mode-tag"
|
||||
>
|
||||
{{ getFulfillmentModeLabel(pkg.fulfillmentMode) }}
|
||||
</el-tag>
|
||||
|
||||
<div v-if="!isNoDeliveryPackage(pkg)" class="pkg-express-group">
|
||||
<span class="express-name">{{ pkg.logisticsName || "快递物流" }}</span>
|
||||
<span class="express-divider">/</span>
|
||||
<span class="express-no-label">运单号:</span>
|
||||
<span class="express-no">{{ pkg.logisticsNo || "-" }}</span>
|
||||
<el-tooltip content="复制运单号" placement="top" v-if="pkg.logisticsNo">
|
||||
<span
|
||||
class="copy-icon-btn"
|
||||
@click="copyText(pkg.logisticsNo, '运单号')"
|
||||
>
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<div v-else class="pkg-no-express-group">
|
||||
<span class="no-express-text">无需物流配送</span>
|
||||
<span v-if="pkg.deliveryRemark" class="no-express-remark">(备注:{{ pkg.deliveryRemark }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pkg-bar-right">
|
||||
<div v-if="pkg.logisticsTime" class="pkg-time">
|
||||
<el-icon class="clock-icon" style="margin-right: 4px;"><Clock /></el-icon>
|
||||
<span>发货时间:{{ pkg.logisticsTime }}</span>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="!isNoDeliveryPackage(pkg) && pkg.logisticsNo"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
class="pkg-track-btn"
|
||||
@click="checkLogistics"
|
||||
>
|
||||
<el-icon style="margin-right: 3px;"><Van /></el-icon>查看物流
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 包裹内商品明细 -->
|
||||
<div class="pkg-goods-container">
|
||||
<div
|
||||
v-for="(item, itemIdx) in pkg.orderPackageItemList || []"
|
||||
:key="item.orderItemSn || item.id || itemIdx"
|
||||
class="pkg-goods-row"
|
||||
>
|
||||
<!-- 商品缩略图 -->
|
||||
<div class="pkg-goods-img-box">
|
||||
<img
|
||||
:src="getPackageItemThumb(item)"
|
||||
class="pkg-goods-img"
|
||||
alt="商品图片"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 商品详情 -->
|
||||
<div class="pkg-goods-detail">
|
||||
<div class="pkg-goods-title" :title="item.goodsName">
|
||||
{{ item.goodsName }}
|
||||
</div>
|
||||
<div v-if="getPackageItemSpecsText(item)" class="pkg-goods-specs">
|
||||
{{ getPackageItemSpecsText(item) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 发货数量 -->
|
||||
<div class="pkg-goods-num-col">
|
||||
<span class="pkg-num-label">发货数量</span>
|
||||
<span class="pkg-num-val">× {{ item.deliverNumber || 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 物流信息与动态跟踪栏 -->
|
||||
<div class="pkg-logistics-footer">
|
||||
<template v-if="!isNoDeliveryPackage(pkg)">
|
||||
<!-- 若已有物流轨迹节点 -->
|
||||
<div v-if="hasPackageTraces(pkg)" class="pkg-trace-content">
|
||||
<el-tag size="small" type="success" effect="light" class="trace-status-tag">最新轨迹</el-tag>
|
||||
<span class="trace-time">{{ getLatestTrace(pkg).AcceptTime || getLatestTrace(pkg).acceptTime }}</span>
|
||||
<span class="trace-desc" :title="getLatestTrace(pkg).AcceptStation || getLatestTrace(pkg).remark">
|
||||
{{ getLatestTrace(pkg).AcceptStation || getLatestTrace(pkg).remark }}
|
||||
</span>
|
||||
<el-button type="primary" link size="small" class="trace-more-btn" @click="checkLogistics">
|
||||
查看完整轨迹
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 暂无第三方轨迹更新时 -->
|
||||
<div v-else class="pkg-trace-content">
|
||||
<el-tag size="small" type="info" effect="light" class="trace-status-tag">物流动态</el-tag>
|
||||
<span class="trace-desc">
|
||||
已生成 {{ pkg.logisticsName || "快递" }} 运单({{ pkg.logisticsNo }}),等待快递网点揽收更新
|
||||
</span>
|
||||
<el-button type="primary" link size="small" class="trace-more-btn" @click="checkLogistics">
|
||||
查看物流
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 无需配送 -->
|
||||
<template v-else>
|
||||
<div class="pkg-trace-content">
|
||||
<el-tag size="small" type="warning" effect="light" class="trace-status-tag">配送说明</el-tag>
|
||||
<span class="trace-desc">
|
||||
{{ pkg.deliveryRemark ? `配送说明:${pkg.deliveryRemark}` : "无需物流配送,商家已完成线上/线下交付" }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="mt_10 order-goods-card">
|
||||
<div v-loading="loading" class="order-fit-wrap">
|
||||
@@ -246,6 +366,7 @@
|
||||
<img alt="商品图片" :src="row.image" class="order-goods-img" />
|
||||
<div class="order-goods-meta">
|
||||
<div class="div-zoom">
|
||||
<el-tag v-if="isGiftItem(row)" size="small" type="danger" effect="plain" style="margin-right: 5px;">赠品</el-tag>
|
||||
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
|
||||
</div>
|
||||
<span v-for="(item, key) in parseOrderItemSpecs(row.specs)" :key="key">
|
||||
@@ -497,7 +618,10 @@
|
||||
<div class="part-delivery-goods">
|
||||
<img :src="row.image" class="part-delivery-goods-img" alt="" />
|
||||
<div class="part-delivery-goods-info">
|
||||
<div class="part-delivery-goods-name">{{ row.goodsName }}</div>
|
||||
<div class="part-delivery-goods-name">
|
||||
<el-tag v-if="isGiftItem(row)" size="small" type="danger" effect="plain" style="margin-right: 5px;">赠品</el-tag>
|
||||
{{ row.goodsName }}
|
||||
</div>
|
||||
<div
|
||||
v-for="(specValue, specKey) in parseOrderItemSpecs(row.specs)"
|
||||
:key="specKey"
|
||||
@@ -546,7 +670,10 @@
|
||||
<div class="part-delivery-goods">
|
||||
<img :src="row.image" class="part-delivery-goods-img" alt="" />
|
||||
<div class="part-delivery-goods-info">
|
||||
<div class="part-delivery-goods-name">{{ row.goodsName }}</div>
|
||||
<div class="part-delivery-goods-name">
|
||||
<el-tag v-if="isGiftItem(row)" size="small" type="danger" effect="plain" style="margin-right: 5px;">赠品</el-tag>
|
||||
{{ row.goodsName }}
|
||||
</div>
|
||||
<div
|
||||
v-for="(specValue, specKey) in parseOrderItemSpecs(row.specs)"
|
||||
:key="specKey"
|
||||
@@ -849,6 +976,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
|
||||
import { onBeforeRouteLeave, useRoute, useRouter } from "vue-router";
|
||||
import { Box, DocumentCopy, Clock, Van } from "@element-plus/icons-vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import * as API_Logistics from "@/api/logistics";
|
||||
import * as RegExp from "@/libs/RegExp.js";
|
||||
@@ -1120,23 +1248,34 @@ onBeforeRouteLeave((to, from, next) => {
|
||||
function lineCardKeyRows(line) {
|
||||
return line.cardKeys || [];
|
||||
}
|
||||
function copySn() {
|
||||
const text = orderInfo.value?.order?.sn || "";
|
||||
function copyText(text, label = "内容") {
|
||||
if (!text) return;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
Message.success("订单号已复制");
|
||||
navigator.clipboard.writeText(String(text)).then(() => {
|
||||
Message.success(`${label}已复制`);
|
||||
}).catch(() => {
|
||||
fallbackCopyText(String(text), label);
|
||||
});
|
||||
} else {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
Message.success("订单号已复制");
|
||||
fallbackCopyText(String(text), label);
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopyText(text, label = "内容") {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
ta.style.position = "fixed";
|
||||
ta.style.opacity = "0";
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
Message.success(`${label}已复制`);
|
||||
}
|
||||
|
||||
function copySn() {
|
||||
copyText(orderInfo.value?.order?.sn, "订单号");
|
||||
}
|
||||
function copyCardKey(row) {
|
||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
@@ -1153,10 +1292,15 @@ function copyCardKey(row) {
|
||||
Message.success("已复制到剪贴板");
|
||||
}
|
||||
}
|
||||
function isGiftItem(row) {
|
||||
if (!row) return false;
|
||||
return row.promotionType === 'GIFT' || (Number(row.flowPrice) === 0 && row.promotionType === 'GIFT');
|
||||
}
|
||||
function getPromotionText(row) {
|
||||
let resultText = "";
|
||||
if (row && row.promotionType) {
|
||||
const type = row.promotionType.split(",");
|
||||
if (type.indexOf("GIFT") != -1) resultText += "满赠赠品 ";
|
||||
if (type.indexOf("PINTUAN") != -1) resultText += "拼团 ";
|
||||
if (type.indexOf("SECKILL") != -1) resultText += "秒杀 ";
|
||||
if (type.indexOf("COUPON") != -1) resultText += "优惠券 ";
|
||||
@@ -1219,6 +1363,40 @@ function isPackageItemMatch(orderItem, pkgItem) {
|
||||
.map(String);
|
||||
return pkgKeys.some((key) => orderKeys.includes(key));
|
||||
}
|
||||
function getPackageItemThumb(item) {
|
||||
if (item && item.thumbnail) {
|
||||
return item.thumbnail;
|
||||
}
|
||||
const match = (data.value || []).find((row) => isPackageItemMatch(row, item));
|
||||
return match?.image || match?.goodsImage || "";
|
||||
}
|
||||
function getPackageItemSpecsText(item) {
|
||||
const match = (data.value || []).find((row) => isPackageItemMatch(row, item));
|
||||
if (!match || !match.specs) return "";
|
||||
try {
|
||||
const specsObj = parseOrderItemSpecs(match.specs);
|
||||
if (!specsObj || typeof specsObj !== "object") return "";
|
||||
return Object.entries(specsObj)
|
||||
.filter(([k]) => k !== "images")
|
||||
.map(([k, v]) => `${k}: ${v}`)
|
||||
.join(" | ");
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
function getFulfillmentTagType(mode) {
|
||||
if (mode === "NO_DELIVERY") return "info";
|
||||
if (mode === "SPLIT") return "warning";
|
||||
return "primary";
|
||||
}
|
||||
function hasPackageTraces(pkg) {
|
||||
return Boolean(pkg && pkg.traces && Array.isArray(pkg.traces.traces) && pkg.traces.traces.length > 0);
|
||||
}
|
||||
function getLatestTrace(pkg) {
|
||||
if (!hasPackageTraces(pkg)) return {};
|
||||
const list = pkg.traces.traces;
|
||||
return list[0] || {};
|
||||
}
|
||||
function getRemainingDeliveryNum(item) {
|
||||
if (item != null && item.remainDeliverNumber != null && item.remainDeliverNumber !== "") {
|
||||
const remain = Number(item.remainDeliverNumber);
|
||||
@@ -1968,6 +2146,324 @@ function editAddressSubmit () {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.delivery-record-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.delivery-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
|
||||
.delivery-section-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.delivery-title-bar {
|
||||
width: 3px;
|
||||
height: 15px;
|
||||
background: #409eff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.delivery-section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.delivery-count-badge {
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.delivery-package-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.delivery-package-item {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #cbd5e1;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pkg-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
|
||||
.pkg-bar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pkg-index-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
|
||||
.pkg-icon {
|
||||
font-size: 15px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-mode-tag {
|
||||
font-size: 12px;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.pkg-express-group {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
background: #f1f5f9;
|
||||
padding: 3px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e2e8f0;
|
||||
|
||||
.express-name {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.express-divider {
|
||||
margin: 0 6px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.express-no-label {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.express-no {
|
||||
font-family: Consolas, Monaco, "Courier New", monospace;
|
||||
font-weight: 600;
|
||||
color: #0284c7;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.copy-icon-btn {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #3b82f6;
|
||||
transition: transform 0.15s ease, color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: #2563eb;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-no-express-group {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
|
||||
.no-express-text {
|
||||
font-weight: 500;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.no-express-remark {
|
||||
color: #94a3b8;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-bar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
|
||||
.pkg-time {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #64748b;
|
||||
|
||||
.clock-icon {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-track-btn {
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-goods-container {
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.pkg-goods-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px dashed #f1f5f9;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.pkg-goods-img-box {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
overflow: hidden;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.pkg-goods-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-goods-detail {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 12px;
|
||||
margin-right: 16px;
|
||||
|
||||
.pkg-goods-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.pkg-goods-specs {
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
background: #f8fafc;
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #edf2f7;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-goods-num-col {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
|
||||
.pkg-num-label {
|
||||
font-size: 11px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.pkg-num-val {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-logistics-footer {
|
||||
padding: 9px 16px;
|
||||
background: #f8fafc;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
|
||||
.pkg-trace-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
|
||||
.trace-status-tag {
|
||||
font-size: 11px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.trace-time {
|
||||
color: #64748b;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
}
|
||||
|
||||
.trace-desc {
|
||||
color: #334155;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
min-width: 160px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.trace-more-btn {
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.package-item-line {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user