From 44405743bb1d1762f61661af017d7a603415fe73 Mon Sep 17 00:00:00 2001 From: Dev Date: Mon, 19 Oct 2026 13:01:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=97=E8=A1=A8=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/miniapp/app.js | 48 ++-- frontend/miniapp/pages/demand/list/index.js | 207 ++++++++++------ frontend/miniapp/pages/demand/list/index.wxml | 2 +- .../miniapp/pages/developer/join/index.js | 2 +- frontend/miniapp/pages/goods/list/index.js | 110 +++++---- frontend/miniapp/pages/message/list/index.js | 92 +++++-- .../services/good/fetchCategoryList.js | 21 +- frontend/miniapp/services/good/fetchGood.js | 2 +- frontend/miniapp/services/home/home.js | 4 +- frontend/miniapp/utils/util.js | 224 +++++++++++------- 10 files changed, 438 insertions(+), 274 deletions(-) diff --git a/frontend/miniapp/app.js b/frontend/miniapp/app.js index 8da4a24..ca88769 100644 --- a/frontend/miniapp/app.js +++ b/frontend/miniapp/app.js @@ -20,29 +20,33 @@ App({ //通过分享链接/小程序码冷启动时缓存推广人,随登录请求一起绑定 savePendingPromoter(options && options.query); this.loggingIn = true; - // 登录 - wx.login({ - success: (res) => { - util.request(api.AuthLoginByWeixin, { - code: res.code, - [PROMOTER_KEY]: getPendingPromoter(), - }, 'POST').then((res) => { - this.loggingIn = false; - if (res.code === 1) { - let userInfo = res.data.userinfo; - wx.setStorageSync('token', res.data.token); - wx.setStorageSync('userInfo', userInfo); - //推广关系为永久绑定,登录成功后无论是否绑定都无需再次提交 - clearPendingPromoter(); - } - }).catch((err) => { - this.loggingIn = false; - console.error('wxLogin failed:', err); - }); - }, - fail: () => { + // 登录。loginReady 给需要鉴权的请求等待,避免首屏带着空 token 发出去 + this.loginReady = new Promise((resolve) => { + const finish = () => { this.loggingIn = false; - }, + resolve(); + }; + wx.login({ + success: (loginRes) => { + util.request(api.AuthLoginByWeixin, { + code: loginRes.code, + [PROMOTER_KEY]: getPendingPromoter(), + }, 'POST', { auth: false }).then((res) => { + if (res.code === 1) { + let userInfo = res.data.userinfo; + util.setToken(res.data.token); + wx.setStorageSync('userInfo', userInfo); + //推广关系为永久绑定,登录成功后无论是否绑定都无需再次提交 + clearPendingPromoter(); + } + }).catch((err) => { + console.error('wxLogin failed:', err); + }).then(finish); + }, + fail: () => { + finish(); + }, + }); }); }, onShow: function (options) { diff --git a/frontend/miniapp/pages/demand/list/index.js b/frontend/miniapp/pages/demand/list/index.js index c379806..18812dd 100644 --- a/frontend/miniapp/pages/demand/list/index.js +++ b/frontend/miniapp/pages/demand/list/index.js @@ -70,15 +70,10 @@ Page({ //图片预览 onImageView(e) { const { images, index } = e.currentTarget.dataset; - console.log('images', images); - console.log('index', index); wx.previewImage({ - current: images[index], // 当前显示图片的http链接 - urls: images, // 需要预览的图片http链接列表 - success: (res) => { - console.log('预览成功'); - }, - complete: (res) => { + current: images[index], + urls: images, + complete: () => { this.setData({ fromPreview: true }); } }); @@ -87,65 +82,69 @@ Page({ // 加载需求列表数据 async loadDemandList(fresh = false, options = {}) { const { silent = false } = options; - if (fresh) { - this.setData({ - page: 1, - hasMore: true, // 刷新时重置 - }); - wx.pageScrollTo({ - scrollTop: 0, - }); + // 上拉加载:请求未结束或没有下一页时直接返回,避免并发把同一页打两次 + if (!fresh && (this._loading || !this.data.hasMore)) { + return; + } + const seq = (this._listSeq || 0) + 1; + this._listSeq = seq; + this._loading = true; + const page = fresh ? 1 : this.data.page; + const { status, keywords, opType } = this.data; + if (fresh) { + wx.pageScrollTo({ scrollTop: 0 }); + } + if (!silent) { + wx.showLoading({ title: '加载中', mask: true }); } - wx.nextTick(() => { - console.log(this.data.hasMore, fresh); - if (!this.data.hasMore && !fresh) return; - }); - try { - const { page, status, keywords, opType } = this.data; - let params = { page, status, keywords, opType }; - // 下拉刷新自带 loading 指示,不再叠加 showLoading - if (!silent) { - wx.showLoading({ - title: '加载中', - }); + const res = await fetchDemandList({ page, status, keywords, opType }); + if (seq !== this._listSeq) { + return; } - const res = await fetchDemandList(params); - const nextList = res.data.data; - console.log(res) - console.log(nextList) - if (!silent) { - wx.hideLoading(); + if (!res || res.code !== 1 || !res.data) { + wx.showToast({ title: (res && res.msg) || '加载失败', icon: 'none' }); + return; } - - // 判断是否还有更多数据 - const { current_page, last_page } = res.data; - let newDemandList = fresh ? nextList : this.data.demandList.concat(nextList); - const hasMore = current_page < last_page; - - // 只有在还有更多页时才自增page + const nextList = Array.isArray(res.data.data) ? res.data.data : []; + const hasMore = res.data.current_page < res.data.last_page; + const start = fresh ? 0 : this.data.demandList.length; + const patch = { + hasMore, + isEmpty: start + nextList.length === 0, + }; if (hasMore) { - this.setData({ - demandList: newDemandList, - page: page + 1, - hasMore - }); + patch.page = page + 1; + } else if (fresh) { + patch.page = 1; + } + if (fresh) { + patch.demandList = nextList; } else { - this.setData({ - demandList: newDemandList, - hasMore: false + nextList.forEach((item, i) => { + patch[`demandList[${start + i}]`] = item; }); } - - this.setData({ - isEmpty: newDemandList.length > 0 ? false : true - }); - // 检查内容是否超出最大高度,放到 nextTick 里 - wx.nextTick(() => { - this.checkDescOverflow(); + this.setData(patch, () => { + if (seq !== this._listSeq) { + return; + } + const indices = []; + for (let i = start; i < this.data.demandList.length; i++) { + indices.push(i); + } + this.checkDescOverflow(indices); }); } catch (err) { - console.error('Failed to load demand list:', err); + if (seq === this._listSeq) { + console.error('Failed to load demand list:', err); + wx.showToast({ title: '网络异常,请重试', icon: 'none' }); + } + } finally { + if (seq === this._listSeq) { + this._loading = false; + wx.hideLoading(); + } } }, @@ -161,6 +160,13 @@ Page({ await this.loadDemandList(true); return; } + const prev = {}; + demandList.forEach((item) => { + prev[item.id] = { expanded: !!item.expanded, showToggle: !!item.showToggle }; + }); + const seq = (this._listSeq || 0) + 1; + this._listSeq = seq; + this._loading = true; try { const res = await fetchDemandList({ page: 1, @@ -169,39 +175,83 @@ Page({ keywords, opType }); - const nextList = res.data.data || []; - //不动 page,保持原有的分页进度;条目流转出当前标签后总数会变,需同步 hasMore + if (seq !== this._listSeq) { + return; + } + if (!res || res.code !== 1 || !res.data) { + return; + } + const raw = Array.isArray(res.data.data) ? res.data.data : []; + // 保留已展开状态,避免返回列表时把「查看全文」收回去 + const nextList = raw.map((item) => { + const old = prev[item.id]; + return { + ...item, + expanded: old ? old.expanded : false, + showToggle: old ? old.showToggle : false, + }; + }); this.setData({ demandList: nextList, hasMore: res.data.current_page < res.data.last_page, isEmpty: nextList.length === 0 - }); - wx.nextTick(() => { - this.checkDescOverflow(); + }, () => { + if (seq !== this._listSeq) { + return; + } + const indices = []; + nextList.forEach((item, index) => { + if (!prev[item.id]) { + indices.push(index); + } + }); + this.checkDescOverflow(indices); }); } catch (err) { - console.error('Failed to refresh demand list:', err); + if (seq === this._listSeq) { + console.error('Failed to refresh demand list:', err); + } + } finally { + if (seq === this._listSeq) { + this._loading = false; + } } }, - // 检查每条内容是否超出最大高度,决定是否显示“查看全文” - checkDescOverflow() { + /** + * 只测量指定下标的描述高度,按路径更新 showToggle + * 不改 expanded,加载下一页时已展开的条目保持展开 + * @param {number[]} indices + */ + checkDescOverflow(indices) { + if (!indices || !indices.length) { + return; + } + const seq = this._listSeq; const query = this.createSelectorQuery(); - const maxHeight = 60; // 120rpx ≈ 60px,实际可根据机型调整 - this.data.demandList.forEach((item, index) => { + const maxHeight = 60; // 120rpx ≈ 60px + indices.forEach((index) => { query.select(`#desc-${index}`).boundingClientRect(); }); query.exec((rects) => { - const newList = this.data.demandList.map((item, idx) => { - // 只有内容高度大于 maxHeight+1 时才显示“查看全文” - const showToggle = rects[idx] && rects[idx].height > (maxHeight + 1); - return { - ...item, - expanded: false, - showToggle - }; + if (seq !== this._listSeq || !rects) { + return; + } + const patch = {}; + rects.forEach((rect, i) => { + const index = indices[i]; + const item = this.data.demandList[index]; + if (!item) { + return; + } + const showToggle = !!(rect && rect.height > maxHeight + 1); + if (!!item.showToggle !== showToggle) { + patch[`demandList[${index}].showToggle`] = showToggle; + } }); - this.setData({ demandList: newList }); + if (Object.keys(patch).length) { + this.setData(patch); + } }); }, @@ -312,9 +362,10 @@ Page({ * 页面上拉触底事件的处理函数 */ onReachBottom() { - if (this.data.hasMore) { - this.loadDemandList(); + if (this._loading || !this.data.hasMore) { + return; } + this.loadDemandList(); }, /** diff --git a/frontend/miniapp/pages/demand/list/index.wxml b/frontend/miniapp/pages/demand/list/index.wxml index 9d5d794..9d6eae7 100644 --- a/frontend/miniapp/pages/demand/list/index.wxml +++ b/frontend/miniapp/pages/demand/list/index.wxml @@ -44,7 +44,7 @@ {{item.expanded ? '收起' : '查看全文'}} - + diff --git a/frontend/miniapp/pages/developer/join/index.js b/frontend/miniapp/pages/developer/join/index.js index 4193af0..bd09690 100644 --- a/frontend/miniapp/pages/developer/join/index.js +++ b/frontend/miniapp/pages/developer/join/index.js @@ -28,7 +28,7 @@ Page({ * 获取基础配置 */ getConfig() { - wx.$util.request(wx.$api.CommonGetMiniConfig, {}, 'GET').then(res => { + wx.$util.request(wx.$api.CommonGetMiniConfig, {}, 'GET', { auth: false }).then(res => { if (res.code === 1) { this.setData({ languageLabels: res.data.languageLabels || [], diff --git a/frontend/miniapp/pages/goods/list/index.js b/frontend/miniapp/pages/goods/list/index.js index da029ce..a908dc6 100644 --- a/frontend/miniapp/pages/goods/list/index.js +++ b/frontend/miniapp/pages/goods/list/index.js @@ -89,62 +89,87 @@ Page({ // 产品列表 async loadGoodsList(reset = false) { + if (!reset && (this._loading || this.data.loadMoreStatus !== 0)) { + return; + } + const seq = (this._listSeq || 0) + 1; + this._listSeq = seq; + this._loading = true; + const page = reset ? 1 : this.data.page; + const { pageSize, keywords, tabValue } = this.data; + this.setData({ + loadMoreStatus: 1, + loading: true, + }); try { - const { loadMoreStatus, goodsList = [], page, pageSize, keywords, tabValue } = this.data; - const params = { - page: reset ? 1 : page, + const result = await fetchGoodsList({ + page, categoryId: tabValue, pageSize, keywords, - }; - if (loadMoreStatus !== 0 && !reset) return; - this.setData({ - loadMoreStatus: 1, - loading: true, }); - const result = await fetchGoodsList(params); - console.log('fetchGoodsList', result) - const data = result.data; - if (result.code == 1) { - const { spuList, totalCount = 0 } = data; - if (totalCount === 0 && reset) { - this.setData({ - emptyInfo: { - tip: '抱歉,未找到相关商品', - }, - total: totalCount, - hasLoaded: true, - loadMoreStatus: 0, - loading: false, - goodsList: [], - }); - return; - } - const _goodsList = reset ? spuList : goodsList.concat(spuList); - const _loadMoreStatus = _goodsList.length === totalCount ? 2 : 0; - this.setData({ - total: totalCount, - page: params.page + 1, - goodsList: _goodsList, - loadMoreStatus: _loadMoreStatus, - }); - } else { + if (seq !== this._listSeq) { + return; + } + if (!result || result.code !== 1 || !result.data) { this.setData({ loading: false, + hasLoaded: true, + loadMoreStatus: 0, }); wx.showToast({ - title: '查询失败,请稍候重试', + title: (result && result.msg) || '查询失败,请稍候重试', + icon: 'none', + }); + return; + } + const { spuList = [], totalCount = 0 } = result.data; + if (totalCount === 0 && reset) { + this.setData({ + emptyInfo: { + tip: '抱歉,未找到相关商品', + }, + total: totalCount, + hasLoaded: true, + loadMoreStatus: 2, + loading: false, + goodsList: [], + page: 1, + }); + return; + } + const start = reset ? 0 : this.data.goodsList.length; + const loaded = start + spuList.length; + const patch = { + total: totalCount, + page: page + 1, + loadMoreStatus: loaded >= totalCount ? 2 : 0, + hasLoaded: true, + loading: false, + }; + if (reset) { + patch.goodsList = spuList; + } else { + spuList.forEach((item, i) => { + patch[`goodsList[${start + i}]`] = item; }); } + this.setData(patch); } catch (error) { + if (seq !== this._listSeq) { + return; + } + console.error('loadGoodsList failed:', error); this.setData({ loading: false, + hasLoaded: true, + loadMoreStatus: 0, }); + } finally { + if (seq === this._listSeq) { + this._loading = false; + } } - this.setData({ - hasLoaded: true, - loading: false, - }); }, @@ -164,8 +189,11 @@ Page({ }, onReachBottom() { + if (this._loading || this.data.loadMoreStatus !== 0) { + return; + } const { goodsList, total } = this.data; - if (goodsList.length === total) { + if (goodsList.length >= total) { this.setData({ loadMoreStatus: 2, }); diff --git a/frontend/miniapp/pages/message/list/index.js b/frontend/miniapp/pages/message/list/index.js index 5a1e83c..d495c52 100644 --- a/frontend/miniapp/pages/message/list/index.js +++ b/frontend/miniapp/pages/message/list/index.js @@ -24,17 +24,18 @@ Page({ loading: false }, - onLoad() { - this.loadList(true); - }, - loadList(refresh = false) { - let { pageSize, list, currentType } = this.data; + if (!refresh && (this._loading || !this.data.hasMore)) { + return; + } + const seq = (this._listSeq || 0) + 1; + this._listSeq = seq; + this._loading = true; + const { pageSize, currentType } = this.data; const page = refresh ? 1 : this.data.page; - - let params = { - page: page, - pageSize: pageSize, + const params = { + page, + pageSize, }; //全部时不传 message_type,由后端返回全类型 if (currentType !== '') { @@ -42,25 +43,52 @@ Page({ } fetchMessageList(params).then((res) => { - let newList = res.data.data; - let hasMore = res.data.current_page < res.data.last_page; - let unreadMap = res.data.unread_map || { '0': 0, '1': 0, '2': 0 }; + if (seq !== this._listSeq) { + return; + } + if (!res || res.code !== 1 || !res.data) { + wx.showToast({ title: (res && res.msg) || '加载失败', icon: 'none' }); + return; + } + const nextList = Array.isArray(res.data.data) ? res.data.data : []; + const hasMore = res.data.current_page < res.data.last_page; + const unreadMap = res.data.unread_map || { '0': 0, '1': 0, '2': 0 }; //直接用后端统计的全量未读总数(含所有类型), //不再由前端累加 0/1/2,避免 message_type 超出这三个值时漏算 - let totalUnread = res.data.unread_total !== undefined && res.data.unread_total !== null + const totalUnread = res.data.unread_total !== undefined && res.data.unread_total !== null ? Number(res.data.unread_total) || 0 : Number(unreadMap['0'] || 0) + Number(unreadMap['1'] || 0) + Number(unreadMap['2'] || 0); - this.setData({ - list: refresh ? newList : list.concat(newList), - page: page + 1, + const patch = { + page: hasMore ? page + 1 : page, unreadCount: res.data.unread_count, - unreadMap: unreadMap, - totalUnread: totalUnread, - hasMore: hasMore, - loading: false - }); - //同步底部tabbar红点 + unreadMap, + totalUnread, + hasMore, + loading: false, + }; + if (refresh) { + patch.list = nextList; + } else { + const start = this.data.list.length; + nextList.forEach((item, i) => { + patch[`list[${start + i}]`] = item; + }); + } + this.setData(patch); this.syncTabBarUnread(totalUnread); + }).catch((err) => { + if (seq !== this._listSeq) { + return; + } + console.error('loadList failed:', err); + wx.showToast({ title: '网络异常,请重试', icon: 'none' }); + }).then(() => { + if (seq !== this._listSeq) { + return; + } + this._loading = false; + this.setData({ loading: false }); + wx.stopPullDownRefresh(); }); }, @@ -99,15 +127,29 @@ Page({ }, onReachBottom() { - if (this.data.hasMore) { - this.loadList(); + if (this._loading || !this.data.hasMore) { + return; } + this.loadList(); }, onClearUnreadTap() { - messageSetRead({ isAll: 1 }).then(() => { + if (this._clearing) { + return; + } + this._clearing = true; + messageSetRead({ isAll: 1 }).then((res) => { + if (!res || res.code !== 1) { + wx.showToast({ title: (res && res.msg) || '操作失败', icon: 'none' }); + return; + } wx.showToast({ title: '全部已读', icon: 'success' }); this.loadList(true); + }).catch((err) => { + console.error('messageSetRead failed:', err); + wx.showToast({ title: '网络异常,请重试', icon: 'none' }); + }).then(() => { + this._clearing = false; }); }, diff --git a/frontend/miniapp/services/good/fetchCategoryList.js b/frontend/miniapp/services/good/fetchCategoryList.js index 4e32563..c4fd51b 100644 --- a/frontend/miniapp/services/good/fetchCategoryList.js +++ b/frontend/miniapp/services/good/fetchCategoryList.js @@ -1,5 +1,4 @@ import { config } from '../../config/index'; -import { CatalogList } from '../../config/api'; /** 获取商品列表 */ function mockFetchGoodCategory() { @@ -13,20 +12,10 @@ export function getCategoryList() { if (config.useMock) { return mockFetchGoodCategory(); } - return new Promise((resolve, reject) => { - wx.request({ - url: CatalogList, - method: 'GET', - success: (res) => { - if (res.statusCode === 200) { - resolve(res.data.data); - } else { - reject(new Error('获取分类列表失败')); - } - }, - fail: (err) => { - reject(err); - } - }); + return wx.$util.request(wx.$api.CatalogList, {}, 'GET', { auth: false }).then((res) => { + if (!res || res.code !== 1) { + return Promise.reject(new Error((res && res.msg) || '获取分类列表失败')); + } + return res.data; }); } diff --git a/frontend/miniapp/services/good/fetchGood.js b/frontend/miniapp/services/good/fetchGood.js index 1037546..5ca9b43 100644 --- a/frontend/miniapp/services/good/fetchGood.js +++ b/frontend/miniapp/services/good/fetchGood.js @@ -12,5 +12,5 @@ export function fetchGood(ID = 0) { if (config.useMock) { return mockFetchGood(ID); } - return wx.$util.request(wx.$api.GoodsDetail, { id: ID }, 'GET'); + return wx.$util.request(wx.$api.GoodsDetail, { id: ID }, 'GET', { auth: false }); } diff --git a/frontend/miniapp/services/home/home.js b/frontend/miniapp/services/home/home.js index acc3aa8..ee17e7d 100644 --- a/frontend/miniapp/services/home/home.js +++ b/frontend/miniapp/services/home/home.js @@ -54,12 +54,12 @@ export function fetchHome() { /** 获取配置数据 */ export function fetchConfig() { - return wx.$util.request(wx.$api.CommonGetMiniConfig, {}, 'GET'); + return wx.$util.request(wx.$api.CommonGetMiniConfig, {}, 'GET', { auth: false }); } /** 获取首页轮播(分类类型 banner) */ export function fetchHomeBanners() { - return wx.$util.request(wx.$api.CatalogList, { type: 'banner' }, 'GET'); + return wx.$util.request(wx.$api.CatalogList, { type: 'banner' }, 'GET', { auth: false }); } // 与后端 site.platform_commission_ratio 默认值一致,接口失败时兜底 diff --git a/frontend/miniapp/utils/util.js b/frontend/miniapp/utils/util.js index 6dbd48e..20f5ecb 100644 --- a/frontend/miniapp/utils/util.js +++ b/frontend/miniapp/utils/util.js @@ -1,6 +1,5 @@ import dayjs from 'dayjs'; var api = require('../config/api.js'); -var util = require('../utils/util.js'); const formatTime = (date, template) => dayjs(date).format(template); @@ -55,63 +54,147 @@ function testMobile(num) { } } +const REQUEST_TIMEOUT = 15000; +let tokenCache = ''; +let tokenLoaded = false; +let reloginTask = null; + +function getAppSafe() { + try { + return getApp(); + } catch (err) { + return null; + } +} + +/** 内存缓存 token,避免每次请求都读 Storage */ +function getToken() { + if (!tokenLoaded) { + tokenCache = wx.getStorageSync('token') || ''; + tokenLoaded = true; + } + return tokenCache; +} + +function setToken(token) { + tokenCache = token || ''; + tokenLoaded = true; + if (tokenCache) { + wx.setStorageSync('token', tokenCache); + } else { + wx.removeStorageSync('token'); + } +} + /** - * 封封微信的的request + * 本地还没有 token 时,等冷启动登录结束再发需要鉴权的请求 + * 已有 token 则直接发,过期由 401 重登处理 */ -function request(url, data = {}, method = "GET") { - return new Promise(function (resolve, reject) { - wx.request({ - url: url, - data: data, - method: method, - header: { - 'Content-Type': 'application/json', - // 'XDEBUG_TRIGGER': 'true', - 'token': wx.getStorageSync('token') - }, - success: function (res) { - if (res.statusCode == 200) { +function ensureLogin() { + if (getToken()) { + return Promise.resolve(); + } + const app = getAppSafe(); + if (app && app.loginReady) { + return app.loginReady; + } + return Promise.resolve(); +} - if (res.data.errno == 401) { - //需要登录后才可以操作 +function isUnauthorized(statusCode, body) { + return statusCode === 401 || !!(body && body.code === 401); +} - // let code = null; - // return login().then((res) => { - // code = res.code; - // return getUserInfo(); - // }).then((userInfo) => { - // //登录远程服务器 - // request(api.AuthLoginByWeixin, { - // code: code, - // userInfo: userInfo - // }, 'POST').then(res => { - // if (res.errno === 0) { - // //存储用户信息 - // wx.setStorageSync('userInfo', res.data.userInfo); - // wx.setStorageSync('token', res.data.token); - // resolve(res); - // } else { - // reject(res); - // } - // }).catch((err) => { - // reject(err); - // }); - // }).catch((err) => { - // reject(err); - // }) - } else { - resolve(res.data); - } - } else { - reject(res.errMsg); +/** 登录失效后重新换 token,进行中的重登合并成一次 */ +function relogin() { + const app = getAppSafe(); + if (app && app.loggingIn && app.loginReady) { + return app.loginReady.then(() => !!getToken()); + } + if (reloginTask) { + return reloginTask; + } + reloginTask = login() + .then((wxRes) => request(api.AuthLoginByWeixin, { code: wxRes.code }, 'POST', { auth: false })) + .then((res) => { + if (res && res.code === 1 && res.data && res.data.token) { + setToken(res.data.token); + if (res.data.userinfo) { + wx.setStorageSync('userInfo', res.data.userinfo); } - - }, - fail: function (err) { - reject(err) + return true; } + return false; }) + .catch((err) => { + console.error('relogin failed:', err); + return false; + }) + .then((ok) => { + reloginTask = null; + return ok; + }); + return reloginTask; +} + +/** + * 统一请求 + * @param {string} url + * @param {object} data + * @param {string} method + * @param {{auth?: boolean, timeout?: number, retried?: boolean}} options + * auth 默认 true:没有 token 时等待登录,401 时重登并重试一次 + * auth 为 false:免登录接口(分类、首页配置等)立即发送,不等待登录 + */ +function request(url, data = {}, method = 'GET', options = {}) { + const auth = options.auth !== false; + const timeout = options.timeout || REQUEST_TIMEOUT; + const retried = !!options.retried; + + const send = () => new Promise((resolve, reject) => { + const header = { 'Content-Type': 'application/json' }; + const token = getToken(); + if (token) { + header.token = token; + } + wx.request({ + url, + data, + method, + timeout, + header, + success(res) { + const body = res.data && typeof res.data === 'object' + ? res.data + : { code: 0, msg: '网络异常', data: null }; + if (res.statusCode === 200 || isUnauthorized(res.statusCode, body)) { + resolve({ unauthorized: isUnauthorized(res.statusCode, body), body }); + return; + } + reject((body && body.msg) || res.errMsg || '网络异常'); + }, + fail(err) { + reject(err); + }, + }); }); + + const dispatch = () => send().then((result) => { + if (result.unauthorized && auth && !retried) { + return relogin().then((ok) => { + if (!ok) { + return result.body; + } + return request(url, data, method, { auth: true, timeout, retried: true }); + }); + } + return result.body; + }); + + if (!auth) { + return dispatch(); + } + return ensureLogin().then(dispatch); } /** @@ -194,40 +277,6 @@ function showSuccessToast(msg) { }) } -function sentRes(url, data, method, fn) { - data = data || null; - if (data == null) { - var content = require('querystring').stringify(data); - } else { - var content = JSON.stringify(data); //json format - } - - var parse_u = require('url').parse(url, true); - var isHttp = parse_u.protocol == 'http:'; - var options = { - host: parse_u.hostname, - port: parse_u.port || (isHttp ? 80 : 443), - path: parse_u.path, - method: method, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(content, "utf8"), - 'Trackingmore-Api-Key': '1b70c67e-d191-4301-9c05-a50436a2526d' - } - }; - var req = require(isHttp ? 'http' : 'https').request(options, function (res) { - var _data = ''; - res.on('data', function (chunk) { - _data += chunk; - }); - res.on('end', function () { - fn != undefined && fn(_data); - }); - }); - req.write(content); - req.end(); -} - function loginNow() { let userInfo = wx.getStorageSync('userInfo'); if (userInfo == '') { @@ -235,10 +284,10 @@ function loginNow() { success: (res) => { request(api.AuthLoginByWeixin, { code: res.code - }, 'POST').then(function (res) { + }, 'POST', { auth: false }).then(function (res) { if (res.code === 1) { let userInfo = res.data.userInfo; - wx.setStorageSync('token', res.data.token); + setToken(res.data.token); wx.setStorageSync('userInfo', userInfo); } }); @@ -563,6 +612,8 @@ module.exports = { phoneRegCheck, formatTimeNum: formatTimeNum, request, + getToken, + setToken, redirect, showErrorToast, showSuccessToast, @@ -570,7 +621,6 @@ module.exports = { login, getUserInfo, testMobile, - sentRes, loginNow, getTextLength, transferBorder,