order-detail.vue 10.5 KB
<template>
  <view class="page-shell order-detail-page">
    <view class="page-blur"></view>
    <view class="page-blur-secondary"></view>
    <view class="page-blur-tertiary"></view>

    <view class="page-content subpage-content">
      <view class="subpage-nav">
        <view class="subpage-back" @tap="goBack">‹</view>
        <view class="subpage-title-wrap">
          <text class="subpage-title">订单详情</text>
          <text class="subpage-subtitle">商品、核销与自提状态,一页为你清晰整理</text>
        </view>
      </view>

      <view class="subpage-hero glass-card">
        <text class="hero-kicker">{{ order.status }}</text>
        <text class="hero-title">{{ order.title }}</text>
        <text class="hero-desc">订单编号 {{ order.orderNo }} · 二维码核销自提</text>
      </view>

      <view class="order-item-card glass-card">
        <view class="order-art">
          <view class="order-art-ring"></view>
          <text class="order-art-label">{{ shortLabel(order.title) }}</text>
        </view>
        <view class="order-copy">
          <text class="order-name">{{ order.title }}</text>
          <text class="order-sku">{{ order.collection }} · {{ order.sku }} × {{ order.quantity }}</text>
          <text class="order-amount">实付 ¥{{ order.amount }}</text>
        </view>
      </view>

      <lf-pickup-code
        v-if="showVerifyCode"
        :code="order.verifyCode"
        :used="verifyUsed"
        :desc="verifyDesc"
      />

      <view v-else-if="order.statusKey === 'pay'" class="subpage-card wait-card">
        <text class="wait-title">支付完成后自动生成核销二维码</text>
        <text class="wait-desc">当前订单尚未完成支付,支付成功后将自动展示门店核销码与自提信息。</text>
      </view>

      <lf-pickup-complete
        v-else
        title="该订单已完成核销"
        desc="门店已完成核销与自提,本页不再展示二维码,可在下方查看核销时间与订单状态。"
        :status="order.status"
        :time="order.pickupTime || order.verifyTime"
      />

      <view class="subpage-card">
        <view class="section-head">
          <text class="section-title">核销信息</text>
          <text class="section-link">门店自提</text>
        </view>
        <view class="info-list">
          <view class="info-row">
            <text class="info-label">核销方式</text>
            <text class="info-value">二维码核销自提</text>
          </view>
          <view class="info-row">
            <text class="info-label">适用门店</text>
            <text class="info-value">任意门店可核销</text>
          </view>
          <view class="info-row">
            <text class="info-label">使用时间</text>
            <text class="info-value">任意时间可用</text>
          </view>
          <view v-if="order.verifyCode" class="info-row">
            <text class="info-label">核销码</text>
            <text class="info-value">{{ order.verifyCode }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">下单时间</text>
            <text class="info-value">{{ order.createdAt || order.time }}</text>
          </view>
          <view v-if="order.verifyTime" class="info-row">
            <text class="info-label">核销时间</text>
            <text class="info-value">{{ order.verifyTime }}</text>
          </view>
          <view v-if="order.pickupTime" class="info-row">
            <text class="info-label">自提完成</text>
            <text class="info-value">{{ order.pickupTime }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">订单状态</text>
            <text class="info-value">{{ order.status }}</text>
          </view>
        </view>
      </view>

      <view class="subpage-card">
        <view class="section-head">
          <text class="section-title">流程进度</text>
          <text class="section-link">状态说明</text>
        </view>
        <view class="step-list">
          <view v-for="item in processSteps" :key="item.title" class="step-item" :class="{ active: item.active }">
            <view class="step-dot"></view>
            <view class="step-copy">
              <text class="step-title">{{ item.title }}</text>
              <text class="step-desc">{{ item.desc }}</text>
            </view>
          </view>
        </view>
      </view>

      <view class="cta-row">
        <view class="secondary-button" @tap="secondaryAction">{{ secondaryLabel }}</view>
        <view class="primary-button" @tap="primaryAction">{{ primaryLabel }}</view>
      </view>
    </view>
  </view>
</template>

<script>
import { getDisplayOrderById, saveLatestPickupOrder } from '@/packageMall/data/orderFlow'

export default {
  data() {
    return {
      order: getDisplayOrderById(5001)
    }
  },
  computed: {
    showVerifyCode() {
      return this.order.statusKey === 'verify'
    },
    verifyUsed() {
      return ['pickup'].indexOf(this.order.statusKey) > -1
    },
    verifyDesc() {
      if (this.verifyUsed) {
        return `门店已完成核销与自提${this.order.pickupTime ? ` · ${this.order.pickupTime}` : ''}`
      }
      return '到店出示二维码即可核销自提,支持任意时间任意门店使用。'
    },
    primaryLabel() {
      switch (this.order.statusKey) {
        case 'pay':
          return '立即支付'
        case 'verify':
        case 'pickup':
          return '继续选购'
        default:
          return '返回订单'
      }
    },
    secondaryLabel() {
      return this.order.statusKey === 'verify' ? '复制核销码' : '联系客服'
    },
    processSteps() {
      const statusMap = {
        pay: 1,
        verify: 3,
        pickup: 4
      }
      const currentStep = statusMap[this.order.statusKey] || 1
      return [
        { title: '提交订单', desc: '商品已加入本次门店自提订单。', active: currentStep >= 1 },
        { title: '完成支付', desc: '支付成功后,系统会自动生成核销二维码。', active: currentStep >= 2 },
        { title: '到店核销', desc: '支持任意时间前往任意门店出示二维码并完成核销。', active: currentStep >= 3 },
        { title: '完成自提', desc: '核销成功后即可领取商品,订单进入完成状态。', active: currentStep >= 4 }
      ]
    }
  },
  onLoad(options) {
    this.order = getDisplayOrderById(options.id)
  },
  onShow() {
    this.order = getDisplayOrderById(this.order.id)
  },
  methods: {
    shortLabel(title) {
      return String(title || '绿纤').replace(/\s+/g, '').slice(0, 2)
    },
    goBack() {
      uni.navigateBack({ fail: () => uni.redirectTo({ url: '/packageProfile/order-list/order-list' }) })
    },
    goList() {
      uni.redirectTo({ url: '/packageProfile/order-list/order-list' })
    },
    primaryAction() {
      if (this.order.statusKey === 'pay') {
        saveLatestPickupOrder({
          orderId: this.order.id,
          createdAt: this.order.createdAt || this.order.time,
          status: '待核销',
          statusKey: 'verify',
          statusNote: '支付成功,核销二维码已生成,到店出示后即可自提。'
        })
        uni.navigateTo({ url: `/packageMall/payment-success/payment-success?orderId=${this.order.id}` })
        return
      }
      if (['verify', 'pickup'].indexOf(this.order.statusKey) > -1) {
        uni.reLaunch({ url: '/packageMall/main/main' })
        return
      }
      this.goList()
    },
    secondaryAction() {
      if (this.order.statusKey === 'verify' && this.order.verifyCode) {
        uni.setClipboardData({ data: this.order.verifyCode, success: () => uni.showToast({ title: '核销码已复制', icon: 'none' }) })
        return
      }
      uni.showToast({ title: '已为您接入专属客服', icon: 'none' })
    }
  }
}
</script>

<style lang="scss" scoped>
@import '@/styles/mixins.scss';
@import '@/styles/subpage.scss';

.order-item-card {
  display: flex;
  padding: 28rpx;
  margin-bottom: 24rpx;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 320ms ease;
}

.order-item-card:active {
  transform: translateY(-4rpx);
}

.order-art {
  width: 180rpx;
  height: 180rpx;
  border-radius: 28rpx;
  flex-shrink: 0;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(241, 246, 241, 0.96);
  border: 1rpx solid rgba(120, 146, 121, 0.14);
  box-shadow: inset 0 1rpx 0 rgba(255, 255, 255, 0.65);
  overflow: hidden;
}

.order-art-ring {
  width: 96rpx;
  height: 96rpx;
  border-radius: 50%;
  border: 1rpx solid rgba(120, 146, 121, 0.18);
  background: rgba(255, 255, 255, 0.45);
  animation: lfPulseSoft 5.2s ease-in-out infinite;
}

.order-art-label {
  position: absolute;
  font-size: 24rpx;
  letter-spacing: 4rpx;
  color: $brand-primary-deep;
  font-weight: 600;
}

.order-art::before {
  content: '';
  position: absolute;
  right: -30rpx;
  top: -30rpx;
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(210, 191, 156, 0.22), rgba(210, 191, 156, 0));
  animation: lfFloatSoft 9.8s ease-in-out infinite;
}

.order-copy {
  flex: 1;
  margin-left: 20rpx;
}

.order-name,
.order-sku,
.order-amount,
.wait-title,
.wait-desc,
.step-title,
.step-desc {
  display: block;
}

.order-name {
  font-size: 30rpx;
  line-height: 1.5;
  font-weight: 700;
}

.order-sku {
  margin-top: 16rpx;
  font-size: 24rpx;
  color: $text-secondary;
}

.order-amount {
  margin-top: 18rpx;
  font-size: 34rpx;
  font-weight: 700;
  color: $brand-primary-deep;
}


.wait-card {
  margin-bottom: 24rpx;
}

.wait-title {
  font-size: 30rpx;
  font-weight: 700;
  color: $text-primary;
}

.wait-desc {
  margin-top: 14rpx;
  font-size: 24rpx;
  line-height: 1.8;
  color: $text-secondary;
}

.step-item {
  display: flex;
  align-items: flex-start;
  padding: 18rpx 0;
  opacity: 0.62;
}

.step-item + .step-item {
  border-top: 1rpx solid rgba(100, 140, 118, 0.08);
}

.step-item.active {
  opacity: 1;
}

.step-dot {
  width: 22rpx;
  height: 22rpx;
  margin: 12rpx 18rpx 0 0;
  flex-shrink: 0;
  border-radius: 50%;
  background: rgba(210, 191, 156, 0.2);
  box-shadow: inset 0 0 0 6rpx rgba(255, 255, 255, 0.8);
}

.step-item.active .step-dot {
  background: $brand-primary-deep;
  box-shadow: inset 0 0 0 6rpx rgba(255, 255, 255, 0.86), 0 0 0 8rpx rgba(100, 140, 118, 0.08);
}

.step-copy {
  flex: 1;
}

.step-title {
  font-size: 28rpx;
  font-weight: 600;
  color: $text-primary;
}

.step-desc {
  margin-top: 10rpx;
  font-size: 23rpx;
  line-height: 1.8;
  color: $text-secondary;
}
</style>