Initial commit

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 12:02:35 +08:00
co-authored by Claude Opus 5
commit d999a5c0e6
643 changed files with 178681 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
import API from "@/api";
function getUserInfoRaw() {
return localStorage.getItem("userInfo");
}
/** 用 token 识别会话,避免 refresh 改写 userInfo JSON 后误清线路缓存 */
function getSessionKey(userInfoRaw) {
if (!userInfoRaw) return "";
try {
const info = JSON.parse(userInfoRaw);
return info && info.token ? String(info.token) : userInfoRaw;
} catch (e) {
return userInfoRaw;
}
}
export const LoginPlugin = {
install(Vue, { router }) {
console.log("Login plugin:::", Vue.prototype, router);
// 已请求失败(如 token 失效)后不再重复拉;重新登录后会重置
let sourceInfoLoadFailed = false;
// 并发去重:同一时刻只允许一次 getSourceList
let sourceInfoPromise = null;
// 递增以作废进行中的请求结果,避免登出后旧请求回写
let loadSeq = 0;
let lastSessionKey = "";
const showError = (error) => {
console.error("update or sync source info error:::", error);
Vue.prototype.$message.error("图片、视频资源将无法展示,推出重新登陆");
};
const resetSourceInfoLoadState = () => {
loadSeq += 1;
sourceInfoLoadFailed = false;
sourceInfoPromise = null;
Vue.prototype.$sourceInfo = null;
};
const updateSourceInfo = async () => {
const userInfoRaw = getUserInfoRaw();
if (!userInfoRaw) {
lastSessionKey = "";
resetSourceInfoLoadState();
return;
}
const sessionKey = getSessionKey(userInfoRaw);
// 会话切换(重新登录 / 换账号)后允许再拉;同会话仅 token 刷新不清理已成功的线路
if (lastSessionKey && lastSessionKey !== sessionKey) {
resetSourceInfoLoadState();
}
lastSessionKey = sessionKey;
if (Vue.prototype.$sourceInfo || sourceInfoLoadFailed) return;
if (sourceInfoPromise) return sourceInfoPromise;
const seq = ++loadSeq;
sourceInfoPromise = (async () => {
try {
const r = await API.getSourceList();
// 请求返回时会话已变(登出 / 重登),丢弃结果
if (seq !== loadSeq || getSessionKey(getUserInfoRaw()) !== sessionKey) {
return;
}
if (r.code !== 200) {
sourceInfoLoadFailed = true;
throw new Error(r.tip || r.msg || "getSourceList failed");
}
syncSourceInfo(r.data);
} catch (error) {
if (seq !== loadSeq || getSessionKey(getUserInfoRaw()) !== sessionKey) {
return;
}
sourceInfoLoadFailed = true;
showError(error);
} finally {
if (seq === loadSeq) {
sourceInfoPromise = null;
}
}
})();
return sourceInfoPromise;
};
const syncSourceInfo = (info) => {
info = info || [];
Vue.prototype.$sourceInfo = info;
sourceInfoLoadFailed = false;
try {
let _index = localStorage.getItem("overallSituationLine") || 0;
if (findUrls(info, "AdminImage")) {
Vue.prototype.$imgUrls = [
...findUrls(info, "IMAGE"),
...findUrls(info, "AdminImage"),
];
} else {
Vue.prototype.$imgUrls = [...findUrls(info, "IMAGE")];
}
Vue.prototype.$vidUrls = findUrls(info, "VID");
Vue.prototype.$audUrls = findUrls(info, "AUDIO");
Vue.prototype.$imgUrl =
Vue.prototype.$imgUrls[_index].url || Vue.prototype.$imgUrls[0].url;
Vue.prototype.$vidUrl = Vue.prototype.$vidUrls[0].url;
Vue.prototype.$audUrl = Vue.prototype.$audUrls[0].url;
} catch (error) {
sourceInfoLoadFailed = true;
showError(error);
}
};
const findUrls = (info, key) => {
return (
info.find((kv) => kv.type === key) &&
info.find((kv) => kv.type === key).domain
);
};
Vue.prototype.$syncSourceInfo = syncSourceInfo;
Vue.prototype.$setOverallSituationLine = (index) => {
localStorage.setItem("overallSituationLine", index);
Vue.prototype.$imgUrl =
Vue.prototype.$imgUrls[index].url || Vue.prototype.$imgUrls[0].url;
Vue.prototype.$vidUrl = Vue.prototype.$vidUrls[0].url;
Vue.prototype.$audUrl = Vue.prototype.$audUrls[0].url;
location.reload();
};
router.beforeEach(async (to, from, next) => {
if (to.path === "/" || to.path === "/login") {
// 回到登录页且本地已无登录态时,清理失败标记,便于重新登录后拉取
if (!getUserInfoRaw()) {
lastSessionKey = "";
resetSourceInfoLoadState();
}
return next();
}
await updateSourceInfo();
return next();
});
},
};