feat: 添加登录失败处理及记录格式化功能
增加登录失败状态处理和重试机制 将记录格式化逻辑提取到工具函数 优化云数据库查询性能
This commit is contained in:
@@ -34,12 +34,19 @@ App({
|
||||
fail: (err) => {
|
||||
console.error('静默登录失败', err)
|
||||
this.globalData.isLoggedIn = false
|
||||
this.globalData.loginFailed = true
|
||||
|
||||
// 通知页面登录失败
|
||||
if (this.loginReadyCallback) {
|
||||
this.loginReadyCallback(null)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
globalData: {
|
||||
userInfo: null,
|
||||
isLoggedIn: false
|
||||
isLoggedIn: false,
|
||||
loginFailed: false
|
||||
}
|
||||
})
|
||||
|
||||
+27
-21
@@ -1,19 +1,26 @@
|
||||
// index.js
|
||||
const { appointmentDB } = require('../../utils/cloud')
|
||||
const { appointmentDB, formatRecord } = require('../../utils/cloud')
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
isLoggedIn: false,
|
||||
loginFailed: false,
|
||||
latestRecord: null
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
if (app.globalData.isLoggedIn) {
|
||||
this.onLoginReady()
|
||||
} else if (app.globalData.loginFailed) {
|
||||
this.onLoginFailed()
|
||||
} else {
|
||||
app.loginReadyCallback = () => {
|
||||
app.loginReadyCallback = (userInfo) => {
|
||||
if (userInfo) {
|
||||
this.onLoginReady()
|
||||
} else {
|
||||
this.onLoginFailed()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -25,20 +32,32 @@ Page({
|
||||
},
|
||||
|
||||
onLoginReady() {
|
||||
this.setData({ isLoggedIn: true })
|
||||
this.setData({ isLoggedIn: true, loginFailed: false })
|
||||
this.loadLatestRecord()
|
||||
},
|
||||
|
||||
async loadLatestRecord() {
|
||||
if (!this.data.isLoggedIn) {
|
||||
this.setData({ latestRecord: null })
|
||||
return
|
||||
onLoginFailed() {
|
||||
this.setData({ isLoggedIn: false, loginFailed: true })
|
||||
},
|
||||
|
||||
onRetry() {
|
||||
this.setData({ loginFailed: false })
|
||||
app.silentLogin()
|
||||
app.loginReadyCallback = (userInfo) => {
|
||||
if (userInfo) {
|
||||
this.onLoginReady()
|
||||
} else {
|
||||
this.onLoginFailed()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async loadLatestRecord() {
|
||||
try {
|
||||
const openid = app.globalData.userInfo.openid
|
||||
const record = await appointmentDB.getLatest(openid)
|
||||
if (record) {
|
||||
this.setData({ latestRecord: this.formatRecord(record) })
|
||||
this.setData({ latestRecord: formatRecord(record) })
|
||||
} else {
|
||||
this.setData({ latestRecord: null })
|
||||
}
|
||||
@@ -48,19 +67,6 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
formatRecord(record) {
|
||||
const date = record.createTime
|
||||
let createTimeStr = ''
|
||||
if (date) {
|
||||
if (typeof date === 'object' && date.$date) {
|
||||
createTimeStr = new Date(date.$date).toLocaleString('zh-CN')
|
||||
} else {
|
||||
createTimeStr = new Date(date).toLocaleString('zh-CN')
|
||||
}
|
||||
}
|
||||
return { ...record, createTime: createTimeStr }
|
||||
},
|
||||
|
||||
goAppointment() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/appointment/appointment'
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<!--index.wxml-->
|
||||
<view class="page">
|
||||
<!-- loading 遮罩 -->
|
||||
<view class="loading-mask" wx:if="{{!isLoggedIn}}">
|
||||
<view class="loading-mask" wx:if="{{!isLoggedIn && !loginFailed}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">正在获取身份信息...</text>
|
||||
</view>
|
||||
|
||||
<!-- 登录失败 -->
|
||||
<view class="loading-mask" wx:if="{{loginFailed}}">
|
||||
<text class="fail-icon">⚠️</text>
|
||||
<text class="fail-text">网络异常,请重试</text>
|
||||
<view class="retry-btn" bindtap="onRetry">重新加载</view>
|
||||
</view>
|
||||
|
||||
<view class="header">
|
||||
<view class="header-icon">🏢</view>
|
||||
<text class="header-title">访客预约系统</text>
|
||||
|
||||
+27
-2
@@ -56,6 +56,30 @@ page {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 登录失败 */
|
||||
.fail-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.fail-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
background: #1890ff;
|
||||
padding: 16rpx 56rpx;
|
||||
border-radius: 36rpx;
|
||||
}
|
||||
|
||||
.retry-btn:active {
|
||||
background: #096dd9;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
@@ -80,11 +104,12 @@ page {
|
||||
border-radius: 20rpx;
|
||||
padding: 36rpx 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 8rpx 32rpx rgba(24, 144, 255, 0.12), 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.action-card:active {
|
||||
background: #f0f0f0;
|
||||
box-shadow: 0 4rpx 16rpx rgba(24, 144, 255, 0.08), 0 1rpx 4rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.action-icon-wrap {
|
||||
@@ -140,7 +165,7 @@ page {
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
margin-top: 8rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 8rpx 32rpx rgba(82, 196, 26, 0.12), 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.latest-header {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// records.js
|
||||
const { appointmentDB } = require('../../utils/cloud')
|
||||
const { appointmentDB, formatRecord } = require('../../utils/cloud')
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
@@ -11,18 +11,12 @@ Page({
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
if (!app.globalData.isLoggedIn) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
this.loadRecords()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (app.globalData.isLoggedIn) {
|
||||
// 仅从预约页返回时刷新,避免 onLoad + onShow 双重加载
|
||||
if (this.data._loaded) {
|
||||
this.loadRecords()
|
||||
}
|
||||
},
|
||||
@@ -32,26 +26,12 @@ Page({
|
||||
try {
|
||||
const openid = app.globalData.userInfo.openid
|
||||
const records = await appointmentDB.getList(openid)
|
||||
|
||||
// 格式化时间
|
||||
const formatted = records.map(item => {
|
||||
const date = item.createTime
|
||||
let createTimeStr = ''
|
||||
if (date) {
|
||||
if (typeof date === 'object' && date.$date) {
|
||||
createTimeStr = new Date(date.$date).toLocaleString('zh-CN')
|
||||
} else {
|
||||
createTimeStr = new Date(date).toLocaleString('zh-CN')
|
||||
}
|
||||
}
|
||||
return { ...item, createTime: createTimeStr }
|
||||
})
|
||||
|
||||
this.setData({ records: formatted, loading: false })
|
||||
const formatted = records.map(item => formatRecord(item))
|
||||
this.setData({ records: formatted, loading: false, _loaded: true })
|
||||
this.filterRecords()
|
||||
} catch (err) {
|
||||
console.error('加载预约记录失败', err)
|
||||
this.setData({ records: [], loading: false })
|
||||
this.setData({ records: [], loading: false, _loaded: true })
|
||||
this.filterRecords()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,13 +14,16 @@
|
||||
<view class="tab {{currentTab === 'rejected' ? 'tab-active' : ''}}" bindtap="switchTab" data-tab="rejected">
|
||||
已拒绝
|
||||
</view>
|
||||
<view class="tab {{currentTab === 'cancelled' ? 'tab-active' : ''}}" bindtap="switchTab" data-tab="cancelled">
|
||||
已取消
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 记录列表 -->
|
||||
<view class="record-list" wx:if="{{!loading && filteredRecords.length > 0}}">
|
||||
<view class="record-card" wx:for="{{filteredRecords}}" wx:key="_id">
|
||||
<view class="record-header">
|
||||
<text class="record-id">{{item.id}}</text>
|
||||
<text class="record-id">{{item._id}}</text>
|
||||
<view class="status-tag status-{{item.status}}">
|
||||
{{item.statusText}}
|
||||
</view>
|
||||
|
||||
+53
-29
@@ -1,7 +1,33 @@
|
||||
// 云数据库操作工具库
|
||||
|
||||
const db = wx.cloud.database()
|
||||
const _ = db.command
|
||||
/**
|
||||
* 延迟获取 db 实例,确保 cloud.init 已完成
|
||||
*/
|
||||
function getDb() {
|
||||
return wx.cloud.database()
|
||||
}
|
||||
|
||||
function getCmd() {
|
||||
return getDb().command
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化云数据库时间字段
|
||||
* @param {object} record - 含 createTime 的记录
|
||||
* @returns {object} 格式化后的记录
|
||||
*/
|
||||
function formatRecord(record) {
|
||||
const date = record.createTime
|
||||
let createTimeStr = ''
|
||||
if (date) {
|
||||
if (typeof date === 'object' && date.$date) {
|
||||
createTimeStr = new Date(date.$date).toLocaleString('zh-CN')
|
||||
} else {
|
||||
createTimeStr = new Date(date).toLocaleString('zh-CN')
|
||||
}
|
||||
}
|
||||
return { ...record, createTime: createTimeStr }
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户相关操作
|
||||
@@ -10,50 +36,32 @@ const userDB = {
|
||||
/**
|
||||
* 通过 openId 查找用户,不存在则创建
|
||||
* @param {string} openid
|
||||
* @param {object} userInfo - { avatarUrl, nickName }
|
||||
* @returns {Promise<object>} 用户记录
|
||||
*/
|
||||
async loginOrCreate(openid, userInfo) {
|
||||
async loginOrCreate(openid) {
|
||||
const db = getDb()
|
||||
const res = await db.collection('users').where({ _openid: openid }).get()
|
||||
if (res.data.length > 0) {
|
||||
// 已存在,更新头像昵称
|
||||
const existing = res.data[0]
|
||||
await db.collection('users').doc(existing._id).update({
|
||||
data: {
|
||||
avatarUrl: userInfo.avatarUrl,
|
||||
nickName: userInfo.nickName,
|
||||
lastLoginTime: db.serverDate()
|
||||
}
|
||||
})
|
||||
return { ...existing, avatarUrl: userInfo.avatarUrl, nickName: userInfo.nickName }
|
||||
return existing
|
||||
} else {
|
||||
// 不存在,新建
|
||||
const addRes = await db.collection('users').add({
|
||||
data: {
|
||||
_openid: openid,
|
||||
avatarUrl: userInfo.avatarUrl,
|
||||
nickName: userInfo.nickName,
|
||||
createTime: db.serverDate(),
|
||||
lastLoginTime: db.serverDate()
|
||||
}
|
||||
})
|
||||
return {
|
||||
_id: addRes._id,
|
||||
_openid: openid,
|
||||
avatarUrl: userInfo.avatarUrl,
|
||||
nickName: userInfo.nickName
|
||||
_openid: openid
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据 openId 获取用户信息
|
||||
* @param {string} openid
|
||||
* @returns {Promise<object|null>}
|
||||
*/
|
||||
async getByOpenId(openid) {
|
||||
const res = await db.collection('users').where({ _openid: openid }).get()
|
||||
return res.data.length > 0 ? res.data[0] : null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +75,7 @@ const appointmentDB = {
|
||||
* @returns {Promise<string>} 新记录 _id
|
||||
*/
|
||||
async create(data) {
|
||||
const db = getDb()
|
||||
const res = await db.collection('appointments').add({
|
||||
data: {
|
||||
...data,
|
||||
@@ -79,16 +88,29 @@ const appointmentDB = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前用户的预约列表(按创建时间倒序)
|
||||
* 获取当前用户的预约列表(按创建时间倒序,自动分页取全部)
|
||||
* @param {string} openid
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
async getList(openid) {
|
||||
const db = getDb()
|
||||
const MAX_LIMIT = 100
|
||||
let allData = []
|
||||
let countResult = await db.collection('appointments').where({ _openid: openid }).count()
|
||||
const total = countResult.total
|
||||
const batchTimes = Math.ceil(total / MAX_LIMIT)
|
||||
|
||||
for (let i = 0; i < batchTimes; i++) {
|
||||
const res = await db.collection('appointments')
|
||||
.where({ _openid: openid })
|
||||
.orderBy('createTime', 'desc')
|
||||
.skip(i * MAX_LIMIT)
|
||||
.limit(MAX_LIMIT)
|
||||
.get()
|
||||
return res.data
|
||||
allData = allData.concat(res.data)
|
||||
}
|
||||
|
||||
return allData
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -97,6 +119,7 @@ const appointmentDB = {
|
||||
* @returns {Promise<object|null>}
|
||||
*/
|
||||
async getLatest(openid) {
|
||||
const db = getDb()
|
||||
const res = await db.collection('appointments')
|
||||
.where({ _openid: openid })
|
||||
.orderBy('createTime', 'desc')
|
||||
@@ -112,7 +135,7 @@ const appointmentDB = {
|
||||
* @returns {Promise<boolean>} 是否成功
|
||||
*/
|
||||
async cancel(id, openid) {
|
||||
// 先校验该预约属于当前用户
|
||||
const db = getDb()
|
||||
const res = await db.collection('appointments').doc(id).get()
|
||||
if (res.data._openid !== openid) {
|
||||
return false
|
||||
@@ -131,8 +154,9 @@ const appointmentDB = {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
db,
|
||||
_,
|
||||
getDb,
|
||||
getCmd,
|
||||
formatRecord,
|
||||
userDB,
|
||||
appointmentDB
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user