267 lines
9.1 KiB
JavaScript
267 lines
9.1 KiB
JavaScript
const API = require('../../utils/api.js')
|
||
|
||
Page({
|
||
data: {
|
||
userInfo: {
|
||
// 留空表示"没有真头像",模板据此渲染暖橙实心底 + 白色 user 线标的默认头像
|
||
// (与首页 .brand-mark 同源)。默认头像不走位图:位图着色读不到 CSS 变量,
|
||
// 改主色时须整图重上色(见 DESIGN.md §7.3),用 t-icon + 令牌则自动跟随
|
||
avatar: '',
|
||
name: '用户',
|
||
// 身份行副行,loadUserInfo 时由手机号脱敏填入
|
||
account: '',
|
||
phone: '',
|
||
sex: '',
|
||
birth: '',
|
||
email: '',
|
||
idnumber: '',
|
||
ssn: '',
|
||
isLoggedIn: false
|
||
},
|
||
// 菜单分组:按"用途"而非"是不是陪诊"分,三组才立得住。
|
||
// 图标与首页同目的地同图形(同一含义只对应一个表达,从色值延伸到图标):
|
||
// 预约记录 calendar-1、就诊人 user-checked、联系我们 call 均与首页一致。
|
||
// requiresLogin 的组在未登录时整组隐藏——这些入口都依赖用户身份
|
||
menuGroups: [{
|
||
groupName: '我的服务',
|
||
requiresLogin: true,
|
||
items: [{
|
||
// 陪诊产品的「我的」页核心是订单,故排全页第一项
|
||
name: '预约记录',
|
||
icon: 'calendar-1',
|
||
bindtap: 'onTapEscortRecord'
|
||
}, {
|
||
name: '就诊人',
|
||
icon: 'user-checked',
|
||
bindtap: 'onTapHealth'
|
||
}, {
|
||
name: '邮寄地址',
|
||
icon: 'location',
|
||
bindtap: 'onTapAddress'
|
||
}]
|
||
}, {
|
||
groupName: '帮助与支持',
|
||
requiresLogin: false,
|
||
items: [{
|
||
name: '联系我们',
|
||
icon: 'call',
|
||
bindtap: 'onTapContact'
|
||
}, {
|
||
// 口径统一为「服务指南」(首页区块标题行、列表页说明卡都用此名),
|
||
// 不另起「陪诊指南」这一第三叫法;与列表页「服务指南 │ 用户协议」同一配对
|
||
name: '服务指南',
|
||
icon: 'book-open',
|
||
bindtap: 'onTapGuide'
|
||
}, {
|
||
name: '用户协议',
|
||
icon: 'secured',
|
||
bindtap: 'onTapAgreement'
|
||
}]
|
||
}, {
|
||
groupName: '其他',
|
||
requiresLogin: false,
|
||
items: [{
|
||
name: '关于我们',
|
||
icon: 'info-circle',
|
||
bindtap: 'onTapAboutUs'
|
||
}]
|
||
}]
|
||
},
|
||
|
||
onLoad() {
|
||
this.loadUserInfo()
|
||
},
|
||
|
||
onShow() {
|
||
this.loadUserInfo()
|
||
// 同步自定义 tabBar 选中态(我的为第 2 项)
|
||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||
this.getTabBar().setData({
|
||
selected: 2
|
||
})
|
||
}
|
||
},
|
||
|
||
loadUserInfo() {
|
||
const app = getApp()
|
||
if (app.globalData.user && app.globalData.user.profile) {
|
||
const profile = app.globalData.user.profile
|
||
this.setData({
|
||
'userInfo.isLoggedIn': true,
|
||
'userInfo.name': profile.name || '用户',
|
||
// 头像补读后端返回值;取不到留空,由模板渲染暖橙默认头像。
|
||
// 注:profileedit 页里新选的图目前不会落库——utils/api.js 没有文件上传接口,
|
||
// 微信返回的是本地临时路径,存进库里也会失效,所以这里只修"显示"这一半
|
||
'userInfo.avatar': profile.avatar || '',
|
||
'userInfo.phone': profile.mobile || '',
|
||
// 身份行副行(微信那行是微信号):本应用没有账号体系,
|
||
// 等价标识是手机号,脱敏成 138****1234 后再上屏
|
||
'userInfo.account': this.maskMobile(profile.mobile || ''),
|
||
'userInfo.sex': profile.sex || '',
|
||
'userInfo.birth': profile.birth ? this.formatDate(profile.birth) : '',
|
||
'userInfo.email': profile.email || '',
|
||
'userInfo.idnumber': profile.idnumber || '',
|
||
'userInfo.ssn': profile.ssn || ''
|
||
})
|
||
} else {
|
||
// 未登录(含刚退出登录):身份行收敛回默认态。
|
||
// 少了这个分支,globalData.user 被清空后页面仍停在"已登录"的样子——
|
||
// 「我的服务」组和退出块都不会消失
|
||
this.setData({
|
||
'userInfo.isLoggedIn': false,
|
||
'userInfo.avatar': '',
|
||
'userInfo.name': '用户',
|
||
'userInfo.account': '',
|
||
'userInfo.phone': ''
|
||
})
|
||
}
|
||
},
|
||
|
||
// 手机号脱敏:11 位取前三后四;位数不符时原样返回,不制造错位星号
|
||
maskMobile(mobile) {
|
||
const digits = String(mobile).replace(/\D/g, '')
|
||
if (digits.length !== 11) return mobile || ''
|
||
return `${digits.slice(0, 3)}****${digits.slice(7)}`
|
||
},
|
||
|
||
formatDate(date) {
|
||
if (!date) return ''
|
||
const d = new Date(date)
|
||
const year = d.getFullYear()
|
||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
},
|
||
|
||
// 顶部身份行整行可点,但未登录时不该跳进"填完才被拦"的资料页
|
||
// (个人信息与邮寄地址改真页面后系统返回键归页面栈管,
|
||
// 不再像原来的浮层那样被系统返回键直接退出小程序)
|
||
onTapProfile() {
|
||
if (!this.data.userInfo.isLoggedIn) return
|
||
wx.navigateTo({
|
||
url: '/pages/mine/profileedit'
|
||
})
|
||
},
|
||
|
||
onTapAddress() {
|
||
wx.navigateTo({
|
||
url: '/pages/mine/addressedit'
|
||
})
|
||
},
|
||
|
||
onTapHealth() {
|
||
wx.navigateTo({
|
||
url: '/pages/health/profilelist'
|
||
})
|
||
},
|
||
|
||
onTapEscortRecord() {
|
||
wx.navigateTo({
|
||
url: '/pages/escort/recordlist'
|
||
})
|
||
},
|
||
|
||
// 帮助与支持:联系我们(页面已存在,与首页「常用功能」同目的地)
|
||
onTapContact() {
|
||
wx.navigateTo({
|
||
url: '/pages/mine/contact'
|
||
})
|
||
},
|
||
|
||
// 帮助与支持:服务指南(与首页区块标题行、列表页说明卡同一页面)
|
||
onTapGuide() {
|
||
wx.navigateTo({
|
||
url: '/pages/escort/guide'
|
||
})
|
||
},
|
||
|
||
// 帮助与支持:用户协议(与陪诊服务列表页的「用户协议」同一页面)
|
||
onTapAgreement() {
|
||
wx.navigateTo({
|
||
url: '/pages/escort/agreement'
|
||
})
|
||
},
|
||
|
||
onTapAboutUs() {
|
||
wx.navigateTo({
|
||
url: '/pages/mine/about'
|
||
})
|
||
},
|
||
|
||
// 页脚三个 B 端入口:都进「合作与入驻」页,用 section 定位到各自的卡片
|
||
onFooterTap(e) {
|
||
const section = e.currentTarget.dataset.section || ''
|
||
wx.navigateTo({
|
||
url: `/pages/mine/business?section=${section}`
|
||
})
|
||
},
|
||
|
||
// 退出登录:二次确认 → 调后端登出 → 清全局与本地缓存
|
||
onLogout() {
|
||
wx.showModal({
|
||
title: '退出登录',
|
||
content: '退出后需要重新授权手机号,才能查看预约记录与就诊人',
|
||
confirmText: '退出',
|
||
// confirmColor 是属性值,读不到 CSS 变量,取值须与令牌同步(DESIGN.md §5.1)
|
||
confirmColor: '#B8520A',
|
||
success: (res) => {
|
||
if (!res.confirm) return
|
||
wx.showLoading({ title: '退出中...' })
|
||
// 必须先调接口:token 由 request.js 从 globalData.user 里取,
|
||
// 先清全局对象就带不上 token 了
|
||
API.user.signout({})
|
||
.then(() => this.doLogout())
|
||
// 接口失败也要把本地退干净,不把用户卡在"已登录"的假象里
|
||
.catch(() => this.doLogout())
|
||
.finally(() => wx.hideLoading())
|
||
}
|
||
})
|
||
},
|
||
|
||
doLogout() {
|
||
const app = getApp()
|
||
app.globalData.user = null
|
||
wx.removeStorageSync('user_phonenumber')
|
||
// 落一个"已主动退出"标记:app.js 的 onShow 每次都会静默重登
|
||
// (wxsignin 只凭 code 换 openid 即可重新签发 token),不拦这一下,
|
||
// 退出就形同虚设——切后台再回来又变成登录态
|
||
wx.setStorageSync('has_logout', true)
|
||
this.loadUserInfo()
|
||
wx.showToast({ title: '已退出登录', icon: 'success' })
|
||
},
|
||
|
||
handleLogin(e) {
|
||
// 微信手机号 code 是一次性凭证(5 分钟内有效、只能用一次),只能在服务端消费一次。
|
||
// 所以不能先调 /user/wxgetphonenumber 把它用掉、再在 wxSignin 里复用——
|
||
// 那样第二次向微信换号必然报 invalid code。
|
||
// wxSignin 内部已做可信的服务端换号(api/user/handler/users.js),直接交给它即可。
|
||
const phoneCode = e.detail.code ? e.detail.code : ''
|
||
wx.login({
|
||
success: (res) => {
|
||
if (!res.code) {
|
||
console.log('登录失败!' + res.errMsg)
|
||
return
|
||
}
|
||
API.user.wxSignin({ phoneCode, code: res.code })
|
||
.then((data) => {
|
||
if (data.code !== 0) {
|
||
// 后端失败(如 code 过期、该手机号已注册等)此前被静默吞掉,
|
||
// 用户点了授权却毫无反馈,这里补一个提示
|
||
wx.showToast({ title: data.msg || '登录失败', icon: 'none' })
|
||
return
|
||
}
|
||
const app = getApp()
|
||
app.globalData.user = data.data.user
|
||
wx.setStorageSync('user_phonenumber', data.data.user?.profile?.mobile || '')
|
||
// 用户重新授权登录,解除对静默重登的拦截
|
||
wx.removeStorageSync('has_logout')
|
||
this.loadUserInfo()
|
||
})
|
||
.catch((err) => {
|
||
wx.showToast({ title: '网络请求失败', icon: 'none' })
|
||
console.log('请求失败', err)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}) |