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"
},
"dependencies": {
"ant-design-vue": "^1.6.3",
"ant-design-vue": "^1.7.8",
"axios": "^0.21.1",
"babel-plugin-import": "^1.13.0",
"clipboard": "^2.0.6",

View File

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

View File

@ -1,31 +1,31 @@
import Vue from "vue";
import {
Button,
FormModel,
Input,
Icon,
message,
Checkbox,
Dropdown,
Menu,
Tree,
Tooltip,
Spin,
notification,
Empty,
Modal,
Radio,
Upload,
Popconfirm,
AutoComplete,
Select
Button,
FormModel,
Input,
Icon,
message,
Checkbox,
Dropdown,
Menu,
Tree,
Tooltip,
Spin,
notification,
Empty,
Modal,
Radio,
Upload,
Popconfirm,
AutoComplete,
Select
} from "ant-design-vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
const IconFont = Icon.createFromIconfontCN({
scriptUrl: "//at.alicdn.com/t/font_1261825_3wf60i93sdm.js"
scriptUrl: "//at.alicdn.com/t/font_1261825_3wf60i93sdm.js"
});
Vue.use(Button);
Vue.use(FormModel);
@ -52,7 +52,9 @@ Vue.prototype.$confirm = Modal.confirm;
Vue.config.productionTip = false;
window.vueInstance = new Vue({
router,
store,
render: h => h(App)
router,
store,
render: h => h(App)
}).$mount("#app");

View File

@ -1,7 +1,8 @@
import Vue from "vue";
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 { checkJwtValid } from "@/util/UserUtil";
Vue.use(VueRouter);
@ -11,7 +12,7 @@ const routes = [
path: "/manage",
component: () => import("@/views/manage/index"),
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") },
]
},
@ -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");
vuex.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
if (!supportNoLogin && !checkJwtValid()) {
vuex.default.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
if (!supportNoLogin && !checkJwtValid(vuex.default.state[GLOBAL_CONFIG][TOKEN])) {
//如不支持未登录进入切jwt已过期直接跳转到登录页面
next({
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;

View File

@ -1,16 +1,55 @@
import Vue from "vue";
import Vuex from "vuex";
import globalConfig from "./modules/globalConfig";
import treeData from "./modules/treeData";
import * as globalConfig from "./modules/globalConfig";
import * as treeData from "./modules/treeData";
import { checkJwtValid } from "@/util/UserUtil";
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
globalConfig,
treeData
}
let store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
[globalConfig.GLOBAL_CONFIG]: globalConfig.store,
[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 HttpUtil from "../../util/HttpUtil";
export const GLOBAL_CONFIG = "globalConfig";
export const USER_INFO = "userInfo";
export const TOKEN = "token";
export const SERVER_CONFIG = "serverConfig";
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说明未获取登录凭证
*/
@ -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 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) {
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");
await localforage.setItem(USER_INFO, userInfo);
commit(USER_INFO, userInfo);
context.commit(USER_INFO, userInfo);
context.commit(IS_INIT, true);
},
async setToken ({ commit }, token) {
await localforage.setItem(TOKEN, token);
window.jwtToken = token;
commit(TOKEN, token);
},
//登出清除数据
async clear (context) {
await localforage.removeItem("userInfo");
await localforage.removeItem("token");
await localforage.removeItem(TOKEN);
context.commit(USER_INFO, 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 = {
userInfo (state, userInfo) {
state.userInfo = userInfo;
[USER_INFO] (state, userInfo) {
state[USER_INFO] = userInfo;
},
token (state, token) {
state.token = token;
[TOKEN] (state, token) {
state[TOKEN] = token;
},
isInit (state, isInit) {
state.isInit = isInit;
},
isPhone (state, status) {
state.isPhone = status;
[IS_INIT] (state, isInit) {
state[IS_INIT] = isInit;
},
[SERVER_CONFIG] (state, serverConfig) {
state[SERVER_CONFIG] = serverConfig;
@ -106,7 +94,7 @@ const mutations = {
};
export default {
export const store = {
namespaced: true,
state,
getters,

View File

@ -1,270 +1,321 @@
import localforage from "localforage";
import { } from "ant-design-vue";
import HttpUtil from "../../util/HttpUtil";
const TOTAL_TREE_DATA = "totalTreeData";
const VERSION = "version";
export const TREE_DATA = "treeData";
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;
/**
* 书签树相关配置
*/
const state = {
//全部书签数据
[TOTAL_TREE_DATA]: {},
[VERSION]: null,
isInit: false,
/**
* 是否正在加载数据
*/
isIniting: false
//全部书签数据
[TOTAL_TREE_DATA]: {},
//版本
[VERSION]: null,
//是否已经初始化书签数据
[IS_INIT]: false,
// 是否正在加载数据
[IS_INITING]: false,
//是否展示刷新书签数据弹窗
[SHOW_REFRESH_TOAST]: false
};
const getters = {
/**
* 通过id获取节点数据
*/
getById: state => id => {
let arr = Object.values(state[TOTAL_TREE_DATA]);
for (let i in arr) {
for (let j in arr[i]) {
if (arr[i][j].bookmarkId === id) {
return arr[i][j];
}
}
}
return null;
}
/**
* 通过id获取节点数据
*/
getById: state => id => {
let arr = Object.values(state[TOTAL_TREE_DATA]);
for (let i in arr) {
for (let j in arr[i]) {
if (arr[i][j].bookmarkId === id) {
return arr[i][j];
}
}
}
return null;
}
};
const actions = {
//登陆后的,从缓存初始化数据
async init(context) {
if (context.state.isInit || context.state.isIniting) {
return;
}
try {
context.commit("isIniting", true);
let realVersion = await HttpUtil.get("/user/version");
let data = await localforage.getItem(TOTAL_TREE_DATA);
let version = await localforage.getItem(VERSION);
if (!data || realVersion > version) {
await context.dispatch("refresh");
} else {
context.commit(TOTAL_TREE_DATA, data);
context.commit(VERSION, version);
}
context.commit("isInit", true);
} finally {
context.commit("isIniting", false);
}
},
/**
* 确保数据加载完毕
*/
ensureDataOk(context) {
return new Promise((resolve, reject) => {
let timer = setInterval(() => {
try {
if (context.state.isInit && context.state.isIniting == false) {
clearInterval(timer);
resolve();
}
} catch (err) {
reject(err);
}
}, 100);
});
},
//刷新缓存数据
async refresh(context) {
let treeData = await HttpUtil.get("/bookmark/currentUser");
if (!treeData[""]) {
treeData[""] = [];
}
Object.values(treeData).forEach(item =>
item.forEach(item1 => {
item1.isLeaf = item1.type === 0;
item1.class = "treeNodeItem";
item1.scopedSlots = { title: "nodeTitle" };
})
);
let version = await HttpUtil.get("/user/version");
await context.dispatch("updateVersion", version);
context.commit(TOTAL_TREE_DATA, treeData);
await localforage.setItem(TOTAL_TREE_DATA, treeData);
},
//清除缓存数据
async clear(context) {
context.commit(TOTAL_TREE_DATA, null);
context.commit(VERSION, null);
context.commit("isInit", false);
context.commit("isIniting", false);
await localforage.removeItem(TOTAL_TREE_DATA);
await localforage.removeItem(VERSION);
},
/**
* 移动节点
*/
async moveNode(context, info) {
let data = context.state[TOTAL_TREE_DATA];
const target = info.node.dataRef;
const current = info.dragNode.dataRef;
//从原来位置中删除当前节点
let currentList = data[current.path];
currentList.splice(
currentList.findIndex(item => item.bookmarkId === current.bookmarkId),
1
);
//请求体
const body = {
bookmarkId: current.bookmarkId,
sourcePath: current.path,
targetPath: "",
//-1 表示排在最后
sort: -1
};
if (info.dropToGap) {
body.targetPath = target.path;
//移动到目标节点的上面或者下面
let targetList = data[target.path];
//目标节点index
let index = targetList.indexOf(target);
//移动节点相对于目标节点位置的增量
let addIndex = info.dropPosition > index ? 1 : 0;
body.sort = target.sort + addIndex;
targetList.splice(index + addIndex, 0, current);
for (let i = index + 1; i < targetList.length; i++) {
targetList[i].sort += 1;
}
} else {
//移动到一个文件夹下面
body.targetPath = target.path + "." + target.bookmarkId;
let targetList = data[body.targetPath];
if (!targetList) {
targetList = [];
data[body.targetPath] = targetList;
}
body.sort = targetList.length > 0 ? targetList[targetList.length - 1].sort + 1 : 1;
targetList.push(current);
}
//更新节点的path和对应子节点path
current.path = body.targetPath;
current.sort = body.sort;
//如果为文件夹还要更新所有子书签的path
if (body.sourcePath !== body.targetPath) {
let keys = Object.keys(data);
//旧路径
let oldPath = body.sourcePath + "." + current.bookmarkId;
//新路径
let newPath = body.targetPath + "." + current.bookmarkId;
keys.forEach(item => {
if (!item.startsWith(oldPath)) {
return;
}
let newPathStr = item.replace(oldPath, newPath);
let list = data[item];
delete data[item];
data[newPathStr] = list;
list.forEach(item1 => (item1.path = newPathStr));
});
}
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
return body;
},
/**
* 更新版本数据
*/
async updateVersion({ commit, state }, version) {
commit(VERSION, version == null ? state[VERSION] + 1 : version);
await localforage.setItem(VERSION, state[VERSION]);
},
/**
* 新增书签文件夹
*/
async addNode(context, { sourceNode, targetNode }) {
if (sourceNode === null) {
if (context.state[TOTAL_TREE_DATA][""] === undefined) {
context.state[TOTAL_TREE_DATA][""] = [];
}
context.state[TOTAL_TREE_DATA][""].push(targetNode);
} else {
if (sourceNode.children === undefined) {
sourceNode.children = [];
}
sourceNode.children.push(targetNode);
}
if (targetNode.type === 0) {
context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = [];
}
targetNode.isLeaf = targetNode.type === 0;
targetNode.class = "treeNodeItem";
targetNode.scopedSlots = { title: "nodeTitle" };
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
},
/**
* 删除节点数据
*/
async deleteData(context, { pathList, bookmarkIdList }) {
//待删除的书签
let bookmarkIdSet = new Set();
bookmarkIdList.forEach(item => bookmarkIdSet.add(item));
//删除子节点
pathList.forEach(item => {
delete state[TOTAL_TREE_DATA][item];
Object.keys(context.state[TOTAL_TREE_DATA])
.filter(key => key.startsWith(item + "."))
.forEach(key => delete state[TOTAL_TREE_DATA][key]);
bookmarkIdSet.add(parseInt(item.split(".").reverse()));
});
//删除直接选中的节点
Object.keys(context.state[TOTAL_TREE_DATA]).forEach(item => {
let list = context.state[TOTAL_TREE_DATA][item];
for (let i = list.length - 1; i >= 0; i--) {
if (bookmarkIdSet.has(list[i].bookmarkId)) {
list.splice(i, 1);
}
}
});
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
},
/**
* 编辑书签节点
*/
async editNode({ dispatch, state, commit }, { node, newName, newUrl, newIcon }) {
node.name = newName;
node.url = newUrl;
node.icon = newIcon;
commit(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
await dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
}
async [noLoginInit] () {
},
async [loginInit] (context) {
if (context.state.isInit || context.state.isIniting) {
return;
}
context.commit(IS_INITING, true);
context.commit(TOTAL_TREE_DATA, await localforage.getItem(TOTAL_TREE_DATA));
context.commit(VERSION, await localforage.getItem(VERSION));
await treeDataCheck(context, true);
context.commit(IS_INIT, true);
context.commit(IS_INITING, false);
timer = setInterval(treeDataCheck, 5 * 60 * 1000);
},
/**
* 确保数据加载完毕
*/
ensureDataOk (context) {
return new Promise((resolve, reject) => {
let timer = setInterval(() => {
try {
if (context.state[IS_INIT] && context.state[IS_INITING] == false) {
clearInterval(timer);
resolve();
}
} catch (err) {
reject(err);
}
}, 50);
});
},
//刷新缓存数据
async refresh (context) {
let treeData = await HttpUtil.get("/bookmark/currentUser");
if (!treeData[""]) {
treeData[""] = [];
}
Object.values(treeData).forEach(item =>
item.forEach(item1 => {
item1.isLeaf = item1.type === 0;
item1.class = "treeNodeItem";
item1.scopedSlots = { title: "nodeTitle" };
})
);
let version = await HttpUtil.get("/user/version");
await context.dispatch("updateVersion", version);
context.commit(TOTAL_TREE_DATA, treeData);
await localforage.setItem(TOTAL_TREE_DATA, treeData);
},
//清除缓存数据
async clear (context) {
context.commit(TOTAL_TREE_DATA, null);
context.commit(VERSION, null);
context.commit(SHOW_REFRESH_TOAST, false);
context.commit("isInit", false);
context.commit("isIniting", false);
await localforage.removeItem(TOTAL_TREE_DATA);
await localforage.removeItem(VERSION);
},
/**
* 移动节点
*/
async moveNode (context, info) {
let data = context.state[TOTAL_TREE_DATA];
const target = info.node.dataRef;
const current = info.dragNode.dataRef;
//从原来位置中删除当前节点
let currentList = data[current.path];
currentList.splice(
currentList.findIndex(item => item.bookmarkId === current.bookmarkId),
1
);
//请求体
const body = {
bookmarkId: current.bookmarkId,
sourcePath: current.path,
targetPath: "",
//-1 表示排在最后
sort: -1
};
if (info.dropToGap) {
body.targetPath = target.path;
//移动到目标节点的上面或者下面
let targetList = data[target.path];
//目标节点index
let index = targetList.indexOf(target);
//移动节点相对于目标节点位置的增量
let addIndex = info.dropPosition > index ? 1 : 0;
body.sort = target.sort + addIndex;
targetList.splice(index + addIndex, 0, current);
for (let i = index + 1; i < targetList.length; i++) {
targetList[i].sort += 1;
}
} else {
//移动到一个文件夹下面
body.targetPath = target.path + "." + target.bookmarkId;
let targetList = data[body.targetPath];
if (!targetList) {
targetList = [];
data[body.targetPath] = targetList;
}
body.sort = targetList.length > 0 ? targetList[targetList.length - 1].sort + 1 : 1;
targetList.push(current);
}
//更新节点的path和对应子节点path
current.path = body.targetPath;
current.sort = body.sort;
//如果为文件夹还要更新所有子书签的path
if (body.sourcePath !== body.targetPath) {
let keys = Object.keys(data);
//旧路径
let oldPath = body.sourcePath + "." + current.bookmarkId;
//新路径
let newPath = body.targetPath + "." + current.bookmarkId;
keys.forEach(item => {
if (!item.startsWith(oldPath)) {
return;
}
let newPathStr = item.replace(oldPath, newPath);
let list = data[item];
delete data[item];
data[newPathStr] = list;
list.forEach(item1 => (item1.path = newPathStr));
});
}
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
return body;
},
/**
* 更新版本数据
*/
async updateVersion ({ commit, state }, version) {
commit(VERSION, version == null ? state[VERSION] + 1 : version);
await localforage.setItem(VERSION, state[VERSION]);
},
/**
* 新增书签文件夹
*/
async addNode (context, { sourceNode, targetNode }) {
if (sourceNode === null) {
if (context.state[TOTAL_TREE_DATA][""] === undefined) {
context.state[TOTAL_TREE_DATA][""] = [];
}
context.state[TOTAL_TREE_DATA][""].push(targetNode);
} else {
if (sourceNode.children === undefined) {
sourceNode.children = [];
}
sourceNode.children.push(targetNode);
}
if (targetNode.type === 0) {
context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = [];
}
targetNode.isLeaf = targetNode.type === 0;
targetNode.class = "treeNodeItem";
targetNode.scopedSlots = { title: "nodeTitle" };
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
},
/**
* 删除节点数据
*/
async deleteData (context, { pathList, bookmarkIdList }) {
//待删除的书签
let bookmarkIdSet = new Set();
bookmarkIdList.forEach(item => bookmarkIdSet.add(item));
//删除子节点
pathList.forEach(item => {
delete state[TOTAL_TREE_DATA][item];
Object.keys(context.state[TOTAL_TREE_DATA])
.filter(key => key.startsWith(item + "."))
.forEach(key => delete state[TOTAL_TREE_DATA][key]);
bookmarkIdSet.add(parseInt(item.split(".").reverse()));
});
//删除直接选中的节点
Object.keys(context.state[TOTAL_TREE_DATA]).forEach(item => {
let list = context.state[TOTAL_TREE_DATA][item];
for (let i = list.length - 1; i >= 0; i--) {
if (bookmarkIdSet.has(list[i].bookmarkId)) {
list.splice(i, 1);
}
}
});
context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
await context.dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
},
/**
* 编辑书签节点
*/
async editNode ({ dispatch, state, commit }, { node, newName, newUrl, newIcon }) {
node.name = newName;
node.url = newUrl;
node.icon = newIcon;
commit(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
await dispatch("updateVersion", null);
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
}
};
const mutations = {
[TOTAL_TREE_DATA]: (state, totalTreeData) => {
state.totalTreeData = totalTreeData;
},
isInit(state, isInit) {
state.isInit = isInit;
},
isIniting(state, isIniting) {
state.isIniting = isIniting;
},
[VERSION]: (state, version) => {
state[VERSION] = version;
}
[TOTAL_TREE_DATA]: (state, totalTreeData) => {
state.totalTreeData = totalTreeData;
},
[IS_INIT] (state, isInit) {
state.isInit = isInit;
},
[IS_INITING] (state, isIniting) {
state.isIniting = isIniting;
},
[VERSION]: (state, version) => {
state[VERSION] = version;
},
[SHOW_REFRESH_TOAST]: (state, val) => {
state[SHOW_REFRESH_TOAST] = val;
}
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
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,
state,
getters,
actions,
mutations
};

View File

@ -12,51 +12,51 @@ import router from "../router/index";
* @param {*} redirect 接口返回未认证是否跳转到登陆
* @returns 数据
*/
async function request(url, method, params, body, isForm, redirect) {
let options = {
url,
baseURL: "/bookmark/api",
method,
params,
headers: {
"jwt-token": vuex.state.globalConfig.token
}
};
//如果是表单类型的请求,添加请求头
if (isForm) {
options.headers["Content-Type"] = "multipart/form-data";
}
if (body) {
options.data = body;
}
let res;
try {
res = await http.default.request(options);
} catch (err) {
window.vueInstance.$message.error("发生了某些异常问题");
console.error(err);
return;
}
const { code, data, message } = res.data;
if (code === 1) {
return data;
} else if (code === -1 && redirect) {
//未登陆根据redirect参数判断是否需要跳转到登陆页
window.vueInstance.$message.error("您尚未登陆,请先登陆");
router.replace(`/public/login?redirect=${encodeURIComponent(router.currentRoute.fullPath)}`);
throw new Error(message);
} else if (code === 0) {
//通用异常使用error提示
window.vueInstance.$notification.error({
message: "异常",
description: message
});
throw new Error(message);
} else if (code === -2) {
//表单异常使用message提示
window.vueInstance.$message.error(message);
throw new Error(message);
}
async function request (url, method, params, body, isForm, redirect) {
let options = {
url,
baseURL: "/bookmark/api",
method,
params,
headers: {
"jwt-token": window.jwtToken
}
};
//如果是表单类型的请求,添加请求头
if (isForm) {
options.headers["Content-Type"] = "multipart/form-data";
}
if (body) {
options.data = body;
}
let res;
try {
res = await http.default.request(options);
} catch (err) {
window.vueInstance.$message.error("发生了某些异常问题");
console.error(err);
return;
}
const { code, data, message } = res.data;
if (code === 1) {
return data;
} else if (code === -1 && redirect) {
//未登陆根据redirect参数判断是否需要跳转到登陆页
window.vueInstance.$message.error("您尚未登陆,请先登陆");
router.replace(`/public/login?redirect=${encodeURIComponent(router.currentRoute.fullPath)}`);
throw new Error(message);
} else if (code === 0) {
//通用异常使用error提示
window.vueInstance.$notification.error({
message: "异常",
description: message
});
throw new Error(message);
} else if (code === -2) {
//表单异常使用message提示
window.vueInstance.$message.error(message);
throw new Error(message);
}
}
/**
@ -65,8 +65,8 @@ async function request(url, method, params, body, isForm, redirect) {
* @param {*} params url参数
* @param {*} redirect 未登陆是否跳转到登陆页
*/
async function get(url, params = null, redirect = true) {
return request(url, "get", params, null, false, redirect);
async function get (url, params = null, redirect = true) {
return request(url, "get", params, null, false, redirect);
}
/**
@ -77,8 +77,8 @@ async function get(url, params = null, redirect = true) {
* @param {*} isForm 是否表单数据
* @param {*} redirect 是否重定向
*/
async function post(url, params, body, isForm = false, redirect = true) {
return request(url, "post", params, body, isForm, redirect);
async function post (url, params, body, isForm = false, redirect = true) {
return request(url, "post", params, body, isForm, redirect);
}
/**
@ -89,8 +89,8 @@ async function post(url, params, body, isForm = false, redirect = true) {
* @param {*} isForm 是否表单数据
* @param {*} redirect 是否重定向
*/
async function put(url, params, body, isForm = false, redirect = true) {
return request(url, "put", params, body, isForm, redirect);
async function put (url, params, body, isForm = false, redirect = true) {
return request(url, "put", params, body, isForm, redirect);
}
/**
@ -99,13 +99,13 @@ async function put(url, params, body, isForm = false, redirect = true) {
* @param {*} params url参数
* @param {*} redirect 是否重定向
*/
async function deletes(url, params = null, redirect = true) {
return request(url, "delete", params, null, redirect);
async function deletes (url, params = null, redirect = true) {
return request(url, "delete", params, null, redirect);
}
export default {
get,
post,
put,
delete: deletes
get,
post,
put,
delete: deletes
};

View File

@ -2,13 +2,31 @@ const TOKEN = "token";
/**
* 本地获取用户信息
*/
export async function getUesrInfo() {
return window.vueInstance.$store.state.globalConfig.userInfo;
export async function getUesrInfo () {
return window.vueInstance.$store.state.globalConfig.userInfo;
}
/**
* 从服务器获取用户信息
*/
export async function getOnlineUserInfo() {
return null;
export async function getOnlineUserInfo () {
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: {
header,
},
data() {},
data() {
return {};
},
methods: {},
};
</script>

View File

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