6 changed files with 770 additions and 0 deletions
|
After Width: | Height: | Size: 3.3 KiB |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '团购券开台', |
||||
navigationBarBackgroundColor: '#121212', |
||||
navigationBarTextStyle: 'white', |
||||
}) |
||||
@ -0,0 +1,449 @@
@@ -0,0 +1,449 @@
|
||||
/** |
||||
* 团购券开台页面样式 |
||||
*/ |
||||
.coupon-start-page { |
||||
min-height: 100vh; |
||||
background-color: #121212; |
||||
padding-bottom: 130px; // 为底部固定区域留空间 |
||||
|
||||
// 内容区域 |
||||
.content-area { |
||||
padding: 30px 30px 0; |
||||
|
||||
// 台球桌信息卡片 |
||||
.table-card { |
||||
background-image: url('../startTable/images/startBiliardCardBg.png'); |
||||
background-size: cover; |
||||
background-position: center; |
||||
border-radius: 12px; |
||||
padding: 24px; |
||||
margin-bottom: 40px; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
|
||||
// 顶部:店名和电话 |
||||
.card-header { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
margin-bottom: 20px; |
||||
|
||||
.store-name { |
||||
font-size: 25px; |
||||
color: #FFFFFF; |
||||
} |
||||
|
||||
.phone-box { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 8px; |
||||
|
||||
.phone-icon { |
||||
width: 29px; |
||||
height: 29px; |
||||
} |
||||
|
||||
.phone-text { |
||||
font-size: 25px; |
||||
color: #FFFFFF; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 台球桌标题 |
||||
.table-title { |
||||
font-weight: bold; |
||||
font-size: 42px; |
||||
color: #FFFFFF; |
||||
line-height: 1.4; |
||||
margin: 24px 0 26px; |
||||
} |
||||
|
||||
// 价格信息 |
||||
.price-info { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 20px; |
||||
margin-bottom: 24px; |
||||
|
||||
.price-item { |
||||
display: flex; |
||||
align-items: baseline; |
||||
gap: 4px; |
||||
|
||||
.price-label { |
||||
font-size: 25px; |
||||
color: #FFFFFF; |
||||
} |
||||
|
||||
.price-value { |
||||
font-size: 22px; |
||||
color: #FFFFFF; |
||||
font-weight: bold; |
||||
background: linear-gradient(0deg, #F6AE54 0%, #FBE49E 50%, #F6AE54 100%); |
||||
-webkit-background-clip: text; |
||||
-webkit-text-fill-color: transparent; |
||||
} |
||||
} |
||||
|
||||
.vip-price { |
||||
background: linear-gradient(90deg, #333333 0%, #000000 100%); |
||||
border-radius: 8px; |
||||
padding: 7px 9px; |
||||
|
||||
.vip-text { |
||||
font-size: 22px; |
||||
color: #FFFFFF; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 价格时段 |
||||
.time-period { |
||||
font-size: 25px; |
||||
color: #FFFFFF; |
||||
} |
||||
} |
||||
|
||||
// 使用说明 |
||||
.instruction-box { |
||||
background-color: #21212A; |
||||
border-radius: 13px; |
||||
padding: 23px; |
||||
margin-bottom: 30px; |
||||
|
||||
.instruction-item { |
||||
display: flex; |
||||
align-items: center; |
||||
margin-bottom: 15px; |
||||
|
||||
&:last-of-type { |
||||
margin-bottom: 10px; |
||||
} |
||||
|
||||
.number-circle { |
||||
width: 32px; |
||||
height: 32px; |
||||
// background-color: #FFFFFF; |
||||
border-radius: 50%; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
margin-right: 12px; |
||||
flex-shrink: 0; |
||||
|
||||
.number { |
||||
font-size: 20px; |
||||
color: #fff; |
||||
} |
||||
} |
||||
|
||||
.instruction-text { |
||||
font-size: 25px; |
||||
color: #999999; |
||||
} |
||||
} |
||||
|
||||
.instruction-note { |
||||
display: block; |
||||
font-size: 25px; |
||||
color: #999999; |
||||
margin-top: 0; |
||||
padding-left: 44px; |
||||
} |
||||
} |
||||
|
||||
// 券码输入框 |
||||
.code-input-box { |
||||
height: 90px; |
||||
background-color: #2A2A2A; |
||||
border-radius: 8px; |
||||
padding: 0 30px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
margin-bottom: 30px; |
||||
|
||||
.code-input { |
||||
flex: 1; |
||||
font-size: 29px; |
||||
color: #FFFFFF; |
||||
} |
||||
|
||||
.code-placeholder { |
||||
color: #666666; |
||||
} |
||||
|
||||
.scan-icon { |
||||
width: 41px; |
||||
height: 41px; |
||||
flex-shrink: 0; |
||||
} |
||||
} |
||||
|
||||
// 券类型选项卡 |
||||
.coupon-tabs { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
margin-bottom: 30px; |
||||
|
||||
.tabs-left { |
||||
display: flex; |
||||
gap: 40px; |
||||
|
||||
.tab-item { |
||||
position: relative; |
||||
padding-bottom: 10px; |
||||
|
||||
.tab-text { |
||||
font-size: 29px; |
||||
color: #999999; |
||||
} |
||||
|
||||
&.active { |
||||
.tab-text { |
||||
color: #FFFFFF; |
||||
} |
||||
|
||||
.tab-indicator { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
height: 6px; |
||||
background-color: #01CA68; |
||||
border-radius: 3px; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.tabs-right { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 8px; |
||||
|
||||
.total-text { |
||||
font-size: 25px; |
||||
color: #999999; |
||||
} |
||||
|
||||
.arrow-text { |
||||
font-size: 32px; |
||||
color: #999999; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 券列表 |
||||
.coupon-list { |
||||
.coupon-item { |
||||
position: relative; |
||||
background-color: #2A2A2A; |
||||
border-radius: 12px; |
||||
margin-bottom: 20px; |
||||
overflow: hidden; |
||||
|
||||
// 左上角标签 |
||||
.coupon-tag { |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
background-color: transparent; |
||||
border: 2px solid #FF6B00; |
||||
border-top: none; |
||||
border-left: none; |
||||
color: #FF6B00; |
||||
font-size: 22px; |
||||
padding: 4px 12px; |
||||
border-bottom-right-radius: 12px; |
||||
z-index: 1; |
||||
} |
||||
|
||||
// 券主体 |
||||
.coupon-main { |
||||
display: flex; |
||||
padding: 30px; |
||||
gap: 20px; |
||||
|
||||
// 左侧:时长和券类型 |
||||
.coupon-left { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
width: 150px; |
||||
flex-shrink: 0; |
||||
padding-top: 40px; // 为左上角标签留空间 |
||||
|
||||
.coupon-duration-wrapper { |
||||
display: flex; |
||||
align-items: baseline; |
||||
gap: 4px; |
||||
margin-bottom: 12px; |
||||
|
||||
.coupon-duration { |
||||
font-size: 50px; |
||||
color: #FF6B00; |
||||
font-weight: bold; |
||||
line-height: 1; |
||||
} |
||||
|
||||
.coupon-unit { |
||||
font-size: 25px; |
||||
color: #FF6B00; |
||||
line-height: 1; |
||||
} |
||||
} |
||||
|
||||
.coupon-type { |
||||
font-size: 29px; |
||||
color: #999999; |
||||
line-height: 1.2; |
||||
} |
||||
} |
||||
|
||||
// 右侧:信息区域 |
||||
.coupon-right { |
||||
flex: 1; |
||||
display: flex; |
||||
flex-direction: column; |
||||
gap: 8px; |
||||
min-width: 0; |
||||
|
||||
// 标题行:标题 + 选择按钮 |
||||
.title-row { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
gap: 12px; |
||||
|
||||
.coupon-title { |
||||
flex: 1; |
||||
font-size: 29px; |
||||
color: #FFFFFF; |
||||
font-weight: 500; |
||||
line-height: 1.4; |
||||
min-width: 0; |
||||
} |
||||
|
||||
.select-btn { |
||||
width: 44px; |
||||
height: 44px; |
||||
border: 2px solid #666666; |
||||
border-radius: 50%; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-shrink: 0; |
||||
|
||||
&.selected { |
||||
background-color: #01CA68; |
||||
border-color: #01CA68; |
||||
|
||||
.check-icon { |
||||
font-size: 28px; |
||||
color: #FFFFFF; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.coupon-expire { |
||||
font-size: 23px; |
||||
color: #999999; |
||||
} |
||||
|
||||
.coupon-desc { |
||||
font-size: 23px; |
||||
color: #FF6B00; |
||||
line-height: 1.5; |
||||
white-space: pre-line; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 详情区域 |
||||
.coupon-details-section { |
||||
padding: 0 30px 30px; |
||||
border-top: 1px dashed #3A3A3A; |
||||
padding-top: 20px; |
||||
|
||||
.detail-row { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
gap: 12px; |
||||
margin-bottom: 8px; |
||||
|
||||
.detail-text { |
||||
flex: 1; |
||||
font-size: 23px; |
||||
color: #999999; |
||||
line-height: 1.8; |
||||
} |
||||
|
||||
.toggle-text { |
||||
font-size: 20px; |
||||
color: #999999; |
||||
flex-shrink: 0; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
|
||||
.detail-text { |
||||
display: block; |
||||
font-size: 23px; |
||||
color: #999999; |
||||
line-height: 1.8; |
||||
margin-bottom: 8px; |
||||
|
||||
&:last-child { |
||||
margin-bottom: 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 底部提示 |
||||
.end-tip { |
||||
display: block; |
||||
text-align: center; |
||||
font-size: 25px; |
||||
color: #666666; |
||||
padding: 40px 0 60px; |
||||
|
||||
} |
||||
} |
||||
|
||||
// 底部固定按钮 |
||||
.footer-bar { |
||||
position: fixed; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
padding: 20px 30px; |
||||
padding-bottom: calc(20px + env(safe-area-inset-bottom)); |
||||
background-color: #121212; |
||||
box-shadow: 0px -2px 10px rgba(0, 0, 0, 0.3); |
||||
|
||||
.exchange-btn { |
||||
width: 100%; |
||||
height: 90px; |
||||
background-color: #01CA68; |
||||
border-radius: 50px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
.exchange-text { |
||||
font-size: 33px; |
||||
color: #FFFFFF; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,307 @@
@@ -0,0 +1,307 @@
|
||||
/** |
||||
* 团购券开台页面 |
||||
*/ |
||||
import { View, Text, Image, Input } from '@tarojs/components' |
||||
import Taro, { useLoad } from '@tarojs/taro' |
||||
import { useState } from 'react' |
||||
import './index.scss' |
||||
|
||||
// 使用本地图片资源
|
||||
const phoneIcon = require('../startTable/images/phone.png') |
||||
const scanIcon = require('./images/scanIconIn.png') |
||||
|
||||
// 券类型枚举
|
||||
enum CouponType { |
||||
THIRD_PARTY = 'third_party', |
||||
ACTIVITY = 'activity', |
||||
} |
||||
|
||||
// 券数据接口
|
||||
interface Coupon { |
||||
id: number |
||||
type: string // '团八' | '黑八'
|
||||
duration: string |
||||
couponType: string // '活动抵时券' | '抵时券'
|
||||
title: string |
||||
expireDate: string |
||||
description: string |
||||
details: string[] |
||||
expanded: boolean |
||||
} |
||||
|
||||
const CouponStart = () => { |
||||
const [couponCode, setCouponCode] = useState('') // 券码
|
||||
const [selectedCouponType, setSelectedCouponType] = useState<CouponType>(CouponType.THIRD_PARTY) |
||||
const [selectedCouponId, setSelectedCouponId] = useState<number>(1) // 选中的券ID
|
||||
|
||||
// 券列表数据
|
||||
const [coupons, setCoupons] = useState<Coupon[]>([ |
||||
{ |
||||
id: 1, |
||||
type: '团八', |
||||
duration: '20', |
||||
couponType: '活动抵时券', |
||||
title: '20分钟通用券(体验券)', |
||||
expireDate: '2025-10-01 16:21', |
||||
description: '需要交押金,超时扣押金\n单笔满40分钟可减免20分钟', |
||||
details: ['不限时段,全天可用', '适用类型:中八', '每桌只单限用1张'], |
||||
expanded: false, |
||||
}, |
||||
{ |
||||
id: 2, |
||||
type: '黑八', |
||||
duration: '180', |
||||
couponType: '抵时券', |
||||
title: '自助中式台球3小时59.9元体验券', |
||||
expireDate: '2025-11-01 09:00', |
||||
description: '', |
||||
details: ['不限时段,全天可用'], |
||||
expanded: false, |
||||
}, |
||||
]) |
||||
|
||||
useLoad(() => { |
||||
console.log('团购券开台页面加载') |
||||
}) |
||||
|
||||
// 拨打电话
|
||||
const handlePhoneCall = () => { |
||||
Taro.makePhoneCall({ |
||||
phoneNumber: '100-666-XXXX', |
||||
}).catch((err) => { |
||||
console.error('拨打电话失败:', err) |
||||
}) |
||||
} |
||||
|
||||
// 扫描券码
|
||||
const handleScanCode = () => { |
||||
Taro.scanCode({ |
||||
scanType: ['qrCode', 'barCode'], |
||||
}).then((res) => { |
||||
setCouponCode(res.result) |
||||
}).catch((err) => { |
||||
console.error('扫码失败:', err) |
||||
}) |
||||
} |
||||
|
||||
// 切换券类型
|
||||
const handleSwitchType = (type: CouponType) => { |
||||
setSelectedCouponType(type) |
||||
} |
||||
|
||||
// 选择券
|
||||
const handleSelectCoupon = (id: number) => { |
||||
setSelectedCouponId(id) |
||||
} |
||||
|
||||
// 展开/收起券详情
|
||||
const handleToggleExpand = (id: number) => { |
||||
setCoupons(prev => prev.map(coupon => |
||||
coupon.id === id ? { ...coupon, expanded: !coupon.expanded } : coupon |
||||
)) |
||||
} |
||||
|
||||
// 兑换开台
|
||||
const handleExchange = () => { |
||||
if (!selectedCouponId) { |
||||
Taro.showToast({ |
||||
title: '请选择一张券', |
||||
icon: 'none', |
||||
duration: 2000 |
||||
}) |
||||
return |
||||
} |
||||
|
||||
console.log('兑换开台,选中券ID:', selectedCouponId) |
||||
Taro.showToast({ |
||||
title: '功能开发中', |
||||
icon: 'none', |
||||
duration: 2000 |
||||
}) |
||||
} |
||||
|
||||
return ( |
||||
<View className="coupon-start-page"> |
||||
{/* 内容区域 */} |
||||
<View className="content-area"> |
||||
{/* 台球桌信息卡片 */} |
||||
<View className="table-card"> |
||||
{/* 顶部:店名和电话 */} |
||||
<View className="card-header"> |
||||
<Text className="store-name">北京黑八大师联盟球厅店</Text> |
||||
<View className="phone-box" onClick={handlePhoneCall}> |
||||
<Image className="phone-icon" src={phoneIcon} mode="aspectFit" /> |
||||
<Text className="phone-text">联系店长</Text> |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 台球桌标题 */} |
||||
<View className="table-title"> |
||||
<Text>[中式台球]来力.山岩-A03/3号球桌</Text> |
||||
</View> |
||||
|
||||
{/* 价格信息 */} |
||||
<View className="price-info"> |
||||
<View className="price-item"> |
||||
<Text className="price-value">¥28.80</Text> |
||||
<Text className="price-label">/小时</Text> |
||||
</View> |
||||
<View className="price-item"> |
||||
<Text className="price-label">押金</Text> |
||||
<Text className="price-value">¥100.00</Text> |
||||
</View> |
||||
<View className="vip-price"> |
||||
<Text className="vip-text">VIP ¥20.00</Text> |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 价格时段 */} |
||||
<View className="time-period"> |
||||
<Text>价格时段:00:00-24:00</Text> |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 使用说明 */} |
||||
<View className="instruction-box"> |
||||
<View className="instruction-item"> |
||||
<View className="number-circle"> |
||||
<Text className="number">❶</Text> |
||||
</View> |
||||
<Text className="instruction-text">打开美团、抖音、大众点评或快手等</Text> |
||||
</View> |
||||
<View className="instruction-item"> |
||||
<View className="number-circle"> |
||||
<Text className="number">❷</Text> |
||||
</View> |
||||
<Text className="instruction-text">长按复制券码贴到对方输入框</Text> |
||||
</View> |
||||
<View className="instruction-item"> |
||||
<View className="number-circle"> |
||||
<Text className="number">❸</Text> |
||||
</View> |
||||
<Text className="instruction-text">点击[兑换],成功兑换团购券</Text> |
||||
</View> |
||||
<Text className="instruction-note">注:也可以通过扫团购券码完成兑换</Text> |
||||
</View> |
||||
|
||||
{/* 券码输入框 */} |
||||
<View className="code-input-box"> |
||||
<Input |
||||
className="code-input" |
||||
placeholder="请输入券码" |
||||
placeholderClass="code-placeholder" |
||||
value={couponCode} |
||||
onInput={(e) => setCouponCode(e.detail.value)} |
||||
/> |
||||
<Image |
||||
className="scan-icon" |
||||
src={scanIcon} |
||||
mode="aspectFit" |
||||
onClick={handleScanCode} |
||||
/> |
||||
</View> |
||||
|
||||
{/* 券类型选项卡 */} |
||||
<View className="coupon-tabs"> |
||||
<View className="tabs-left"> |
||||
<View |
||||
className={`tab-item ${selectedCouponType === CouponType.THIRD_PARTY ? 'active' : ''}`} |
||||
onClick={() => handleSwitchType(CouponType.THIRD_PARTY)} |
||||
> |
||||
<Text className="tab-text">第三方卡券</Text> |
||||
{selectedCouponType === CouponType.THIRD_PARTY && <View className="tab-indicator" />} |
||||
</View> |
||||
<View |
||||
className={`tab-item ${selectedCouponType === CouponType.ACTIVITY ? 'active' : ''}`} |
||||
onClick={() => handleSwitchType(CouponType.ACTIVITY)} |
||||
> |
||||
<Text className="tab-text">活动卡券</Text> |
||||
{selectedCouponType === CouponType.ACTIVITY && <View className="tab-indicator" />} |
||||
</View> |
||||
</View> |
||||
<View className="tabs-right"> |
||||
<Text className="total-text">共{coupons.length}张</Text> |
||||
<Text className="arrow-text">›</Text> |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 券列表 */} |
||||
<View className="coupon-list"> |
||||
{coupons.map((coupon) => ( |
||||
<View key={coupon.id} className="coupon-item"> |
||||
{/* 左上角标签 */} |
||||
<View className="coupon-tag">{coupon.type}</View> |
||||
|
||||
<View className="coupon-main" onClick={() => handleSelectCoupon(coupon.id)}> |
||||
{/* 左侧:时长和券类型 */} |
||||
<View className="coupon-left"> |
||||
<View className="coupon-duration-wrapper"> |
||||
<Text className="coupon-duration">{coupon.duration}</Text> |
||||
<Text className="coupon-unit">分钟</Text> |
||||
</View> |
||||
<Text className="coupon-type">{coupon.couponType}</Text> |
||||
</View> |
||||
|
||||
{/* 右侧:信息区域 */} |
||||
<View className="coupon-right"> |
||||
{/* 标题行:标题 + 选择按钮 */} |
||||
<View className="title-row"> |
||||
<Text className="coupon-title">{coupon.title}</Text> |
||||
<View |
||||
className={`select-btn ${selectedCouponId === coupon.id ? 'selected' : ''}`} |
||||
onClick={(e) => { |
||||
e.stopPropagation() |
||||
handleSelectCoupon(coupon.id) |
||||
}} |
||||
> |
||||
{selectedCouponId === coupon.id && <Text className="check-icon">✓</Text>} |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 有效期 */} |
||||
<Text className="coupon-expire">有效日期至:{coupon.expireDate}</Text> |
||||
|
||||
{/* 描述 */} |
||||
{coupon.description && <Text className="coupon-desc">{coupon.description}</Text>} |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 详情区域 */} |
||||
<View className="coupon-details-section"> |
||||
{/* 第一条详情 + 展开/收起按钮 */} |
||||
{coupon.details.length > 0 && ( |
||||
<View className="detail-row"> |
||||
<Text className="detail-text">{coupon.details[0]}</Text> |
||||
<Text |
||||
className="toggle-text" |
||||
onClick={() => handleToggleExpand(coupon.id)} |
||||
> |
||||
{coupon.expanded ? '∧' : '∨'} |
||||
</Text> |
||||
</View> |
||||
)} |
||||
|
||||
{/* 展开时显示其余详情 */} |
||||
{coupon.expanded && coupon.details.slice(1).map((detail, index) => ( |
||||
<Text key={index} className="detail-text">{detail}</Text> |
||||
))} |
||||
</View> |
||||
</View> |
||||
))} |
||||
</View> |
||||
|
||||
{/* 底部提示 */} |
||||
<Text className="end-tip">到底了~</Text> |
||||
</View> |
||||
|
||||
{/* 底部固定按钮 */} |
||||
<View className="footer-bar"> |
||||
<View className="exchange-btn" onClick={handleExchange}> |
||||
<Text className="exchange-text">兑换开台</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default CouponStart |
||||
Loading…
Reference in new issue