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/
.DS_Store
# xuchao: Playwright 验证产物(截图/控制台日志),不入库
.playwright-mcp/
+359 -3
View File
@@ -37,7 +37,362 @@
@import "uview-ui/index.scss";
@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 */
// 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 导致图片不显示/溢出
uni-image {
display: inline-block;
@@ -60,8 +415,9 @@
// xuchao: 宽屏(>768px,框架此时把 rpx 基准锁在 375)下,把页面列居中、两侧露出底色
@media screen and (min-width: 769px) {
body {
// xuchao: 页面级 page 背景会染到 body,需 !important 才能露出两侧底色
background: #dfe4ea !important;
// xuchao: 页面级 page 背景会染到 body,需 !important 才能露出两侧底色;
// 底色取设计系统描边色,与 #FAFAFA 页面形成柔和层次
background: #E2E8F0 !important;
}
uni-page-body {
@@ -74,7 +430,7 @@
margin: 0 auto !important;
min-height: 100vh;
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 */
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">
<!-- xuchao: 门票订单悬浮按钮 —— 固定距屏幕底部 50px、左侧 12px,单击直达订单列表 -->
<view class="box" @click="onTap">
<image class="box__img" :src="mainImg" mode="aspectFill"></image>
<!-- xuchao: 门票订单悬浮按钮 —— 渐变圆钮固定于左下,单击直达订单列表 -->
<view class="gft-box" @click="onTap">
<view class="gft-btn mm-grad">
<u-icon name="order" color="#ffffff" size="44"></u-icon>
</view>
</view>
</template>
<script>
export default {
name: "gmy-float-touch",
props: {
mainImg: {
type: String,
default: "../../static/main.png"
}
},
methods: {
// xuchao: 单击直接派发订单事件
// xuchao: 单击直接派发订单事件(保留原契约:menuClick 回调参数 4)
onTap: function() {
this.$emit("menuClick", 4)
}
@@ -24,27 +20,27 @@
</script>
<style lang="scss">
.box {
/* xuchao: 类名加 gft- 前缀,组件样式全局生效,避免裸 .box 撞名 */
.gft-box {
position: fixed;
left: 12px;
bottom: 50px;
width: 100upx;
height: 100upx;
left: 24rpx;
bottom: 100rpx;
z-index: 999;
}
.gft-btn {
width: 104rpx;
height: 104rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.box__img {
width: 100upx;
height: 100upx;
/* xuchao: 蓝色投影呼应签名渐变,强化"悬浮"层次 */
box-shadow: 0 12rpx 28rpx rgba(0, 82, 255, 0.35);
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);
}
</style>
+110 -69
View File
@@ -1,27 +1,38 @@
<template>
<view class="auth_rq">
<image class="auth_rq_image" src="https://aliyunoss.hn12301.net/sd/ali_logo.png"></image>
<view class="auth_rq_view">质惠游</view>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 品牌区 —— Logo + 渐变标题 -->
<view class="brand mm-fade-up">
<image class="brand__logo" src="https://aliyunoss.hn12301.net/sd/ali_logo.png"></image>
<view class="brand__name mm-display mm-grad-text">质惠游</view>
</view>
<!-- xuchao: 开发模式提示(正式环境不显示) -->
<view class="auth_rq_tips" v-if="devMode">开发模式:输入测试手机号直接登录</view>
<view class="tips mm-fade-up" v-if="devMode">开发模式:输入测试手机号直接登录</view>
<view class="auth_rq_input" :style="{marginTop: devMode ? '40rpx' : '146rpx'}">
<u-input v-model="value" type="number" :border="false" :custom-style='customStyle' placeholder='请输入手机号' />
</view>
<!-- xuchao: 开发模式免验证码 -->
<view class="auth_rq_input" v-if="!devMode" style="margin-top: 50rpx;">
<u-input v-model="value1" type="number" :border="false" :custom-style='customStyle' placeholder='请输入验证码' />
<text style="margin-left: 30rpx;" :style="{color:isFlag?'#488CFE':'#666'}" @tap="getCode">{{tips}}</text>
</view>
<!-- xuchao: 登录表单 -->
<view class="form mm-fade-up" :style="{marginTop: devMode ? '40rpx' : '96rpx'}">
<view class="field">
<u-input v-model="value" type="number" :border="false" :custom-style='customStyle'
placeholder='请输入手机号' placeholder-class="mm-ph" />
</view>
<!-- xuchao: 开发模式免验证码 -->
<view class="field field--code" v-if="!devMode">
<view class="field__input">
<u-input v-model="value1" type="number" :border="false" :custom-style='customStyle'
placeholder='请输入验证码' placeholder-class="mm-ph" />
</view>
<text :class="['field__code', isFlag ? '' : 'field__code--off']" @tap="getCode">{{tips}}</text>
</view>
<button class="auth_rq_button1" :style="{marginTop: devMode ? '80rpx' : '80rpx'}" @click="doLogin">立即登录</button>
<view class="mm-btn login__btn" @click="doLogin">立即登录</view>
<view style="margin-top: 32rpx;margin-left: 70rpx;">
<u-checkbox-group>
<u-checkbox v-model="checked" shape="circle" style="color: #929292;">我已阅读并同意<text
style="color: #488CFE;" @click="readXy()">用户服务协议</text></u-checkbox>
</u-checkbox-group>
<view class="agree">
<u-checkbox-group>
<u-checkbox v-model="checked" shape="circle" active-color="#0052FF">我已阅读并同意<text
class="agree__link" @click="readXy()">用户服务协议</text></u-checkbox>
</u-checkbox-group>
</view>
</view>
<u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode"
@@ -35,8 +46,12 @@
// 正式流程:微信网页授权(code 换用户信息)+ 短信验证码绑定手机号
import config from '@/common/config.js'
import auth from '@/common/auth.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
devMode: config.devMode,
@@ -47,8 +62,9 @@
value: '',
value1: '',
isFlag: true,
// xuchao: 输入框背景由外层 .field 提供,组件内部保持透明
customStyle: {
background: '#f8f8f8',
background: 'transparent',
}
}
},
@@ -158,60 +174,85 @@
}
}
</script>
<style>
page {
background: #ffffff;
}
</style>
<style lang="less" scoped>
.auth_rq {
.auth_rq_input {
display: flex;
width: 610rpx;
height: 98rpx;
align-items: center;
padding-left: 40rpx;
margin-left: 70rpx;
background-color: #F8F8F8;
border-radius: 8px;
padding-right: 40rpx;
}
.page {
padding: 0 70rpx;
}
.auth_rq_image {
width: 164rpx;
height: 140rpx;
margin-top: 182rpx;
margin-left: 294rpx;
}
/* ---------- 品牌区 ---------- */
.brand {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 140rpx;
}
.auth_rq_view {
width: 750rpx;
text-align: center;
color: #0DD381;
font-size: 36rpx;
margin-top: 18rpx;
}
.brand__logo {
width: 164rpx;
height: 140rpx;
}
// xuchao: 开发模式提示条
.auth_rq_tips {
width: 610rpx;
margin: 40rpx 0 0 70rpx;
padding: 16rpx 24rpx;
box-sizing: border-box;
background-color: #FDF6EC;
color: #E6A23C;
font-size: 24rpx;
border-radius: 8px;
}
.brand__name {
margin-top: 20rpx;
font-size: 52rpx;
letter-spacing: 0.12em;
}
.auth_rq_button1 {
width: 610rpx;
margin-left: 70rpx;
margin-top: 80rpx;
height: 98rpx;
border-radius: 49rpx;
background-color: #488CFE;
color: #fff;
}
/* ---------- 开发模式提示条 ---------- */
.tips {
margin-top: 48rpx;
padding: 16rpx 24rpx;
background: rgba(0, 82, 255, 0.05);
border: 2rpx solid rgba(0, 82, 255, 0.15);
color: var(--mm-accent);
font-size: 24rpx;
border-radius: 16rpx;
text-align: center;
}
/* ---------- 表单 ---------- */
.field {
display: flex;
align-items: center;
height: 96rpx;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 0 40rpx;
transition: box-shadow 0.2s ease;
}
.field--code {
margin-top: 32rpx;
}
.field__input {
flex: 1;
min-width: 0;
}
.field__code {
flex: none;
margin-left: 30rpx;
font-size: 26rpx;
color: var(--mm-accent);
}
.field__code--off {
color: var(--mm-muted-fg);
}
.login__btn {
margin-top: 80rpx;
}
.agree {
margin-top: 32rpx;
color: var(--mm-muted-fg);
font-size: 24rpx;
}
.agree__link {
color: var(--mm-accent);
}
</style>
+427 -256
View File
@@ -1,192 +1,150 @@
<template>
<view v-if="obj">
<view class="detail_h">
<view style="width: 654rpx;display: flex;justify-content: space-between;">
<text style="font-size: 48rpx;font-weight: bold;"
:style="{color:obj.order.orderStatus=='WAITPAY'?'#FF0000':'#000'}">{{orderStatus}}</text>
<view>
<text style="color: #000000;font-size: 20rpx;">¥</text>
<text
style="color: #000000;font-size: 48rpx;font-weight: bold;margin-right: 12rpx;">{{(obj.order.orderAmount/100).toFixed(2)}}</text>
<u-icon name="arrow-right" color="#808080" size="28"></u-icon>
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 状态英雄卡 —— 待支付用主题蓝强调 -->
<view class="hero mm-card mm-fade-up">
<view class="hero__row">
<text :class="['hero__status mm-display', isWaitPay ? 'hero__status--accent' : '']">{{orderStatus}}</text>
<view class="hero__price">
<text class="hero__cur">¥</text>
<text class="hero__num mm-display">{{(obj.order.orderAmount/100).toFixed(2)}}</text>
</view>
</view>
<text style="color:#808080;margin-top: 24rpx;display: block;">{{orderTitle}}</text>
<view class="hero__tip">{{orderTitle}}</view>
</view>
<view class="detail_rq">
<text style="color: #000;line-height: 88rpx;font-size: 36rpx;font-weight: bold;">商品信息</text>
<view style="width: 702rpx;display: flex;justify-content: space-between;">
<text style="color: #000;font-weight: bold;">{{ticketName}}</text>
<u-icon name="arrow-right" color="#808080" size="24"></u-icon>
<!-- xuchao: 商品信息卡 -->
<view class="sec mm-card mm-fade-up">
<view class="mm-sec-title">商品信息</view>
<view class="goods">
<text class="goods__name">{{ticketName}}</text>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">票型类型</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
【{{getSkuType(obj.sku.typeId)}}】&nbsp;{{obj.sku.skuName}}</view>
<view class="kv">
<view class="kv__label">票型类型</view>
<view class="kv__val">【{{getSkuType(obj.sku.typeId)}}】 {{obj.sku.skuName}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">使用日期</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #FF0000;">{{obj.orderGoods.bookDate}} <text
<view class="kv">
<view class="kv__label">使用日期</view>
<view class="kv__val kv__val--accent">{{obj.orderGoods.bookDate}} <text
v-if="obj.orderGoods.timeSlot">({{obj.orderGoods.timeSlot}})</text></view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">生效时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">出票后立即生效</view>
<view class="kv">
<view class="kv__label">生效时间</view>
<view class="kv__val">出票后立即生效</view>
</view>
<view class="rypz" v-if="obj.touristList.length!=0">
<view style="width: 654rpx;text-align: center;display: flex;flex-direction: column;">
<text style="color: #000000;font-size: 32rpx;font-weight: bold;">入园凭证</text>
<scroll-view scroll-x="true" class="xsms-scroll">
<view style="width: 404rpx;display: inline-block;"
:style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}"
v-for="(item,index) in obj.touristList" :key="index">
<view class="rq_item">凭证码</view>
<view
style="width: 404rpx;background: #F8F9FC;display: flex;flex-direction: column;align-items: center;padding-bottom: 24rpx0;border-radius: 0rpx 0rpx 24rpx 24rpx;">
<view class="rq_num">{{item.qrCode}}</view>
<view class="rq_btn" @tap='coyp(item.qrCode)'>复制</view>
<image style="width: 240rpx;height: 240rpx;margin-top: 24rpx;" :src="item.qrCodeBase64">
</image>
<!-- xuchao: 入园凭证(横向滑动,每位游客一张凭证卡) -->
<view class="vch" v-if="obj.touristList.length!=0">
<view class="vch__title">入园凭证</view>
<scroll-view scroll-x="true" class="vch__scroll">
<view class="vch__item" :style="{marginRight:index==obj.touristList.length-1?'0rpx':'24rpx'}"
v-for="(item,index) in obj.touristList" :key="index">
<view class="vch__tag">凭证码</view>
<view class="vch__body">
<view class="vch__num">{{item.qrCode}}</view>
<view class="vch__copy" @tap="coyp(item.qrCode)">复制</view>
<view class="vch__qr">
<image :src="item.qrCodeBase64"></image>
</view>
</view>
</scroll-view>
</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园方式</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
{{obj.sku.enterChangeFlag?'凭('+getTyepStr(obj.sku.enterType)+')换票入园':'无需换票'}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="obj.sku.enterChangeFlag">
<view style="width: 120rpx;color: #808080;">换票时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
{{obj.sku.changeStart}}~{{obj.sku.changeEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="obj.sku.enterChangeFlag">
<view style="width: 120rpx;color: #808080;">换票地点</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.changePostion}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.enterStart}}~{{obj.sku.enterEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="width: 120rpx;color: #808080;">入园地点</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.sku.enterPostion}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">门票</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">
¥{{(obj.orderGoods.skuPrice/100).toFixed(2)}} x {{obj.order.buyNum}}</view>
</view>
<view style="position: relative;height: 96rpx;display: flex;align-items: center;" @click="show1=true">
<text style="color: black;font-weight: bold;display: block;width: 120rpx;">取消政策</text>
<image src="https://aliyunoss.hn12301.net/sd/ticket_dg.png"
style="width: 40rpx;height: 40rpx;margin-left: 12rpx;margin-right: 12rpx;"></image>
<text
style="color: rgba(0, 0, 0, 0.8);">{{obj.sku.refundType==1?'随时退':obj.sku.refundType==2?'有条件退':obj.sku.refundType==3?'不可退款':''}}</text>
<u-icon name="arrow-right" color="#808080" size="24" style="position: absolute;right: 0rpx;"></u-icon>
</view>
</view>
<view class="detail_rq" style="border-top: none;padding-bottom: 24rpx;">
<text style="color: #000;line-height: 88rpx;font-size: 36rpx;font-weight: bold;">订单信息</text>
<view v-if="obj.sku.enterDemand=='2'">
<view style="display: flex;margin-top: 24rpx;" v-for="(item,index) in obj.touristList">
<view style="width: 120rpx;color: #808080;">出行人</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;line-height: 48rpx;">{{item.name}} <br />
{{item.phone}}
<br />{{item.identityNo}}
</view>
</view>
</view>
<view v-if="obj.sku.enterDemand=='1'"
style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">联系人</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.orderContactName}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;border-bottom: 2rpx #E1E1E1 dashed;padding-bottom: 24rpx;">
<view style="width: 120rpx;color: #808080;">手机号</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.orderContactMobile}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;position: relative;">
<view style="width: 120rpx;color: #808080;">订单编号</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;">{{obj.order.id}}</view>
<text style="position: absolute;right: 0rpx;color: #808080;" @tap='coyp(obj.order.id)'>复制</text>
</scroll-view>
</view>
<view style="display: flex;margin-top: 24rpx;position: relative;" v-if="obj.order.orderStatus!='WAITPAY'">
<view style="width: 120rpx;color: #808080;">支付时间</view>
<view style="width: 566rpx;margin-left: 16rpx;color: #000;" v-if="obj.payment">
{{obj.payment.createdTime||''}}
</view>
<view class="kv">
<view class="kv__label">入园方式</view>
<view class="kv__val">{{obj.sku.enterChangeFlag?'凭('+getTyepStr(obj.sku.enterType)+')换票入园':'无需换票'}}</view>
</view>
<view class="kv" v-if="obj.sku.enterChangeFlag">
<view class="kv__label">换票时间</view>
<view class="kv__val">{{obj.sku.changeStart}}~{{obj.sku.changeEnd}}</view>
</view>
<view class="kv" v-if="obj.sku.enterChangeFlag">
<view class="kv__label">换票地点</view>
<view class="kv__val">{{obj.sku.changePostion}}</view>
</view>
<view class="kv">
<view class="kv__label">入园时间</view>
<view class="kv__val">{{obj.sku.enterStart}}~{{obj.sku.enterEnd}}</view>
</view>
<view class="kv">
<view class="kv__label">入园地点</view>
<view class="kv__val">{{obj.sku.enterPostion}}</view>
</view>
<view class="kv kv--dash">
<view class="kv__label">门票</view>
<view class="kv__val">¥{{(obj.orderGoods.skuPrice/100).toFixed(2)}} x {{obj.order.buyNum}}</view>
</view>
<!-- xuchao: 取消政策 —— 点击弹层查看条款 -->
<view class="policy" @click="show1=true">
<text class="policy__label">取消政策</text>
<u-icon name="question-circle" color="#0052FF" size="28"></u-icon>
<text
class="policy__val">{{obj.sku.refundType==1?'随时退':obj.sku.refundType==2?'有条件退':obj.sku.refundType==3?'不可退款':''}}</text>
<u-icon name="arrow-right" color="#CBD5E1" size="24" class="policy__arrow"></u-icon>
</view>
</view>
<view style="width: 750rpx;height: 120rpx;"></view>
<view class="detail_btn">
<!-- <view class="tozf">再次购买</view> -->
<view class="tozf" v-if="obj.order.orderStatus=='WAITPAY'" @tap='toPay()'>去支付</view>
<view class="toqx" v-if="obj.order.orderStatus=='WAITPAY'" @tap='cancel()'>取消订单</view>
<view class="toqx" @tap='aginPlay()'
v-if="obj.order.orderStatus=='HAVESEND'||obj.order.orderStatus=='COMPLETED'||obj.order.orderStatus=='BACKPAYED'">
重新购买</view>
<view class="toqx" v-if="obj.order.orderStatus=='HAVESEND'" @tap='refund()'>申请退款</view>
<!-- xuchao: 订单信息卡 -->
<view class="sec mm-card mm-fade-up">
<view class="mm-sec-title">订单信息</view>
<template v-if="obj.sku.enterDemand=='2'">
<view class="kv" v-for="(item,index) in obj.touristList" :key="index">
<view class="kv__label">出行人</view>
<view class="kv__val">{{item.name}} {{item.phone}} {{item.identityNo}}</view>
</view>
</template>
<view class="kv kv--dash" v-if="obj.sku.enterDemand=='1'">
<view class="kv__label">联系人</view>
<view class="kv__val">{{obj.order.orderContactName}}</view>
</view>
<view class="kv kv--dash">
<view class="kv__label">手机号</view>
<view class="kv__val">{{obj.order.orderContactMobile}}</view>
</view>
<view class="kv">
<view class="kv__label">订单编号</view>
<view class="kv__val">{{obj.order.id}}</view>
<text class="kv__copy" @tap="coyp(obj.order.id)">复制</text>
</view>
<view class="kv" v-if="obj.order.orderStatus!='WAITPAY'">
<view class="kv__label">支付时间</view>
<view class="kv__val" v-if="obj.payment">{{obj.payment.createdTime||''}}</view>
</view>
</view>
<u-popup v-model="show1" border-radius='20' mode='bottom'>
<view style="padding:48rpx;display: flex;flex-direction: column;">
<text style="color: #000;font-size: 36rpx;font-weight: bold;">取消政策</text>
<!-- xuchao: 底部按钮区预留(有操作按钮时才占位) -->
<view class="safe" v-if="hasActs"></view>
<!-- xuchao: 底部操作栏 -->
<view class="bar" v-if="hasActs">
<view class="bar__ghost" v-if="obj.order.orderStatus=='HAVESEND'" @tap="refund()">申请退款</view>
<view class="bar__ghost" @tap="aginPlay()"
v-if="obj.order.orderStatus=='HAVESEND'||obj.order.orderStatus=='COMPLETED'||obj.order.orderStatus=='BACKPAYED'">重新购买</view>
<view class="bar__ghost" v-if="obj.order.orderStatus=='WAITPAY'" @tap="cancel()">取消订单</view>
<view class="bar__main mm-grad" v-if="obj.order.orderStatus=='WAITPAY'" @tap="toPay()">去支付</view>
</view>
<!-- <view
style="height: 88rpx;display: flex;align-items: center;position: relative;border-top: 1px dashed #E1E1E1;margin-top: 24rpx;">
<image src="https://aliyunoss.hn12301.net/sd/ticket_dg.png"
style="width: 40rpx;height: 40rpx;margin-right: 12rpx;"></image>
<text style="color: rgba(0, 0, 0, 0.8);">随时退</text>
</view>
<view style="position: relative;color: rgba(0, 0, 0, 0.5);">
该商品支持随时退,经核实未使用后,可免费取消。
</view> -->
<u-parse style="margin-top: 24rpx;" :html="obj.sku.refundPolicy"></u-parse>
<view style="width: 654rpx;height: 120rpx;"></view>
<view
style="width: 654rpx;position: fixed;height: 88rpx;line-height: 88rpx;background: #fff;bottom: 0rpx;left: 0rpx;right: 0rpx;margin: 0 auto;border-bottom: constant(safe-area-inset-bottom) solid #fff;border-bottom: env(safe-area-inset-bottom) solid #fff;">
<u-button shape="circle" size="default"
style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;"
@click="show1=false">确定</u-button>
</view>
<!-- xuchao: 取消政策弹层 -->
<u-popup v-model="show1" border-radius="20" mode="bottom">
<view class="pp mm-safe-bottom">
<view class="mm-sec-title">取消政策</view>
<u-parse class="pp__parse" :html="obj.sku.refundPolicy"></u-parse>
<view class="mm-btn pp__btn" @click="show1=false">确定</view>
</view>
</u-popup>
<u-popup v-model="show" mode='bottom' border-radius='20'>
<view style="padding: 48rpx;">
<view style="width: 654rpx;text-align: center;font-size: 30rpx;font-weight: bold;">请选择游客</view>
<u-checkbox-group :wrap='true'>
<u-checkbox v-model="item.checked" v-for="(item, index) in list" :key="index"
:name="item.name">{{item.name}}</u-checkbox>
<!-- xuchao: 申请退款 —— 选择游客弹层 -->
<u-popup v-model="show" mode="bottom" border-radius="20">
<view class="pp mm-safe-bottom">
<view class="pp__center-title">请选择游客</view>
<u-checkbox-group :wrap="true">
<u-checkbox v-model="item.checked" v-for="(item, index) in list" :key="index" :name="item.name"
active-color="#0052FF">{{item.name}}</u-checkbox>
</u-checkbox-group>
<u-button shape="circle" size="default"
style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;margin-top: 24rpx;"
@click="sure()">确定</u-button>
<view class="mm-btn pp__btn" @click="sure()">确定</view>
</view>
</u-popup>
</view>
@@ -200,7 +158,11 @@
import {
getSkuType
} from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
enterTypeList: [{
@@ -228,7 +190,7 @@
label: '6'
},
{
value: "台湾居民来往大陆通行证",
value: "台湾居民来往内地通行证",
label: '7'
}
],
@@ -294,6 +256,16 @@
}
return str
},
// xuchao: 待支付状态用主题蓝强调
isWaitPay() {
return this.obj && this.obj.order.orderStatus == 'WAITPAY'
},
// xuchao: 是否存在底部操作按钮(决定操作栏与占位是否渲染)
hasActs() {
if (!this.obj) return false
const s = this.obj.order.orderStatus
return s == 'WAITPAY' || s == 'HAVESEND' || s == 'COMPLETED' || s == 'BACKPAYED'
}
},
onLoad(e) {
@@ -410,114 +382,313 @@
}
}
</script>
<style>
page {
background: #F8F9FC;
}
</style>
<style scoped lang="less">
.xsms-scroll {
.page {
padding: 32rpx 32rpx 40rpx;
}
/* ---------- 状态英雄卡 ---------- */
.hero {
padding: 32rpx;
}
.hero__row {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.hero__status {
font-size: 44rpx;
line-height: 1.1;
color: var(--mm-fg);
}
.hero__status--accent {
color: var(--mm-accent);
}
.hero__price {
display: flex;
align-items: baseline;
}
.hero__cur {
font-size: 24rpx;
color: var(--mm-fg);
margin-right: 2rpx;
}
.hero__num {
font-size: 48rpx;
line-height: 1;
color: var(--mm-fg);
}
.hero__tip {
margin-top: 16rpx;
font-size: 24rpx;
line-height: 1.6;
color: var(--mm-muted-fg);
}
/* ---------- 内容卡片 ---------- */
.sec {
margin-top: 24rpx;
padding: 32rpx 24rpx;
}
.goods {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
}
.goods__name {
flex: 1;
min-width: 0;
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
/* xuchao: 键值行 —— 标签 + 取值,kv--dash 带虚线分隔 */
.kv {
display: flex;
margin-top: 24rpx;
}
.kv--dash {
border-bottom: 2rpx dashed var(--mm-border);
padding-bottom: 24rpx;
}
.kv__label {
flex: none;
width: 120rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.kv__val {
flex: 1;
min-width: 0;
margin-left: 16rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-fg);
}
.kv__val--accent {
color: var(--mm-accent);
font-weight: 500;
}
.kv__copy {
flex: none;
margin-left: 16rpx;
font-size: 24rpx;
color: var(--mm-accent);
}
/* ---------- 入园凭证 ---------- */
.vch {
margin-top: 28rpx;
border: 2rpx dashed var(--mm-border);
border-radius: 20rpx;
padding: 24rpx;
}
.vch__title {
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: var(--mm-fg);
}
.vch__scroll {
overflow: hidden;
white-space: nowrap;
margin-top: 24rpx;
width: 654rpx;
width: 100%;
}
.rypz {
width: 702rpx;
border: 1px dashed #E1E1E1;
.vch__item {
width: 404rpx;
display: inline-block;
}
.vch__tag {
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 82, 255, 0.08);
color: var(--mm-accent);
font-size: 26rpx;
font-weight: 600;
letter-spacing: 0.1em;
border-radius: 20rpx 20rpx 0 0;
}
.vch__body {
display: flex;
flex-direction: column;
align-items: center;
background: var(--mm-muted);
border-radius: 0 0 20rpx 20rpx;
padding-bottom: 32rpx;
}
.vch__num {
width: 356rpx;
margin-top: 24rpx;
padding: 24rpx;
.rq_item {
width: 404rpx;
height: 64rpx;
border-radius: 20rpx 20rpx 0rpx 0rpx;
background: #EEEFF3;
color: #000;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.rq_num {
width: 356rpx;
margin: 24rpx;
color: #000;
font-weight: bold;
white-space: normal;
overflow-wrap: break-word;
}
.rq_btn {
color: rgba(0, 0, 0, 0.8);
width: 72rpx;
height: 36rpx;
font-size: 20rpx;
border-radius: 36rpx;
border: 1px solid #E1E1E1;
display: flex;
align-items: center;
justify-content: center;
}
font-size: 28rpx;
font-weight: 600;
letter-spacing: 0.04em;
text-align: center;
white-space: normal;
overflow-wrap: break-word;
color: var(--mm-fg);
}
.tozf {
height: 64rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 64rpx;
color: #fff;
font-weight: bold;
padding: 0rpx 24rpx;
.vch__copy {
margin-top: 16rpx;
width: 88rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid rgba(0, 82, 255, 0.4);
border-radius: 999rpx;
font-size: 22rpx;
color: var(--mm-accent);
}
.toqx {
height: 64rpx;
background: rgba(0, 0, 0, 0.1);
border-radius: 64rpx;
color: #000;
font-weight: bold;
padding: 0rpx 24rpx;
display: flex;
align-items: center;
}
.detail_btn {
width: 750rpx;
height: 88rpx;
display: flex;
align-items: center;
background-color: #fff;
position: fixed;
bottom: 0rpx;
// xuchao: 视口水平居中(宽屏下与页面列对齐)+ 全面屏底部安全区
left: 0rpx;
right: 0rpx;
margin-left: auto;
margin-right: auto;
border-bottom: constant(safe-area-inset-bottom) solid #fff;
border-bottom: env(safe-area-inset-bottom) solid #fff;
gap: 20rpx;
padding-right: 24rpx;
justify-content: flex-end;
}
.detail_h {
margin: 48rpx 24rpx 0rpx 24rpx;
.vch__qr {
margin-top: 24rpx;
padding: 12rpx;
background: #fff;
border-radius: 20rpx 20rpx 0rpx 0rpx;
padding: 24rpx;
width: 702rpx;
border-radius: 12rpx;
}
.detail_rq {
border-top: 1px #E1E1E1 solid;
background-color: #fff;
padding: 24rpx 24rpx 0rpx 24rpx;
.vch__qr image {
width: 240rpx;
height: 240rpx;
display: block;
}
/* ---------- 取消政策入口 ---------- */
.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>
+157 -33
View File
@@ -1,25 +1,52 @@
<template>
<view>
<view class="item" v-for="(item,index) in lookList" :key="index" @tap='toOrderDetail(item.id)'>
<view class="item_h">
<text>订单编号:{{item.id}}</text>
<text>{{getText(item.orderStatus,1)}}</text>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 页头徽章(自定义导航无标题栏,补一个页面标识) -->
<view class="head mm-fade-up">
<view class="mm-badge">
<view class="mm-badge__dot"></view>
<text class="mm-badge__text">门票订单</text>
</view>
<view class="item_rq">
<view style="font-weight: bold;color: #000;">{{item.skuName}}</view>
<view style="width: 606rpx;display: flex;justify-content: space-between;color: rgba(0, 0, 0, 0.8);margin-top: 24rpx;">
<text>使用日期:{{item.orderDate}}</text>
<text>¥{{(item.orderAmount/100).toFixed(2)}}</text>
</view>
<!-- xuchao: 订单卡片列表 —— 整卡可点进详情 -->
<view class="list mm-stagger">
<view class="order mm-card mm-fade-up" v-for="(item,index) in lookList" :key="index"
@tap="toOrderDetail(item.id)">
<view class="order__head">
<text class="order__no">订单编号:{{item.id}}</text>
<text :class="['order__status', statusClass(item.orderStatus)]">{{getText(item.orderStatus,1)}}</text>
</view>
<view class="order__body">
<view class="order__name">{{item.skuName}}</view>
<view class="order__foot">
<text class="order__date">使用日期:{{item.orderDate}}</text>
<view class="order__price">
<text class="order__cur">¥</text>
<text class="order__num mm-display mm-grad-text">{{(item.orderAmount/100).toFixed(2)}}</text>
</view>
</view>
</view>
</view>
</view>
<u-loadmore :status="status" />
<!-- xuchao: 空态(首次请求返回空列表才显示) -->
<view class="empty mm-fade-up" v-if="showEmpty">
<u-icon name="empty-order" color="#CBD5E1" size="120"></u-icon>
<view class="empty__text">暂无相关订单</view>
</view>
<u-loadmore v-if="lookList.length > 0" :status="status" />
</view>
</template>
<script>
var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default{
components: {
'gmy-back-touch': gmyBackTouch
},
data(){
return{
status: 'loadmore',
@@ -33,6 +60,8 @@
pageAll:2,
isLoad:true,
lookList:[],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
orderList:[{
text:'待支付',
value:'WAITPAY',
@@ -64,6 +93,11 @@
}]
}
},
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad(){
that=this
this.obj.orderPhone=uni.getStorageSync('phone')
@@ -93,11 +127,19 @@
return str
}
},
// xuchao: 订单状态 → 徽章色调(待办类用主题蓝强调,终态用中性灰)
statusClass(e) {
if (['WAITPAY', 'WAITSEND', 'REFUNDING'].indexOf(e) > -1) {
return 'order__status--active'
}
return ''
},
queryTicketsOrderPageMethod() {
if (that.obj.pageNum <= that.pageAll&&this.isLoad) {
this.isLoad=false
this.$u.api.queryTicketsOrderPage(this.obj).then(res => {
this.isLoad=true
this.firstLoaded = true
that.obj.pageNum = that.obj.pageNum * 1 + 1;
that.pageAll = Math.ceil(res.data.total / 10)
if (that.page == 2) {
@@ -120,29 +162,111 @@
}
</script>
<style>
page{
background: #F8F9FC;
padding-top: 24rpx;
}
</style>
<style lang="less" scoped>
.item{
width: 750rpx;
margin-bottom: 20rpx;
.page {
padding: 32rpx 32rpx 40rpx;
}
.head {
display: flex;
margin-bottom: 32rpx;
}
.list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.order {
padding: 24rpx;
background: #fff;
.item_h{
width: 702rpx;
display: flex;
justify-content: space-between;
color: rgba(0, 0, 0, 0.5);
}
.item_rq{
width: 654rpx;
margin: 24rpx 24rpx 0rpx 24rpx;
background: #F8F9FC;
padding: 24rpx;
}
transition: transform 0.2s ease-out, box-shadow 0.3s ease-out;
}
.order:active {
transform: scale(0.985);
box-shadow: var(--mm-shadow-sm);
}
.order__head {
display: flex;
align-items: center;
justify-content: space-between;
}
.order__no {
font-size: 22rpx;
letter-spacing: 0.02em;
color: var(--mm-muted-fg);
}
.order__status {
flex: none;
font-size: 22rpx;
color: var(--mm-muted-fg);
background: var(--mm-muted);
border-radius: 999rpx;
padding: 4rpx 20rpx;
}
/* xuchao: 待办类状态(待支付/出票中/退款中)—— 主题蓝强调 */
.order__status--active {
color: var(--mm-accent);
background: rgba(0, 82, 255, 0.08);
}
.order__body {
margin-top: 20rpx;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 24rpx;
}
.order__name {
font-size: 30rpx;
font-weight: 600;
color: var(--mm-fg);
}
.order__foot {
margin-top: 16rpx;
display: flex;
align-items: baseline;
justify-content: space-between;
}
.order__date {
font-size: 24rpx;
color: var(--mm-muted-fg);
}
.order__price {
display: flex;
align-items: baseline;
}
.order__cur {
font-size: 24rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 2rpx;
}
.order__num {
font-size: 40rpx;
line-height: 1;
}
.empty {
margin-top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.empty__text {
font-size: 26rpx;
color: var(--mm-muted-fg);
}
</style>
+558 -306
View File
@@ -1,151 +1,137 @@
<template>
<view v-if="skuObj">
<!-- <view style="position: fixed;top: 0px;width: 750rpx;z-index: 1000;" :style="{height:windowHeight+'px'}">
<view style="width: inherit;" :style="{height:windowHeight-62+'px'}"></view> -->
<view class="db_rq" :style="{top:windowHeight-62+'px'}">
<view>
<text style="color: rgba(0, 0, 0, 0.5);font-size: 20rpx;">总价</text>
<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 v-if="skuObj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 商品与日期/时段/数量选择卡 -->
<view class="sec mm-card mm-fade-up">
<view class="sku__name">{{skuObj.sku.skuName}}</view>
<view class="sku__age">适用年龄:{{skuObj.sku.typeInfo}}</view>
<view style="margin-bottom: 10rpx;display: flex;align-items: center;gap: 10rpx;color: #457AF7;">
明细<u-icon name="arrow-down-fill" size='20'></u-icon></view>
</view>
</view>
<view class="divider"></view>
<view class="bt" @tap="buy">立即购买</view>
</view>
<!-- </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 class="label">使用日期</view>
<view class="dates">
<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 />
{{formatToMonthDay(item.enableDate)}}
<view class="jt_tag">{{item.isSelect?'√':item.isEnabled?'可订':'不可订'}}</view>
<view class="date__tag">{{item.isSelect?'√':item.isEnabled?'可订':'不可订'}}</view>
</view>
<view class="gd_rq" @tap="show=true">更多<br />
日期
<u-icon name="arrow-right" style="margin-left: 5rpx;"></u-icon>
<view class="date date--more" @tap="show=true">更多<br />日期
<u-icon name="arrow-right" color="#64748B" size="22"></u-icon>
</view>
</view>
<view style="margin-top: 24rpx;color: rgba(0, 0, 0, 0.5);font-size: 500;" v-if="!skuObj.sku.stockDateType">
适用时段</view>
<view style="width: 702rpx;display: flex;gap: 12rpx;margin-top: 12rpx;flex-wrap: wrap;"
v-if="!skuObj.sku.stockDateType">
<view :class="item.isSelect?'xz_item':'nx_item' " v-for="(item,index) in timeSlotList" :key="index"
@click="selectSlot(index)">{{item.name}}
<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>
<template v-if="!skuObj.sku.stockDateType">
<view class="label">适用时段</view>
<view class="slots">
<view :class="['slot', item.isSelect?'slot--on':'']" v-for="(item,index) in timeSlotList"
:key="index" @click="selectSlot(index)">{{item.name}}
<view class="slot__tag">余{{item.stock}}张</view>
</view>
</view>
<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>
</template>
<view class="divider"></view>
<!-- xuchao: 购买数量 —— 自定义步进器:数字居中,加减号点击即加减,上限取当前日期/时段库存 -->
<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 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>
</view>
</template>
@@ -154,7 +140,11 @@
import pay from '@/common/pay.js'
var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
show1: false,
@@ -167,7 +157,6 @@
whday: '',
skuObj: '',
calendarList: [],
windowHeight: '',
mobile: '',
personList: [],
objInfo: '',
@@ -282,11 +271,6 @@
},
onLoad() {
that = this
uni.getSystemInfo({
success: function(res) {
that.windowHeight = res.windowHeight
}
});
this.createInfo.mobile = uni.getStorageSync('phone')
this.createInfo.contactMobile = uni.getStorageSync('phone')
this.skuObj = uni.getStorageSync('scenicDetail')
@@ -414,8 +398,20 @@
const day = date.getDate().toString().padStart(2, '0');
return `${month}月${day}日`;
},
valChange(e) {
console.log('当前值为: ' + e.value)
// xuchao: 数量步进 —— 减到下限 1 为止,加到当前库存上限并提示
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) {
this.calendarList.forEach((item, index) => {
@@ -446,6 +442,15 @@
});
return
}
// xuchao: 出行人多于购买数量也拦截(如先选多人后减量),提示移除多余位
if (this.personList.length > this.value) {
uni.showToast({
icon: 'none',
title: '只需' + this.value + '位出行人,请移除多余' + (this.personList.length - this.value) + '位',
duration: 2000
});
return
}
} else {
if (this.createInfo.contactName == '') {
uni.showToast({
@@ -532,204 +537,451 @@
}
}
</script>
<style>
page {
background: #F8F9FC;
}
</style>
<style scoped lang="less">
.bt {
width: 288rpx;
height: 100rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 100rpx;
/* xuchao: 底部预留 200rpx,避免内容被固定结算栏遮挡 */
.page {
padding: 32rpx 32rpx 200rpx;
}
.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;
align-items: center;
justify-content: center;
font-size: 36rpx;
color: #fff;
gap: 8rpx;
margin-top: 16rpx;
}
.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;
right: 24rpx;
bottom: 14rpx;
font-weight: bold;
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;
}
.db_rq {
width: 750rpx;
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;
.date--on .date__tag {
background: var(--mm-grad);
color: #fff;
border-radius: 4rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
}
.nx_name {
height: 56rpx;
padding: 0rpx 24rpx;
line-height: 56rpx;
color: rgba(0, 0, 0, 0.5);
border-radius: 4rpx;
background: #F8F9FC;
border: 1px solid rgba(0, 0, 0, 0.1);
.date--more {
flex: none;
width: 96rpx;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 22rpx;
line-height: 1.6;
}
.nx_item {
width: 226rpx;
/* ---------- 时段 chips ---------- */
.slots {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
margin-top: 16rpx;
}
.slot {
width: 210rpx;
height: 110rpx;
border-radius: 8rpx;
border: 1px solid #E1E1E1;
color: rgba(0, 0, 0, 0.5);
border: 2rpx solid var(--mm-border);
border-radius: 16rpx;
color: var(--mm-muted-fg);
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.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;
}
box-sizing: border-box;
}
.xz_item {
width: 226rpx;
height: 110rpx;
border-radius: 8rpx;
border: 1px solid #F66C31;
color: #F66C31;
.slot--on {
border-color: var(--mm-accent);
background: rgba(0, 82, 255, 0.04);
color: var(--mm-accent);
}
.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;
align-items: center;
justify-content: center;
position: relative;
.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;
}
transition: transform 0.15s ease;
}
.gd_rq {
width: 120rpx;
height: 170rpx;
border: 1px solid #E1E1E1;
border-radius: 8rpx;
color: rgba(0, 0, 0, 0.5);
.stepper__btn:active {
transform: scale(0.92);
background: var(--mm-muted);
}
.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;
align-items: 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 {
width: 186rpx;
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;
}
.bar__buy:active {
transform: scale(0.96);
}
.jt_rq {
width: 186rpx;
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;
.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 {
padding: 48rpx;
display: flex;
flex-direction: column;
}
.pp__sub {
margin-top: 24rpx;
font-size: 28rpx;
font-weight: 600;
color: var(--mm-fg);
}
.mt_rq {
width: 186rpx;
height: 170rpx;
border: 1px solid #F66C31;
border-radius: 8rpx;
color: #F66C31;
position: relative;
line-height: 24px;
padding-top: 48rpx;
padding-left: 24rpx;
.pp__row {
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2rpx dashed var(--mm-border);
}
.jt_tag {
font-size: 16rpx;
height: 30rpx;
display: flex;
align-items: center;
padding: 0rpx 10rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 0px 8rpx 0px 8rpx;
position: absolute;
color: #fff;
top: 0rpx;
right: 0rpx;
}
.pp__row-label {
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.pp__row-val {
font-size: 26rpx;
color: var(--mm-fg);
}
.pp__total {
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>
+98 -73
View File
@@ -1,31 +1,36 @@
<template>
<view v-if="obj">
<view class="od_top">
<view class="od_top_first">
<image src="http://cdn.hntgov.net/online/static/ps_ps.png"></image>支付成功
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 成功状态英雄区 —— 渐变对勾 + 柔光 -->
<view class="hero mm-fade-up">
<view class="hero__icon mm-grad">
<u-icon name="checkmark" color="#ffffff" size="64"></u-icon>
</view>
<view class="hero__title mm-display">支付成功</view>
<view class="hero__sub">门票订单已提交,请留意出票通知</view>
</view>
<view style="background-color: #fff;">
<view class="od_sh">
<image style="width: 32rpx;height: 28rpx;" src="http://cdn.hntgov.net/online/static/pas_ps.png">
</image>
<view class="od_sh_right">
<!-- xuchao: 支付方式改为微信支付 -->
<text style="color: #696969;">支付方式:<text style="color: #3D3D3D;">微信支付</text></text>
<text style="color: #696969;padding-top: 16rpx;">实付金额:<text
style="color: #3D3D3D;">¥{{(obj.order.orderAmount/100).toFixed(2)||0}}</text></text>
<!-- xuchao: 支付信息卡 -->
<view class="card mm-card mm-fade-up">
<view class="row">
<text class="row__label">支付方式</text>
<text class="row__val">微信支付</text>
</view>
<view class="row">
<text class="row__label">实付金额</text>
<view class="row__price">
<text class="row__cur">¥</text>
<text class="row__num mm-display mm-grad-text">{{(obj.order.orderAmount/100).toFixed(2)||0}}</text>
</view>
</view>
<view class="od_line"></view>
<view class="ps_item">
<!-- xuchao: 接通"查看订单"入口(旧版此按钮被注释成了死页面) -->
<view class="ps_item_item" style="color: #181617;" @tap='seeOrder()'>查看订单</view>
<view style="width: 1rpx;height: 48rpx;background-color: #E7E6E6;"></view>
<view class="ps_item_item" style="color: #ED2D38;" @tap='toMall()'>继续逛逛</view>
</view>
</view>
<!-- xuchao: 操作按钮 —— 主操作渐变、次操作描边 -->
<view class="acts mm-fade-up">
<view class="mm-btn" @tap="seeOrder()">查看订单</view>
<view class="mm-btn-ghost" @tap="toMall()">继续逛逛</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
@@ -33,7 +38,11 @@
<script>
// xuchao: 支付成功页 —— 支付完成后跳入,轮询订单状态直到后端确认
var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
id: '',
@@ -90,74 +99,90 @@
}
</script>
<style>
page {
background-color: #f7f7f7;
}
</style>
<style scoped lang="scss">
.ps_item {
width: 690rpx;
margin-left: 30rpx;
height: 96rpx;
align-items: center;
display: flex;
.ps_item_item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: bold;
}
<style scoped lang="less">
.page {
padding: 96rpx 32rpx 60rpx;
}
.od_top {
width: 750rpx;
height: 210rpx;
background: linear-gradient(90deg, #FF3F3C -8%, #FFA371 107%);
padding-top: 56rpx;
padding-left: 70rpx;
/* ---------- 英雄区 ---------- */
.hero {
display: flex;
flex-direction: column;
align-items: center;
}
.od_top_first {
.hero__icon {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
display: flex;
align-items: center;
color: #FFF;
font-size: 34rpx;
justify-content: center;
box-shadow: var(--mm-shadow-accent), 0 0 0 24rpx rgba(0, 82, 255, 0.06);
}
.od_top_first image {
width: 50rpx;
height: 50rpx;
margin-right: 20rpx;
.hero__title {
margin-top: 36rpx;
font-size: 48rpx;
color: var(--mm-fg);
}
.od_sh {
padding: 30rpx;
.hero__sub {
margin-top: 12rpx;
font-size: 24rpx;
color: var(--mm-muted-fg);
}
/* ---------- 支付信息卡 ---------- */
.card {
margin-top: 64rpx;
padding: 8rpx 32rpx;
}
.row {
display: flex;
align-items: center;
font-size: 26rpx;
background-color: #fff;
justify-content: space-between;
height: 104rpx;
.od_sh_right {
width: 640rpx;
margin-left: 18rpx;
display: flex;
flex-direction: column;
&:first-child {
border-bottom: 2rpx solid var(--mm-muted);
}
}
.od_line {
width: 690rpx;
height: 1rpx;
background-color: #E7E6E6;
margin-left: 30rpx;
.row__label {
font-size: 28rpx;
color: var(--mm-muted-fg);
}
.row__val {
font-size: 28rpx;
font-weight: 500;
color: var(--mm-fg);
}
.row__price {
display: flex;
align-items: baseline;
}
.row__cur {
font-size: 26rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 4rpx;
}
.row__num {
font-size: 44rpx;
line-height: 1;
}
/* ---------- 操作按钮 ---------- */
.acts {
margin-top: 64rpx;
display: flex;
flex-direction: column;
gap: 24rpx;
}
</style>
+36 -30
View File
@@ -1,35 +1,40 @@
<template>
<view>
<u-form :model="form" :rules="rules" ref="uForm">
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;">
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 表单统一收进一张卡片,分隔线由 u-form-item 自带(已随主题换色) -->
<view class="card mm-card mm-fade-up">
<u-form :model="form" :rules="rules" ref="uForm">
<u-form-item label-width="200" :label-position="labelPosition" label="出行人姓名" prop="name">
<u-input placeholder="请输入出行人姓名" v-model="form.name" type="text"></u-input>
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label-width="200" :label-position="labelPosition" label="联系号码" prop="phone">
<u-input placeholder="请输入联系号码" v-model="form.phone" type="text"></u-input>
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label="证件类型" prop="idType" label-width="200" :label-position="labelPosition">
<u-input v-model="idTypeName" placeholder='请选择证件类型' type="select" @click="idTypeShow()" />
</u-form-item>
</view>
<view style="background-color: #fff;padding-left: 30rpx;padding-right: 30rpx;margin-top: 20rpx;">
<u-form-item label-width="200" :label-position="labelPosition" label="证件号码" prop="identityNo">
<u-input placeholder="请输入证件号码" v-model="form.identityNo" type="text"></u-input>
</u-form-item>
</view>
</u-form>
<view class="pp_hh" @click="submit()">完成</view>
</u-form>
</view>
<!-- xuchao: 底部固定提交按钮(视口水平居中,宽屏下与页面列对齐) -->
<view class="bar">
<view class="mm-btn" @click="submit()">完成</view>
</view>
<u-action-sheet :list="actionSheetList" v-model="show" @click="actionSheetCallback"></u-action-sheet>
</view>
</template>
<script>
var that = ''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
show: false,
@@ -54,7 +59,7 @@
},
{
value: 'TAIWAN',
text: '台湾居民来往大陆通行证'
text: '台湾居民来往内地通行证'
}
],
form: {
@@ -198,23 +203,24 @@
</script>
<style scoped lang="less">
.pp_hh {
width: 702rpx;
height: 88rpx;
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 28rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
// xuchao: 视口水平居中(宽屏下与页面列对齐)
left: 0rpx;
right: 0rpx;
margin: 0 auto;
/* xuchao: 底部预留 220rpx,避免表单被固定提交按钮遮挡 */
.page {
padding: 32rpx 32rpx 220rpx;
}
.card {
padding: 12rpx 32rpx;
}
/* xuchao: 底部固定按钮 —— left/right 0 + margin auto 保证宽屏居中时与页面列对齐 */
.bar {
position: fixed;
// xuchao: 全面屏底部安全区 —— 悬浮按钮整体上移
bottom: calc(25rpx + constant(safe-area-inset-bottom));
bottom: calc(25rpx + env(safe-area-inset-bottom));
left: 0;
right: 0;
margin: 0 auto;
width: 686rpx;
bottom: calc(32rpx + constant(safe-area-inset-bottom));
bottom: calc(32rpx + env(safe-area-inset-bottom));
z-index: 99;
}
</style>
+176 -210
View File
@@ -1,22 +1,46 @@
<template>
<view>
<view class='nr_item' v-for="(item,index) in lookList" :key="index" @click="toDetail(item.id)">
<image class="nr_img" :src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image>
<view class="nr_item_r">
<text class="n_text">{{item.name||''}}</text>
<view class="nr_tags">
<text :class="index1==0?'t_text1':index1==1?'t_text':index1==2?'t_text2':'t_text3'"
v-for="(item1,index1) in ['节日福利/春游秋游','余额券','满减券']" :key="index1">{{item1}}</text>
<view class="page mm-page">
<!-- xuchao: 景区卡片列表 —— 整卡可点进详情,入场逐个渐显 -->
<view class="list mm-stagger">
<view class="card mm-card mm-fade-up" v-for="(item,index) in lookList" :key="index"
@click="toDetail(item.id)">
<image class="card__img" mode="aspectFill"
:src="item.mainPic||'https://aliyunoss.hn12301.net/sd/all_moren.png'"></image>
<view class="card__body">
<view class="card__name">{{item.name||''}}</view>
<view class="card__desc" v-if="item.subTypeId">{{item.subTypeId}}</view>
<view class="card__foot">
<view class="card__price">
<view class="card__origin">原价 <text
class="card__origin-num">¥{{(item.price/100).toFixed(0)}}</text></view>
<view class="card__now">
<text class="card__cur">¥</text>
<text class="card__num mm-display mm-grad-text">{{(item.unitPrice/100).toFixed(0)}}</text>
<text class="card__unit">元</text>
</view>
</view>
<view class="card__cta mm-grad">
<text>预订</text>
<u-icon name="arrow-right" color="#ffffff" size="22"></u-icon>
</view>
</view>
</view>
<view class="r_text">{{item.subTypeId}}</view>
<view class="nr_origin">原售价<text
class="nr_origin_num">{{(item.price/100).toFixed(0)}}</text>元</view>
<view class="p_text"><text class="p_label">会员补贴价</text><text class="p_symbol">¥</text><text
class="p_num">{{(item.unitPrice/100).toFixed(0)}}</text><text class="p_unit">元</text></view>
</view>
</view>
<!-- xuchao: 空态(首次请求返回空列表才显示,避免加载中闪烁) -->
<view class="empty mm-fade-up" v-if="showEmpty">
<u-icon name="empty-coupon" color="#CBD5E1" size="120"></u-icon>
<view class="empty__text">暂无在售门票产品</view>
</view>
<!-- xuchao: 列表底部:仅分页加载时显示"正在加载…" -->
<view class="foot" v-if="lookList.length > 0 && !isLoad">
<text>正在加载…</text>
</view>
<!-- xuchao: 订单悬浮按钮 -->
<gmy-float-touch :mainImg="mainImg" @menuClick="floatTouchClick"></gmy-float-touch>
<gmy-float-touch @menuClick="floatTouchClick"></gmy-float-touch>
</view>
</template>
@@ -30,10 +54,11 @@
components: { gmyFloatTouch },
data() {
return {
mainImg: "../../static/logo4.png",
pageAll: 2,
isLoad: true,
lookList: [],
// xuchao: 首次请求是否已返回,用于区分"加载中"与"真没数据"
firstLoaded: false,
obj: {
"name": "",
"typeId": "",
@@ -44,6 +69,11 @@
}
}
},
computed: {
showEmpty() {
return this.firstLoaded && this.lookList.length === 0
}
},
onLoad() {
that = this
// xuchao: 未登录则(正式)跳登录页 / (开发)静默假登录
@@ -59,8 +89,10 @@
this.isLoad = false
this.$u.api.queryTicketsPage(this.obj).then(res => {
this.isLoad = true
this.firstLoaded = true
this.obj.pageNum = this.obj.pageNum * 1 + 1
this.pageAll = Math.ceil(res.data.total / 10)
this.total = res.data.total
this.lookList = this.lookList.concat(res.data.list)
})
}
@@ -82,209 +114,143 @@
}
</script>
<style>
page {
background: #FFEFD5;
}
</style>
<style scoped lang="less">
.nr_item {
// xuchao: 左右边距用百分比,随屏幕宽度等比变化;宽度自适应(去掉固定 702rpx)
width: auto;
box-sizing: border-box;
margin: 24rpx 3.2% 0;
display: flex;
background: #fff;
padding: 12rpx;
.nr_img {
width: 268rpx;
height: 268rpx;
flex: none;
}
.nr_item_r {
// xuchao: 右侧信息列改为弹性填充,配合卡片宽度自适应
flex: 1;
min-width: 0;
margin-left: 20rpx;
display: flex;
gap: 8rpx;
flex-direction: column;
position: relative;
.nr_tags {
display: flex;
gap: 12rpx;
flex-wrap: wrap;
}
.p_text {
position: absolute;
bottom: 0rpx;
right: 0rpx;
// xuchao: border-box 让盒子精确等于底图宽度;右侧 padding 让出蓝色箭头区,文字在橙色槽内居中
width: 250rpx;
height: 50rpx;
box-sizing: border-box;
background: url('https://aliyunoss.hn12301.net/xgh/image_bj_text.png');
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding-left: 14rpx;
padding-right: 51rpx;
border-radius: 4rpx;
}
.p_label {
font-size: 16rpx;
}
.p_symbol {
font-size: 18rpx;
margin: 0 2rpx 0 6rpx;
}
.p_num {
font-size: 32rpx;
font-style: italic;
font-weight: bold;
}
.p_unit {
font-size: 16rpx;
// xuchao: 价格与"元"之间留出明显间距(8rpx ≈ 375 屏 4px)
margin-left: 8rpx;
}
.nr_origin {
position: absolute;
bottom: 60rpx;
right: 0rpx;
width: 100%;
text-align: right;
color: #4F4F4F;
font-size: 12rpx;
}
.nr_origin_num {
font-size: 28rpx;
text-decoration: line-through;
font-weight: bold;
}
.r_text {
width: 100%;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 20rpx;
}
.n_text {
color: #008F90;
font-weight: bolder;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
font-size: 32rpx;
}
.t_text {
background-color: #FDEEC3;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #D57817;
border-radius: 4rpx;
}
.t_text1 {
background-color: #FDF4ED;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #AF4744;
border-radius: 4rpx;
}
.t_text2 {
background-color: #cbebfb;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #6ba0b3;
border-radius: 4rpx;
}
.t_text3 {
background-color: #F0F1F5;
font-size: 18rpx;
padding: 5rpx 10rpx;
color: #525357;
border-radius: 4rpx;
}
}
.page {
padding: 32rpx 32rpx 40rpx;
}
// 以下为旧版遗留、当前模板未使用的样式(.item / .r_text1 / .n_text1),按约定仅提示不删
.item {
width: 702rpx;
padding: 12rpx;
border-radius: 8rpx;
background-color: #fff;
position: relative;
margin-top: 24rpx;
/* ---------- 卡片列表 ---------- */
.list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.card {
display: flex;
padding: 20rpx;
box-sizing: border-box;
transition: transform 0.2s ease-out, box-shadow 0.3s ease-out;
}
.card:active {
transform: scale(0.985);
box-shadow: var(--mm-shadow-sm);
}
.card__img {
width: 208rpx;
height: 208rpx;
flex: none;
border-radius: 18rpx;
background: var(--mm-muted);
}
.card__body {
flex: 1;
min-width: 0;
margin-left: 24rpx;
display: flex;
flex-direction: column;
}
.item_name {
width: 434rpx;
line-height: normal;
color: #000;
font-size: 28rpx;
font-weight: bold;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.card__name {
font-size: 30rpx;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--mm-fg);
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.tag {
font-size: 20rpx;
color: #fff;
padding: 6rpx 5rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
border-radius: 4rpx;
margin-top: 12rpx;
margin-right: 12rpx;
}
.card__desc {
margin-top: 8rpx;
font-size: 22rpx;
line-height: 1.45;
color: var(--mm-muted-fg);
overflow: hidden !important;
text-overflow: ellipsis !important;
display: -webkit-box !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.tag1 {
font-size: 20rpx;
color: #999999;
padding: 5rpx;
border: 2rpx #999999 solid;
border-radius: 4rpx;
margin-top: 12rpx;
margin-right: 12rpx;
}
.card__foot {
margin-top: auto;
padding-top: 12rpx;
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.hot_tag {
height: 36rpx;
color: #fff;
font-size: 20rpx;
position: absolute;
top: 12rpx;
left: 12rpx;
background: rgba(0, 0, 0, 0.5);
padding: 0rpx 8rpx;
border-radius: 8rpx 0rpx 8rpx 0rpx;
line-height: 36rpx;
}
.card__origin {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.card__origin-num {
text-decoration: line-through;
}
.card__now {
display: flex;
align-items: baseline;
}
.card__cur {
font-size: 24rpx;
font-weight: 600;
color: var(--mm-accent);
margin-right: 2rpx;
}
.card__num {
font-size: 46rpx;
line-height: 1;
}
.card__unit {
font-size: 20rpx;
color: var(--mm-muted-fg);
margin-left: 6rpx;
}
/* xuchao: 预订按钮 —— 渐变胶囊 + 箭头,承担整卡的行动号召 */
.card__cta {
flex: none;
display: flex;
align-items: center;
gap: 6rpx;
height: 60rpx;
padding: 0 26rpx;
border-radius: 999rpx;
color: #fff;
font-size: 24rpx;
box-shadow: 0 6rpx 16rpx rgba(0, 82, 255, 0.25);
}
/* ---------- 空态与底部 ---------- */
.empty {
margin-top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.empty__text {
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.foot {
padding: 44rpx 0 20rpx;
text-align: center;
font-size: 22rpx;
letter-spacing: 0.1em;
color: var(--mm-muted-fg);
}
</style>
+408 -214
View File
@@ -1,159 +1,151 @@
<template>
<view v-if="obj">
<view class="wrap">
<u-swiper :list="list" height='750' mode='number' indicator-pos='bottomRight'
marginBottom='30rpx'></u-swiper>
<view v-if="obj" class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 头图轮播(保留原有数字指示器) -->
<u-swiper :list="list" height="750" mode="number" indicator-pos="bottomRight"
marginBottom="30rpx"></u-swiper>
<!-- xuchao: 信息面板 —— 上移盖住头图底部的圆角 sheet,承载景区核心信息 -->
<view class="sheet mm-fade-up">
<view class="t_head">
<view class="t_name mm-display">{{obj.tickets.name}}</view>
<view class="t_level mm-grad" v-if="obj.shop.level">{{obj.shop.level}}</view>
</view>
<view class="t_info" v-if="obj.tickets.remark1">{{obj.tickets.remark1}}</view>
<!-- xuchao: 开园时间 —— 蓝色浅底提示条 -->
<view class="t_open" v-if="obj.tickets.openDate">
<u-icon name="clock-fill" color="#0052FF" size="28"></u-icon>
<text class="t_open__label">开园时间</text>
<text class="t_open__val">{{obj.tickets.openDate}}</text>
</view>
<!-- xuchao: 地址 + 导航/电话快捷圆钮 -->
<view class="t_addr">
<view class="t_addr__text">{{obj.shop.address}}</view>
<view class="t_addr__acts">
<view class="t_addr__btn mm-grad" @click="handleOpen()">
<u-icon name="map-fill" color="#ffffff" size="32"></u-icon>
</view>
<view class="t_addr__btn mm-grad" @click="playPhone()">
<u-icon name="phone-fill" color="#ffffff" size="32"></u-icon>
</view>
</view>
</view>
</view>
<view style="position: absolute;top: 700rpx;">
<view class="title">
<view class="t_name">{{obj.tickets.name}} <text style="color: #FF8D28;">{{obj.shop.level}}</text></view>
<view class="t_info">{{obj.tickets.remark1}}</view>
<view class="t_opentime">
<view style="display: flex;gap: 8rpx;color: #FF8D28;">
<image src="https://aliyunoss.hn12301.net/xgh/ticket_opentime.png"
style="width: 36rpx;height: 36rpx;"></image>开园时间
</view>
<text style="color: #8E8E93;font-size: 24rpx;">{{obj.tickets.openDate}}</text>
</view>
<view class="t_address">
<view style="width: 548rpx;">{{obj.shop.address}}</view>
<image src="https://aliyunoss.hn12301.net/xgh/ticket_dh.png" @click="handleOpen()"></image>
<image style="margin-left: 24rpx;" src="https://aliyunoss.hn12301.net/xgh/ticket_dhua.png"
@click="playPhone()"></image>
</view>
<!-- <view style="color: #FF0000;font-size: 32rpx;margin-top: 24rpx;"><text
style="font-size: 20rpx;">¥</text>{{(obj.minPrice/100).toFixed(0)}} ~ {{(obj.maxPrice/100).toFixed(0)}}</view>
<view style="display: flex;margin-top: 24rpx;">
<text class="tag" v-for="item in obj.tickets.tags.split(',')" :key="item">{{item}}</text>
</view> -->
</view>
<view class="ticket">
<!-- <text style="color: #000;font-size: 36rpx;line-height: 88rpx;font-weight: 700;">成人票</text> -->
<view class="ticket_item" style="margin-top: 0rpx;" v-for="(item,index) in obj.skuList" :key="index">
<view style="width: 338rpx;display: flex;flex-direction: column;">
<text
style="color: #000;font-size: 28rpx;font-weight: 700;">【{{getSkuType(item.typeId)}}】{{item.skuName}}</text>
<view
style="color: #7C7C7E;display: flex;align-items: center;margin-top: 16rpx;font-size: 24rpx;">
{{item.bookTimeLimit}}之前{{item.bookType?'可订当天':'可订明日'}}<text
style="color: #DFE0E2;font-size: 48rpx;padding: 0rpx 12rpx;">·</text>
<!-- xuchao: 门票规格列表 -->
<view class="sec mm-fade-up">
<view class="sec__title mm-sec-title">门票规格</view>
<view class="sku" v-for="(item,index) in obj.skuList" :key="index">
<!-- xuchao: 名称提为独立整行,填充卡片宽度,不再被右侧价格列挤压换行 -->
<view class="sku__name">【{{getSkuType(item.typeId)}}】{{item.skuName}}</view>
<view class="sku__body">
<view class="sku__left">
<view class="sku__rule">{{item.bookTimeLimit}}之前{{item.bookType?'可订当天':'可订次日'}}</view>
<view class="sku__know" @click="seeSkuDetail(item.id)">
<text>预订须知</text>
<u-icon name="arrow-right" color="#0052FF" size="20"></u-icon>
</view>
<view style="color: #0066F6;margin-top: 16rpx;" @click="seeSkuDetail(item.id)">预订须知 ></view>
</view>
<view
style="width: 292rpx;display: flex;flex-direction: column;margin-left: 24rpx;align-items: flex-end;">
<view
style="color: #FF0000;font-size: 28rpx;margin-top: 0rpx;display: flex;justify-content: flex-end;">
原售价<text
style="font-size: 28rpx;font-weight: normal;text-decoration: line-through;">¥{{item.calendar.listPrice?(item.calendar.listPrice/100).toFixed(2):0}}</text>
</view>
<view style="color: #FF0000;font-size: 36rpx;font-weight: bold;line-height: 88rpx;">
<view class="sku__right">
<view class="sku__origin">原售价 <text
class="sku__origin-num">¥{{item.calendar.listPrice?(item.calendar.listPrice/100).toFixed(2):0}}</text></view>
<view class="sku__price">
<text class="sku__price-label">¥</text>
<text
style="font-size: 20rpx;font-weight: normal;">工会会员补贴价¥</text>{{item.calendar.salePrice?(item.calendar.salePrice/100).toFixed(2):0}}
class="sku__price-num mm-display mm-grad-text">{{item.calendar.salePrice?(item.calendar.salePrice/100).toFixed(2):0}}</text>
</view>
<view class="btn" @tap="toOrderSure(item.id)">购买</view>
<view class="sku__buy mm-grad" @tap="toOrderSure(item.id)">购买</view>
</view>
</view>
</view>
<view class="ticket">
<text style="color: #000;font-size: 36rpx;line-height: 88rpx;font-weight: 700;">产品介绍</text>
</view>
<!-- xuchao: 产品介绍(后端富文本) -->
<view class="sec mm-fade-up">
<view class="sec__title mm-sec-title">产品介绍</view>
<view class="intro">
<u-parse :html="obj.tickets.introduction"></u-parse>
</view>
</view>
<u-popup v-model="show" border-radius='20' mode='bottom' height='1000'>
<view style="padding:0rpx 48rpx;" v-if="skuObj">
<view
style="width: 654rpx;height: 142rpx;line-height: 142rpx;text-align: center;color: #000;font-size: 32rpx;font-weight: bold;border-bottom: 2rpx #E1E1E1 dashed;">
{{skuObj.sku.skuName}}
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">预订规则</text>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">预订时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
{{skuObj.sku.bookTimeLimit}}之前{{skuObj.sku.bookType?'可订当天':'可订明日'}}
</view>
<!-- xuchao: 预订须知弹层 -->
<u-popup v-model="show" border-radius="20" mode="bottom" height="1000">
<view class="pp" v-if="skuObj">
<view class="pp__head">{{skuObj.sku.skuName}}</view>
<view class="pp__sec">
<view class="mm-sec-title">预订规则</view>
<view class="pp__row">
<view class="pp__label">预订时间</view>
<view class="pp__val">{{skuObj.sku.bookTimeLimit}}之前{{skuObj.sku.bookType?'可订当天':'可订次日'}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">生效时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">出票后立即使用</view>
<view class="pp__row">
<view class="pp__label">生效时间</view>
<view class="pp__val">出票后立即使用</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">有效期</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">指定出行日期1天内有效</view>
<view class="pp__row">
<view class="pp__label">有效期</view>
<view class="pp__val">指定出行日期1天内有效</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">适用人群</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.typeInfo}}</view>
<view class="pp__row">
<view class="pp__label">适用人群</view>
<view class="pp__val">{{skuObj.sku.typeInfo}}</view>
</view>
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">费用说明</text>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">费用包含</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWith}}</view>
<view class="pp__sec">
<view class="mm-sec-title">费用说明</view>
<view class="pp__row">
<view class="pp__label">费用包含</view>
<view class="pp__val">{{skuObj.sku.contentWith}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">费用不含</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.contentWithout}}</view>
</view>
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">使用说明</text>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">入园方式</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
{{skuObj.sku.enterChangeFlag?'凭('+getTyepStr(skuObj.sku.enterType)+')换票入园':'无需换票'}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="skuObj.sku.enterChangeFlag">
<view style="color: #808080;width: 120rpx;">换票时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
{{skuObj.sku.changeStart}}~{{skuObj.sku.changeEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;" v-if="skuObj.sku.enterChangeFlag">
<view style="color: #808080;width: 120rpx;">换票地点</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.changePostion}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">入园时间</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">
{{skuObj.sku.enterStart}}~{{skuObj.sku.enterEnd}}
</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">入园地址</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.enterPostion}}</view>
<view class="pp__row">
<view class="pp__label">费用不含</view>
<view class="pp__val">{{skuObj.sku.contentWithout}}</view>
</view>
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">取消政策</text>
<u-parse style="margin-top: 24rpx;" :html="skuObj.sku.refundPolicy"></u-parse>
<view class="pp__sec">
<view class="mm-sec-title">使用说明</view>
<view class="pp__row">
<view class="pp__label">入园方式</view>
<view class="pp__val">{{skuObj.sku.enterChangeFlag?'凭('+getTyepStr(skuObj.sku.enterType)+')换票入园':'无需换票'}}</view>
</view>
<view class="pp__row" v-if="skuObj.sku.enterChangeFlag">
<view class="pp__label">换票时间</view>
<view class="pp__val">{{skuObj.sku.changeStart}}~{{skuObj.sku.changeEnd}}</view>
</view>
<view class="pp__row" v-if="skuObj.sku.enterChangeFlag">
<view class="pp__label">换票地点</view>
<view class="pp__val">{{skuObj.sku.changePostion}}</view>
</view>
<view class="pp__row">
<view class="pp__label">入园时间</view>
<view class="pp__val">{{skuObj.sku.enterStart}}~{{skuObj.sku.enterEnd}}</view>
</view>
<view class="pp__row">
<view class="pp__label">入园地址</view>
<view class="pp__val">{{skuObj.sku.enterPostion}}</view>
</view>
</view>
<view class="pp__sec">
<view class="mm-sec-title">取消政策</view>
<u-parse class="pp__parse" :html="skuObj.sku.refundPolicy"></u-parse>
</view>
<view style="width: 654rpx;padding: 48rpx 0rpx">
<text style="color: #000;font-size: 32rpx;font-weight: bold;">其他说明</text>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">补充说明</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.remark}}</view>
<view class="pp__sec">
<view class="mm-sec-title">其他说明</view>
<view class="pp__row">
<view class="pp__label">补充说明</view>
<view class="pp__val">{{skuObj.sku.remark}}</view>
</view>
<view style="display: flex;margin-top: 24rpx;">
<view style="color: #808080;width: 120rpx;">发票说明</view>
<view style="width: 494rpx;margin-left: 48rpx;color: #000;">{{skuObj.sku.receiptPolicy}}</view>
<view class="pp__row">
<view class="pp__label">发票说明</view>
<view class="pp__val">{{skuObj.sku.receiptPolicy}}</view>
</view>
</view>
</view>
</u-popup>
@@ -165,7 +157,11 @@
import {
getSkuType
} from '../../unit/utils.js'
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
show: false,
@@ -267,11 +263,20 @@
id: e
}).then(res => {
if (res) {
// xuchao: 头图首张放单票主图(与列表卡片一致),再拼宣传图,跳过与主图重复项
const mainPic = res.data.tickets.mainPic
if (mainPic) {
this.list.push({
image: mainPic
})
}
if (res.data.tickets.propagatePic) {
res.data.tickets.propagatePic.split(',').forEach((item, index) => {
this.list.push({
image: item
})
if (item && item !== mainPic) {
this.list.push({
image: item
})
}
})
}
@@ -297,103 +302,292 @@
},
};
</script>
<style>
page {
background: #F8F9FC;
}
</style>
<style lang="less" scoped>
.wrap {
width: 750rpx;
height: 420rpx;
.page {
padding-bottom: 80rpx;
}
.ticket {
width: 750rpx;
background: #fff;
padding: 24rpx;
/* xuchao: 头图固定 298px 高(u-swiper 的 height 属性只认 rpx,故在此覆写其内联高度),
图片按比例缩放居中裁切(object-fit: cover) */
.u-swiper-wrap /deep/ uni-swiper {
height: 298px !important;
}
.u-swiper-wrap /deep/ img {
object-fit: cover;
}
/* ---------- 信息 sheet(盖住头图底部) ---------- */
.sheet {
position: relative;
z-index: 10;
margin-top: -56rpx;
background: var(--mm-card);
border-radius: 40rpx 40rpx 0 0;
padding: 40rpx 32rpx 28rpx;
box-shadow: 0 -12rpx 32rpx rgba(15, 23, 42, 0.06);
}
.t_head {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.t_name {
flex: 1;
min-width: 0;
font-size: 44rpx;
line-height: 1.2;
color: var(--mm-fg);
}
/* xuchao: 景区等级徽标 —— 渐变胶囊,与景区名右对齐 */
.t_level {
flex: none;
margin: 8rpx 0 0 16rpx;
padding: 6rpx 20rpx;
border-radius: 999rpx;
color: #fff;
font-size: 22rpx;
box-shadow: var(--mm-shadow-accent);
}
.t_info {
margin-top: 12rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-muted-fg);
}
.t_open {
margin-top: 24rpx;
.ticket_item {
margin-top: 24rpx;
background: #F8F9FC;
border-radius: 8rpx;
padding: 24rpx;
display: flex;
}
.btn {
width: 152rpx;
height: 56rpx;
border-radius: 200rpx;
background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20rpx;
}
display: flex;
align-items: center;
background: rgba(0, 82, 255, 0.05);
border-radius: 16rpx;
padding: 18rpx 24rpx;
}
.title {
width: 750rpx;
background-color: #fff;
margin-top: 50rpx;
.t_open__label {
flex: none;
margin: 0 16rpx 0 12rpx;
font-size: 24rpx;
font-weight: 500;
color: var(--mm-accent);
}
.t_open__val {
font-size: 24rpx;
color: var(--mm-fg);
}
.t_addr {
margin-top: 24rpx;
display: flex;
align-items: center;
background: var(--mm-muted);
border-radius: 20rpx;
padding: 24rpx;
}
z-index: 10000;
.t_addr__text {
flex: 1;
min-width: 0;
font-size: 26rpx;
line-height: 1.5;
color: var(--mm-fg);
}
.t_info {
color: #FF383C;
line-height: 48rpx;
margin-top: 12rpx;
font-weight: bold;
}
.t_addr__acts {
flex: none;
display: flex;
gap: 16rpx;
margin-left: 20rpx;
}
.t_opentime {
background-color: #F8F9FD;
padding: 8rpx;
display: flex;
flex-direction: column;
gap: 12rpx;
margin-top: 24rpx;
}
.t_addr__btn {
width: 76rpx;
height: 76rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--mm-shadow-accent);
transition: transform 0.15s ease;
}
.t_address {
width: 702rpx;
height: 100rpx;
margin-top: 24rpx;
background: url('https://aliyunoss.hn12301.net/xgh/t_address_bj.png');
background-size: 100% 100%;
color: #000;
display: flex;
align-items: center;
padding-left: 24rpx;
.t_addr__btn:active {
transform: scale(0.9);
}
image {
width: 40rpx;
height: 70rpx;
}
/* ---------- 内容分区(门票规格 / 产品介绍) ---------- */
.sec {
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;
color: #000;
font-size: 36rpx;
font-weight: 700;
line-height: 48rpx;
}
/* ---------- 规格卡片 ---------- */
.sku {
margin-top: 20rpx;
background: var(--mm-card);
border: 2rpx solid var(--mm-border);
border-radius: 20rpx;
padding: 24rpx;
box-shadow: var(--mm-shadow-sm);
}
.tag {
color: #7C7C7E;
font-size: 20rpx;
padding: 5rpx 10rpx;
background: #F8F9FC;
border-radius: 4rpx;
display: block;
margin-right: 12rpx;
}
/* xuchao: 名称下方的两栏区(左规则/预订须知,右价格/购买) */
.sku__body {
margin-top: 20rpx;
display: flex;
}
.sku__left {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.sku__name {
font-size: 28rpx;
font-weight: 600;
line-height: 1.4;
color: var(--mm-fg);
}
.sku__rule {
font-size: 22rpx;
color: var(--mm-muted-fg);
}
.sku__know {
margin-top: 14rpx;
display: flex;
align-items: center;
gap: 4rpx;
font-size: 24rpx;
color: var(--mm-accent);
}
.sku__right {
flex: none;
width: 268rpx;
margin-left: 20rpx;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.sku__origin {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.sku__origin-num {
text-decoration: line-through;
}
.sku__price {
display: flex;
align-items: baseline;
margin-top: 10rpx;
}
.sku__price-label {
font-size: 20rpx;
color: var(--mm-muted-fg);
}
.sku__price-num {
font-size: 44rpx;
line-height: 1;
}
.sku__buy {
margin-top: 16rpx;
width: 160rpx;
height: 64rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 26rpx;
font-weight: 500;
box-shadow: 0 6rpx 16rpx rgba(0, 82, 255, 0.25);
transition: transform 0.15s ease;
}
.sku__buy:active {
transform: scale(0.94);
}
/* ---------- 产品介绍富文本 ---------- */
.intro {
margin-top: 16rpx;
font-size: 26rpx;
line-height: 1.7;
color: var(--mm-fg);
}
.intro /deep/ img {
max-width: 100% !important;
display: block;
border-radius: 12rpx;
}
/* ---------- 预订须知弹层 ---------- */
.pp {
padding: 0 48rpx 80rpx;
}
.pp__head {
height: 120rpx;
line-height: 120rpx;
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: var(--mm-fg);
border-bottom: 2rpx solid var(--mm-border);
}
.pp__sec {
padding-top: 40rpx;
}
.pp__row {
display: flex;
margin-top: 24rpx;
}
.pp__label {
flex: none;
width: 120rpx;
font-size: 26rpx;
color: var(--mm-muted-fg);
}
.pp__val {
flex: 1;
margin-left: 48rpx;
font-size: 26rpx;
line-height: 1.6;
color: var(--mm-fg);
}
.pp__parse {
margin-top: 24rpx;
font-size: 26rpx;
line-height: 1.7;
color: var(--mm-fg);
}
</style>
+168 -41
View File
@@ -1,27 +1,54 @@
<template>
<view>
<text class="text">需添加 &nbsp;{{value}}&nbsp;个出行人</text>
<view class="h_item" @click="toAddPerson">
<text class="text1">新增出行人</text>
<text class="text2">+</text>
</view>
<view class="item" v-for="(item,index) in list" :key="index">
<image src="https://aliyunoss.hn12301.net/sd/ticket_edit.png" style="width: 48rpx;height: 48rpx;" @click="toPerson(item)"></image>
<text style="margin-left: 24rpx;">{{item.name}}</text>
<text style="margin-left: 24rpx;">{{item.identityNo}}</text>
<view style="position: absolute;right: 0rpx;">
<u-checkbox v-model="item.isSelect" active-color="#457AF7"></u-checkbox>
<view class="page mm-page">
<gmy-back-touch></gmy-back-touch>
<!-- xuchao: 顶部提示徽章 —— 需添加的出行人数量 -->
<view class="tip mm-fade-up">
<view class="mm-badge">
<view class="mm-badge__dot"></view>
<text class="mm-badge__text">需添加 {{value}} 个出行人(已选 {{selectedCount}})</text>
</view>
</view>
<view style="width: 702rpx;position: fixed;left: 0rpx;right: 0rpx;margin: 0 auto;bottom: calc(24rpx + env(safe-area-inset-bottom));">
<u-button shape="circle" size="default" style="background: linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%);color: #fff;" @click="select">完成</u-button>
<!-- xuchao: 新增出行人 —— 虚线描边引导卡 -->
<view class="add mm-fade-up" @click="toAddPerson">
<view class="add__icon mm-grad">
<u-icon name="plus" color="#ffffff" size="30"></u-icon>
</view>
<text class="add__text">新增出行人</text>
<u-icon name="arrow-right" color="#CBD5E1" size="28"></u-icon>
</view>
<!-- xuchao: 出行人列表 —— 整卡点击勾选,笔形图标进编辑 -->
<view class="list mm-stagger">
<view class="person mm-card mm-fade-up" v-for="(item,index) in list" :key="index" @click="toggle(item)">
<view class="person__avatar mm-grad">{{(item.name||'').charAt(0)}}</view>
<view class="person__info">
<view class="person__name">{{item.name}}</view>
<view class="person__id">{{item.identityNo}}</view>
</view>
<view class="person__edit" @click.stop="toPerson(item)">
<u-icon name="edit-pen" color="#64748B" size="36"></u-icon>
</view>
<view class="person__check" @click.stop>
<u-checkbox v-model="item.isSelect" active-color="#0052FF"></u-checkbox>
</view>
</view>
</view>
<!-- xuchao: 底部固定完成按钮(视口水平居中,宽屏下与页面列对齐) -->
<view class="bar">
<view class="mm-btn" @click="select">完成</view>
</view>
</view>
</template>
<script>
var that=''
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
export default {
components: {
'gmy-back-touch': gmyBackTouch
},
data() {
return {
checked:true,
@@ -31,16 +58,34 @@ export default {
},
onLoad(e){
that=this
this.value=e.value
this.value=Number(e.value)
},
onShow(){
this.getMemberTouristMethod()
},
computed: {
// xuchao: 已勾选的出行人数量
selectedCount() {
return this.list.filter(item => item.isSelect).length
}
},
methods: {
// 选中某个复选框时,由checkbox时触发
checkboxChange(e) {
console.log(e);
},
// xuchao: 整卡点击即勾选/取消;勾选数达到需求量 value 后再勾选则提示拦截
toggle(item) {
if (!item.isSelect && this.selectedCount >= this.value) {
uni.showToast({
icon: 'none',
title: '只需 ' + this.value + ' 位出行人',
duration: 1500
})
return
}
item.isSelect = !item.isSelect
},
toAddPerson(){
uni.navigateTo({
url: 'person'
@@ -82,38 +127,120 @@ export default {
</script>
<style lang="less" scoped>
.text{
color: rgba(0, 0, 0, 0.8);
margin: 24rpx 0rpx 0rpx 24rpx;
font-size: 36rpx;
height: 88rpx;
font-weight: bold;
/* xuchao: 底部预留 220rpx,避免列表被固定完成按钮遮挡 */
.page {
padding: 32rpx 32rpx 220rpx;
}
.h_item{
width: 702rpx;
.tip {
display: flex;
}
.add {
margin-top: 24rpx;
display: flex;
margin-left: 24rpx;
justify-content: space-between;
border-bottom: 1px dashed #E1E1E1;
align-items: center;
padding-bottom: 24rpx;
.text1{
color:rgba(0, 0, 0, 0.8);
}
.text2{
color:#457AF7;
font-size: 40rpx;
font-weight: bold;
}
border: 2rpx dashed rgba(0, 82, 255, 0.4);
border-radius: 20rpx;
background: rgba(0, 82, 255, 0.04);
padding: 28rpx 24rpx;
transition: transform 0.15s ease;
}
.item{
width: 702rpx;
margin-left: 24rpx;
height: 112rpx;
.add:active {
transform: scale(0.985);
}
.add__icon {
flex: none;
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex;
align-items: center;
position: relative;
justify-content: center;
box-shadow: var(--mm-shadow-accent);
}
.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>
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-font-size-paragraph:15px;
@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-family: 'YouShe';
/* xuchao: 该文件经 App.vue @import 内联后,webpack 以"项目根目录"为基准解析 url,
故 ./static/ 即根目录 static/,勿改成相对本文件的 ../../ 路径(会解析失败) */
src: url('./static/YouShe.ttf') format('truetype');
font-display: swap;
}
+15 -6
View File
@@ -63,7 +63,8 @@
<view class="u-calendar__bottom__btn">
<!-- <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)"
: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>
@@ -187,9 +188,10 @@
default: '#999999'
},
// 选中|起始结束日期背景色
// xuchao: 默认色换主题蓝(原绿色与整站风格不一致)
activeBgColor: {
type: String,
default: '#22B464'
default: '#0052FF'
},
// 选中|起始结束日期字体颜色
activeColor: {
@@ -199,12 +201,12 @@
// 范围内日期背景色
rangeBgColor: {
type: String,
default: 'rgba(123,202,74,0.13)'
default: 'rgba(0, 82, 255, 0.08)'
},
// 范围内日期字体颜色
rangeColor: {
type: String,
default: '#22B464'
default: '#0052FF'
},
// mode=range时生效,起始日期自定义文案
startText: {
@@ -246,7 +248,7 @@
return {
// 星期几,值为1-7
customStyle: {
background: 'linear-gradient(180deg, #FF9E49 0%, #ED3B1A 100%)'
background: 'linear-gradient(135deg, #0052FF, #4D7CFF)'
},
weekday: 1,
weekdayArr: [],
@@ -307,7 +309,8 @@
},
getColor1(index, type) {
let color = type == 1 ? '' : '#f00';
// xuchao: 未选中日期价格色由红改主题蓝
let color = type == 1 ? '' : '#0052FF';
let day = index + 1
let date = `${this.year}-${this.month}-${day}`
let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
@@ -567,6 +570,12 @@
.u-calendar {
color: $u-content-color;
/* xuchao: 限高视口 85%,超出时日历内部上下滑动(小屏机型弹窗不再顶出屏幕);
overscroll-behavior 防止滚到边缘后链式滚动背景页 */
max-height: 85vh;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
&__header {
width: 100%;
+3 -1
View File
@@ -3,6 +3,8 @@
zIndex: uZindex - 1
}]" 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>
<!-- xuchao: touchmove 去掉 .prevent —— 它会拦截默认行为,导致 H5 下内层滚动容器无法触摸滚动;
背景页防穿透改由滚动容器的 overscroll-behavior: contain 承担(见 App.vue) -->
<view
class="u-drawer-content"
@tap="modeCenterClose(mode)"
@@ -12,7 +14,7 @@
showDrawer ? 'u-drawer-content-visible' : '',
zoom && mode == 'center' ? 'u-animation-zoom' : ''
]"
@touchmove.stop.prevent
@touchmove.stop
@tap.stop.prevent
:style="[style]"
>