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