Merge pull request #33 from FleyX/dev

Dev
This commit is contained in:
FleyX 2022-04-28 10:46:28 +08:00 committed by GitHub
commit 35910c34e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 287 additions and 30 deletions

View File

@ -1,9 +1,6 @@
package com.fanxb.bookmark.business.bookmark.dao; package com.fanxb.bookmark.business.bookmark.dao;
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/** /**
* @author fanxb * @author fanxb
@ -28,6 +25,15 @@ public interface HostIconDao {
* @return {@link String} * @return {@link String}
* @author fanxb * @author fanxb
*/ */
@Select("select iconPath from host_icon where host=#{host}") @Select("select iconPath from host_icon where host=#{host} limit 1")
String selectByHost(String host); String selectByHost(String host);
/**
* 删除一条
*
* @param host host
* @author FleyX
*/
@Delete("delete from host_icon where host=#{host}")
void deleteByHost(String host);
} }

View File

@ -345,6 +345,7 @@ public class BookmarkServiceImpl implements BookmarkService {
try { try {
byte[] b = Base64Decoder.decode(icon.substring(icon.indexOf(",") + 1)); byte[] b = Base64Decoder.decode(icon.substring(icon.indexOf(",") + 1));
String iconPath = saveToFile(iconUrl, host, b); String iconPath = saveToFile(iconUrl, host, b);
hostIconDao.deleteByHost(host);
hostIconDao.insert(host, iconPath); hostIconDao.insert(host, iconPath);
return iconPath; return iconPath;
} catch (Exception e) { } catch (Exception e) {
@ -404,7 +405,7 @@ public class BookmarkServiceImpl implements BookmarkService {
* @author FleyX * @author FleyX
*/ */
private String saveToFile(String iconUrl, String host, byte[] b) { private String saveToFile(String iconUrl, String host, byte[] b) {
String fileName = URLEncoder.encode(host, StandardCharsets.UTF_8) + iconUrl.substring(iconUrl.lastIndexOf(".")); String fileName = host.replace(":", ".") + iconUrl.substring(iconUrl.lastIndexOf("."));
String filePath = Paths.get(FileConstant.FAVICON_PATH, host.replace("www", "").replaceAll("\\.", "").substring(0, 2), fileName).toString(); 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()); FileUtil.writeBytes(b, Paths.get(CommonConstant.fileSavePath, filePath).toString());
return File.separator + filePath; return File.separator + filePath;

View File

@ -0,0 +1,14 @@
package com.fanxb.bookmark.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.common.entity.po.GlobalConfigPo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author fanxb
*/
@Mapper
public interface GlobalConfigDao extends BaseMapper<GlobalConfigPo> {
}

View File

@ -0,0 +1,20 @@
package com.fanxb.bookmark.common.entity.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 全局配置表
*
* @author FleyX
*/
@Data
@TableName("global_config")
public class GlobalConfigPo {
@TableId(value = "code")
private String code;
private String value;
private String description;
}

View File

@ -2,6 +2,8 @@ package com.fanxb.bookmark.common.entity.vo;
import lombok.Data; import lombok.Data;
import java.util.Map;
/** /**
* 全局公共配置 * 全局公共配置
* *
@ -17,4 +19,8 @@ public class GlobalConfigVo {
* bing每日一图地址 * bing每日一图地址
*/ */
private String bingImgSrc; private String bingImgSrc;
/**
* 浏览器插件版本plugin
*/
private Map<String, String> map;
} }

View File

@ -6,6 +6,8 @@ import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.common.constant.NumberConstant; import com.fanxb.bookmark.common.constant.NumberConstant;
import com.fanxb.bookmark.common.constant.RedisConstant; import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.dao.GlobalConfigDao;
import com.fanxb.bookmark.common.entity.po.GlobalConfigPo;
import com.fanxb.bookmark.common.entity.vo.GlobalConfigVo; import com.fanxb.bookmark.common.entity.vo.GlobalConfigVo;
import com.fanxb.bookmark.common.service.ConfigService; import com.fanxb.bookmark.common.service.ConfigService;
import com.fanxb.bookmark.common.util.HttpUtil; import com.fanxb.bookmark.common.util.HttpUtil;
@ -15,10 +17,9 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date; import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** /**
* @author fanxb * @author fanxb
@ -30,10 +31,12 @@ public class ConfigServiceImpl implements ConfigService {
private final StringRedisTemplate stringRedisTemplate; private final StringRedisTemplate stringRedisTemplate;
private final GlobalConfigDao globalConfigDao;
@Autowired @Autowired
public ConfigServiceImpl(StringRedisTemplate stringRedisTemplate) { public ConfigServiceImpl(StringRedisTemplate stringRedisTemplate, GlobalConfigDao globalConfigDao) {
this.stringRedisTemplate = stringRedisTemplate; this.stringRedisTemplate = stringRedisTemplate;
this.globalConfigDao = globalConfigDao;
} }
@Value("${bing.host}") @Value("${bing.host}")
@ -43,9 +46,12 @@ public class ConfigServiceImpl implements ConfigService {
@Override @Override
public GlobalConfigVo getGlobalConfig() { public GlobalConfigVo getGlobalConfig() {
List<GlobalConfigPo> pos = globalConfigDao.selectByMap(Collections.emptyMap());
Map<String, String> map = pos.stream().collect(Collectors.toMap(GlobalConfigPo::getCode, GlobalConfigPo::getValue));
GlobalConfigVo vo = new GlobalConfigVo(); GlobalConfigVo vo = new GlobalConfigVo();
vo.setProxyExist(HttpUtil.getProxyExist()); vo.setProxyExist(HttpUtil.getProxyExist());
vo.setBingImgSrc(getCacheBingImg()); vo.setBingImgSrc(getCacheBingImg());
vo.setMap(map);
return vo; return vo;
} }

View File

@ -0,0 +1,13 @@
CREATE TABLE bookmark.global_config
(
code varchar(20) NOT NULL,
value varchar(100) NOT NULL COMMENT '',
description varchar(100) NOT NULL COMMENT '描述',
CONSTRAINT global_config_pk PRIMARY KEY (code)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_0900_ai_ci
COMMENT='全局配置表';
insert into global_config
values ("pluginVersion", "0.1.1", "浏览器插件版本");

View File

@ -0,0 +1,3 @@
update global_config
set value='0.1.2'
where code = "pluginVersion";

View File

@ -62,7 +62,7 @@ export default {
file: null, file: null,
}, },
rules: { rules: {
name: [{ required: true, min: 1, max: 1000, message: "名称长度为1-1000", trigger: "change" }], name: [{ required: true, min: 1, max: 1000, message: "名称长度为1-200", trigger: "change" }],
url: [{ required: true, min: 1, message: "不能为空", trigger: "change" }], url: [{ required: true, min: 1, message: "不能为空", trigger: "change" }],
}, },
}; };

View File

@ -19,7 +19,8 @@ import {
Popconfirm, Popconfirm,
AutoComplete, AutoComplete,
Select, Select,
Popover Popover,
Breadcrumb
} 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";
@ -46,6 +47,7 @@ Vue.use(Popconfirm);
Vue.use(AutoComplete); Vue.use(AutoComplete);
Vue.use(Select); Vue.use(Select);
Vue.use(Popover); Vue.use(Popover);
Vue.use(Breadcrumb);
Vue.component("my-icon", IconFont); Vue.component("my-icon", IconFont);
Vue.prototype.$message = message; Vue.prototype.$message = message;

View File

@ -22,7 +22,8 @@ let noLoginFinish = false;
//执行各自的非登陆初始化 //执行各自的非登陆初始化
(async () => { (async () => {
await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.noLoginInit); await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.noLoginInit);
await store.dispatch(treeData.TREE_DATA + "/" + treeData.noLoginInit); //无需等待执行
store.dispatch(treeData.TREE_DATA + "/" + treeData.noLoginInit);
noLoginFinish = true; noLoginFinish = true;
})(); })();
@ -35,8 +36,9 @@ export async function loginInit () {
} }
console.log(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN]); console.log(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN]);
if (checkJwtValid(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); store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.loginInit);
store.dispatch(treeData.TREE_DATA + "/" + treeData.loginInit);
} }
} }

View File

@ -249,12 +249,16 @@ const actions = {
} }
context.state[TOTAL_TREE_DATA][""].push(targetNode); context.state[TOTAL_TREE_DATA][""].push(targetNode);
} else { } else {
let path = sourceNode.path + "." + sourceNode.bookmarkId;
if (!context.state[TOTAL_TREE_DATA][path]) {
context.state[TOTAL_TREE_DATA][path] = [];
}
if (sourceNode.children === undefined) { if (sourceNode.children === undefined) {
sourceNode.children = []; sourceNode.children = context.state[TOTAL_TREE_DATA][path];
} }
sourceNode.children.push(targetNode); sourceNode.children.push(targetNode);
} }
if (targetNode.type === 0) { if (targetNode.type === 1) {
context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = []; context.state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = [];
} }
targetNode.isLeaf = targetNode.type === 0; targetNode.isLeaf = targetNode.type === 0;

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<a :href="pinObj.url" v-if="pinObj" class="pinBookmarkItem" target="_blank"> <a :href="pinObj.url" v-if="pinObj" class="pinBookmarkItem">
<img :src="pinObj.icon.length > 0 ? pinObj.icon : '/favicon.ico'" class="icon" /> <img :src="pinObj.icon.length > 0 ? pinObj.icon : '/favicon.ico'" class="icon" />
<span class="text" :title="pinObj.name">{{ pinObj.name }}</span> <span class="text" :title="pinObj.name">{{ pinObj.name }}</span>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">

View File

@ -1,16 +1,51 @@
<template> <template>
<div class="ssoAddBookmark"> <div class="ssoAddBookmark">
正在添加请稍后 <div class="body">
<!-- <button @click="closeIframe">关闭</button> --> <div>
<a-input placeholder="标题" v-model="form.name" />
<a-input placeholder="网址" v-model="form.url" />
</div>
<div class="list">
<div class="path">
<div>保存路径:</div>
<a-breadcrumb>
<a-breadcrumb-item class="breadItem"><span @click="breadClick(null)"></span></a-breadcrumb-item>
<a-breadcrumb-item class="breadItem" v-for="item in breadList" :key="item.bookmarkId">
<span @click="breadClick(item)">{{ item.name.length > 4 ? item.name.substr(0, 3) + "..." : item.name }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<div class="folderList">
<div class="item" v-for="item in folderList" :key="item.bookmarkId" @click="folderClick(item)">{{ item.name }}</div>
</div>
</div>
</div>
<div class="action">
<div v-if="showAddInput" style="display: flex">
<a-input v-model="addFolderName" style="width: 8em" />
<a-button shape="circle" icon="close" @click="showAddInput = false" />
<a-button type="primary" shape="circle" icon="check" @click="addFolder" />
</div>
<a-button v-else type="link" @click="showAddInput = true">新建文件夹</a-button>
<div>
<a-button style="marging-right: 1em" type="" @click="closeIframe">取消</a-button>
<a-button type="primary" @click="addBookmark">{{ breadList.length === 0 ? "保存到根" : "保存" }}</a-button>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import HttpUtil from "@/util/HttpUtil"; import HttpUtil from "@/util/HttpUtil";
import { TREE_DATA, addNode } from "@/store/modules/treeData"; import { mapState } from "vuex";
import { TREE_DATA, TOTAL_TREE_DATA, addNode } from "@/store/modules/treeData";
export default { export default {
data() { data() {
return { return {
breadList: [],
showAddInput: false,
addFolderName: "",
form: { form: {
name: null, name: null,
url: null, url: null,
@ -21,6 +56,13 @@ export default {
}, },
}; };
}, },
computed: {
...mapState(TREE_DATA, [TOTAL_TREE_DATA]),
folderList() {
let path = this.getCurrentPath();
return this.totalTreeData[path] ? this.totalTreeData[path].filter((item) => item.type == 1) : [];
},
},
mounted() { mounted() {
// //
window.addEventListener("message", (event) => { window.addEventListener("message", (event) => {
@ -34,7 +76,7 @@ export default {
this.form.url = event.data.data.url; this.form.url = event.data.data.url;
this.form.icon = event.data.data.icon; this.form.icon = event.data.data.icon;
this.form.iconUrl = event.data.data.iconUrl; this.form.iconUrl = event.data.data.iconUrl;
this.addBookmark(); // this.addBookmark();
} }
}); });
console.log("向父节点获取数据"); console.log("向父节点获取数据");
@ -46,11 +88,73 @@ export default {
}, },
// //
async addBookmark() { async addBookmark() {
this.form.path = this.getCurrentPath();
let res = await HttpUtil.put("/bookmark", null, this.form); let res = await HttpUtil.put("/bookmark", null, this.form);
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: this.breadList.length == 0 ? null : this.breadList[this.breadList.length - 1],
targetNode: res,
});
setTimeout(this.closeIframe, 500); setTimeout(this.closeIframe, 500);
}, },
//
async breadClick(item) {
console.log(item);
//
if (item == null && this.breadList.length == 0) {
return;
}
if (item === this.breadList[this.breadList.length - 1]) {
return;
}
if (item == null) {
this.breadList = [];
} else {
let index = this.breadList.indexOf(item);
this.breadList = [...this.breadList.slice(0, index + 1)];
}
},
//
folderClick(item) {
this.form.path = item.path + "." + item.bookmarkId;
this.breadList.push(item);
},
//
async addFolder() {
let length = this.addFolderName.trim().length;
if (length == 0) {
this.$message.error("文件夹名称不为空");
return;
}
if (length > 200) {
this.$message.error("文件夹名称长度不能大于200");
return;
}
let form = {
name: this.addFolderName,
path: this.getCurrentPath(),
type: 1,
url: "",
};
let res = await HttpUtil.put("/bookmark", null, form);
await this.$store.dispatch(TREE_DATA + "/" + addNode, {
sourceNode: this.breadList.length == 0 ? null : this.breadList[this.breadList.length - 1],
targetNode: res,
});
this.addFolderName = "";
this.showAddInput = false;
this.breadList = [...this.breadList];
this.$message.success("新增成功");
},
//
getCurrentPath() {
if (this.breadList.length == 0) {
return "";
} else {
let lastOne = this.breadList[this.breadList.length - 1];
return lastOne.path + "." + lastOne.bookmarkId;
}
},
}, },
}; };
</script> </script>
@ -58,10 +162,64 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.ssoAddBookmark { .ssoAddBookmark {
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center; padding: 0.5em;
padding-bottom: 1em;
background: white; background: white;
width: 100%; width: 100%;
height: 100vh; height: 100vh;
.body {
flex: 1;
height: 0;
display: flex;
flex-direction: column;
.list {
padding-top: 1em;
display: flex;
flex-direction: column;
flex: 1;
height: 0;
.path {
display: flex;
overflow: auto;
font-size: 0.9em;
.breadItem {
cursor: pointer;
}
.breadItem:last-child {
cursor: text;
}
}
.folderList {
flex: 1;
overflow: auto;
height: 0;
margin-left: 0.5em;
.item {
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item:hover {
background: green;
}
}
}
}
.action {
padding-top: 1em;
display: flex;
justify-content: space-between;
}
} }
</style> </style>

View File

@ -11,7 +11,7 @@
</div> </div>
<div> <div>
浏览器插件 浏览器插件
<a href="/static/bookmarkBrowserPlugin.zip" download="浏览器插件.zip" target="_blank">点击下载</a> <a href="/static/bookmarkBrowserPlugin.zip" download="浏览器插件.zip" target="_blank">最新版本{{ serverConfig.map.pluginVersion }}(注意更新)</a>
,使用详情请参考使用教程 ,使用详情请参考使用教程
</div> </div>
<div>交流反馈qq群150056494,邮箱fleyx20@outlook.com</div> <div>交流反馈qq群150056494,邮箱fleyx20@outlook.com</div>
@ -34,8 +34,14 @@
</template> </template>
<script> <script>
import { mapState } from "vuex";
import { GLOBAL_CONFIG, SERVER_CONFIG } from "@/store/modules/globalConfig";
export default { export default {
name: "about", name: "about",
data() {
return { version: "" };
},
computed: { ...mapState(GLOBAL_CONFIG, [SERVER_CONFIG]) },
mounted() { mounted() {
window.qieziStatisticKey = "b74c4b571b644782a837433209827874"; window.qieziStatisticKey = "b74c4b571b644782a837433209827874";
let script = document.createElement("script"); let script = document.createElement("script");
@ -43,6 +49,12 @@ export default {
script.defer = true; script.defer = true;
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js"; script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
document.getElementsByTagName("head")[0].appendChild(script); document.getElementsByTagName("head")[0].appendChild(script);
let serverConfig = this.$store.state[GLOBAL_CONFIG + "/" + SERVER_CONFIG];
console.log(serverConfig);
if (serverConfig.map.pluginVersion) {
this.version = serverConfig.map.pluginVersion;
}
}, },
}; };
</script> </script>

View File

@ -8,7 +8,7 @@
html, html,
body { body {
padding: 0.2em; padding: 0.2em;
width: 8em; min-width: 8em;
} }
#content { #content {
color: red; color: red;
@ -23,7 +23,11 @@
<!-- <button title="同步浏览器书签到云端(覆盖云端)">同步浏览器</button> --> <!-- <button title="同步浏览器书签到云端(覆盖云端)">同步浏览器</button> -->
</div> </div>
<p id="content"></p> <p id="content"></p>
<div class="bottom">插件版本:<span id="version"></span></div> <div class="bottom">
<div>插件版本:<span id="version"></span></div>
<div>最新版本:<a href="" id="newestVersion">最新版本</a></div>
<div><a id="about" target="_blank">关于</a></span></div>
</div>
<script type="text/javascript" src="/static/js/axios.min.js"></script> <script type="text/javascript" src="/static/js/axios.min.js"></script>
<script type="text/javascript" src="/static/js/config.js"></script> <script type="text/javascript" src="/static/js/config.js"></script>
<script type="text/javascript" src="/popup/index.js"></script> <script type="text/javascript" src="/popup/index.js"></script>

View File

@ -9,7 +9,13 @@ var action = document.getElementById("action");
//初始化 //初始化
login.href = bookmarkHost + "/manage/sso/auth"; login.href = bookmarkHost + "/manage/sso/auth";
document.getElementById("version").innerText = version; document.getElementById("version").innerText = version;
document.getElementById("about").href = bookmarkHost + "/public/about";
sendToBg("getToken", null); sendToBg("getToken", null);
let newestBlock = document.getElementById("newestVersion");
newestBlock.href = bookmarkHost + "/static/bookmarkBrowserPlugin.zip";
let res = await axios.get("/common/config/global");
console.log(res);
newestBlock.innerText = res.data.data.map.pluginVersion;
})(); })();
/** /**

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.1"; var version = "0.1.2";
window.token = localStorage.getItem('token'); window.token = localStorage.getItem('token');
axios.defaults.baseURL = bookmarkHost + '/bookmark/api'; axios.defaults.baseURL = bookmarkHost + '/bookmark/api';

View File

@ -62,7 +62,7 @@ async function addBookmark (data) {
document.getElementsByTagName("body")[0].appendChild(addBlockDiv); document.getElementsByTagName("body")[0].appendChild(addBlockDiv);
iframe = document.createElement("iframe"); iframe = document.createElement("iframe");
iframe.src = bookmarkHost + "/noHead/addBookmark?token=" + data.token; iframe.src = bookmarkHost + "/noHead/addBookmark?token=" + data.token;
iframe.setAttribute("style", "width:70%;min-height:60vh;margin-left:15%;margin-top:10vh;padding:0;border:0;border-radius:10px"); iframe.setAttribute("style", "width:640px;display:block;height:80vh;margin:0 auto;margin-top:10vh;padding:0;border:0;border-radius:10px");
addBlockDiv.appendChild(iframe); addBlockDiv.appendChild(iframe);
} }