- 设计令牌层: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>
259 lines
6.7 KiB
Vue
259 lines
6.7 KiB
Vue
<template>
|
||
<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="tips mm-fade-up" v-if="devMode">开发模式:输入测试手机号直接登录</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>
|
||
|
||
<view class="mm-btn login__btn" @click="doLogin">立即登录</view>
|
||
|
||
<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"
|
||
@change="codeChange"></u-verification-code>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// xuchao: 登录页 —— 微信版
|
||
// 开发模式:输入测试手机号直接登录(假登录)
|
||
// 正式流程:微信网页授权(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,
|
||
redirect: '',
|
||
tips: '发送验证码',
|
||
seconds: 60,
|
||
checked: false,
|
||
value: '',
|
||
value1: '',
|
||
isFlag: true,
|
||
// xuchao: 输入框背景由外层 .field 提供,组件内部保持透明
|
||
customStyle: {
|
||
background: 'transparent',
|
||
}
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
// xuchao: 记录登录前的页面,登录成功后回跳
|
||
if (options.redirect) {
|
||
this.redirect = options.redirect
|
||
}
|
||
// xuchao: 正式流程 —— 公众号网页授权重定向回来时 URL 带 code,用它换用户信息
|
||
if (options.code && !config.devMode) {
|
||
auth.loginByWechat(options.code).then(res => {
|
||
// 后端已绑定手机的用户直接登录成功
|
||
if (res && res.mobile) {
|
||
uni.setStorageSync('openId', res.openId)
|
||
uni.setStorageSync('phone', res.mobile)
|
||
this.finishLogin()
|
||
}
|
||
// 未绑定手机的用户停留在本页,走短信验证码绑定
|
||
}).catch(() => {})
|
||
}
|
||
},
|
||
methods: {
|
||
// xuchao: 登录入口
|
||
doLogin() {
|
||
if (!this.checked) {
|
||
uni.showToast({ icon: 'none', title: '请先阅读并同意用户服务协议' })
|
||
return
|
||
}
|
||
if (!/^1\d{10}$/.test(this.value)) {
|
||
uni.showToast({ icon: 'none', title: '请输入正确的手机号' })
|
||
return
|
||
}
|
||
if (config.devMode) {
|
||
// 开发模式:只允许测试手机号登录
|
||
if (config.testPhones.indexOf(this.value) === -1) {
|
||
uni.showToast({ icon: 'none', title: '开发模式仅支持测试手机号' })
|
||
return
|
||
}
|
||
auth.loginByMock(this.value)
|
||
this.finishLogin()
|
||
return
|
||
}
|
||
// 正式流程:短信验证码校验 + 绑定手机号
|
||
if (!this.value1) {
|
||
uni.showToast({ icon: 'none', title: '请输入验证码' })
|
||
return
|
||
}
|
||
auth.bindPhoneBySms(this.value, this.value1).then(() => {
|
||
this.finishLogin()
|
||
}).catch(() => {})
|
||
},
|
||
// xuchao: 登录成功后回跳
|
||
finishLogin() {
|
||
uni.showToast({ icon: 'success', title: '登录成功' })
|
||
setTimeout(() => {
|
||
if (this.redirect) {
|
||
uni.reLaunch({ url: this.redirect })
|
||
} else if (getCurrentPages().length > 1) {
|
||
uni.navigateBack()
|
||
} else {
|
||
uni.reLaunch({ url: '/pages/ticket/scenic' })
|
||
}
|
||
}, 500)
|
||
},
|
||
end() {
|
||
this.isFlag = true
|
||
},
|
||
start() {
|
||
this.isFlag = false
|
||
},
|
||
readXy() {
|
||
uni.downloadFile({
|
||
url: "https://aliyunoss.hn12301.net/sd/zfzxy.docx",
|
||
success: function(res) {
|
||
uni.openDocument({
|
||
filePath: res.tempFilePath,
|
||
showMenu: true
|
||
});
|
||
}
|
||
});
|
||
},
|
||
codeChange(text) {
|
||
this.tips = text;
|
||
},
|
||
// xuchao: 发送验证码 —— 开发模式仅走倒计时,正式流程需后端短信接口
|
||
getCode() {
|
||
if (!/^1\d{10}$/.test(this.value)) {
|
||
uni.showToast({ icon: 'none', title: '请输入正确的手机号' })
|
||
return
|
||
}
|
||
if (!this.$refs.uCode.canGetCode) {
|
||
this.$u.toast('倒计时结束后再发送');
|
||
return
|
||
}
|
||
if (config.devMode) {
|
||
this.$u.toast('验证码已发送');
|
||
this.$refs.uCode.start();
|
||
return
|
||
}
|
||
// TODO 后端就绪后替换为真实发送短信验证码接口
|
||
uni.showLoading({ title: '正在获取验证码' })
|
||
setTimeout(() => {
|
||
uni.hideLoading();
|
||
this.$u.toast('短信接口未接入');
|
||
}, 1000);
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.page {
|
||
padding: 0 70rpx;
|
||
}
|
||
|
||
/* ---------- 品牌区 ---------- */
|
||
.brand {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-top: 140rpx;
|
||
}
|
||
|
||
.brand__logo {
|
||
width: 164rpx;
|
||
height: 140rpx;
|
||
}
|
||
|
||
.brand__name {
|
||
margin-top: 20rpx;
|
||
font-size: 52rpx;
|
||
letter-spacing: 0.12em;
|
||
}
|
||
|
||
/* ---------- 开发模式提示条 ---------- */
|
||
.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>
|