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,