From 21b3a06b515a986d112bd26740cfd0581ea00277 Mon Sep 17 00:00:00 2001 From: wally <18603454788@163.com> Date: Thu, 22 Jul 2021 15:57:15 +0800 Subject: [PATCH] feat: ws storage --- CHANGELOG.md | 1 + README.md | 8 ++ src/store/index.js | 4 +- src/store/messages/getters.js | 3 + src/store/messages/index.js | 4 + src/store/messages/mutations.js | 84 ++++++++++++++++++ src/store/messages/state.js | 8 ++ src/store/socket/actions.js | 147 ++++++++++++++++++++++++++++++++ src/store/socket/index.js | 5 ++ src/store/socket/mutations.js | 26 ++++++ src/store/socket/state.js | 7 ++ src/utils/storage.js | 100 ++++++++++++++++++++++ src/utils/tall.js | 2 + 13 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 src/store/messages/getters.js create mode 100644 src/store/messages/index.js create mode 100644 src/store/messages/mutations.js create mode 100644 src/store/messages/state.js create mode 100644 src/store/socket/actions.js create mode 100644 src/store/socket/index.js create mode 100644 src/store/socket/mutations.js create mode 100644 src/store/socket/state.js create mode 100644 src/utils/storage.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e663dc2..d6aab7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ --|--|-- - | api 封装 | [8dcb8a2](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/8dcb8a2) - | env host修改 | [a79a4a5](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/a79a4a5) + - | merge globals | [b0957cc](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/b0957cc) - | mock | [51c24a5](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/51c24a5) pwa 小程序 | 移除了pwa,alloyFinger添加平台判断 | [875fab4](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/875fab4) - | uview-ui | [a9ea34b](http://gitea@dd.tall.wiki:wally/TALL-MUI-3/commits/a9ea34b) diff --git a/README.md b/README.md index 4cfabd0..17525e9 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ yarn yarn dev:h5 ``` +浏览器输入网址: +127.0.0.1:8080/#/?u=xxx&p=x&r=x&pn=x + - u: userId + - p: projectId + - r: roleId + - pn: projectName + - t: taskId + + 微信小程序 ``` yarn dev:mp-weixin diff --git a/src/store/index.js b/src/store/index.js index 571bd49..ebaf57a 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -3,6 +3,8 @@ import Vuex from 'vuex'; import home from './home/index'; import db from './db/index'; import user from './user/index'; +import messages from './messages/index'; +import socket from './socket/index'; Vue.use(Vuex); -export default new Vuex.Store({ modules: { home, db, user } }); +export default new Vuex.Store({ modules: { home, db, user, messages, socket } }); diff --git a/src/store/messages/getters.js b/src/store/messages/getters.js new file mode 100644 index 0000000..56c8c75 --- /dev/null +++ b/src/store/messages/getters.js @@ -0,0 +1,3 @@ +const getters = {}; + +export default getters; diff --git a/src/store/messages/index.js b/src/store/messages/index.js new file mode 100644 index 0000000..48fb83c --- /dev/null +++ b/src/store/messages/index.js @@ -0,0 +1,4 @@ +import state from './state'; +import mutations from './mutations'; + +export default { namespaced: true, state, mutations }; diff --git a/src/store/messages/mutations.js b/src/store/messages/mutations.js new file mode 100644 index 0000000..0df09f0 --- /dev/null +++ b/src/store/messages/mutations.js @@ -0,0 +1,84 @@ +import { setStorageSync, getStorageSync, removeStorageSync } from '@/utils/storage'; + +const mutations = { + /** + * 初始化消息栈 + * @param {object} state + * @param {string} type + * type: + * syncMessages 同步消息栈 + * faultMessages 故障消息 未处理消息栈 + * faults 所有的故障消息栈 + */ + messagesInit(state, type) { + const messages = getStorageSync(type) ? JSON.parse(getStorageSync(type)) : []; + state[type] = messages; + }, + + /** + * 将新 消息添加到 消息栈 最前边 + * @param { object } state + * @param { object } data + * data: message, type + * message 消息对象 + * type: + * syncMessages 同步消息栈 + * faultMessages 故障消息 未处理消息栈 + * faults 所有的故障消息栈 + * game 游戏的消息 + * + * cache: boolean true 本地存储 false 不存储 + */ + messagesAdd(state, data) { + const messages = state[data.type]; + if (messages.length > 0) { + const result = messages.find(msg => msg.id === data.message.id); + if (result) return; + } + messages.unshift(data.message); + // eslint-disable-next-line no-param-reassign + state[data.type] = messages; + if (data.cache) { + setStorageSync(data.type, JSON.stringify(messages)); + } + }, + + /** + * 通过消息id移除指定 同步 消息 + * @param { object } state + * @param { object } data + * data: messageId, type + * messageId: 要移除的消息的messageId + * type: + * syncMessages 同步消息栈 + * faultMessages 故障消息 未处理消息栈 + * faults 所有的故障消息栈 + * cache: boolean true 本地存储 false 不存储 + */ + messagesRemoveById(state, data) { + const messages = state[data.type]; + const index = messages.findIndex(msg => msg.id === data.messageId); + if (index < 0) return; + messages.splice(index, 1); + // eslint-disable-next-line no-param-reassign + state[data.type] = messages; + if (data.cache) { + setStorageSync(data.type, JSON.stringify(messages)); + } + }, + + /** + * 清除指定type的消息 + * @param {any} state + * @param {object} data + * data: type, cache + */ + messagesClear(state, data) { + state[data.type] = []; + if (data.cache) { + removeStorageSync(data.type); + } + }, +}; + +export default mutations; diff --git a/src/store/messages/state.js b/src/store/messages/state.js new file mode 100644 index 0000000..de25201 --- /dev/null +++ b/src/store/messages/state.js @@ -0,0 +1,8 @@ +const state = { + syncMessages: [], // 同步消息 + faultMessages: [], // 新收到的未处理的 故障消息 + faults: [], // 所有的故障消息 + game: [], // 游戏的消息 +}; + +export default state; diff --git a/src/store/socket/actions.js b/src/store/socket/actions.js new file mode 100644 index 0000000..9d30b8f --- /dev/null +++ b/src/store/socket/actions.js @@ -0,0 +1,147 @@ +const WS_BASE_URL = process.env.VUE_APP_MSG_URL; + +let prevTime = 0; +let socketMsgQueue = []; // socket消息队列 +let sendHeartTimer = null; + +const actions = { + // 初始化socket + initSocket({ commit, dispatch, state, rootState }) { + if (state.lockSocket) return; + const { token } = rootState.user; + if (!token) return; + commit('setLockSocket', true); + commit('setSocket', uni.connectSocket({ url: WS_BASE_URL, complete: () => {} })); + dispatch('onSocketOpen'); + dispatch('onSocketMessage'); + dispatch('onSocketClose'); + state.socket.onError(errMsg => console.error(errMsg)); + commit('setLockSocket', false); + }, + + // 监听ws打开 + onSocketOpen({ dispatch, commit, state }) { + state.socket.onOpen(res => { + console.log('ws open: ', res); + commit('setConnected', true); + prevTime = Date.now(); + // this.auth(); + dispatch('auth'); + for (let i = 0; i < socketMsgQueue.length; i++) { + dispatch('sendSocketMessage', socketMsgQueue[i]); + } + socketMsgQueue = []; + }); + }, + + // 监听收到的ws消息 + onSocketMessage({ dispatch, state }) { + state.socket.onMessage(res => { + // console.log('收到消息:', res); + prevTime = Date.now(); + if (!res || !res.data || !JSON.parse(res.data)) return; + const resData = JSON.parse(res.data); + const { messageSet, ackId } = resData; + // 处理消息体对象 + messageSet.forEach(item => dispatch('handleMessagesData', item)); + ackId && dispatch('sendSocketMessage', { type: 'Ack', data: { ackId } }); + }); + }, + + /** + * 处理收到的消息内容 + * @param {object} item 单个消息体对象 + */ + handleMessagesData({ dispatch, commit }, item) { + const data = JSON.parse(item.data); + switch (data.type) { + case 'Sync': // 开始某个节点 + commit('messages/messagesAdd', { message: data, type: 'syncMessages' }, { root: true }); + break; + // case 'Chrome': // !收到开始游戏的消息 + // console.log('handleMessagesData', data); + // // @ts-ignore + // util.openGameApp({ + // type: data.data.type, + // projectId: data.data.projectId, + // id: data.data.recordId, + // token: rootState.user.token, + // }); + // break; + // case 'Deliver': // 交付物相关消息 + // commit('messages/messagesAdd', { type: 'checkMessages', message: data }, { root: true }); + // break; + case 'ChannelStatus': + dispatch('handleAuthMessage', data); + break; + // case 'switchoverProject': // 康复相关消息 + // dispatch('home/getProjectById', data.data.projectId, { root: true }); + // break; + // case 'startDrill': // 康复开始训练相关消息 + // console.log('setStartDrillInfo', data.data); + // commit('home/setStartDrillMessages', data.data, { root: true }); + // break; + default: + break; + } + }, + + // 发送消息 + sendSocketMessage({ state }, data) { + if (state.connected) { + const msg = JSON.stringify({ toDomain: 'Server', data: JSON.stringify(data) }); + state.socket.send({ data: msg }); + } else { + socketMsgQueue.push(data); + } + }, + + // 监听关闭事件 + onSocketClose({ dispatch, commit, state }) { + console.log('onSocketClose'); + state.socket.onClose(() => { + commit('setConnected', false); + if (sendHeartTimer) clearInterval(sendHeartTimer); + setTimeout(() => { + dispatch('initSocket'); + }, 300); + }); + }, + + // websocket发送channelId进行认证 + auth({ dispatch, rootState }) { + const { token } = rootState.user; + if (!token) return; + const data = { type: 'Auth', data: { token } }; + dispatch('sendSocketMessage', data); + }, + + // 心跳检测 + sendHeart({ dispatch, state }) { + if (sendHeartTimer) clearInterval(sendHeartTimer); + sendHeartTimer = setInterval(() => { + if (Date.now() - prevTime >= 15000) { + dispatch('sendSocketMessage', { type: 'Ping' }); + if (Date.now() - prevTime >= 20000) { + state.socket.close(); + } + } + }, 5000); + }, + + /** + * 处理auth认证返回的ChannelStatus消息 + * @param {object} data 消息内容对象 + */ + handleAuthMessage({ commit, dispatch }, data) { + if (data.data.authed) { + dispatch('sendHeart'); + } else { + uni.$u.toast('消息系统认证失败, 请退出重新登录'); + uni.$t.removeStorageSync('anyringToken'); + commit('setSocket', null); + } + }, +}; + +export default actions; diff --git a/src/store/socket/index.js b/src/store/socket/index.js new file mode 100644 index 0000000..f5a4ee5 --- /dev/null +++ b/src/store/socket/index.js @@ -0,0 +1,5 @@ +import state from './state'; +import mutations from './mutations'; +import actions from './actions'; + +export default { namespaced: true, state, mutations, actions }; diff --git a/src/store/socket/mutations.js b/src/store/socket/mutations.js new file mode 100644 index 0000000..7e53b1b --- /dev/null +++ b/src/store/socket/mutations.js @@ -0,0 +1,26 @@ +const mutations = { + // 设置socket实例 + setSocket(state, socket) { + state.socket = socket; + }, + + /** + * 设置socket连接状态 + * @param {Object} state + * @param {boolean} connected 是否连接 true -> 连接 + */ + setConnected(state, connected) { + state.connected = connected; + }, + + /** + * 设置连接锁 正在连接中 锁上 避免多个连接同时发出 + * @param {Object} state + * @param {boolean} lockSocket 是否正在连接的过程中 + */ + setLockSocket(state, lockSocket) { + state.lockSocket = lockSocket; + }, +}; + +export default mutations; diff --git a/src/store/socket/state.js b/src/store/socket/state.js new file mode 100644 index 0000000..d4aa6e3 --- /dev/null +++ b/src/store/socket/state.js @@ -0,0 +1,7 @@ +const state = { + socket: null, // websocket实例 + connected: false, // 是否处于连接状态 + lockSocket: false, // 是否正在连接状态 +}; + +export default state; diff --git a/src/utils/storage.js b/src/utils/storage.js new file mode 100644 index 0000000..01d115c --- /dev/null +++ b/src/utils/storage.js @@ -0,0 +1,100 @@ +export default { + /** + * 设置本地存储 同步 + * @param {string} key + * @param {*} data + */ + setStorageSync(key, data) { + const value = typeof data === 'string' ? data : JSON.stringify(data); + uni.setStorageSync(key, value); + }, + + /** + * 获取本地存储的信息 根据key + * @param {string} key + * @return {string} + */ + getStorageSync(key) { + return uni.getStorageSync(key); + }, + + /** + * 根据key移除某条数据 同步 + * @param {string} key + */ + removeStorageSync(key) { + uni.removeStorageSync(key); + }, + + /** + * 清楚全部数据 同步 + */ + clearStorageSync() { + uni.clearStorageSync(); + }, + + /** + * 设置本地存储 异步 + * @param {string} key + * @param {*} data + */ + setStorage(key, data) { + return new Promise((resolve, reject) => { + const value = typeof data === 'string' ? data : JSON.stringify(data); + uni.setStorage({ + key, + data: value, + success() { + resolve(`数据${key}存储成功`); + }, + fail() { + reject(`数据${key}存储失败`); + }, + }); + }); + }, + + /** + * 获取本地存储的信息 根据key 异步 + * @param {string} key + * @return {string} + */ + getStorage(key) { + return new Promise((resolve, reject) => { + uni.getStorage({ + key, + success(res) { + resolve(res.data); + }, + fail(error) { + reject(`数据${key}获取失败, error: ${error}`); + }, + }); + }); + }, + + /** + * 根据key移除某条数据 异步 + * @param {string} key + */ + removeStorage(key) { + return new Promise((resolve, reject) => { + uni.removeStorage({ + key, + success(res) { + resolve(res); + }, + fail(error) { + reject(`数据${key}删除失败, error: ${error}`); + }, + }); + }); + }, + + /** + * 清楚全部数据 异步 + */ + clearStorage() { + uni.clearStorage(); + }, +}; diff --git a/src/utils/tall.js b/src/utils/tall.js index b7bd84e..8a962b0 100644 --- a/src/utils/tall.js +++ b/src/utils/tall.js @@ -1,11 +1,13 @@ import app from '@/config/app.js'; import zIndex from '@/config/zIndex.js'; import plugin from '@/config/plugin.js'; +import storage from '@/utils/storage.js'; const $t = { zIndex, // 定位元素层级 app, // app级别的相关配置 plugin, // 插件相关配置信息 + storage, // 本地存储storage封装 }; uni.$t = $t;