YingXingAI/App.vue

511 lines
13 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", "");
this.$u.vuex("vuex_userInfo", "");
uni.clearStorage();
this.$u.route({
url: "/pages/login/login/login",
});
return;
uni.navigateTo({
url: "/pages/login/roleSelection",
});
return;
}
},
// onShow(){
// console.log('onShow')
// },
methods: {
getLocation() {
var that = this;
if (this.isWechat()) {
const isiOS = !!navigator.userAgent.match(
/\(i[^;]+;( U;)? CPU.+Mac OS X/
); //ios终端
// 进行签名的时候 Android 不用使用之前的链接, ios 需要
const signLink = isiOS
? window.entryUrl
: window.location.href.split("#")[0];
//获取当前url然后传递给后台获取授权和签名信息后台需要解码才能使用
// const url =encodeURIComponent(signLink);
const url = signLink;
const data = {
url: url,
};
this.$u.api.GetInfoMation(data).then((res) => {
jweixin.config({
debug: false,
appId: res.appId,
timestamp: res.timestamp,
nonceStr: res.noncestr,
signature: res.signature,
jsApiList: [
//这里是需要用到的接口名称
"getLocation", //获取位置
"openLocation", //打开位置
],
});
// jweixin.config 执行失败时调用
jweixin.error((err) => {
uni.hideLoading();
console.log("授权失败,您可能无法使用部分功能", err);
});
jweixin.ready(function () {
jweixin.getLocation({
type: "gcj02", // 默认为wgs84的gps坐标如果要返 回直接给openLocation用的火星坐标可传入'gcj02'
success: (res) => {
uni.hideLoading();
that.$u.vuex("vuex_userLocation", res);
var data = {
userId: that.vuex_user.id,
longitude: res.longitude + "",
latitude: res.latitude + "",
};
that.$u.api.upPosition(data);
},
fail: function (res) {
uni.hideLoading();
console.log(res, "err");
},
complete: function (res) {
uni.hideLoading();
console.log(res, "is");
},
});
});
});
} else {
uni.getLocation({
type: "gcj02 ",
isHighAccuracy: true,
highAccuracyExpireTime: 3000,
success: (res) => {
uni.hideLoading();
this.$u.vuex("vuex_userLocation", res);
var data = {
userId: this.vuex_user.id,
longitude: res.longitude + "",
latitude: res.latitude + "",
};
this.$u.api.upPosition(data);
},
fail: (err) => {
uni.hideLoading();
console.log("地理位置获取失败", err);
},
});
}
},
isWechat() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/micromessenger/i) == "micromessenger") {
// console.log(‘是微信客户端’)
return true;
} else {
// console.log(‘不是微信客户端’)
return false;
}
},
// 构建 WebSocket 连接地址(与 oa-web-phone 保持一致的握手参数)
buildWsUrl() {
const protocol =
window.location.protocol.indexOf("https") === 0 ? "wss" : "ws";
const userId =
(this.vuex_user && (this.vuex_user.id || this.vuex_user.Id)) || "";
return `${protocol}://120.55.234.65:8082/api/Dialogue/HandleConnection?Id=${userId}`; // &equipmentType=0
},
// 初始化原生 WebSocket 连接
initWebSocket() {
console.log(this.buildWsUrl());
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(() => {
try {
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();
}
} catch (err) {
this.lockReconnect = false;
this.reconnect();
}
// 心跳发送后等待后端响应,超时则断开重连
this.serverTimeoutObj = setTimeout(() => {
try {
this.ws && this.ws.close();
} catch (e) {}
this.lockReconnect = false;
this.reconnect();
}, this.timeout);
}, this.timeout);
},
// 连接成功
handleWsOpen() {
this.lockReconnect = true;
// 可按需缓存连接实例到全局
// this.$u.vuex('vuex_websocket', this.ws)
this.reset();
},
// 收到消息
handleWsMessage(e) {
// 收到任何消息都重置心跳
this.reset();
// 心跳消息不处理
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;
// 退出登录通知
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) {
this.lockReconnect = false;
this.reconnect();
},
// 连接错误
handleWsError(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>