From 25a3dba93188783336edc323de4adf18fd28f742 Mon Sep 17 00:00:00 2001
From: dzc <2388224167@qq.com>
Date: Thu, 30 Jul 2026 20:20:24 +0800
Subject: [PATCH] =?UTF-8?q?fix:bug=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env | 14 +-
src/App.vue | 50 ++-
src/common/chat-token-manager.js | 106 ++++-
src/common/request.js | 19 +-
src/common/wssocket.js | 290 ++++++++++----
src/main.ts | 10 +-
src/pages/home/city.vue | 201 ++++++----
src/pages/home/detail.vue | 1 +
src/pages/home/report.vue | 1 +
src/pages/message/chat-box.vue | 1 +
src/pages/message/friend.vue | 1 +
src/pages/message/user-info.vue | 1 +
src/pages/moments/publish.vue | 368 ++++++++++++------
src/pages/profile/about.vue | 1 +
src/pages/profile/apply-vip-record.vue | 3 +-
src/pages/profile/apply-vip.vue | 97 +++--
src/pages/profile/black-list.vue | 1 +
src/pages/profile/face-verify/boy-verify.vue | 146 +++----
.../profile/face-verify/girl-verify-1.vue | 1 +
.../profile/face-verify/girl-verify-2.vue | 1 +
.../profile/face-verify/girl-verify-3.vue | 1 +
.../profile/face-verify/girl-verify-4.vue | 253 +++++++-----
src/pages/profile/feedback.vue | 1 +
src/pages/profile/gender-choose.vue | 1 +
src/pages/profile/like.vue | 1 +
src/pages/profile/my-moment.vue | 38 +-
src/pages/profile/pay-result.vue | 1 +
src/pages/profile/recharge-record.vue | 1 +
src/pages/profile/recharge.vue | 137 +++++--
src/pages/profile/set-wechat.vue | 1 +
src/pages/profile/setting.vue | 1 +
src/pages/profile/upload-album.vue | 1 +
src/pages/profile/userinfo-simple.vue | 3 +-
src/pages/profile/userinfo.vue | 1 +
src/pages/profile/verify-status.vue | 1 +
src/pages/profile/view-record.vue | 1 +
src/pages/tabbar/home.vue | 192 ++++-----
src/pages/tabbar/message.vue | 1 +
src/pages/tabbar/moments.vue | 1 +
src/pages/tabbar/profile.vue | 1 +
src/pages/user/goods_details_store/index.vue | 45 +--
src/pages/wechat-tabbar/index.vue | 1 +
src/pages/wechat-tabbar/me.vue | 3 +-
src/pages/wechat/pages/gift/gift-record.vue | 3 +-
src/pages/wechat/pages/gift/history.vue | 3 +-
src/pages/wechat/pages/gift/index.vue | 3 +-
src/pages/wechat/pages/inviter/index.vue | 1 +
src/pages/wechat/pages/poster.vue | 3 +-
src/pages/wechat/pages/withdraw.vue | 3 +-
src/pages/wechat/pages/withraw-record.vue | 1 +
src/sheep/api/member/auth.js | 33 ++
src/sheep/api/member/recent.ts | 27 ++
.../components/moment-card/moment-card.vue | 322 +++++++++------
src/sheep/components/s-layout/s-layout.vue | 6 +-
src/sheep/config/index.js | 9 +
src/sheep/platform/index.js | 26 +-
src/sheep/platform/pay.js | 151 ++++---
src/sheep/platform/status-bar.js | 104 +++++
src/sheep/request/index.js | 5 +-
src/sheep/store/user.js | 5 +-
src/sheep/ui/su-video/su-video.vue | 35 +-
src/sheep/util/location.ts | 108 +++++
src/sheep/util/wechat-tool.ts | 2 +-
src/shime-uni.d.ts | 5 +-
64 files changed, 1980 insertions(+), 875 deletions(-)
create mode 100644 src/sheep/platform/status-bar.js
create mode 100644 src/sheep/util/location.ts
diff --git a/.env b/.env
index 1e542811..9a2fa39e 100644
--- a/.env
+++ b/.env
@@ -1,7 +1,10 @@
-# 打包目标
-# H5 - 全部都是使用H5支付
-# APP - 全部都是使用APP支付
-SHOPRO_BUILD_TARGET=H5
+# 支付目标
+# H5 - 强制使用 H5/WAP 支付渠道
+# APP - 非微信小程序端跳转到微信小程序支付
+# SHOPRO_BUILD_TARGET 为兼容旧配置的默认值,下面两项可分别覆盖支付宝和微信支付
+SHOPRO_BUILD_TARGET=APP
+SHOPRO_ALIPAY_BUILD_TARGET=H5
+SHOPRO_WECHAT_BUILD_TARGET=APP
# 版本号
SHOPRO_VERSION=v2.4.1
@@ -12,6 +15,7 @@ SHOPRO_API_PATH=/app-api
# 后端接口 - 测试环境(通过 process.env.NODE_ENV == development)
# SHOPRO_DEV_BASE_URL = https://api.htysapp.com
SHOPRO_DEV_BASE_URL=https://baac9a029ea84f36ba28c023fed4fc5f.hd.takin.cc
+# SHOPRO_DEV_BASE_URL=http://localhost:48083
# 后端接口 - 正式环境(通过 process.env.NODE_ENV != development)
SHOPRO_BASE_URL=https://api.htysapp.com
@@ -26,7 +30,7 @@ SHOPRO_WEBSOCKET_PATH=/infra/ws
SHOPRO_STATIC_URL=http://test.yudao.iocoder.cn
### SHOPRO_STATIC_URL = https://file.sheepjs.com
-# 前端 H5 访问域名
+# 前端 H5 访问域名(App 调起 H5 支付时作为支付完成后的回跳地址)
SHOPRO_H5_URL=http://127.0.0.1:3000
# 是否开启直播 1 开启直播 | 0 关闭直播
diff --git a/src/App.vue b/src/App.vue
index dc125078..800dadeb 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -7,9 +7,24 @@ import * as enums from './common/enums';
import * as wsApi from './common/wssocket';
import chatTokenManager from './common/chat-token-manager';
import CHAT_ENV from '@/chat-env.js';
+import AuthUtil from '@/sheep/api/member/auth';
import useChatStore from '@/chat-store/chatStore.js';
+const reissueChatToken = async () => {
+ const response = await AuthUtil.reissueChatToken();
+ const chatToken = response?.data?.chatToken;
+ if (response?.code !== 0 || !chatToken) {
+ const error = new Error(response?.msg || '重新签发聊天 token 失败');
+ error.code = response?.code;
+ throw error;
+ }
+ return chatToken;
+};
+
+// 页面可能早于异步 onLaunch 完成加载,需在模块初始化时就注册重签能力。
+chatTokenManager.setReissueToken(reissueChatToken);
+
export default {
/** ↓↓↓ 以下为app启动聊天相关模块初始化,WebSocket链接,加载聊天消息,加载离线消息,加载群组消息,初始化聊天相关的全局状态仓库 */
data() {
@@ -75,7 +90,11 @@ export default {
this.refreshUnreadBadge()
} catch (error) {
console.log("初始化聊天:", error);
- this.exit();
+ this.configStore?.setAppInit(false);
+ uni.showToast({
+ title: '聊天服务暂时不可用,请稍后重试',
+ icon: 'none',
+ });
}
},
initWebSocket() {
@@ -344,7 +363,6 @@ export default {
handleSystemMessage(msg) {
if (msg.type == enums.MESSAGE_TYPE.USER_BANNED) {
// 用户被封禁
- wsApi.close(3099);
uni.showModal({
content: '您的账号已被管理员封禁,原因:' + msg.content,
showCancel: false,
@@ -433,7 +451,7 @@ export default {
console.log('exit');
this.isExit = true;
chatTokenManager.stop();
- wsApi.close(3099);
+ wsApi.destroy(3099);
uni.removeStorageSync('loginInfo');
uni.reLaunch({
url: '/pages/login/login',
@@ -447,19 +465,19 @@ export default {
// this.audioTip.play();
},
startChatTokenCheck() {
- // 轮询只处理聊天 token;仅凭证明确失效时退出,网络错误等待下次检查。
+ // IM 双 token 都过期时,使用仍有效的主站登录态重新签发。
chatTokenManager.start({
+ reissueToken: reissueChatToken,
onRefreshError: (error) => {
- const code = error?.code ?? error?.data?.code;
- if (code == 400 || code == 401) {
- this.exit();
- }
+ console.warn('聊天 token 自动续期失败,等待下次重试', error);
},
});
},
reconnectWs() {
- // 已退出则不再重连
- if (this.isExit) {
+ let loginInfo = uni.getStorageSync('loginInfo');
+ // 退出时可能已有异步重连排队,需要同时校验主站和 IM 登录态。
+ if (this.isExit || !uni.getStorageSync('token') || !loginInfo?.accessToken) {
+ this.reconnecting = false;
return;
}
// 记录标志
@@ -468,11 +486,19 @@ export default {
this.userStore
.loadUser()
.then((userInfo) => {
+ loginInfo = uni.getStorageSync('loginInfo');
+ if (this.isExit || !uni.getStorageSync('token') || !loginInfo?.accessToken) {
+ this.reconnecting = false;
+ return;
+ }
// 重新连接
- let loginInfo = uni.getStorageSync('loginInfo');
wsApi.reconnect(CHAT_ENV.WS_URL, loginInfo.accessToken);
})
.catch(() => {
+ if (this.isExit || !uni.getStorageSync('token')) {
+ this.reconnecting = false;
+ return;
+ }
// 5s后重试
setTimeout(() => {
this.reconnectWs();
@@ -528,7 +554,7 @@ export default {
// #ifndef MP-WEIXIN
this.closeSplashscreen(1000); // 延迟1s,避免用户看到页面跳转
let loginInfo = uni.getStorageSync('loginInfo');
- if (!loginInfo || !loginInfo.refreshToken) {
+ if (!uni.getStorageSync('token')) {
this.closeSplashscreen(0);
return;
}
diff --git a/src/common/chat-token-manager.js b/src/common/chat-token-manager.js
index 3e6e4d33..80cc3dbb 100644
--- a/src/common/chat-token-manager.js
+++ b/src/common/chat-token-manager.js
@@ -10,6 +10,8 @@ class ChatTokenManager {
// 所有调用共享同一个刷新 Promise,避免并发刷新后 token 相互覆盖。
this.refreshPromise = null;
this.onRefreshError = null;
+ // IM 的 refreshToken 也失效时,由主站登录态重新签发一套聊天 token。
+ this.reissueToken = null;
// 用于忽略定时器停止后才返回的旧检查结果。
this.monitorVersion = 0;
}
@@ -17,9 +19,13 @@ class ChatTokenManager {
// 启动时立即检查一次,之后每 30 秒检查一次;重复调用不会创建多个定时器。
start(options = {}) {
this.onRefreshError = options.onRefreshError || null;
+ if (options.reissueToken) {
+ this.reissueToken = options.reissueToken;
+ }
const loginInfo = uni.getStorageSync('loginInfo');
- if (!loginInfo?.refreshToken) {
+ const canReissueFromMainLogin = uni.getStorageSync('token') && this.reissueToken;
+ if (!loginInfo?.refreshToken && !canReissueFromMainLogin) {
this.stop();
return;
}
@@ -44,6 +50,10 @@ class ChatTokenManager {
this.onRefreshError = null;
}
+ setReissueToken(reissueToken) {
+ this.reissueToken = reissueToken;
+ }
+
runCheck(monitorVersion = this.monitorVersion) {
this.checkAccessToken().catch((error) => {
if (monitorVersion !== this.monitorVersion) {
@@ -57,6 +67,9 @@ class ChatTokenManager {
checkAccessToken() {
const loginInfo = uni.getStorageSync('loginInfo');
if (!loginInfo?.refreshToken) {
+ if (uni.getStorageSync('token') && typeof this.reissueToken === 'function') {
+ return this.refreshAccessToken(loginInfo);
+ }
this.stop();
return Promise.resolve(null);
}
@@ -74,6 +87,17 @@ class ChatTokenManager {
return false;
}
+ const loginInfo = uni.getStorageSync('loginInfo');
+ if (
+ loginInfo?.accessToken === accessToken &&
+ Number.isFinite(Number(loginInfo.accessTokenExpiresAt))
+ ) {
+ return (
+ Number(loginInfo.accessTokenExpiresAt) - Date.now() <=
+ TOKEN_REFRESH_THRESHOLD_SECONDS * 1000
+ );
+ }
+
try {
// JWT 第二段是 payload,H5 和 App 均通过 uni API 完成 Base64 解码。
const payload = accessToken.split('.')[1];
@@ -103,33 +127,50 @@ class ChatTokenManager {
}
refreshAccessToken(loginInfo = uni.getStorageSync('loginInfo')) {
- if (!loginInfo?.refreshToken) {
- return Promise.reject(new Error('缺少聊天 refreshToken'));
- }
-
if (this.refreshPromise) {
return this.refreshPromise;
}
- const refreshToken = loginInfo.refreshToken;
- this.refreshPromise = this.requestRefreshToken(refreshToken)
- .then((res) => {
- if (!res || res.data?.code != 200) {
- throw res?.data || new Error('刷新聊天 token 失败');
+ const refreshVersion = this.monitorVersion;
+ const accessToken = loginInfo?.accessToken;
+ const refreshToken = loginInfo?.refreshToken;
+ const refreshRequest = refreshToken
+ ? this.requestRefreshToken(refreshToken).then((res) => {
+ if (!res || res.data?.code != 200 || !res.data?.data) {
+ throw res?.data || new Error('刷新聊天 token 失败');
+ }
+ return res.data.data;
+ })
+ : Promise.reject(new Error('缺少聊天 refreshToken'));
+
+ this.refreshPromise = refreshRequest
+ .catch((error) => {
+ if (!this.canReissueToken(error, refreshToken)) {
+ throw error;
+ }
+ return this.reissueToken();
+ })
+ .then((newLoginInfo) => {
+ if (refreshVersion !== this.monitorVersion) {
+ throw new Error('聊天登录状态已停止,忽略本次 token 刷新结果');
+ }
+ if (!newLoginInfo?.accessToken || !newLoginInfo?.refreshToken) {
+ throw new Error('重新签发的聊天 token 不完整');
}
// 刷新期间用户可能退出或重新登录,旧请求结果不能覆盖新的登录状态。
const currentLoginInfo = uni.getStorageSync('loginInfo');
- if (currentLoginInfo?.refreshToken !== refreshToken) {
+ const loginChanged = refreshToken
+ ? currentLoginInfo?.refreshToken !== refreshToken
+ : accessToken && currentLoginInfo?.accessToken !== accessToken;
+ if (loginChanged) {
if (currentLoginInfo?.accessToken) {
return currentLoginInfo;
}
throw new Error('聊天登录状态已变化,忽略本次 token 刷新结果');
}
- const newLoginInfo = res.data.data;
- uni.setStorageSync('loginInfo', newLoginInfo);
- return newLoginInfo;
+ return this.saveTokenInfo(newLoginInfo);
})
.finally(() => {
this.refreshPromise = null;
@@ -138,6 +179,43 @@ class ChatTokenManager {
return this.refreshPromise;
}
+ canReissueToken(error, refreshToken) {
+ if (!uni.getStorageSync('token') || typeof this.reissueToken !== 'function') {
+ return false;
+ }
+ if (!refreshToken) {
+ return true;
+ }
+
+ const code = error?.code ?? error?.data?.code ?? error?.statusCode;
+ const message = error?.message ?? error?.data?.message ?? error?.errMsg ?? '';
+ return (
+ code == 400 ||
+ code == 401 ||
+ message.includes('登录信息已过期') ||
+ message.includes('refreshToken已过期')
+ );
+ }
+
+ saveTokenInfo(tokenInfo) {
+ if (!tokenInfo) {
+ return tokenInfo;
+ }
+
+ const now = Date.now();
+ const storedTokenInfo = { ...tokenInfo };
+ const accessTokenExpiresIn = Number(tokenInfo.accessTokenExpiresIn);
+ const refreshTokenExpiresIn = Number(tokenInfo.refreshTokenExpiresIn);
+ if (Number.isFinite(accessTokenExpiresIn) && accessTokenExpiresIn > 0) {
+ storedTokenInfo.accessTokenExpiresAt = now + accessTokenExpiresIn * 1000;
+ }
+ if (Number.isFinite(refreshTokenExpiresIn) && refreshTokenExpiresIn > 0) {
+ storedTokenInfo.refreshTokenExpiresAt = now + refreshTokenExpiresIn * 1000;
+ }
+ uni.setStorageSync('loginInfo', storedTokenInfo);
+ return storedTokenInfo;
+ }
+
requestRefreshToken(refreshToken) {
return new Promise((resolve, reject) => {
uni.request({
diff --git a/src/common/request.js b/src/common/request.js
index 3a109ff2..fc64ef01 100644
--- a/src/common/request.js
+++ b/src/common/request.js
@@ -1,7 +1,9 @@
import CHAT_ENV from '@/chat-env.js';
import chatTokenManager from './chat-token-manager';
-const request = (options) => {
+const MAX_AUTH_RETRY_COUNT = 2;
+
+const request = (options, authRetryCount = 0) => {
const header = options.header || {};
const loginInfo = uni.getStorageSync('loginInfo');
if (loginInfo) {
@@ -23,33 +25,32 @@ const request = (options) => {
await chatTokenManager.refreshAccessToken(currentLoginInfo);
} catch (error) {
console.log('刷新token失败', error);
- getApp().$vm.exit();
return reject(error);
}
}
return resolve(res.data.data);
- } else if (res.data.code == 400) {
- console.log('code 400');
- getApp().$vm.exit();
- } else if (res.data.code == 401) {
+ } else if (res.data.code == 400 || res.data.code == 401) {
console.log('token失效,尝试重新获取');
const currentLoginInfo = uni.getStorageSync('loginInfo');
+ if (authRetryCount >= MAX_AUTH_RETRY_COUNT) {
+ return reject(res.data);
+ }
+
// 当前请求使用的是旧 token,直接用已刷新的 token 重试。
if (
currentLoginInfo?.accessToken &&
currentLoginInfo.accessToken !== header.accessToken
) {
- return resolve(request(options));
+ return resolve(request(options, authRetryCount + 1));
}
try {
await chatTokenManager.refreshAccessToken(currentLoginInfo);
// 重新发送刚才的请求
- return resolve(request(options));
+ return resolve(request(options, authRetryCount + 1));
} catch (error) {
console.log('刷新token失败', error);
- getApp().$vm.exit();
return reject(error);
}
} else {
diff --git a/src/common/wssocket.js b/src/common/wssocket.js
index 44891d4a..fc45eaf0 100644
--- a/src/common/wssocket.js
+++ b/src/common/wssocket.js
@@ -1,129 +1,266 @@
let accessToken = '';
+let wsUrl = '';
let messageCallBack = null;
let closeCallBack = null;
let connectCallBack = null;
-let isConnect = false; //连接标识 避免重复连接
+let isConnect = false;
+let enabled = false;
let rec = null;
-let lastConnectTime = new Date(); // 最后一次连接时间
+let connectRetryTimer = null;
+let lastConnectTime = 0;
let socketTask = null;
-let connect = (wsurl, token) => {
- accessToken = token;
- if (isConnect) {
+const clearReconnectTimers = () => {
+ if (rec) {
+ clearTimeout(rec);
+ rec = null;
+ }
+ if (connectRetryTimer) {
+ clearTimeout(connectRetryTimer);
+ connectRetryTimer = null;
+ }
+};
+
+const closeSocketTask = (task, code = 1000) => {
+ if (!task) {
return;
}
- lastConnectTime = new Date();
- socketTask = uni.connectSocket({
- url: wsurl,
- success: (res) => {},
- fail: (e) => {
- console.log(e);
- console.log('WebSocket连接失败,10s后重连');
- setTimeout(() => {
- connect();
- }, 10000);
- },
- });
+ try {
+ task.close({
+ code,
+ fail: (error) => {
+ console.log('关闭websocket连接失败', error);
+ },
+ });
+ } catch (error) {
+ console.log('关闭websocket连接失败', error);
+ }
+};
- socketTask.onOpen((res) => {
+const finishConnection = (task, event) => {
+ // 旧连接可能晚于新连接触发事件,不能让它污染当前连接状态。
+ if (task !== socketTask) {
+ return;
+ }
+ socketTask = null;
+ isConnect = false;
+ heartCheck.stop();
+ closeCallBack && closeCallBack(event);
+};
+
+const scheduleConnectRetry = (url, token) => {
+ if (!enabled) {
+ return;
+ }
+ if (connectRetryTimer) {
+ clearTimeout(connectRetryTimer);
+ }
+ connectRetryTimer = setTimeout(() => {
+ connectRetryTimer = null;
+ if (!enabled || socketTask) {
+ return;
+ }
+ connect(url, token);
+ }, 10000);
+};
+
+let connect = (url, token) => {
+ if (!url || !token) {
+ console.warn('缺少WebSocket地址或登录凭证,已跳过连接');
+ return;
+ }
+
+ wsUrl = url;
+ accessToken = token;
+ enabled = true;
+ clearReconnectTimers();
+ if (socketTask) {
+ return;
+ }
+
+ lastConnectTime = Date.now();
+ let task = null;
+ try {
+ task = uni.connectSocket({
+ url,
+ success: () => {},
+ fail: (error) => {
+ if (!enabled || (task && task !== socketTask)) {
+ return;
+ }
+ console.log(error);
+ console.log('WebSocket连接失败,10s后重连');
+ socketTask = null;
+ isConnect = false;
+ scheduleConnectRetry(url, token);
+ },
+ });
+ } catch (error) {
+ console.log('WebSocket连接失败,10s后重连', error);
+ scheduleConnectRetry(url, token);
+ return;
+ }
+
+ if (!task) {
+ scheduleConnectRetry(url, token);
+ return;
+ }
+ socketTask = task;
+
+ task.onOpen(() => {
+ if (task !== socketTask || !enabled) {
+ closeSocketTask(task);
+ return;
+ }
console.log('WebSocket连接成功');
isConnect = true;
- // 发送登录命令
- let loginInfo = {
- cmd: 0,
- data: {
- accessToken: accessToken,
- },
- };
- socketTask.send({
- data: JSON.stringify(loginInfo),
+ if (connectRetryTimer) {
+ clearTimeout(connectRetryTimer);
+ connectRetryTimer = null;
+ }
+ task.send({
+ data: JSON.stringify({
+ cmd: 0,
+ data: {
+ accessToken: token,
+ },
+ }),
});
});
- socketTask.onMessage((res) => {
- let sendInfo = JSON.parse(res.data);
+ task.onMessage((res) => {
+ if (task !== socketTask || !enabled) {
+ return;
+ }
+ let sendInfo;
+ try {
+ sendInfo = JSON.parse(res.data);
+ } catch (error) {
+ console.warn('WebSocket消息解析失败', error);
+ return;
+ }
+
if (sendInfo.cmd == 0) {
heartCheck.start();
connectCallBack && connectCallBack();
console.log('WebSocket登录成功');
} else if (sendInfo.cmd == 1) {
- // 重新开启心跳定时
heartCheck.reset();
} else {
- // 其他消息转发出去
console.log('接收到消息', sendInfo);
messageCallBack && messageCallBack(sendInfo.cmd, sendInfo.data);
}
});
- socketTask.onClose((res) => {
+ task.onClose((res) => {
+ if (task !== socketTask) {
+ return;
+ }
console.log('WebSocket连接关闭');
- isConnect = false;
- closeCallBack && closeCallBack(res);
+ finishConnection(task, res);
});
- socketTask.onError((e) => {
- console.log('ws错误:', e);
- close(1006);
- isConnect = false;
- closeCallBack && closeCallBack(e);
+ task.onError((error) => {
+ if (task !== socketTask || !enabled) {
+ return;
+ }
+ console.log('ws错误:', error);
+ closeSocketTask(task);
+ finishConnection(task, error);
});
};
-//定义重连函数
-let reconnect = (wsurl, accessToken) => {
- console.log('尝试重新连接');
- if (isConnect) {
+// 网络异常断开后重连;主动销毁后不会再接受旧任务发起的重连。
+let reconnect = (url, token) => {
+ if (!enabled || socketTask) {
return;
}
- // 延迟10秒重连 避免过多次过频繁请求重连
- let timeDiff = new Date().getTime() - lastConnectTime.getTime();
- let delay = timeDiff < 10000 ? 10000 - timeDiff : 0;
- rec && clearTimeout(rec);
- rec = setTimeout(function () {
- connect(wsurl, accessToken);
+ const reconnectUrl = url || wsUrl;
+ const reconnectToken = token || accessToken;
+ if (!reconnectUrl || !reconnectToken) {
+ return;
+ }
+
+ console.log('尝试重新连接');
+ const timeDiff = Date.now() - lastConnectTime;
+ const delay = timeDiff < 10000 ? 10000 - timeDiff : 0;
+ if (rec) {
+ clearTimeout(rec);
+ }
+ rec = setTimeout(() => {
+ rec = null;
+ if (!enabled || socketTask) {
+ return;
+ }
+ connect(reconnectUrl, reconnectToken);
}, delay);
};
-//设置关闭连接
-let close = (code) => {
- if (!isConnect) {
+// 临时关闭当前连接,关闭事件仍交给上层决定是否需要重连。
+let close = (code = 1000) => {
+ clearReconnectTimers();
+ heartCheck.stop();
+ isConnect = false;
+ if (!socketTask) {
return;
}
- socketTask.close({
- code: code,
- complete: (res) => {
- isConnect = false;
- console.log('关闭websocket连接');
- },
- fail: (e) => {
- console.log('关闭websocket连接失败', e);
- },
- });
+ closeSocketTask(socketTask, code);
+};
+
+// 退出登录时永久销毁当前连接,并抑制旧连接的关闭回调和重连任务。
+let destroy = (code = 1000) => {
+ enabled = false;
+ clearReconnectTimers();
+ heartCheck.stop();
+
+ const task = socketTask;
+ socketTask = null;
+ isConnect = false;
+ accessToken = '';
+ wsUrl = '';
+ closeSocketTask(task, code);
};
-// 心跳设置
let heartCheck = {
- timeout: 20000, //每段时间发送一次心跳包 这里设置为20s
- timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
- start: function () {
- if (isConnect) {
+ timeout: 20000,
+ timeoutObj: null,
+ start() {
+ if (enabled && isConnect) {
console.log('发送WebSocket心跳');
- let heartBeat = {
- cmd: 1,
- data: {},
- };
- sendMessage(JSON.stringify(heartBeat));
+ sendMessage(
+ JSON.stringify({
+ cmd: 1,
+ data: {},
+ }),
+ );
}
},
- reset: function () {
- clearTimeout(this.timeoutObj);
+ reset() {
+ this.stop();
+ if (!enabled || !isConnect) {
+ return;
+ }
this.timeoutObj = setTimeout(() => heartCheck.start(), this.timeout);
},
+ stop() {
+ if (this.timeoutObj) {
+ clearTimeout(this.timeoutObj);
+ this.timeoutObj = null;
+ }
+ },
};
let sendMessage = (message) => {
- socketTask.send({ data: message });
+ if (!enabled || !isConnect || !socketTask) {
+ return false;
+ }
+ try {
+ socketTask.send({ data: message });
+ return true;
+ } catch (error) {
+ console.log('发送WebSocket消息失败', error);
+ return false;
+ }
};
let onConnect = (callback) => {
@@ -138,5 +275,4 @@ let onClose = (callback) => {
closeCallBack = callback;
};
-// 将方法暴露出去
-export { connect, reconnect, close, sendMessage, onConnect, onMessage, onClose };
+export { connect, reconnect, close, destroy, sendMessage, onConnect, onMessage, onClose };
diff --git a/src/main.ts b/src/main.ts
index c3eac2b5..3e57c1b8 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -2,6 +2,7 @@
import App from './App.vue';
import { createSSRApp } from 'vue';
import { setupPinia } from './sheep/store';
+import { initStatusBarHeight, statusBarPageStyle } from '@/sheep/platform/status-bar';
import 'virtual:uno.css';
/** 聊天common模块 */
@@ -18,14 +19,12 @@ import * as recorder from './common/recorder-h5';
import ImageResize from 'quill-image-resize-mp';
import Quill from 'quill';
import { patchH5Navigation } from '@/sheep/router/h5-navigation';
-import { initH5HostShell } from '@/sheep/platform/h5-shell';
// 以下组件用于兼容部分手机聊天边框无法输入的问题
window.Quill = Quill;
window.ImageResize = { default: ImageResize };
// 调试器
// import VConsole from 'vconsole';
// new VConsole();
-initH5HostShell();
patchH5Navigation();
// #endif
// #ifndef H5
@@ -39,6 +38,9 @@ import useGroupStore from '@/chat-store/groupStore.js';
import useConfigStore from '@/chat-store/configStore.js';
import useUserStore from '@/chat-store/userStore.js';
+// H5 会同步应用宿主高度;App 和小程序会在异步系统信息返回后更新页面根变量。
+void initStatusBarHeight();
+
/** 聊天页面的导航组件 */
import barGroup from '@/components/bar/bar-group';
import arrowBar from '@/components/bar/arrow-bar';
@@ -48,6 +50,10 @@ import switchBar from '@/components/bar/switch-bar';
export function createApp() {
const app = createSSRApp(App);
+ Object.defineProperty(app.config.globalProperties, '$statusBarPageStyle', {
+ get: () => statusBarPageStyle.value,
+ });
+
const pinia = setupPinia(app);
// 聊天common模块挂载为全局属性
diff --git a/src/pages/home/city.vue b/src/pages/home/city.vue
index f0340dfa..7e04d2c4 100644
--- a/src/pages/home/city.vue
+++ b/src/pages/home/city.vue
@@ -1,4 +1,5 @@
+
@@ -15,106 +16,156 @@
热门城市
-
+
{{ item.t }}
-
+
\ No newline at end of file
+
diff --git a/src/pages/home/detail.vue b/src/pages/home/detail.vue
index c8ca9dd1..6ddf9946 100644
--- a/src/pages/home/detail.vue
+++ b/src/pages/home/detail.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/home/report.vue b/src/pages/home/report.vue
index fe215d58..3b059fb2 100644
--- a/src/pages/home/report.vue
+++ b/src/pages/home/report.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/message/chat-box.vue b/src/pages/message/chat-box.vue
index 10166222..de1268e9 100644
--- a/src/pages/message/chat-box.vue
+++ b/src/pages/message/chat-box.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/message/friend.vue b/src/pages/message/friend.vue
index 82516fc0..4c78507d 100644
--- a/src/pages/message/friend.vue
+++ b/src/pages/message/friend.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/message/user-info.vue b/src/pages/message/user-info.vue
index c9d1b3a5..8f5ccbdb 100644
--- a/src/pages/message/user-info.vue
+++ b/src/pages/message/user-info.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/moments/publish.vue b/src/pages/moments/publish.vue
index ecb8dd5d..14b3bf10 100644
--- a/src/pages/moments/publish.vue
+++ b/src/pages/moments/publish.vue
@@ -1,13 +1,17 @@
+
-
+
-
- 发布
+
+ {{ publishing ? '提交中' : isEditMode ? '重新发布' : '发布' }}
@@ -17,20 +21,34 @@
-
+
-
+
@@ -39,8 +57,16 @@
-
+
@@ -50,127 +76,247 @@
diff --git a/src/pages/profile/about.vue b/src/pages/profile/about.vue
index e126c982..444ca45e 100644
--- a/src/pages/profile/about.vue
+++ b/src/pages/profile/about.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/apply-vip-record.vue b/src/pages/profile/apply-vip-record.vue
index 211f177b..3aec2687 100644
--- a/src/pages/profile/apply-vip-record.vue
+++ b/src/pages/profile/apply-vip-record.vue
@@ -1,4 +1,5 @@
+
@@ -53,4 +54,4 @@ const { listData, loadMoreStatus, refreshList, listLoadMore } = useScroll(
onShow(() => {
refreshList();
});
-
\ No newline at end of file
+
diff --git a/src/pages/profile/apply-vip.vue b/src/pages/profile/apply-vip.vue
index 4f3d01ef..f7f38314 100644
--- a/src/pages/profile/apply-vip.vue
+++ b/src/pages/profile/apply-vip.vue
@@ -1,4 +1,5 @@
+
@@ -140,6 +141,7 @@ import PayOrderApi from '@/sheep/api/pay/order';
// @ts-ignore
import sheep from '@/sheep';
import { jumpToWechatMiniProgram, wechatNavBack } from '@/sheep/util/wechat-tool';
+import AuthUtil from '@/sheep/api/member/auth';
import appConfig from '@/sheep/config';
const messageBox = useMessage();
@@ -181,6 +183,7 @@ const nodes = ref(''); // 会员协议文本
const payMethod = ref<'alipay' | 'wechat'>('alipay'); // 支付方式:alipay / wechat
const openTypeList = ref([]); // 会员类型列表
const curOpenTypeId = ref();
+const isAutoLoggingIn = ref(false);
// 当前选择的会员类型
const currentOpenType = computed(() =>
openTypeList.value?.find((item) => item.id == curOpenTypeId.value),
@@ -189,8 +192,36 @@ const isLogin = computed(() => sheep.$store('user').isLogin);
onLoad(async (params) => {
console.log('onLoad:', params);
+ // #ifdef MP-WEIXIN
+ if (params?.loginCode) {
+ isAutoLoggingIn.value = true;
+ let loginCompleted = false;
+ try {
+ const { code } = await AuthUtil.miniProgramLogin(params.loginCode);
+ if (code !== 0) {
+ throw new Error('小程序自动登录失败');
+ }
+ loginCompleted = true;
+ uni.removeStorageSync('openid');
+ const bindSuccess = await sheep.$platform.useProvider('wechat').bind();
+ if (!bindSuccess) {
+ throw new Error('微信自动绑定失败');
+ }
+ } catch (error) {
+ console.error('|小程序自动登录及绑定|意外的错误error:', error);
+ if (loginCompleted) {
+ uni.showToast({ title: '微信自动绑定失败,请重试', icon: 'none' });
+ } else {
+ authDialog();
+ }
+ return;
+ } finally {
+ isAutoLoggingIn.value = false;
+ }
+ }
+ // #endif
if (!isLogin.value) {
- return
+ return;
}
const { data = null } = await RecentApi.getMemberOpenTypeList();
openTypeList.value = data ?? [];
@@ -199,16 +230,19 @@ onLoad(async (params) => {
}
if (params) {
if (params.openTypeId && params.payMethod) {
- payMethod.value = params.payMethod;
- curOpenTypeId.value = params.openTypeId;
+ payMethod.value = params.payMethod as 'alipay' | 'wechat';
+ curOpenTypeId.value = Number(params.openTypeId);
handleOpenVip();
}
}
});
onShow(async () => {
+ if (isAutoLoggingIn.value) {
+ return;
+ }
if (!isLogin.value) {
- authDialog()
+ authDialog();
} else {
getChatConfig();
}
@@ -278,30 +312,37 @@ const handleOpenVip = () => {
msg: `确认使用${payMethodItem.title}${fen2yuan(currentOpenType.value?.preferentialPrice)}元,开通${currentOpenType.value?.title}会员吗?`,
})
.then(async () => {
- if (appConfig.buildTarget === 'H5') {
- if (payMethod.value === 'wechat') {
- disposeWechatPay(curOpenTypeId.value);
- } else {
- disposeAlipayPay(curOpenTypeId.value);
- }
- } else {
- // #ifdef MP-WEIXIN
- if (payMethod.value === 'wechat') {
- disposeWechatPay(curOpenTypeId.value);
- } else {
- disposeAlipayPay(curOpenTypeId.value);
- }
- // #endif
- // #ifndef MP-WEIXIN
- jumpToWechatPage();
- // #endif
+ if (appConfig.paymentBuildTargets[payMethod.value] === 'H5') {
+ disposePay(curOpenTypeId.value, true);
+ return;
}
+ // #ifdef MP-WEIXIN
+ disposePay(curOpenTypeId.value);
+ // #endif
+ // #ifndef MP-WEIXIN
+ jumpToWechatPage();
+ // #endif
})
.catch(() => { });
};
+const disposePay = (openTypeId: any, forceH5Pay = false) => {
+ if (payMethod.value === 'wechat') {
+ disposeWechatPay(openTypeId, forceH5Pay);
+ } else {
+ disposeAlipayPay(openTypeId, forceH5Pay);
+ }
+};
+
+const executePay = (payOrderId: any, forceH5Pay: boolean) => {
+ if (forceH5Pay) {
+ return sheep.$platform.payH5(payMethod.value, 'recharge', payOrderId);
+ }
+ return sheep.$platform.pay(payMethod.value, 'recharge', payOrderId);
+};
+
// 处理支付宝支付
-const disposeAlipayPay = async (openTypeId: any) => {
+const disposeAlipayPay = async (openTypeId: any, forceH5Pay = false) => {
uni.showLoading({ title: '支付中...', mask: true });
try {
const { code, data } = await RecentApi.memberOpenTypePay({
@@ -312,7 +353,7 @@ const disposeAlipayPay = async (openTypeId: any) => {
uni.showToast({ title: '创建订单失败', icon: 'none' });
throw new Error('创建订单失败');
}
- await sheep.$platform.pay(payMethod.value, 'recharge', data);
+ await executePay(data, forceH5Pay);
// 更新用户信息
$store('user').getInfo();
uni.showToast({ title: '创建订单成功', icon: 'success' });
@@ -322,7 +363,7 @@ const disposeAlipayPay = async (openTypeId: any) => {
}
// 处理微信支付
-const disposeWechatPay = async (openTypeId: any) => {
+const disposeWechatPay = async (openTypeId: any, forceH5Pay = false) => {
uni.showLoading({ title: '支付中...', mask: true });
try {
const { code, data } = await RecentApi.memberOpenTypePay({
@@ -333,7 +374,7 @@ const disposeWechatPay = async (openTypeId: any) => {
uni.showToast({ title: '创建订单失败', icon: 'none' });
throw new Error('创建订单失败');
}
- await sheep.$platform.pay(payMethod.value, 'recharge', data);
+ await executePay(data, forceH5Pay);
// 更新用户信息
$store('user').getInfo();
uni.showToast({ title: '支付成功', icon: 'success' });
@@ -345,9 +386,15 @@ const disposeWechatPay = async (openTypeId: any) => {
// 跳转到微信小程序
const jumpToWechatPage = async () => {
try {
+ const { code, data: loginCode } = await AuthUtil.createMiniProgramLoginCode();
+ if (code !== 0 || !loginCode) {
+ uni.showToast({ title: '获取小程序登录凭证失败', icon: 'none' });
+ throw new Error('获取小程序登录凭证失败');
+ }
const url = await jumpToWechatMiniProgram('openVip', {
openTypeId: curOpenTypeId.value,
payMethod: payMethod.value,
+ loginCode,
});
if (!url) {
uni.showToast({ title: '获取小程序短链失败', icon: 'none' });
diff --git a/src/pages/profile/black-list.vue b/src/pages/profile/black-list.vue
index a4fff30f..0965a01f 100644
--- a/src/pages/profile/black-list.vue
+++ b/src/pages/profile/black-list.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/face-verify/boy-verify.vue b/src/pages/profile/face-verify/boy-verify.vue
index b78ac4ee..987e97e3 100644
--- a/src/pages/profile/face-verify/boy-verify.vue
+++ b/src/pages/profile/face-verify/boy-verify.vue
@@ -1,6 +1,14 @@
+
-
+
@@ -13,16 +21,24 @@
上传本人头像,女生更放心与你见面~
-
+
-
+
开始认证
@@ -34,68 +50,68 @@
diff --git a/src/pages/profile/face-verify/girl-verify-1.vue b/src/pages/profile/face-verify/girl-verify-1.vue
index 75ab7ddd..fe49b270 100644
--- a/src/pages/profile/face-verify/girl-verify-1.vue
+++ b/src/pages/profile/face-verify/girl-verify-1.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/face-verify/girl-verify-2.vue b/src/pages/profile/face-verify/girl-verify-2.vue
index aa41a352..4d016aeb 100644
--- a/src/pages/profile/face-verify/girl-verify-2.vue
+++ b/src/pages/profile/face-verify/girl-verify-2.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/face-verify/girl-verify-3.vue b/src/pages/profile/face-verify/girl-verify-3.vue
index 23842296..f47958bf 100644
--- a/src/pages/profile/face-verify/girl-verify-3.vue
+++ b/src/pages/profile/face-verify/girl-verify-3.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/face-verify/girl-verify-4.vue b/src/pages/profile/face-verify/girl-verify-4.vue
index c0d69e7a..790eb156 100644
--- a/src/pages/profile/face-verify/girl-verify-4.vue
+++ b/src/pages/profile/face-verify/girl-verify-4.vue
@@ -1,6 +1,13 @@
+
-
+
@@ -18,29 +25,54 @@
上传清晰的五官头像,让更多用户发现你~
-
+ }"
+ />
确保本人头像
才能通过审核哦
-
+
-
-
+
+
-
+
开始认证
@@ -48,112 +80,125 @@
diff --git a/src/pages/profile/feedback.vue b/src/pages/profile/feedback.vue
index 9ada7351..446cc2e2 100644
--- a/src/pages/profile/feedback.vue
+++ b/src/pages/profile/feedback.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/gender-choose.vue b/src/pages/profile/gender-choose.vue
index e7d54704..8a7efc01 100644
--- a/src/pages/profile/gender-choose.vue
+++ b/src/pages/profile/gender-choose.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/like.vue b/src/pages/profile/like.vue
index c82cde8d..29e44db9 100644
--- a/src/pages/profile/like.vue
+++ b/src/pages/profile/like.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/my-moment.vue b/src/pages/profile/my-moment.vue
index c7f2fa3c..541775a5 100644
--- a/src/pages/profile/my-moment.vue
+++ b/src/pages/profile/my-moment.vue
@@ -1,4 +1,5 @@
+
@@ -15,9 +16,14 @@
-
-
+
+
+
+
+
+
@@ -27,11 +33,31 @@ import MomentCard from '@/sheep/components/moment-card/moment-card.vue';
import ScrollList from '@/sheep/components/scroll-list/scroll-list.vue';
import useScroll from '@/sheep/hooks/use-scroll';
import RecentApi from '@/sheep/api/member/recent';
-import { onMounted } from 'vue';
+import { onShow } from '@dcloudio/uni-app';
+import { reactive, ref } from 'vue';
-const { listData, loadMoreStatus, refreshList, listLoadMore } = useScroll(RecentApi.myMomentList);
+const activeTab = ref(0);
+const auditTabs = [
+ { label: '全部', auditStatus: undefined },
+ { label: '审核中', auditStatus: 0 },
+ { label: '已通过', auditStatus: 1 },
+ { label: '已拒绝', auditStatus: 2 },
+];
+const queryParams = reactive<{ auditStatus?: number }>({
+ auditStatus: undefined,
+});
-onMounted(() => {
+const { listData, loadMoreStatus, refreshList, listLoadMore } = useScroll(
+ RecentApi.myMomentList,
+ queryParams,
+);
+
+const handleTabChange = ({ index }: { index: number }) => {
+ queryParams.auditStatus = auditTabs[index]?.auditStatus;
+ refreshList();
+};
+
+onShow(() => {
refreshList();
});
diff --git a/src/pages/profile/pay-result.vue b/src/pages/profile/pay-result.vue
index a484ac68..7ba90fce 100644
--- a/src/pages/profile/pay-result.vue
+++ b/src/pages/profile/pay-result.vue
@@ -1,5 +1,6 @@
+
diff --git a/src/pages/profile/recharge-record.vue b/src/pages/profile/recharge-record.vue
index 88519318..6c7c6250 100644
--- a/src/pages/profile/recharge-record.vue
+++ b/src/pages/profile/recharge-record.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/recharge.vue b/src/pages/profile/recharge.vue
index 413281ae..dcf2d756 100644
--- a/src/pages/profile/recharge.vue
+++ b/src/pages/profile/recharge.vue
@@ -1,4 +1,5 @@
-
+
+
@@ -108,6 +109,7 @@ import sheep from '@/sheep';
// @ts-ignore
import PayOrderApi from '@/sheep/api/pay/order';
import { jumpToWechatMiniProgram, wechatNavBack } from '@/sheep/util/wechat-tool';
+import AuthUtil from '@/sheep/api/member/auth';
import appConfig from '@/sheep/config';
const messageBox = useMessage();
@@ -133,17 +135,46 @@ const rechargePackag = ref<{ id: number; name: string; payPrice: number; bonusPr
const userWallet = computed(() => sheep.$store('user').userWallet);
const isLogin = computed(() => sheep.$store('user').isLogin);
+const isAutoLoggingIn = ref(false);
onLoad(async (params) => {
console.log('onLoad:', params);
+ // #ifdef MP-WEIXIN
+ if (params?.loginCode) {
+ isAutoLoggingIn.value = true;
+ let loginCompleted = false;
+ try {
+ const { code } = await AuthUtil.miniProgramLogin(params.loginCode);
+ if (code !== 0) {
+ throw new Error('小程序自动登录失败');
+ }
+ loginCompleted = true;
+ uni.removeStorageSync('openid');
+ const bindSuccess = await sheep.$platform.useProvider('wechat').bind();
+ if (!bindSuccess) {
+ throw new Error('微信自动绑定失败');
+ }
+ } catch (error) {
+ console.error('|小程序自动登录及绑定|意外的错误error:', error);
+ if (loginCompleted) {
+ uni.showToast({ title: '微信自动绑定失败,请重试', icon: 'none' });
+ } else {
+ authDialog();
+ }
+ return;
+ } finally {
+ isAutoLoggingIn.value = false;
+ }
+ }
+ // #endif
if (!isLogin.value) {
- return
+ return;
}
const { data } = await RecentApi.getWalletRechargePackage();
rechargePackag.value = data ?? [];
if (params) {
if (params.packageId && params.payMethod) {
- payMethod.value = params.payMethod;
+ payMethod.value = params.payMethod as 'alipay' | 'wechat';
const item = rechargePackag.value.find((item) => item.id === Number(params.packageId));
if (!item) {
uni.showToast({ title: '请选择会员类型', icon: 'none' });
@@ -155,8 +186,11 @@ onLoad(async (params) => {
});
onShow(async () => {
+ if (isAutoLoggingIn.value) {
+ return;
+ }
if (!isLogin.value) {
- authDialog()
+ authDialog();
} else {
// 查询钱包余额
getWallet();
@@ -198,32 +232,39 @@ const handleRecharge = (item: any) => {
.confirm({
title: '提示',
msg: `确认使用${payMethodItem.title}${fen2yuanSimple(item.payPrice)}元获取${fen2yuanSimple(item.bonusPrice + item.payPrice)}砖石?`,
- })
- .then(async () => {
- if (appConfig.buildTarget === 'H5') {
- if (payMethod.value === 'wechat') {
- disposeWechatPay(item.id);
- } else {
- disposeAlipayPay(item.id);
- }
- } else {
- // #ifdef MP-WEIXIN
- if (payMethod.value === 'wechat') {
- disposeWechatPay(item.id);
- } else {
- disposeAlipayPay(item.id);
- }
- // #endif
- // #ifndef MP-WEIXIN
- jumpToWechatPage(item.id);
- // #endif
+ })
+ .then(async () => {
+ if (appConfig.paymentBuildTargets[payMethod.value] === 'H5') {
+ disposePay(item.id, true);
+ return;
}
+ // #ifdef MP-WEIXIN
+ disposePay(item.id);
+ // #endif
+ // #ifndef MP-WEIXIN
+ jumpToWechatPage(item.id);
+ // #endif
})
.catch(() => { });
};
-// 处理支付宝支付
-const disposeAlipayPay = async (packageId: any) => {
+const disposePay = (packageId: any, forceH5Pay = false) => {
+ if (payMethod.value === 'wechat') {
+ disposeWechatPay(packageId, forceH5Pay);
+ } else {
+ disposeAlipayPay(packageId, forceH5Pay);
+ }
+};
+
+const executePay = (payOrderId: any, forceH5Pay: boolean) => {
+ if (forceH5Pay) {
+ return sheep.$platform.payH5(payMethod.value, 'recharge', payOrderId);
+ }
+ return sheep.$platform.pay(payMethod.value, 'recharge', payOrderId);
+};
+
+// 处理支付宝支付
+const disposeAlipayPay = async (packageId: any, forceH5Pay = false) => {
uni.showLoading({ title: '支付中...', mask: true });
try {
const { code, data } = await RecentApi.walletRecharge({ packageId: packageId });
@@ -232,7 +273,7 @@ const disposeAlipayPay = async (packageId: any) => {
uni.showToast({ title: '创建订单失败', icon: 'none' });
throw new Error();
}
- await sheep.$platform.pay(payMethod.value, 'recharge', data.payOrderId);
+ await executePay(data.payOrderId, forceH5Pay);
// 更新用户钱包
getWallet();
uni.showToast({ title: '创建订单成功', icon: 'success' });
@@ -244,7 +285,7 @@ const disposeAlipayPay = async (packageId: any) => {
}
// 处理微信支付
-const disposeWechatPay = async (packageId: any) => {
+const disposeWechatPay = async (packageId: any, forceH5Pay = false) => {
uni.showLoading({ title: '支付中...', mask: true });
try {
const { code, data } = await RecentApi.walletRecharge({ packageId: packageId });
@@ -253,7 +294,7 @@ const disposeWechatPay = async (packageId: any) => {
uni.showToast({ title: '创建订单失败', icon: 'none' });
throw new Error();
}
- await sheep.$platform.pay(payMethod.value, 'recharge', data.payOrderId);
+ await executePay(data.payOrderId, forceH5Pay);
// 更新用户钱包
getWallet();
uni.showToast({ title: '支付成功', icon: 'success' });
@@ -266,20 +307,30 @@ const disposeWechatPay = async (packageId: any) => {
// 跳转到微信小程序
const jumpToWechatPage = async (packageId: any) => {
- console.log("跳转到微信小程序");
- const url = await jumpToWechatMiniProgram('recharge', {
- packageId,
- payMethod: payMethod.value,
- });
- if (!url) {
- uni.showToast({ title: '获取小程序短链失败', icon: 'none' });
- throw new Error('获取小程序短链失败');
+ try {
+ console.log('跳转到微信小程序');
+ const { code, data: loginCode } = await AuthUtil.createMiniProgramLoginCode();
+ if (code !== 0 || !loginCode) {
+ uni.showToast({ title: '获取小程序登录凭证失败', icon: 'none' });
+ throw new Error('获取小程序登录凭证失败');
+ }
+ const url = await jumpToWechatMiniProgram('recharge', {
+ packageId,
+ payMethod: payMethod.value,
+ loginCode,
+ });
+ if (!url) {
+ uni.showToast({ title: '获取小程序短链失败', icon: 'none' });
+ throw new Error('获取小程序短链失败');
+ }
+ // #ifdef H5
+ window.location.href = url
+ // #endif
+ // #ifdef APP-PLUS
+ plus.runtime.openURL(url)
+ // #endif
+ } catch (error) {
+ console.log('|跳转微信小程序|,意外的错误error:', error);
}
- // #ifdef H5
- window.location.href = url
- // #endif
- // #ifdef APP-PLUS
- plus.runtime.openURL(url)
- // #endif
};
-
\ No newline at end of file
+
diff --git a/src/pages/profile/set-wechat.vue b/src/pages/profile/set-wechat.vue
index 476f0eb7..e228f03a 100644
--- a/src/pages/profile/set-wechat.vue
+++ b/src/pages/profile/set-wechat.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/setting.vue b/src/pages/profile/setting.vue
index 737ab86a..7ccea72f 100644
--- a/src/pages/profile/setting.vue
+++ b/src/pages/profile/setting.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/upload-album.vue b/src/pages/profile/upload-album.vue
index 8e524854..7e97d6b5 100644
--- a/src/pages/profile/upload-album.vue
+++ b/src/pages/profile/upload-album.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/profile/userinfo-simple.vue b/src/pages/profile/userinfo-simple.vue
index f2eb2bd5..4416e178 100644
--- a/src/pages/profile/userinfo-simple.vue
+++ b/src/pages/profile/userinfo-simple.vue
@@ -1,4 +1,5 @@
+
@@ -44,4 +45,4 @@ const writeoff = () => {
uni.showToast({ title: '注销成功', icon: 'success' });
})
}
-
\ No newline at end of file
+
diff --git a/src/pages/profile/userinfo.vue b/src/pages/profile/userinfo.vue
index c9e6fd3c..2b0aac48 100644
--- a/src/pages/profile/userinfo.vue
+++ b/src/pages/profile/userinfo.vue
@@ -1,4 +1,5 @@
+
+
diff --git a/src/pages/profile/view-record.vue b/src/pages/profile/view-record.vue
index f6ecfb9a..f924d123 100644
--- a/src/pages/profile/view-record.vue
+++ b/src/pages/profile/view-record.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/tabbar/home.vue b/src/pages/tabbar/home.vue
index 1d730c9b..dbd94979 100644
--- a/src/pages/tabbar/home.vue
+++ b/src/pages/tabbar/home.vue
@@ -1,4 +1,5 @@
+
@@ -13,8 +14,12 @@
- (current = e.detail.current)" :duration="200">
+ (current = e.detail.current)"
+ :duration="200"
+ >
@@ -22,22 +27,30 @@
-
+
-
+ @click="openSystemGPS"
+ >
点击开启位置权限,匹配您附近的那个Ta~
-
+ @click="openSystemGPS"
+ >
点击开启位置权限,匹配您附近的那个Ta~
@@ -47,100 +60,97 @@
diff --git a/src/pages/tabbar/message.vue b/src/pages/tabbar/message.vue
index 08dbec55..78eb9ef5 100644
--- a/src/pages/tabbar/message.vue
+++ b/src/pages/tabbar/message.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/tabbar/moments.vue b/src/pages/tabbar/moments.vue
index 40c77d82..26c3400a 100644
--- a/src/pages/tabbar/moments.vue
+++ b/src/pages/tabbar/moments.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/tabbar/profile.vue b/src/pages/tabbar/profile.vue
index 620d62f4..eb28b795 100644
--- a/src/pages/tabbar/profile.vue
+++ b/src/pages/tabbar/profile.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/user/goods_details_store/index.vue b/src/pages/user/goods_details_store/index.vue
index f1b0c84d..965629ba 100644
--- a/src/pages/user/goods_details_store/index.vue
+++ b/src/pages/user/goods_details_store/index.vue
@@ -59,6 +59,7 @@
import { onMounted, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
+ import { getCurrentLocation, getLocationErrorMessage } from '@/sheep/util/location';
const LONGITUDE = 'user_longitude';
const LATITUDE = 'user_latitude';
@@ -77,39 +78,33 @@
phoneNumber: phone,
});
};
- const selfLocation = () => {
+ const saveLocation = (latitude, longitude) => {
+ state.user_latitude = latitude;
+ state.user_longitude = longitude;
+ uni.setStorageSync(LATITUDE, latitude);
+ uni.setStorageSync(LONGITUDE, longitude);
+ };
+
+ const selfLocation = async () => {
// #ifdef H5
const jsWxSdk = sheep.$platform.useProvider('wechat').jsWxSdk;
if (jsWxSdk.isWechat()) {
jsWxSdk.getLocation((res) => {
- state.user_latitude = res.latitude;
- state.user_longitude = res.longitude;
- uni.setStorageSync(LATITUDE, res.latitude);
- uni.setStorageSync(LONGITUDE, res.longitude);
+ saveLocation(res.latitude, res.longitude);
getList();
});
- } else {
- // #endif
- uni.getLocation({
- type: 'gcj02',
- success: (res) => {
- try {
- state.user_latitude = res.latitude;
- state.user_longitude = res.longitude;
- uni.setStorageSync(LATITUDE, res.latitude);
- uni.setStorageSync(LONGITUDE, res.longitude);
- } catch (e) {
- console.error(e);
- }
- getList();
- },
- complete: () => {
- getList();
- },
- });
- // #ifdef H5
+ return;
}
// #endif
+
+ try {
+ const location = await getCurrentLocation({ type: 'gcj02' });
+ saveLocation(location.latitude, location.longitude);
+ } catch (error) {
+ uni.showToast({ title: getLocationErrorMessage(error), icon: 'none', duration: 5000 });
+ } finally {
+ getList();
+ }
};
const showMaoLocation = (e) => {
// #ifdef H5
diff --git a/src/pages/wechat-tabbar/index.vue b/src/pages/wechat-tabbar/index.vue
index 1b106a5e..b4e525fc 100644
--- a/src/pages/wechat-tabbar/index.vue
+++ b/src/pages/wechat-tabbar/index.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/pages/wechat-tabbar/me.vue b/src/pages/wechat-tabbar/me.vue
index 06682d95..93f58971 100644
--- a/src/pages/wechat-tabbar/me.vue
+++ b/src/pages/wechat-tabbar/me.vue
@@ -1,4 +1,5 @@
+
@@ -77,4 +78,4 @@ const doLogout = () => {
console.log("error:", error);
});
}
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/gift/gift-record.vue b/src/pages/wechat/pages/gift/gift-record.vue
index 351edef9..53d9f52d 100644
--- a/src/pages/wechat/pages/gift/gift-record.vue
+++ b/src/pages/wechat/pages/gift/gift-record.vue
@@ -1,4 +1,5 @@
+
@@ -31,4 +32,4 @@ const { listData, loadMoreStatus, refreshList, listLoadMore } = useScroll(
onShow(() => {
refreshList();
});
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/gift/history.vue b/src/pages/wechat/pages/gift/history.vue
index 07b9c1ab..8813ee76 100644
--- a/src/pages/wechat/pages/gift/history.vue
+++ b/src/pages/wechat/pages/gift/history.vue
@@ -1,4 +1,5 @@
+
@@ -108,4 +109,4 @@ const tabsChange = ({ index }) => {
}
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/gift/index.vue b/src/pages/wechat/pages/gift/index.vue
index 8608c954..8f7e988a 100644
--- a/src/pages/wechat/pages/gift/index.vue
+++ b/src/pages/wechat/pages/gift/index.vue
@@ -1,4 +1,5 @@
+
@@ -157,4 +158,4 @@ const getTxConfig = async () => {
}
};
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/inviter/index.vue b/src/pages/wechat/pages/inviter/index.vue
index 1e4ed78c..04f6fb6c 100644
--- a/src/pages/wechat/pages/inviter/index.vue
+++ b/src/pages/wechat/pages/inviter/index.vue
@@ -1,5 +1,6 @@
+
diff --git a/src/pages/wechat/pages/poster.vue b/src/pages/wechat/pages/poster.vue
index fa8fd54f..59b33011 100644
--- a/src/pages/wechat/pages/poster.vue
+++ b/src/pages/wechat/pages/poster.vue
@@ -1,4 +1,5 @@
+
@@ -94,4 +95,4 @@ const savePoster = () => {
)
};
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/withdraw.vue b/src/pages/wechat/pages/withdraw.vue
index 930749dd..2a65d576 100644
--- a/src/pages/wechat/pages/withdraw.vue
+++ b/src/pages/wechat/pages/withdraw.vue
@@ -1,4 +1,5 @@
+
@@ -278,4 +279,4 @@ async function alert() {
zIndex: 200,
});
}
-
\ No newline at end of file
+
diff --git a/src/pages/wechat/pages/withraw-record.vue b/src/pages/wechat/pages/withraw-record.vue
index 9e529643..ae2cb162 100644
--- a/src/pages/wechat/pages/withraw-record.vue
+++ b/src/pages/wechat/pages/withraw-record.vue
@@ -1,4 +1,5 @@
+
diff --git a/src/sheep/api/member/auth.js b/src/sheep/api/member/auth.js
index e91f0190..ff0d7464 100644
--- a/src/sheep/api/member/auth.js
+++ b/src/sheep/api/member/auth.js
@@ -77,6 +77,39 @@ const AuthUtil = {
},
});
},
+ // 创建 H5 跳转微信小程序时使用的一次性登录码
+ createMiniProgramLoginCode: () => {
+ return request({
+ url: '/member/auth/mini-program-login-code',
+ method: 'POST',
+ custom: {
+ showLoading: false,
+ },
+ });
+ },
+ // 微信小程序使用一次性登录码恢复 H5 用户登录态
+ miniProgramLogin: (code) => {
+ return request({
+ url: '/member/auth/mini-program-login',
+ method: 'POST',
+ params: { code },
+ custom: {
+ isToken: false,
+ showLoading: false,
+ },
+ });
+ },
+ // IM 的 accessToken 和 refreshToken 都失效后,使用主站登录态重新签发
+ reissueChatToken: () => {
+ return request({
+ url: '/member/auth/chat-token',
+ method: 'POST',
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ });
+ },
// 社交授权的跳转
socialAuthRedirect: (type, redirectUri) => {
return request({
diff --git a/src/sheep/api/member/recent.ts b/src/sheep/api/member/recent.ts
index fa89032c..d3267fbc 100644
--- a/src/sheep/api/member/recent.ts
+++ b/src/sheep/api/member/recent.ts
@@ -58,6 +58,33 @@ const RecentApi = {
},
});
},
+ // 获取本人动态,用于修改后重新发布
+ getMomentForEdit: (recentId: string | number) => {
+ return request({
+ url: '/member/recent/get-recent',
+ method: 'GET',
+ params: { recentId },
+ custom: {
+ showLoading: true,
+ loadingMsg: '加载中',
+ showError: false,
+ },
+ });
+ },
+ // 修改审核不通过的动态并重新提交审核
+ updateAndResubmitMoment: (data: any) => {
+ return request({
+ url: '/member/recent/update-and-resubmit',
+ method: 'POST',
+ data,
+ custom: {
+ showLoading: true,
+ loadingMsg: '重新发布中',
+ showSuccess: false,
+ showError: false,
+ },
+ });
+ },
// 获取用户详情页
getRecentDetail: (params: { id: string | number }) => {
return request({
diff --git a/src/sheep/components/moment-card/moment-card.vue b/src/sheep/components/moment-card/moment-card.vue
index 05937979..7ba9bcc3 100644
--- a/src/sheep/components/moment-card/moment-card.vue
+++ b/src/sheep/components/moment-card/moment-card.vue
@@ -1,15 +1,21 @@
-
+
{{ member?.memberNickname }}
-
+
{{ item.tagTitle }}
@@ -29,19 +35,44 @@
+
+
+
+ {{ auditStatusMeta.text }}
+
+
+ {{ data.auditReason }}
+
+
+ 重新发布
+
+
{{ data.description }}
-
-
-
-
-
-
-
-
-
+
+
@@ -65,112 +96,171 @@
+
+
diff --git a/src/sheep/components/s-layout/s-layout.vue b/src/sheep/components/s-layout/s-layout.vue
index 9e2f88b0..4b6b605f 100644
--- a/src/sheep/components/s-layout/s-layout.vue
+++ b/src/sheep/components/s-layout/s-layout.vue
@@ -1,5 +1,9 @@
-
+
0) {
- device.statusBarHeight = h5StatusBarHeight;
+const initialStatusBarHeight = getStatusBarHeight();
+if (initialStatusBarHeight > 0) {
+ device.statusBarHeight = initialStatusBarHeight;
}
-// #endif
+initStatusBarHeight().then((statusBarHeight) => {
+ device.statusBarHeight = statusBarHeight;
+});
const os = uni.getDeviceInfo().platform;
@@ -86,6 +87,11 @@ const pay = (payment, orderType, orderSN) => {
return new Pay(payment, orderType, orderSN);
};
+// 强制使用 H5/WAP 支付渠道,不根据当前运行平台切换到 App 支付
+const payH5 = (payment, orderType, orderSN) => {
+ return Pay.payH5(payment, orderType, orderSN);
+};
+
/**
* 检查更新 (只检查小程序和App)
* @param {Boolean} silence - 静默检查
@@ -146,9 +152,8 @@ const capsule = getCapsule();
// 标题栏高度
const getNavBar = () => {
- return device.statusBarHeight + 44;
+ return getStatusBarHeight() + 44;
};
-const navbar = getNavBar();
function getLandingPage() {
let page = '';
@@ -171,10 +176,13 @@ const _platform = {
checkUpdate,
checkNetwork,
pay,
+ payH5,
share,
load,
capsule,
- navbar,
+ get navbar() {
+ return getNavBar();
+ },
landingPage,
isWechatInstalled,
};
diff --git a/src/sheep/platform/pay.js b/src/sheep/platform/pay.js
index ac0fad2b..a3d2ab57 100644
--- a/src/sheep/platform/pay.js
+++ b/src/sheep/platform/pay.js
@@ -3,6 +3,7 @@ import sheep from '@/sheep';
import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
// #endif
import { getRootUrl } from '@/sheep/helper';
+import { h5Url } from '@/sheep/config';
import PayOrderApi from '@/sheep/api/pay/order';
/**
@@ -14,11 +15,17 @@ import PayOrderApi from '@/sheep/api/pay/order';
*/
export default class SheepPay {
- constructor(payment, orderType, id) {
+ constructor(payment, orderType, id, autoPay = true) {
this.payment = payment;
this.id = id;
this.orderType = orderType;
- this.payAction();
+ if (autoPay) {
+ this.payAction();
+ }
+ }
+
+ static payH5(payment, orderType, id) {
+ return new SheepPay(payment, orderType, id, false).h5PayAction();
}
payAction() {
@@ -83,42 +90,55 @@ export default class SheepPay {
return payAction[sheep.$platform.name][this.payment]();
}
- // 预支付
- prepay(channel) {
- return new Promise(async (resolve, reject) => {
- let data = {
- id: this.id,
- channelCode: channel,
- channelExtras: {},
- };
- // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
- if (['wx_pub', 'wx_lite'].includes(channel)) {
- const openid = await sheep.$platform.useProvider('wechat').getOpenid(true);
- // 如果获取不到 openid,微信无法发起支付,此时需要引导
- if (!openid) {
- this.bindWeixin();
- return;
- }
- data.channelExtras.openid = openid;
+ h5PayAction() {
+ const payAction = {
+ wechat: () => this.wechatWapPay(),
+ alipay: () => this.redirectPay(),
+ };
+ const action = payAction[this.payment];
+ if (!action) {
+ return Promise.reject(new Error(`H5 支付不支持支付方式: ${this.payment}`));
+ }
+ return action().then((started) => {
+ if (!started) {
+ throw new Error('H5 支付调起失败');
}
- // 发起预支付 API 调用
- PayOrderApi.submitOrder(data).then((res) => {
- // 成功时
- res.code === 0 && resolve(res);
- // 失败时
- if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
- // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
- if (
- res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid
- res.msg.indexOf('下单账号与支付账号不一致') >= 0
- ) {
- // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
- this.bindWeixin();
- }
- }
- });
+ return true;
});
}
+
+ // 预支付
+ async prepay(channel, returnUrl = '') {
+ const data = {
+ id: this.id,
+ channelCode: channel,
+ channelExtras: {},
+ };
+ if (returnUrl) {
+ data.returnUrl = returnUrl;
+ }
+ // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
+ if (['wx_pub', 'wx_lite'].includes(channel)) {
+ const openid = await sheep.$platform.useProvider('wechat').getOpenid(true);
+ // 如果获取不到 openid,微信无法发起支付,此时需要引导
+ if (!openid) {
+ this.bindWeixin();
+ return { code: -1, data: null };
+ }
+ data.channelExtras.openid = openid;
+ }
+ const res = await PayOrderApi.submitOrder(data);
+ const message = res.msg || '';
+ if (
+ res.code !== 0 &&
+ (message.includes('无效的openid') || message.includes('下单账号与支付账号不一致'))
+ ) {
+ // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
+ this.bindWeixin();
+ }
+ return res;
+ }
+
// #ifdef H5
// 微信公众号 JSSDK 支付
async wechatOfficialAccountPay() {
@@ -145,27 +165,66 @@ export default class SheepPay {
},
});
}
+ // #endif
- // 浏览器微信 H5 支付 TODO 芋艿:待接入(注意:H5 支付是给普通浏览器,不是微信公众号的支付,绝大多数人用不到,可以忽略)
+ // 微信 H5 支付
async wechatWapPay() {
- const { error, data } = await this.prepay('wx_wap');
- if (error === 0) {
- const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${this.payment
- }&orderType=${this.orderType}`;
- location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
+ const returnUrl = this.getH5PayResultUrl();
+ const { code, data } = await this.prepay('wx_wap', returnUrl);
+ if (code !== 0 || !data?.displayContent) {
+ return false;
}
+ const separator = data.displayContent.includes('?') ? '&' : '?';
+ const payUrl = returnUrl
+ ? `${data.displayContent}${separator}redirect_url=${encodeURIComponent(returnUrl)}`
+ : data.displayContent;
+ this.openH5PayUrl(payUrl, '微信支付');
+ return true;
}
// 支付链接(支付宝 wap 支付)
async redirectPay() {
- let { code, data } = await this.prepay('alipay_wap');
- if (code !== 0) {
- return;
+ const { code, data } = await this.prepay('alipay_wap', this.getH5PayResultUrl());
+ if (code !== 0 || !data?.displayContent) {
+ return false;
}
- location.href = data.displayContent;
+ this.openH5PayUrl(data.displayContent, '支付宝支付');
+ return true;
}
- // #endif
+ getH5PayResultUrl() {
+ let rootUrl = getRootUrl();
+ if (!rootUrl && h5Url) {
+ rootUrl = `${String(h5Url).replace(/\/+$/, '')}/#/`;
+ }
+ if (!rootUrl) {
+ return '';
+ }
+ return `${rootUrl}pages/profile/pay-result?id=${encodeURIComponent(this.id)}&payment=${encodeURIComponent(
+ this.payment,
+ )}&orderType=${encodeURIComponent(this.orderType)}`;
+ }
+
+ openH5PayUrl(url, title) {
+ // #ifdef H5
+ window.location.href = url;
+ // #endif
+ // #ifdef APP-PLUS
+ plus.runtime.openURL(url);
+ // #endif
+ // #ifdef MP
+ uni.showModal({
+ title,
+ content: '请复制链接到手机浏览器完成支付',
+ confirmText: '复制链接',
+ success: (res) => {
+ if (res.confirm) {
+ sheep.$helper.copyText(url);
+ }
+ },
+ });
+ // #endif
+ }
// 微信小程序支付
async wechatMiniProgramPay() {
diff --git a/src/sheep/platform/status-bar.js b/src/sheep/platform/status-bar.js
new file mode 100644
index 00000000..95018e73
--- /dev/null
+++ b/src/sheep/platform/status-bar.js
@@ -0,0 +1,104 @@
+import { computed, ref } from 'vue';
+import { initH5HostShell } from './h5-shell';
+
+const MAX_STATUS_BAR_HEIGHT = 200;
+const statusBarHeight = ref(readInitialStatusBarHeight());
+let initPromise = null;
+
+function normalizeStatusBarHeight(value) {
+ if (value === undefined || value === null || value === '') {
+ return null;
+ }
+ const height = Number.parseFloat(value);
+ if (!Number.isFinite(height)) {
+ return null;
+ }
+ return Math.max(0, Math.min(height, MAX_STATUS_BAR_HEIGHT));
+}
+
+function resolveSystemStatusBarHeight(systemInfo) {
+ if (!systemInfo) {
+ return null;
+ }
+
+ const safeAreaTop = normalizeStatusBarHeight(systemInfo.safeArea?.top);
+ const screenTop = normalizeStatusBarHeight(systemInfo.screenTop) ?? 0;
+ const candidates = [
+ systemInfo.statusBarHeight,
+ systemInfo.safeAreaInsets?.top,
+ safeAreaTop === null ? null : Math.max(0, safeAreaTop - screenTop),
+ ];
+ let zeroHeight = null;
+
+ for (const candidate of candidates) {
+ const height = normalizeStatusBarHeight(candidate);
+ if (height > 0) {
+ return height;
+ }
+ if (height === 0) {
+ zeroHeight = 0;
+ }
+ }
+ return zeroHeight;
+}
+
+function readInitialStatusBarHeight() {
+ // #ifdef H5
+ return 0;
+ // #endif
+
+ // #ifndef H5
+ try {
+ return resolveSystemStatusBarHeight(uni.getWindowInfo?.()) ?? 0;
+ } catch (error) {
+ return 0;
+ }
+ // #endif
+}
+
+function getSystemInfoAsync() {
+ return new Promise((resolve) => {
+ uni.getSystemInfo({
+ success: resolve,
+ fail: () => resolve(null),
+ });
+ });
+}
+
+export const statusBarPageStyle = computed(
+ () => `--status-bar-height: ${statusBarHeight.value}px;`,
+);
+
+export function getStatusBarHeight() {
+ return statusBarHeight.value;
+}
+
+export function initStatusBarHeight() {
+ if (initPromise) {
+ return initPromise;
+ }
+
+ // #ifdef H5
+ const h5StatusBarHeight = initH5HostShell();
+ statusBarHeight.value = h5StatusBarHeight;
+ window.addEventListener('app-shell-status-bar-change', (event) => {
+ const nextHeight = normalizeStatusBarHeight(event.detail?.height);
+ if (nextHeight !== null) {
+ statusBarHeight.value = nextHeight;
+ }
+ });
+ initPromise = Promise.resolve(h5StatusBarHeight);
+ // #endif
+
+ // #ifndef H5
+ initPromise = getSystemInfoAsync().then((systemInfo) => {
+ const nextHeight = resolveSystemStatusBarHeight(systemInfo);
+ if (nextHeight !== null) {
+ statusBarHeight.value = nextHeight;
+ }
+ return statusBarHeight.value;
+ });
+ // #endif
+
+ return initPromise;
+}
diff --git a/src/sheep/request/index.js b/src/sheep/request/index.js
index dfb12db9..19926f52 100644
--- a/src/sheep/request/index.js
+++ b/src/sheep/request/index.js
@@ -10,6 +10,7 @@ import $platform from '@/sheep/platform';
// import { showAuthModal } from '@/sheep/hooks/useModal';
import AuthUtil from '@/sheep/api/member/auth';
import { getTerminal } from '@/sheep/helper/const';
+import chatTokenManager from '@/common/chat-token-manager';
const options = {
// 显示操作成功消息 默认不显示
@@ -115,7 +116,9 @@ http.interceptors.response.use(
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) {
$store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken);
- uni.setStorageSync('loginInfo', response.data.data.chatToken); // 聊天相关token
+ if (response.data.data.chatToken) {
+ chatTokenManager.saveTokenInfo(response.data.data.chatToken);
+ }
}
// 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loading
diff --git a/src/sheep/store/user.js b/src/sheep/store/user.js
index 661f8149..c1c0deb7 100644
--- a/src/sheep/store/user.js
+++ b/src/sheep/store/user.js
@@ -9,6 +9,7 @@ import PayWalletApi from '@/sheep/api/pay/wallet';
import OrderApi from '@/sheep/api/trade/order';
import CouponApi from '@/sheep/api/promotion/coupon';
import chatTokenManager from '@/common/chat-token-manager';
+import * as wsApi from '@/common/wssocket';
// 默认用户信息
const defaultUserInfo = {
@@ -122,6 +123,9 @@ const user = defineStore({
// 重置用户默认数据
resetUserData() {
+ // 先销毁 IM 长连接,避免旧账号在退出后继续心跳或自动重连
+ wsApi.destroy(1000);
+ chatTokenManager.stop();
// 清空 token
this.setToken();
// 清空用户相关的缓存
@@ -129,7 +133,6 @@ const user = defineStore({
this.userWallet = clone(defaultUserWallet);
this.numData = cloneDeep(defaultNumData);
// 清空聊天相关
- chatTokenManager.stop();
uni.removeStorageSync('loginInfo');
// 清空购物车的缓存
cart().emptyList();
diff --git a/src/sheep/ui/su-video/su-video.vue b/src/sheep/ui/su-video/su-video.vue
index f52965fb..868bc214 100644
--- a/src/sheep/ui/su-video/su-video.vue
+++ b/src/sheep/ui/su-video/su-video.vue
@@ -1,12 +1,16 @@
-
-
-
-
-