shinnosuke
2026-08-06 e5c6b415449d0b7a5e9027dba009e46543004dc0
talent-card-admin/src/views/OrderDetailView.vue
@@ -15,7 +15,16 @@
const items = computed(() => detail.value?.items || [])
const payments = computed(() => detail.value?.payments || [])
const refunds = computed(() => detail.value?.refunds || [])
const discountAmount = computed(() => Math.max(0, Number(detail.value?.orderAmount || 0) - Number(detail.value?.subsidyAmount || 0) - Number(detail.value?.payAmount || 0)))
const previewVisible = ref(false)
const previewUrl = ref('')
const talentDiscountAmount = computed(() => Math.max(0, Number(detail.value?.orderAmount || 0) - Number(detail.value?.basePayAmount ?? detail.value?.orderAmount ?? 0)))
const couponAmount = computed(() => Math.max(0, Number(detail.value?.couponAmount || 0)))
const talentDiscountRate = computed(() => {
  const original = Number(detail.value?.orderAmount || 0)
  const base = Number(detail.value?.basePayAmount ?? original)
  if (!original || !base) return '-'
  return `${(base / original * 10).toFixed(1).replace(/\.0$/, '')}折`
})
function money(value: unknown) {
  if (value === null || value === undefined || value === '') return '-'
@@ -36,6 +45,17 @@
function paymentStatus(value: string) {
  return ({ pending: '待支付', processing: '支付中', paid: '支付成功', failed: '支付失败', closed: '已关闭' } as Record<string, string>)[value] || value || '-'
}
function previewImage(url: string) {
  if (!url) return
  previewUrl.value = url
  previewVisible.value = true
}
function paymentTime(payment: Detail) {
  if (detail.value?.orderStatus === 0 || payment.status === 'pending') return '-'
  return dateTime(payment.paidAt)
}
function refundStatus(value: number) {
@@ -127,12 +147,17 @@
          <el-table-column prop="name" label="商品名称" min-width="220" show-overflow-tooltip />
          <el-table-column label="封面图" width="130" align="center">
            <template #default="{ row }">
              <el-image v-if="row.coverUrl" :src="row.coverUrl" fit="cover" class="product-cover" preview-teleported />
              <el-image v-if="row.coverUrl" :src="row.coverUrl" fit="cover" class="product-cover" :preview-src-list="[row.coverUrl]" preview-teleported />
              <span v-else class="empty-cover">-</span>
            </template>
          </el-table-column>
          <el-table-column label="商品金额" min-width="130" align="right">
            <template #default="{ row }">{{ money(row.amount) }}</template>
          </el-table-column>
          <el-table-column label="操作" width="100" align="center">
            <template #default="{ row }">
              <el-button link type="primary" :disabled="!row.coverUrl" @click="previewImage(row.coverUrl)">查看</el-button>
            </template>
          </el-table-column>
        </el-table>
      </section>
@@ -144,9 +169,14 @@
          <div><span>商品金额</span><strong>{{ money(detail.orderAmount) }}</strong></div>
          <div><span>商品数量</span><strong>{{ items.length }}</strong></div>
          <div><span>小计</span><strong>{{ money(detail.orderAmount) }}</strong></div>
          <div><span>优惠信息</span><strong>{{ discountAmount ? `优惠 ¥${(discountAmount / 100).toFixed(2)}` : '-' }}</strong></div>
          <div><span>优惠金额</span><strong>{{ discountAmount ? `-¥${(discountAmount / 100).toFixed(2)}` : '-' }}</strong></div>
          <div><span>政府补贴</span><strong>{{ money(detail.subsidyAmount) }}</strong></div>
          <div class="discount-summary-row">
            <span>优惠信息</span>
            <div class="discount-summary-content">
              <div><span>人才折扣</span><strong>{{ talentDiscountRate }}</strong><span>折扣金额</span><strong class="discount-value">{{ talentDiscountAmount ? `-¥${(talentDiscountAmount / 100).toFixed(2)}` : '-' }}</strong></div>
              <div v-if="detail.couponName || couponAmount"><span>商品优惠</span><strong>{{ detail.couponName || '-' }}</strong><span>优惠金额</span><strong class="discount-value">{{ couponAmount ? `-¥${(couponAmount / 100).toFixed(2)}` : '-' }}</strong></div>
              <div><span>补贴金额</span><strong>{{ money(detail.subsidyAmount) }}</strong></div>
            </div>
          </div>
          <div><span>实际应付</span><strong class="amount-strong">{{ money(detail.payAmount) }}</strong></div>
          <div><span>下单时间</span><strong>{{ dateTime(detail.createdAt) }}</strong></div>
          <div><span>核销时间</span><strong>{{ dateTime(detail.verifyTime) }}</strong></div>
@@ -158,7 +188,7 @@
        <h3>支付信息</h3>
        <div v-if="payments.length" class="records-stack">
          <div v-for="payment in payments" :key="payment.paymentNo" class="info-card info-grid payment-grid">
            <div><span>支付时间</span><strong>{{ dateTime(payment.paidAt || payment.createdAt) }}</strong></div>
            <div><span>支付时间</span><strong>{{ paymentTime(payment) }}</strong></div>
            <div><span>支付方式</span><strong>{{ payment.provider === 'HUIFU' ? '微信支付' : payment.provider || '-' }}</strong></div>
            <div><span>支付流水号</span><strong>{{ payment.providerTransactionNo || payment.paymentNo || '-' }}</strong></div>
            <div><span>支付金额</span><strong class="amount-strong">{{ money(payment.amount) }}</strong></div>
@@ -190,6 +220,9 @@
        <el-button v-if="canRefund()" type="danger" plain @click="refund">退款</el-button>
      </div>
    </template>
    <el-dialog v-model="previewVisible" title="封面图预览" width="640px" destroy-on-close>
      <el-image v-if="previewUrl" :src="previewUrl" fit="contain" class="order-preview-image" />
    </el-dialog>
  </div>
</template>
@@ -209,9 +242,15 @@
.detail-items { margin-top: 10px; }
.detail-items :deep(th.el-table__cell) { color: #1d2129; font-weight: 600; background: #f3f4f6; }
.product-cover { width: 48px; height: 38px; vertical-align: middle; }
.order-preview-image { display: block; width: 100%; max-height: 520px; }
.empty-cover { color: #86909c; }
.order-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.order-grid div:nth-child(5), .order-grid div:nth-child(9) { grid-column: span 2; }
.discount-summary-row { grid-column: 1 / -1; display: flex; align-items: flex-start !important; gap: 14px !important; }
.discount-summary-content { display: grid; flex: 1; gap: 10px; }
.discount-summary-content > div { display: grid; grid-template-columns: 72px minmax(110px, 1fr) 72px minmax(100px, 1fr); align-items: baseline; gap: 10px; }
.discount-summary-content span { color: #4e5969; font-size: 13px; }
.discount-summary-content strong { color: #1d2129; font-size: 13px; font-weight: 500; }
.discount-summary-content .discount-value { color: #d99000; }
.payment-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.refund-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.amount-strong { color: #1d2129; font-weight: 600 !important; }