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

@ -1,31 +1,31 @@
import Vue from "vue"; import Vue from "vue";
import { import {
Button, Button,
FormModel, FormModel,
Input, Input,
Icon, Icon,
message, message,
Checkbox, Checkbox,
Dropdown, Dropdown,
Menu, Menu,
Tree, Tree,
Tooltip, Tooltip,
Spin, Spin,
notification, notification,
Empty, Empty,
Modal, Modal,
Radio, Radio,
Upload, Upload,
Popconfirm, Popconfirm,
AutoComplete, AutoComplete,
Select Select
} from "ant-design-vue"; } from "ant-design-vue";
import App from "./App.vue"; import App from "./App.vue";
import router from "./router"; import router from "./router";
import store from "./store"; import store from "./store";
const IconFont = Icon.createFromIconfontCN({ 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(Button);
Vue.use(FormModel); Vue.use(FormModel);
@ -52,7 +52,9 @@ Vue.prototype.$confirm = Modal.confirm;
Vue.config.productionTip = false; Vue.config.productionTip = false;
window.vueInstance = new Vue({ window.vueInstance = new Vue({
router, router,
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,270 +1,321 @@
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;
/** /**
* 书签树相关配置 * 书签树相关配置
*/ */
const state = { const state = {
//全部书签数据 //全部书签数据
[TOTAL_TREE_DATA]: {}, [TOTAL_TREE_DATA]: {},
[VERSION]: null, //版本
isInit: false, [VERSION]: null,
/** //是否已经初始化书签数据
* 是否正在加载数据 [IS_INIT]: false,
*/ // 是否正在加载数据
isIniting: false [IS_INITING]: false,
//是否展示刷新书签数据弹窗
[SHOW_REFRESH_TOAST]: false
}; };
const getters = { const getters = {
/** /**
* 通过id获取节点数据 * 通过id获取节点数据
*/ */
getById: state => id => { getById: state => id => {
let arr = Object.values(state[TOTAL_TREE_DATA]); let arr = Object.values(state[TOTAL_TREE_DATA]);
for (let i in arr) { for (let i in arr) {
for (let j in arr[i]) { for (let j in arr[i]) {
if (arr[i][j].bookmarkId === id) { if (arr[i][j].bookmarkId === id) {
return arr[i][j]; return arr[i][j];
} }
} }
} }
return null; return null;
} }
}; };
const actions = { const actions = {
//登陆后的,从缓存初始化数据 async [noLoginInit] () {
async init(context) {
if (context.state.isInit || context.state.isIniting) { },
return; async [loginInit] (context) {
} if (context.state.isInit || context.state.isIniting) {
try { return;
context.commit("isIniting", true); }
let realVersion = await HttpUtil.get("/user/version"); context.commit(IS_INITING, true);
let data = await localforage.getItem(TOTAL_TREE_DATA); context.commit(TOTAL_TREE_DATA, await localforage.getItem(TOTAL_TREE_DATA));
let version = await localforage.getItem(VERSION); context.commit(VERSION, await localforage.getItem(VERSION));
if (!data || realVersion > version) { await treeDataCheck(context, true);
await context.dispatch("refresh"); context.commit(IS_INIT, true);
} else { context.commit(IS_INITING, false);
context.commit(TOTAL_TREE_DATA, data); timer = setInterval(treeDataCheck, 5 * 60 * 1000);
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[IS_INIT] && context.state[IS_INITING] == false) {
*/ clearInterval(timer);
ensureDataOk(context) { resolve();
return new Promise((resolve, reject) => { }
let timer = setInterval(() => { } catch (err) {
try { reject(err);
if (context.state.isInit && context.state.isIniting == false) { }
clearInterval(timer); }, 50);
resolve(); });
} },
} catch (err) { //刷新缓存数据
reject(err); async refresh (context) {
} let treeData = await HttpUtil.get("/bookmark/currentUser");
}, 100); if (!treeData[""]) {
}); treeData[""] = [];
}, }
//刷新缓存数据 Object.values(treeData).forEach(item =>
async refresh(context) { item.forEach(item1 => {
let treeData = await HttpUtil.get("/bookmark/currentUser"); item1.isLeaf = item1.type === 0;
if (!treeData[""]) { item1.class = "treeNodeItem";
treeData[""] = []; item1.scopedSlots = { title: "nodeTitle" };
} })
Object.values(treeData).forEach(item => );
item.forEach(item1 => { let version = await HttpUtil.get("/user/version");
item1.isLeaf = item1.type === 0; await context.dispatch("updateVersion", version);
item1.class = "treeNodeItem"; context.commit(TOTAL_TREE_DATA, treeData);
item1.scopedSlots = { title: "nodeTitle" }; await localforage.setItem(TOTAL_TREE_DATA, treeData);
}) },
); //清除缓存数据
let version = await HttpUtil.get("/user/version"); async clear (context) {
await context.dispatch("updateVersion", version); context.commit(TOTAL_TREE_DATA, null);
context.commit(TOTAL_TREE_DATA, treeData); context.commit(VERSION, null);
await localforage.setItem(TOTAL_TREE_DATA, treeData); context.commit(SHOW_REFRESH_TOAST, false);
}, context.commit("isInit", false);
//清除缓存数据 context.commit("isIniting", false);
async clear(context) { await localforage.removeItem(TOTAL_TREE_DATA);
context.commit(TOTAL_TREE_DATA, null); await localforage.removeItem(VERSION);
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;
*/ //从原来位置中删除当前节点
async moveNode(context, info) { let currentList = data[current.path];
let data = context.state[TOTAL_TREE_DATA]; currentList.splice(
const target = info.node.dataRef; currentList.findIndex(item => item.bookmarkId === current.bookmarkId),
const current = info.dragNode.dataRef; 1
//从原来位置中删除当前节点 );
let currentList = data[current.path]; //请求体
currentList.splice( const body = {
currentList.findIndex(item => item.bookmarkId === current.bookmarkId), bookmarkId: current.bookmarkId,
1 sourcePath: current.path,
); targetPath: "",
//请求体 //-1 表示排在最后
const body = { sort: -1
bookmarkId: current.bookmarkId, };
sourcePath: current.path, if (info.dropToGap) {
targetPath: "", body.targetPath = target.path;
//-1 表示排在最后 //移动到目标节点的上面或者下面
sort: -1 let targetList = data[target.path];
}; //目标节点index
if (info.dropToGap) { let index = targetList.indexOf(target);
body.targetPath = target.path; //移动节点相对于目标节点位置的增量
//移动到目标节点的上面或者下面 let addIndex = info.dropPosition > index ? 1 : 0;
let targetList = data[target.path]; body.sort = target.sort + addIndex;
//目标节点index targetList.splice(index + addIndex, 0, current);
let index = targetList.indexOf(target); for (let i = index + 1; i < targetList.length; i++) {
//移动节点相对于目标节点位置的增量 targetList[i].sort += 1;
let addIndex = info.dropPosition > index ? 1 : 0; }
body.sort = target.sort + addIndex; } else {
targetList.splice(index + addIndex, 0, current); //移动到一个文件夹下面
for (let i = index + 1; i < targetList.length; i++) { body.targetPath = target.path + "." + target.bookmarkId;
targetList[i].sort += 1; let targetList = data[body.targetPath];
} if (!targetList) {
} else { targetList = [];
//移动到一个文件夹下面 data[body.targetPath] = targetList;
body.targetPath = target.path + "." + target.bookmarkId; }
let targetList = data[body.targetPath]; body.sort = targetList.length > 0 ? targetList[targetList.length - 1].sort + 1 : 1;
if (!targetList) { targetList.push(current);
targetList = []; }
data[body.targetPath] = targetList; //更新节点的path和对应子节点path
} current.path = body.targetPath;
body.sort = targetList.length > 0 ? targetList[targetList.length - 1].sort + 1 : 1; current.sort = body.sort;
targetList.push(current); //如果为文件夹还要更新所有子书签的path
} if (body.sourcePath !== body.targetPath) {
//更新节点的path和对应子节点path let keys = Object.keys(data);
current.path = body.targetPath; //旧路径
current.sort = body.sort; let oldPath = body.sourcePath + "." + current.bookmarkId;
//如果为文件夹还要更新所有子书签的path //新路径
if (body.sourcePath !== body.targetPath) { let newPath = body.targetPath + "." + current.bookmarkId;
let keys = Object.keys(data); keys.forEach(item => {
//旧路径 if (!item.startsWith(oldPath)) {
let oldPath = body.sourcePath + "." + current.bookmarkId; return;
//新路径 }
let newPath = body.targetPath + "." + current.bookmarkId; let newPathStr = item.replace(oldPath, newPath);
keys.forEach(item => { let list = data[item];
if (!item.startsWith(oldPath)) { delete data[item];
return; data[newPathStr] = list;
} list.forEach(item1 => (item1.path = newPathStr));
let newPathStr = item.replace(oldPath, newPath); });
let list = data[item]; }
delete data[item]; context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
data[newPathStr] = list; await context.dispatch("updateVersion", null);
list.forEach(item1 => (item1.path = newPathStr)); await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
}); return body;
} },
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 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][""] = [];
*/ }
async addNode(context, { sourceNode, targetNode }) { context.state[TOTAL_TREE_DATA][""].push(targetNode);
if (sourceNode === null) { } else {
if (context.state[TOTAL_TREE_DATA][""] === undefined) { if (sourceNode.children === undefined) {
context.state[TOTAL_TREE_DATA][""] = []; sourceNode.children = [];
} }
context.state[TOTAL_TREE_DATA][""].push(targetNode); sourceNode.children.push(targetNode);
} else { }
if (sourceNode.children === undefined) { if (targetNode.type === 0) {
sourceNode.children = []; context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = [];
} }
sourceNode.children.push(targetNode); targetNode.isLeaf = targetNode.type === 0;
} targetNode.class = "treeNodeItem";
if (targetNode.type === 0) { targetNode.scopedSlots = { title: "nodeTitle" };
context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = []; context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
} await context.dispatch("updateVersion", null);
targetNode.isLeaf = targetNode.type === 0; await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
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));
*/ //删除子节点
async deleteData(context, { pathList, bookmarkIdList }) { pathList.forEach(item => {
//待删除的书签 delete state[TOTAL_TREE_DATA][item];
let bookmarkIdSet = new Set(); Object.keys(context.state[TOTAL_TREE_DATA])
bookmarkIdList.forEach(item => bookmarkIdSet.add(item)); .filter(key => key.startsWith(item + "."))
//删除子节点 .forEach(key => delete state[TOTAL_TREE_DATA][key]);
pathList.forEach(item => { bookmarkIdSet.add(parseInt(item.split(".").reverse()));
delete state[TOTAL_TREE_DATA][item]; });
Object.keys(context.state[TOTAL_TREE_DATA]) //删除直接选中的节点
.filter(key => key.startsWith(item + ".")) Object.keys(context.state[TOTAL_TREE_DATA]).forEach(item => {
.forEach(key => delete state[TOTAL_TREE_DATA][key]); let list = context.state[TOTAL_TREE_DATA][item];
bookmarkIdSet.add(parseInt(item.split(".").reverse())); for (let i = list.length - 1; i >= 0; i--) {
}); if (bookmarkIdSet.has(list[i].bookmarkId)) {
//删除直接选中的节点 list.splice(i, 1);
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)) { context.commit(TOTAL_TREE_DATA, context.state[TOTAL_TREE_DATA]);
list.splice(i, 1); await context.dispatch("updateVersion", null);
} await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
} },
}); /**
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]);
async editNode({ dispatch, state, commit }, { node, newName, newUrl, newIcon }) { await dispatch("updateVersion", null);
node.name = newName; await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
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 = { 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 {
namespaced: true, async function treeDataCheck (context, isFirst) {
state, if (toastShow) {
getters, return;
actions, }
mutations 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 接口返回未认证是否跳转到登陆 * @param {*} redirect 接口返回未认证是否跳转到登陆
* @returns 数据 * @returns 数据
*/ */
async function request(url, method, params, body, isForm, redirect) { async function request (url, method, params, body, isForm, redirect) {
let options = { let options = {
url, url,
baseURL: "/bookmark/api", baseURL: "/bookmark/api",
method, method,
params, params,
headers: { headers: {
"jwt-token": vuex.state.globalConfig.token "jwt-token": window.jwtToken
} }
}; };
//如果是表单类型的请求,添加请求头 //如果是表单类型的请求,添加请求头
if (isForm) { if (isForm) {
options.headers["Content-Type"] = "multipart/form-data"; options.headers["Content-Type"] = "multipart/form-data";
} }
if (body) { if (body) {
options.data = body; options.data = body;
} }
let res; let res;
try { try {
res = await http.default.request(options); res = await http.default.request(options);
} catch (err) { } catch (err) {
window.vueInstance.$message.error("发生了某些异常问题"); window.vueInstance.$message.error("发生了某些异常问题");
console.error(err); console.error(err);
return; return;
} }
const { code, data, message } = res.data; const { code, data, message } = res.data;
if (code === 1) { if (code === 1) {
return data; return data;
} else if (code === -1 && redirect) { } else if (code === -1 && redirect) {
//未登陆根据redirect参数判断是否需要跳转到登陆页 //未登陆根据redirect参数判断是否需要跳转到登陆页
window.vueInstance.$message.error("您尚未登陆,请先登陆"); window.vueInstance.$message.error("您尚未登陆,请先登陆");
router.replace(`/public/login?redirect=${encodeURIComponent(router.currentRoute.fullPath)}`); router.replace(`/public/login?redirect=${encodeURIComponent(router.currentRoute.fullPath)}`);
throw new Error(message); throw new Error(message);
} else if (code === 0) { } else if (code === 0) {
//通用异常使用error提示 //通用异常使用error提示
window.vueInstance.$notification.error({ window.vueInstance.$notification.error({
message: "异常", message: "异常",
description: message description: message
}); });
throw new Error(message); throw new Error(message);
} else if (code === -2) { } else if (code === -2) {
//表单异常使用message提示 //表单异常使用message提示
window.vueInstance.$message.error(message); window.vueInstance.$message.error(message);
throw new Error(message); throw new Error(message);
} }
} }
/** /**
@ -65,8 +65,8 @@ async function request(url, method, params, body, isForm, redirect) {
* @param {*} params url参数 * @param {*} params url参数
* @param {*} redirect 未登陆是否跳转到登陆页 * @param {*} redirect 未登陆是否跳转到登陆页
*/ */
async function get(url, params = null, redirect = true) { async function get (url, params = null, redirect = true) {
return request(url, "get", params, null, false, redirect); return request(url, "get", params, null, false, redirect);
} }
/** /**
@ -77,8 +77,8 @@ async function get(url, params = null, redirect = true) {
* @param {*} isForm 是否表单数据 * @param {*} isForm 是否表单数据
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function post(url, params, body, isForm = false, redirect = true) { async function post (url, params, body, isForm = false, redirect = true) {
return request(url, "post", params, body, isForm, redirect); 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 {*} isForm 是否表单数据
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function put(url, params, body, isForm = false, redirect = true) { async function put (url, params, body, isForm = false, redirect = true) {
return request(url, "put", params, body, isForm, redirect); 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 {*} params url参数
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function deletes(url, params = null, redirect = true) { async function deletes (url, params = null, redirect = true) {
return request(url, "delete", params, null, redirect); return request(url, "delete", params, null, redirect);
} }
export default { export default {
get, get,
post, post,
put, put,
delete: deletes delete: deletes
}; };

View File

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