177 lines
5.7 KiB
TypeScript
177 lines
5.7 KiB
TypeScript
import React from 'react';
|
|
import { t } from '@/i18n';
|
|
import Taro from '@tarojs/taro';
|
|
// Taro mini-program component (WeChat / Alipay)
|
|
|
|
/**
|
|
* E2. 小程序核心页面 - 持有券详情
|
|
*
|
|
* QR code card (gradient bg) + coupon info + action buttons + usage rules
|
|
* 支持转赠 / 出售操作
|
|
*/
|
|
|
|
const infoRows = [
|
|
{ label: t('coupon_face_value'), value: '¥25.00' },
|
|
{ label: t('my_coupon_purchase_price'), value: '¥21.25' },
|
|
{ label: t('coupon_valid_until'), value: '2026/12/31' },
|
|
{ label: t('my_coupon_order_no'), value: 'GNX-20260209-001234' },
|
|
{ label: t('my_coupon_resell_count'), value: '3次' },
|
|
];
|
|
|
|
const usageRules = [
|
|
t('my_coupon_use_in_store'),
|
|
t('my_coupon_use_in_time'),
|
|
t('my_coupon_one_per_visit'),
|
|
t('my_coupon_no_cash'),
|
|
];
|
|
|
|
const MyCouponDetailPage: React.FC = () => {
|
|
const handleTransfer = () => {
|
|
Taro.navigateTo({ url: '/pages/transfer/index' });
|
|
};
|
|
|
|
const handleSell = () => {
|
|
Taro.showModal({
|
|
title: t('my_coupon_sell'),
|
|
content: t('my_coupon_sell_in_app'),
|
|
showCancel: false,
|
|
confirmText: t('confirm'),
|
|
});
|
|
};
|
|
|
|
return (
|
|
<view className="coupon-detail-page">
|
|
{/* QR Code Card */}
|
|
<view className="qr-card">
|
|
<view className="qr-header">
|
|
<view className="qr-brand-row">
|
|
<text className="qr-brand">Starbucks</text>
|
|
<view className="qr-status">
|
|
<text className="qr-status-text">{t('my_coupon_active')}</text>
|
|
</view>
|
|
</view>
|
|
<text className="qr-name">星巴克 ¥25 礼品卡</text>
|
|
</view>
|
|
|
|
<view className="qr-code-area">
|
|
<view className="qr-placeholder">
|
|
<text className="qr-placeholder-text">QR CODE</text>
|
|
</view>
|
|
</view>
|
|
|
|
<text className="qr-code-text">GNX-STB-A1B2C3D4</text>
|
|
<text className="qr-hint">{t('my_coupon_show_qr_hint')}</text>
|
|
</view>
|
|
|
|
{/* Info Card */}
|
|
<view className="info-card">
|
|
{infoRows.map((item, i) => (
|
|
<view key={i} className="info-row">
|
|
<text className="info-label">{item.label}</text>
|
|
<text className="info-value">{item.value}</text>
|
|
</view>
|
|
))}
|
|
</view>
|
|
|
|
{/* Action Buttons */}
|
|
<view className="action-row">
|
|
<view className="action-btn action-btn-secondary" onClick={handleTransfer}>
|
|
<text className="action-btn-secondary-text">{t('my_coupon_transfer')}</text>
|
|
</view>
|
|
<view className="action-btn action-btn-outline" onClick={handleSell}>
|
|
<text className="action-btn-outline-text">{t('my_coupon_sell')}</text>
|
|
</view>
|
|
</view>
|
|
|
|
{/* Usage Rules */}
|
|
<view className="rules-card">
|
|
<text className="rules-title">{t('my_coupon_usage_note')}</text>
|
|
{usageRules.map((rule, i) => (
|
|
<view key={i} className="rule-item">
|
|
<text className="rule-dot">•</text>
|
|
<text className="rule-text">{rule}</text>
|
|
</view>
|
|
))}
|
|
</view>
|
|
</view>
|
|
);
|
|
};
|
|
|
|
export default MyCouponDetailPage;
|
|
|
|
/*
|
|
CSS (index.scss):
|
|
|
|
.coupon-detail-page { background: #F8F9FC; min-height: 100vh; padding: 0 32rpx 40rpx; }
|
|
|
|
.qr-card {
|
|
margin-top: 24rpx; padding: 32rpx;
|
|
background: linear-gradient(135deg, #6C5CE7, #9B8FFF);
|
|
border-radius: 24rpx;
|
|
display: flex; flex-direction: column; align-items: center;
|
|
}
|
|
.qr-header { width: 100%; margin-bottom: 24rpx; }
|
|
.qr-brand-row {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
}
|
|
.qr-brand { font-size: 26rpx; color: rgba(255,255,255,0.8); }
|
|
.qr-status {
|
|
padding: 4rpx 16rpx; background: rgba(0,196,140,0.2);
|
|
border: 1rpx solid rgba(0,196,140,0.5); border-radius: 999rpx;
|
|
}
|
|
.qr-status-text { font-size: 22rpx; color: #00E6A0; font-weight: 600; }
|
|
.qr-name {
|
|
font-size: 32rpx; font-weight: 600; color: white; margin-top: 8rpx;
|
|
}
|
|
|
|
.qr-code-area {
|
|
width: 360rpx; height: 360rpx;
|
|
background: white; border-radius: 16rpx;
|
|
display: flex; align-items: center; justify-content: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
.qr-placeholder {
|
|
width: 280rpx; height: 280rpx;
|
|
border: 4rpx dashed #E4E7F0; border-radius: 8rpx;
|
|
display: flex; align-items: center; justify-content: center;
|
|
}
|
|
.qr-placeholder-text { font-size: 28rpx; color: #A0A8BE; font-weight: 600; }
|
|
|
|
.qr-code-text {
|
|
font-size: 28rpx; color: white; font-weight: 600;
|
|
font-family: 'Menlo', 'Courier New', monospace;
|
|
letter-spacing: 2rpx; margin-bottom: 8rpx;
|
|
}
|
|
.qr-hint { font-size: 22rpx; color: rgba(255,255,255,0.7); }
|
|
|
|
.info-card {
|
|
background: white; border-radius: 16rpx; padding: 24rpx;
|
|
border: 1rpx solid #F1F3F8; margin-top: 20rpx;
|
|
}
|
|
.info-row {
|
|
display: flex; justify-content: space-between;
|
|
padding: 14rpx 0; border-bottom: 1rpx solid #F1F3F8;
|
|
}
|
|
.info-row:last-child { border-bottom: none; }
|
|
.info-label { font-size: 26rpx; color: #5C6478; }
|
|
.info-value { font-size: 26rpx; color: #141723; font-weight: 500; }
|
|
|
|
.action-row {
|
|
display: flex; gap: 20rpx; margin-top: 20rpx;
|
|
}
|
|
.action-btn { flex: 1; height: 88rpx; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; }
|
|
.action-btn-secondary { background: #F3F1FF; }
|
|
.action-btn-secondary-text { color: #6C5CE7; font-size: 28rpx; font-weight: 600; }
|
|
.action-btn-outline { background: white; border: 2rpx solid #F1F3F8; }
|
|
.action-btn-outline-text { color: #5C6478; font-size: 28rpx; font-weight: 600; }
|
|
|
|
.rules-card {
|
|
background: white; border-radius: 16rpx; padding: 24rpx;
|
|
border: 1rpx solid #F1F3F8; margin-top: 20rpx;
|
|
}
|
|
.rules-title { font-size: 28rpx; font-weight: 600; color: #141723; margin-bottom: 16rpx; }
|
|
.rule-item { display: flex; align-items: flex-start; margin-bottom: 12rpx; }
|
|
.rule-dot { color: #A0A8BE; margin-right: 12rpx; font-size: 24rpx; }
|
|
.rule-text { font-size: 24rpx; color: #5C6478; line-height: 1.5; }
|
|
*/
|