Files
zy-client-a/0000_gb_points_temp/src/components/pay/PayTheme1.vue
telangpu 2fd1a741cf update
2026-04-27 16:33:26 +08:00

237 lines
17 KiB
Vue

<template>
<div class="pt1__page">
<section class="pt1__hero">
<div class="pt1__hero-deco">
<div class="pt1__deco-circle pt1__deco-circle--1"></div>
<div class="pt1__deco-circle pt1__deco-circle--2"></div>
<div class="pt1__deco-circle pt1__deco-circle--3"></div>
</div>
<div class="pt1__hero-content">
<p class="pt1__balance-label">{{ t('pay_theme1.balance_label') }}</p>
<div class="pt1__points-row">
<svg class="pt1__star" width="32" height="32" viewBox="0 0 24 24" fill="none"><path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z" fill="currentColor"/></svg>
<span class="pt1__points-val">{{ totalPoint.toLocaleString() }}</span>
</div>
<p class="pt1__points-sub">{{ t('pay_theme1.points_available') }}</p>
</div>
</section>
<section class="pt1__expiry-wrap">
<div class="pt1__expiry-card">
<div class="pt1__expiry-top">
<div class="pt1__warn-icon-wrap">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M10.29 3.86L1.82 18A2 2 0 0 0 3.53 21H20.47A2 2 0 0 0 22.18 18L13.71 3.86A2 2 0 0 0 10.29 3.86Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 9V13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 17H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="pt1__expiry-text">
<h3 class="pt1__expiry-title">{{ t('pay_theme1.expiry_title') }}</h3>
<p class="pt1__expiry-desc">{{ t('pay_theme1.expiry_desc') }}</p>
</div>
</div>
<div class="pt1__countdown">
<div class="pt1__countdown-hd">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M12 6V12L16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span class="pt1__countdown-label">{{ t('pay_theme1.time_remaining') }}</span>
</div>
<div class="pt1__countdown-display">
<div class="pt1__time-block"><span class="pt1__time-val">{{ countdown.days }}</span><span class="pt1__time-unit">{{ t('pay_theme1.days') }}</span></div>
<span class="pt1__sep">:</span>
<div class="pt1__time-block"><span class="pt1__time-val">{{ countdown.hours }}</span><span class="pt1__time-unit">{{ t('pay_theme1.hours') }}</span></div>
<span class="pt1__sep">:</span>
<div class="pt1__time-block"><span class="pt1__time-val">{{ countdown.minutes }}</span><span class="pt1__time-unit">{{ t('pay_theme1.minutes') }}</span></div>
</div>
</div>
</div>
</section>
<section class="pt1__cta-wrap">
<button type="button" class="pt1__cta-btn" @click="handleSubmit">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M13 2L3 14H12L11 22L21 10H12L13 2Z" fill="currentColor"/></svg>
{{ t('pay_theme1.redeem_now') }}
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</section>
<section class="pt1__rewards-wrap">
<div class="pt1__rewards-hd">
<h3 class="pt1__rewards-title">{{ t('pay_theme1.available_rewards') }}</h3>
<span class="pt1__rewards-count">{{ t('pay_theme1.rewards_count') }}</span>
</div>
<div class="pt1__rewards-list">
<div :class="['pt1__card', selectedCard === 0 && 'pt1__card--selected']" @click="selectCard(0)">
<div class="pt1__card-row">
<div class="pt1__icon-wrap pt1__icon-wrap--featured">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M20 6H17.82C17.93 5.69 18 5.35 18 5C18 3.34 16.66 2 15 2C13.95 2 13.04 2.54 12.5 3.35L12 4.02L11.5 3.34C10.96 2.54 10.05 2 9 2C7.34 2 6 3.34 6 5C6 5.35 6.07 5.69 6.18 6H4C2.89 6 2.01 6.89 2.01 8L2 19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V8C22 6.89 21.11 6 20 6ZM15 4C15.55 4 16 4.45 16 5C16 5.55 15.55 6 15 6C14.45 6 14 5.55 14 5C14 4.45 14.45 4 15 4ZM9 4C9.55 4 10 4.45 10 5C10 5.55 9.55 6 9 6C8.45 6 8 5.55 8 5C8 4.45 8.45 4 9 4ZM20 19H4V8H20V19Z" fill="currentColor"/></svg>
</div>
<div class="pt1__card-info">
<div class="pt1__card-title-row">
<h4 class="pt1__card-name">{{ t('pay_theme1.browse_products') }}</h4>
<span class="pt1__badge pt1__badge--popular">{{ t('pay_theme1.badge_popular') }}</span>
</div>
<p class="pt1__card-desc">{{ t('pay_theme1.browse_desc') }}</p>
</div>
<div class="pt1__pts"><p class="pt1__pts-val">-</p><p class="pt1__pts-label">{{ t('points') }}</p></div>
</div>
<div class="pt1__confirm-row" v-if="selectedCard === 0">
<button type="button" class="pt1__confirm-btn" @click.stop="handleSubmit">{{ t('pay_theme1.confirm_redeem') }}</button>
</div>
</div>
<div class="pt1__card pt1__card--disabled">
<div class="pt1__card-row">
<div class="pt1__icon-wrap"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M6 2L3 6V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V6L18 2H6ZM6 4H18L20 7H4L6 4ZM5 9H19V20H5V9ZM9 11V18H11V11H9ZM13 11V18H15V11H13Z" fill="currentColor"/></svg></div>
<div class="pt1__card-info">
<div class="pt1__card-title-row"><h4 class="pt1__card-name">{{ t('pay_theme1.reward1_name') }}</h4><span class="pt1__badge pt1__badge--soldout">{{ t('pay_theme1.badge_soldout') }}</span></div>
<p class="pt1__card-desc">{{ t('pay_theme1.reward1_desc') }}</p>
</div>
<div class="pt1__pts"><p class="pt1__pts-val pt1__pts-val--gray">2,500</p><p class="pt1__pts-label">{{ t('points') }}</p></div>
</div>
</div>
<div class="pt1__card pt1__card--disabled">
<div class="pt1__card-row">
<div class="pt1__icon-wrap"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M1 9L2 10C5.5 6.5 10.5 6.5 14 10L15 9C10.5 4.5 4.5 4.5 1 9ZM5 13L6 14C7.5 12.5 9.5 12.5 11 14L12 13C9.5 10.5 6.5 10.5 5 13ZM9 17L12 20L15 17C13.5 15.5 10.5 15.5 9 17Z" fill="currentColor"/></svg></div>
<div class="pt1__card-info">
<div class="pt1__card-title-row"><h4 class="pt1__card-name">{{ t('pay_theme1.reward2_name') }}</h4><span class="pt1__badge pt1__badge--soldout">{{ t('pay_theme1.badge_soldout') }}</span></div>
<p class="pt1__card-desc">{{ t('pay_theme1.reward2_desc') }}</p>
</div>
<div class="pt1__pts"><p class="pt1__pts-val pt1__pts-val--gray">1,500</p><p class="pt1__pts-label">{{ t('points') }}</p></div>
</div>
</div>
<div class="pt1__card pt1__card--disabled">
<div class="pt1__card-row">
<div class="pt1__icon-wrap"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M18 4L4 20M9 7C9 8.1 8.1 9 7 9C5.9 9 5 8.1 5 7C5 5.9 5.9 5 7 5C8.1 5 9 5.9 9 7ZM19 17C19 18.1 18.1 19 17 19C15.9 19 15 18.1 15 17C15 15.9 15.9 15 17 15C18.1 15 19 15.9 19 17Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div class="pt1__card-info">
<div class="pt1__card-title-row"><h4 class="pt1__card-name">{{ t('pay_theme1.reward3_name') }}</h4><span class="pt1__badge pt1__badge--soldout">{{ t('pay_theme1.badge_soldout') }}</span></div>
<p class="pt1__card-desc">{{ t('pay_theme1.reward3_desc') }}</p>
</div>
<div class="pt1__pts"><p class="pt1__pts-val pt1__pts-val--gray">2,000</p><p class="pt1__pts-label">{{ t('points') }}</p></div>
</div>
</div>
<div class="pt1__card pt1__card--disabled">
<div class="pt1__card-row">
<div class="pt1__icon-wrap"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M6.62 10.79C8.06 13.62 10.38 15.94 13.21 17.38L15.41 15.18C15.69 14.9 16.08 14.82 16.43 14.93C17.55 15.3 18.76 15.51 20 15.51C20.55 15.51 21 15.96 21 16.51V20C21 20.55 20.55 21 20 21C10.61 21 3 13.39 3 4C3 3.45 3.45 3 4 3H7.5C8.05 3 8.5 3.45 8.5 4C8.5 5.24 8.7 6.45 9.07 7.57C9.18 7.92 9.1 8.31 8.82 8.59L6.62 10.79Z" fill="currentColor"/></svg></div>
<div class="pt1__card-info">
<div class="pt1__card-title-row"><h4 class="pt1__card-name">{{ t('pay_theme1.reward4_name') }}</h4><span class="pt1__badge pt1__badge--soldout">{{ t('pay_theme1.badge_soldout') }}</span></div>
<p class="pt1__card-desc">{{ t('pay_theme1.reward4_desc') }}</p>
</div>
<div class="pt1__pts"><p class="pt1__pts-val pt1__pts-val--gray">1,200</p><p class="pt1__pts-label">{{ t('points') }}</p></div>
</div>
</div>
</div>
</section>
<section class="pt1__how-wrap">
<div class="pt1__how-card">
<h4 class="pt1__how-title">{{ t('pay_theme1.how_it_works') }}</h4>
<div class="pt1__how-steps">
<div class="pt1__step"><div class="pt1__step-num">1</div><p class="pt1__step-text">{{ t('pay_theme1.step1') }}</p></div>
<div class="pt1__step"><div class="pt1__step-num">2</div><p class="pt1__step-text">{{ t('pay_theme1.step2') }}</p></div>
<div class="pt1__step"><div class="pt1__step-num">3</div><p class="pt1__step-text">{{ t('pay_theme1.step3') }}</p></div>
</div>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
import type { ThemeColors } from '@/config/themes'
interface Props {
colors: ThemeColors
phone: string
payDate: string
invoiceNumber: string
domainName: string
totalPoint: number
}
const props = defineProps<Props>()
const emit = defineEmits<{ (e: 'submit'): void }>()
const { t } = useI18n()
const selectedCard = ref<number | null>(null)
const COUNTDOWN_KEY = 'payTheme1_countdown_target'
const TARGET_SECONDS = 2 * 24 * 3600 + 6 * 3600 + 2 * 60
function getTargetTime(): number {
const stored = localStorage.getItem(COUNTDOWN_KEY)
if (stored) return Number(stored)
const target = Date.now() + TARGET_SECONDS * 1000
localStorage.setItem(COUNTDOWN_KEY, String(target))
return target
}
const countdown = reactive({ days: '02', hours: '06', minutes: '02' })
let timer: ReturnType<typeof setInterval> | null = null
function pad(n: number) { return String(n).padStart(2, '0') }
function tick() {
const diff = Math.max(0, Math.floor((getTargetTime() - Date.now()) / 1000))
countdown.days = pad(Math.floor(diff / 86400))
countdown.hours = pad(Math.floor((diff % 86400) / 3600))
countdown.minutes = pad(Math.floor((diff % 3600) / 60))
}
onMounted(() => { tick(); timer = setInterval(tick, 1000) })
onUnmounted(() => { if (timer) clearInterval(timer) })
const selectCard = (index: number) => { selectedCard.value = selectedCard.value === index ? null : index }
const handleSubmit = () => emit('submit')
</script>
<style scoped>
.pt1__page { background: #f2f2f2; min-height: 100vh; font-family: Arial, Helvetica, sans-serif; color: #111; }
.pt1__hero { position: relative; background: var(--global-primary-color, #e60000); padding: 40px 20px 44px; overflow: hidden; text-align: center; }
.pt1__hero-deco { position: absolute; inset: 0; pointer-events: none; }
.pt1__deco-circle { position: absolute; border-radius: 50%; background: rgba(255,255,255,0.1); }
.pt1__deco-circle--1 { width: 200px; height: 200px; top: -70px; right: -60px; }
.pt1__deco-circle--2 { width: 130px; height: 130px; bottom: -50px; left: -35px; }
.pt1__deco-circle--3 { width: 90px; height: 90px; top: 30px; left: 50px; }
.pt1__hero-content { position: relative; }
.pt1__balance-label { font-size: 14px; color: rgba(255,255,255,0.85); margin: 0 0 8px; }
.pt1__points-row { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 8px; }
.pt1__star { color: #fff; }
.pt1__points-val { font-size: 52px; font-weight: 900; color: #fff; line-height: 1; }
.pt1__points-sub { font-size: 14px; color: rgba(255,255,255,0.8); margin: 0; }
.pt1__expiry-wrap { padding: 20px 14px 0; }
.pt1__expiry-card { background: #fff; border-radius: 14px; padding: 20px 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.07); }
.pt1__expiry-top { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 18px; }
.pt1__warn-icon-wrap { flex-shrink: 0; width: 42px; height: 42px; border-radius: 50%; background: #fff8e1; border: 1.5px solid #ffc107; display: flex; align-items: center; justify-content: center; color: #e6a817; }
.pt1__expiry-text { flex: 1; }
.pt1__expiry-title { font-size: 15px; font-weight: 700; color: #111; margin: 0 0 6px; }
.pt1__expiry-desc { font-size: 13px; color: #555; margin: 0; line-height: 1.6; }
.pt1__countdown-hd { display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 12px; color: #666; }
.pt1__countdown-label { font-size: 13px; color: #666; }
.pt1__countdown-display { display: flex; align-items: center; justify-content: center; gap: 10px; }
.pt1__time-block { min-width: 58px; background: #1a1a1a; border-radius: 8px; padding: 10px 12px 8px; text-align: center; }
.pt1__time-val { display: block; font-size: 24px; font-weight: 800; color: #fff; line-height: 1; }
.pt1__time-unit { display: block; font-size: 10px; color: #aaa; margin-top: 4px; text-transform: capitalize; }
.pt1__sep { font-size: 22px; font-weight: 800; color: #1a1a1a; }
.pt1__cta-wrap { padding: 20px 14px; }
.pt1__cta-btn { width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; background: var(--global-primary-color, #e60000); color: #fff; border: none; border-radius: 32px; padding: 16px 20px; font-size: 16px; font-weight: 700; cursor: pointer; transition: opacity 0.18s; }
.pt1__cta-btn:active { opacity: 0.85; }
.pt1__rewards-wrap { padding: 0 14px 20px; }
.pt1__rewards-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.pt1__rewards-title { font-size: 16px; font-weight: 700; margin: 0; }
.pt1__rewards-count { font-size: 12px; color: #999; }
.pt1__rewards-list { display: flex; flex-direction: column; gap: 12px; }
.pt1__card { background: #fff; border-radius: 14px; box-shadow: 0 1px 6px rgba(0,0,0,0.07); overflow: hidden; cursor: pointer; border: 1.5px solid transparent; transition: border-color 0.18s, box-shadow 0.18s; }
.pt1__card--selected { border-color: var(--global-primary-color, #e60000); box-shadow: 0 0 0 3px rgba(230,0,0,0.10); }
.pt1__card--disabled { cursor: default; }
.pt1__card-row { display: flex; align-items: center; gap: 14px; padding: 16px; }
.pt1__icon-wrap { flex-shrink: 0; width: 46px; height: 46px; border-radius: 12px; background: #fde8e8; color: var(--global-primary-color, #e60000); display: flex; align-items: center; justify-content: center; }
.pt1__icon-wrap--featured { background: var(--global-primary-color, #e60000); color: #fff; }
.pt1__card-info { flex: 1; min-width: 0; }
.pt1__card-title-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 4px; }
.pt1__card-name { font-size: 14px; font-weight: 700; margin: 0; color: #111; }
.pt1__card-desc { font-size: 12px; color: #888; margin: 0; }
.pt1__badge { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 6px; border-radius: 4px; color: #fff; white-space: nowrap; }
.pt1__badge--popular { background: #ff8c00; }
.pt1__badge--soldout { background: #ccc; color: #666; }
.pt1__pts { text-align: right; flex-shrink: 0; }
.pt1__pts-val { font-size: 17px; font-weight: 800; color: var(--global-primary-color, #e60000); margin: 0; line-height: 1.2; }
.pt1__pts-val--gray { color: #bbb; }
.pt1__pts-label { font-size: 11px; color: #aaa; margin: 0; }
.pt1__confirm-row { padding: 0 14px 14px; }
.pt1__confirm-btn { width: 100%; background: var(--global-primary-color, #e60000); color: #fff; border: none; border-radius: 8px; padding: 12px; font-size: 14px; font-weight: 700; cursor: pointer; transition: opacity 0.16s; }
.pt1__confirm-btn:active { opacity: 0.82; }
.pt1__how-wrap { padding: 0 14px 32px; }
.pt1__how-card { background: #fff; border-radius: 14px; padding: 20px 18px; box-shadow: 0 1px 6px rgba(0,0,0,0.07); }
.pt1__how-title { font-size: 15px; font-weight: 700; margin: 0 0 16px; }
.pt1__how-steps { display: flex; flex-direction: column; gap: 16px; }
.pt1__step { display: flex; align-items: flex-start; gap: 12px; }
.pt1__step-num { flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%; background: var(--global-primary-color, #e60000); color: #fff; font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.pt1__step-text { font-size: 13px; color: #333; line-height: 1.5; margin: 4px 0 0; }
</style>