From 40290e6ac875c2291bce6dc774ac56b42fafbd76 Mon Sep 17 00:00:00 2001 From: fanxb Date: Mon, 21 Mar 2022 17:06:52 +0800 Subject: [PATCH] temp --- bookmark_front/package.json | 2 +- bookmark_front/src/App.vue | 4 - bookmark_front/src/main.js | 48 +- bookmark_front/src/router/index.js | 34 +- bookmark_front/src/store/index.js | 59 +- .../src/store/modules/globalConfig.js | 70 +-- bookmark_front/src/store/modules/treeData.js | 551 ++++++++++-------- bookmark_front/src/util/HttpUtil.js | 114 ++-- bookmark_front/src/util/UserUtil.js | 26 +- bookmark_front/src/views/home/index.vue | 4 +- .../src/views/manage/bookmarkTree/index.vue | 20 +- bookmark_front/src/views/manage/index.vue | 67 --- 12 files changed, 516 insertions(+), 483 deletions(-) diff --git a/bookmark_front/package.json b/bookmark_front/package.json index 6c9da68..a037b24 100644 --- a/bookmark_front/package.json +++ b/bookmark_front/package.json @@ -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", diff --git a/bookmark_front/src/App.vue b/bookmark_front/src/App.vue index d1ff0f1..ddbbc49 100644 --- a/bookmark_front/src/App.vue +++ b/bookmark_front/src/App.vue @@ -7,10 +7,6 @@ diff --git a/bookmark_front/src/main.js b/bookmark_front/src/main.js index 834f196..e6cdad7 100644 --- a/bookmark_front/src/main.js +++ b/bookmark_front/src/main.js @@ -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"); + + diff --git a/bookmark_front/src/router/index.js b/bookmark_front/src/router/index.js index 62d089f..e277616 100644 --- a/bookmark_front/src/router/index.js +++ b/bookmark_front/src/router/index.js @@ -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; diff --git a/bookmark_front/src/store/index.js b/bookmark_front/src/store/index.js index c8a51b2..fcf0a23 100644 --- a/bookmark_front/src/store/index.js +++ b/bookmark_front/src/store/index.js @@ -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; + diff --git a/bookmark_front/src/store/modules/globalConfig.js b/bookmark_front/src/store/modules/globalConfig.js index 80f7df9..7a5ca07 100644 --- a/bookmark_front/src/store/modules/globalConfig.js +++ b/bookmark_front/src/store/modules/globalConfig.js @@ -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, diff --git a/bookmark_front/src/store/modules/treeData.js b/bookmark_front/src/store/modules/treeData.js index 8534108..14f7289 100644 --- a/bookmark_front/src/store/modules/treeData.js +++ b/bookmark_front/src/store/modules/treeData.js @@ -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 }; + diff --git a/bookmark_front/src/util/HttpUtil.js b/bookmark_front/src/util/HttpUtil.js index 72d3a90..3bcf934 100644 --- a/bookmark_front/src/util/HttpUtil.js +++ b/bookmark_front/src/util/HttpUtil.js @@ -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 }; diff --git a/bookmark_front/src/util/UserUtil.js b/bookmark_front/src/util/UserUtil.js index 1b9d208..61fb7bf 100644 --- a/bookmark_front/src/util/UserUtil.js +++ b/bookmark_front/src/util/UserUtil.js @@ -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; +} \ No newline at end of file diff --git a/bookmark_front/src/views/home/index.vue b/bookmark_front/src/views/home/index.vue index b77844a..fecf8d6 100644 --- a/bookmark_front/src/views/home/index.vue +++ b/bookmark_front/src/views/home/index.vue @@ -9,7 +9,9 @@ export default { components: { header, }, - data() {}, + data() { + return {}; + }, methods: {}, }; diff --git a/bookmark_front/src/views/manage/bookmarkTree/index.vue b/bookmark_front/src/views/manage/bookmarkTree/index.vue index 66a6f74..a6e9eba 100644 --- a/bookmark_front/src/views/manage/bookmarkTree/index.vue +++ b/bookmark_front/src/views/manage/bookmarkTree/index.vue @@ -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; diff --git a/bookmark_front/src/views/manage/index.vue b/bookmark_front/src/views/manage/index.vue index 43037b3..818b480 100644 --- a/bookmark_front/src/views/manage/index.vue +++ b/bookmark_front/src/views/manage/index.vue @@ -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; - }, - }); - } - }, - }, };