feat: 质惠游 H5 一期 —— 门票模块全链路(微信公众号版)
从 xghmall(云闪付版湘工游)迁移民票模块,改造为微信公众号 H5: - 技术栈沿用 uni-app + Vue2 + uView(含定制版日历组件) - 登录/支付独立成层(common/auth.js、common/pay.js): 一期假登录(测试手机号)+ 模拟支付,后端微信接口就绪后只换这两处 - 登录页重写:短信验证码 + 公众号网页授权(code 换用户信息)预留 - 门票列表页为入口页(进页必须登录),加订单悬浮按钮 - 支付成功页接通:查看订单/继续逛逛 + 订单状态轮询 - 详情页定位导航改用 uni.openLocation + 高德网页兜底 - 出行人页:身份证 18 位格式校验,删除死的删除按钮,修正证件类型回显笔误 - 清理云闪付 SDK、支付宝授权、死页面引用等残留 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<view v-if="obj">
|
||||
<view class="wrap">
|
||||
<u-swiper :list="list" height='750' mode='number' indicator-pos='bottomRight'
|
||||
marginBottom='30rpx'></u-swiper>
|
||||
</view>
|
||||
<view style="position: absolute;top: 700rpx;">
|
||||
<view class="title">
|
||||
<view class="t_name">{{obj.tickets.name}} <text style="color: #FF8D28;">{{obj.shop.level}}</text></view>
|
||||
<view class="t_info">{{obj.tickets.remark1}}</view>
|
||||
|
||||
<view class="t_opentime">
|
||||
<view style="display: flex;gap: 8rpx;color: #FF8D28;">
|
||||
<image src="https://aliyunoss.hn12301.net/xgh/ticket_opentime.png"
|
||||
style="width: 36rpx;height: 36rpx;"></image>开园时间
|
||||
</view>
|
||||
<text style="color: #8E8E93;font-size: 24rpx;">{{obj.tickets.openDate}}</text>
|
||||
</view>
|
||||
|
||||
<view class="t_address">
|
||||
<view style="width: 548rpx;">{{obj.shop.address}}</view>
|
||||
|
||||
<image src="https://aliyunoss.hn12301.net/xgh/ticket_dh.png" @click="handleOpen()"></image>
|
||||
<image style="margin-left: 24rpx;" src="https://aliyunoss.hn12301.net/xgh/ticket_dhua.png"
|
||||
@click="playPhone()"></image>
|
||||
</view>
|
||||
<!-- <view style="color: #FF0000;font-size: 32rpx;margin-top: 24rpx;"><text
|
||||
style="font-size: 20rpx;">¥</text>{{(obj.minPrice/100).toFixed(0)}} ~ {{(obj.maxPrice/100).toFixed(0)}}</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<text class="tag" v-for="item in obj.tickets.tags.split(',')" :key="item">{{item}}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="ticket">
|
||||
<!-- <text style="color: #000;font-size: 36rpx;line-height: 88rpx;font-weight: 700;">成人票</text> -->
|
||||
<view class="ticket_item" style="margin-top: 0rpx;" v-for="(item,index) in obj.skuList" :key="index">
|
||||
<view style="width: 338rpx;display: flex;flex-direction: column;">
|
||||
<text
|
||||
style="color: #000;font-size: 28rpx;font-weight: 700;">【{{getSkuType(item.typeId)}}】{{item.skuName}}</text>
|
||||
<view
|
||||
style="color: #7C7C7E;display: flex;align-items: center;margin-top: 16rpx;font-size: 24rpx;">
|
||||
{{item.bookTimeLimit}}之前{{item.bookType?'可订当天':'可订明日'}}<text
|
||||
style="color: #DFE0E2;font-size: 48rpx;padding: 0rpx 12rpx;">·</text>
|
||||
</view>
|
||||
<view style="color: #0066F6;margin-top: 16rpx;" @click="seeSkuDetail(item.id)">预订须知 ></view>
|
||||
</view>
|
||||
<view
|
||||
style="width: 292rpx;display: flex;flex-direction: column;margin-left: 24rpx;align-items: flex-end;">
|
||||
<view
|
||||
style="color: #FF0000;font-size: 28rpx;margin-top: 0rpx;display: flex;justify-content: flex-end;">
|
||||
原售价<text
|
||||
style="font-size: 28rpx;font-weight: normal;text-decoration: line-through;">¥{{item.calendar.listPrice?(item.calendar.listPrice/100).toFixed(2):0}}</text>
|
||||
</view>
|
||||
|
||||
<view style="color: #FF0000;font-size: 36rpx;font-weight: bold;line-height: 88rpx;">
|
||||
<text
|
||||
style="font-size: 20rpx;font-weight: normal;">工会会员补贴价¥</text>{{item.calendar.salePrice?(item.calendar.salePrice/100).toFixed(2):0}}
|
||||
</view>
|
||||
<view class="btn" @tap="toOrderSure(item.id)">购买</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ticket">
|
||||
<text style="color: #000;font-size: 36rpx;line-height: 88rpx;font-weight: 700;">产品介绍</text>
|
||||
<u-parse :html="obj.tickets.introduction"></u-parse>
|
||||
</view>
|
||||
</view>
|
||||
<u-popup v-model="show" border-radius='20' mode='bottom' height='1000'>
|
||||
<view style="padding:0rpx 48rpx;" v-if="skuObj">
|
||||
<view
|
||||
style="width: 654rpx;height: 142rpx;line-height: 142rpx;text-align: center;color: #000;font-size: 32rpx;font-weight: bold;border-bottom: 2rpx #E1E1E1 dashed;">
|
||||
{{skuObj.sku.skuName}}
|
||||
</view>
|
||||
<view style="width: 654rpx;padding: 48rpx 0rpx">
|
||||
<text style="color: #000;font-size: 32rpx;font-weight: bold;">预订规则</text>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">预订时间</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
|
||||
{{skuObj.sku.bookTimeLimit}}之前{{skuObj.sku.bookType?'可订当天':'可订明日'}}
|
||||
</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">生效时间</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">出票后立即使用</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">有效期</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">指定出行日期1天内有效</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">适用人群</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.typeInfo}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="width: 654rpx;padding: 48rpx 0rpx">
|
||||
<text style="color: #000;font-size: 32rpx;font-weight: bold;">费用说明</text>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">费用包含</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWith}}</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">费用不含</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWithout}}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<view style="width: 654rpx;padding: 48rpx 0rpx">
|
||||
<text style="color: #000;font-size: 32rpx;font-weight: bold;">使用说明</text>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">入园方式</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
|
||||
{{skuObj.sku.enterChangeFlag?'凭('+getTyepStr(skuObj.sku.enterType)+')换票入园':'无需换票'}}
|
||||
</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;" v-if="skuObj.sku.enterChangeFlag">
|
||||
<view style="color: #808080;width: 120rpx;">换票时间</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
|
||||
{{skuObj.sku.changeStart}}~{{skuObj.sku.changeEnd}}
|
||||
</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;" v-if="skuObj.sku.enterChangeFlag">
|
||||
<view style="color: #808080;width: 120rpx;">换票地点</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.changePostion}}</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">入园时间</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
|
||||
{{skuObj.sku.enterStart}}~{{skuObj.sku.enterEnd}}
|
||||
</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">入园地址</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.enterPostion}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view style="width: 654rpx;padding: 48rpx 0rpx">
|
||||
<text style="color: #000;font-size: 32rpx;font-weight: bold;">取消政策</text>
|
||||
<u-parse style="margin-top: 24rpx;" :html="skuObj.sku.refundPolicy"></u-parse>
|
||||
</view>
|
||||
|
||||
|
||||
<view style="width: 654rpx;padding: 48rpx 0rpx">
|
||||
<text style="color: #000;font-size: 32rpx;font-weight: bold;">其他说明</text>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">补充说明</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.remark}}</view>
|
||||
</view>
|
||||
<view style="display: flex;margin-top: 24rpx;">
|
||||
<view style="color: #808080;width: 120rpx;">发票说明</view>
|
||||
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.receiptPolicy}}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var that = ''
|
||||
import {
|
||||
getSkuType
|
||||
} from '../../unit/utils.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
list: [],
|
||||
obj: '',
|
||||
skuObj: '',
|
||||
typeStr: '',
|
||||
getSkuType: '',
|
||||
enterTypeList: [{
|
||||
value: "二维码",
|
||||
label: 1
|
||||
},
|
||||
{
|
||||
value: "实体票",
|
||||
label: 2
|
||||
},
|
||||
{
|
||||
value: "第三方凭证码",
|
||||
label: 3
|
||||
},
|
||||
{
|
||||
value: "身份证",
|
||||
label: 4
|
||||
},
|
||||
{
|
||||
value: "护照",
|
||||
label: 5
|
||||
},
|
||||
{
|
||||
value: "港澳居民来往内地通行证",
|
||||
label: 6
|
||||
},
|
||||
{
|
||||
value: "台湾居民来往大陆通行证",
|
||||
label: 7
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
that = this
|
||||
this.getSkuType = getSkuType
|
||||
this.getTicketsDetailMethod(e.id)
|
||||
},
|
||||
methods: {
|
||||
playPhone() {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: that.obj.shop.mobile
|
||||
})
|
||||
|
||||
},
|
||||
// xuchao: 到景区去 —— 微信/浏览器环境用 uni.openLocation 打开地图,失败兜底跳高德网页
|
||||
handleOpen() {
|
||||
const lat = parseFloat(this.obj.shop.lat)
|
||||
const lng = parseFloat(this.obj.shop.lng)
|
||||
if (!lat || !lng) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '暂无该景区位置信息'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.openLocation({
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
name: this.obj.shop.name,
|
||||
address: this.obj.shop.address || '',
|
||||
fail: () => {
|
||||
// 备用方案:跳转高德地图网页
|
||||
window.location.href =
|
||||
`https://uri.amap.com/marker?position=${lng},${lat}&name=${this.obj.shop.name}`
|
||||
}
|
||||
})
|
||||
},
|
||||
getTyepStr(e) {
|
||||
let typeStr = ''
|
||||
e.split(',').forEach((item) => {
|
||||
this.enterTypeList.forEach((item1) => {
|
||||
if (item == item1.label) {
|
||||
typeStr = typeStr + item1.value + '、'
|
||||
}
|
||||
})
|
||||
})
|
||||
return typeStr.slice(0, -1)
|
||||
},
|
||||
seeSkuDetail(e) {
|
||||
this.$u.api.getSkuDetail({
|
||||
id: e
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
this.skuObj = res.data
|
||||
this.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
getTicketsDetailMethod(e) {
|
||||
this.list = []
|
||||
this.$u.api.getTicketsDetail({
|
||||
id: e
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
if (res.data.tickets.propagatePic) {
|
||||
res.data.tickets.propagatePic.split(',').forEach((item, index) => {
|
||||
this.list.push({
|
||||
image: item
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.obj = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
toOrderSure(e) {
|
||||
this.$u.api.getSkuDetail({
|
||||
id: e
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
this.skuObj = res.data
|
||||
// this.$store.commit('setScenicDetail', res.data)
|
||||
uni.setStorageSync('scenicDetail',res.data)
|
||||
uni.navigateTo({
|
||||
url: 'ordersure'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #F8F9FC;
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.wrap {
|
||||
width: 750rpx;
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
width: 750rpx;
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
margin-top: 24rpx;
|
||||
|
||||
.ticket_item {
|
||||
margin-top: 24rpx;
|
||||
background: #F8F9FC;
|
||||
border-radius: 8rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 152rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 200rpx;
|
||||
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 750rpx;
|
||||
background-color: #fff;
|
||||
margin-top: 50rpx;
|
||||
padding: 24rpx;
|
||||
|
||||
z-index: 10000;
|
||||
|
||||
.t_info {
|
||||
color: #FF383C;
|
||||
line-height: 48rpx;
|
||||
margin-top: 12rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.t_opentime {
|
||||
background-color: #F8F9FD;
|
||||
padding: 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.t_address {
|
||||
width: 702rpx;
|
||||
height: 100rpx;
|
||||
margin-top: 24rpx;
|
||||
background: url('https://aliyunoss.hn12301.net/xgh/t_address_bj.png');
|
||||
background-size: 100% 100%;
|
||||
color: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 24rpx;
|
||||
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 70rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.t_name {
|
||||
width: 702rpx;
|
||||
color: #000;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #7C7C7E;
|
||||
font-size: 20rpx;
|
||||
padding: 5rpx 10rpx;
|
||||
background: #F8F9FC;
|
||||
border-radius: 4rpx;
|
||||
display: block;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user