feat: 全站 UI 按 Minimalist Modern 重设计,新增返回悬浮钮

- 设计令牌层:mm-* CSS 变量与工具类、uView 换肤、优设标题黑字体
- 九个页面全部重写为新风格(列表/详情/下单/出行人/订单/支付成功/登录)
- 补齐 H5 运行时缺失的基础样式(uni-view/scroll-view/toast/swiper 等,App.vue 全局)
- 修复:详情头图轮播、预订须知弹层滚动、出行人数量按购买数限制、订单详情去箭头
- 新增 gmy-back-touch 返回悬浮钮,除首页外全页接入,点击返回上一页

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-08-05 12:48:45 +08:00
co-authored by Claude Opus 4.8
parent dd25c0ae63
commit 77e5cb9510
19 changed files with 2690 additions and 1335 deletions
+110 -69
View File
@@ -1,27 +1,38 @@
<template>
<view class="auth_rq">
<image class="auth_rq_image" src="https://aliyunoss.hn12301.net/sd/ali_logo.png"></image>
<view class="auth_rq_view">质惠游</view>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 品牌区 —— Logo + 渐变标题 -->
<view class="brand mm-fade-up">
<image class="brand__logo" src="https://aliyunoss.hn12301.net/sd/ali_logo.png"></image>
<view class="brand__name mm-display mm-grad-text">质惠游</view>
</view>
<!-- xuchao: 开发模式提示(正式环境不显示) -->
<view class="auth_rq_tips" v-if="devMode">开发模式:输入测试手机号直接登录</view>
<view class="tips mm-fade-up" v-if="devMode">开发模式:输入测试手机号直接登录</view>
<view class="auth_rq_input" :style="{marginTop: devMode ? '40rpx' : '146rpx'}">
<u-input v-model="value" type="number" :border="false" :custom-style='customStyle' placeholder='请输入手机号' />
</view>
<!-- xuchao: 开发模式免验证码 -->
<view class="auth_rq_input" v-if="!devMode" style="margin-top: 50rpx;">
<u-input v-model="value1" type="number" :border="false" :custom-style='customStyle' placeholder='请输入验证码' />
<text style="margin-left: 30rpx;" :style="{color:isFlag?'#488CFE':'#666'}" @tap="getCode">{{tips}}</text>
</view>
<!-- xuchao: 登录表单 -->
<view class="form mm-fade-up" :style="{marginTop: devMode ? '40rpx' : '96rpx'}">
<view class="field">
<u-input v-model="value" type="number" :border="false" :custom-style='customStyle'
placeholder='请输入手机号' placeholder-class="mm-ph" />
</view>
<!-- xuchao: 开发模式免验证码 -->
<view class="field field--code" v-if="!devMode">
<view class="field__input">
<u-input v-model="value1" type="number" :border="false" :custom-style='customStyle'
placeholder='请输入验证码' placeholder-class="mm-ph" />
</view>
<text :class="['field__code', isFlag ? '' : 'field__code--off']" @tap="getCode">{{tips}}</text>
</view>
<button class="auth_rq_button1" :style="{marginTop: devMode ? '80rpx' : '80rpx'}" @click="doLogin">立即登录</button>
<view class="mm-btn login__btn" @click="doLogin">立即登录</view>
<view style="margin-top: 32rpx;margin-left: 70rpx;">
<u-checkbox-group>
<u-checkbox v-model="checked" shape="circle" style="color: #929292;">我已阅读并同意<text
style="color: #488CFE;" @click="readXy()">用户服务协议</text></u-checkbox>
</u-checkbox-group>
<view class="agree">
<u-checkbox-group>
<u-checkbox v-model="checked" shape="circle" active-color="#0052FF">我已阅读并同意<text
class="agree__link" @click="readXy()">用户服务协议</text></u-checkbox>
</u-checkbox-group>
</view>
</view>
<u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode"
@@ -35,8 +46,12 @@
// 正式流程:微信网页授权(code 换用户信息)+ 短信验证码绑定手机号
import config from '@/common/config.js'
import auth from '@/common/auth.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
devMode: config.devMode,
@@ -47,8 +62,9 @@
value: '',
value1: '',
isFlag: true,
// xuchao: 输入框背景由外层 .field 提供,组件内部保持透明
customStyle: {
background: '#f8f8f8',
background: 'transparent',
}
}
},
@@ -158,60 +174,85 @@
}
}
</script>
<style>
page {
background: #ffffff;
}
</style>
<style lang="less" scoped>
.auth_rq {
.auth_rq_input {
display: flex;
width: 610rpx;
height: 98rpx;
align-items: center;
padding-left: 40rpx;
margin-left: 70rpx;
background-color: #F8F8F8;
border-radius: 8px;
padding-right: 40rpx;
}
.page {
padding: 0 70rpx;
}
.auth_rq_image {
width: 164rpx;
height: 140rpx;
margin-top: 182rpx;
margin-left: 294rpx;
}
/* ---------- 品牌区 ---------- */
.brand {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 140rpx;
}
.auth_rq_view {
width: 750rpx;
text-align: center;
color: #0DD381;
font-size: 36rpx;
margin-top: 18rpx;
}
.brand__logo {
width: 164rpx;
height: 140rpx;
}
// xuchao: 开发模式提示条
.auth_rq_tips {
width: 610rpx;
margin: 40rpx 0 0 70rpx;
padding: 16rpx 24rpx;
box-sizing: border-box;
background-color: #FDF6EC;
color: #E6A23C;
font-size: 24rpx;
border-radius: 8px;
}
.brand__name {
margin-top: 20rpx;
font-size: 52rpx;
letter-spacing: 0.12em;
}
.auth_rq_button1 {
width: 610rpx;
margin-left: 70rpx;
margin-top: 80rpx;
height: 98rpx;
border-radius: 49rpx;
background-color: #488CFE;
color: #fff;
}
/* ---------- 开发模式提示条 ---------- */
.tips {
margin-top: 48rpx;
padding: 16rpx 24rpx;
background: rgba(0, 82, 255, 0.05);
border: 2rpx solid rgba(0, 82, 255, 0.15);
color: var(--mm-accent);
font-size: 24rpx;
border-radius: 16rpx;
text-align: center;
}
/* ---------- 表单 ---------- */
.field {
display: flex;
align-items: center;
height: 96rpx;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 0 40rpx;
transition: box-shadow 0.2s ease;
}
.field--code {
margin-top: 32rpx;
}
.field__input {
flex: 1;
min-width: 0;
}
.field__code {
flex: none;
margin-left: 30rpx;
font-size: 26rpx;
color: var(--mm-accent);
}
.field__code--off {
color: var(--mm-muted-fg);
}
.login__btn {
margin-top: 80rpx;
}
.agree {
margin-top: 32rpx;
color: var(--mm-muted-fg);
font-size: 24rpx;
}
.agree__link {
color: var(--mm-accent);
}
</style>
+429 -258
View File
@@ -1,192 +1,150 @@
<template>
<view v-if="obj">
<view class="detail_h">
<view style="width: 654rpx;display: flex;justify-content: space-between;">
<text style="font-size: 48rpx;font-weight: bold;"
:style="{color:obj.order.orderStatus=='WAITPAY'?'#FF0000':'#000'}">{{orderStatus}}</text>
<view>
<text style="color: #000000;font-size: 20rpx;">¥</text>
<text
style="color: #000000;font-size: 48rpx;font-weight: bold;margin-right: 12rpx;">{{(obj.order.orderAmount/100).toFixed(2)}}</text>
<u-icon name="arrow-right" color="#808080" size="28"></u-icon>
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 状态英雄卡 —— 待支付用主题蓝强调 -->
<view class="hero mm-card mm-fade-up">
<view class="hero__row">
<text :class="['hero__status mm-display', isWaitPay ? 'hero__status--accent' : '']">{{orderStatus}}</text>
<view class="hero__price">
<text class="hero__cur">¥</text>
<text class="hero__num mm-display">{{(obj.order.orderAmount/100).toFixed(2)}}</text>
</view>
</view>
<text style="color:#808080;margin-top: 24rpx;display: block;">{{orderTitle}}</text>
<view class="hero__tip">{{orderTitle}}</view>
</view>
<view class="detail_rq">
<text style="color: #000;line-height: 88rpx;font-size: 36rpx;font-weight: bold;">商品信息</text>
<view style="width: 702rpx;display: flex;justify-content: space-between;">
<text style="color: #000;font-weight: bold;">{{ticketName}}</text>
<u-icon name="arrow-right" color="#808080" size="24"></u-icon>
<!-- xuchao: 商品信息卡 -->
<view class="sec mm-card mm-fade-up">
<view class="mm-sec-title">商品信息</view>
<view class="goods">
<text class="goods__name">{{ticketName}}</text>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">票型类型</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
【{{getSkuType(obj.sku.typeId)}}】&nbsp;{{obj.sku.skuName}}</view>
<view class="kv">
<view class="kv__label">票型类型</view>
<view class="kv__val">【{{getSkuType(obj.sku.typeId)}}】 {{obj.sku.skuName}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">使用日期</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #FF0000;">{{obj.orderGoods.bookDate}} <text
<view class="kv">
<view class="kv__label">使用日期</view>
<view class="kv__val kv__val--accent">{{obj.orderGoods.bookDate}} <text
v-if="obj.orderGoods.timeSlot">({{obj.orderGoods.timeSlot}})</text></view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">生效时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">出票后立即生效</view>
<view class="kv">
<view class="kv__label">生效时间</view>
<view class="kv__val">出票后立即生效</view>
</view>
<view class="rypz" v-if="obj.touristList.length!=0">
<view style="width: 654rpx;text-align: center;display: flex;flex-direction: column;">
<text style="color: #000000;font-size: 32rpx;font-weight: bold;">入园凭证</text>
<scroll-view scroll-x="true" class="xsms-scroll">
<view style="width: 404rpx;display: inline-block;"
:style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}"
v-for="(item,index) in obj.touristList" :key="index">
<view class="rq_item">凭证码</view>
<view
style="width: 404rpx;background: #F8F9FC;display: flex;flex-direction: column;align-items: center;padding-bottom: 24rpx0;border-radius: 0rpx 0rpx 24rpx 24rpx;">
<view class="rq_num">{{item.qrCode}}</view>
<view class="rq_btn" @tap='coyp(item.qrCode)'>复制</view>
<image style="width: 240rpx;height: 240rpx;margin-top: 24rpx;" :src="item.qrCodeBase64">
</image>
<!-- xuchao: 入园凭证(横向滑动,每位游客一张凭证卡) -->
<view class="vch" v-if="obj.touristList.length!=0">
<view class="vch__title">入园凭证</view>
<scroll-view scroll-x="true" class="vch__scroll">
<view class="vch__item" :style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}"
v-for="(item,index) in obj.touristList" :key="index">
<view class="vch__tag">凭证码</view>
<view class="vch__body">
<view class="vch__num">{{item.qrCode}}</view>
<view class="vch__copy" @tap="coyp(item.qrCode)">复制</view>
<view class="vch__qr">
<image :src="item.qrCodeBase64"></image>
</view>
</view>
</scroll-view>
</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园方式</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
{{obj.sku.enterChangeFlag?'凭('+getTyepStr(obj.sku.enterType)+')换票入园':'无需换票'}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="obj.sku.enterChangeFlag">
<view style="width: 120rpx;color: #808080;">换票时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
{{obj.sku.changeStart}}~{{obj.sku.changeEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="obj.sku.enterChangeFlag">
<view style="width: 120rpx;color: #808080;">换票地点</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.changePostion}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.enterStart}}~{{obj.sku.enterEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园地点</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.enterPostion}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">门票</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
¥{{(obj.orderGoods.skuPrice/100).toFixed(2)}} x {{obj.order.buyNum}}</view>
</view>
<view style="position: relative;height: 96rpx;display: flex;align-items: center;" @click="show1=true">
<text style="color: black;font-weight: bold;display: block;width: 120rpx;">取消政策</text>
<image src="https://aliyunoss.hn12301.net/sd/ticket_dg.png"
style="width: 40rpx;height: 40rpx;margin-left: 12rpx;margin-right: 12rpx;"></image>
<text
style="color: rgba(0, 0, 0, 0.8);">{{obj.sku.refundType==1?'随时退':obj.sku.refundType==2?'有条件退':obj.sku.refundType==3?'不可退款':''}}</text>
<u-icon name="arrow-right" color="#808080" size="24" style="position: absolute;right: 0rpx;"></u-icon>
</view>
</view>
<view class="detail_rq" style="border-top: none;padding-bottom: 24rpx;">
<text style="color: #000;line-height: 88rpx;font-size: 36rpx;font-weight: bold;">订单信息</text>
<view v-if="obj.sku.enterDemand=='2'">
<view style="display: flex;margin-top: 24rpx;" v-for="(item,index) in obj.touristList">
<view style="width: 120rpx;color: #808080;">出行人</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;line-height: 48rpx;">{{item.name}} <br />
{{item.phone}}
<br />{{item.identityNo}}
</view>
</view>
</view>
<view v-if="obj.sku.enterDemand=='1'"
style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">联系人</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.orderContactName}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">手机号</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.orderContactMobile}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;position: relative;">
<view style="width: 120rpx;color: #808080;">订单编号</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.id}}</view>
<text style="position: absolute;right: 0rpx;color: #808080;" @tap='coyp(obj.order.id)'>复制</text>
</scroll-view>
</view>
<view style="display: flex;margin-top: 24rpx;position: relative;" v-if="obj.order.orderStatus!='WAITPAY'">
<view style="width: 120rpx;color: #808080;">支付时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;" v-if="obj.payment">
{{obj.payment.createdTime||''}}
</view>
<view class="kv">
<view class="kv__label">入园方式</view>
<view class="kv__val">{{obj.sku.enterChangeFlag?'凭('+getTyepStr(obj.sku.enterType)+')换票入园':'无需换票'}}</view>
</view>
<view class="kv" v-if="obj.sku.enterChangeFlag">
<view class="kv__label">换票时间</view>
<view class="kv__val">{{obj.sku.changeStart}}~{{obj.sku.changeEnd}}</view>
</view>
<view class="kv" v-if="obj.sku.enterChangeFlag">
<view class="kv__label">换票地点</view>
<view class="kv__val">{{obj.sku.changePostion}}</view>
</view>
<view class="kv">
<view class="kv__label">入园时间</view>
<view class="kv__val">{{obj.sku.enterStart}}~{{obj.sku.enterEnd}}</view>
</view>
<view class="kv">
<view class="kv__label">入园地点</view>
<view class="kv__val">{{obj.sku.enterPostion}}</view>
</view>
<view class="kv kv--dash">
<view class="kv__label">门票</view>
<view class="kv__val">¥{{(obj.orderGoods.skuPrice/100).toFixed(2)}} x {{obj.order.buyNum}}</view>
</view>
<!-- xuchao: 取消政策 —— 点击弹层查看条款 -->
<view class="policy" @click="show1=true">
<text class="policy__label">取消政策</text>
<u-icon name="question-circle" color="#0052FF" size="28"></u-icon>
<text
class="policy__val">{{obj.sku.refundType==1?'随时退':obj.sku.refundType==2?'有条件退':obj.sku.refundType==3?'不可退款':''}}</text>
<u-icon name="arrow-right" color="#CBD5E1" size="24" class="policy__arrow"></u-icon>
</view>
</view>
<view style="width: 750rpx;height: 120rpx;"></view>
<view class="detail_btn">
<!-- <view class="tozf">再次购买</view> -->
<view class="tozf" v-if="obj.order.orderStatus=='WAITPAY'" @tap='toPay()'>去支付</view>
<view class="toqx" v-if="obj.order.orderStatus=='WAITPAY'" @tap='cancel()'>取消订单</view>
<view class="toqx" @tap='aginPlay()'
v-if="obj.order.orderStatus=='HAVESEND'||obj.order.orderStatus=='COMPLETED'||obj.order.orderStatus=='BACKPAYED'">
重新购买</view>
<view class="toqx" v-if="obj.order.orderStatus=='HAVESEND'" @tap='refund()'>申请退款</view>
<!-- xuchao: 订单信息卡 -->
<view class="sec mm-card mm-fade-up">
<view class="mm-sec-title">订单信息</view>
<template v-if="obj.sku.enterDemand=='2'">
<view class="kv" v-for="(item,index) in obj.touristList" :key="index">
<view class="kv__label">出行人</view>
<view class="kv__val">{{item.name}} {{item.phone}} {{item.identityNo}}</view>
</view>
</template>
<view class="kv kv--dash" v-if="obj.sku.enterDemand=='1'">
<view class="kv__label">联系人</view>
<view class="kv__val">{{obj.order.orderContactName}}</view>
</view>
<view class="kv kv--dash">
<view class="kv__label">手机号</view>
<view class="kv__val">{{obj.order.orderContactMobile}}</view>
</view>
<view class="kv">
<view class="kv__label">订单编号</view>
<view class="kv__val">{{obj.order.id}}</view>
<text class="kv__copy" @tap="coyp(obj.order.id)">复制</text>
</view>
<view class="kv" v-if="obj.order.orderStatus!='WAITPAY'">
<view class="kv__label">支付时间</view>
<view class="kv__val" v-if="obj.payment">{{obj.payment.createdTime||''}}</view>
</view>
</view>
<u-popup v-model="show1" border-radius='20' mode='bottom'>
<view style="padding:48rpx;display: flex;flex-direction: column;">
<text style="color: #000;font-size: 36rpx;font-weight: bold;">取消政策</text>
<!-- xuchao: 底部按钮区预留(有操作按钮时才占位) -->
<view class="safe" v-if="hasActs"></view>
<!-- xuchao: 底部操作栏 -->
<view class="bar" v-if="hasActs">
<view class="bar__ghost" v-if="obj.order.orderStatus=='HAVESEND'" @tap="refund()">申请退款</view>
<view class="bar__ghost" @tap="aginPlay()"
v-if="obj.order.orderStatus=='HAVESEND'||obj.order.orderStatus=='COMPLETED'||obj.order.orderStatus=='BACKPAYED'">重新购买</view>
<view class="bar__ghost" v-if="obj.order.orderStatus=='WAITPAY'" @tap="cancel()">取消订单</view>
<view class="bar__main mm-grad" v-if="obj.order.orderStatus=='WAITPAY'" @tap="toPay()">去支付</view>
</view>
<!-- <view
style="height: 88rpx;display: flex;align-items: center;position: relative;border-top: 1px dashed #E1E1E1;margin-top: 24rpx;">
<image src="https://aliyunoss.hn12301.net/sd/ticket_dg.png"
style="width: 40rpx;height: 40rpx;margin-right: 12rpx;"></image>
<text style="color: rgba(0, 0, 0, 0.8);">随时退</text>
</view>
<view style="position: relative;color: rgba(0, 0, 0, 0.5);">
该商品支持随时退,经核实未使用后,可免费取消。
</view> -->
<u-parse style="margin-top: 24rpx;" :html="obj.sku.refundPolicy"></u-parse>
<view style="width: 654rpx;height: 120rpx;"></view>
<view
style="width: 654rpx;position: fixed;height: 88rpx;line-height: 88rpx;background: #fff;bottom: 0rpx;left: 0rpx;right: 0rpx;margin: 0 auto;border-bottom: constant(safe-area-inset-bottom) solid #fff;border-bottom: env(safe-area-inset-bottom) solid #fff;">
<u-button shape="circle" size="default"
style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;"
@click="show1=false">确定</u-button>
</view>
<!-- xuchao: 取消政策弹层 -->
<u-popup v-model="show1" border-radius="20" mode="bottom">
<view class="pp mm-safe-bottom">
<view class="mm-sec-title">取消政策</view>
<u-parse class="pp__parse" :html="obj.sku.refundPolicy"></u-parse>
<view class="mm-btn pp__btn" @click="show1=false">确定</view>
</view>
</u-popup>
<u-popup v-model="show" mode='bottom' border-radius='20'>
<view style="padding: 48rpx;">
<view style="width: 654rpx;text-align: center;font-size: 30rpx;font-weight: bold;">请选择游客</view>
<u-checkbox-group :wrap='true'>
<u-checkbox v-model="item.checked" v-for="(item, index) in list" :key="index"
:name="item.name">{{item.name}}</u-checkbox>
<!-- xuchao: 申请退款 —— 选择游客弹层 -->
<u-popup v-model="show" mode="bottom" border-radius="20">
<view class="pp mm-safe-bottom">
<view class="pp__center-title">请选择游客</view>
<u-checkbox-group :wrap="true">
<u-checkbox v-model="item.checked" v-for="(item, index) in list" :key="index" :name="item.name"
active-color="#0052FF">{{item.name}}</u-checkbox>
</u-checkbox-group>
<u-button shape="circle" size="default"
style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;margin-top: 24rpx;"
@click="sure()">确定</u-button>
<view class="mm-btn pp__btn" @click="sure()">确定</view>
</view>
</u-popup>
</view>
@@ -200,7 +158,11 @@
import {
getSkuType
} from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
enterTypeList: [{
@@ -228,7 +190,7 @@
label: '6'
},
{
value: "台湾居民来往大陆通行证",
value: "台湾居民来往内地通行证",
label: '7'
}
],
@@ -294,6 +256,16 @@
}
return str
},
// xuchao: 待支付状态用主题蓝强调
isWaitPay() {
return this.obj && this.obj.order.orderStatus == 'WAITPAY'
},
// xuchao: 是否存在底部操作按钮(决定操作栏与占位是否渲染)
hasActs() {
if (!this.obj) return false
const s = this.obj.order.orderStatus
return s == 'WAITPAY' || s == 'HAVESEND' || s == 'COMPLETED' || s == 'BACKPAYED'
}
},
onLoad(e) {
@@ -341,7 +313,7 @@
},
refund() {
//
//
this.show = true
},
sure() {
@@ -410,114 +382,313 @@
}
}
</script>
<style>
page {
background: #F8F9FC;
}
</style>
<style scoped lang="less">
.xsms-scroll {
.page {
padding: 32rpx 32rpx 40rpx;
}
/* ---------- 状态英雄卡 ---------- */
.hero {
padding: 32rpx;
}
.hero__row {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.hero__status {
font-size: 44rpx;
line-height: 1.1;
color: var(--mm-fg);
}
.hero__status--accent {
color: var(--mm-accent);
}
.hero__price {
display: flex;
align-items: baseline;
}
.hero__cur {
font-size: 24rpx;
color: var(--mm-fg);
margin-right: 2rpx;
}
.hero__num {
font-size: 48rpx;
line-height: 1;
color: var(--mm-fg);
}
.hero__tip {
margin-top: 16rpx;
font-size: 24rpx;
line-height: 1.6;
color: var(--mm-muted-fg);
}
/* ---------- 内容卡片 ---------- */
.sec {
margin-top: 24rpx;
padding: 32rpx 24rpx;
}
.goods {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
}
.goods__name {
flex: 1;
min-width: 0;
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
/* xuchao: 键值行 —— 标签 + 取值,kv--dash 带虚线分隔 */
.kv {
display: flex;
margin-top: 24rpx;
}
.kv--dash {
border-bottom: 2rpx dashed var(--mm-border);
padding-bottom: 24rpx;
}
.kv__label {
flex: none;
width: 120rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.kv__val {
flex: 1;
min-width: 0;
margin-left: 16rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-fg);
}
.kv__val--accent {
color: var(--mm-accent);
font-weight: 500;
}
.kv__copy {
flex: none;
margin-left: 16rpx;
font-size: 24rpx;
color: var(--mm-accent);
}
/* ---------- 入园凭证 ---------- */
.vch {
margin-top: 28rpx;
border: 2rpx dashed var(--mm-border);
border-radius: 20rpx;
padding: 24rpx;
}
.vch__title {
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: var(--mm-fg);
}
.vch__scroll {
overflow: hidden;
white-space: nowrap;
margin-top: 24rpx;
width: 654rpx;
width: 100%;
}
.rypz {
width: 702rpx;
border: 1px dashed #E1E1E1;
.vch__item {
width: 404rpx;
display: inline-block;
}
.vch__tag {
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 82, 255, 0.08);
color: var(--mm-accent);
font-size: 26rpx;
font-weight: 600;
letter-spacing: 0.1em;
border-radius: 20rpx 20rpx 0 0;
}
.vch__body {
display: flex;
flex-direction: column;
align-items: center;
background: var(--mm-muted);
border-radius: 0 0 20rpx 20rpx;
padding-bottom: 32rpx;
}
.vch__num {
width: 356rpx;
margin-top: 24rpx;
padding: 24rpx;
.rq_item {
width: 404rpx;
height: 64rpx;
border-radius: 20rpx 20rpx 0rpx 0rpx;
background: #EEEFF3;
color: #000;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.rq_num {
width: 356rpx;
margin: 24rpx;
color: #000;
font-weight: bold;
white-space: normal;
overflow-wrap: break-word;
}
.rq_btn {
color: rgba(0, 0, 0, 0.8);
width: 72rpx;
height: 36rpx;
font-size: 20rpx;
border-radius: 36rpx;
border: 1px solid #E1E1E1;
display: flex;
align-items: center;
justify-content: center;
}
font-size: 28rpx;
font-weight: 600;
letter-spacing: 0.04em;
text-align: center;
white-space: normal;
overflow-wrap: break-word;
color: var(--mm-fg);
}
.tozf {
height: 64rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 64rpx;
color: #fff;
font-weight: bold;
padding: 0rpx 24rpx;
.vch__copy {
margin-top: 16rpx;
width: 88rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid rgba(0, 82, 255, 0.4);
border-radius: 999rpx;
font-size: 22rpx;
color: var(--mm-accent);
}
.toqx {
height: 64rpx;
background: rgba(0, 0, 0, 0.1);
border-radius: 64rpx;
color: #000;
font-weight: bold;
padding: 0rpx 24rpx;
display: flex;
align-items: center;
}
.detail_btn {
width: 750rpx;
height: 88rpx;
display: flex;
align-items: center;
background-color: #fff;
position: fixed;
bottom: 0rpx;
// xuchao: 视口水平居中(宽屏下与页面列对齐)+ 全面屏底部安全区
left: 0rpx;
right: 0rpx;
margin-left: auto;
margin-right: auto;
border-bottom: constant(safe-area-inset-bottom) solid #fff;
border-bottom: env(safe-area-inset-bottom) solid #fff;
gap: 20rpx;
padding-right: 24rpx;
justify-content: flex-end;
}
.detail_h {
margin: 48rpx 24rpx 0rpx 24rpx;
.vch__qr {
margin-top: 24rpx;
padding: 12rpx;
background: #fff;
border-radius: 20rpx 20rpx 0rpx 0rpx;
padding: 24rpx;
width: 702rpx;
border-radius: 12rpx;
}
.detail_rq {
border-top: 1px #E1E1E1 solid;
background-color: #fff;
padding: 24rpx 24rpx 0rpx 24rpx;
.vch__qr image {
width: 240rpx;
height: 240rpx;
display: block;
}
</style>
/* ---------- 取消政策入口 ---------- */
.policy {
display: flex;
align-items: center;
height: 96rpx;
position: relative;
}
.policy__label {
font-size: 28rpx;
font-weight: 600;
color: var(--mm-fg);
margin-right: 12rpx;
}
.policy__val {
margin-left: 12rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.policy__arrow {
position: absolute;
right: 0;
}
/* ---------- 底部操作栏 ---------- */
.safe {
height: 160rpx;
}
.bar {
position: fixed;
left: 0;
right: 0;
margin: 0 auto;
width: 750rpx;
bottom: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 20rpx;
background: var(--mm-card);
border-top: 2rpx solid var(--mm-border);
padding: 20rpx 24rpx;
/* xuchao: 全面屏底部安全区 */
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.bar__main {
height: 72rpx;
padding: 0 40rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999rpx;
color: #fff;
font-size: 28rpx;
font-weight: 600;
box-shadow: var(--mm-shadow-accent);
transition: transform 0.15s ease;
}
.bar__main:active {
transform: scale(0.96);
}
.bar__ghost {
height: 72rpx;
padding: 0 32rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999rpx;
border: 2rpx solid var(--mm-border);
color: var(--mm-fg);
font-size: 28rpx;
transition: background 0.15s ease;
}
.bar__ghost:active {
background: var(--mm-muted);
}
/* ---------- 弹层 ---------- */
.pp {
padding: 48rpx;
display: flex;
flex-direction: column;
}
.pp__parse {
margin-top: 24rpx;
font-size: 26rpx;
line-height: 1.7;
color: var(--mm-fg);
}
.pp__btn {
margin-top: 48rpx;
}
.pp__center-title {
text-align: center;
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
margin-bottom: 24rpx;
}
</style>
+162 -38
View File
@@ -1,38 +1,67 @@
<template>
<view>
<view class="item" v-for="(item,index) in lookList" :key="index" @tap='toOrderDetail(item.id)'>
<view class="item_h">
<text>订单编号:{{item.id}}</text>
<text>{{getText(item.orderStatus,1)}}</text>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 页头徽章(自定义导航无标题栏,补一个页面标识) -->
<view class="head mm-fade-up">
<view class="mm-badge">
<view class="mm-badge__dot"></view>
<text class="mm-badge__text">门票订单</text>
</view>
<view class="item_rq">
<view style="font-weight: bold;color: #000;">{{item.skuName}}</view>
<view style="width: 606rpx;display: flex;justify-content: space-between;color: rgba(0, 0, 0, 0.8);margin-top: 24rpx;">
<text>使用日期:{{item.orderDate}}</text>
<text>¥{{(item.orderAmount/100).toFixed(2)}}</text>
</view>
<!-- xuchao: 订单卡片列表 —— 整卡可点进详情 -->
<view class="list mm-stagger">
<view class="order mm-card mm-fade-up" v-for="(item,index) in lookList" :key="index"
@tap="toOrderDetail(item.id)">
<view class="order__head">
<text class="order__no">订单编号:{{item.id}}</text>
<text :class="['order__status', statusClass(item.orderStatus)]">{{getText(item.orderStatus,1)}}</text>
</view>
<view class="order__body">
<view class="order__name">{{item.skuName}}</view>
<view class="order__foot">
<text class="order__date">使用日期:{{item.orderDate}}</text>
<view class="order__price">
<text class="order__cur">¥</text>
<text class="order__num mm-display mm-grad-text">{{(item.orderAmount/100).toFixed(2)}}</text>
</view>
</view>
</view>
</view>
</view>
<u-loadmore :status="status" />
<!-- xuchao: 空态(首次请求返回空列表才显示) -->
<view class="empty mm-fade-up" v-if="showEmpty">
<u-icon name="empty-order" color="#CBD5E1" size="120"></u-icon>
<view class="empty__text">暂无相关订单</view>
</view>
<u-loadmore v-if="lookList.length > 0" :status="status" />
</view>
</template>
<script>
var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default{
components: {
'gmy-back-touch': gmyBackTouch
},
data(){
return{
status: 'loadmore',
obj: {
"orderPhone": "",
"orderStatus": "",
"orderStatus": "",
"pageSize": 10,
"pageNum": 1,
},
pageAll:2,
isLoad:true,
lookList:[],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
orderList:[{
text:'待支付',
value:'WAITPAY',
@@ -64,11 +93,16 @@
}]
}
},
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad(){
that=this
this.obj.orderPhone=uni.getStorageSync('phone')
},
onShow() {
this.pageAll=2
@@ -93,11 +127,19 @@
return str
}
},
// xuchao: 订单状态 → 徽章色调(待办类用主题蓝强调,终态用中性灰)
statusClass(e) {
if (['WAITPAY', 'WAITSEND', 'REFUNDING'].indexOf(e) > -1) {
return 'order__status--active'
}
return ''
},
queryTicketsOrderPageMethod() {
if (that.obj.pageNum <= that.pageAll&&this.isLoad) {
this.isLoad=false
this.$u.api.queryTicketsOrderPage(this.obj).then(res => {
this.isLoad=true
this.firstLoaded = true
that.obj.pageNum = that.obj.pageNum * 1 + 1;
that.pageAll = Math.ceil(res.data.total / 10)
if (that.page == 2) {
@@ -120,29 +162,111 @@
}
</script>
<style>
page{
background: #F8F9FC;
padding-top: 24rpx;
<style lang="less" scoped>
.page {
padding: 32rpx 32rpx 40rpx;
}
.head {
display: flex;
margin-bottom: 32rpx;
}
.list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.order {
padding: 24rpx;
transition: transform 0.2s ease-out, box-shadow 0.3s ease-out;
}
.order:active {
transform: scale(0.985);
box-shadow: var(--mm-shadow-sm);
}
.order__head {
display: flex;
align-items: center;
justify-content: space-between;
}
.order__no {
font-size: 22rpx;
letter-spacing: 0.02em;
color: var(--mm-muted-fg);
}
.order__status {
flex: none;
font-size: 22rpx;
color: var(--mm-muted-fg);
background: var(--mm-muted);
border-radius: 999rpx;
padding: 4rpx 20rpx;
}
/* xuchao: 待办类状态(待支付/出票中/退款中)—— 主题蓝强调 */
.order__status--active {
color: var(--mm-accent);
background: rgba(0, 82, 255, 0.08);
}
.order__body {
margin-top: 20rpx;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 24rpx;
}
.order__name {
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
.order__foot {
margin-top: 16rpx;
display: flex;
align-items: baseline;
justify-content: space-between;
}
.order__date {
font-size: 24rpx;
color: var(--mm-muted-fg);
}
.order__price {
display: flex;
align-items: baseline;
}
.order__cur {
font-size: 24rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 2rpx;
}
.order__num {
font-size: 40rpx;
line-height: 1;
}
.empty {
margin-top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.empty__text {
font-size: 26rpx;
color: var(--mm-muted-fg);
}
</style>
<style lang="less" scoped>
.item{
width: 750rpx;
margin-bottom: 20rpx;
padding: 24rpx;
background: #fff;
.item_h{
width: 702rpx;
display: flex;
justify-content: space-between;
color: rgba(0, 0, 0, 0.5);
}
.item_rq{
width: 654rpx;
margin: 24rpx 24rpx 0rpx 24rpx;
background: #F8F9FC;
padding: 24rpx;
}
}
</style>
+594 -342
View File
File diff suppressed because it is too large Load Diff
+98 -73
View File
@@ -1,31 +1,36 @@
<template>
<view v-if="obj">
<view class="od_top">
<view class="od_top_first">
<image src="http://cdn.hntgov.net/online/static/ps_ps.png"></image>支付成功
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 成功状态英雄区 —— 渐变对勾 + 柔光 -->
<view class="hero mm-fade-up">
<view class="hero__icon mm-grad">
<u-icon name="checkmark" color="#ffffff" size="64"></u-icon>
</view>
<view class="hero__title mm-display">支付成功</view>
<view class="hero__sub">门票订单已提交,请留意出票通知</view>
</view>
<view style="background-color: #fff;">
<view class="od_sh">
<image style="width: 32rpx;height: 28rpx;" src="http://cdn.hntgov.net/online/static/pas_ps.png">
</image>
<view class="od_sh_right">
<!-- xuchao: 支付方式改为微信支付 -->
<text style="color: #696969;">支付方式:<text style="color: #3D3D3D;">微信支付</text></text>
<text style="color: #696969;padding-top: 16rpx;">实付金额:<text
style="color: #3D3D3D;">¥{{(obj.order.orderAmount/100).toFixed(2)||0}}</text></text>
<!-- xuchao: 支付信息卡 -->
<view class="card mm-card mm-fade-up">
<view class="row">
<text class="row__label">支付方式</text>
<text class="row__val">微信支付</text>
</view>
<view class="row">
<text class="row__label">实付金额</text>
<view class="row__price">
<text class="row__cur">¥</text>
<text class="row__num mm-display mm-grad-text">{{(obj.order.orderAmount/100).toFixed(2)||0}}</text>
</view>
</view>
<view class="od_line"></view>
<view class="ps_item">
<!-- xuchao: 接通"查看订单"入口(旧版此按钮被注释成了死页面) -->
<view class="ps_item_item" style="color: #181617;" @tap='seeOrder()'>查看订单</view>
<view style="width: 1rpx;height: 48rpx;background-color: #E7E6E6;"></view>
<view class="ps_item_item" style="color: #ED2D38;" @tap='toMall()'>继续逛逛</view>
</view>
</view>
<!-- xuchao: 操作按钮 —— 主操作渐变、次操作描边 -->
<view class="acts mm-fade-up">
<view class="mm-btn" @tap="seeOrder()">查看订单</view>
<view class="mm-btn-ghost" @tap="toMall()">继续逛逛</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
@@ -33,7 +38,11 @@
<script>
// xuchao: 支付成功页 —— 支付完成后跳入,轮询订单状态直到后端确认
var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
id: '',
@@ -90,74 +99,90 @@
}
</script>
<style>
page {
background-color: #f7f7f7;
}
</style>
<style scoped lang="scss">
.ps_item {
width: 690rpx;
margin-left: 30rpx;
height: 96rpx;
align-items: center;
display: flex;
.ps_item_item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: bold;
}
<style scoped lang="less">
.page {
padding: 96rpx 32rpx 60rpx;
}
.od_top {
width: 750rpx;
height: 210rpx;
background: linear-gradient(90deg, #FF3F3C -8%, #FFA371 107%);
padding-top: 56rpx;
padding-left: 70rpx;
/* ---------- 英雄区 ---------- */
.hero {
display: flex;
flex-direction: column;
align-items: center;
}
.od_top_first {
.hero__icon {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
display: flex;
align-items: center;
color: #FFF;
font-size: 34rpx;
justify-content: center;
box-shadow: var(--mm-shadow-accent), 0 0 0 24rpx rgba(0, 82, 255, 0.06);
}
.od_top_first image {
width: 50rpx;
height: 50rpx;
margin-right: 20rpx;
.hero__title {
margin-top: 36rpx;
font-size: 48rpx;
color: var(--mm-fg);
}
.od_sh {
padding: 30rpx;
.hero__sub {
margin-top: 12rpx;
font-size: 24rpx;
color: var(--mm-muted-fg);
}
/* ---------- 支付信息卡 ---------- */
.card {
margin-top: 64rpx;
padding: 8rpx 32rpx;
}
.row {
display: flex;
align-items: center;
font-size: 26rpx;
background-color: #fff;
justify-content: space-between;
height: 104rpx;
.od_sh_right {
width: 640rpx;
margin-left: 18rpx;
display: flex;
flex-direction: column;
&:first-child {
border-bottom: 2rpx solid var(--mm-muted);
}
}
.od_line {
width: 690rpx;
height: 1rpx;
background-color: #E7E6E6;
margin-left: 30rpx;
.row__label {
font-size: 28rpx;
color: var(--mm-muted-fg);
}
.row__val {
font-size: 28rpx;
font-weight: 500;
color: var(--mm-fg);
}
.row__price {
display: flex;
align-items: baseline;
}
.row__cur {
font-size: 26rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 4rpx;
}
.row__num {
font-size: 44rpx;
line-height: 1;
}
/* ---------- 操作按钮 ---------- */
.acts {
margin-top: 64rpx;
display: flex;
flex-direction: column;
gap: 24rpx;
}
</style>
+42 -36
View File
@@ -1,35 +1,40 @@
<template>
<view>
<u-form :model="form" :rules="rules" ref="uForm">
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;">
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 表单统一收进一张卡片,分隔线由 u-form-item 自带(已随主题换色) -->
<view class="card mm-card mm-fade-up">
<u-form :model="form" :rules="rules" ref="uForm">
<u-form-item label-width="200" :label-position="labelPosition" label="出行人姓名" prop="name">
<u-input placeholder="请输入出行人姓名" v-model="form.name" type="text"></u-input>
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label-width="200" :label-position="labelPosition" label="联系号码" prop="phone">
<u-input placeholder="请输入联系号码" v-model="form.phone" type="text"></u-input>
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label="证件类型" prop="idType" label-width="200" :label-position="labelPosition">
<u-input v-model="idTypeName" placeholder='请选择证件类型' type="select" @click="idTypeShow()" />
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label-width="200" :label-position="labelPosition" label="证件号码" prop="identityNo">
<u-input placeholder="请输入证件号码" v-model="form.identityNo" type="text"></u-input>
</u-form-item>
</view>
</u-form>
<view class="pp_hh" @click="submit()">完成</view>
</u-form>
</view>
<!-- xuchao: 底部固定提交按钮(视口水平居中,宽屏下与页面列对齐) -->
<view class="bar">
<view class="mm-btn" @click="submit()">完成</view>
</view>
<u-action-sheet :list="actionSheetList" v-model="show" @click="actionSheetCallback"></u-action-sheet>
</view>
</template>
<script>
var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
show: false,
@@ -54,7 +59,7 @@
},
{
value: 'TAIWAN',
text: '台湾居民来往大陆通行证'
text: '台湾居民来往内地通行证'
}
],
form: {
@@ -70,19 +75,19 @@
name: [{
required: true,
message: '请填写真实姓名',
// 可以单个或者同时写两个触发验证方式
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
}],
phone: [{
required: true,
message: '请填写手机号码',
// 可以单个或者同时写两个触发验证方式
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
}],
identityNo: [{
required: true,
message: '请填写证件号码',
// 可以单个或者同时写两个触发验证方式
// 可以单个或者同时写两个触发验证方式
trigger: ['change', 'blur'],
}],
},
@@ -176,7 +181,7 @@
});
},
updateTicketsMemberTouristMethod() {
this.$set(this.form,'id',this.id)
console.log(this.form);
that.$u.api.updateTicketsMemberTourist(this.form).then(res => {
@@ -198,23 +203,24 @@
</script>
<style scoped lang="less">
.pp_hh {
width: 702rpx;
height: 88rpx;
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 28rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
// xuchao: 视口水平居中(宽屏下与页面列对齐)
left: 0rpx;
right: 0rpx;
margin: 0 auto;
position: fixed;
// xuchao: 全面屏底部安全区 —— 悬浮按钮整体上移
bottom: calc(25rpx + constant(safe-area-inset-bottom));
bottom: calc(25rpx + env(safe-area-inset-bottom));
/* xuchao: 底部预留 220rpx,避免表单被固定提交按钮遮挡 */
.page {
padding: 32rpx 32rpx 220rpx;
}
</style>
.card {
padding: 12rpx 32rpx;
}
/* xuchao: 底部固定按钮 —— left/right 0 + margin auto 保证宽屏居中时与页面列对齐 */
.bar {
position: fixed;
left: 0;
right: 0;
margin: 0 auto;
width: 686rpx;
bottom: calc(32rpx + constant(safe-area-inset-bottom));
bottom: calc(32rpx + env(safe-area-inset-bottom));
z-index: 99;
}
</style>
+176 -210
View File
@@ -1,22 +1,46 @@
<template>
<view>
<view class='nr_item' v-for="(item,index) in lookList" :key="index" @click="toDetail(item.id)">
<image class="nr_img" :src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image>
<view class="nr_item_r">
<text class="n_text">{{item.name||''}}</text>
<view class="nr_tags">
<text :class="index1==0?'t_text1':index1==1?'t_text':index1==2?'t_text2':'t_text3'"
v-for="(item1,index1) in ['节日福利/春游秋游','余额券','满减券']" :key="index1">{{item1}}</text>
<view class="page mm-page">
<!-- xuchao: 景区卡片列表 —— 整卡可点进详情,入场逐个渐显 -->
<view class="list mm-stagger">
<view class="card mm-card mm-fade-up" v-for="(item,index) in lookList" :key="index"
@click="toDetail(item.id)">
<image class="card__img" mode="aspectFill"
:src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image>
<view class="card__body">
<view class="card__name">{{item.name||''}}</view>
<view class="card__desc" v-if="item.subTypeId">{{item.subTypeId}}</view>
<view class="card__foot">
<view class="card__price">
<view class="card__origin">原价 <text
class="card__origin-num">¥{{(item.price/100).toFixed(0)}}</text></view>
<view class="card__now">
<text class="card__cur">¥</text>
<text class="card__num mm-display mm-grad-text">{{(item.unitPrice/100).toFixed(0)}}</text>
<text class="card__unit">元</text>
</view>
</view>
<view class="card__cta mm-grad">
<text>预订</text>
<u-icon name="arrow-right" color="#ffffff" size="22"></u-icon>
</view>
</view>
</view>
<view class="r_text">{{item.subTypeId}}</view>
<view class="nr_origin">原售价<text
class="nr_origin_num">{{(item.price/100).toFixed(0)}}</text>元</view>
<view class="p_text"><text class="p_label">会员补贴价</text><text class="p_symbol">¥</text><text
class="p_num">{{(item.unitPrice/100).toFixed(0)}}</text><text class="p_unit">元</text></view>
</view>
</view>
<!-- xuchao: 空态(首次请求返回空列表才显示,避免加载中闪烁) -->
<view class="empty mm-fade-up" v-if="showEmpty">
<u-icon name="empty-coupon" color="#CBD5E1" size="120"></u-icon>
<view class="empty__text">暂无在售门票产品</view>
</view>
<!-- xuchao: 列表底部:仅分页加载时显示"正在加载…" -->
<view class="foot" v-if="lookList.length > 0 && !isLoad">
<text>正在加载…</text>
</view>
<!-- xuchao: 订单悬浮按钮 -->
<gmy-float-touch :mainImg="mainImg" @menuClick="floatTouchClick"></gmy-float-touch>
<gmy-float-touch @menuClick="floatTouchClick"></gmy-float-touch>
</view>
</template>
@@ -30,10 +54,11 @@
components: { gmyFloatTouch },
data() {
return {
mainImg: "../../static/logo4.png",
pageAll: 2,
isLoad: true,
lookList: [],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
obj: {
"name": "",
"typeId": "",
@@ -44,6 +69,11 @@
}
}
},
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad() {
that = this
// xuchao: 未登录则(正式)跳登录页 / (开发)静默假登录
@@ -59,8 +89,10 @@
this.isLoad = false
this.$u.api.queryTicketsPage(this.obj).then(res => {
this.isLoad = true
this.firstLoaded = true
this.obj.pageNum = this.obj.pageNum * 1 + 1
this.pageAll = Math.ceil(res.data.total / 10)
this.total = res.data.total
this.lookList = this.lookList.concat(res.data.list)
})
}
@@ -82,209 +114,143 @@
}
</script>
<style>
page {
background: #FFEFD5;
}
</style>
<style scoped lang="less">
.nr_item {
// xuchao: 左右边距用百分比,随屏幕宽度等比变化;宽度自适应(去掉固定 702rpx)
width: auto;
box-sizing: border-box;
margin: 24rpx 3.2% 0;
display: flex;
background: #fff;
padding: 12rpx;
.nr_img {
width: 268rpx;
height: 268rpx;
flex: none;
}
.nr_item_r {
// xuchao: 右侧信息列改为弹性填充,配合卡片宽度自适应
flex: 1;
min-width: 0;
margin-left: 20rpx;
display: flex;
gap: 8rpx;
flex-direction: column;
position: relative;
.nr_tags {
display: flex;
gap: 12rpx;
flex-wrap: wrap;
}
.p_text {
position: absolute;
bottom: 0rpx;
right: 0rpx;
// xuchao: border-box 让盒子精确等于底图宽度;右侧 padding 让出蓝色箭头区,文字在橙色槽内居中
width: 250rpx;
height: 50rpx;
box-sizing: border-box;
background: url('https://aliyunoss.hn12301.net/xgh/image_bj_text.png');
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding-left: 14rpx;
padding-right: 51rpx;
border-radius: 4rpx;
}
.p_label {
font-size: 16rpx;
}
.p_symbol {
font-size: 18rpx;
margin: 0 2rpx 0 6rpx;
}
.p_num {
font-size: 32rpx;
font-style: italic;
font-weight: bold;
}
.p_unit {
font-size: 16rpx;
// xuchao: 价格与"元"之间留出明显间距(8rpx ≈ 375 屏 4px)
margin-left: 8rpx;
}
.nr_origin {
position: absolute;
bottom: 60rpx;
right: 0rpx;
width: 100%;
text-align: right;
color: #4F4F4F;
font-size: 12rpx;
}
.nr_origin_num {
font-size: 28rpx;
text-decoration: line-through;
font-weight: bold;
}
.r_text {
width: 100%;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 20rpx;
}
.n_text {
color: #008F90;
font-weight: bolder;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
font-size: 32rpx;
}
.t_text {
background-color: #FDEEC3;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #D57817;
border-radius: 4rpx;
}
.t_text1 {
background-color: #FDF4ED;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #AF4744;
border-radius: 4rpx;
}
.t_text2 {
background-color: #cbebfb;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #6ba0b3;
border-radius: 4rpx;
}
.t_text3 {
background-color: #F0F1F5;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #525357;
border-radius: 4rpx;
}
}
.page {
padding: 32rpx 32rpx 40rpx;
}
// 以下为旧版遗留、当前模板未使用的样式(.item / .r_text1 / .n_text1),按约定仅提示不删
.item {
width: 702rpx;
padding: 12rpx;
border-radius: 8rpx;
background-color: #fff;
position: relative;
margin-top: 24rpx;
/* ---------- 卡片列表 ---------- */
.list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.card {
display: flex;
padding: 20rpx;
box-sizing: border-box;
transition: transform 0.2s ease-out, box-shadow 0.3s ease-out;
}
.card:active {
transform: scale(0.985);
box-shadow: var(--mm-shadow-sm);
}
.card__img {
width: 208rpx;
height: 208rpx;
flex: none;
border-radius: 18rpx;
background: var(--mm-muted);
}
.card__body {
flex: 1;
min-width: 0;
margin-left: 24rpx;
display: flex;
flex-direction: column;
}
.item_name {
width: 434rpx;
line-height: normal;
color: #000;
font-size: 28rpx;
font-weight: bold;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.card__name {
font-size: 30rpx;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--mm-fg);
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.tag {
font-size: 20rpx;
color: #fff;
padding: 6rpx 5rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 4rpx;
margin-top: 12rpx;
margin-right: 12rpx;
}
.card__desc {
margin-top: 8rpx;
font-size: 22rpx;
line-height: 1.45;
color: var(--mm-muted-fg);
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.tag1 {
font-size: 20rpx;
color: #999999;
padding: 5rpx;
border: 2rpx #999999 solid;
border-radius: 4rpx;
margin-top: 12rpx;
margin-right: 12rpx;
}
.card__foot {
margin-top: auto;
padding-top: 12rpx;
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.hot_tag {
height: 36rpx;
color: #fff;
font-size: 20rpx;
position: absolute;
top: 12rpx;
left: 12rpx;
background: rgba(0, 0, 0, 0.5);
padding: 0rpx 8rpx;
border-radius: 8rpx 0rpx 8rpx 0rpx;
line-height: 36rpx;
}
.card__origin {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.card__origin-num {
text-decoration: line-through;
}
.card__now {
display: flex;
align-items: baseline;
}
.card__cur {
font-size: 24rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 2rpx;
}
.card__num {
font-size: 46rpx;
line-height: 1;
}
.card__unit {
font-size: 20rpx;
color: var(--mm-muted-fg);
margin-left: 6rpx;
}
/* xuchao: 预订按钮 —— 渐变胶囊 + 箭头,承担整卡的行动号召 */
.card__cta {
flex: none;
display: flex;
align-items: center;
gap: 6rpx;
height: 60rpx;
padding: 0 26rpx;
border-radius: 999rpx;
color: #fff;
font-size: 24rpx;
box-shadow: 0 6rpx 16rpx rgba(0, 82, 255, 0.25);
}
/* ---------- 空态与底部 ---------- */
.empty {
margin-top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.empty__text {
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.foot {
padding: 44rpx 0 20rpx;
text-align: center;
font-size: 22rpx;
letter-spacing: 0.1em;
color: var(--mm-muted-fg);
}
</style>
+423 -229
View File
@@ -1,159 +1,151 @@
<template>
<view v-if="obj">
<view class="wrap">
<u-swiper :list="list" height='750' mode='number' indicator-pos='bottomRight'
marginBottom='30rpx'></u-swiper>
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 头图轮播(保留原有数字指示器) -->
<u-swiper :list="list" height="750" mode="number" indicator-pos="bottomRight"
marginBottom="30rpx"></u-swiper>
<!-- xuchao: 信息面板 —— 上移盖住头图底部的圆角 sheet,承载景区核心信息 -->
<view class="sheet mm-fade-up">
<view class="t_head">
<view class="t_name mm-display">{{obj.tickets.name}}</view>
<view class="t_level mm-grad" v-if="obj.shop.level">{{obj.shop.level}}</view>
</view>
<view class="t_info" v-if="obj.tickets.remark1">{{obj.tickets.remark1}}</view>
<!-- xuchao: 开园时间 —— 蓝色浅底提示条 -->
<view class="t_open" v-if="obj.tickets.openDate">
<u-icon name="clock-fill" color="#0052FF" size="28"></u-icon>
<text class="t_open__label">开园时间</text>
<text class="t_open__val">{{obj.tickets.openDate}}</text>
</view>
<!-- xuchao: 地址 + 导航/电话快捷圆钮 -->
<view class="t_addr">
<view class="t_addr__text">{{obj.shop.address}}</view>
<view class="t_addr__acts">
<view class="t_addr__btn mm-grad" @click="handleOpen()">
<u-icon name="map-fill" color="#ffffff" size="32"></u-icon>
</view>
<view class="t_addr__btn mm-grad" @click="playPhone()">
<u-icon name="phone-fill" color="#ffffff" size="32"></u-icon>
</view>
</view>
</view>
</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>
<!-- xuchao: 门票规格列表 -->
<view class="sec mm-fade-up">
<view class="sec__title mm-sec-title">门票规格</view>
<view class="sku" v-for="(item,index) in obj.skuList" :key="index">
<!-- xuchao: 名称提为独立整行,填充卡片宽度,不再被右侧价格列挤压换行 -->
<view class="sku__name">【{{getSkuType(item.typeId)}}】{{item.skuName}}</view>
<view class="sku__body">
<view class="sku__left">
<view class="sku__rule">{{item.bookTimeLimit}}之前{{item.bookType?'可订当天':'可订次日'}}</view>
<view class="sku__know" @click="seeSkuDetail(item.id)">
<text>预订须知</text>
<u-icon name="arrow-right" color="#0052FF" size="20"></u-icon>
</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;">
<view class="sku__right">
<view class="sku__origin">原售价 <text
class="sku__origin-num">¥{{item.calendar.listPrice?(item.calendar.listPrice/100).toFixed(2):0}}</text></view>
<view class="sku__price">
<text class="sku__price-label">¥</text>
<text
style="font-size: 20rpx;font-weight: normal;">工会会员补贴价¥</text>{{item.calendar.salePrice?(item.calendar.salePrice/100).toFixed(2):0}}
class="sku__price-num mm-display mm-grad-text">{{item.calendar.salePrice?(item.calendar.salePrice/100).toFixed(2):0}}</text>
</view>
<view class="btn" @tap="toOrderSure(item.id)">购买</view>
<view class="sku__buy mm-grad" @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>
</view>
<!-- xuchao: 产品介绍(后端富文本) -->
<view class="sec mm-fade-up">
<view class="sec__title mm-sec-title">产品介绍</view>
<view class="intro">
<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>
<!-- xuchao: 预订须知弹层 -->
<u-popup v-model="show" border-radius="20" mode="bottom" height="1000">
<view class="pp" v-if="skuObj">
<view class="pp__head">{{skuObj.sku.skuName}}</view>
<view class="pp__sec">
<view class="mm-sec-title">预订规则</view>
<view class="pp__row">
<view class="pp__label">预订时间</view>
<view class="pp__val">{{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 class="pp__row">
<view class="pp__label">生效时间</view>
<view class="pp__val">出票后立即使用</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 class="pp__row">
<view class="pp__label">有效期</view>
<view class="pp__val">指定出行日期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 class="pp__row">
<view class="pp__label">适用人群</view>
<view class="pp__val">{{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 class="pp__sec">
<view class="mm-sec-title">费用说明</view>
<view class="pp__row">
<view class="pp__label">费用包含</view>
<view class="pp__val">{{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 class="pp__row">
<view class="pp__label">费用不含</view>
<view class="pp__val">{{skuObj.sku.contentWithout}}</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 class="pp__sec">
<view class="mm-sec-title">使用说明</view>
<view class="pp__row">
<view class="pp__label">入园方式</view>
<view class="pp__val">{{skuObj.sku.enterChangeFlag?'凭('+getTyepStr(skuObj.sku.enterType)+')换票入园':'无需换票'}}</view>
</view>
<view class="pp__row" v-if="skuObj.sku.enterChangeFlag">
<view class="pp__label">换票时间</view>
<view class="pp__val">{{skuObj.sku.changeStart}}~{{skuObj.sku.changeEnd}}</view>
</view>
<view class="pp__row" v-if="skuObj.sku.enterChangeFlag">
<view class="pp__label">换票地点</view>
<view class="pp__val">{{skuObj.sku.changePostion}}</view>
</view>
<view class="pp__row">
<view class="pp__label">入园时间</view>
<view class="pp__val">{{skuObj.sku.enterStart}}~{{skuObj.sku.enterEnd}}</view>
</view>
<view class="pp__row">
<view class="pp__label">入园地址</view>
<view class="pp__val">{{skuObj.sku.enterPostion}}</view>
</view>
</view>
<view class="pp__sec">
<view class="mm-sec-title">取消政策</view>
<u-parse class="pp__parse" :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 class="pp__sec">
<view class="mm-sec-title">其他说明</view>
<view class="pp__row">
<view class="pp__label">补充说明</view>
<view class="pp__val">{{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 class="pp__row">
<view class="pp__label">发票说明</view>
<view class="pp__val">{{skuObj.sku.receiptPolicy}}</view>
</view>
</view>
</view>
</u-popup>
@@ -165,7 +157,11 @@
import {
getSkuType
} from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
show: false,
@@ -267,11 +263,20 @@
id: e
}).then(res => {
if (res) {
// xuchao: 头图首张放单票主图(与列表卡片一致),再拼宣传图,跳过与主图重复项
const mainPic = res.data.tickets.mainPic
if (mainPic) {
this.list.push({
image: mainPic
})
}
if (res.data.tickets.propagatePic) {
res.data.tickets.propagatePic.split(',').forEach((item, index) => {
this.list.push({
image: item
})
if (item && item !== mainPic) {
this.list.push({
image: item
})
}
})
}
@@ -297,103 +302,292 @@
},
};
</script>
<style>
page {
background: #F8F9FC;
<style lang="less" scoped>
.page {
padding-bottom: 80rpx;
}
/* xuchao: 头图固定 298px 高(u-swiper 的 height 属性只认 rpx,故在此覆写其内联高度),
图片按比例缩放居中裁切(object-fit: cover) */
.u-swiper-wrap /deep/ uni-swiper {
height: 298px !important;
}
.u-swiper-wrap /deep/ img {
object-fit: cover;
}
/* ---------- 信息 sheet(盖住头图底部) ---------- */
.sheet {
position: relative;
z-index: 10;
margin-top: -56rpx;
background: var(--mm-card);
border-radius: 40rpx 40rpx 0 0;
padding: 40rpx 32rpx 28rpx;
box-shadow: 0 -12rpx 32rpx rgba(15, 23, 42, 0.06);
}
.t_head {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.t_name {
flex: 1;
min-width: 0;
font-size: 44rpx;
line-height: 1.2;
color: var(--mm-fg);
}
/* xuchao: 景区等级徽标 —— 渐变胶囊,与景区名右对齐 */
.t_level {
flex: none;
margin: 8rpx 0 0 16rpx;
padding: 6rpx 20rpx;
border-radius: 999rpx;
color: #fff;
font-size: 22rpx;
box-shadow: var(--mm-shadow-accent);
}
.t_info {
margin-top: 12rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-muted-fg);
}
.t_open {
margin-top: 24rpx;
display: flex;
align-items: center;
background: rgba(0, 82, 255, 0.05);
border-radius: 16rpx;
padding: 18rpx 24rpx;
}
.t_open__label {
flex: none;
margin: 0 16rpx 0 12rpx;
font-size: 24rpx;
font-weight: 500;
color: var(--mm-accent);
}
.t_open__val {
font-size: 24rpx;
color: var(--mm-fg);
}
.t_addr {
margin-top: 24rpx;
display: flex;
align-items: center;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 24rpx;
}
.t_addr__text {
flex: 1;
min-width: 0;
font-size: 26rpx;
line-height: 1.5;
color: var(--mm-fg);
}
.t_addr__acts {
flex: none;
display: flex;
gap: 16rpx;
margin-left: 20rpx;
}
.t_addr__btn {
width: 76rpx;
height: 76rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--mm-shadow-accent);
transition: transform 0.15s ease;
}
.t_addr__btn:active {
transform: scale(0.9);
}
/* ---------- 内容分区(门票规格 / 产品介绍) ---------- */
.sec {
margin: 24rpx 32rpx 0;
background: var(--mm-muted);
border-radius: var(--mm-radius);
padding: 28rpx 24rpx;
}
.sec__title {
margin-bottom: 4rpx;
}
/* ---------- 规格卡片 ---------- */
.sku {
margin-top: 20rpx;
background: var(--mm-card);
border: 2rpx solid var(--mm-border);
border-radius: 20rpx;
padding: 24rpx;
box-shadow: var(--mm-shadow-sm);
}
/* xuchao: 名称下方的两栏区(左规则/预订须知,右价格/购买) */
.sku__body {
margin-top: 20rpx;
display: flex;
}
.sku__left {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.sku__name {
font-size: 28rpx;
font-weight: 600;
line-height: 1.4;
color: var(--mm-fg);
}
.sku__rule {
font-size: 22rpx;
color: var(--mm-muted-fg);
}
.sku__know {
margin-top: 14rpx;
display: flex;
align-items: center;
gap: 4rpx;
font-size: 24rpx;
color: var(--mm-accent);
}
.sku__right {
flex: none;
width: 268rpx;
margin-left: 20rpx;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.sku__origin {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.sku__origin-num {
text-decoration: line-through;
}
.sku__price {
display: flex;
align-items: baseline;
margin-top: 10rpx;
}
.sku__price-label {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.sku__price-num {
font-size: 44rpx;
line-height: 1;
}
.sku__buy {
margin-top: 16rpx;
width: 160rpx;
height: 64rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 26rpx;
font-weight: 500;
box-shadow: 0 6rpx 16rpx rgba(0, 82, 255, 0.25);
transition: transform 0.15s ease;
}
.sku__buy:active {
transform: scale(0.94);
}
/* ---------- 产品介绍富文本 ---------- */
.intro {
margin-top: 16rpx;
font-size: 26rpx;
line-height: 1.7;
color: var(--mm-fg);
}
.intro /deep/ img {
max-width: 100% !important;
display: block;
border-radius: 12rpx;
}
/* ---------- 预订须知弹层 ---------- */
.pp {
padding: 0 48rpx 80rpx;
}
.pp__head {
height: 120rpx;
line-height: 120rpx;
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: var(--mm-fg);
border-bottom: 2rpx solid var(--mm-border);
}
.pp__sec {
padding-top: 40rpx;
}
.pp__row {
display: flex;
margin-top: 24rpx;
}
.pp__label {
flex: none;
width: 120rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.pp__val {
flex: 1;
margin-left: 48rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-fg);
}
.pp__parse {
margin-top: 24rpx;
font-size: 26rpx;
line-height: 1.7;
color: var(--mm-fg);
}
</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>
+172 -45
View File
@@ -1,27 +1,54 @@
<template>
<view>
<text class="text">需添加 &nbsp;{{value}}&nbsp;个出行人</text>
<view class="h_item" @click="toAddPerson">
<text class="text1">新增出行人</text>
<text class="text2">+</text>
</view>
<view class="item" v-for="(item,index) in list" :key="index">
<image src="https://aliyunoss.hn12301.net/sd/ticket_edit.png" style="width: 48rpx;height: 48rpx;" @click="toPerson(item)"></image>
<text style="margin-left: 24rpx;">{{item.name}}</text>
<text style="margin-left: 24rpx;">{{item.identityNo}}</text>
<view style="position: absolute;right: 0rpx;">
<u-checkbox v-model="item.isSelect" active-color="#457AF7"></u-checkbox>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 顶部提示徽章 —— 需添加的出行人数量 -->
<view class="tip mm-fade-up">
<view class="mm-badge">
<view class="mm-badge__dot"></view>
<text class="mm-badge__text">需添加 {{value}} 个出行人(已选 {{selectedCount}})</text>
</view>
</view>
<view style="width: 702rpx;position: fixed;left: 0rpx;right: 0rpx;margin: 0 auto;bottom: calc(24rpx + env(safe-area-inset-bottom));">
<u-button shape="circle" size="default" style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;" @click="select">完成</u-button>
</view>
<!-- xuchao: 新增出行人 —— 虚线描边引导卡 -->
<view class="add mm-fade-up" @click="toAddPerson">
<view class="add__icon mm-grad">
<u-icon name="plus" color="#ffffff" size="30"></u-icon>
</view>
<text class="add__text">新增出行人</text>
<u-icon name="arrow-right" color="#CBD5E1" size="28"></u-icon>
</view>
<!-- xuchao: 出行人列表 —— 整卡点击勾选,笔形图标进编辑 -->
<view class="list mm-stagger">
<view class="person mm-card mm-fade-up" v-for="(item,index) in list" :key="index" @click="toggle(item)">
<view class="person__avatar mm-grad">{{(item.name||'').charAt(0)}}</view>
<view class="person__info">
<view class="person__name">{{item.name}}</view>
<view class="person__id">{{item.identityNo}}</view>
</view>
<view class="person__edit" @click.stop="toPerson(item)">
<u-icon name="edit-pen" color="#64748B" size="36"></u-icon>
</view>
<view class="person__check" @click.stop>
<u-checkbox v-model="item.isSelect" active-color="#0052FF"></u-checkbox>
</view>
</view>
</view>
<!-- xuchao: 底部固定完成按钮(视口水平居中,宽屏下与页面列对齐) -->
<view class="bar">
<view class="mm-btn" @click="select">完成</view>
</view>
</view>
</template>
<script>
var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
checked:true,
@@ -31,16 +58,34 @@ export default {
},
onLoad(e){
that=this
this.value=e.value
this.value=Number(e.value)
},
onShow(){
this.getMemberTouristMethod()
},
computed: {
// xuchao: 已勾选的出行人数量
selectedCount() {
return this.list.filter(item => item.isSelect).length
}
},
methods: {
// 选中某个复选框时,由checkbox时触发
checkboxChange(e) {
console.log(e);
},
// xuchao: 整卡点击即勾选/取消;勾选数达到需求量 value 后再勾选则提示拦截
toggle(item) {
if (!item.isSelect && this.selectedCount >= this.value) {
uni.showToast({
icon: 'none',
title: '只需 ' + this.value + ' 位出行人',
duration: 1500
})
return
}
item.isSelect = !item.isSelect
},
toAddPerson(){
uni.navigateTo({
url: 'person'
@@ -58,7 +103,7 @@ export default {
this.$set(item,'isSelect',false)
})
this.list=res.data
}
})
},
@@ -72,7 +117,7 @@ export default {
let pages = getCurrentPages(); //获取所有页面栈实例列表
let nowPage = pages[pages.length - 1]; //当前页页面实例
let prevPage = pages[pages.length - 2]; //上一页页面实例
prevPage.objitem = JSON.stringify(arr); //修改上一页面的 couponNumber 参数值为 value
prevPage.objitem = JSON.stringify(arr); //修改上一页面的 couponNumber 参数值为 value
uni.navigateBack({ //uni.navigateTo跳转的返回,默认1为返回上一级
delta: 1
});
@@ -82,38 +127,120 @@ export default {
</script>
<style lang="less" scoped>
.text{
color: rgba(0, 0, 0, 0.8);
margin: 24rpx 0rpx 0rpx 24rpx;
font-size: 36rpx;
height: 88rpx;
font-weight: bold;
/* xuchao: 底部预留 220rpx,避免列表被固定完成按钮遮挡 */
.page {
padding: 32rpx 32rpx 220rpx;
}
.h_item{
width: 702rpx;
.tip {
display: flex;
}
.add {
margin-top: 24rpx;
display: flex;
margin-left: 24rpx;
justify-content: space-between;
border-bottom: 1px dashed #E1E1E1;
align-items: center;
padding-bottom: 24rpx;
.text1{
color:rgba(0, 0, 0, 0.8);
}
.text2{
color:#457AF7;
font-size: 40rpx;
font-weight: bold;
}
border: 2rpx dashed rgba(0, 82, 255, 0.4);
border-radius: 20rpx;
background: rgba(0, 82, 255, 0.04);
padding: 28rpx 24rpx;
transition: transform 0.15s ease;
}
.item{
width: 702rpx;
margin-left: 24rpx;
height: 112rpx;
.add:active {
transform: scale(0.985);
}
.add__icon {
flex: none;
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex;
align-items: center;
position: relative;
justify-content: center;
box-shadow: var(--mm-shadow-accent);
}
</style>
.add__text {
flex: 1;
margin-left: 20rpx;
font-size: 28rpx;
font-weight: 500;
color: var(--mm-accent);
}
.list {
margin-top: 24rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.person {
display: flex;
align-items: center;
padding: 28rpx 24rpx;
transition: transform 0.15s ease;
}
.person:active {
transform: scale(0.985);
}
.person__avatar {
flex: none;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 32rpx;
font-weight: 600;
box-shadow: var(--mm-shadow-accent);
}
.person__info {
flex: 1;
min-width: 0;
margin-left: 20rpx;
}
.person__name {
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
.person__id {
margin-top: 6rpx;
font-size: 22rpx;
letter-spacing: 0.04em;
color: var(--mm-muted-fg);
}
.person__edit {
flex: none;
padding: 0 24rpx 0 8rpx;
}
.person__check {
flex: none;
/* xuchao: 复选框仅作状态展示,勾选统一走整卡 toggle,数量上限才拦得住 */
pointer-events: none;
}
/* xuchao: 底部固定按钮 —— left/right 0 + margin auto 保证宽屏居中时与页面列对齐 */
.bar {
position: fixed;
left: 0;
right: 0;
margin: 0 auto;
width: 686rpx;
bottom: calc(32rpx + constant(safe-area-inset-bottom));
bottom: calc(32rpx + env(safe-area-inset-bottom));
z-index: 99;
}
</style>