feat: 新增绑卡激活、旅游卡购买、工会购买、使用说明、常见问题、订单管理页面

- 新增 pages/card-bind 绑卡激活页(证件类型下拉、照片上传、表单校验)
- 新增 pages/card-buy 超级玩家卡购买页(微信支付)
- 新增 pages/union-buy 工会购买页
- 新增 pages/usage-guide 使用说明页
- 新增 pages/faq 常见问题页(购买类/预约类/绑定类折叠问答)
- 新增 pages/order 订单列表页(Tab筛选/下拉刷新/状态标签)
- 新增 pages/order 订单详情页(状态差异化展示/卡号复制)
- 登录页背景改为白色、按钮统一主色、协议未同意提示
- 首页金刚区跳转对接(绑卡/预约/入园/FAQ)
- 旅游卡页使用说明跳转、景区权益改为使用说明
- 我的页面菜单跳转对接(订单/使用说明/FAQ)
- 景区列表新增卡种筛选、吸顶区动态高度适配
- 全局统一思源宋体、主色#3c9cff、rpx单位

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-06-26 00:32:21 +08:00
co-authored by Claude
parent ff524ac973
commit 5ac924a76c
17 changed files with 3201 additions and 113 deletions
+374
View File
@@ -0,0 +1,374 @@
<template>
<view class="page">
<!-- 购买类 -->
<view class="category">
<text class="category-title">购买类</text>
<view class="category-divider" />
<view
v-for="(item, idx) in purchaseList"
:key="'p' + idx"
class="faq-card"
>
<view class="faq-header" @tap="toggle('p', idx)">
<view class="faq-index">{{ idx + 1 }}</view>
<text class="faq-question">{{ item.q }}</text>
<view class="faq-arrow" :class="{ 'faq-arrow--up': isOpen('p', idx) }" />
</view>
<view v-show="isOpen('p', idx)" class="faq-body">
<view v-for="(p, pi) in item.a" :key="pi" class="faq-paragraph">
<text
v-for="(seg, si) in p"
:key="si"
:class="seg.highlight ? 'faq-highlight' : 'faq-text'"
@tap="seg.copy ? copyText(seg.text) : null"
>{{ seg.text }}</text>
</view>
</view>
</view>
</view>
<!-- 预约类 -->
<view class="category">
<text class="category-title">预约类</text>
<view class="category-divider" />
<view
v-for="(item, idx) in reserveList"
:key="'r' + idx"
class="faq-card"
>
<view class="faq-header" @tap="toggle('r', idx)">
<view class="faq-index">{{ idx + 1 }}</view>
<text class="faq-question">{{ item.q }}</text>
<view class="faq-arrow" :class="{ 'faq-arrow--up': isOpen('r', idx) }" />
</view>
<view v-show="isOpen('r', idx)" class="faq-body">
<view v-for="(p, pi) in item.a" :key="pi" class="faq-paragraph">
<text
v-for="(seg, si) in p"
:key="si"
:class="seg.highlight ? 'faq-highlight' : 'faq-text'"
@tap="seg.copy ? copyText(seg.text) : null"
>{{ seg.text }}</text>
</view>
</view>
</view>
</view>
<!-- 绑定类 -->
<view class="category">
<text class="category-title">绑定类</text>
<view class="category-divider" />
<view
v-for="(item, idx) in bindList"
:key="'b' + idx"
class="faq-card"
>
<view class="faq-header" @tap="toggle('b', idx)">
<view class="faq-index">{{ idx + 1 }}</view>
<text class="faq-question">{{ item.q }}</text>
<view class="faq-arrow" :class="{ 'faq-arrow--up': isOpen('b', idx) }" />
</view>
<view v-show="isOpen('b', idx)" class="faq-body">
<view v-for="(p, pi) in item.a" :key="pi" class="faq-paragraph">
<text
v-for="(seg, si) in p"
:key="si"
:class="seg.highlight ? 'faq-highlight' : 'faq-text'"
@tap="seg.copy ? copyText(seg.text) : null"
>{{ seg.text }}</text>
</view>
</view>
</view>
</view>
<!-- 底部安全区 -->
<view class="safe-bottom" />
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 展开状态:默认全部展开
const openMap = ref<Record<string, boolean>>({})
// 初始化所有条目为展开
const purchaseList = getPurchaseList()
const reserveList = getReserveList()
const bindList = getBindList()
purchaseList.forEach((_, i) => { openMap.value[`p${i}`] = true })
reserveList.forEach((_, i) => { openMap.value[`r${i}`] = true })
bindList.forEach((_, i) => { openMap.value[`b${i}`] = true })
function toggle(cat: string, idx: number) {
const key = `${cat}${idx}`
openMap.value[key] = !openMap.value[key]
}
function isOpen(cat: string, idx: number) {
return openMap.value[`${cat}${idx}`] ?? false
}
function copyText(text: string) {
uni.setClipboardData({
data: text,
success: () => uni.showToast({ title: '已复制', icon: 'success' })
})
}
type Seg = { text: string; highlight?: boolean; copy?: boolean }
type FaqItem = { q: string; a: Seg[][] }
function getPurchaseList(): FaqItem[] {
return [
{
q: '赣湘全域旅游年卡/超级玩家卡该如何进行购买?',
a: [
[{ text: '答:首先搜索"爱上萍乡"小程序,进入首页界面,点击购买即可。' }],
[{ text: '温馨提醒:', highlight: false }, { text: '1. 赣湘全域旅游年卡/超级玩家卡一经激活或销售七日以上,不适用于退款规则;2. 部分商家限制仅对女性或特定年龄段开放,详情请查阅各商家资料规则展示页面。' }]
]
},
{
q: '对于赣湘全域旅游年卡/超级玩家卡的使用疑问怎么进行人工咨询?',
a: [
[
{ text: '答:爱上萍乡公众号的客服电话是:' },
{ text: '15388025272', highlight: true, copy: true },
{ text: ',微信号为' },
{ text: 'cshaika2024', highlight: true, copy: true },
{ text: ',人工客服工作时间:9:00-17:00(周一至周五),周末及法定节假日不受理,请大家在工作日与我们联系哦~' }
]
]
},
{
q: '赣湘全域旅游年卡/超级玩家卡该如何进行退款',
a: [
[{ text: '答:赣湘全域旅游年卡/超级玩家卡绑定激活或销售七日以上不可退卡,不适用7天无理由退货,在"爱上萍乡"小程序上购买但未激活的用户请咨询客服中心处理退卡事宜,在其他平台上购买年卡的用户请于对应平台客服进行沟通。' }]
]
},
{
q: '未成年人可以购买吗?',
a: [
[{ text: '答:可以的,赣湘全域旅游年卡/超级玩家卡覆盖的部分景区、商家目前已经对未满18周岁的少年儿童开放,未成年人也可使用本人身份证信息实名制激活使用,大小同价,由于系统对接或行业法规等限制,部分景区、商家未向少年儿童开放,建议购买前一定要详细查阅平台公告及商品详细信息哦~' }]
]
},
{
q: '小孩可以去哪些景区?在哪里可以查看?',
a: [
[{ text: '答:自2023年7月31日起,赣湘全域旅游年卡/超级玩家卡包含的景区陆续向18周岁以下儿童放开使用限制,目前已有部分景区允许儿童凭借年卡免费入园。儿童可使用权益的景区商户名单可在"爱上萍乡"-购买 - 商品详情页或各景区、商户规则展示页面进行了解。' }]
]
},
{
q: '港澳台/外国人可以购买使用赣湘全域旅游年卡/超级玩家卡吗?',
a: [
[{ text: '答:赣湘全域旅游年卡/超级玩家卡需要实名上传中国大陆的身份证信息,港澳台/外国人目前是不能激活使用年卡的。' }]
]
}
]
}
function getReserveList(): FaqItem[] {
return [
{
q: '如何预约',
a: [
[{ text: '答:部分景区、商家至少需要提前一天进行线上预约或电话预约(最多可预约七天),详细规则请进入"爱上萍乡"公众号 - 点击使用指引 - 商家预约 - 预约即可,提交预约信息后将扣除赣湘全域旅游年卡/超级玩家卡使用次数。' }],
[{ text: '需至少提前一天取消预约,详细规则请进入"爱上萍乡"公众号 - 点击使用指引 - 我的预约 - 进入预约详情界面 - 点击取消预约即可。' }],
[{ text: '使用日当天不可取消预约,过期视为自动放弃使用权益。' }]
]
},
{
q: '如何出示赣湘全域旅游年卡/超级玩家卡二维码',
a: [
[{ text: '答:关注"爱上萍乡"公众号 - 点击使用指引 - 点击入园码即可出示年卡二维码;' }]
]
},
{
q: '如何找到景区和商家',
a: [
[{ text: '答:关注"爱上萍乡"公众号 - 点击年卡 - 年卡权益,下方的游吃住行娱乐购里的景区商家都可点击查看具体的赣湘全域旅游年卡/超级玩家卡使用规则。' }]
]
},
{
q: '预约了,如果不去会扣除次数吗?',
a: [
[{ text: '答:如预约之后因非客观因素无法前往且未提前取消预约则会扣除年卡使用次数,扣除使用次数后的权益不可恢复。' }]
]
},
{
q: '景区没有预约不能进去客服可以帮我预约吗?',
a: [
[{ text: '答:后台并没有权限给客户操作预约,请大家在"爱上萍乡"公众号自行预约哦。' }]
]
}
]
}
function getBindList(): FaqItem[] {
return [
{
q: '赣湘全域旅游年卡/超级玩家卡如何绑定?',
a: [
[{ text: '答:购买赣湘全域旅游年卡/超级玩家卡后,平台会通过短信发送一个8位数的核销码,请大家搜索"爱上萍乡"公众号,点击使用指引—激活年卡—核销码激活,最后上传个人身份信息即可。' }]
]
},
{
q: '赣湘全域旅游年卡/超级玩家卡可以解绑吗?',
a: [
[{ text: '答:注意啦!赣湘全域旅游年卡/超级玩家卡一旦绑定身份信息后,是无法进行解绑或换绑的哦。' }]
]
},
{
q: '一个账号能绑定多张卡吗?',
a: [
[{ text: '答:一个微信用户只能绑定4张赣湘全域旅游年卡/超级玩家卡,请不要多绑或者错绑哦~' }],
[{ text: '成人绑定年卡建议分开绑定,以免造成出行不便哦(只需把8位数的核销码发给需要绑定的人,选择核销码激活上传自己的身份信息即可)' }]
]
},
{
q: '赣湘全域旅游年卡/超级玩家卡已激活绑定能否给别人使用?',
a: [
[{ text: '答:赣湘全域旅游年卡/超级玩家卡实行一卡一人,已激活绑定的年卡仅限本人使用,不可转借他人。' }]
]
}
]
}
</script>
<style lang="scss" scoped>
$font: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
$primary: #3c9cff;
$text-main: #111111;
$text-label: #222222;
$text-body: #333333;
$text-hint: #666666;
$divider: #eeeeee;
.page {
min-height: 100vh;
background: #f7f8fa;
padding: 48rpx 24rpx 0;
font-family: $font;
}
/* ===== 分类区块 ===== */
.category {
margin-bottom: 32rpx;
}
.category-title {
font-family: $font;
font-size: 32rpx;
font-weight: 700;
color: $text-main;
display: block;
margin-bottom: 16rpx;
}
.category-divider {
height: 1rpx;
background: $divider;
margin-bottom: 24rpx;
}
/* ===== 问答卡片 ===== */
.faq-card {
background: #ffffff;
border-radius: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
margin-bottom: 20rpx;
overflow: hidden;
}
.faq-header {
display: flex;
align-items: flex-start;
padding: 32rpx;
transition: background 0.15s ease;
&:active {
background: #f9f9f9;
}
}
.faq-index {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: rgba(60, 156, 255, 0.08);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 16rpx;
margin-top: 4rpx;
font-family: $font;
font-size: 24rpx;
font-weight: 700;
color: $primary;
}
.faq-question {
flex: 1;
font-family: $font;
font-size: 30rpx;
font-weight: 500;
color: $text-label;
line-height: 1.5;
}
.faq-arrow {
width: 16rpx;
height: 16rpx;
border-right: 3rpx solid #cccccc;
border-bottom: 3rpx solid #cccccc;
transform: rotate(45deg);
flex-shrink: 0;
margin-left: 16rpx;
margin-top: 10rpx;
transition: transform 0.2s ease;
}
.faq-arrow--up {
transform: rotate(-135deg);
}
/* ===== 回答区域 ===== */
.faq-body {
padding: 0 32rpx 32rpx;
}
.faq-paragraph {
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.faq-text {
font-family: $font;
font-size: 26rpx;
font-weight: 400;
color: $text-body;
line-height: 1.7;
}
.faq-highlight {
font-family: $font;
font-size: 26rpx;
font-weight: 400;
color: $primary;
line-height: 1.7;
}
/* ===== 底部安全区 ===== */
.safe-bottom {
height: 64rpx;
padding-bottom: env(safe-area-inset-bottom);
}
</style>