feat:首页完成

This commit is contained in:
fanxb 2022-03-29 17:16:29 +08:00
parent ee22c4b77a
commit 0a54e784db
18 changed files with 227 additions and 89 deletions

View File

@ -1,34 +0,0 @@
package com.fanxb.bookmark.business.bookmark.consumer;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/29
* Time: 13:08
*/
@MqConsumer(RedisConstant.BOOKMARK_DELETE_ES)
public class BookmarkDeleteEsConsumer implements RedisConsumer {
@Autowired
private EsUtil esUtil;
@Override
public void deal(String message) {
List<String> strings = JSONArray.parseArray(message, String.class);
if (CollectionUtil.isEmpty(strings)) {
return;
}
esUtil.deleteBatch(EsConstant.BOOKMARK_INDEX, strings);
}
}

View File

@ -0,0 +1,45 @@
package com.fanxb.bookmark.business.bookmark.consumer;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/29
* Time: 13:08
*/
@MqConsumer(RedisConstant.BOOKMARK_DELETE_ES)
public class BookmarkDeleteMessageConsumer implements RedisConsumer {
private final PinBookmarkDao pinBookmarkDao;
private final EsUtil esUtil;
@Autowired
public BookmarkDeleteMessageConsumer(PinBookmarkDao pinBookmarkDao, EsUtil esUtil) {
this.pinBookmarkDao = pinBookmarkDao;
this.esUtil = esUtil;
}
@Override
public void deal(String message) {
BookmarkDeleteMessage obj = JSON.parseObject(message, BookmarkDeleteMessage.class);
//删除首页固定的数据
pinBookmarkDao.deleteUnExistBookmark(obj.getUserId());
//删除es数据
if (CollectionUtil.isNotEmpty(obj.getBookmarkIds())) {
esUtil.deleteBatch(EsConstant.BOOKMARK_INDEX, obj.getBookmarkIds());
}
}
}

View File

@ -33,4 +33,12 @@ public interface PinBookmarkDao extends BaseMapper<PInBookmarkPo> {
*/
@Select("select ifnull(max(sort),0) from pin_bookmark where userId=#{userId}")
int getUserMaxSort(int userId);
/**
* 删除书签后需要清理此表,将不存在的书签删除
*
* @param userId userId
* @author fanxb
*/
void deleteUnExistBookmark(int userId);
}

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.business.bookmark.entity.redis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Collection;
import java.util.List;
/**
* @author fanxb
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookmarkDeleteMessage {
/**
* 用户id
*/
private int userId;
/**
* 批量删除的书签id
*/
private Collection<String> bookmarkIds;
}

View File

@ -7,6 +7,7 @@ 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;
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.business.bookmark.service.PinYinService;
@ -187,7 +188,7 @@ public class BookmarkServiceImpl implements BookmarkService {
bookmarkDao.deleteUserBookmark(userId, bookmarkIdList);
set.addAll(bookmarkIdList.stream().map(String::valueOf).collect(Collectors.toSet()));
}
RedisUtil.addToMq(RedisConstant.BOOKMARK_DELETE_ES, set);
RedisUtil.addToMq(RedisConstant.BOOKMARK_DELETE_ES, new BookmarkDeleteMessage(userId, set));
userApi.versionPlus(userId);
}

View File

@ -1,5 +1,7 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
@ -7,6 +9,8 @@ import com.fanxb.bookmark.business.bookmark.entity.vo.HomePinItemVo;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.business.bookmark.service.HomePinService;
import com.fanxb.bookmark.common.entity.UserContext;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -14,6 +18,7 @@ import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -43,8 +48,11 @@ public class HomePinServiceImpl implements HomePinService {
List<HomePinItemVo> res = new ArrayList<>(MAX_PIN);
if (UserContextHolder.get() != null) {
res.addAll(pinBookmarkDao.selectUserPin(UserContextHolder.get().getUserId()));
if (res.size() < MAX_PIN) {
res.addAll(bookmarkDao.selectPopular(UserContextHolder.get().getUserId(), MAX_PIN - res.size()).stream()
if (res.size() < MAX_PIN - 1) {
//需要从取到的书签数据中排除已经固定到首页的数据
Set<Integer> existBookmark = res.stream().map(HomePinItemVo::getBookmarkId).collect(Collectors.toSet());
List<Bookmark> bookmarks = bookmarkDao.selectPopular(UserContextHolder.get().getUserId(), MAX_PIN - 1);
res.addAll(bookmarks.stream().filter(item -> !existBookmark.contains(item.getBookmarkId())).limit(MAX_PIN - res.size() - 1)
.map(item -> new HomePinItemVo(null, item.getBookmarkId(), item.getName(), item.getUrl(), item.getIcon())).collect(Collectors.toList()));
}
}
@ -54,6 +62,10 @@ public class HomePinServiceImpl implements HomePinService {
@Override
public PInBookmarkPo addOne(PInBookmarkPo po) {
int userId = UserContextHolder.get().getUserId();
long count = pinBookmarkDao.selectCount(new QueryWrapper<PInBookmarkPo>().eq("userId", userId));
if (count > MAX_PIN) {
throw new CustomException("固定数量已超过最大限制:" + MAX_PIN);
}
po.setUserId(userId);
po.setCreateDate(System.currentTimeMillis());
po.setSort(pinBookmarkDao.getUserMaxSort(userId) + 1);

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao">
<delete id="deleteUnExistBookmark">
delete
a
from pin_bookmark a
left join bookmark b on
a.bookmarkId = b.bookmarkId
where a.userId = #{userId}
and b.bookmarkId is null
</delete>
</mapper>

View File

@ -44,7 +44,10 @@ export default {
name: "addBookmark",
props: {
isAdd: Boolean, //
addType: String, //
addType: {
type: String,
default: "bookmark",
}, //
targetNode: Object,
},
data() {
@ -69,13 +72,15 @@ export default {
},
created() {
console.log(this.isAdd, this.targetNode);
if (!this.isAdd) {
if (this.isAdd) {
this.form.type = this.addType;
} else {
this.form.type = this.targetNode.type == 0 ? "bookmark" : "folder";
this.form.name = this.targetNode.name;
this.form.url = this.form.type === "bookmark" ? this.targetNode.url : "";
}
this.token = this.$store.state.globalConfig.token;
this.form.path = this.targetNode == null ? "" : this.targetNode.path + (this.isAdd ? "." + this.targetNode.bookmarkId : "");
this.form.path = !this.targetNode ? "" : this.targetNode.path + (this.isAdd ? "." + this.targetNode.bookmarkId : "");
},
methods: {
/**
@ -101,7 +106,7 @@ export default {
await this.$store.dispatch("treeData/editNode", { node: this.targetNode, newName: this.form.name, newUrl: this.form.url, newIcon });
}
this.$message.success("操作成功");
this.$emit("close", this.form.type);
this.$emit("close", this.form.type, res);
this.loading = false;
});
},

View File

@ -1,5 +1,7 @@
<template>
<div class="bottom">这是首页的底部</div>
<div class="bottom">
<router-link to="/public/about">关于</router-link>
</div>
</template>
<script>
@ -14,6 +16,8 @@ export default {
<style lang="less" scoped>
.bottom {
height: 0.2rem;
height: 0.4rem;
padding: 0.1rem;
text-align: right;
}
</style>

View File

@ -16,7 +16,7 @@
<router-link to="manage">书签管理</router-link>
</a-menu-item>
<a-menu-item key="personSpace">
<router-link to="/manage/personSpace">个人中心</router-link>
<router-link to="/manage/personSpace/userInfo">个人中心</router-link>
</a-menu-item>
<a-menu-item key="logout">
<a href="javascript:;">退出</a>
@ -42,7 +42,6 @@ export default {
async menuClick(item) {
if (item.key == "logout") {
await logoutClear();
// this.$router.replace("/public/login");
}
},
},

View File

@ -4,35 +4,14 @@
开源地址:
<a href="https://github.com/FleyX/bookmark" target="_blank">github.com/FleyX/bookmark</a>
&emsp;
<a href="https://github.com/FleyX/bookmark/issues" target="_blank">反馈/建议</a>
<router-link to="/public/about">关于本站</router-link>
</div>
<!-- 统计 -->
<a href="https://qiezi.fleyx.com" style="display: block; text-align: center; color: black" target="_blank">
<div id="qieziStatisticHtmlHostPv" style="display: none">
总访问次数:
<span id="qieziStatisticHtmlHostPvValue"></span>
&nbsp;
</div>
<div id="qieziStatisticHtmlHostUv" style="display: none">
&nbsp;总访客数:
<span id="qieziStatisticHtmlHostUvValue"></span>
</div>
</a>
</div>
</template>
<script>
export default {
name: "Bottom",
mounted() {
window.qieziStatisticKey = "b74c4b571b644782a837433209827874";
let script = document.createElement("script");
script.type = "text/javascript";
script.defer = true;
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
document.getElementsByTagName("head")[0].appendChild(script);
},
};
</script>

View File

@ -25,6 +25,7 @@ const routes = [
{ path: "register", component: () => import("@/views/public/register/index") },
{ path: "resetPassword", component: () => import("@/views/public/passwordReset/index") },
{ path: "oauth/github", component: () => import("@/views/public/oauth/github/index") },
{ path: "about", component: () => import("@/views/public/about/index") },
{ path: "404", component: () => import("@/views/public/notFound/index") },
]
},

View File

@ -16,6 +16,10 @@ export const HOME_PIN_LIST = "homePinList";
* 刷新首页固定标签
*/
export const refreshHomePinList = "refreshHomePinList";
/**
* 通过id获取书签数据
*/
export const getById = "getById";
export const noLoginInit = "noLoginInit";
export const loginInit = "loginInit";
export const refresh = "refresh";
@ -52,10 +56,7 @@ const state = {
};
const getters = {
/**
* 通过id获取节点数据
*/
getById: state => id => {
[getById]: state => id => {
let arr = Object.values(state[TOTAL_TREE_DATA]);
for (let i in arr) {
for (let j in arr[i]) {

View File

@ -3,9 +3,6 @@
<div class="oneLine">
<pin-bookmark-item v-for="(item, index) in lineOne" :key="index" :pinObj="item" />
</div>
<div class="oneLine">
<pin-bookmark-item v-for="(item, index) in lineTwo" :key="index" :pinObj="item" />
</div>
</div>
</template>
@ -16,7 +13,7 @@ import { TREE_DATA, HOME_PIN_LIST } from "@/store/modules/treeData";
/**
* 首页网页固定
*/
const LINE_NUM = 10;
const LINE_NUM = 20;
export default {
name: "PinBookmark",
components: { PinBookmarkItem },
@ -30,16 +27,6 @@ export default {
return this.homePinList.slice(0, LINE_NUM);
}
},
lineTwo() {
if (this.homePinList.length < LINE_NUM) {
return [];
}
if (this.homePinList.length - LINE_NUM < LINE_NUM) {
return [...this.homePinList.slice(LINE_NUM), null];
} else {
return this.homePinList.slice(LINE_NUM, LINE_NUM * 2);
}
},
},
};
</script>
@ -48,8 +35,11 @@ export default {
.pinBookmark {
.oneLine {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin: 0 auto;
max-width: 62em;
}
}
</style>

View File

@ -3,27 +3,68 @@
<a href="pinObj.url" v-if="pinObj" class="pinBookmarkItem">
<img :src="pinObj.icon.length > 0 ? pinObj.icon : '/favicon.ico'" class="icon" />
<span class="text" :title="pinObj.name">{{ pinObj.name }}</span>
<span class="action actionShow">...</span>
<a-dropdown :trigger="['click']">
<span class="action actionShow" title="操作" @click="(e) => e.preventDefault()">...</span>
<a-menu slot="overlay" @click="objAction">
<a-menu-item key="pinOrNot">{{ pinObj.id ? "取消固定" : "固定书签" }}</a-menu-item>
<a-menu-item key="editBookmark">修改书签</a-menu-item>
<a-menu-item key="delete">删除书签</a-menu-item>
</a-menu>
</a-dropdown>
</a>
<div v-else class="pinBookmarkItem" @click="showAddBlock = !showAddBlock" title="新增书签并固定到首页">
<a-icon style="font-size: 1.5em" type="plus" />
</div>
<a-modal v-model="showAddBlock" title="新增" :footer="null" @afterClose="targetNode = null">
<add-bookmark v-if="showAddBlock" :isAdd="targetNode == null" :targetNode="targetNode" addType="bookmark" @close="addBlockClose" />
</a-modal>
</div>
</template>
<script>
import AddBookmark from "@/components/main/things/AddBookmark.vue";
import { TREE_DATA, refreshHomePinList, getById, deleteData } from "@/store/modules/treeData";
import HttpUtil from "@/util/HttpUtil";
export default {
name: "pinBookmarkItem",
components: { AddBookmark },
props: {
pinObj: Object,
},
data() {
return {
showAddBlock: false,
targetNode: null,
};
},
methods: {
add() {},
//
async addBlockClose(type, obj) {
console.log(type, obj);
if (type == "bookmark" && this.targetNode == null) {
//
await HttpUtil.put("/home/pin", null, { bookmarkId: obj.bookmarkId });
}
await this.$store.dispatch(TREE_DATA + "/" + refreshHomePinList);
this.showAddBlock = false;
},
//
async objAction({ key }) {
let { id, bookmarkId } = this.pinObj;
if (key == "pinOrNot") {
//
await (id ? HttpUtil.delete("/home/pin?id=" + id) : HttpUtil.put("/home/pin", null, { bookmarkId }));
await this.$store.dispatch(TREE_DATA + "/" + refreshHomePinList);
} else if (key == "editBookmark") {
this.targetNode = this.$store.getters[TREE_DATA + "/" + getById](bookmarkId);
this.showAddBlock = true;
} else if (key == "delete") {
let body = { pathList: [], bookmarkIdList: [bookmarkId] };
await HttpUtil.post("/bookmark/batchDelete", null, body);
await this.$store.dispatch(TREE_DATA + "/" + deleteData, body);
await this.$store.dispatch(TREE_DATA + "/" + refreshHomePinList);
}
},
},
};
</script>

View File

@ -43,7 +43,7 @@ export default {
background-attachment: fixed;
.content {
height: calc(~"100vh" - 1.01rem);
height: calc(~"100vh" - 1.21rem);
display: flex;
flex-direction: column;
justify-content: space-around;

View File

@ -288,7 +288,7 @@ export default {
}
this.loading = true;
await HttpUtil.post("/bookmark/batchDelete", null, { pathList, bookmarkIdList });
this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList });
await this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList });
//
pathList.forEach((item) => {
const id = parseInt(item.split(".").reverse()[0]);

View File

@ -0,0 +1,45 @@
<template>
<div class="about">
<div>一个开源的书签管理系统</div>
<div>
源码地址
<a href="https://github.com/FleyX/bookmark" target="_blank">github.com/FleyX/bookmark</a>
</div>
<!-- <div>
使用教程
<a href="https://github.com/FleyX/bookmark" target="_blank">点击跳转</a>
</div> -->
<div>交流反馈qq群150056494,邮箱fleyx20@outlook.com</div>
<div>
统计
<a href="https://qiezi.fleyx.com" style="" target="_blank">
<div id="qieziStatisticHtmlHostPv" style="display: none">
总访问次数:
<span id="qieziStatisticHtmlHostPvValue"></span>
&nbsp;
</div>
<div id="qieziStatisticHtmlHostUv" style="display: none">
&nbsp;总访客数:
<span id="qieziStatisticHtmlHostUvValue"></span>
</div>
</a>
</div>
</div>
</template>
<script>
export default {
name: "about",
mounted() {
window.qieziStatisticKey = "b74c4b571b644782a837433209827874";
let script = document.createElement("script");
script.type = "text/javascript";
script.defer = true;
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
document.getElementsByTagName("head")[0].appendChild(script);
},
};
</script>
<style lang="less" scoped></style>