feat:支持浏览器新标签页

This commit is contained in:
fanxb 2022-04-17 14:53:44 +08:00
parent b74be9f961
commit 1087f5e48b
13 changed files with 221 additions and 114 deletions

View File

@ -1,8 +1,10 @@
package com.fanxb.bookmark.business.bookmark.service.impl; package com.fanxb.bookmark.business.bookmark.service.impl;
import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.fanxb.bookmark.business.api.UserApi; import com.fanxb.bookmark.business.api.UserApi;
@ -21,6 +23,7 @@ import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.po.Bookmark; import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.exception.CustomException; import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.*; import com.fanxb.bookmark.common.util.*;
import com.mysql.cj.conf.url.SingleConnectionUrl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -37,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -53,7 +57,6 @@ import java.util.stream.Collectors;
* 类功能详述 * 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/7/8 15:00
*/ */
@Service @Service
@Slf4j @Slf4j
@ -116,7 +119,6 @@ public class BookmarkServiceImpl implements BookmarkService {
* @param path 节点路径不包含自身 * @param path 节点路径不包含自身
* @param sort 当前层级中的排序序号 * @param sort 当前层级中的排序序号
* @author fanxb * @author fanxb
* @date 2019/7/8 14:49
*/ */
private void dealBookmark(int userId, Element ele, String path, int sort, List<Bookmark> bookmarks) { private void dealBookmark(int userId, Element ele, String path, int sort, List<Bookmark> bookmarks) {
if (!DT.equalsIgnoreCase(ele.tagName())) { if (!DT.equalsIgnoreCase(ele.tagName())) {
@ -125,7 +127,7 @@ public class BookmarkServiceImpl implements BookmarkService {
Element first = ele.child(0); Element first = ele.child(0);
if (A.equalsIgnoreCase(first.tagName())) { if (A.equalsIgnoreCase(first.tagName())) {
//说明为链接 //说明为链接
Bookmark node = new Bookmark(userId, path, first.ownText(), first.attr("href"), first.attr("icon") Bookmark node = new Bookmark(userId, path, first.ownText(), first.attr("href"), ""
, Long.parseLong(first.attr("add_date")) * 1000, sort); , Long.parseLong(first.attr("add_date")) * 1000, sort);
//存入数据库 //存入数据库
insertOne(node); insertOne(node);
@ -155,7 +157,6 @@ public class BookmarkServiceImpl implements BookmarkService {
* @param node node * @param node node
* @return boolean 如果已经存在返回true否则false * @return boolean 如果已经存在返回true否则false
* @author fanxb * @author fanxb
* @date 2019/7/8 17:25
*/ */
private boolean insertOne(Bookmark node) { private boolean insertOne(Bookmark node) {
//先根据name,userId,parentId获取此节点id //先根据name,userId,parentId获取此节点id
@ -217,7 +218,7 @@ public class BookmarkServiceImpl implements BookmarkService {
bookmark.setUserId(userId); bookmark.setUserId(userId);
bookmark.setCreateTime(System.currentTimeMillis()); bookmark.setCreateTime(System.currentTimeMillis());
bookmark.setAddTime(bookmark.getCreateTime()); bookmark.setAddTime(bookmark.getCreateTime());
bookmark.setIcon(getIconPath(bookmark.getUrl())); bookmark.setIcon(getIconPath(bookmark.getUrl(), bookmark.getIcon(), bookmark.getIconUrl()));
//文件夹和书签都建立搜索key //文件夹和书签都建立搜索key
pinYinService.changeBookmark(bookmark); pinYinService.changeBookmark(bookmark);
bookmarkDao.insertOne(bookmark); bookmarkDao.insertOne(bookmark);
@ -231,7 +232,7 @@ public class BookmarkServiceImpl implements BookmarkService {
bookmark.setUserId(userId); bookmark.setUserId(userId);
if (bookmark.getType() == 0) { if (bookmark.getType() == 0) {
pinYinService.changeBookmark(bookmark); pinYinService.changeBookmark(bookmark);
bookmark.setIcon(getIconPath(bookmark.getUrl())); bookmark.setIcon(getIconPath(bookmark.getUrl(), null, null));
} }
bookmarkDao.editBookmark(bookmark); bookmarkDao.editBookmark(bookmark);
userApi.versionPlus(userId); userApi.versionPlus(userId);
@ -290,7 +291,7 @@ public class BookmarkServiceImpl implements BookmarkService {
while ((deal = bookmarkDao.selectUserNoIcon(userId, start, size)).size() > 0) { while ((deal = bookmarkDao.selectUserNoIcon(userId, start, size)).size() > 0) {
start += size; start += size;
deal.forEach(item -> { deal.forEach(item -> {
String icon = getIconPath(item.getUrl()); String icon = getIconPath(item.getUrl(), null, null);
if (StrUtil.isNotEmpty(icon)) { if (StrUtil.isNotEmpty(icon)) {
bookmarkDao.updateIcon(item.getBookmarkId(), icon); bookmarkDao.updateIcon(item.getBookmarkId(), icon);
} }
@ -322,37 +323,56 @@ public class BookmarkServiceImpl implements BookmarkService {
} }
/** /**
* 获取icon * 获取icon,通过网络获取或者从base64还原
* *
* @param url url * @param url url
* @param icon icon
* @param iconUrl iconUrl
* @return {@link String} * @return {@link String}
* @author fanxb * @author fanxb
*/ */
private String getIconPath(String url) { private String getIconPath(String url, String icon, String iconUrl) {
if (StrUtil.isEmpty(url)) {
return "";
}
String host; String host;
try { try {
URL urlObj = new URL(url); URL urlObj = new URL(url);
host = urlObj.getHost(); host = urlObj.getAuthority();
} catch (Exception e) { } catch (Exception e) {
log.warn("url无法解析出domain:{}", url); log.warn("url无法解析出domain:{}", url);
return ""; return "";
} }
if (StrUtil.isNotBlank(icon)) {
//优先从base64还原出图片
try {
byte[] b = Base64Decoder.decode(icon.substring(icon.indexOf(",") + 1));
String iconPath = saveToFile(iconUrl, host, b);
hostIconDao.insert(host, iconPath);
return iconPath;
} catch (Exception e) {
log.error("解析base64获取icon故障:{}", iconUrl, e);
}
}
String iconPath = hostIconDao.selectByHost(host); String iconPath = hostIconDao.selectByHost(host);
if (iconPath != null) { if (iconPath != null) {
return iconPath; return iconPath;
} }
iconPath = saveFile(host, urlIconAddress + "/icon?url=" + host + "&size=16..64..256"); //再根据url解析
iconPath = saveFile(host, urlIconAddress + "/icon?url=" + host + "&size=16..128..256");
if (StrUtil.isNotEmpty(iconPath)) { if (StrUtil.isNotEmpty(iconPath)) {
hostIconDao.insert(host, iconPath); hostIconDao.insert(host, iconPath);
} }
return iconPath; return iconPath;
} }
/**
* 保存文件到icon路径
*
* @param host host
* @param url url
* @return {@link String}
* @author FleyX
*/
private String saveFile(String host, String url) { private String saveFile(String host, String url) {
try {
try (Response res = HttpUtil.getClient(false).newCall(new Request.Builder().url(url) try (Response res = HttpUtil.getClient(false).newCall(new Request.Builder().url(url)
.header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36") .header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36")
.get().build()).execute()) { .get().build()).execute()) {
@ -362,18 +382,31 @@ public class BookmarkServiceImpl implements BookmarkService {
} }
byte[] data = res.body().byteStream().readAllBytes(); byte[] data = res.body().byteStream().readAllBytes();
if (data.length > 0) { if (data.length > 0) {
String iconUrl = res.request().url().toString(); String iconUrl = new URL(res.request().url().toString()).getPath();
String fileName = URLEncoder.encode(host, StandardCharsets.UTF_8) + iconUrl.substring(iconUrl.lastIndexOf(".")); return saveToFile(iconUrl, host, data);
String filePath = Paths.get(FileConstant.FAVICON_PATH, host.substring(0, 2), fileName).toString();
FileUtil.writeBytes(data, Paths.get(CommonConstant.fileSavePath, filePath).toString());
return File.separator + filePath;
} else { } else {
log.info("未获取到icon:{}", url); log.info("未获取到icon:{}", url);
} }
}
} catch (Exception e) { } catch (Exception e) {
log.error("url获取icon故障:{}", url, e); log.error("url获取icon故障:{}", url, e);
} }
return ""; return "";
} }
/**
* 保存到文件中
*
* @param iconUrl icon文件名
* @param host host
* @param b 数据
* @return {@link String}
* @author FleyX
*/
private String saveToFile(String iconUrl, String host, byte[] b) {
String fileName = URLEncoder.encode(host, StandardCharsets.UTF_8) + iconUrl.substring(iconUrl.lastIndexOf("."));
String filePath = Paths.get(FileConstant.FAVICON_PATH, host.replace("www", "").replaceAll("\\.", "").substring(0, 2), fileName).toString();
FileUtil.writeBytes(b, Paths.get(CommonConstant.fileSavePath, filePath).toString());
return File.separator + filePath;
}
} }

View File

@ -12,7 +12,6 @@ import java.util.List;
* 类功能详述 * 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/7/8 11:19
*/ */
@Data @Data
public class Bookmark { public class Bookmark {
@ -36,6 +35,7 @@ public class Bookmark {
private String name; private String name;
private String url = ""; private String url = "";
private String icon = ""; private String icon = "";
private String iconUrl;
private Integer sort; private Integer sort;
private String searchKey = ""; private String searchKey = "";
private Long addTime; private Long addTime;

View File

@ -51,7 +51,7 @@
</div> </div>
</div> </div>
</div> </div>
<a ref="targetA" style="left: 1000000px" target="_blank" /> <a ref="targetA" style="left: 1000000px" />
</div> </div>
</template> </template>

View File

@ -58,7 +58,7 @@ router.beforeEach(async (to, from, next) => {
await vuex.default.dispatch("treeData/clear"); await vuex.default.dispatch("treeData/clear");
await vuex.default.dispatch("globalConfig/clear"); await vuex.default.dispatch("globalConfig/clear");
next({ next({
path: "/public/login?to=" + btoa(location.href), path: "/public/login?to=" + btoa(to.fullPath),
replace: true replace: true
}); });
} else { } else {

View File

@ -22,7 +22,13 @@ export const refreshHomePinList = "refreshHomePinList";
* 通过id获取书签数据 * 通过id获取书签数据
*/ */
export const getById = "getById"; export const getById = "getById";
/**
* 登录前初始化
*/
export const noLoginInit = "noLoginInit"; export const noLoginInit = "noLoginInit";
/**
* 登陆后初始化
*/
export const loginInit = "loginInit"; export const loginInit = "loginInit";
export const refresh = "refresh"; export const refresh = "refresh";
export const clear = "clear"; export const clear = "clear";
@ -39,6 +45,10 @@ export const addNode = "addNode";
* 版本检查定时调度 * 版本检查定时调度
*/ */
let timer = null; let timer = null;
/**
* 检查本地版本是否有更新
*/
let checkLocalDataTimer = null;
/** /**
* 刷新书签确认弹窗是否展示 * 刷新书签确认弹窗是否展示
*/ */
@ -76,8 +86,8 @@ const getters = {
}; };
const actions = { const actions = {
async [noLoginInit]() {}, async [noLoginInit] () { },
async [loginInit](context) { async [loginInit] (context) {
if (context.state.isInit || context.state.isIniting) { if (context.state.isInit || context.state.isIniting) {
return; return;
} }
@ -89,11 +99,12 @@ const actions = {
context.commit(IS_INIT, true); context.commit(IS_INIT, true);
context.commit(IS_INITING, false); context.commit(IS_INITING, false);
timer = setInterval(() => treeDataCheck(context, false), CHECK_INTERVAL); timer = setInterval(() => treeDataCheck(context, false), CHECK_INTERVAL);
checkLocalDataTimer = setInterval(() => checkLocalData(context), 2000);
}, },
/** /**
* 确保数据加载完毕 * 确保数据加载完毕
*/ */
ensureDataOk(context) { ensureDataOk (context) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let timer = setInterval(() => { let timer = setInterval(() => {
try { try {
@ -108,7 +119,7 @@ const actions = {
}); });
}, },
//刷新缓存数据 //刷新缓存数据
async [refresh](context) { async [refresh] (context) {
let treeData = await HttpUtil.get("/bookmark/currentUser"); let treeData = await HttpUtil.get("/bookmark/currentUser");
if (!treeData[""]) { if (!treeData[""]) {
treeData[""] = []; treeData[""] = [];
@ -127,7 +138,7 @@ const actions = {
await localforage.setItem(TOTAL_TREE_DATA, treeData); await localforage.setItem(TOTAL_TREE_DATA, treeData);
}, },
//清除缓存数据 //清除缓存数据
async [clear](context) { async [clear] (context) {
context.commit(TOTAL_TREE_DATA, null); context.commit(TOTAL_TREE_DATA, null);
context.commit(VERSION, null); context.commit(VERSION, null);
context.commit(SHOW_REFRESH_TOAST, false); context.commit(SHOW_REFRESH_TOAST, false);
@ -137,13 +148,16 @@ const actions = {
if (timer != null) { if (timer != null) {
clearInterval(timer); clearInterval(timer);
} }
if (checkLocalDataTimer != null) {
clearInterval(checkLocalDataTimer);
}
await localforage.removeItem(TOTAL_TREE_DATA); await localforage.removeItem(TOTAL_TREE_DATA);
await localforage.removeItem(VERSION); await localforage.removeItem(VERSION);
}, },
/** /**
* 移动节点 * 移动节点
*/ */
async moveNode(context, info) { async moveNode (context, info) {
let data = context.state[TOTAL_TREE_DATA]; let data = context.state[TOTAL_TREE_DATA];
const target = info.node.dataRef; const target = info.node.dataRef;
const current = info.dragNode.dataRef; const current = info.dragNode.dataRef;
@ -211,7 +225,7 @@ const actions = {
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]); await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
return body; return body;
}, },
async [refreshHomePinList]({ commit }) { async [refreshHomePinList] ({ commit }) {
let list = await HttpUtil.get("/home/pin"); let list = await HttpUtil.get("/home/pin");
commit(HOME_PIN_LIST, list); commit(HOME_PIN_LIST, list);
let map = {}; let map = {};
@ -221,14 +235,14 @@ const actions = {
/** /**
* 更新版本数据 * 更新版本数据
*/ */
async updateVersion({ commit, state }, version) { async updateVersion ({ commit, state }, version) {
commit(VERSION, version == null ? state[VERSION] + 1 : version); commit(VERSION, version == null ? state[VERSION] + 1 : version);
await localforage.setItem(VERSION, state[VERSION]); await localforage.setItem(VERSION, state[VERSION]);
}, },
/** /**
* 新增书签文件夹 * 新增书签文件夹
*/ */
async [addNode](context, { sourceNode, targetNode }) { async [addNode] (context, { sourceNode, targetNode }) {
if (sourceNode === null) { if (sourceNode === null) {
if (context.state[TOTAL_TREE_DATA][""] === undefined) { if (context.state[TOTAL_TREE_DATA][""] === undefined) {
context.state[TOTAL_TREE_DATA][""] = []; context.state[TOTAL_TREE_DATA][""] = [];
@ -253,7 +267,7 @@ const actions = {
/** /**
* 删除节点数据 * 删除节点数据
*/ */
async [deleteData](context, { pathList, bookmarkIdList }) { async [deleteData] (context, { pathList, bookmarkIdList }) {
//待删除的书签 //待删除的书签
let bookmarkIdSet = new Set(); let bookmarkIdSet = new Set();
bookmarkIdList.forEach(item => bookmarkIdSet.add(item)); bookmarkIdList.forEach(item => bookmarkIdSet.add(item));
@ -281,7 +295,7 @@ const actions = {
/** /**
* 编辑书签节点 * 编辑书签节点
*/ */
async editNode({ dispatch, state, commit }, { node, newName, newUrl, newIcon }) { async editNode ({ dispatch, state, commit }, { node, newName, newUrl, newIcon }) {
node.name = newName; node.name = newName;
node.url = newUrl; node.url = newUrl;
node.icon = newIcon; node.icon = newIcon;
@ -295,10 +309,10 @@ const mutations = {
[TOTAL_TREE_DATA]: (state, totalTreeData) => { [TOTAL_TREE_DATA]: (state, totalTreeData) => {
state.totalTreeData = totalTreeData; state.totalTreeData = totalTreeData;
}, },
[IS_INIT](state, isInit) { [IS_INIT] (state, isInit) {
state.isInit = isInit; state.isInit = isInit;
}, },
[IS_INITING](state, isIniting) { [IS_INITING] (state, isIniting) {
state.isIniting = isIniting; state.isIniting = isIniting;
}, },
[VERSION]: (state, version) => { [VERSION]: (state, version) => {
@ -322,7 +336,7 @@ const mutations = {
* @param {*} isFirst * @param {*} isFirst
* @returns * @returns
*/ */
async function treeDataCheck(context, isFirst) { async function treeDataCheck (context, isFirst) {
if (toastShow || !checkJwtValid(context.rootState.globalConfig.token)) { if (toastShow || !checkJwtValid(context.rootState.globalConfig.token)) {
return; return;
} }
@ -336,14 +350,14 @@ async function treeDataCheck(context, isFirst) {
closable: false, closable: false,
keyboard: false, keyboard: false,
maskClosable: false, maskClosable: false,
onOk() { onOk () {
toastShow = false; toastShow = false;
return new Promise(async resolve => { return new Promise(async resolve => {
await context.dispatch(refresh); await context.dispatch(refresh);
resolve(); resolve();
}); });
}, },
onCancel() { onCancel () {
toastShow = false; toastShow = false;
} }
}); });
@ -354,6 +368,24 @@ async function treeDataCheck(context, isFirst) {
} }
} }
/**
* 检查本地缓存数据是否有更新
* @param {*} context
*/
async function checkLocalData (context) {
let data = await localforage.getItem(TOTAL_TREE_DATA);
let version = await localforage.getItem(VERSION);
if (!data || !version) {
return;
}
if (version > context.state[VERSION]) {
console.log("从local缓存更新数据", version);
context.commit(TOTAL_TREE_DATA, data);
context.commit(VERSION, version);
}
}
export const store = { export const store = {
namespaced: true, namespaced: true,
state, state,

View File

@ -77,7 +77,7 @@
<a-menu-item v-if="!rec.dataRef.isLeaf" key="add">新增</a-menu-item> <a-menu-item v-if="!rec.dataRef.isLeaf" key="add">新增</a-menu-item>
<a-menu-item v-else key="copy" class="copy-to-board" :data="rec.dataRef.url">复制URL</a-menu-item> <a-menu-item v-else key="copy" class="copy-to-board" :data="rec.dataRef.url">复制URL</a-menu-item>
<a-menu-item v-if="rec.dataRef.isLeaf" key="pin"> <a-menu-item v-if="rec.dataRef.isLeaf" key="pin">
{{ homePinList.filter(item => item.id && item.bookmarkId == rec.dataRef.bookmarkId).length > 0 ? "从首页移除" : "固定到首页" }} {{ homePinList.filter((item) => item.id && item.bookmarkId == rec.dataRef.bookmarkId).length > 0 ? "从首页移除" : "固定到首页" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="edit">编辑</a-menu-item> <a-menu-item key="edit">编辑</a-menu-item>
<a-menu-item key="delete">删除</a-menu-item> <a-menu-item key="delete">删除</a-menu-item>
@ -114,7 +114,7 @@ export default {
loadedKeys: [], // loadedKeys: [], //
replaceFields: { replaceFields: {
title: "name", title: "name",
key: "bookmarkId" key: "bookmarkId",
}, },
mulSelect: false, // mulSelect: false, //
currentSelect: null, // currentSelect: null, //
@ -126,19 +126,19 @@ export default {
// null // null
targetNode: null, targetNode: null,
// //
isAdd: false isAdd: false,
}, },
copyBoard: null // copyBoard: null, //
}; };
}, },
computed: { computed: {
...mapState("treeData", ["totalTreeData", HOME_PIN_LIST]), ...mapState("treeData", ["totalTreeData", HOME_PIN_LIST]),
...mapState("globalConfig", ["isPhone"]) ...mapState("globalConfig", ["isPhone"]),
}, },
watch: { watch: {
totalTreeData(newVal, oldVal) { totalTreeData(newVal, oldVal) {
this.resetData(); this.resetData();
} },
}, },
async mounted() { async mounted() {
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true); this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true);
@ -147,21 +147,14 @@ export default {
this.loading = false; this.loading = false;
//clipboard //clipboard
this.copyBoard = new ClipboardJS(".copy-to-board", { this.copyBoard = new ClipboardJS(".copy-to-board", {
text: function(trigger) { text: function (trigger) {
return trigger.attributes.data.nodeValue; return trigger.attributes.data.nodeValue;
} },
}); });
this.copyBoard.on("success", e => { this.copyBoard.on("success", (e) => {
this.$message.success("复制成功"); this.$message.success("复制成功");
e.clearSelection(); e.clearSelection();
}); });
window.onblur = e => {
console.log("窗口非激活");
};
window.onfocus = e => {
console.log("窗口激活");
};
}, },
beforeDestroy() { beforeDestroy() {
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false); this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false);
@ -175,7 +168,7 @@ export default {
*/ */
loadData(treeNode) { loadData(treeNode) {
console.log("加载数据", treeNode); console.log("加载数据", treeNode);
return new Promise(resolve => { return new Promise((resolve) => {
const data = typeof treeNode === "number" ? this.$store.getters["treeData/getById"](treeNode) : treeNode.dataRef; const data = typeof treeNode === "number" ? this.$store.getters["treeData/getById"](treeNode) : treeNode.dataRef;
let newPath = data.path + "." + data.bookmarkId; let newPath = data.path + "." + data.bookmarkId;
if (!this.totalTreeData[newPath]) { if (!this.totalTreeData[newPath]) {
@ -219,9 +212,9 @@ export default {
this.expandedKeys = [ this.expandedKeys = [
...item.path ...item.path
.split(".") .split(".")
.filter(item => item.length > 0) .filter((item) => item.length > 0)
.map(item => parseInt(item)), .map((item) => parseInt(item)),
item.bookmarkId item.bookmarkId,
]; ];
} else { } else {
this.expandedKeys.pop(); this.expandedKeys.pop();
@ -235,7 +228,7 @@ export default {
} else { } else {
this.checkedKeys.splice(this.checkedKeys.indexOf(item.bookmarkId), 1); this.checkedKeys.splice(this.checkedKeys.indexOf(item.bookmarkId), 1);
this.checkedNodes.splice( this.checkedNodes.splice(
this.checkedNodes.findIndex(item1 => item1.bookmarkId === item.bookmarkId), this.checkedNodes.findIndex((item1) => item1.bookmarkId === item.bookmarkId),
1 1
); );
} }
@ -255,9 +248,9 @@ export default {
this.expandedKeys = [ this.expandedKeys = [
...item.path ...item.path
.split(".") .split(".")
.filter(item => item.length > 0) .filter((item) => item.length > 0)
.map(item => parseInt(item)), .map((item) => parseInt(item)),
item.bookmarkId item.bookmarkId,
]; ];
} }
} else { } else {
@ -280,7 +273,7 @@ export default {
const bookmarkIdList = []; const bookmarkIdList = [];
const pathList = []; const pathList = [];
if (this.checkedNodes) { if (this.checkedNodes) {
this.checkedNodes.forEach(item => this.checkedNodes.forEach((item) =>
item.type === 1 ? pathList.push(item.path + "." + item.bookmarkId) : bookmarkIdList.push(item.bookmarkId) item.type === 1 ? pathList.push(item.path + "." + item.bookmarkId) : bookmarkIdList.push(item.bookmarkId)
); );
} }
@ -297,7 +290,7 @@ export default {
await HttpUtil.post("/bookmark/batchDelete", null, { pathList, bookmarkIdList }); await HttpUtil.post("/bookmark/batchDelete", null, { pathList, bookmarkIdList });
await this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList }); await this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList });
// //
pathList.forEach(item => { pathList.forEach((item) => {
const id = parseInt(item.split(".").reverse()[0]); const id = parseInt(item.split(".").reverse()[0]);
let index = this.loadedKeys.indexOf(id); let index = this.loadedKeys.indexOf(id);
if (index > -1) { if (index > -1) {
@ -329,13 +322,13 @@ export default {
this.refresh(false); this.refresh(false);
this.expandedKeys = item.path this.expandedKeys = item.path
.split(".") .split(".")
.filter(one => one.length > 0) .filter((one) => one.length > 0)
.map(one => parseInt(one)); .map((one) => parseInt(one));
this.loadedKeys = item.path this.loadedKeys = item.path
.split(".") .split(".")
.filter(one => one.length > 0) .filter((one) => one.length > 0)
.map(one => parseInt(one)); .map((one) => parseInt(one));
this.expandedKeys.forEach(async one => await this.loadData(one)); this.expandedKeys.forEach(async (one) => await this.loadData(one));
this.currentSelect = item; this.currentSelect = item;
}, },
/** /**
@ -351,7 +344,7 @@ export default {
this.addModal = { this.addModal = {
show: false, show: false,
targetNode: null, targetNode: null,
isAdd: false isAdd: false,
}; };
}, },
async onDrop(info) { async onDrop(info) {
@ -395,12 +388,12 @@ export default {
await this.deleteBookmarks(); await this.deleteBookmarks();
resolve(); resolve();
}); });
} },
}); });
} else if (key === "edit") { } else if (key === "edit") {
this.editData(); this.editData();
} else if (key === "pin") { } else if (key === "pin") {
let pin = this.homePinList.filter(one => one.id && one.bookmarkId == item.bookmarkId); let pin = this.homePinList.filter((one) => one.id && one.bookmarkId == item.bookmarkId);
if (pin.length > 0) { if (pin.length > 0) {
await HttpUtil.delete("/home/pin", { id: pin[0].id }); await HttpUtil.delete("/home/pin", { id: pin[0].id });
} else { } else {
@ -418,8 +411,8 @@ export default {
dealList(root, map[""], map); dealList(root, map[""], map);
let content = exportFileHead + root.outerHTML; let content = exportFileHead + root.outerHTML;
downloadFile(moment().format("YYYY-MM-DD") + "导出书签.html", content); downloadFile(moment().format("YYYY-MM-DD") + "导出书签.html", content);
} },
} },
}; };
</script> </script>

View File

@ -14,14 +14,16 @@ export default {
form: { form: {
name: null, name: null,
url: null, url: null,
icon: null,
iconUrl: null,
type: 0, type: 0,
path: "" path: "",
} },
}; };
}, },
mounted() { mounted() {
// //
window.addEventListener("message", event => { window.addEventListener("message", (event) => {
if (!event.data.code) { if (!event.data.code) {
return; return;
} }
@ -30,6 +32,8 @@ export default {
console.log("新增书签"); console.log("新增书签");
this.form.name = event.data.data.name; this.form.name = event.data.data.name;
this.form.url = event.data.data.url; this.form.url = event.data.data.url;
this.form.icon = event.data.data.icon;
this.form.iconUrl = event.data.data.iconUrl;
this.addBookmark(); this.addBookmark();
} }
}); });
@ -46,8 +50,8 @@ export default {
this.$message.success("添加成功"); this.$message.success("添加成功");
await this.$store.dispatch(TREE_DATA + "/" + addNode, { sourceNode: null, targetNode: res }); await this.$store.dispatch(TREE_DATA + "/" + addNode, { sourceNode: null, targetNode: res });
setTimeout(this.closeIframe, 500); setTimeout(this.closeIframe, 500);
} },
} },
}; };
</script> </script>

View File

@ -67,11 +67,15 @@ export default {
loading: false, // loading: false, //
oauthLogining: false, //true:oauth oauthLogining: false, //true:oauth
page: null, //oauth page: null, //oauth
redirect: null,
}; };
}, },
async created() { async created() {
let _this = this; let _this = this;
window.addEventListener("storage", this.storageDeal.bind(this)); window.addEventListener("storage", this.storageDeal.bind(this));
if (this.$route.query.to) {
this.redirect = atob(this.$route.query.to);
}
}, },
destroyed() { destroyed() {
window.removeEventListener("storage", this.storageDeal); window.removeEventListener("storage", this.storageDeal);
@ -85,7 +89,7 @@ export default {
this.loading = true; this.loading = true;
let token = await httpUtil.post("/user/login", null, this.form); let token = await httpUtil.post("/user/login", null, this.form);
await this.$store.dispatch("globalConfig/setToken", token); await this.$store.dispatch("globalConfig/setToken", token);
this.$router.replace("/"); this.$router.replace(this.redirect ? this.redirect : "/");
} finally { } finally {
this.loading = false; this.loading = false;
} }

View File

@ -15,6 +15,7 @@ chrome.contextMenus.onClicked.addListener(async function (info, tab) {
let body = { let body = {
name: tab.title, name: tab.title,
url: tab.url, url: tab.url,
iconUrl: tab.favIconUrl
}; };
sendToContent(tab.id, { code: "addBookmark", data: body, token: await getVal("token") }); sendToContent(tab.id, { code: "addBookmark", data: body, token: await getVal("token") });
}); });
@ -33,7 +34,8 @@ chrome.runtime.onMessage.addListener(async (data, sender, sendResponse) => {
await sendToContent(sender.tab.id, { code: "setTokenOk" }); await sendToContent(sender.tab.id, { code: "setTokenOk" });
} else if (data.code == 'getToken') { } else if (data.code == 'getToken') {
let token = await getVal("token"); let token = await getVal("token");
sendToPopup({ code: "setToken", data: await getVal("token") });
sendToPopup({ code: "setToken", data: token });
} else if (data.code == "clearToken") { } else if (data.code == "clearToken") {
await clearVal("token"); await clearVal("token");
} }
@ -83,7 +85,12 @@ function setVal (key, val) {
*/ */
function getVal (key) { function getVal (key) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.storage.local.get([key], function (res) { chrome.storage.local.get([key], async function (res) {
if (key === 'token' && !checkTokenValid(res[key])) {
console.log("token过期");
await clearVal("token");
res[key] = null;
}
console.log("取值成功", res); console.log("取值成功", res);
resolve(res[key]); resolve(res[key]);
}) })
@ -98,3 +105,23 @@ function clearVal (key) {
}) })
}) })
} }
/**
* 检查token是否有效
* @param {*} token
* @returns
*/
function checkTokenValid (token) {
try {
if (token && token.trim().length > 0) {
//检查token是否还有效
let content = JSON.parse(atob(token.split(".")[1]));
if (content.exp > Date.now() / 1000) {
return true;
}
}
} catch (err) {
console.error(token, err);
}
return false;
}

View File

@ -8,7 +8,11 @@
"newtab": "tab/index.html" "newtab": "tab/index.html"
}, },
"action": { "action": {
"default_popup": "popup/index.html" "default_popup": "popup/index.html",
"default_icon": {
"48": "static/icons/favicon.png",
"128": "static/icons/favicon.png"
}
}, },
"icons": { "icons": {
"48": "static/icons/favicon.png", "48": "static/icons/favicon.png",

View File

@ -1,7 +1,7 @@
// var bookmarkHost = "https://fleyx.com"; // var bookmarkHost = "https://fleyx.com";
var bookmarkHost = "http://localhost:8080"; var bookmarkHost = "http://localhost:8080";
var version = "0.1"; var version = "0.1.1";
window.token = localStorage.getItem('token'); window.token = localStorage.getItem('token');
axios.defaults.baseURL = bookmarkHost + '/bookmark/api'; axios.defaults.baseURL = bookmarkHost + '/bookmark/api';

View File

@ -46,6 +46,15 @@ async function addBookmark (data) {
return; return;
} }
//新增书签 //新增书签
try {
if (data.data.iconUrl) {
let icon = await axios.get(data.data.iconUrl, { responseType: 'arraybuffer' });
console.log(JSON.stringify(new Uint8Array(icon.data)));
data.data.icon = `data:` + icon.headers['content-type'] + ';base64,' + window.btoa(String.fromCharCode(...new Uint8Array(icon.data)));
}
} catch (error) {
console.error(error);
}
console.log("新增书签", data.data); console.log("新增书签", data.data);
bookmarkInfo = data.data; bookmarkInfo = data.data;
addBlockDiv = document.createElement("div"); addBlockDiv = document.createElement("div");

View File

@ -1,9 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-cn"> <html lang="zh-cn">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title> <link rel="icon" href="https://fleyx.com/favicon.ico" type="image/x-icon" crossorigin="anonymous" sizes="32*32"/>
<title>新建标签页</title>
<style> <style>
html, html,
body { body {
@ -13,8 +14,8 @@
padding: 0; padding: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<iframe src="https://fleyx.com" style="display: block" height="100%" width="100%" frameborder="0" scrolling="auto"></iframe> <iframe src="https://fleyx.com" style="display: block;border: 0" height="100%" width="100%"></iframe>
</body> </body>
</html> </html>