YingXingAI/App.vue

513 lines
13 KiB
Vue
Raw Normal View History

2025-06-30 14:43:02 +08:00
<script>
import router from "./static/common/js/router";
import config from "./static/common/js/config.js";
2025-06-30 14:43:02 +08:00
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, // 重连延时定时器
};
2025-06-30 14:43:02 +08:00
},
globalData: {},
created() {},
2025-06-30 14:43:02 +08:00
onLaunch() {
if (typeof window.entryUrl === "undefined" || window.entryUrl === "") {
window.entryUrl = location.href.split("#")[0];
2025-06-30 14:43:02 +08:00
}
router.initApp(this);
// 处理项目加载时白名单不生效异常问题
let isUrl = this._route.fullPath.split("?")[0];
let notNeed = config.whiteList.includes(isUrl);
2025-06-30 14:43:02 +08:00
if (notNeed) {
uni.navigateTo({
url: this._route.fullPath,
});
return;
2025-06-30 14:43:02 +08:00
}
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;
2025-06-30 14:43:02 +08:00
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;
2025-06-30 14:43:02 +08:00
}
},
// onShow(){
// console.log('onShow')
// },
methods: {
getLocation() {
var that = this;
if (this.isWechat()) {
const isiOS = !!navigator.userAgent.match(
/\(i[^;]+;( U;)? CPU.+Mac OS X/
2025-06-30 14:43:02 +08:00
); //ios终端
// 进行签名的时候 Android 不用使用之前的链接, ios 需要
const signLink = isiOS
? window.entryUrl
: window.location.href.split("#")[0];
2025-06-30 14:43:02 +08:00
//获取当前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);
2025-06-30 14:43:02 +08:00
var data = {
userId: that.vuex_user.id,
longitude: res.longitude + "",
latitude: res.latitude + "",
};
that.$u.api.upPosition(data);
2025-06-30 14:43:02 +08:00
},
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);
2025-06-30 14:43:02 +08:00
var data = {
userId: this.vuex_user.id,
longitude: res.longitude + "",
latitude: res.latitude + "",
};
this.$u.api.upPosition(data);
2025-06-30 14:43:02 +08:00
},
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 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() {
console.log(this.buildWsUrl());
2025-12-05 11:05:39 +08:00
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();
2025-06-30 14:43:02 +08:00
}
} catch (err) {
this.lockReconnect = false;
this.reconnect();
2025-06-30 14:43:02 +08:00
}
// 心跳发送后等待后端响应,超时则断开重连
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);
2025-06-30 14:43:02 +08:00
}
} else {
if (msgList.indexOf("WebSocketMessage") < 0) {
msgList += "WebSocketMessage,";
this.$u.vuex("vuex_msgList", msgList);
2025-06-30 14:43:02 +08:00
}
}
},
// 连接关闭
handleWsClose(e) {
this.lockReconnect = false;
this.reconnect();
},
// 连接错误
handleWsError(e) {
console.log("[WebSocket] 连接错误:", e);
this.lockReconnect = false;
this.reconnect();
2025-06-30 14:43:02 +08:00
},
// 启动连接入口(需存在用户)
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");
2025-12-05 11:05:39 +08:00
if (hasUser && hasToken) {
this.initWebSocket();
2025-06-30 14:43:02 +08:00
} else {
this.lockReconnect = false;
this.reconnect();
2025-06-30 14:43:02 +08:00
}
},
},
mounted() {
// 使用与 oa-web-phone 相同的原生 WebSocket 通信方式
this.startLink();
2025-06-30 14:43:02 +08:00
},
};
</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;
2025-06-30 14:43:02 +08:00
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;
2025-06-30 14:43:02 +08:00
-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>