feat:书签,搜索基本完成
This commit is contained in:
parent
f968a41303
commit
91d69aab6a
14
bookMarkService/business/api/pom.xml
Normal file
14
bookMarkService/business/api/pom.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>bookmark-business</artifactId>
|
||||
<groupId>com.fanxb</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>bookmark-business-api</artifactId>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
package com.fanxb.bookmark.business.api;
|
||||
public interface UserApi {
|
||||
/**
|
||||
* 版本自增
|
||||
* @param userId 用户id
|
||||
*/
|
||||
void versionPlus(int userId);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 用于模块间的方法相互调用,具体使用如下:
|
||||
* 1. 首先在business模块下建立interface接口,然后在个具体模块中实现
|
||||
* 2. 最后诸如interface,就能实现同级模块间的直接调用
|
||||
*/
|
||||
package com.fanxb.bookmark.business.api;
|
@ -12,6 +12,11 @@
|
||||
<artifactId>bookmark-business-bookmark</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fanxb</groupId>
|
||||
<artifactId>bookmark-business-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--html文件解析-->
|
||||
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
|
||||
<dependency>
|
||||
|
@ -35,10 +35,7 @@ public class PinyinUpdateConsumer implements RedisConsumer {
|
||||
if (CollectionUtil.isEmpty(bookmarks)) {
|
||||
return;
|
||||
}
|
||||
List<String> resList = pinYinService.changeStrings(bookmarks.stream().map(Bookmark::getName).collect(Collectors.toList()));
|
||||
for (int i = 0, size = bookmarks.size(); i < size; i++) {
|
||||
bookmarkDao.updateSearchKey(bookmarks.get(i).getBookmarkId(), resList.get(i));
|
||||
}
|
||||
|
||||
//更新本用户书签更新版本
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_UPDATE_VERSION, bookmarks.get(0).getUserId());
|
||||
}
|
||||
|
@ -184,6 +184,14 @@ public interface BookmarkDao extends BaseMapper<Bookmark> {
|
||||
@Update("update bookmark set searchKey=#{searchKey} where bookmarkId=#{bookmarkId}")
|
||||
void updateSearchKey(@Param("bookmarkId") int bookmarkId, @Param("searchKey") String searchKey);
|
||||
|
||||
/**
|
||||
* 批量更新searchKey
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2020/3/22 22:08
|
||||
*/
|
||||
void updateSearchKeyBatch(List<Bookmark> list);
|
||||
|
||||
/**
|
||||
* 分页获取所有的书签
|
||||
*
|
||||
|
@ -2,6 +2,7 @@ package com.fanxb.bookmark.business.bookmark.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fanxb.bookmark.business.api.UserApi;
|
||||
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
|
||||
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
|
||||
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
|
||||
@ -11,7 +12,6 @@ import com.fanxb.bookmark.business.bookmark.service.PinYinService;
|
||||
import com.fanxb.bookmark.common.constant.EsConstant;
|
||||
import com.fanxb.bookmark.common.constant.RedisConstant;
|
||||
import com.fanxb.bookmark.common.entity.Bookmark;
|
||||
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
|
||||
import com.fanxb.bookmark.common.util.EsUtil;
|
||||
import com.fanxb.bookmark.common.util.RedisUtil;
|
||||
import com.fanxb.bookmark.common.util.UserContextHolder;
|
||||
@ -43,16 +43,20 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
public class BookmarkServiceImpl implements BookmarkService {
|
||||
|
||||
@Autowired
|
||||
private BookmarkDao bookmarkDao;
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
@Autowired
|
||||
private PinYinService pinYinService;
|
||||
private final BookmarkDao bookmarkDao;
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final PinYinService pinYinService;
|
||||
private final UserApi userApi;
|
||||
private final EsUtil esUtil;
|
||||
|
||||
@Autowired
|
||||
private EsUtil esUtil;
|
||||
|
||||
public BookmarkServiceImpl(BookmarkDao bookmarkDao, StringRedisTemplate redisTemplate, PinYinService pinYinService, UserApi userApi, EsUtil esUtil) {
|
||||
this.bookmarkDao = bookmarkDao;
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.pinYinService = pinYinService;
|
||||
this.userApi = userApi;
|
||||
this.esUtil = esUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@ -77,10 +81,20 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
dealBookmark(userId, elements.get(i), path, sortBase + count + i - 1, bookmarks);
|
||||
}
|
||||
}
|
||||
updateVersion(userId);
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_INSERT_ES, bookmarks);
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_PINYIN_CHANGE, bookmarks);
|
||||
|
||||
//每一千条处理插入一次
|
||||
List<Bookmark> tempList = new ArrayList<>(1000);
|
||||
for(int i=0;i<bookmarks.size();i++){
|
||||
tempList.add(bookmarks.get(i));
|
||||
if(tempList.size()==1000 || i==bookmarks.size()-1){
|
||||
List<String> resList = pinYinService.changeStrings(bookmarks.stream().map(Bookmark::getName).collect(Collectors.toList()));
|
||||
for(int j=0;i<resList.size();i++){
|
||||
tempList.get(j).setSearchKey(resList.get(j));
|
||||
}
|
||||
bookmarkDao.updateSearchKeyBatch(tempList);
|
||||
tempList.clear();
|
||||
}
|
||||
}
|
||||
userApi.versionPlus(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,7 +193,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
set.addAll(bookmarkIdList.stream().map(String::valueOf).collect(Collectors.toSet()));
|
||||
}
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_DELETE_ES, set);
|
||||
updateVersion(userId);
|
||||
userApi.versionPlus(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -197,7 +211,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
if (bookmark.getType() == 0) {
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_INSERT_ES, Collections.singleton(bookmark));
|
||||
}
|
||||
updateVersion(userId);
|
||||
userApi.versionPlus(userId);
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
@ -210,7 +224,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
if (bookmark.getType() == 0) {
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_INSERT_ES, Collections.singleton(bookmark));
|
||||
}
|
||||
updateVersion(userId);
|
||||
userApi.versionPlus(userId);
|
||||
}
|
||||
|
||||
|
||||
@ -231,7 +245,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
}
|
||||
//更新被移动节点的path和sort
|
||||
bookmarkDao.updatePathAndSort(userId, body.getBookmarkId(), body.getTargetPath(), body.getSort());
|
||||
updateVersion(userId);
|
||||
userApi.versionPlus(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -256,16 +270,5 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
return bookmarkDao.selectPopular(UserContextHolder.get().getUserId(), num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述: 向mq发送消息通知,书签数据更新
|
||||
*
|
||||
* @param userId userId
|
||||
* @author fanxb
|
||||
* @date 2020/5/10 12:07
|
||||
*/
|
||||
private void updateVersion(int userId) {
|
||||
RedisUtil.addToMq(RedisConstant.BOOKMARK_UPDATE_VERSION, userId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -111,5 +111,15 @@
|
||||
limit ${start}, ${size}
|
||||
</select>
|
||||
|
||||
<update id="updateSearchKeyBatch">
|
||||
UPDATE `bookmark` a JOIN
|
||||
(
|
||||
<foreach collection="list" item="item" separator="union">
|
||||
select #{item.bookmarkId} as bookmarkId,#{item.searchKey} as searchKey
|
||||
</foreach>
|
||||
) b USING(bookmarkId)
|
||||
SET a.searchKey=b.searchKey;
|
||||
</update>
|
||||
|
||||
|
||||
</mapper>
|
@ -14,6 +14,7 @@
|
||||
<modules>
|
||||
<module>user</module>
|
||||
<module>bookmark</module>
|
||||
<module>api</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
@ -11,5 +11,12 @@
|
||||
|
||||
<artifactId>bookmark-business-user</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fanxb</groupId>
|
||||
<artifactId>bookmark-business-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,21 @@
|
||||
package com.fanxb.bookmark.business.user.apiImpl;
|
||||
|
||||
import com.fanxb.bookmark.business.api.UserApi;
|
||||
import com.fanxb.bookmark.business.user.dao.UserDao;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class UserApiImpl implements UserApi {
|
||||
private final UserDao userDao;
|
||||
|
||||
@Autowired
|
||||
public UserApiImpl(UserDao userDao) {
|
||||
this.userDao = userDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void versionPlus(int userId) {
|
||||
userDao.updateUserVersion(userId);
|
||||
}
|
||||
}
|
@ -20,11 +20,11 @@ public class UserInfoUpdateConsumer implements RedisConsumer {
|
||||
|
||||
@Override
|
||||
public void deal(String message) {
|
||||
int userId = Integer.parseInt(message);
|
||||
if (userId == -1) {
|
||||
userDao.updateAllBookmarkUpdateVersion();
|
||||
} else {
|
||||
userDao.updateLastBookmarkUpdateTime(userId);
|
||||
}
|
||||
// int userId = Integer.parseInt(message);
|
||||
// if (userId == -1) {
|
||||
// userDao.updateAllBookmarkUpdateVersion();
|
||||
// } else {
|
||||
// userDao.updateLastBookmarkUpdateTime(userId);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ public interface UserDao {
|
||||
* @date 2020/1/26 下午3:47
|
||||
*/
|
||||
@Update("update user set version=version+1 where userId=#{userId}")
|
||||
void updateLastBookmarkUpdateTime(int userId);
|
||||
void updateUserVersion(int userId);
|
||||
|
||||
/**
|
||||
* 功能描述: 更新所有用户的更新时间
|
||||
|
@ -1 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fanxb</groupId>
<artifactId>bookMarkService</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common</module>
<module>business</module>
<module>web</module>
</modules>
<properties>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<!--定义es版本号,因为spring-boot-start-parent中引入了es的依赖,会导致es的版本问题,无法正常使用high-level-client-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.2.0</version>
</dependency>
<!--<!– https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-client –>-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.2.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fanxb</groupId>
<artifactId>bookMarkService</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common</module>
<module>business</module>
<module>web</module>
<module>api</module>
</modules>
<properties>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<!--定义es版本号,因为spring-boot-start-parent中引入了es的依赖,会导致es的版本问题,无法正常使用high-level-client-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.2.0</version>
</dependency>
<!--<!– https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-client –>-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.2.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
|
@ -3,12 +3,14 @@ module.exports = {
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ["plugin:vue/essential", "@vue/airbnb"],
|
||||
extends: ["plugin:vue/essential"],
|
||||
parserOptions: {
|
||||
parser: "babel-eslint"
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"max-len": [0, { code: 300 }],
|
||||
"no-cycle": 0
|
||||
}
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
"ant-design-vue": "^1.6.3",
|
||||
"axios": "^0.19.2",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"clipboard": "^2.0.6",
|
||||
"core-js": "^3.6.5",
|
||||
"localforage": "^1.7.4",
|
||||
"vue": "^2.6.11",
|
||||
|
@ -16,13 +16,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<div v-else prop="file">
|
||||
<a-upload-dragger
|
||||
name="file"
|
||||
:data="{ path: form.path }"
|
||||
:headers="{ 'jwt-token': token }"
|
||||
action="/bookmark/api/bookmark/uploadBookmarkFile"
|
||||
@change="fileChange"
|
||||
>
|
||||
<a-upload-dragger name="file" :data="{ path: form.path }" :headers="{ 'jwt-token': token }" action="/bookmark/api/bookmark/uploadBookmarkFile" @change="fileChange">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<a-icon type="inbox" />
|
||||
</p>
|
||||
@ -77,6 +71,9 @@ export default {
|
||||
this.form.path = this.targetNode == null ? "" : this.targetNode.path + (this.isAdd ? "." + this.targetNode.bookmarkId : "");
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 文件提交不走这儿
|
||||
*/
|
||||
submit() {
|
||||
//名称校验
|
||||
this.loading = true;
|
||||
@ -88,27 +85,29 @@ export default {
|
||||
let res = null;
|
||||
if (this.isAdd) {
|
||||
res = await HttpUtil.put("/bookmark", null, this.form);
|
||||
res.isLeaf = res.type === 0;
|
||||
await this.$store.dispatch("treeData/addNode", { sourceNode: this.targetNode, targetNode: res });
|
||||
} else {
|
||||
this.form.bookmarkId = this.targetNode.bookmarkId;
|
||||
await HttpUtil.post("/bookmark/updateOne", null, this.form);
|
||||
this.targetNode.name = this.form.name;
|
||||
this.targetNode.url = this.form.url;
|
||||
await this.$store.dispatch("treeData/editNode", { node: this.targetNode, newName: this.form.name, newUrl: this.form.url });
|
||||
}
|
||||
this.$message.success("操作成功");
|
||||
this.$emit("close", res);
|
||||
this.$emit("close", false);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
fileChange(info) {
|
||||
if (info.file.status === "error") {
|
||||
this.$notification.error({
|
||||
message: "异常",
|
||||
description: "文件内容无法解析,确保该文件为书签文件",
|
||||
});
|
||||
} else if (info.file.status === "done") {
|
||||
this.$message.success("解析成功");
|
||||
this.$emit("close", null);
|
||||
console.log(info);
|
||||
if (info.file.status === "done") {
|
||||
if (info.file.response.code === 0) {
|
||||
this.$notification.error({
|
||||
message: "异常",
|
||||
description: "文件内容无法解析,确保该文件为书签文件",
|
||||
});
|
||||
} else {
|
||||
this.$message.success("解析成功");
|
||||
this.$emit("close", true);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -24,11 +24,13 @@
|
||||
.treeNodeItem {
|
||||
span {
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
div {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ const actions = {
|
||||
}
|
||||
context.commit("isIniting", true);
|
||||
let data = await localforage.getItem(TOTAL_TREE_DATA);
|
||||
if (data == null) {
|
||||
if (!data) {
|
||||
await context.dispatch("refresh");
|
||||
} else {
|
||||
context.commit(TOTAL_TREE_DATA, data);
|
||||
@ -62,7 +62,6 @@ const actions = {
|
||||
try {
|
||||
if (context.state.isInit && context.state.isIniting == false) {
|
||||
clearInterval(timer);
|
||||
console.log(timer);
|
||||
resolve();
|
||||
}
|
||||
} catch (err) {
|
||||
@ -79,15 +78,15 @@ const actions = {
|
||||
}
|
||||
Object.values(treeData).forEach(item =>
|
||||
item.forEach(item1 => {
|
||||
item1.isLeaf = item.type === 0;
|
||||
item1.isLeaf = item1.type === 0;
|
||||
item1.class = "treeNodeItem";
|
||||
item1.scopedSlots = { title: "nodeTitle" };
|
||||
})
|
||||
);
|
||||
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||
await context.dispatch("updateVersion", userInfo.version);
|
||||
context.commit(TOTAL_TREE_DATA, treeData);
|
||||
await localforage.setItem(TOTAL_TREE_DATA, treeData);
|
||||
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||
context.commit(VERSION, userInfo.version);
|
||||
await localforage.setItem(VERSION, userInfo.version);
|
||||
},
|
||||
//清除缓存数据
|
||||
async clear(context) {
|
||||
@ -98,6 +97,9 @@ const actions = {
|
||||
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;
|
||||
@ -161,13 +163,85 @@ const actions = {
|
||||
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 }) {
|
||||
node.name = newName;
|
||||
node.url = newUrl;
|
||||
commit(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
|
||||
await dispatch("updateVersion", null);
|
||||
await localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
totalTreeData(state, totalTreeData) {
|
||||
localforage.setItem(TOTAL_TREE_DATA, totalTreeData);
|
||||
state.totalTreeData = totalTreeData;
|
||||
},
|
||||
isInit(state, isInit) {
|
||||
@ -176,60 +250,9 @@ const mutations = {
|
||||
isIniting(state, isIniting) {
|
||||
state.isIniting = isIniting;
|
||||
},
|
||||
deleteData(state, { pathList, bookmarkIdList }) {
|
||||
//待删除的书签
|
||||
let bookmarkIdSet = new Set();
|
||||
bookmarkIdList.forEach(item => bookmarkIdSet.add(item));
|
||||
//删除子节点
|
||||
pathList.forEach(item => {
|
||||
delete state[TOTAL_TREE_DATA][item];
|
||||
Object.keys(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(state.totalTreeData).forEach(item => {
|
||||
let list = state.totalTreeData[item];
|
||||
for (let i = list.length - 1; i >= 0; i--) {
|
||||
if (bookmarkIdSet.has(list[i].bookmarkId)) {
|
||||
list.splice(i, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
|
||||
},
|
||||
/**
|
||||
* 新增书签、文件夹
|
||||
*/
|
||||
addNode(state, { sourceNode, targetNode }) {
|
||||
if (sourceNode === null) {
|
||||
if (state[TOTAL_TREE_DATA][""] === undefined) {
|
||||
state[TOTAL_TREE_DATA][""] = [];
|
||||
}
|
||||
state[TOTAL_TREE_DATA][""].push(targetNode);
|
||||
} else {
|
||||
if (sourceNode.children === undefined) {
|
||||
sourceNode.children = [];
|
||||
}
|
||||
sourceNode.children.push(targetNode);
|
||||
}
|
||||
if (targetNode.type === 0) {
|
||||
state[TOTAL_TREE_DATA][targetNode.path + "." + targetNode.bookmarkId] = [];
|
||||
}
|
||||
localforage.setItem(TOTAL_TREE_DATA, state[TOTAL_TREE_DATA]);
|
||||
},
|
||||
/**
|
||||
* 更新版本文件
|
||||
*/
|
||||
|
||||
version(state, version) {
|
||||
console.log("version:", version);
|
||||
if (version == null && state.version != null) {
|
||||
state.version = state.version + 1;
|
||||
} else {
|
||||
state.version = version;
|
||||
}
|
||||
localforage.setItem(VERSION, state[VERSION]);
|
||||
state[VERSION] = version;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -34,8 +34,9 @@ export default {
|
||||
console.log("globalConfig加载完毕");
|
||||
await this.$store.dispatch("treeData/init");
|
||||
console.log("treeData加载完毕");
|
||||
console.log("state数据:", this.$store.state);
|
||||
await this.checkVersion();
|
||||
this.timer = setInterval(this.checkVersion, 60 * 1000);
|
||||
this.timer = setInterval(this.checkVersion, 10 * 1000);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.timer != null) {
|
||||
|
@ -11,13 +11,10 @@
|
||||
<a-tooltip title="多选">
|
||||
<a-button type="primary" shape="circle" icon="check" @click="switchMul" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
v-if="
|
||||
<a-tooltip v-if="
|
||||
(checkedKeys.length === 0 && (currentSelect == null || currentSelect.type === 1)) ||
|
||||
(checkedKeys.length === 1 && checkedNodes[0].type === 1)
|
||||
"
|
||||
title="添加书签"
|
||||
>
|
||||
" title="添加书签">
|
||||
<a-button type="primary" shape="circle" icon="plus" @click="addData" />
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="currentSelect || checkedKeys.length === 1" title="编辑书签">
|
||||
@ -26,38 +23,32 @@
|
||||
<a-tooltip v-if="moveShow" title="移动书签">
|
||||
<a-button type="primary" shape="circle" icon="scissor" />
|
||||
</a-tooltip>
|
||||
<a-popconfirm
|
||||
v-if="checkedKeys.length > 0 || currentSelect"
|
||||
title="此操作同时也会删除子节点数据,确认?"
|
||||
ok-text="是"
|
||||
cancel-text="否"
|
||||
@confirm="deleteBookmarks"
|
||||
>
|
||||
<a-popconfirm v-if="checkedKeys.length > 0 || currentSelect" title="此操作同时也会删除子节点数据,确认?" ok-text="是" cancel-text="否" @confirm="deleteBookmarks">
|
||||
<a-tooltip title="删除书签">
|
||||
<a-button type="danger" shape="circle" icon="delete" />
|
||||
</a-tooltip>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
<a-empty v-if="treeData.length == 0 && loading == false" description="无数据,点击上方 + 新增"></a-empty>
|
||||
<a-tree
|
||||
v-else
|
||||
:tree-data="treeData"
|
||||
:loaded-keys="loadedKeys"
|
||||
:selected-keys="currentSelect ? [currentSelect.bookmarkId] : []"
|
||||
:load-data="loadData"
|
||||
:checked-keys="checkedKeys"
|
||||
:replace-fields="replaceFields"
|
||||
:expandedKeys="expandedKeys"
|
||||
@select="select"
|
||||
@expand="expand"
|
||||
@check="check"
|
||||
blockNode
|
||||
:checkable="mulSelect"
|
||||
checkStrictly
|
||||
:draggable="!isPhone"
|
||||
@drop="onDrop"
|
||||
@rightClick="rightClick"
|
||||
/>
|
||||
<a-empty v-if="treeData.length == 0" description="无数据,点击上方 + 新增"></a-empty>
|
||||
<a-tree v-else :tree-data="treeData" :loaded-keys="loadedKeys" :selected-keys="currentSelect ? [currentSelect.bookmarkId] : []" :load-data="loadData" :checked-keys="checkedKeys" :replace-fields="replaceFields" :expandedKeys="expandedKeys" @select="select" @expand="expand" @check="check" blockNode :checkable="mulSelect" checkStrictly :draggable="!isPhone" @drop="onDrop">
|
||||
<a-dropdown :trigger="['contextmenu']" slot="nodeTitle" slot-scope="rec">
|
||||
<div class="titleContext">
|
||||
<a-icon type="folder" v-if="!rec.dataRef.isLeaf" />
|
||||
<img v-else-if="rec.dataRef.icon.length>0" :src="rec.dataRef.icon" />
|
||||
<a-icon type="book" v-else />
|
||||
<span @click.prevent style="display:inline-block;min-width:50%;padding-left:0.4em">
|
||||
{{rec.dataRef.name}}
|
||||
</span>
|
||||
</div>
|
||||
<a-menu slot="overlay" @click="rightClick($event,rec.dataRef)">
|
||||
<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 key="edit">编辑</a-menu-item>
|
||||
<a-menu-item key="delete">删除</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
|
||||
</a-tree>
|
||||
<!-- 新增、修改 -->
|
||||
<a-modal v-model="addModal.show" :title="addModal.isAdd ? '新增' : '编辑'" :footer="null">
|
||||
<add-bookmark v-if="addModal.show" :isAdd="addModal.isAdd" :targetNode="addModal.targetNode" @close="close" />
|
||||
@ -70,32 +61,34 @@ 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 ClipboardJS from "clipboard";
|
||||
export default {
|
||||
name: "BookmarkManage",
|
||||
components: { AddBookmark, Search },
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
expandedKeys: [], //已展开的keys
|
||||
checkedKeys: [], //已选择的keys,多选框优先级高于树节点选择
|
||||
checkedNodes: [], //选中的节点数据
|
||||
loadedKeys: [], //已加载数据
|
||||
expandedKeys: [], // 已展开的keys
|
||||
checkedKeys: [], // 已选择的keys,多选框优先级高于树节点选择
|
||||
checkedNodes: [], // 选中的节点数据
|
||||
loadedKeys: [], // 已加载数据
|
||||
replaceFields: {
|
||||
title: "name",
|
||||
key: "bookmarkId",
|
||||
},
|
||||
mulSelect: false, //多选框是否显示
|
||||
currentSelect: null, //当前树的选择项
|
||||
loading: true, //是否显示loading
|
||||
moveShow: false, //是否显示移动节点
|
||||
//新增书签弹窗相关
|
||||
mulSelect: false, // 多选框是否显示
|
||||
currentSelect: null, // 当前树的选择项
|
||||
loading: true, // 是否显示loading
|
||||
moveShow: false, // 是否显示移动节点
|
||||
// 新增书签弹窗相关
|
||||
addModal: {
|
||||
show: false,
|
||||
//新增、修改目标数据,null说明向根节点增加数据
|
||||
// 新增、修改目标数据,null说明向根节点增加数据
|
||||
targetNode: null,
|
||||
//是否为新增动作
|
||||
// 是否为新增动作
|
||||
isAdd: false,
|
||||
},
|
||||
copyBoard: null, //剪贴板对象
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -106,6 +99,21 @@ export default {
|
||||
await this.$store.dispatch("treeData/ensureDataOk");
|
||||
this.treeData = this.totalTreeData[""];
|
||||
this.loading = false;
|
||||
//初始化clipboard
|
||||
this.copyBoard = new ClipboardJS(".copy-to-board", {
|
||||
text: function (trigger) {
|
||||
return trigger.attributes.data.nodeValue;
|
||||
},
|
||||
});
|
||||
this.copyBoard.on("success", (e) => {
|
||||
this.$message.success("复制成功");
|
||||
e.clearSelection();
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
if (this.copyBoard != null) {
|
||||
this.copyBoard.destroy();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
@ -118,9 +126,7 @@ export default {
|
||||
if (!this.totalTreeData[newPath]) {
|
||||
this.totalTreeData[newPath] = [];
|
||||
}
|
||||
this.totalTreeData[newPath].forEach((item) => (item.isLeaf = item.type === 0));
|
||||
data.children = this.totalTreeData[newPath];
|
||||
this.treeData = [...this.treeData];
|
||||
this.loadedKeys.push(data.bookmarkId);
|
||||
resolve();
|
||||
});
|
||||
@ -130,7 +136,7 @@ export default {
|
||||
this.loading = true;
|
||||
await this.$store.dispatch("treeData/refresh");
|
||||
}
|
||||
this.treeData = [...this.totalTreeData[""]];
|
||||
this.treeData = this.totalTreeData[""];
|
||||
this.expandedKeys = [];
|
||||
this.checkedKeys = [];
|
||||
this.checkedNodes = [];
|
||||
@ -140,7 +146,7 @@ export default {
|
||||
},
|
||||
expand(expandedKeys, { expanded, node }) {
|
||||
if (expanded) {
|
||||
let item = node.dataRef;
|
||||
const item = node.dataRef;
|
||||
this.expandedKeys = [
|
||||
...item.path
|
||||
.split(".")
|
||||
@ -152,15 +158,8 @@ export default {
|
||||
this.expandedKeys.pop();
|
||||
}
|
||||
},
|
||||
rightClick({ node }) {
|
||||
if (this.currentSelect === node.dataRef) {
|
||||
this.currentSelect = null;
|
||||
} else {
|
||||
this.currentSelect = node.dataRef;
|
||||
}
|
||||
},
|
||||
check(key, { checked, node }) {
|
||||
let item = node.dataRef;
|
||||
const item = node.dataRef;
|
||||
if (checked) {
|
||||
this.checkedKeys.push(item.bookmarkId);
|
||||
this.checkedNodes.push(item);
|
||||
@ -173,14 +172,14 @@ export default {
|
||||
}
|
||||
},
|
||||
select(key, { selected, node }) {
|
||||
let item = node.dataRef;
|
||||
const item = node.dataRef;
|
||||
if (item.type === 1) {
|
||||
if (selected && this.mulSelect === false) {
|
||||
this.currentSelect = item;
|
||||
} else {
|
||||
this.currentSelect = null;
|
||||
}
|
||||
let index = this.expandedKeys.indexOf(item.bookmarkId);
|
||||
const index = this.expandedKeys.indexOf(item.bookmarkId);
|
||||
if (index > -1) {
|
||||
this.expandedKeys.splice(index, 1);
|
||||
} else {
|
||||
@ -197,6 +196,7 @@ export default {
|
||||
window.open(item.url);
|
||||
}
|
||||
},
|
||||
//切换多选
|
||||
switchMul() {
|
||||
if (this.mulSelect) {
|
||||
this.mulSelect = false;
|
||||
@ -207,9 +207,9 @@ export default {
|
||||
}
|
||||
},
|
||||
async deleteBookmarks() {
|
||||
//删除,如果有多选,删除多选,否则删除树节点选中项
|
||||
const bookmarkIdList = [],
|
||||
pathList = [];
|
||||
// 删除,如果有多选,删除多选,否则删除树节点选中项
|
||||
const bookmarkIdList = [];
|
||||
const pathList = [];
|
||||
if (this.checkedNodes) {
|
||||
this.checkedNodes.forEach((item) =>
|
||||
item.type === 1 ? pathList.push(item.path + "." + item.bookmarkId) : bookmarkIdList.push(item.bookmarkId)
|
||||
@ -230,10 +230,10 @@ export default {
|
||||
pathList,
|
||||
bookmarkIdList,
|
||||
});
|
||||
this.$store.commit("treeData/deleteData", { pathList, bookmarkIdList });
|
||||
this.$store.dispatch("treeData/deleteData", { pathList, bookmarkIdList });
|
||||
//删除已经被删除的数据
|
||||
pathList.forEach((item) => {
|
||||
let id = parseInt(item.split(".").reverse()[0]);
|
||||
const id = parseInt(item.split(".").reverse()[0]);
|
||||
let index = this.loadedKeys.indexOf(id);
|
||||
if (index > -1) {
|
||||
this.loadedKeys.splice(index, 1);
|
||||
@ -243,7 +243,6 @@ export default {
|
||||
this.expandedKeys.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.$store.commit("treeData/version", null);
|
||||
this.checkedNodes = [];
|
||||
this.checkedKeys = [];
|
||||
this.currentSelect = null;
|
||||
@ -276,25 +275,14 @@ export default {
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
* @param data data为null说明需要刷新书签树,不为浪即为修改/新增的对象
|
||||
* @param isUpload 说明为上传书签文件,需要刷新缓存数据
|
||||
*/
|
||||
async close(data) {
|
||||
console.log(data);
|
||||
if (this.addModal.isAdd) {
|
||||
//新增
|
||||
if (data == null) {
|
||||
//上传书签文件
|
||||
this.refresh(true);
|
||||
} else {
|
||||
//单个新增
|
||||
this.$store.commit("treeData/addNode", { sourceNode: this.addModal.targetNode, targetNode: data });
|
||||
this.treeData = [...this.totalTreeData[""]];
|
||||
}
|
||||
async close(isUpload) {
|
||||
if (isUpload) {
|
||||
this.refresh(true);
|
||||
} else {
|
||||
//编辑
|
||||
this.treeData = [...this.totalTreeData[""]];
|
||||
this.treeData.__ob__.dep.notify();
|
||||
}
|
||||
this.$store.commit("treeData/version", null);
|
||||
this.addModal = {
|
||||
show: false,
|
||||
targetNode: null,
|
||||
@ -308,20 +296,46 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
let body = await this.$store.dispatch("treeData/moveNode", info);
|
||||
const body = await this.$store.dispatch("treeData/moveNode", info);
|
||||
try {
|
||||
await HttpUtil.post("/bookmark/moveNode", null, body);
|
||||
this.$message.success("移动完成");
|
||||
this.treeData = [...this.totalTreeData[""]];
|
||||
this.$store.commit("treeData/version", null);
|
||||
this.treeData.__ob__.dep.notify();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.$message.error("后台移动失败,将于2s后刷新页面,以免前后台数据不一致");
|
||||
// setTimeout(() => window.location.reload(), 2000);
|
||||
setTimeout(() => this.refresh(true), 2000);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
//右键点击
|
||||
async rightClick({ key }, item) {
|
||||
if (key === "copy") {
|
||||
return;
|
||||
}
|
||||
//清楚多选状态,并设置当前选中
|
||||
this.mulSelect = false;
|
||||
this.checkedKeys = [];
|
||||
this.checkedNodes = [];
|
||||
this.currentSelect = item;
|
||||
if (key === "add") {
|
||||
this.addData();
|
||||
} else if (key === "delete") {
|
||||
this.$confirm({
|
||||
title: "确认删除?",
|
||||
content: "将删除当前节点和所有子节点,且不可恢复",
|
||||
onOk: () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await this.deleteBookmarks();
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
});
|
||||
} else if (key === "edit") {
|
||||
this.editData();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -338,4 +352,8 @@ export default {
|
||||
font-size: 0.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.titleContext {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user