YingXingAI/store/index.js

330 lines
11 KiB
JavaScript
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.

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
let lifeData = {};
try {
// 尝试获取本地是否存在lifeData变量第一次启动APP时是不存在的
lifeData = uni.getStorageSync("lifeData");
} catch (e) {}
// 需要永久存储且下次APP启动需要取出的在state中的变量名
let saveStateKeys = [
"vuex_user",
"vuex_token",
"vuex_userType",
"vuex_msgUser",
];
// 保存变量到本地存储中
const saveLifeData = function (key, value) {
// 判断变量名是否在需要存储的数组中
if (saveStateKeys.indexOf(key) != -1) {
// 获取本地存储的lifeData对象将变量添加到对象中
let tmp = uni.getStorageSync("lifeData");
// 第一次打开APP不存在lifeData变量故放一个{}空对象
tmp = tmp ? tmp : {};
tmp[key] = value;
// 执行这一步后所有需要存储的变量都挂载在本地的lifeData对象中
uni.setStorageSync("lifeData", tmp);
}
};
const store = new Vuex.Store({
state: {
vuex_teacherInfo: lifeData.vuex_teacherInfo
? lifeData.vuex_teacherInfo
: "",
// 如果上面从本地获取的lifeData对象下有对应的属性就赋值给state中对应的变量
// 加上vuex_前缀是防止变量名冲突也让人一目了然
vuex_userType: lifeData.vuex_userType ? lifeData.vuex_userType : 0, // 0:学生 1:教师
vuex_user: lifeData.vuex_user ? lifeData.vuex_user : "",
vuex_token: lifeData.vuex_token ? lifeData.vuex_token : "",
// 如果vuex_version无需保存到本地永久存储无需lifeData.vuex_version方式
vuex_version: "1.0.0",
// 当前聊天窗口消息列表(数组
vuex_msgList: [],
// 最近联系人/会话列表
vuex_userMsgList: [],
// 当前聊天用户对象
vuex_msgUser: lifeData.vuex_msgUser ? lifeData.vuex_msgUser : {},
// 消息窗口滚动位置
vuex_msgScrollTop: 0,
// 自定义tabbar数据
vuex_iPhone: lifeData.vuex_iPhone ? lifeData.vuex_iPhone : false,
// tabbar相关配置
vuex_tabbar_config: {
activeColor: "#3CB5FB",
inactiveColor: "#666666",
},
vuex_tabbar: [
{
pagePath: "pages/home/index/index",
iconPath: "/static/common/tabbar/index.png",
selectedIconPath: "/static/common/tabbar/selectindex.png",
text: "首页",
},
{
pagePath: "pages/message/msgList/msgList",
iconPath: "/static/common/tabbar/message.png",
selectedIconPath: "/static/common/tabbar/selectmessage.png",
// count: 2, //提示数量
// 初始化红点:基于未读计数
isDot: lifeData.vuex_msgList ? true : false, // 显示红色小点
text: "消息",
},
// {
// pagePath: "/pages/main/index/index",
// iconPath: "/static/common/tabbar/findFriends.png",
// selectedIconPath: "/static/common/tabbar/selectFindFridend.png",
// text: "找校友"
// },
// {
// pagePath: "/pages/AlumniCircle/alumnus/alumnus",
// iconPath: "/static/common/tabbar/AlumniCircle.png",
// selectedIconPath: "/static/common/tabbar/selectAlumniCircle.png",
// text: "校友圈"
// },
// {
// pagePath: "/pages/my/my/my",
// iconPath: "/static/common/tabbar/my.png",
// selectedIconPath: "/static/common/tabbar/selectmy.png",
// text: "我的"
// }
],
},
mutations: {
$uStore(state, payload) {
// 判断是否多层级调用state中为对象存在的情况诸如user.info.score = 1
let nameArr = payload.name.split(".");
let saveKey = "";
let len = nameArr.length;
if (len >= 2) {
let obj = state[nameArr[0]];
for (let i = 1; i < len - 1; i++) {
obj = obj[nameArr[i]];
}
obj[nameArr[len - 1]] = payload.value;
saveKey = nameArr[0];
} else {
// 单层级变量在state就是一个普通变量的情况
state[payload.name] = payload.value;
saveKey = payload.name;
}
// 保存变量到本地,见顶部函数定义
saveLifeData(saveKey, state[saveKey]);
},
// ===== 消息中心:列表、当前聊天、消息推送等 =====
// 设置会话列表(服务端返回)
set_UserMsgList(state, list) {
state.vuex_userMsgList = Array.isArray(list) ? list.slice() : [];
if (state.vuex_msgUser && state.vuex_msgUser.friendId) {
state.vuex_userMsgList = state.vuex_userMsgList.map((item) => {
if (item && item.friendId === state.vuex_msgUser.friendId) {
return { ...item, unReadCount: 0 };
}
return item;
});
}
},
// 设置当前聊天用户
set_MsgUser(state, user) {
state.vuex_msgUser = user || null;
if (state.vuex_msgUser && state.vuex_msgUser.friendId) {
state.vuex_userMsgList = (state.vuex_userMsgList || []).map((item) => {
if (item && item.friendId === state.vuex_msgUser.friendId) {
return { ...item, unReadCount: 0 };
}
return item;
});
}
},
// 现在没有id先覆盖整个list
push_MsgList(state, list) {
state.vuex_msgList = list || [];
},
// 推送一条新消息(去重)
push_Msg(state, msg) {
// 注现在的消息没有id无法去重暂时用push_MsgList
if (!msg || !msg.id) return;
const exists = (state.vuex_msgList || []).some(
(item) => item && item.id === msg.id
);
if (!exists) {
state.vuex_msgList.push(msg);
}
},
// 插入历史消息到头部
unshift_Msg(state, msg) {
if (!msg) return;
state.vuex_msgList.unshift(msg);
},
// 更新置顶状态(本地)
update_TopState(state, { friendId, isTop }) {
state.vuex_userMsgList = (state.vuex_userMsgList || []).map((item) => {
if (item && item.friendId === friendId) {
return { ...item, isTop: !!isTop };
}
return item;
});
},
// 更新消息窗口滚动位置
set_MsgScrollTop(state, top) {
state.vuex_msgScrollTop = Number(top) || 0;
},
// 清空消息相关状态(登出/切换账号)
clear_MessageState(state) {
state.vuex_msgList = [];
state.vuex_userMsgList = [];
state.vuex_msgUser = null;
state.vuex_msgScrollTop = 0;
},
// 统一登出清理:清除 token 与用户数据,并同步到本地存储
// 注意:仅清理与登录态相关的关键字段,避免影响其他持久化数据
logout(state) {
// 清除登录令牌
state.vuex_token = "";
state.vuex_teacherInfo = "";
state.vuex_user = "";
// 清理消息相关状态
state.vuex_msgList = [];
state.vuex_userMsgList = [];
state.vuex_msgUser = null;
state.vuex_msgScrollTop = 0;
// 同步更新本地持久化
saveLifeData("vuex_token", state.vuex_token);
saveLifeData("vuex_teacherInfo", state.vuex_teacherInfo);
saveLifeData("vuex_user", state.vuex_user);
},
},
actions: {
// 获取聊天列表(最近联系人)
async getUserlist({ commit, state }) {
// 在 Vuex action 中没有组件 this上下文不包含 $u
// 通过 Vue.prototype.$u 使用 uView 的 http 实例
return Vue.prototype.$u.api.GetDialogueListApi().then((res) => {
const list = (res && res.data.item1 ? res.data.item1 : []).map(
(item) => {
const unReadCount =
state.vuex_msgUser &&
state.vuex_msgUser.friendId === item.friendId
? 0
: item.unReadCount || 0;
return {
...item,
// avatar: item.avatar,
unReadCount,
};
}
);
commit("set_UserMsgList", list);
return list;
});
},
// 设置当前聊天用户
setMsgUser({ commit, dispatch }, user) {
console.log("setMsgUser执行了", user);
commit("set_MsgUser", user);
// 刷新列表,清零未读
dispatch("getUserlist");
},
// 获取聊天记录(私聊)
async fetchChatRecord(
{ commit, state },
{ userId, friendId, chatType = 0, PageIndex = 1, PageSize = 20 }
) {
const params = { userId, friendId, PageIndex, PageSize };
Vue.prototype.$u.api.GetChatHistoryDataApi(params).then((res) => {
const list = res && res.data ? res.data : [];
// 将最新消息插入到消息列表尾部(或头部)
list.forEach((msg) => commit("push_Msg", msg));
});
},
// 设置消息置顶(服务端 + 本地)
async setTopMsgUser({ commit, dispatch }, user) {
if (!user || !user.friendId) return;
if (user.isTop) {
return;
} else {
Vue.prototype.$u.api
.OverheadOneDialogueApi({ dialogueManagementId: user.friendId })
.then((res) => {
commit("update_TopState", { friendId: user.friendId, isTop: true });
dispatch("getUserlist");
});
}
},
// 统一入口:打开或创建会话
// 使用位置:所有需要进入聊天的入口调用该方法,避免各页面重复逻辑
// 1) 获取最新会话列表
// 2) 查找是否存在 friendId 对应的会话
// 3) 有则设置当前会话并(可选)跳转;无则创建,成功后递归重试
async openOrCreateDialogue({ commit, state, dispatch }, user) {
const { friendId, friendName, avatar, chatType = 0 } = user || {};
const attempt =
user && typeof user.attempt === "number" ? user.attempt : 1;
if (!friendId) return Promise.reject(new Error("缺少 friendId"));
// 清空消息列表,避免旧消息干扰
commit("push_MsgList", []);
// 第一步:获取列表(复用现有 action保证 state 同步更新)
const list = await dispatch("getUserlist");
// 第二步:查找是否存在该会话(兼容后端字段 friendId/receiverId
const target = (list || []).find(
(i) => i && (i.receiverId === friendId || i.friendId === friendId)
);
if (target) {
// 获取消息接收方用户信息
Vue.prototype.$u.api
.GetReceiverUserInfoApi({ Id: friendId })
.then((res) => {
if (res.succeed && res.data) {
commit("set_MsgUser", { ...target, ...res.data });
} else {
commit("set_MsgUser", target);
}
});
// 跳转到对话页,参数按需调整
uni.navigateTo({
url: `/pages/chat/index`,
});
return target;
}
// 第二次调用时,无论成功与否均在第三步(创建)之前结束
if (attempt >= 2) {
return false;
}
// 第三步:不存在则创建,成功后重试本流程
const res = await Vue.prototype.$u.api.AddDialogueApi({
receiverId: friendId,
onlineConsultationType: 0, // 0招生在线1迎新在线可按业务传入
});
if (res && res.succeed === true) {
// 创建成功后,重新执行流程(再次获取列表并进入会话)
return dispatch("openOrCreateDialogue", {
friendId,
friendName,
avatar,
chatType,
attempt: attempt + 1,
});
}
return Promise.reject(new Error(res?.error || "创建会话失败"));
},
},
});
export default store;