feat:网页段缓存书签数据完成
This commit is contained in:
parent
d95441d860
commit
3c93016bd3
@ -106,7 +106,7 @@ public class UserController {
|
|||||||
/**
|
/**
|
||||||
* 功能描述: 校验密码,生成一个actionId
|
* 功能描述: 校验密码,生成一个actionId
|
||||||
*
|
*
|
||||||
* @param password password
|
* @param obj obj
|
||||||
* @return com.fanxb.bookmark.common.entity.Result
|
* @return com.fanxb.bookmark.common.entity.Result
|
||||||
* @author fanxb
|
* @author fanxb
|
||||||
* @date 2019/11/11 23:31
|
* @date 2019/11/11 23:31
|
||||||
|
@ -22,4 +22,8 @@ public class User {
|
|||||||
private String password;
|
private String password;
|
||||||
private long createTime;
|
private long createTime;
|
||||||
private long lastLoginTime;
|
private long lastLoginTime;
|
||||||
|
/**
|
||||||
|
* 上次更新书签时间
|
||||||
|
*/
|
||||||
|
private long bookmarkChangeTime;
|
||||||
}
|
}
|
||||||
|
@ -34,5 +34,6 @@
|
|||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
|
<script src="/js/localforage.main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
7
front/public/js/localforage.main.js
Normal file
7
front/public/js/localforage.main.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,10 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link, withRouter } from "react-router-dom";
|
import { Link, withRouter } from "react-router-dom";
|
||||||
import { Menu, Dropdown, Divider } from "antd";
|
import { Menu, Dropdown, Divider, Modal } from "antd";
|
||||||
import httpUtil from "../../util/httpUtil";
|
import httpUtil from "../../util/httpUtil";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import styles from "./index.module.less";
|
import styles from "./index.module.less";
|
||||||
import * as infoAction from "../../redux/action/LoginInfoAction";
|
import * as infoAction from "../../redux/action/LoginInfoAction";
|
||||||
|
import { checkCacheStatus, clearCache } from "../../util/cacheUtil";
|
||||||
|
|
||||||
|
const { confirm } = Modal;
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return state[infoAction.DATA_NAME];
|
return state[infoAction.DATA_NAME];
|
||||||
@ -20,13 +23,44 @@ function mapDispatchToProps(dispatch) {
|
|||||||
class MainLayout extends React.Component {
|
class MainLayout extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {
|
||||||
|
timer: null,
|
||||||
|
//刷新数据弹窗是否展示中
|
||||||
|
showDialog: false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentWillMount() {
|
async componentWillMount() {
|
||||||
if (!this.props.username) {
|
if (!this.props.username) {
|
||||||
let res = await httpUtil.get("/user/currentUserInfo");
|
let res = await httpUtil.get("/user/currentUserInfo");
|
||||||
this.props.changeUserInfo(res);
|
this.props.changeUserInfo(res);
|
||||||
|
if (this.state.timer != null) {
|
||||||
|
clearInterval(this.state.timer);
|
||||||
|
}
|
||||||
|
await this.checkCache();
|
||||||
|
this.state.timer = setInterval(this.checkCache.bind(this), 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkCache() {
|
||||||
|
//检查缓存情况
|
||||||
|
if (this.state.showDialog) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _this = this;
|
||||||
|
if (!(await checkCacheStatus())) {
|
||||||
|
this.state.showDialog = true;
|
||||||
|
confirm({
|
||||||
|
title: "缓存过期",
|
||||||
|
content: "书签数据有更新,是否立即刷新?",
|
||||||
|
onOk() {
|
||||||
|
_this.state.showDialog = false;
|
||||||
|
clearCache();
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
_this.state.showDialog = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +74,11 @@ class MainLayout extends React.Component {
|
|||||||
);
|
);
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
return (
|
return (
|
||||||
<Dropdown overlay={menu} placement="bottomCenter" trigger={["hover", "click"]}>
|
<Dropdown
|
||||||
|
overlay={menu}
|
||||||
|
placement="bottomCenter"
|
||||||
|
trigger={["hover", "click"]}
|
||||||
|
>
|
||||||
<span style={{ cursor: "pointer" }}>
|
<span style={{ cursor: "pointer" }}>
|
||||||
<img className={styles.icon} src={icon} alt="icon" />
|
<img className={styles.icon} src={icon} alt="icon" />
|
||||||
{username}
|
{username}
|
||||||
@ -77,20 +115,34 @@ class MainLayout extends React.Component {
|
|||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<img style={{ width: "1.5rem" }} src="/img/bookmarkLogo.png" alt="logo" />
|
<img
|
||||||
|
style={{ width: "1.5rem" }}
|
||||||
|
src="/img/bookmarkLogo.png"
|
||||||
|
alt="logo"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
{this.renderUserArea()}
|
{this.renderUserArea()}
|
||||||
</div>
|
</div>
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: 0 }} />
|
||||||
<div style={{ minHeight: `calc(${document.body.clientHeight}px - 1.45rem)` }} className={styles.content}>
|
<div
|
||||||
|
style={{
|
||||||
|
minHeight: `calc(${document.body.clientHeight}px - 1.45rem)`
|
||||||
|
}}
|
||||||
|
className={styles.content}
|
||||||
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
开源地址:<a href="https://github.com/FleyX/bookmark">github.com/FleyX/bookmark</a>
|
开源地址:
|
||||||
|
<a href="https://github.com/FleyX/bookmark">
|
||||||
|
github.com/FleyX/bookmark
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MainLayout));
|
export default withRouter(
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)(MainLayout)
|
||||||
|
);
|
||||||
|
@ -7,8 +7,7 @@ import { stopTransfer } from "../../../util/eventUtil";
|
|||||||
const { TreeNode } = Tree;
|
const { TreeNode } = Tree;
|
||||||
|
|
||||||
function menuVisible(item, visible) {
|
function menuVisible(item, visible) {
|
||||||
if (visible) {
|
if (visible) { window.copyUrl = item.url;
|
||||||
window.copyUrl = item.url;
|
|
||||||
}
|
}
|
||||||
this.props.changeCurrentClickItem(item);
|
this.props.changeCurrentClickItem(item);
|
||||||
}
|
}
|
||||||
@ -56,7 +55,11 @@ export function renderNodeContent(item) {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{/* 触发右键菜单 */}
|
{/* 触发右键菜单 */}
|
||||||
<Dropdown overlay={menu} trigger={["contextMenu"]} onVisibleChange={menuVisible.bind(this, item)}>
|
<Dropdown
|
||||||
|
overlay={menu}
|
||||||
|
trigger={["contextMenu"]}
|
||||||
|
onVisibleChange={menuVisible.bind(this, item)}
|
||||||
|
>
|
||||||
{item.type === 0 ? (
|
{item.type === 0 ? (
|
||||||
<a href={item.url} className={styles.nodeContent}>
|
<a href={item.url} className={styles.nodeContent}>
|
||||||
{item.name}
|
{item.name}
|
||||||
@ -66,8 +69,18 @@ export function renderNodeContent(item) {
|
|||||||
)}
|
)}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
{isEdit ? (
|
{isEdit ? (
|
||||||
<Dropdown overlay={menu} trigger={["click"]} onVisibleChange={menuVisible.bind(this, item)}>
|
<Dropdown
|
||||||
<Button size="small" onClick={stopTransfer.bind(this)} type="primary" icon="menu" shape="circle" />
|
overlay={menu}
|
||||||
|
trigger={["click"]}
|
||||||
|
onVisibleChange={menuVisible.bind(this, item)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={stopTransfer.bind(this)}
|
||||||
|
type="primary"
|
||||||
|
icon="menu"
|
||||||
|
shape="circle"
|
||||||
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
) : null}
|
) : null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -130,7 +143,9 @@ export function batchDelete() {
|
|||||||
bookmarkIdList = [];
|
bookmarkIdList = [];
|
||||||
checkedNodes.forEach(item => {
|
checkedNodes.forEach(item => {
|
||||||
const data = item.props.dataRef;
|
const data = item.props.dataRef;
|
||||||
data.type === 0 ? bookmarkIdList.push(data.bookmarkId) : folderIdList.push(data.bookmarkId);
|
data.type === 0
|
||||||
|
? bookmarkIdList.push(data.bookmarkId)
|
||||||
|
: folderIdList.push(data.bookmarkId);
|
||||||
});
|
});
|
||||||
deleteBookmark.call(this, folderIdList, bookmarkIdList);
|
deleteBookmark.call(this, folderIdList, bookmarkIdList);
|
||||||
}
|
}
|
||||||
@ -203,11 +218,13 @@ export function onDrop(info) {
|
|||||||
sort: -1
|
sort: -1
|
||||||
};
|
};
|
||||||
//从原来所属的节点列表中删除当前节点
|
//从原来所属的节点列表中删除当前节点
|
||||||
const currentBelowList = current.path === "" ? treeData : getBelowList(treeData, current);
|
const currentBelowList =
|
||||||
|
current.path === "" ? treeData : getBelowList(treeData, current);
|
||||||
currentBelowList.splice(currentBelowList.indexOf(current), 1);
|
currentBelowList.splice(currentBelowList.indexOf(current), 1);
|
||||||
if (info.dropToGap) {
|
if (info.dropToGap) {
|
||||||
body.targetPath = target.path;
|
body.targetPath = target.path;
|
||||||
const targetBelowList = target.path === "" ? treeData : getBelowList(treeData, target);
|
const targetBelowList =
|
||||||
|
target.path === "" ? treeData : getBelowList(treeData, target);
|
||||||
const index = targetBelowList.indexOf(target);
|
const index = targetBelowList.indexOf(target);
|
||||||
if (info.dropPosition > index) {
|
if (info.dropPosition > index) {
|
||||||
body.sort = target.sort + 1;
|
body.sort = target.sort + 1;
|
||||||
@ -236,7 +253,11 @@ export function onDrop(info) {
|
|||||||
current.sort = body.sort;
|
current.sort = body.sort;
|
||||||
//如果当前节点和目标节点不在一个层级中,需要更新当前子节点的path信息
|
//如果当前节点和目标节点不在一个层级中,需要更新当前子节点的path信息
|
||||||
if (body.sourcePath !== body.targetPath) {
|
if (body.sourcePath !== body.targetPath) {
|
||||||
updateChildrenPath(current.children, body.sourcePath + "." + body.bookmarkId, body.targetPath + "." + body.bookmarkId);
|
updateChildrenPath(
|
||||||
|
current.children,
|
||||||
|
body.sourcePath + "." + body.bookmarkId,
|
||||||
|
body.targetPath + "." + body.bookmarkId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
httpUtil
|
httpUtil
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Tree, Empty, Button, Spin } from "antd";
|
import { Tree, Empty, Button, Spin } from "antd";
|
||||||
import MainLayout from "../../../layout/MainLayout";
|
import MainLayout from "../../../layout/MainLayout";
|
||||||
import httpUtil from "../../../util/httpUtil";
|
|
||||||
import styles from "./index.module.less";
|
import styles from "./index.module.less";
|
||||||
import { batchDelete, renderTreeNodes, onDrop } from "./function.js";
|
import { batchDelete, renderTreeNodes, onDrop } from "./function.js";
|
||||||
|
import { cacheBookmarkData, getBookmarkList } from "../../../util/cacheUtil";
|
||||||
import AddModal from "./AddModal";
|
import AddModal from "./AddModal";
|
||||||
import Search from "../../../components/Search";
|
import Search from "../../../components/Search";
|
||||||
|
|
||||||
@ -43,15 +43,11 @@ class OverView extends React.Component {
|
|||||||
/**
|
/**
|
||||||
* 初始化第一级书签
|
* 初始化第一级书签
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
async componentDidMount() {
|
||||||
this.props.refresh();
|
this.props.refresh();
|
||||||
httpUtil
|
await cacheBookmarkData();
|
||||||
.get("/bookmark/currentUser/path?path=")
|
this.props.updateTreeData(getBookmarkList(""));
|
||||||
.then(res => {
|
|
||||||
this.props.updateTreeData(res);
|
|
||||||
this.props.changeIsInit(true);
|
this.props.changeIsInit(true);
|
||||||
})
|
|
||||||
.catch(() => this.props.changeIsInit(true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,14 +58,12 @@ class OverView extends React.Component {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const item = e.props.dataRef;
|
const item = e.props.dataRef;
|
||||||
const newPath = item.path + "." + item.bookmarkId;
|
const newPath = item.path + "." + item.bookmarkId;
|
||||||
httpUtil.get("/bookmark/currentUser/path?path=" + newPath).then(res => {
|
item.children = getBookmarkList(newPath);
|
||||||
item.children = res;
|
|
||||||
this.props.updateTreeData([...treeData]);
|
this.props.updateTreeData([...treeData]);
|
||||||
loadedKeys.push(item.bookmarkId.toString());
|
loadedKeys.push(item.bookmarkId.toString());
|
||||||
this.props.changeLoadedKeys(loadedKeys);
|
this.props.changeLoadedKeys(loadedKeys);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* 节点选择
|
* 节点选择
|
||||||
|
55
front/src/util/cacheUtil.js
Normal file
55
front/src/util/cacheUtil.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
import httpUtil from "./httpUtil";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存工具类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全部书签数据key
|
||||||
|
*/
|
||||||
|
export const TREE_LIST_KEY = "treeListData";
|
||||||
|
/**
|
||||||
|
* 获取全部书签时间
|
||||||
|
*/
|
||||||
|
export const TREE_LIST_TIME_KEY = "treeListDataTime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存书签数据
|
||||||
|
*/
|
||||||
|
export async function cacheBookmarkData() {
|
||||||
|
let res = await localforage.getItem(TREE_LIST_KEY);
|
||||||
|
if (!res) {
|
||||||
|
res = await httpUtil.get("/bookmark/currentUser");
|
||||||
|
await localforage.setItem(TREE_LIST_KEY, res);
|
||||||
|
await localforage.setItem(TREE_LIST_TIME_KEY, Date.now());
|
||||||
|
}
|
||||||
|
window[TREE_LIST_KEY] = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存数据
|
||||||
|
* @param {*} path path
|
||||||
|
*/
|
||||||
|
export function getBookmarkList(path) {
|
||||||
|
return window[TREE_LIST_KEY][path];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存情况
|
||||||
|
* @return 返回true说明未过期,否则说明过期了
|
||||||
|
*/
|
||||||
|
export async function checkCacheStatus() {
|
||||||
|
let date = await localforage.getItem(TREE_LIST_TIME_KEY, Date.now());
|
||||||
|
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||||
|
return !date || date > userInfo.bookmarkChangeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清楚缓存数据
|
||||||
|
*/
|
||||||
|
export async function clearCache() {
|
||||||
|
await localforage.removeItem(TREE_LIST_KEY);
|
||||||
|
await localforage.removeItem(TREE_LIST_TIME_KEY);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user