diff --git a/buyer/src/components/goodsDetail/ShowGoods.vue b/buyer/src/components/goodsDetail/ShowGoods.vue index 2049fb989..6705a4305 100644 --- a/buyer/src/components/goodsDetail/ShowGoods.vue +++ b/buyer/src/components/goodsDetail/ShowGoods.vue @@ -70,7 +70,7 @@ v-if="promotionMap['SECKILL']" :time="promotionMap['SECKILL'].endTime" > - +
@@ -89,76 +89,6 @@
{{ $filters.unitPrice(skuDetail.price, "¥") }}
- -
- 优惠券 -
- - {{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }} - 满{{ item.consumeThreshold }}打{{ - item.couponDiscount - }}折 - - - -
-
- -
-

- 拼团 - 拼团价 - - {{ $filters.unitPrice(promotionMap["PINTUAN"].price, "¥") }} - -

-
- -
-

- 促销 - 满减 - 满{{ promotionMap["FULL_DISCOUNT"].fullMoney }}元,立减现金{{ - promotionMap["FULL_DISCOUNT"].fullMinus - }}元 - 满{{ promotionMap["FULL_DISCOUNT"].fullMoney }}元,立享{{ - promotionMap["FULL_DISCOUNT"].fullRate - }}折 -

-
@@ -170,6 +100,69 @@
+ + +
+ +
+ 优惠券 +
+ + {{ item.consumeThreshold > 0 ? '满' + item.consumeThreshold + '减' + item.price : '无门槛,立减' + item.price + '元' }} + 满{{ item.consumeThreshold }}打{{ + item.couponDiscount + }}折 + + + +
+
+ + +
+ 促 销 +
+ +
+ {{ getFullDiscountTag(promotionMap['FULL_DISCOUNT']) }} + {{ formatFullDiscountDesc(promotionMap['FULL_DISCOUNT']) }} +
+ +
+ 拼团 + + 拼团价 + {{ $filters.unitPrice(promotionMap["PINTUAN"].price, "¥") }} + ,多人拼单更实惠 + +
+
+
+
{ 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 { diff --git a/buyer/src/pages/home/orderCenter/MyOrder.vue b/buyer/src/pages/home/orderCenter/MyOrder.vue index 2a996c635..364c5753f 100644 --- a/buyer/src/pages/home/orderCenter/MyOrder.vue +++ b/buyer/src/pages/home/orderCenter/MyOrder.vue @@ -67,7 +67,10 @@ />
-
{{ goods.name }}
+
+ 赠品 + {{ goods.name }} +
共{{ goods.num }}件
{{ refundPriceList(goods.isRefund) }} @@ -151,7 +154,10 @@ />
-
{{ goods.name }}
+
+ 赠品 + {{ goods.name }} +
共{{ goods.num }}件
{{ refundPriceList(goods.isRefund) }} @@ -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': diff --git a/buyer/src/pages/home/orderCenter/OrderDetail.vue b/buyer/src/pages/home/orderCenter/OrderDetail.vue index bd80996a9..10dc7fd8d 100644 --- a/buyer/src/pages/home/orderCenter/OrderDetail.vue +++ b/buyer/src/pages/home/orderCenter/OrderDetail.vue @@ -269,40 +269,54 @@ @click="goodsDetail(goods.skuId, goods.goodsId)" class="hover-color goods-name" > + 赠品 {{ goods.goodsName }}

货号:{{ goods.id }}

- - {{ $filters.unitPrice(goods.goodsPrice, "¥") }} × {{ goods.num }} - - {{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }} + +
- - 投诉 - 申请售后 + +
@@ -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; +} diff --git a/buyer/src/pages/payment/Pay.vue b/buyer/src/pages/payment/Pay.vue index a052f4db0..5f585a085 100644 --- a/buyer/src/pages/payment/Pay.vue +++ b/buyer/src/pages/payment/Pay.vue @@ -147,6 +147,24 @@ {{ goods.goodsSku?.quantity > 0 ? "有货" : "无货" }} {{ $filters.unitPrice(goods.subTotal, "¥") }} + +
+ + + + 赠品 + {{ gift.goodsName || gift.name || '随单实物赠品' }} + + + ¥0.00 + {{ gift.num || 1 }} + 有货 + ¥0.00 +
@@ -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; +} diff --git a/manager/src/views/order/order/orderDetail.vue b/manager/src/views/order/order/orderDetail.vue index 4f301949f..04cf82190 100644 --- a/manager/src/views/order/order/orderDetail.vue +++ b/manager/src/views/order/order/orderDetail.vue @@ -368,6 +368,7 @@ />
+ 赠品 {{ row.goodsName }}
@@ -391,7 +392,11 @@ @@ -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 += "优惠券 "; diff --git a/seller/src/views/order/order/orderDetail.vue b/seller/src/views/order/order/orderDetail.vue index 1cad73b9f..55d83ba54 100644 --- a/seller/src/views/order/order/orderDetail.vue +++ b/seller/src/views/order/order/orderDetail.vue @@ -191,32 +191,152 @@
- - 发货记录 - - - - - - - - - - - - + +
+
+ + 发货时间:{{ pkg.logisticsTime }} +
+ + 查看物流 + +
+
+ + +
+
+ +
+ 商品图片 +
+ + +
+
+ {{ item.goodsName }} +
+
+ {{ getPackageItemSpecsText(item) }} +
+
+ + +
+ 发货数量 + × {{ item.deliverNumber || 1 }} +
+
+
+ + + +
+
@@ -246,6 +366,7 @@ 商品图片
@@ -497,7 +618,10 @@
-
{{ row.goodsName }}
+
+ 赠品 + {{ row.goodsName }} +
-
{{ row.goodsName }}
+
+ 赠品 + {{ row.goodsName }} +
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; }