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
+2
View File
@@ -1,2 +1,4 @@
/unpackage/ /unpackage/
.DS_Store .DS_Store
# xuchao: Playwright 验证产物(截图/控制台日志),不入库
.playwright-mcp/
+359 -3
View File
@@ -37,7 +37,362 @@
@import "uview-ui/index.scss"; @import "uview-ui/index.scss";
@import "./unit/font/index.css"; @import "./unit/font/index.css";
/* xuchao: Minimalist Modern 设计令牌 —— H5 全局 CSS 变量,页面样式统一用 var(--mm-*) 取值 */
page {
--mm-bg: #FAFAFA;
--mm-fg: #0F172A;
--mm-muted: #F1F5F9;
--mm-muted-fg: #64748B;
--mm-accent: #0052FF;
--mm-accent-2: #4D7CFF;
--mm-card: #FFFFFF;
--mm-border: #E2E8F0;
--mm-grad: linear-gradient(135deg, #0052FF, #4D7CFF);
--mm-grad-h: linear-gradient(to right, #0052FF, #4D7CFF);
--mm-shadow-sm: 0 2rpx 6rpx rgba(15, 23, 42, 0.06);
--mm-shadow-md: 0 8rpx 16rpx rgba(15, 23, 42, 0.07);
--mm-shadow-lg: 0 20rpx 30rpx rgba(15, 23, 42, 0.08);
--mm-shadow-accent: 0 8rpx 28rpx rgba(0, 82, 255, 0.25);
--mm-radius: 24rpx;
background: var(--mm-bg);
color: var(--mm-fg);
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC",
"HarmonyOS Sans SC", "Source Han Sans SC", "Microsoft YaHei", sans-serif;
}
/* xuchao: 页面根容器 —— 该 H5 运行时 uni-view 默认 inline,根视图必须显式 block,
否则内边距/最小高度不生效,子级会顶满屏幕 */
.mm-page {
display: block;
min-height: 100vh;
box-sizing: border-box;
}
/* xuchao: mm- 前缀全局工具类 —— 各页面共用,禁止页面内再写一次性渐变/阴影 */
/* 签名渐变背景 */
.mm-grad {
background: var(--mm-grad);
}
/* 渐变文字(标题关键词、价格数字) */
.mm-grad-text {
background: var(--mm-grad-h);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* 标准卡片:白底 + 描边 + 圆角 + 柔和投影 */
.mm-card {
background: var(--mm-card);
border: 2rpx solid var(--mm-border);
border-radius: var(--mm-radius);
box-shadow: var(--mm-shadow-md);
}
/* 主按钮:渐变底 + 白字 + 蓝色投影,按下有触感反馈 */
.mm-btn {
display: flex;
align-items: center;
justify-content: center;
height: 96rpx;
border-radius: var(--mm-radius);
background: var(--mm-grad);
color: #fff;
font-size: 30rpx;
font-weight: 500;
letter-spacing: 0.02em;
box-shadow: var(--mm-shadow-accent);
transition: all 0.2s ease-out;
}
.mm-btn:active {
transform: scale(0.98);
filter: brightness(1.1);
}
/* 次级按钮:描边幽灵态,用于"返回/取消"类操作 */
.mm-btn-ghost {
display: flex;
align-items: center;
justify-content: center;
height: 96rpx;
border-radius: var(--mm-radius);
border: 2rpx solid var(--mm-border);
background: transparent;
color: var(--mm-fg);
font-size: 30rpx;
transition: all 0.2s ease-out;
}
.mm-btn-ghost:active {
background: var(--mm-muted);
}
/* 区块标签徽章:小圆点 + 宽字距文字(设计系统的 section label 形态) */
.mm-badge {
display: inline-flex;
align-items: center;
border-radius: 999rpx;
border: 2rpx solid rgba(0, 82, 255, 0.3);
background: rgba(0, 82, 255, 0.05);
padding: 10rpx 24rpx;
}
.mm-badge__dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: var(--mm-accent);
margin-right: 12rpx;
animation: mm-pulse 2s ease-in-out infinite;
}
.mm-badge__text {
font-size: 22rpx;
letter-spacing: 0.15em;
color: var(--mm-accent);
}
/* 区块标题:左侧签名渐变竖条 + 标题文字,各页内容分区统一用它 */
.mm-sec-title {
display: flex;
align-items: center;
font-size: 32rpx;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--mm-fg);
}
.mm-sec-title::before {
content: '';
width: 8rpx;
height: 32rpx;
border-radius: 4rpx;
background: var(--mm-grad);
margin-right: 16rpx;
}
/* xuchao: 输入占位符浅灰(设计令牌)—— 配合 placeholder-class="mm-ph" 使用 */
.mm-ph {
color: #CBD5E1;
}
.mm-ph::placeholder {
color: #CBD5E1;
opacity: 1;
}
/* 展示字体(本地 YouShe 优设标题黑),大标题与价格数字用 */
.mm-display {
font-family: "YouShe", "PingFang SC", sans-serif;
font-weight: 400;
}
/* 入场动效:上移渐显;配合 .mm-stagger 给子元素逐个延迟 */
.mm-fade-up {
animation: mm-fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.mm-stagger>*:nth-child(1) { animation-delay: 0.05s; }
.mm-stagger>*:nth-child(2) { animation-delay: 0.12s; }
.mm-stagger>*:nth-child(3) { animation-delay: 0.19s; }
.mm-stagger>*:nth-child(4) { animation-delay: 0.26s; }
.mm-stagger>*:nth-child(5) { animation-delay: 0.33s; }
.mm-stagger>*:nth-child(6) { animation-delay: 0.4s; }
.mm-stagger>*:nth-child(7) { animation-delay: 0.47s; }
.mm-stagger>*:nth-child(n+8) { animation-delay: 0.54s; }
@keyframes mm-fade-up {
from {
opacity: 0;
transform: translateY(40rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 脉动圆点:徽章"进行中"指示 */
@keyframes mm-pulse {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.3);
opacity: 0.7;
}
}
/* 底部安全区(iPhone X 系列全面屏) */
.mm-safe-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
/* 用户偏好减少动效时,关闭所有装饰性动画 */
@media (prefers-reduced-motion: reduce) {
.mm-fade-up,
.mm-badge__dot {
animation: none !important;
}
.mm-btn,
.mm-btn-ghost {
transition: none !important;
}
}
/* #ifdef H5 */ /* #ifdef H5 */
// xuchao: 该 H5 运行时 uni-view 默认 inline(标准 uni-app 为 block),
// 无 display 声明的样式容器会退化为行内:内边距/外边距/高/文本对齐全部失效。
// 全局恢复 block,与标准 uni-app H5 行为一致。
uni-view,
uni-scroll-view {
display: block;
}
// xuchao: 该 H5 运行时还缺 scroll-view 内置滚动样式 —— 内层 .uni-scroll-view 无 overflow
// 声明且高度被内容撑开,所有 scroll-view 都不能滚(弹窗里滑动会穿透去滚背后页面)。
// 补齐:外层裁切、内层承担滚动;overscroll-behavior 防止滚到边缘后链式滚动背景页。
uni-scroll-view {
overflow: hidden;
}
uni-scroll-view .uni-scroll-view {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
// xuchao: 横向 scroll-view(带 scroll-x 属性)改为横向滚动、内容不换行
uni-scroll-view[scroll-x] .uni-scroll-view {
height: auto;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
}
// xuchao: 该 H5 运行时缺 uni-swiper 内置样式 —— 宿主元素 inline,组件内联写入的
// height 不生效,整个轮播塌成 0(详情页头图不显示)。按标准 uni-app H5 补齐:
// 宿主 block 定高裁切,wrapper 铺满,slides 绝对定位(其 inset:0 为内联样式)。
uni-swiper {
display: block;
position: relative;
width: 100%;
height: 150px;
overflow: hidden;
}
uni-swiper .uni-swiper-wrapper {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
uni-swiper .uni-swiper-slides {
position: absolute;
}
// xuchao: 该 H5 运行时缺 uni-toast 内置样式 —— 提示以文档流内联渲染(透明底黑字、
// 掉到页面底部视口外),等同不可见。恢复标准 uni-app H5 形态:全屏 fixed 居中 + 深色胶囊。
uni-toast {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
pointer-events: none;
}
.uni-sample-toast {
max-width: 70%;
padding: 20rpx 36rpx;
border-radius: 16rpx;
background: rgba(15, 23, 42, 0.82);
color: #fff;
font-size: 28rpx;
line-height: 1.5;
text-align: center;
word-break: break-all;
}
// xuchao: 该 H5 运行时缺少 uni-app 内置的 input 基础样式(输入框退化为浏览器默认带框小输入条),
// 按标准 uni-app H5 补齐:wrapper 撑满、真实 input 绝对定位铺满去边框、占位符绝对定位叠在上方。
uni-input,
uni-textarea {
display: block;
position: relative;
}
.uni-input-wrapper,
.uni-textarea-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.uni-input-input,
.uni-textarea-input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: none;
outline: none;
background: transparent;
border-radius: 0;
font: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
}
.uni-input-placeholder,
.uni-textarea-placeholder {
position: absolute;
left: 0;
top: 0;
right: 0;
pointer-events: none;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* xuchao: 占位符统一浅灰(设计令牌) */
color: #CBD5E1;
}
// xuchao: 模板里的原生 input/textarea 同样剥掉浏览器默认边框
input,
textarea {
border: none;
outline: none;
background: transparent;
border-radius: 0;
font: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
}
// xuchao: 修复 uni-image 内部填充元素宽高为 0 导致图片不显示/溢出 // xuchao: 修复 uni-image 内部填充元素宽高为 0 导致图片不显示/溢出
uni-image { uni-image {
display: inline-block; display: inline-block;
@@ -60,8 +415,9 @@
// xuchao: 宽屏(>768px,框架此时把 rpx 基准锁在 375)下,把页面列居中、两侧露出底色 // xuchao: 宽屏(>768px,框架此时把 rpx 基准锁在 375)下,把页面列居中、两侧露出底色
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {
body { body {
// xuchao: 页面级 page 背景会染到 body,需 !important 才能露出两侧底色 // xuchao: 页面级 page 背景会染到 body,需 !important 才能露出两侧底色;
background: #dfe4ea !important; // 底色取设计系统描边色,与 #FAFAFA 页面形成柔和层次
background: #E2E8F0 !important;
} }
uni-page-body { uni-page-body {
@@ -74,7 +430,7 @@
margin: 0 auto !important; margin: 0 auto !important;
min-height: 100vh; min-height: 100vh;
overflow-x: hidden; overflow-x: hidden;
box-shadow: 0 0 24px rgba(31, 45, 61, 0.14); box-shadow: 0 0 24px rgba(15, 23, 42, 0.14);
} }
} }
/* #endif */ /* #endif */
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,57 @@
<template name="gmy-back-touch">
<!-- xuchao: 返回悬浮钮 —— 交互效果与首页订单钮一致(外层定位盒响应点击、按压缩放),位置上移 100px -->
<view class="gbt-box" @click="onTap">
<image class="gbt-btn" src="/static/back.png"></image>
</view>
</template>
<script>
export default {
name: "gmy-back-touch",
methods: {
// xuchao: 返回上一页;H5 下 navigateBack 实为 history.back,
// 新标签直开(应用内无栈且无浏览历史)时它不触发回调,需直接兜底回首页
onTap: function() {
if (getCurrentPages().length <= 1 && history.length <= 1) {
uni.reLaunch({
url: '/pages/ticket/scenic'
})
return
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.reLaunch({
url: '/pages/ticket/scenic'
})
}
})
}
}
}
</script>
<style lang="scss">
/* xuchao: 类名加 gbt- 前缀,组件样式全局生效,避免裸类名撞名 */
.gbt-box {
position: fixed;
left: 24rpx;
/* xuchao: 在首页订单钮位置基础上向上移动 50px */
bottom: calc(100rpx + 50px);
z-index: 999;
}
/* xuchao: 与 gft-btn 同款尺寸、蓝色投影、按压缩放反馈 */
.gbt-btn {
width: 104rpx;
height: 104rpx;
border-radius: 50%;
display: block;
box-shadow: 0 12rpx 28rpx rgba(0, 82, 255, 0.35);
transition: transform 0.12s ease;
}
.gbt-box:active .gbt-btn {
transform: scale(0.9);
}
</style>
+19 -23
View File
@@ -1,21 +1,17 @@
<template name="gmy-float-touch"> <template name="gmy-float-touch">
<!-- xuchao: 门票订单悬浮按钮 —— 固定距屏幕底部 50px、左侧 12px,单击直达订单列表 --> <!-- xuchao: 门票订单悬浮按钮 —— 渐变圆钮固定于左下,单击直达订单列表 -->
<view class="box" @click="onTap"> <view class="gft-box" @click="onTap">
<image class="box__img" :src="mainImg" mode="aspectFill"></image> <view class="gft-btn mm-grad">
<u-icon name="order" color="#ffffff" size="44"></u-icon>
</view>
</view> </view>
</template> </template>
<script> <script>
export default { export default {
name: "gmy-float-touch", name: "gmy-float-touch",
props: {
mainImg: {
type: String,
default: "../../static/main.png"
}
},
methods: { methods: {
// xuchao: 单击直接派发订单事件 // xuchao: 单击直接派发订单事件(保留原契约:menuClick 回调参数 4)
onTap: function() { onTap: function() {
this.$emit("menuClick", 4) this.$emit("menuClick", 4)
} }
@@ -24,27 +20,27 @@
</script> </script>
<style lang="scss"> <style lang="scss">
.box { /* xuchao: 类名加 gft- 前缀,组件样式全局生效,避免裸 .box 撞名 */
.gft-box {
position: fixed; position: fixed;
left: 12px; left: 24rpx;
bottom: 50px; bottom: 100rpx;
width: 100upx;
height: 100upx;
z-index: 999; z-index: 999;
}
.gft-btn {
width: 104rpx;
height: 104rpx;
border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} /* xuchao: 蓝色投影呼应签名渐变,强化"悬浮"层次 */
box-shadow: 0 12rpx 28rpx rgba(0, 82, 255, 0.35);
.box__img {
width: 100upx;
height: 100upx;
transition: transform 0.12s ease; transition: transform 0.12s ease;
// xuchao: 轻微投影 + 按下回弹,让悬浮钮有"浮起/可点"的反馈
filter: drop-shadow(0 6rpx 12rpx rgba(0, 0, 0, 0.18));
} }
.box:active .box__img { .gft-box:active .gft-btn {
transform: scale(0.9); transform: scale(0.9);
} }
</style> </style>
+110 -69
View File
@@ -1,27 +1,38 @@
<template> <template>
<view class="auth_rq"> <view class="page mm-page">
<image class="auth_rq_image" src="https://aliyunoss.hn12301.net/sd/ali_logo.png"></image> <gmy-back-touch></gmy-back-touch>
<view class="auth_rq_view">质惠游</view> <!-- 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: 开发模式提示(正式环境不显示) --> <!-- 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'}"> <!-- xuchao: 登录表单 -->
<u-input v-model="value" type="number" :border="false" :custom-style='customStyle' placeholder='请输入手机号' /> <view class="form mm-fade-up" :style="{marginTop: devMode ? '40rpx' : '96rpx'}">
</view> <view class="field">
<!-- xuchao: 开发模式免验证码 --> <u-input v-model="value" type="number" :border="false" :custom-style='customStyle'
<view class="auth_rq_input" v-if="!devMode" style="margin-top: 50rpx;"> placeholder='请输入手机号' placeholder-class="mm-ph" />
<u-input v-model="value1" type="number" :border="false" :custom-style='customStyle' placeholder='请输入验证码' /> </view>
<text style="margin-left: 30rpx;" :style="{color:isFlag?'#488CFE':'#666'}" @tap="getCode">{{tips}}</text> <!-- xuchao: 开发模式免验证码 -->
</view> <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;"> <view class="agree">
<u-checkbox-group> <u-checkbox-group>
<u-checkbox v-model="checked" shape="circle" style="color: #929292;">我已阅读并同意<text <u-checkbox v-model="checked" shape="circle" active-color="#0052FF">我已阅读并同意<text
style="color: #488CFE;" @click="readXy()">用户服务协议</text></u-checkbox> class="agree__link" @click="readXy()">用户服务协议</text></u-checkbox>
</u-checkbox-group> </u-checkbox-group>
</view>
</view> </view>
<u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode" <u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode"
@@ -35,8 +46,12 @@
// 正式流程:微信网页授权(code 换用户信息)+ 短信验证码绑定手机号 // 正式流程:微信网页授权(code 换用户信息)+ 短信验证码绑定手机号
import config from '@/common/config.js' import config from '@/common/config.js'
import auth from '@/common/auth.js' import auth from '@/common/auth.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
devMode: config.devMode, devMode: config.devMode,
@@ -47,8 +62,9 @@
value: '', value: '',
value1: '', value1: '',
isFlag: true, isFlag: true,
// xuchao: 输入框背景由外层 .field 提供,组件内部保持透明
customStyle: { customStyle: {
background: '#f8f8f8', background: 'transparent',
} }
} }
}, },
@@ -158,60 +174,85 @@
} }
} }
</script> </script>
<style>
page {
background: #ffffff;
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.auth_rq { .page {
.auth_rq_input { padding: 0 70rpx;
display: flex; }
width: 610rpx;
height: 98rpx;
align-items: center;
padding-left: 40rpx;
margin-left: 70rpx;
background-color: #F8F8F8;
border-radius: 8px;
padding-right: 40rpx;
}
.auth_rq_image { /* ---------- 品牌区 ---------- */
width: 164rpx; .brand {
height: 140rpx; display: flex;
margin-top: 182rpx; flex-direction: column;
margin-left: 294rpx; align-items: center;
} margin-top: 140rpx;
}
.auth_rq_view { .brand__logo {
width: 750rpx; width: 164rpx;
text-align: center; height: 140rpx;
color: #0DD381; }
font-size: 36rpx;
margin-top: 18rpx;
}
// xuchao: 开发模式提示条 .brand__name {
.auth_rq_tips { margin-top: 20rpx;
width: 610rpx; font-size: 52rpx;
margin: 40rpx 0 0 70rpx; letter-spacing: 0.12em;
padding: 16rpx 24rpx; }
box-sizing: border-box;
background-color: #FDF6EC;
color: #E6A23C;
font-size: 24rpx;
border-radius: 8px;
}
.auth_rq_button1 { /* ---------- 开发模式提示条 ---------- */
width: 610rpx; .tips {
margin-left: 70rpx; margin-top: 48rpx;
margin-top: 80rpx; padding: 16rpx 24rpx;
height: 98rpx; background: rgba(0, 82, 255, 0.05);
border-radius: 49rpx; border: 2rpx solid rgba(0, 82, 255, 0.15);
background-color: #488CFE; color: var(--mm-accent);
color: #fff; 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> </style>
+427 -256
View File
@@ -1,192 +1,150 @@
<template> <template>
<view v-if="obj"> <view v-if="obj" class="page mm-page">
<view class="detail_h"> <gmy-back-touch></gmy-back-touch>
<view style="width: 654rpx;display: flex;justify-content: space-between;"> <!-- xuchao: 状态英雄卡 —— 待支付用主题蓝强调 -->
<text style="font-size: 48rpx;font-weight: bold;" <view class="hero mm-card mm-fade-up">
:style="{color:obj.order.orderStatus=='WAITPAY'?'#FF0000':'#000'}">{{orderStatus}}</text> <view class="hero__row">
<text :class="['hero__status mm-display', isWaitPay ? 'hero__status--accent' : '']">{{orderStatus}}</text>
<view> <view class="hero__price">
<text style="color: #000000;font-size: 20rpx;">¥</text> <text class="hero__cur">¥</text>
<text <text class="hero__num mm-display">{{(obj.order.orderAmount/100).toFixed(2)}}</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> </view>
</view> </view>
<text style="color:#808080;margin-top: 24rpx;display: block;">{{orderTitle}}</text> <view class="hero__tip">{{orderTitle}}</view>
</view> </view>
<view class="detail_rq">
<text style="color: #000;line-height: 88rpx;font-size: 36rpx;font-weight: bold;">商品信息</text> <!-- xuchao: 商品信息卡 -->
<view style="width: 702rpx;display: flex;justify-content: space-between;"> <view class="sec mm-card mm-fade-up">
<text style="color: #000;font-weight: bold;">{{ticketName}}</text> <view class="mm-sec-title">商品信息</view>
<u-icon name="arrow-right" color="#808080" size="24"></u-icon> <view class="goods">
<text class="goods__name">{{ticketName}}</text>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="kv">
<view style="width: 120rpx;color: #808080;">票型类型</view> <view class="kv__label">票型类型</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;"> <view class="kv__val">【{{getSkuType(obj.sku.typeId)}}】 {{obj.sku.skuName}}</view>
【{{getSkuType(obj.sku.typeId)}}】&nbsp;{{obj.sku.skuName}}</view>
</view> </view>
<view class="kv">
<view style="display: flex;margin-top: 24rpx;"> <view class="kv__label">使用日期</view>
<view style="width: 120rpx;color: #808080;">使用日期</view> <view class="kv__val kv__val--accent">{{obj.orderGoods.bookDate}} <text
<view style="width: 566rpx;margin-left: 16rpx;color: #FF0000;">{{obj.orderGoods.bookDate}} <text
v-if="obj.orderGoods.timeSlot">({{obj.orderGoods.timeSlot}})</text></view> v-if="obj.orderGoods.timeSlot">({{obj.orderGoods.timeSlot}})</text></view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="kv">
<view style="width: 120rpx;color: #808080;">生效时间</view> <view class="kv__label">生效时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">出票后立即生效</view> <view class="kv__val">出票后立即生效</view>
</view> </view>
<view class="rypz" v-if="obj.touristList.length!=0">
<view style="width: 654rpx;text-align: center;display: flex;flex-direction: column;"> <!-- xuchao: 入园凭证(横向滑动,每位游客一张凭证卡) -->
<text style="color: #000000;font-size: 32rpx;font-weight: bold;">入园凭证</text> <view class="vch" v-if="obj.touristList.length!=0">
<scroll-view scroll-x="true" class="xsms-scroll"> <view class="vch__title">入园凭证</view>
<view style="width: 404rpx;display: inline-block;" <scroll-view scroll-x="true" class="vch__scroll">
:style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}" <view class="vch__item" :style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}"
v-for="(item,index) in obj.touristList" :key="index"> v-for="(item,index) in obj.touristList" :key="index">
<view class="rq_item">凭证码</view> <view class="vch__tag">凭证码</view>
<view <view class="vch__body">
style="width: 404rpx;background: #F8F9FC;display: flex;flex-direction: column;align-items: center;padding-bottom: 24rpx0;border-radius: 0rpx 0rpx 24rpx 24rpx;"> <view class="vch__num">{{item.qrCode}}</view>
<view class="rq_num">{{item.qrCode}}</view> <view class="vch__copy" @tap="coyp(item.qrCode)">复制</view>
<view class="rq_btn" @tap='coyp(item.qrCode)'>复制</view> <view class="vch__qr">
<image style="width: 240rpx;height: 240rpx;margin-top: 24rpx;" :src="item.qrCodeBase64"> <image :src="item.qrCodeBase64"></image>
</image>
</view> </view>
</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> </scroll-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>
</view> </view>
<view style="display: flex;margin-top: 24rpx;position: relative;" v-if="obj.order.orderStatus!='WAITPAY'"> <view class="kv">
<view style="width: 120rpx;color: #808080;">支付时间</view> <view class="kv__label">入园方式</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;" v-if="obj.payment"> <view class="kv__val">{{obj.sku.enterChangeFlag?'凭('+getTyepStr(obj.sku.enterType)+')换票入园':'无需换票'}}</view>
{{obj.payment.createdTime||''}} </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> </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>
<!-- xuchao: 订单信息卡 -->
<view class="toqx" v-if="obj.order.orderStatus=='HAVESEND'" @tap='refund()'>申请退款</view> <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> </view>
<u-popup v-model="show1" border-radius='20' mode='bottom'> <!-- xuchao: 底部按钮区预留(有操作按钮时才占位) -->
<view style="padding:48rpx;display: flex;flex-direction: column;"> <view class="safe" v-if="hasActs"></view>
<text style="color: #000;font-size: 36rpx;font-weight: bold;">取消政策</text>
<!-- 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 <!-- xuchao: 取消政策弹层 -->
style="height: 88rpx;display: flex;align-items: center;position: relative;border-top: 1px dashed #E1E1E1;margin-top: 24rpx;"> <u-popup v-model="show1" border-radius="20" mode="bottom">
<image src="https://aliyunoss.hn12301.net/sd/ticket_dg.png" <view class="pp mm-safe-bottom">
style="width: 40rpx;height: 40rpx;margin-right: 12rpx;"></image> <view class="mm-sec-title">取消政策</view>
<text style="color: rgba(0, 0, 0, 0.8);">随时退</text> <u-parse class="pp__parse" :html="obj.sku.refundPolicy"></u-parse>
</view> <view class="mm-btn pp__btn" @click="show1=false">确定</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>
</view> </view>
</u-popup> </u-popup>
<u-popup v-model="show" mode='bottom' border-radius='20'> <!-- xuchao: 申请退款 —— 选择游客弹层 -->
<view style="padding: 48rpx;"> <u-popup v-model="show" mode="bottom" border-radius="20">
<view style="width: 654rpx;text-align: center;font-size: 30rpx;font-weight: bold;">请选择游客</view> <view class="pp mm-safe-bottom">
<u-checkbox-group :wrap='true'> <view class="pp__center-title">请选择游客</view>
<u-checkbox v-model="item.checked" v-for="(item, index) in list" :key="index" <u-checkbox-group :wrap="true">
:name="item.name">{{item.name}}</u-checkbox> <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-checkbox-group>
<view class="mm-btn pp__btn" @click="sure()">确定</view>
<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> </view>
</u-popup> </u-popup>
</view> </view>
@@ -200,7 +158,11 @@
import { import {
getSkuType getSkuType
} from '../../unit/utils.js' } from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
enterTypeList: [{ enterTypeList: [{
@@ -228,7 +190,7 @@
label: '6' label: '6'
}, },
{ {
value: "台湾居民来往大陆通行证", value: "台湾居民来往内地通行证",
label: '7' label: '7'
} }
], ],
@@ -294,6 +256,16 @@
} }
return str 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) { onLoad(e) {
@@ -410,114 +382,313 @@
} }
} }
</script> </script>
<style>
page {
background: #F8F9FC;
}
</style>
<style scoped lang="less"> <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; overflow: hidden;
white-space: nowrap; white-space: nowrap;
margin-top: 24rpx; margin-top: 24rpx;
width: 654rpx; width: 100%;
} }
.rypz { .vch__item {
width: 702rpx; width: 404rpx;
border: 1px dashed #E1E1E1; 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; margin-top: 24rpx;
padding: 24rpx; font-size: 28rpx;
font-weight: 600;
.rq_item { letter-spacing: 0.04em;
width: 404rpx; text-align: center;
height: 64rpx; white-space: normal;
border-radius: 20rpx 20rpx 0rpx 0rpx; overflow-wrap: break-word;
background: #EEEFF3; color: var(--mm-fg);
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;
}
} }
.tozf { .vch__copy {
height: 64rpx; margin-top: 16rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%); width: 88rpx;
border-radius: 64rpx; height: 44rpx;
color: #fff;
font-weight: bold;
padding: 0rpx 24rpx;
display: flex; display: flex;
align-items: center; 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 { .vch__qr {
height: 64rpx; margin-top: 24rpx;
background: rgba(0, 0, 0, 0.1); padding: 12rpx;
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;
background: #fff; background: #fff;
border-radius: 20rpx 20rpx 0rpx 0rpx; border-radius: 12rpx;
padding: 24rpx;
width: 702rpx;
} }
.detail_rq { .vch__qr image {
border-top: 1px #E1E1E1 solid; width: 240rpx;
background-color: #fff; height: 240rpx;
padding: 24rpx 24rpx 0rpx 24rpx; display: block;
}
/* ---------- 取消政策入口 ---------- */
.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> </style>
+157 -33
View File
@@ -1,25 +1,52 @@
<template> <template>
<view> <view class="page mm-page">
<view class="item" v-for="(item,index) in lookList" :key="index" @tap='toOrderDetail(item.id)'> <gmy-back-touch></gmy-back-touch>
<view class="item_h"> <!-- xuchao: 页头徽章(自定义导航无标题栏,补一个页面标识) -->
<text>订单编号:{{item.id}}</text> <view class="head mm-fade-up">
<text>{{getText(item.orderStatus,1)}}</text> <view class="mm-badge">
<view class="mm-badge__dot"></view>
<text class="mm-badge__text">门票订单</text>
</view> </view>
<view class="item_rq"> </view>
<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;"> <!-- xuchao: 订单卡片列表 —— 整卡可点进详情 -->
<text>使用日期:{{item.orderDate}}</text> <view class="list mm-stagger">
<text>¥{{(item.orderAmount/100).toFixed(2)}}</text> <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> </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> </view>
</template> </template>
<script> <script>
var that='' var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default{ export default{
components: {
'gmy-back-touch': gmyBackTouch
},
data(){ data(){
return{ return{
status: 'loadmore', status: 'loadmore',
@@ -33,6 +60,8 @@
pageAll:2, pageAll:2,
isLoad:true, isLoad:true,
lookList:[], lookList:[],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
orderList:[{ orderList:[{
text:'待支付', text:'待支付',
value:'WAITPAY', value:'WAITPAY',
@@ -64,6 +93,11 @@
}] }]
} }
}, },
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad(){ onLoad(){
that=this that=this
this.obj.orderPhone=uni.getStorageSync('phone') this.obj.orderPhone=uni.getStorageSync('phone')
@@ -93,11 +127,19 @@
return str return str
} }
}, },
// xuchao: 订单状态 → 徽章色调(待办类用主题蓝强调,终态用中性灰)
statusClass(e) {
if (['WAITPAY', 'WAITSEND', 'REFUNDING'].indexOf(e) > -1) {
return 'order__status--active'
}
return ''
},
queryTicketsOrderPageMethod() { queryTicketsOrderPageMethod() {
if (that.obj.pageNum <= that.pageAll&&this.isLoad) { if (that.obj.pageNum <= that.pageAll&&this.isLoad) {
this.isLoad=false this.isLoad=false
this.$u.api.queryTicketsOrderPage(this.obj).then(res => { this.$u.api.queryTicketsOrderPage(this.obj).then(res => {
this.isLoad=true this.isLoad=true
this.firstLoaded = true
that.obj.pageNum = that.obj.pageNum * 1 + 1; that.obj.pageNum = that.obj.pageNum * 1 + 1;
that.pageAll = Math.ceil(res.data.total / 10) that.pageAll = Math.ceil(res.data.total / 10)
if (that.page == 2) { if (that.page == 2) {
@@ -120,29 +162,111 @@
} }
</script> </script>
<style>
page{
background: #F8F9FC;
padding-top: 24rpx;
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.item{ .page {
width: 750rpx; padding: 32rpx 32rpx 40rpx;
margin-bottom: 20rpx; }
.head {
display: flex;
margin-bottom: 32rpx;
}
.list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.order {
padding: 24rpx; padding: 24rpx;
background: #fff; transition: transform 0.2s ease-out, box-shadow 0.3s ease-out;
.item_h{ }
width: 702rpx;
display: flex; .order:active {
justify-content: space-between; transform: scale(0.985);
color: rgba(0, 0, 0, 0.5); box-shadow: var(--mm-shadow-sm);
} }
.item_rq{
width: 654rpx; .order__head {
margin: 24rpx 24rpx 0rpx 24rpx; display: flex;
background: #F8F9FC; align-items: center;
padding: 24rpx; 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>
+558 -306
View File
@@ -1,151 +1,137 @@
<template> <template>
<view v-if="skuObj"> <view v-if="skuObj" class="page mm-page">
<!-- <view style="position: fixed;top: 0px;width: 750rpx;z-index: 1000;" :style="{height:windowHeight+'px'}"> <gmy-back-touch></gmy-back-touch>
<view style="width: inherit;" :style="{height:windowHeight-62+'px'}"></view> --> <!-- xuchao: 商品与日期/时段/数量选择卡 -->
<view class="db_rq" :style="{top:windowHeight-62+'px'}"> <view class="sec mm-card mm-fade-up">
<view> <view class="sku__name">{{skuObj.sku.skuName}}</view>
<text style="color: rgba(0, 0, 0, 0.5);font-size: 20rpx;">总价</text> <view class="sku__age">适用年龄:{{skuObj.sku.typeInfo}}</view>
<view style="display: flex;align-items: flex-end;gap: 24rpx;" @tap='show1=true'>
<text style="color: #FF0000;font-size: 48rpx;font-weight: bold;">¥{{salesPriceSum}}</text>
<view style="margin-bottom: 10rpx;display: flex;align-items: center;gap: 10rpx;color: #457AF7;"> <view class="divider"></view>
明细<u-icon name="arrow-down-fill" size='20'></u-icon></view>
</view>
</view>
<view class="bt" @tap="buy">立即购买</view> <view class="label">使用日期</view>
</view> <view class="dates">
<!-- </view> -->
<view style="padding: 24rpx;background-color: #fff;">
<view style="color: #000;font-size: 32rpx;font-weight: bold;line-height: normal;">
{{skuObj.sku.skuName}}
</view>
<text style="color: rgba(0, 0, 0, 0.5);font-size: 20rpx;">适用年龄:{{skuObj.sku.typeInfo}}</text>
<view style="margin-top: 24rpx;border-top: 1px dashed rgba(0, 0, 0, 0.1);"></view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.5);font-size: 500;position: relative;">使用日期
</view>
<view style="width: 702rpx;display: flex;gap: 8rpx;margin-top: 12rpx;">
<view v-for="(item,index) in cList" :key="index" v-if="index<3" <view v-for="(item,index) in cList" :key="index" v-if="index<3"
:class="item.isSelect?'mt_rq':item.isEnabled?'jt_rq':'ht_rq'" @click="selectDate(item)"> :class="['date', item.isSelect?'date--on':'', (!item.isEnabled&&!item.isSelect)?'date--off':'']"
@click="selectDate(item)">
{{getDayName(item.enableDate)}}<br /> {{getDayName(item.enableDate)}}<br />
{{formatToMonthDay(item.enableDate)}} {{formatToMonthDay(item.enableDate)}}
<view class="jt_tag">{{item.isSelect?'√':item.isEnabled?'可订':'不可订'}}</view> <view class="date__tag">{{item.isSelect?'√':item.isEnabled?'可订':'不可订'}}</view>
</view> </view>
<view class="date date--more" @tap="show=true">更多<br />日期
<view class="gd_rq" @tap="show=true">更多<br /> <u-icon name="arrow-right" color="#64748B" size="22"></u-icon>
日期
<u-icon name="arrow-right" style="margin-left: 5rpx;"></u-icon>
</view> </view>
</view> </view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.5);font-size: 500;" v-if="!skuObj.sku.stockDateType"> <template v-if="!skuObj.sku.stockDateType">
适用时段</view> <view class="label">适用时段</view>
<view style="width: 702rpx;display: flex;gap: 12rpx;margin-top: 12rpx;flex-wrap: wrap;" <view class="slots">
v-if="!skuObj.sku.stockDateType"> <view :class="['slot', item.isSelect?'slot--on':'']" v-for="(item,index) in timeSlotList"
<view :class="item.isSelect?'xz_item':'nx_item' " v-for="(item,index) in timeSlotList" :key="index" :key="index" @click="selectSlot(index)">{{item.name}}
@click="selectSlot(index)">{{item.name}} <view class="slot__tag">余{{item.stock}}张</view>
<view class="xz_tag">余{{item.stock}}张</view>
</view>
</view>
<view style="margin-top: 24rpx;border-top: 1px dashed rgba(0, 0, 0, 0.1);"></view>
<view style="display: flex;align-items: center;margin-top: 24rpx;height: 96rpx;">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">购买数量</text>
<u-number-box v-model="value" :max='getMaxStock()' :min='1' @change="valChange"
style="position: absolute;right: 24rpx;"></u-number-box>
</view>
</view>
<view style="padding: 24rpx;background-color: #fff;margin-top: 20rpx;" v-if="skuObj.sku.enterDemand=='2'">
<view style="display: flex;margin-top: 24rpx;align-items: flex-end;">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">游客信息</text>
<view style="margin-left: 24rpx;color: rgba(0, 0, 0, 0.5);font-size: 20rpx;">
需要 <text style="color: #000;font-size: 24rpx;font-weight: bold;">{{value}}位</text> 出行游客信息,用于入园身份验证
</view>
</view>
<view style="margin-top: 24rpx;border-top: 1px dashed rgba(0, 0, 0, 0.1);"></view>
<view style="display: flex;gap: 24rpx;margin-top: 24rpx;">
<view class="xz_name" v-for="(item,index) in personList" :key="index">{{item.name}}</view>
<view class="nx_name" @tap='addPerson'>新增/更换 ></view>
</view>
<view v-for="(item,index) in personList" :key="index"
style="display: flex;width: 702rpx;color: #000;padding: 24rpx 0rpx;position:relative;border-bottom: 1px dashed rgba(0, 0, 0, 0.1);align-items: center;">
<u-icon name="close" style="color: rgba(0, 0, 0, 0.5);" @click="deletePerson(index)"></u-icon>
<text style="margin-left: 24rpx;">游客{{index+1}}</text>
<input style="margin-left: 104rpx;font-size: 28rpx;" v-model="item.name" :disabled="true" />
<input style="position: absolute;right: 0rpx;font-size: 28rpx;" v-model="item.identityNo"
:disabled="true" />
<!-- <text style="color: #0066F6;position: absolute;right: 0rpx;">编辑</text> -->
</view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.8);">
联系手机
<text style="color: #000;margin-left: 95rpx;">{{createInfo.mobile}}</text>
</view>
</view>
<view style="padding: 24rpx;background-color: #fff;margin-top: 20rpx;" v-else>
<view style="display: flex;margin-top: 24rpx;align-items: flex-end;">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">联系人信息</text>
</view>
<view style="margin-top: 24rpx;border-top: 1px dashed rgba(0, 0, 0, 0.1);"></view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.8);display: flex;align-items: center;">
联系人
<input :adjust-position="false" type="text" v-model="createInfo.contactName"
style="color: #000;margin-left: 95rpx;" placeholder="请输入联系人姓名" />
</view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.8);">
手机号
<text style="color: #000;margin-left: 95rpx;">{{createInfo.mobile}}</text>
</view>
</view>
<u-calendar v-model="show" :mode="mode" activeBgColor='#F4612C' :calendarList='calendarList'
@change="change"></u-calendar>
<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>
<text style="color: #000;font-size: 32rpx;font-weight: bold;margin-top: 18rpx;">景区/场馆</text>
<view
style="height: 88rpx;display: flex;align-items: center;position: relative;border-bottom: 1px dashed #E1E1E1;">
<text style="color: rgba(0, 0, 0, 0.5);">门票</text>
<text style="color: #000;position: absolute;right: 0rpx;">¥{{salesPrice}}
&nbsp;&nbsp;x{{value}}</text>
</view>
<view style="position: relative;margin-top: 24rpx;">
<view style="color: rgba(0, 0, 0, 0.5);font-size: 20rpx;position: absolute;right: 0rpx;">总价<text
style="color: #FF0000;">¥</text><text
style="color: #FF0000;font-size: 32rpx;">{{salesPriceSum}}</text>
</view> </view>
</view> </view>
<view style="width: 654rpx;height: 120rpx;"></view> </template>
<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;"> <view class="divider"></view>
<u-button shape="circle" size="default"
style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;" <!-- xuchao: 购买数量 —— 自定义步进器:数字居中,加减号点击即加减,上限取当前日期/时段库存 -->
@click="show1=false">确定</u-button> <view class="qty">
<text class="qty__label">购买数量</text>
<view class="stepper">
<view :class="['stepper__btn', value<=1?'stepper__btn--off':'']" @click="minusNum">
<u-icon name="minus" :color="value<=1?'#CBD5E1':'#0052FF'" size="26"></u-icon>
</view>
<text class="stepper__val mm-display">{{value}}</text>
<view :class="['stepper__btn', value>=getMaxStock()?'stepper__btn--off':'']" @click="plusNum">
<u-icon name="plus" :color="value>=getMaxStock()?'#CBD5E1':'#0052FF'" size="26"></u-icon>
</view>
</view>
</view>
</view>
<!-- xuchao: 游客信息卡(需实名入园时) -->
<view class="sec mm-card mm-fade-up" v-if="skuObj.sku.enterDemand=='2'">
<view class="sec__head">
<view class="mm-sec-title">游客信息</view>
<view class="sec__hint">需要 <text class="sec__hint-num">{{value}}位</text> 出行游客信息,用于入园身份验证</view>
</view>
<view class="persons">
<view class="p_on mm-grad" v-for="(item,index) in personList" :key="index">{{item.name}}</view>
<view class="p_add" @tap="addPerson">新增/更换
<u-icon name="arrow-right" color="#64748B" size="20"></u-icon>
</view> </view>
</view> </view>
<view class="p_row" v-for="(item,index) in personList" :key="index">
<view class="p_row__del" @click="deletePerson(index)">
<u-icon name="close-circle" color="#CBD5E1" size="36"></u-icon>
</view>
<text class="p_row__label">游客{{index+1}}</text>
<view class="p_row__info">
<view class="p_row__name">{{item.name}}</view>
<view class="p_row__id">{{item.identityNo}}</view>
</view>
</view>
<view class="c_row">
<text class="c_row__label">联系手机</text>
<text class="c_row__val">{{createInfo.mobile}}</text>
</view>
</view>
<!-- xuchao: 联系人信息卡(无需实名时) -->
<view class="sec mm-card mm-fade-up" v-else>
<view class="mm-sec-title">联系人信息</view>
<view class="c_row">
<text class="c_row__label">联系人</text>
<input class="c_row__input" :adjust-position="false" type="text" v-model="createInfo.contactName"
placeholder="请输入联系人姓名" placeholder-class="mm-ph" />
</view>
<view class="c_row">
<text class="c_row__label">手机号</text>
<text class="c_row__val">{{createInfo.mobile}}</text>
</view>
</view>
<!-- xuchao: 更多日期日历(选中色随主题) -->
<u-calendar v-model="show" :mode="mode" activeBgColor="#0052FF" :calendarList="calendarList"
@change="change"></u-calendar>
<!-- xuchao: 底部结算栏 —— 总价 + 明细 + 立即购买 -->
<view class="bar">
<view class="bar__sum">
<view class="bar__label">总价</view>
<view class="bar__price" @tap="show1=true">
<text class="bar__cur">¥</text>
<text class="bar__num mm-display mm-grad-text">{{salesPriceSum}}</text>
<view class="bar__detail">明细
<u-icon name="arrow-down-fill" color="#0052FF" size="20"></u-icon>
</view>
</view>
</view>
<view class="bar__buy mm-grad" @tap="buy">立即购买</view>
</view>
<!-- xuchao: 费用明细弹层 -->
<u-popup v-model="show1" border-radius="20" mode="bottom">
<view class="pp mm-safe-bottom">
<view class="mm-sec-title">费用明细</view>
<view class="pp__sub">景区/场馆</view>
<view class="pp__row">
<text class="pp__row-label">门票</text>
<text class="pp__row-val">¥{{salesPrice}} x{{value}}</text>
</view>
<view class="pp__total">
<text class="pp__total-label">总价</text>
<view class="pp__total-price">
<text class="pp__total-cur">¥</text>
<text class="pp__total-num mm-display mm-grad-text">{{salesPriceSum}}</text>
</view>
</view>
<view class="mm-btn" @click="show1=false">确定</view>
</view>
</u-popup> </u-popup>
</view> </view>
</template> </template>
@@ -154,7 +140,11 @@
import pay from '@/common/pay.js' import pay from '@/common/pay.js'
var that = '' var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
show1: false, show1: false,
@@ -167,7 +157,6 @@
whday: '', whday: '',
skuObj: '', skuObj: '',
calendarList: [], calendarList: [],
windowHeight: '',
mobile: '', mobile: '',
personList: [], personList: [],
objInfo: '', objInfo: '',
@@ -282,11 +271,6 @@
}, },
onLoad() { onLoad() {
that = this that = this
uni.getSystemInfo({
success: function(res) {
that.windowHeight = res.windowHeight
}
});
this.createInfo.mobile = uni.getStorageSync('phone') this.createInfo.mobile = uni.getStorageSync('phone')
this.createInfo.contactMobile = uni.getStorageSync('phone') this.createInfo.contactMobile = uni.getStorageSync('phone')
this.skuObj = uni.getStorageSync('scenicDetail') this.skuObj = uni.getStorageSync('scenicDetail')
@@ -414,8 +398,20 @@
const day = date.getDate().toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0');
return `${month}月${day}日`; return `${month}月${day}日`;
}, },
valChange(e) { // xuchao: 数量步进 —— 减到下限 1 为止,加到当前库存上限并提示
console.log('当前值为: ' + e.value) minusNum() {
if (this.value > 1) this.value--
},
plusNum() {
if (this.value < this.getMaxStock()) {
this.value++
} else {
uni.showToast({
icon: 'none',
title: '库存不足了',
duration: 1500
})
}
}, },
change(e) { change(e) {
this.calendarList.forEach((item, index) => { this.calendarList.forEach((item, index) => {
@@ -446,6 +442,15 @@
}); });
return return
} }
// xuchao: 出行人多于购买数量也拦截(如先选多人后减量),提示移除多余位
if (this.personList.length > this.value) {
uni.showToast({
icon: 'none',
title: '只需' + this.value + '位出行人,请移除多余' + (this.personList.length - this.value) + '位',
duration: 2000
});
return
}
} else { } else {
if (this.createInfo.contactName == '') { if (this.createInfo.contactName == '') {
uni.showToast({ uni.showToast({
@@ -532,204 +537,451 @@
} }
} }
</script> </script>
<style>
page {
background: #F8F9FC;
}
</style>
<style scoped lang="less"> <style scoped lang="less">
.bt { /* xuchao: 底部预留 200rpx,避免内容被固定结算栏遮挡 */
width: 288rpx; .page {
height: 100rpx; padding: 32rpx 32rpx 200rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%); }
border-radius: 100rpx;
.sec {
margin-top: 24rpx;
padding: 32rpx 24rpx;
&:first-child {
margin-top: 0;
}
}
.divider {
margin: 28rpx 0;
border-top: 2rpx dashed var(--mm-border);
}
.label {
font-size: 24rpx;
color: var(--mm-muted-fg);
margin-top: 4rpx;
}
/* ---------- 商品摘要 ---------- */
.sku__name {
font-size: 32rpx;
font-weight: 600;
line-height: 1.4;
color: var(--mm-fg);
}
.sku__age {
margin-top: 8rpx;
font-size: 22rpx;
color: var(--mm-muted-fg);
}
/* ---------- 日期 chips ---------- */
.dates {
display: flex; display: flex;
align-items: center; gap: 8rpx;
justify-content: center; margin-top: 16rpx;
font-size: 36rpx; }
color: #fff;
.date {
flex: 1;
height: 170rpx;
border: 2rpx solid var(--mm-border);
border-radius: 16rpx;
background: var(--mm-card);
color: var(--mm-muted-fg);
position: relative;
font-size: 26rpx;
line-height: 1.5;
padding: 40rpx 0 0 24rpx;
box-sizing: border-box;
}
.date--off {
background: var(--mm-muted);
color: #CBD5E1;
}
.date--on {
border-color: var(--mm-accent);
background: rgba(0, 82, 255, 0.04);
color: var(--mm-accent);
}
.date__tag {
position: absolute; position: absolute;
right: 24rpx; top: 0;
bottom: 14rpx; right: 0;
font-weight: bold; height: 34rpx;
padding: 0 12rpx;
display: flex;
align-items: center;
font-size: 18rpx;
background: var(--mm-muted);
color: var(--mm-muted-fg);
border-radius: 0 14rpx 0 14rpx;
} }
.db_rq { .date--on .date__tag {
width: 750rpx; background: var(--mm-grad);
background-color: #fff;
height: 62px;
position: fixed;
// xuchao: 改为底部锚定(原来用 JS 算 top,宽屏缩放后会错位),并给全面屏留出底部安全区
top: auto !important;
bottom: 0;
// 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;
padding: 14rpx 24rpx;
}
.xz_name {
height: 56rpx;
padding: 0rpx 24rpx;
line-height: 56rpx;
color: #fff; color: #fff;
border-radius: 4rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
} }
.nx_name { .date--more {
height: 56rpx; flex: none;
padding: 0rpx 24rpx; width: 96rpx;
line-height: 56rpx; padding: 0;
color: rgba(0, 0, 0, 0.5); display: flex;
border-radius: 4rpx; flex-direction: column;
background: #F8F9FC; align-items: center;
border: 1px solid rgba(0, 0, 0, 0.1); justify-content: center;
font-size: 22rpx;
line-height: 1.6;
} }
.nx_item { /* ---------- 时段 chips ---------- */
width: 226rpx; .slots {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
margin-top: 16rpx;
}
.slot {
width: 210rpx;
height: 110rpx; height: 110rpx;
border-radius: 8rpx; border: 2rpx solid var(--mm-border);
border: 1px solid #E1E1E1; border-radius: 16rpx;
color: rgba(0, 0, 0, 0.5); color: var(--mm-muted-fg);
font-size: 26rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
box-sizing: border-box;
.xz_tag {
height: 30rpx;
padding: 0rpx 10rpx;
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.5);
font-size: 16rpx;
background: #E1E1E1;
border-radius: 0px 8rpx 0px 8rpx;
position: absolute;
top: 0rpx;
right: 0rpx;
}
} }
.xz_item { .slot--on {
width: 226rpx; border-color: var(--mm-accent);
height: 110rpx; background: rgba(0, 82, 255, 0.04);
border-radius: 8rpx; color: var(--mm-accent);
border: 1px solid #F66C31; }
color: #F66C31;
.slot__tag {
position: absolute;
top: 0;
right: 0;
height: 34rpx;
padding: 0 12rpx;
display: flex;
align-items: center;
font-size: 18rpx;
background: var(--mm-muted);
color: var(--mm-muted-fg);
border-radius: 0 14rpx 0 14rpx;
}
.slot--on .slot__tag {
background: var(--mm-grad);
color: #fff;
}
/* ---------- 购买数量 ---------- */
.qty {
display: flex;
align-items: center;
justify-content: space-between;
}
.qty__label {
font-size: 32rpx;
font-weight: 600;
color: var(--mm-fg);
}
/* xuchao: 数量步进器 —— 两端描边按钮、中间数字居中 */
.stepper {
display: flex;
align-items: center;
}
.stepper__btn {
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
border: 2rpx solid var(--mm-border);
background: var(--mm-card);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; transition: transform 0.15s ease;
.xz_tag {
height: 30rpx;
padding: 0rpx 10rpx;
display: flex;
align-items: center;
color: #fff;
font-size: 16rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 0px 8rpx 0px 8rpx;
position: absolute;
top: 0rpx;
right: 0rpx;
}
} }
.gd_rq { .stepper__btn:active {
width: 120rpx; transform: scale(0.92);
height: 170rpx; background: var(--mm-muted);
border: 1px solid #E1E1E1; }
border-radius: 8rpx;
color: rgba(0, 0, 0, 0.5); .stepper__btn--off {
border-color: var(--mm-muted);
background: var(--mm-muted);
}
.stepper__val {
width: 110rpx;
text-align: center;
font-size: 36rpx;
color: var(--mm-fg);
}
/* ---------- 游客/联系人 ---------- */
.sec__head {
display: flex;
align-items: flex-end;
}
.sec__hint {
margin-left: 16rpx;
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.sec__hint-num {
font-size: 24rpx;
font-weight: 600;
color: var(--mm-fg);
}
.persons {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-top: 24rpx;
}
/* xuchao: 已选游客徽章 —— 渐变胶囊(保持原有识别度) */
.p_on {
height: 60rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
border-radius: 999rpx;
color: #fff;
font-size: 26rpx;
box-shadow: var(--mm-shadow-accent);
}
.p_add {
height: 60rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
gap: 4rpx;
border-radius: 999rpx;
border: 2rpx dashed var(--mm-border);
color: var(--mm-muted-fg);
font-size: 26rpx;
}
.p_row {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 2rpx solid var(--mm-border);
}
.p_row__del {
flex: none;
display: flex;
align-items: center;
}
.p_row__label {
flex: none;
margin-left: 20rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.p_row__info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.p_row__name {
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
.p_row__id {
margin-top: 4rpx;
font-size: 22rpx;
letter-spacing: 0.04em;
color: var(--mm-muted-fg);
}
.c_row {
display: flex;
align-items: center;
padding: 24rpx 0 0;
}
.c_row__label {
flex: none;
width: 140rpx;
font-size: 28rpx;
color: var(--mm-muted-fg);
}
.c_row__val {
font-size: 28rpx;
color: var(--mm-fg);
}
.c_row__input {
flex: 1;
font-size: 28rpx;
color: var(--mm-fg);
}
/* ---------- 底部结算栏 ---------- */
.bar {
position: fixed;
left: 0;
right: 0;
margin: 0 auto;
width: 750rpx;
bottom: 0;
z-index: 99;
display: flex;
align-items: center;
background: var(--mm-card);
border-top: 2rpx solid var(--mm-border);
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.05);
padding: 16rpx 32rpx;
/* xuchao: 全面屏底部安全区 */
padding-bottom: calc(16rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
}
.bar__sum {
flex: 1;
min-width: 0;
}
.bar__label {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.bar__price {
display: flex;
align-items: baseline;
gap: 16rpx;
}
.bar__cur {
font-size: 28rpx;
font-weight: 600;
color: var(--mm-accent);
}
.bar__num {
font-size: 52rpx;
line-height: 1.1;
}
.bar__detail {
display: flex;
align-items: center;
gap: 4rpx;
font-size: 22rpx;
color: var(--mm-accent);
}
.bar__buy {
flex: none;
width: 288rpx;
height: 96rpx;
border-radius: 999rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #fff;
font-size: 32rpx;
font-weight: 600;
box-shadow: var(--mm-shadow-accent);
transition: transform 0.15s ease;
} }
.ht_rq { .bar__buy:active {
width: 186rpx; transform: scale(0.96);
height: 170rpx;
border: 1px solid #E1E1E1;
border-radius: 8rpx;
color: rgba(0, 0, 0, 0.5);
position: relative;
line-height: 24px;
padding-top: 48rpx;
padding-left: 24rpx;
background: #F8F9FC;
.jt_tag {
font-size: 16rpx;
height: 30rpx;
display: flex;
align-items: center;
padding: 0rpx 10rpx;
background: #E1E1E1;
border-radius: 0px 8rpx 0px 8rpx;
position: absolute;
top: 0rpx;
right: 0rpx;
}
} }
/* ---------- 费用明细弹层 ---------- */
.jt_rq { .pp {
width: 186rpx; padding: 48rpx;
height: 170rpx; display: flex;
border: 1px solid #E1E1E1; flex-direction: column;
border-radius: 8rpx;
color: rgba(0, 0, 0, 0.5);
position: relative;
line-height: 24px;
padding-top: 48rpx;
padding-left: 24rpx;
.jt_tag {
font-size: 16rpx;
height: 30rpx;
display: flex;
align-items: center;
padding: 0rpx 10rpx;
background: #E1E1E1;
border-radius: 0px 8rpx 0px 8rpx;
position: absolute;
top: 0rpx;
right: 0rpx;
}
} }
.pp__sub {
margin-top: 24rpx;
font-size: 28rpx;
font-weight: 600;
color: var(--mm-fg);
}
.mt_rq { .pp__row {
width: 186rpx; height: 88rpx;
height: 170rpx; display: flex;
border: 1px solid #F66C31; align-items: center;
border-radius: 8rpx; justify-content: space-between;
color: #F66C31; border-bottom: 2rpx dashed var(--mm-border);
position: relative; }
line-height: 24px;
padding-top: 48rpx;
padding-left: 24rpx;
.jt_tag { .pp__row-label {
font-size: 16rpx; font-size: 26rpx;
height: 30rpx; color: var(--mm-muted-fg);
display: flex; }
align-items: center;
padding: 0rpx 10rpx; .pp__row-val {
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%); font-size: 26rpx;
border-radius: 0px 8rpx 0px 8rpx; color: var(--mm-fg);
position: absolute; }
color: #fff;
top: 0rpx; .pp__total {
right: 0rpx; display: flex;
} align-items: baseline;
justify-content: flex-end;
gap: 12rpx;
margin: 24rpx 0 40rpx;
}
.pp__total-label {
font-size: 22rpx;
color: var(--mm-muted-fg);
}
.pp__total-price {
display: flex;
align-items: baseline;
}
.pp__total-cur {
font-size: 26rpx;
font-weight: 600;
color: var(--mm-accent);
}
.pp__total-num {
font-size: 44rpx;
line-height: 1;
} }
</style> </style>
+98 -73
View File
@@ -1,31 +1,36 @@
<template> <template>
<view v-if="obj"> <view v-if="obj" class="page mm-page">
<view class="od_top"> <gmy-back-touch></gmy-back-touch>
<view class="od_top_first"> <!-- xuchao: 成功状态英雄区 —— 渐变对勾 + 柔光 -->
<image src="http://cdn.hntgov.net/online/static/ps_ps.png"></image>支付成功 <view class="hero mm-fade-up">
<view class="hero__icon mm-grad">
<u-icon name="checkmark" color="#ffffff" size="64"></u-icon>
</view> </view>
<view class="hero__title mm-display">支付成功</view>
<view class="hero__sub">门票订单已提交,请留意出票通知</view>
</view> </view>
<view style="background-color: #fff;">
<view class="od_sh"> <!-- xuchao: 支付信息卡 -->
<image style="width: 32rpx;height: 28rpx;" src="http://cdn.hntgov.net/online/static/pas_ps.png"> <view class="card mm-card mm-fade-up">
</image> <view class="row">
<view class="od_sh_right"> <text class="row__label">支付方式</text>
<!-- xuchao: 支付方式改为微信支付 --> <text class="row__val">微信支付</text>
<text style="color: #696969;">支付方式:<text style="color: #3D3D3D;">微信支付</text></text> </view>
<text style="color: #696969;padding-top: 16rpx;">实付金额:<text <view class="row">
style="color: #3D3D3D;">¥{{(obj.order.orderAmount/100).toFixed(2)||0}}</text></text> <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> </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> </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" /> <u-toast ref="uToast" />
</view> </view>
</template> </template>
@@ -33,7 +38,11 @@
<script> <script>
// xuchao: 支付成功页 —— 支付完成后跳入,轮询订单状态直到后端确认 // xuchao: 支付成功页 —— 支付完成后跳入,轮询订单状态直到后端确认
var that = '' var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
id: '', id: '',
@@ -90,74 +99,90 @@
} }
</script> </script>
<style> <style scoped lang="less">
page { .page {
background-color: #f7f7f7; padding: 96rpx 32rpx 60rpx;
}
</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;
}
} }
.od_top { /* ---------- 英雄区 ---------- */
width: 750rpx; .hero {
height: 210rpx;
background: linear-gradient(90deg, #FF3F3C -8%, #FFA371 107%);
padding-top: 56rpx;
padding-left: 70rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
} }
.od_top_first { .hero__icon {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
color: #FFF; box-shadow: var(--mm-shadow-accent), 0 0 0 24rpx rgba(0, 82, 255, 0.06);
font-size: 34rpx;
} }
.od_top_first image { .hero__title {
width: 50rpx; margin-top: 36rpx;
height: 50rpx; font-size: 48rpx;
margin-right: 20rpx; color: var(--mm-fg);
} }
.od_sh { .hero__sub {
padding: 30rpx; margin-top: 12rpx;
font-size: 24rpx;
color: var(--mm-muted-fg);
}
/* ---------- 支付信息卡 ---------- */
.card {
margin-top: 64rpx;
padding: 8rpx 32rpx;
}
.row {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 26rpx; justify-content: space-between;
background-color: #fff; height: 104rpx;
.od_sh_right { &:first-child {
width: 640rpx; border-bottom: 2rpx solid var(--mm-muted);
margin-left: 18rpx;
display: flex;
flex-direction: column;
} }
} }
.od_line { .row__label {
width: 690rpx; font-size: 28rpx;
height: 1rpx; color: var(--mm-muted-fg);
background-color: #E7E6E6; }
margin-left: 30rpx;
.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> </style>
+36 -30
View File
@@ -1,35 +1,40 @@
<template> <template>
<view> <view class="page mm-page">
<u-form :model="form" :rules="rules" ref="uForm"> <gmy-back-touch></gmy-back-touch>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;"> <!-- 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-form-item label-width="200" :label-position="labelPosition" label="出行人姓名" prop="name">
<u-input placeholder="请输入出行人姓名" v-model="form.name" type="text"></u-input> <u-input placeholder="请输入出行人姓名" v-model="form.name" type="text"></u-input>
</u-form-item> </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-form-item label-width="200" :label-position="labelPosition" label="联系号码" prop="phone">
<u-input placeholder="请输入联系号码" v-model="form.phone" type="text"></u-input> <u-input placeholder="请输入联系号码" v-model="form.phone" type="text"></u-input>
</u-form-item> </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-form-item label="证件类型" prop="idType" label-width="200" :label-position="labelPosition">
<u-input v-model="idTypeName" placeholder='请选择证件类型' type="select" @click="idTypeShow()" /> <u-input v-model="idTypeName" placeholder='请选择证件类型' type="select" @click="idTypeShow()" />
</u-form-item> </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-form-item label-width="200" :label-position="labelPosition" label="证件号码" prop="identityNo">
<u-input placeholder="请输入证件号码" v-model="form.identityNo" type="text"></u-input> <u-input placeholder="请输入证件号码" v-model="form.identityNo" type="text"></u-input>
</u-form-item> </u-form-item>
</view> </u-form>
</u-form> </view>
<view class="pp_hh" @click="submit()">完成</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> <u-action-sheet :list="actionSheetList" v-model="show" @click="actionSheetCallback"></u-action-sheet>
</view> </view>
</template> </template>
<script> <script>
var that = '' var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
show: false, show: false,
@@ -54,7 +59,7 @@
}, },
{ {
value: 'TAIWAN', value: 'TAIWAN',
text: '台湾居民来往大陆通行证' text: '台湾居民来往内地通行证'
} }
], ],
form: { form: {
@@ -198,23 +203,24 @@
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.pp_hh { /* xuchao: 底部预留 220rpx,避免表单被固定提交按钮遮挡 */
width: 702rpx; .page {
height: 88rpx; padding: 32rpx 32rpx 220rpx;
border-radius: 48rpx; }
display: flex;
align-items: center; .card {
justify-content: center; padding: 12rpx 32rpx;
color: #fff; }
font-size: 28rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%); /* xuchao: 底部固定按钮 —— left/right 0 + margin auto 保证宽屏居中时与页面列对齐 */
// xuchao: 视口水平居中(宽屏下与页面列对齐) .bar {
left: 0rpx;
right: 0rpx;
margin: 0 auto;
position: fixed; position: fixed;
// xuchao: 全面屏底部安全区 —— 悬浮按钮整体上移 left: 0;
bottom: calc(25rpx + constant(safe-area-inset-bottom)); right: 0;
bottom: calc(25rpx + env(safe-area-inset-bottom)); 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> </style>
+176 -210
View File
@@ -1,22 +1,46 @@
<template> <template>
<view> <view class="page mm-page">
<view class='nr_item' v-for="(item,index) in lookList" :key="index" @click="toDetail(item.id)"> <!-- xuchao: 景区卡片列表 —— 整卡可点进详情,入场逐个渐显 -->
<image class="nr_img" :src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image> <view class="list mm-stagger">
<view class="nr_item_r"> <view class="card mm-card mm-fade-up" v-for="(item,index) in lookList" :key="index"
<text class="n_text">{{item.name||''}}</text> @click="toDetail(item.id)">
<view class="nr_tags"> <image class="card__img" mode="aspectFill"
<text :class="index1==0?'t_text1':index1==1?'t_text':index1==2?'t_text2':'t_text3'" :src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image>
v-for="(item1,index1) in ['节日福利/春游秋游','余额券','满减券']" :key="index1">{{item1}}</text> <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>
<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>
</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: 订单悬浮按钮 --> <!-- xuchao: 订单悬浮按钮 -->
<gmy-float-touch :mainImg="mainImg" @menuClick="floatTouchClick"></gmy-float-touch> <gmy-float-touch @menuClick="floatTouchClick"></gmy-float-touch>
</view> </view>
</template> </template>
@@ -30,10 +54,11 @@
components: { gmyFloatTouch }, components: { gmyFloatTouch },
data() { data() {
return { return {
mainImg: "../../static/logo4.png",
pageAll: 2, pageAll: 2,
isLoad: true, isLoad: true,
lookList: [], lookList: [],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
obj: { obj: {
"name": "", "name": "",
"typeId": "", "typeId": "",
@@ -44,6 +69,11 @@
} }
} }
}, },
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad() { onLoad() {
that = this that = this
// xuchao: 未登录则(正式)跳登录页 / (开发)静默假登录 // xuchao: 未登录则(正式)跳登录页 / (开发)静默假登录
@@ -59,8 +89,10 @@
this.isLoad = false this.isLoad = false
this.$u.api.queryTicketsPage(this.obj).then(res => { this.$u.api.queryTicketsPage(this.obj).then(res => {
this.isLoad = true this.isLoad = true
this.firstLoaded = true
this.obj.pageNum = this.obj.pageNum * 1 + 1 this.obj.pageNum = this.obj.pageNum * 1 + 1
this.pageAll = Math.ceil(res.data.total / 10) this.pageAll = Math.ceil(res.data.total / 10)
this.total = res.data.total
this.lookList = this.lookList.concat(res.data.list) this.lookList = this.lookList.concat(res.data.list)
}) })
} }
@@ -82,209 +114,143 @@
} }
</script> </script>
<style>
page {
background: #FFEFD5;
}
</style>
<style scoped lang="less"> <style scoped lang="less">
.nr_item { .page {
// xuchao: 左右边距用百分比,随屏幕宽度等比变化;宽度自适应(去掉固定 702rpx) padding: 32rpx 32rpx 40rpx;
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;
}
}
} }
// 以下为旧版遗留、当前模板未使用的样式(.item / .r_text1 / .n_text1),按约定仅提示不删 /* ---------- 卡片列表 ---------- */
.item { .list {
width: 702rpx;
padding: 12rpx;
border-radius: 8rpx;
background-color: #fff;
position: relative;
margin-top: 24rpx;
display: flex; 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; margin-left: 24rpx;
display: flex;
flex-direction: column;
}
.item_name { .card__name {
width: 434rpx; font-size: 30rpx;
line-height: normal; font-weight: 600;
color: #000; letter-spacing: -0.01em;
font-size: 28rpx; color: var(--mm-fg);
font-weight: bold; overflow: hidden !important;
overflow: hidden !important; text-overflow: ellipsis !important;
text-overflow: ellipsis !important; display: -webkit-box !important;
display: -webkit-box !important; -webkit-line-clamp: 1;
-webkit-line-clamp: 2; -webkit-box-orient: vertical;
-webkit-box-orient: vertical; }
}
.tag { .card__desc {
font-size: 20rpx; margin-top: 8rpx;
color: #fff; font-size: 22rpx;
padding: 6rpx 5rpx; line-height: 1.45;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%); color: var(--mm-muted-fg);
border-radius: 4rpx; overflow: hidden !important;
margin-top: 12rpx; text-overflow: ellipsis !important;
margin-right: 12rpx; display: -webkit-box !important;
} -webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.tag1 { .card__foot {
font-size: 20rpx; margin-top: auto;
color: #999999; padding-top: 12rpx;
padding: 5rpx; display: flex;
border: 2rpx #999999 solid; align-items: flex-end;
border-radius: 4rpx; justify-content: space-between;
margin-top: 12rpx; }
margin-right: 12rpx;
}
.hot_tag { .card__origin {
height: 36rpx; font-size: 20rpx;
color: #fff; color: var(--mm-muted-fg);
font-size: 20rpx; }
position: absolute;
top: 12rpx; .card__origin-num {
left: 12rpx; text-decoration: line-through;
background: rgba(0, 0, 0, 0.5); }
padding: 0rpx 8rpx;
border-radius: 8rpx 0rpx 8rpx 0rpx; .card__now {
line-height: 36rpx; 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> </style>
+408 -214
View File
@@ -1,159 +1,151 @@
<template> <template>
<view v-if="obj"> <view v-if="obj" class="page mm-page">
<view class="wrap"> <gmy-back-touch></gmy-back-touch>
<u-swiper :list="list" height='750' mode='number' indicator-pos='bottomRight' <!-- xuchao: 头图轮播(保留原有数字指示器) -->
marginBottom='30rpx'></u-swiper> <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>
<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"> <!-- xuchao: 门票规格列表 -->
<view style="display: flex;gap: 8rpx;color: #FF8D28;"> <view class="sec mm-fade-up">
<image src="https://aliyunoss.hn12301.net/xgh/ticket_opentime.png" <view class="sec__title mm-sec-title">门票规格</view>
style="width: 36rpx;height: 36rpx;"></image>开园时间 <view class="sku" v-for="(item,index) in obj.skuList" :key="index">
</view> <!-- xuchao: 名称提为独立整行,填充卡片宽度,不再被右侧价格列挤压换行 -->
<text style="color: #8E8E93;font-size: 24rpx;">{{obj.tickets.openDate}}</text> <view class="sku__name">【{{getSkuType(item.typeId)}}】{{item.skuName}}</view>
</view> <view class="sku__body">
<view class="sku__left">
<view class="t_address"> <view class="sku__rule">{{item.bookTimeLimit}}之前{{item.bookType?'可订当天':'可订次日'}}</view>
<view style="width: 548rpx;">{{obj.shop.address}}</view> <view class="sku__know" @click="seeSkuDetail(item.id)">
<text>预订须知</text>
<image src="https://aliyunoss.hn12301.net/xgh/ticket_dh.png" @click="handleOpen()"></image> <u-icon name="arrow-right" color="#0052FF" size="20"></u-icon>
<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>
<view style="color: #0066F6;margin-top: 16rpx;" @click="seeSkuDetail(item.id)">预订须知 ></view>
</view> </view>
<view <view class="sku__right">
style="width: 292rpx;display: flex;flex-direction: column;margin-left: 24rpx;align-items: flex-end;"> <view class="sku__origin">原售价 <text
<view class="sku__origin-num">¥{{item.calendar.listPrice?(item.calendar.listPrice/100).toFixed(2):0}}</text></view>
style="color: #FF0000;font-size: 28rpx;margin-top: 0rpx;display: flex;justify-content: flex-end;"> <view class="sku__price">
原售价<text <text class="sku__price-label">¥</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 <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>
<view class="btn" @tap="toOrderSure(item.id)">购买</view> <view class="sku__buy mm-grad" @tap="toOrderSure(item.id)">购买</view>
</view> </view>
</view> </view>
</view> </view>
<view class="ticket"> </view>
<text style="color: #000;font-size: 36rpx;line-height: 88rpx;font-weight: 700;">产品介绍</text>
<!-- 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> <u-parse :html="obj.tickets.introduction"></u-parse>
</view> </view>
</view> </view>
<u-popup v-model="show" border-radius='20' mode='bottom' height='1000'>
<view style="padding:0rpx 48rpx;" v-if="skuObj"> <!-- xuchao: 预订须知弹层 -->
<view <u-popup v-model="show" border-radius="20" mode="bottom" height="1000">
style="width: 654rpx;height: 142rpx;line-height: 142rpx;text-align: center;color: #000;font-size: 32rpx;font-weight: bold;border-bottom: 2rpx #E1E1E1 dashed;"> <view class="pp" v-if="skuObj">
{{skuObj.sku.skuName}} <view class="pp__head">{{skuObj.sku.skuName}}</view>
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx"> <view class="pp__sec">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">预订规则</text> <view class="mm-sec-title">预订规则</view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">预订时间</view> <view class="pp__label">预订时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;"> <view class="pp__val">{{skuObj.sku.bookTimeLimit}}之前{{skuObj.sku.bookType?'可订当天':'可订次日'}}</view>
{{skuObj.sku.bookTimeLimit}}之前{{skuObj.sku.bookType?'可订当天':'可订明日'}}
</view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">生效时间</view> <view class="pp__label">生效时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">出票后立即使用</view> <view class="pp__val">出票后立即使用</view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">有效期</view> <view class="pp__label">有效期</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">指定出行日期1天内有效</view> <view class="pp__val">指定出行日期1天内有效</view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">适用人群</view> <view class="pp__label">适用人群</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.typeInfo}}</view> <view class="pp__val">{{skuObj.sku.typeInfo}}</view>
</view> </view>
</view> </view>
<view style="width: 654rpx;padding: 48rpx 0rpx"> <view class="pp__sec">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">费用说明</text> <view class="mm-sec-title">费用说明</view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">费用包含</view> <view class="pp__label">费用包含</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWith}}</view> <view class="pp__val">{{skuObj.sku.contentWith}}</view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">费用不含</view> <view class="pp__label">费用不含</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWithout}}</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>
<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> </view>
<view class="pp__sec">
<view style="width: 654rpx;padding: 48rpx 0rpx"> <view class="mm-sec-title">使用说明</view>
<text style="color: #000;font-size: 32rpx;font-weight: bold;">取消政策</text> <view class="pp__row">
<u-parse style="margin-top: 24rpx;" :html="skuObj.sku.refundPolicy"></u-parse> <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>
<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"> <view class="pp__sec">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">其他说明</text> <view class="mm-sec-title">其他说明</view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">补充说明</view> <view class="pp__label">补充说明</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.remark}}</view> <view class="pp__val">{{skuObj.sku.remark}}</view>
</view> </view>
<view style="display: flex;margin-top: 24rpx;"> <view class="pp__row">
<view style="color: #808080;width: 120rpx;">发票说明</view> <view class="pp__label">发票说明</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.receiptPolicy}}</view> <view class="pp__val">{{skuObj.sku.receiptPolicy}}</view>
</view> </view>
</view> </view>
</view> </view>
</u-popup> </u-popup>
@@ -165,7 +157,11 @@
import { import {
getSkuType getSkuType
} from '../../unit/utils.js' } from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
show: false, show: false,
@@ -267,11 +263,20 @@
id: e id: e
}).then(res => { }).then(res => {
if (res) { if (res) {
// xuchao: 头图首张放单票主图(与列表卡片一致),再拼宣传图,跳过与主图重复项
const mainPic = res.data.tickets.mainPic
if (mainPic) {
this.list.push({
image: mainPic
})
}
if (res.data.tickets.propagatePic) { if (res.data.tickets.propagatePic) {
res.data.tickets.propagatePic.split(',').forEach((item, index) => { res.data.tickets.propagatePic.split(',').forEach((item, index) => {
this.list.push({ if (item && item !== mainPic) {
image: item this.list.push({
}) image: item
})
}
}) })
} }
@@ -297,103 +302,292 @@
}, },
}; };
</script> </script>
<style>
page {
background: #F8F9FC;
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.wrap { .page {
width: 750rpx; padding-bottom: 80rpx;
height: 420rpx;
} }
.ticket { /* xuchao: 头图固定 298px 高(u-swiper 的 height 属性只认 rpx,故在此覆写其内联高度),
width: 750rpx; 图片按比例缩放居中裁切(object-fit: cover) */
background: #fff; .u-swiper-wrap /deep/ uni-swiper {
padding: 24rpx; 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; margin-top: 24rpx;
display: flex;
.ticket_item { align-items: center;
margin-top: 24rpx; background: rgba(0, 82, 255, 0.05);
background: #F8F9FC; border-radius: 16rpx;
border-radius: 8rpx; padding: 18rpx 24rpx;
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 { .t_open__label {
width: 750rpx; flex: none;
background-color: #fff; margin: 0 16rpx 0 12rpx;
margin-top: 50rpx; 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; padding: 24rpx;
}
z-index: 10000; .t_addr__text {
flex: 1;
min-width: 0;
font-size: 26rpx;
line-height: 1.5;
color: var(--mm-fg);
}
.t_info { .t_addr__acts {
color: #FF383C; flex: none;
line-height: 48rpx; display: flex;
margin-top: 12rpx; gap: 16rpx;
font-weight: bold; margin-left: 20rpx;
} }
.t_opentime { .t_addr__btn {
background-color: #F8F9FD; width: 76rpx;
padding: 8rpx; height: 76rpx;
display: flex; border-radius: 50%;
flex-direction: column; display: flex;
gap: 12rpx; align-items: center;
margin-top: 24rpx; justify-content: center;
} box-shadow: var(--mm-shadow-accent);
transition: transform 0.15s ease;
}
.t_address { .t_addr__btn:active {
width: 702rpx; transform: scale(0.9);
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; .sec {
height: 70rpx; margin: 24rpx 32rpx 0;
} background: var(--mm-muted);
border-radius: var(--mm-radius);
padding: 28rpx 24rpx;
}
} .sec__title {
margin-bottom: 4rpx;
}
.t_name { /* ---------- 规格卡片 ---------- */
width: 702rpx; .sku {
color: #000; margin-top: 20rpx;
font-size: 36rpx; background: var(--mm-card);
font-weight: 700; border: 2rpx solid var(--mm-border);
line-height: 48rpx; border-radius: 20rpx;
} padding: 24rpx;
box-shadow: var(--mm-shadow-sm);
}
.tag { /* xuchao: 名称下方的两栏区(左规则/预订须知,右价格/购买) */
color: #7C7C7E; .sku__body {
font-size: 20rpx; margin-top: 20rpx;
padding: 5rpx 10rpx; display: flex;
background: #F8F9FC; }
border-radius: 4rpx;
display: block; .sku__left {
margin-right: 12rpx; 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>
+168 -41
View File
@@ -1,27 +1,54 @@
<template> <template>
<view> <view class="page mm-page">
<text class="text">需添加 &nbsp;{{value}}&nbsp;个出行人</text> <gmy-back-touch></gmy-back-touch>
<view class="h_item" @click="toAddPerson"> <!-- xuchao: 顶部提示徽章 —— 需添加的出行人数量 -->
<text class="text1">新增出行人</text> <view class="tip mm-fade-up">
<text class="text2">+</text> <view class="mm-badge">
</view> <view class="mm-badge__dot"></view>
<view class="item" v-for="(item,index) in list" :key="index"> <text class="mm-badge__text">需添加 {{value}} 个出行人(已选 {{selectedCount}})</text>
<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> </view>
</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> <!-- 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>
</view> </view>
</template> </template>
<script> <script>
var that='' var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default { export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() { data() {
return { return {
checked:true, checked:true,
@@ -31,16 +58,34 @@ export default {
}, },
onLoad(e){ onLoad(e){
that=this that=this
this.value=e.value this.value=Number(e.value)
}, },
onShow(){ onShow(){
this.getMemberTouristMethod() this.getMemberTouristMethod()
}, },
computed: {
// xuchao: 已勾选的出行人数量
selectedCount() {
return this.list.filter(item => item.isSelect).length
}
},
methods: { methods: {
// 选中某个复选框时,由checkbox时触发 // 选中某个复选框时,由checkbox时触发
checkboxChange(e) { checkboxChange(e) {
console.log(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(){ toAddPerson(){
uni.navigateTo({ uni.navigateTo({
url: 'person' url: 'person'
@@ -82,38 +127,120 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.text{ /* xuchao: 底部预留 220rpx,避免列表被固定完成按钮遮挡 */
color: rgba(0, 0, 0, 0.8); .page {
margin: 24rpx 0rpx 0rpx 24rpx; padding: 32rpx 32rpx 220rpx;
font-size: 36rpx;
height: 88rpx;
font-weight: bold;
} }
.h_item{
width: 702rpx; .tip {
display: flex;
}
.add {
margin-top: 24rpx; margin-top: 24rpx;
display: flex; display: flex;
margin-left: 24rpx;
justify-content: space-between;
border-bottom: 1px dashed #E1E1E1;
align-items: center; align-items: center;
padding-bottom: 24rpx; border: 2rpx dashed rgba(0, 82, 255, 0.4);
.text1{ border-radius: 20rpx;
color:rgba(0, 0, 0, 0.8); background: rgba(0, 82, 255, 0.04);
} padding: 28rpx 24rpx;
.text2{ transition: transform 0.15s ease;
color:#457AF7;
font-size: 40rpx;
font-weight: bold;
}
} }
.item{
width: 702rpx; .add:active {
margin-left: 24rpx; transform: scale(0.985);
height: 112rpx; }
.add__icon {
flex: none;
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
position: relative; justify-content: center;
box-shadow: var(--mm-shadow-accent);
}
.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> </style>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+25
View File
@@ -75,3 +75,28 @@ $uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色 $uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px; $uni-font-size-paragraph:15px;
@import "uview-ui/theme.scss"; @import "uview-ui/theme.scss";
/* xuchao: Minimalist Modern 设计令牌(SCSS 变量)——全站统一色彩来源 */
$mm-bg: #FAFAFA; // 页面底色,暖白
$mm-fg: #0F172A; // 主文字,深石板(同时用作反色区块背景)
$mm-muted: #F1F5F9; // 次级表面/浅填充
$mm-muted-fg: #64748B; // 次级文字
$mm-accent: #0052FF; // 电光蓝,唯一强调色
$mm-accent-2: #4D7CFF; // 签名渐变终点色
$mm-card: #FFFFFF; // 卡片表面
$mm-border: #E2E8F0; // 描边
/* xuchao: 覆盖 uView 主题变量,让内置组件(按钮/输入/弹层/日历等)换肤为新设计系统。
放在 theme.scss 引入之后,Sass 变量后赋值生效。 */
$u-main-color: $mm-fg;
$u-content-color: #475569;
$u-tips-color: $mm-muted-fg;
$u-light-color: #CBD5E1;
$u-border-color: $mm-border;
$u-bg-color: $mm-muted;
$u-type-primary: $mm-accent;
$u-type-primary-light: #E8EEFF;
$u-type-primary-disabled: #A8BEFF;
$u-type-primary-dark: #0041CC;
/* uni-app 默认主色同步为电光蓝 */
$uni-color-primary: $mm-accent;
+3 -1
View File
@@ -1,5 +1,7 @@
@font-face { @font-face {
font-family: 'YouShe'; font-family: 'YouShe';
/* xuchao: 该文件经 App.vue @import 内联后,webpack 以"项目根目录"为基准解析 url,
故 ./static/ 即根目录 static/,勿改成相对本文件的 ../../ 路径(会解析失败) */
src: url('./static/YouShe.ttf') format('truetype'); src: url('./static/YouShe.ttf') format('truetype');
font-display: swap;
} }
+15 -6
View File
@@ -63,7 +63,8 @@
<view class="u-calendar__bottom__btn"> <view class="u-calendar__bottom__btn">
<!-- <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)" <!-- <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)"
:custom-style="customStyle">确定</u-button> --> :custom-style="customStyle">确定</u-button> -->
<view style="background:linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);width:inherit;height: 80rpx;border-radius: 60rpx;display: flex;align-items: center;justify-content: center;color: #fff;" @click="btnFix(false)">确定</view> <!-- xuchao: 确定按钮换主题蓝渐变(原橙红渐变与整站风格冲突) -->
<view style="background:linear-gradient(135deg, #0052FF, #4D7CFF);box-shadow:0 8rpx 24rpx rgba(0, 82, 255, 0.25);width:inherit;height: 80rpx;border-radius: 60rpx;display: flex;align-items: center;justify-content: center;color: #fff;" @click="btnFix(false)">确定</view>
</view> </view>
</view> </view>
</view> </view>
@@ -187,9 +188,10 @@
default: '#999999' default: '#999999'
}, },
// 选中|起始结束日期背景色 // 选中|起始结束日期背景色
// xuchao: 默认色换主题蓝(原绿色与整站风格不一致)
activeBgColor: { activeBgColor: {
type: String, type: String,
default: '#22B464' default: '#0052FF'
}, },
// 选中|起始结束日期字体颜色 // 选中|起始结束日期字体颜色
activeColor: { activeColor: {
@@ -199,12 +201,12 @@
// 范围内日期背景色 // 范围内日期背景色
rangeBgColor: { rangeBgColor: {
type: String, type: String,
default: 'rgba(123,202,74,0.13)' default: 'rgba(0, 82, 255, 0.08)'
}, },
// 范围内日期字体颜色 // 范围内日期字体颜色
rangeColor: { rangeColor: {
type: String, type: String,
default: '#22B464' default: '#0052FF'
}, },
// mode=range时生效,起始日期自定义文案 // mode=range时生效,起始日期自定义文案
startText: { startText: {
@@ -246,7 +248,7 @@
return { return {
// 星期几,值为1-7 // 星期几,值为1-7
customStyle: { customStyle: {
background: 'linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%)' background: 'linear-gradient(135deg, #0052FF, #4D7CFF)'
}, },
weekday: 1, weekday: 1,
weekdayArr: [], weekdayArr: [],
@@ -307,7 +309,8 @@
}, },
getColor1(index, type) { getColor1(index, type) {
let color = type == 1 ? '' : '#f00'; // xuchao: 未选中日期价格色由红改主题蓝
let color = type == 1 ? '' : '#0052FF';
let day = index + 1 let day = index + 1
let date = `${this.year}-${this.month}-${day}` let date = `${this.year}-${this.month}-${day}`
let timestamp = new Date(date.replace(/\-/g, '/')).getTime(); let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
@@ -567,6 +570,12 @@
.u-calendar { .u-calendar {
color: $u-content-color; color: $u-content-color;
/* xuchao: 限高视口 85%,超出时日历内部上下滑动(小屏机型弹窗不再顶出屏幕);
overscroll-behavior 防止滚到边缘后链式滚动背景页 */
max-height: 85vh;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
&__header { &__header {
width: 100%; width: 100%;
+3 -1
View File
@@ -3,6 +3,8 @@
zIndex: uZindex - 1 zIndex: uZindex - 1
}]" class="u-drawer" hover-stop-propagation> }]" class="u-drawer" hover-stop-propagation>
<u-mask :duration="duration" :custom-style="maskCustomStyle" :maskClickAble="maskCloseAble" :z-index="uZindex - 2" :show="showDrawer && mask" @click="maskClick"></u-mask> <u-mask :duration="duration" :custom-style="maskCustomStyle" :maskClickAble="maskCloseAble" :z-index="uZindex - 2" :show="showDrawer && mask" @click="maskClick"></u-mask>
<!-- xuchao: touchmove 去掉 .prevent —— 它会拦截默认行为,导致 H5 下内层滚动容器无法触摸滚动;
背景页防穿透改由滚动容器的 overscroll-behavior: contain 承担(见 App.vue) -->
<view <view
class="u-drawer-content" class="u-drawer-content"
@tap="modeCenterClose(mode)" @tap="modeCenterClose(mode)"
@@ -12,7 +14,7 @@
showDrawer ? 'u-drawer-content-visible' : '', showDrawer ? 'u-drawer-content-visible' : '',
zoom && mode == 'center' ? 'u-animation-zoom' : '' zoom && mode == 'center' ? 'u-animation-zoom' : ''
]" ]"
@touchmove.stop.prevent @touchmove.stop
@tap.stop.prevent @tap.stop.prevent
:style="[style]" :style="[style]"
> >