This commit is contained in:
fanxb 2022-03-21 17:06:52 +08:00
parent ff87689671
commit 40290e6ac8
12 changed files with 516 additions and 483 deletions

View File

@ -8,7 +8,7 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"ant-design-vue": "^1.6.3", "ant-design-vue": "^1.7.8",
"axios": "^0.21.1", "axios": "^0.21.1",
"babel-plugin-import": "^1.13.0", "babel-plugin-import": "^1.13.0",
"clipboard": "^2.0.6", "clipboard": "^2.0.6",

View File

@ -7,10 +7,6 @@
<script> <script>
export default { export default {
name: "App", name: "App",
async created(){
//
await this.$store.dispatch("globalConfig/refreshServerConfig");
}
}; };
</script> </script>

View File

@ -56,3 +56,5 @@ window.vueInstance = new Vue({
store, store,
render: h => h(App) render: h => h(App)
}).$mount("#app"); }).$mount("#app");

View File

@ -1,7 +1,8 @@
import Vue from "vue"; import Vue from "vue";
import VueRouter from "vue-router"; import VueRouter from "vue-router";
import vuex from "../store/index.js"; import * as vuex from "../store/index.js";
import { GLOBAL_CONFIG, SUPPORT_NO_LOGIN, TOKEN } from "@/store/modules/globalConfig"; import { GLOBAL_CONFIG, SUPPORT_NO_LOGIN, TOKEN } from "@/store/modules/globalConfig";
import { checkJwtValid } from "@/util/UserUtil";
Vue.use(VueRouter); Vue.use(VueRouter);
@ -11,7 +12,7 @@ const routes = [
path: "/manage", path: "/manage",
component: () => import("@/views/manage/index"), component: () => import("@/views/manage/index"),
children: [ children: [
{ path: "/bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") }, { path: "bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") },
{ path: "personSpace/userInfo", component: () => import("@/views/manage/personSpace/index") }, { path: "personSpace/userInfo", component: () => import("@/views/manage/personSpace/index") },
] ]
}, },
@ -37,10 +38,14 @@ const router = new VueRouter({
/** /**
* 在此进行登录信息判断以及重定向到登录页面 * 在此进行登录信息判断以及重定向到登录页面
*/ */
router.beforeEach((to, from, next) => { router.beforeEach(async (to, from, next) => {
//进入主页面/管理页面时,确认已经进行初始化操作
if (to.path === '/' || to.path.startsWith("/manage")) {
await vuex.loginInit();
}
let supportNoLogin = to.path === '/' || to.path.startsWith("/public"); let supportNoLogin = to.path === '/' || to.path.startsWith("/public");
vuex.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin); vuex.default.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
if (!supportNoLogin && !checkJwtValid()) { if (!supportNoLogin && !checkJwtValid(vuex.default.state[GLOBAL_CONFIG][TOKEN])) {
//如不支持未登录进入切jwt已过期直接跳转到登录页面 //如不支持未登录进入切jwt已过期直接跳转到登录页面
next({ next({
path: "/public/login?to=" + btoa(location.href), path: "/public/login?to=" + btoa(location.href),
@ -51,23 +56,6 @@ router.beforeEach((to, from, next) => {
} }
}) })
/**
* 检查jwt是否有效
*/
function checkJwtValid () {
let token = vuex.state[GLOBAL_CONFIG][TOKEN];
try {
if (token && token.trim().length > 0) {
//检查token是否还有效
let content = window.atob(token.split(".")[1]);
if (content.exp > Date.now() / 1000) {
return true;
}
}
} catch (err) {
console.error(err);
}
return false;
}
export default router; export default router;

View File

@ -1,16 +1,55 @@
import Vue from "vue"; import Vue from "vue";
import Vuex from "vuex"; import Vuex from "vuex";
import globalConfig from "./modules/globalConfig"; import * as globalConfig from "./modules/globalConfig";
import treeData from "./modules/treeData"; import * as treeData from "./modules/treeData";
import { checkJwtValid } from "@/util/UserUtil";
Vue.use(Vuex); Vue.use(Vuex);
export default new Vuex.Store({ let store = new Vuex.Store({
state: {}, state: {},
mutations: {}, mutations: {},
actions: {}, actions: {},
modules: { modules: {
globalConfig, [globalConfig.GLOBAL_CONFIG]: globalConfig.store,
treeData [treeData.TREE_DATA]: treeData.store
} }
}); });
let noLoginFinish = false;
//执行各自的非登陆初始化
(async () => {
await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.noLoginInit);
await store.dispatch(treeData.TREE_DATA + "/" + treeData.noLoginInit);
noLoginFinish = true;
})();
/**
* 执行各模块的登陆后初始化
*/
export async function loginInit () {
if (!noLoginFinish) {
await finishNoLogin();
}
console.log(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN]);
if (checkJwtValid(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN])) {
await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.loginInit);
await store.dispatch(treeData.TREE_DATA + "/" + treeData.loginInit);
}
}
async function finishNoLogin () {
return new Promise((resolve) => {
let timer = setInterval(() => {
if (noLoginFinish) {
clearInterval(timer);
resolve();
}
}, 100);
})
}
export default store;

View File

@ -1,11 +1,15 @@
import localforage from "localforage"; import localforage from "localforage";
import HttpUtil from "../../util/HttpUtil"; import HttpUtil from "../../util/HttpUtil";
export const GLOBAL_CONFIG = "globalConfig"; export const GLOBAL_CONFIG = "globalConfig";
export const USER_INFO = "userInfo"; export const USER_INFO = "userInfo";
export const TOKEN = "token"; export const TOKEN = "token";
export const SERVER_CONFIG = "serverConfig"; export const SERVER_CONFIG = "serverConfig";
export const SUPPORT_NO_LOGIN = "supportNoLogin"; export const SUPPORT_NO_LOGIN = "supportNoLogin";
export const IS_INIT = "isInit";
export const noLoginInit = "noLoginInit";
export const loginInit = "loginInit";
/** /**
* 存储全局配置 * 存储全局配置
*/ */
@ -13,7 +17,7 @@ const state = {
/** /**
* 用户信息 * 用户信息
*/ */
[USER_INFO]: {}, [USER_INFO]: null,
/** /**
* token,null说明未获取登录凭证 * token,null说明未获取登录凭证
*/ */
@ -21,11 +25,11 @@ const state = {
/** /**
* 是否已经初始化完成,避免多次重复初始化 * 是否已经初始化完成,避免多次重复初始化
*/ */
isInit: false, [IS_INIT]: false,
/** /**
* 是否移动端 * 是否移动端
*/ */
isPhone: false, isPhone: /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent),
/** /**
* 是否支持未登录进入页面 * 是否支持未登录进入页面
*/ */
@ -39,63 +43,47 @@ const state = {
const getters = {}; const getters = {};
const actions = { const actions = {
//未登录需要进行的初始化
async [noLoginInit] ({ commit }) {
commit(SERVER_CONFIG, await HttpUtil.get("/common/config/global"));
let token = await localforage.getItem(TOKEN);
if (token) {
commit(TOKEN, token);
window.jwtToken = token;
}
},
//登陆后的,初始化数据 //登陆后的,初始化数据
async init (context) { async [loginInit] (context) {
if (context.state.isInit) { if (context.state.isInit) {
return; return;
} }
const token = await localforage.getItem(TOKEN);
await context.dispatch("setToken", token);
let userInfo = await localforage.getItem(USER_INFO);
if (userInfo) {
context.commit(USER_INFO, userInfo);
}
try {
await context.dispatch("refreshUserInfo");
} catch (err) {
console.error(err);
}
context.commit("isInit", true);
context.commit("isPhone", /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent));
},
async refreshUserInfo ({ commit }) {
let userInfo = await HttpUtil.get("/user/currentUserInfo"); let userInfo = await HttpUtil.get("/user/currentUserInfo");
await localforage.setItem(USER_INFO, userInfo); context.commit(USER_INFO, userInfo);
commit(USER_INFO, userInfo); context.commit(IS_INIT, true);
}, },
async setToken ({ commit }, token) { async setToken ({ commit }, token) {
await localforage.setItem(TOKEN, token); await localforage.setItem(TOKEN, token);
window.jwtToken = token;
commit(TOKEN, token); commit(TOKEN, token);
}, },
//登出清除数据 //登出清除数据
async clear (context) { async clear (context) {
await localforage.removeItem("userInfo"); await localforage.removeItem(TOKEN);
await localforage.removeItem("token");
context.commit(USER_INFO, null); context.commit(USER_INFO, null);
context.commit(TOKEN, null); context.commit(TOKEN, null);
context.commit("isInit", false); context.commit(IS_INIT, false);
}, },
/**
* 从服务器读取全局配置
*/
async refreshServerConfig ({ commit }) {
commit(SERVER_CONFIG, await HttpUtil.get("/common/config/global"));
}
}; };
const mutations = { const mutations = {
userInfo (state, userInfo) { [USER_INFO] (state, userInfo) {
state.userInfo = userInfo; state[USER_INFO] = userInfo;
}, },
token (state, token) { [TOKEN] (state, token) {
state.token = token; state[TOKEN] = token;
}, },
isInit (state, isInit) { [IS_INIT] (state, isInit) {
state.isInit = isInit; state[IS_INIT] = isInit;
},
isPhone (state, status) {
state.isPhone = status;
}, },
[SERVER_CONFIG] (state, serverConfig) { [SERVER_CONFIG] (state, serverConfig) {
state[SERVER_CONFIG] = serverConfig; state[SERVER_CONFIG] = serverConfig;
@ -106,7 +94,7 @@ const mutations = {
}; };
export default { export const store = {
namespaced: true, namespaced: true,
state, state,
getters, getters,

View File

@ -1,8 +1,26 @@
import localforage from "localforage"; import localforage from "localforage";
import { } from "ant-design-vue";
import HttpUtil from "../../util/HttpUtil"; import HttpUtil from "../../util/HttpUtil";
const TOTAL_TREE_DATA = "totalTreeData"; export const TREE_DATA = "treeData";
const VERSION = "version"; export const TOTAL_TREE_DATA = "totalTreeData";
export const VERSION = "version";
export const SHOW_REFRESH_TOAST = "showRefreshToast";
export const IS_INIT = "isInit";
export const IS_INITING = "isIniting";
export const noLoginInit = "noLoginInit";
export const loginInit = "loginInit";
/**
* 版本检查定时调度
*/
let timer = null;
/**
* 刷新书签确认弹窗是否展示
*/
let toastShow = false;
/** /**
* 书签树相关配置 * 书签树相关配置
@ -10,12 +28,14 @@ const VERSION = "version";
const state = { const state = {
//全部书签数据 //全部书签数据
[TOTAL_TREE_DATA]: {}, [TOTAL_TREE_DATA]: {},
//版本
[VERSION]: null, [VERSION]: null,
isInit: false, //是否已经初始化书签数据
/** [IS_INIT]: false,
* 是否正在加载数据 // 是否正在加载数据
*/ [IS_INITING]: false,
isIniting: false //是否展示刷新书签数据弹窗
[SHOW_REFRESH_TOAST]: false
}; };
const getters = { const getters = {
@ -36,26 +56,20 @@ const getters = {
}; };
const actions = { const actions = {
//登陆后的,从缓存初始化数据 async [noLoginInit] () {
async init(context) {
},
async [loginInit] (context) {
if (context.state.isInit || context.state.isIniting) { if (context.state.isInit || context.state.isIniting) {
return; return;
} }
try { context.commit(IS_INITING, true);
context.commit("isIniting", true); context.commit(TOTAL_TREE_DATA, await localforage.getItem(TOTAL_TREE_DATA));
let realVersion = await HttpUtil.get("/user/version"); context.commit(VERSION, await localforage.getItem(VERSION));
let data = await localforage.getItem(TOTAL_TREE_DATA); await treeDataCheck(context, true);
let version = await localforage.getItem(VERSION); context.commit(IS_INIT, true);
if (!data || realVersion > version) { context.commit(IS_INITING, false);
await context.dispatch("refresh"); timer = setInterval(treeDataCheck, 5 * 60 * 1000);
} else {
context.commit(TOTAL_TREE_DATA, data);
context.commit(VERSION, version);
}
context.commit("isInit", true);
} finally {
context.commit("isIniting", false);
}
}, },
/** /**
* 确保数据加载完毕 * 确保数据加载完毕
@ -64,14 +78,14 @@ const actions = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let timer = setInterval(() => { let timer = setInterval(() => {
try { try {
if (context.state.isInit && context.state.isIniting == false) { if (context.state[IS_INIT] && context.state[IS_INITING] == false) {
clearInterval(timer); clearInterval(timer);
resolve(); resolve();
} }
} catch (err) { } catch (err) {
reject(err); reject(err);
} }
}, 100); }, 50);
}); });
}, },
//刷新缓存数据 //刷新缓存数据
@ -96,6 +110,7 @@ const actions = {
async clear (context) { async clear (context) {
context.commit(TOTAL_TREE_DATA, null); context.commit(TOTAL_TREE_DATA, null);
context.commit(VERSION, null); context.commit(VERSION, null);
context.commit(SHOW_REFRESH_TOAST, false);
context.commit("isInit", false); context.commit("isInit", false);
context.commit("isIniting", false); context.commit("isIniting", false);
await localforage.removeItem(TOTAL_TREE_DATA); await localforage.removeItem(TOTAL_TREE_DATA);
@ -249,22 +264,58 @@ const mutations = {
[TOTAL_TREE_DATA]: (state, totalTreeData) => { [TOTAL_TREE_DATA]: (state, totalTreeData) => {
state.totalTreeData = totalTreeData; state.totalTreeData = totalTreeData;
}, },
isInit(state, isInit) { [IS_INIT] (state, isInit) {
state.isInit = isInit; state.isInit = isInit;
}, },
isIniting(state, isIniting) { [IS_INITING] (state, isIniting) {
state.isIniting = isIniting; state.isIniting = isIniting;
}, },
[VERSION]: (state, version) => { [VERSION]: (state, version) => {
state[VERSION] = version; state[VERSION] = version;
},
[SHOW_REFRESH_TOAST]: (state, val) => {
state[SHOW_REFRESH_TOAST] = val;
} }
}; };
export default {
async function treeDataCheck (context, isFirst) {
if (toastShow) {
return;
}
let realVersion = await HttpUtil.get("/user/version");
if (realVersion !== context.state[VERSION]) {
if (SHOW_REFRESH_TOAST && !isFirst) {
//如果在书签管理页面需要弹窗提示
window.vueInstance.$confirm({
title: "书签数据有更新,是否立即刷新?",
cancelText: "稍后提醒",
closable: false,
keyboard: false,
maskClosable: false,
onOk () {
toastShow = false;
return new Promise(async (resolve) => {
await context.dispatch("treeData/clear");
resolve();
});
},
afterClose () {
toastShow = false;
},
});
toastShow = true;
} else {
await context.dispatch("refresh");
}
}
}
export const store = {
namespaced: true, namespaced: true,
state, state,
getters, getters,
actions, actions,
mutations mutations
}; };

View File

@ -19,7 +19,7 @@ async function request(url, method, params, body, isForm, redirect) {
method, method,
params, params,
headers: { headers: {
"jwt-token": vuex.state.globalConfig.token "jwt-token": window.jwtToken
} }
}; };
//如果是表单类型的请求,添加请求头 //如果是表单类型的请求,添加请求头

View File

@ -12,3 +12,21 @@ export async function getUesrInfo() {
export async function getOnlineUserInfo () { export async function getOnlineUserInfo () {
return null; return null;
} }
/**
* 检查jwt是否有效
*/
export function checkJwtValid (token) {
try {
if (token && token.trim().length > 0) {
//检查token是否还有效
let content = JSON.parse(window.atob(token.split(".")[1]));
if (content.exp > Date.now() / 1000) {
return true;
}
}
} catch (err) {
console.error(err);
}
return false;
}

View File

@ -9,7 +9,9 @@ export default {
components: { components: {
header, header,
}, },
data() {}, data() {
return {};
},
methods: {}, methods: {},
}; };
</script> </script>

View File

@ -92,10 +92,11 @@
import AddBookmark from "@/components/main/things/AddBookmark.vue"; import AddBookmark from "@/components/main/things/AddBookmark.vue";
import Search from "@/components/main/Search.vue"; import Search from "@/components/main/Search.vue";
import HttpUtil from "@/util/HttpUtil.js"; import HttpUtil from "@/util/HttpUtil.js";
import { mapState, mapActions } from "vuex"; import { mapState } from "vuex";
import { downloadFile } from "@/util/FileUtil"; import { downloadFile } from "@/util/FileUtil";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import moment from "moment"; import moment from "moment";
import { TREE_DATA, SHOW_REFRESH_TOAST } from "@/store/modules/treeData";
export default { export default {
name: "BookmarkManage", name: "BookmarkManage",
components: { AddBookmark, Search }, components: { AddBookmark, Search },
@ -130,6 +131,7 @@ export default {
...mapState("globalConfig", ["isPhone"]), ...mapState("globalConfig", ["isPhone"]),
}, },
async mounted() { async mounted() {
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true);
await this.$store.dispatch("treeData/ensureDataOk"); await this.$store.dispatch("treeData/ensureDataOk");
this.treeData = this.totalTreeData[""]; this.treeData = this.totalTreeData[""];
this.loading = false; this.loading = false;
@ -144,7 +146,8 @@ export default {
e.clearSelection(); e.clearSelection();
}); });
}, },
destroyed() { beforeDestroy() {
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false);
if (this.copyBoard != null) { if (this.copyBoard != null) {
this.copyBoard.destroy(); this.copyBoard.destroy();
} }
@ -165,11 +168,21 @@ export default {
resolve(); resolve();
}); });
}, },
/**
* 刷新书签数据
*/
async refresh(deleteCache) { async refresh(deleteCache) {
if (deleteCache) { if (deleteCache) {
this.loading = true; this.loading = true;
await this.$store.dispatch("treeData/refresh"); await this.$store.dispatch("treeData/refresh");
} }
this.resetData();
this.loading = false;
},
/**
* 重置当前data中书签相关数据
*/
resetData() {
this.treeData = this.totalTreeData[""]; this.treeData = this.totalTreeData[""];
this.expandedKeys = []; this.expandedKeys = [];
this.checkedKeys = []; this.checkedKeys = [];
@ -178,6 +191,9 @@ export default {
this.currentSelect = null; this.currentSelect = null;
this.loading = false; this.loading = false;
}, },
/**
* 树节点展开
*/
expand(expandedKeys, { expanded, node }) { expand(expandedKeys, { expanded, node }) {
if (expanded) { if (expanded) {
const item = node.dataRef; const item = node.dataRef;

View File

@ -13,76 +13,9 @@ import Content from "@/layout/manage/Content.vue";
import Bottom from "@/layout/manage/Bottom.vue"; import Bottom from "@/layout/manage/Bottom.vue";
import Top from "@/layout/manage/Top.vue"; import Top from "@/layout/manage/Top.vue";
import httpUtil from "../../util/HttpUtil";
export default { export default {
name: "Home", name: "Home",
components: { Top, Content, Bottom }, components: { Top, Content, Bottom },
data() {
return {
timer: null,
//
count: 0,
//
total: 1,
//
isOpen: false,
};
},
async mounted() {
//
await this.$store.dispatch("globalConfig/init");
console.log("globalConfig加载完毕");
await this.$store.dispatch("treeData/init");
console.log("treeData加载完毕");
console.log("state数据:", this.$store.state);
//
this.timer = setInterval(this.checkVersion, 5 * 60 * 1000);
},
destroyed() {
if (this.timer != null) {
clearInterval(this.timer);
}
},
methods: {
/**
* 检查当前页面版本是否和服务器版本一致
*/
async checkVersion() {
this.count++;
if (this.count < this.total || this.isOpen) {
return;
}
this.count = 0;
let version = await httpUtil.get("/user/version");
const _this = this;
if (this.$store.state.treeData.version < version) {
this.isOpen = true;
this.$confirm({
title: "书签数据有更新,是否立即刷新?",
content: "点击确定将刷新整个页面,请注意!",
cancelText: "稍后提醒",
closable: false,
keyboard: false,
maskClosable: false,
onOk() {
_this.isOpen = false;
return new Promise(async (resolve) => {
await _this.$store.dispatch("treeData/clear");
window.location.reload();
resolve();
});
},
onCancel() {
_this.isOpen = false;
_this.total = 5;
},
afterClose() {
_this.isOpen = false;
},
});
}
},
},
}; };
</script> </script>