YingXingAI/App.vue

420 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
import router from "./static/common/js/router";
import config from "./static/common/js/config.js";
var jweixin = require("jweixin-module");
export default {
data() {
return {
show: false,
// WebSocket 实例与连接控制
ws: null, // 当前 WebSocket 连接
lockReconnect: false, // 是否处于稳定连接,防止重复重连
// timeout: 30000, // 心跳间隔(毫秒)
timeoutObj: null, // 心跳倒计时定时器
serverTimeoutObj: null, // 心跳响应等待定时器
timeoutnum: null, // 重连延时定时器
};
},
globalData: {},
created() {},
onLaunch() {
if (typeof window.entryUrl === "undefined" || window.entryUrl === "") {
window.entryUrl = location.href.split("#")[0];
}
router.initApp(this);
// 处理项目加载时白名单不生效异常问题
let isUrl = this._route.fullPath.split("?")[0];
let notNeed = config.whiteList.includes(isUrl);
if (notNeed) {
uni.navigateTo({
url: this._route.fullPath,
});
return;
}
var that = this;
uni.getSystemInfo({
success: function (res) {
// 根据 model 进行判断
if (res.model.indexOf("iPhone") >= 0) {
that.$u.vuex("vuex_iPhone", true);
}
},
});
if (!that.vuex_token) {
const type = that.vuex_userType || 0; // 0:学生 1:教师
this.$u.vuex("vuex_user", "");
this.$u.vuex("vuex_token", "");
uni.clearStorage();
uni.reLaunch({
url: `/pages/login/login/index?type=${type}`,
});
return;
}
return;
if (!that.vuex_user.isFill) {
this.$u.vuex("vuex_msgList", "");
this.$u.vuex("vuex_user", "");
this.$u.vuex("vuex_token", "");
uni.clearStorage();
this.$u.route({
url: "/pages/login/login/login",
});
return;
uni.navigateTo({
url: "/pages/login/roleSelection",
});
return;
}
},
// onShow(){
// console.log('onShow')
// },
methods: {
// 构建 WebSocket 连接地址(与 oa-web-phone 保持一致的握手参数)
buildWsUrl() {
const protocol =
window.location.protocol.indexOf("https") === 0 ? "wss" : "ws";
const Id =
(this.vuex_user && (this.vuex_user.id || this.vuex_user.Id)) || "";
return `${protocol}://120.55.234.65:8082/api/Dialogue/HandleConnection?Id=${Id}`; // &equipmentType=0
},
// 初始化原生 WebSocket 连接
initWebSocket() {
try {
this.ws = new WebSocket(this.buildWsUrl());
this.ws.onopen = () => this.handleWsOpen();
this.ws.onmessage = (e) => this.handleWsMessage(e);
this.ws.onclose = (e) => this.handleWsClose(e);
this.ws.onerror = (e) => this.handleWsError(e);
} catch (err) {
console.log("[WebSocket] 创建连接失败:", err);
this.reconnect();
}
},
// 重连(防抖,避免频繁重试)
reconnect() {
if (this.lockReconnect) return;
this.timeoutnum && clearTimeout(this.timeoutnum);
this.timeoutnum = setTimeout(() => {
this.startLink();
}, 5000);
},
// 心跳重置并重启
reset() {
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.start();
},
// 启动心跳与超时处理
start() {
this.timeoutObj = setTimeout(() => {
//这里发送一个心跳,后端收到后,返回一个心跳消息
if (
this.ws &&
this.ws.readyState === 1 &&
this.vuex_user &&
(this.vuex_user.Id || this.vuex_user.id)
) {
//如果连接正常
this.ws.send("heartCheck");
} else {
//否则重连
this.lockReconnect = false;
this.reconnect();
}
// 心跳发送后等待后端响应,超时则断开重连
this.serverTimeoutObj = setTimeout(() => {
// console.log("[WebSocket] 心跳响应超时,断开重连");
this.ws && this.ws.close();
this.lockReconnect = false;
this.reconnect();
}, 30000);
}, 30000);
},
// 连接成功
handleWsOpen() {
this.lockReconnect = true;
// 可按需缓存连接实例到全局
// this.$u.vuex('vuex_websocket', this.ws)
this.reset();
},
// 收到消息
handleWsMessage(e) {
// 收到任何消息都重置心跳
this.reset();
console.log("[WebSocket] 收到消息:", e);
// 心跳消息不处理
// if (typeof e.data === "string" && e.data.indexOf("heartCheck") >= 0) {
// return;
// }
// 尝试解析为 JSON与 oa-web-phone 保持一致的结构:{ Type, Data }
let msgData = null;
try {
msgData = JSON.parse(e.data);
} catch (err) {
console.log("[WebSocket] 消息解析失败:", err, e.data);
return;
}
const type = msgData.Type;
console.log("收到消息类型:", type);
// 收到服务端心跳响应不处理
if (type === "pong") return;
// 退出登录通知
if (type === "Exit") {
try {
this.ws && this.ws.close();
} catch (err) {}
// 清理本地登录信息并返回登录页
this.$u.vuex("vuex_user", "");
this.$u.vuex("vuex_token", "");
uni.clearStorage();
const typeNum = this.vuex_userType || 0; // 0:学生 1:教师
uni.reLaunch({ url: `/pages/login/login/index?type=${typeNum}` });
return;
}
// 标记 TabBar 红点
const tab = this.vuex_tabbar || [];
if (Array.isArray(tab) && tab[1]) {
tab[1].isDot = true;
this.$u.vuex("vuex_tabbar", tab);
}
// 维护消息提示队列(与原逻辑兼容,使用通用 key
let msgList = this.vuex_msgList || "";
if (type === "ChatMessageDto") {
const data = msgData.Data || {};
const msgUserId = data.ChatType === 1 ? data?.ToId : data?.UserId;
const key = `chat_${msgUserId || "unknown"}`;
if (msgList.indexOf(key) < 0) {
msgList += key + ",";
this.$u.vuex("vuex_msgList", msgList);
}
} else {
if (msgList.indexOf("WebSocketMessage") < 0) {
msgList += "WebSocketMessage,";
this.$u.vuex("vuex_msgList", msgList);
}
}
},
// 连接关闭
handleWsClose(e) {
// console.log(`[WebSocket] 连接关闭: code=${e.code}, reason=${e.reason}`);
this.lockReconnect = false;
this.reconnect();
},
// 连接错误
handleWsError(e) {
// console.log("[WebSocket] 连接错误:", e);
this.lockReconnect = false;
this.reconnect();
},
// 启动连接入口(需存在用户)
startLink() {
if (this.lockReconnect) return;
const hasUser =
this.vuex_user && (this.vuex_user.id || this.vuex_user.Id);
const hasToken =
!!this.vuex_token || !!window.localStorage.getItem("token");
if (hasUser && hasToken) {
this.initWebSocket();
} else {
this.lockReconnect = false;
this.reconnect();
}
},
},
mounted() {
// 使用与 oa-web-phone 相同的原生 WebSocket 通信方式
this.startLink(); // 现在一直断线重连,先注释
},
};
</script>
<style lang="scss">
@import "uview-ui/index.scss";
@import "common/demo.scss";
.u-tabbar__content__item__button {
top: 0.07rem !important;
}
.u-navbar-placeholder,
.u-navbar-inner {
height: 0.44rem !important;
}
.u-tabbar__content__item__text {
font-size: 20rpx !important;
}
.u-tabbar__content__item__button .u-icon__img {
width: 44rpx !important;
height: 44rpx !important;
}
.u-size-mini {
font-size: 0.12rem !important;
}
uni-modal .uni-modal__hd {
padding: 0.1rem;
}
uni-modal .uni-modal__bd {
padding-top: 20rpx;
// font-weight: bold;
font-size: 34rpx;
color: rgba(0, 0, 0, 0.9);
}
uni-modal .uni-modal__btn {
border-radius: 16rpx;
font-size: 28rpx;
}
uni-modal .uni-modal__btn:after {
border: none !important;
}
uni-modal .uni-modal__btn_default {
background: #e6f6ff;
color: #3cb5fb !important;
margin-right: 20rpx;
}
uni-modal .uni-modal__ft {
line-height: 80rpx;
}
uni-modal .uni-modal__ft:after {
border: none !important;
}
uni-modal .uni-modal__btn_primary {
background: #3cb5fb;
color: #ffffff !important;
}
uni-modal .uni-modal {
padding: 40rpx;
box-sizing: border-box;
border-radius: 30rpx;
}
.u-tabbar__content {
height: 0.5rem !important;
}
.phone .u-tabbar__content {
padding-bottom: 0.14rem;
}
.u-tabs-scorll-flex .u-tabs-item {
transition: all 0.1s;
font-size: 0.15rem !important;
}
* {
font-family: "pingfang";
}
html {
font-size: 200rpx;
}
uni-page-body {
height: 100%;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
"Microsoft Yahei", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
// max-width: 1536rpx;
margin: 0 auto;
background-color: #f6f7fa;
}
view,
image,
text {
box-sizing: border-box;
flex-shrink: 0;
}
#app {
width: 100vw;
height: 100vh;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-center {
display: flex;
justify-content: center;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
.justify-evenly {
display: flex;
justify-content: space-evenly;
}
.justify-around {
display: flex;
justify-content: space-around;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.items-start {
display: flex;
align-items: flex-start;
}
.items-center {
display: flex;
align-items: center;
}
.items-end {
display: flex;
align-items: flex-end;
}
</style>