书签导入功能

This commit is contained in:
WangHao
2020-08-27 21:51:33 +08:00
parent 5e67a19e04
commit 96672852ae
9 changed files with 1407 additions and 676 deletions

View File

@ -0,0 +1,132 @@
package com.ruoyi.web.controller.yunbookmark;
import com.ruoyi.bookmark.domain.SqBookmark;
import com.ruoyi.bookmark.domain.SqMenu;
import com.ruoyi.bookmark.service.ISqBookmarkService;
import com.ruoyi.bookmark.service.ISqMenuService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.BookmarkHtml.ImportHtml;
import com.ruoyi.common.utils.StringUtils;
import org.jsoup.HttpStatusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.net.ssl.SSLHandshakeException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @Auther: Wang
* @Date:
* 功能描述: 导入书签 导出书签
*/
@RestController
@RequestMapping("/IO")
public class BrowserController extends BaseController {
public static Logger logger = LoggerFactory.getLogger(BrowserController.class);
@Autowired
private ISqMenuService iSqMenuService;
@Autowired
private ISqBookmarkService iSqBookmarkService;
@RequestMapping("/import")
@PreAuthorize("@ss.hasPermi('bookmark:bookmark:list')")
public AjaxResult importCollect(@RequestParam("htmlFile") MultipartFile htmlFile){
logger.debug("开始上传状态是:");
SysUser sysUser=getAuthUser();
Long userID= sysUser.getUserId();
try {
Map<String, Map<String, String>> map = ImportHtml.parseHtml(htmlFile.getInputStream());
// Map<String, List<Map>> map = ImportHtml.importHtmlMore(htmlFile.getInputStream());
if(null == map || map.isEmpty()){
logger.info("未获取到url连接");
return AjaxResult.error("未获取到url连接,空书签");
}
for (Entry<String, Map<String, String>> entry : map.entrySet()) {
String favoritesName = entry.getKey();
//添加目录
SqMenu sqMenu=new SqMenu();
sqMenu.setUserId(userID);
sqMenu.setCreateTime(new Date());
sqMenu.setMenuName(favoritesName);
sqMenu.setParentId(0L);
iSqMenuService.insertSqMenu(sqMenu);
importHtml(entry.getValue(), sqMenu.getMenuId(), userID);
}
}catch (SSLHandshakeException e){
logger.error("文章解析出错:",e);
}
catch (DataIntegrityViolationException e){
logger.error("导入存储异常:",e);
}
catch (HttpStatusException a){
logger.error("文档解析错误:",a);
}
catch (Exception e) {
logger.error("导入html异常:",e);
}
return AjaxResult.success("导入成功");
}
/**
* 导入收藏文章
*/
public void importHtml(Map<String, String> map,Long menuID,Long userId){
for(Entry<String, String> entry : map.entrySet()){
try {
//获取URL后查询最新的URL信息
Map<String, String> result = ImportHtml.getCollectFromUrl(entry.getKey());
SqBookmark sqBookmark =new SqBookmark();
sqBookmark.setUserid(userId);
sqBookmark.setTitle(entry.getValue());
sqBookmark.setUrl(entry.getKey());
sqBookmark.setUrls(ImportHtml.Urlutils(new URL(entry.getKey())));
if(StringUtils.isBlank(result.get("description"))){
sqBookmark.setDescription(entry.getValue());
}else{
sqBookmark.setDescription(result.get("description"));
}
sqBookmark.setMenuId(menuID);
sqBookmark.setCreateTime(new Date());
iSqBookmarkService.insertSqBookmark(sqBookmark);
} catch (Exception e) {
logger.error("导入存储异常:",e);
}
}
}
}

View File

@ -11,7 +11,7 @@
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
</appender> </appender>
<!-- 系统日志输出 --> <!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file> <file>${log.path}/sys-info.log</file>
@ -34,7 +34,7 @@
<onMismatch>DENY</onMismatch> <onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file> <file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 --> <!-- 循环政策:基于时间创建日志文件 -->
@ -56,7 +56,7 @@
<onMismatch>DENY</onMismatch> <onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<!-- 用户访问日志输出 --> <!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file> <file>${log.path}/sys-user.log</file>
@ -70,7 +70,7 @@
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
</appender> </appender>
<!-- 系统模块日志级别控制 --> <!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" /> <logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 --> <!-- Spring日志级别控制 -->
@ -79,15 +79,15 @@
<root level="info"> <root level="info">
<appender-ref ref="console" /> <appender-ref ref="console" />
</root> </root>
<!--系统操作日志--> <!--系统操作日志-->
<root level="info"> <root level="info">
<appender-ref ref="file_info" /> <appender-ref ref="file_info" />
<appender-ref ref="file_error" /> <appender-ref ref="file_error" />
</root> </root>
<!--系统用户操作日志--> <!--系统用户操作日志-->
<logger name="sys-user" level="info"> <logger name="sys-user" level="info">
<appender-ref ref="sys-user"/> <appender-ref ref="sys-user"/>
</logger> </logger>
</configuration> </configuration>

View File

@ -125,6 +125,18 @@
<version>2.1.5</version> <version>2.1.5</version>
</dependency> </dependency>
<!--jsoup解析html-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<!--常用工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,38 @@
package com.ruoyi.common.utils.BookmarkHtml;
import org.springframework.stereotype.Component;
/**
* @Auther: Wang
* @Date: 2020/08/22 23:03
* 功能描述:
*/
@Component
public class Const {
public static String BASE_PATH;
public static String LOGIN_SESSION_KEY = "Favorites_user";
public static String PASSWORD_KEY = "@#$%^&*()OPG#$%^&*(HG";
public static String DES3_KEY = "9964DYByKL967c3308imytCB";
public static String default_logo="img/logo.jpg";
public static String userAgent="Mozilla";
public static String default_Profile=BASE_PATH+"/img/logo.jpg";
public static String LAST_REFERER = "LAST_REFERER";
public static int COOKIE_TIMEOUT= 30*24*60*60;
// @Autowired(required = true)
// public void setBasePath(@Value("${favorites.base.path}")String basePath) {
// Const.BASE_PATH = basePath;
// }
}

View File

@ -0,0 +1,363 @@
package com.ruoyi.common.utils.BookmarkHtml;
import com.ruoyi.common.utils.StringUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Auther: Wang
* @Date: 2020/08/22 22:56
* 功能描述:
*/
public class ImportHtml {
public static Logger logger = LoggerFactory.getLogger(ImportHtml.class);
/**
* @param url
* @return
*/
// public static String getImge(String url){
// String logo="";
// logo=getPageImg(url);
// if(StringUtils.isBlank(logo) || logo.length()>300){
// logo=Const.BASE_PATH + Const.default_logo;
// }
// return logo;
// }
/**
* @param url
* @return
*/
// public static String getPageImg(String url){
// String imgUrl="";
// Document doc;
// try {
// doc = Jsoup.connect(url).userAgent(Const.userAgent).get();
// Elements images = doc.select("img[src~=(?i)\\.(png|jpe?g|gif)]");
// for(Element image : images){
// imgUrl=image.attr("src");
// if(StringUtils.isNotBlank(imgUrl) ){
// if(imgUrl.startsWith("//")){
// imgUrl = "http:" + imgUrl;
// }else if(!imgUrl.startsWith("http") && !imgUrl.startsWith("/")){
// imgUrl=URLUtil.getDomainUrl(url) + "/" + imgUrl;
// }else if(!imgUrl.startsWith("http")){
// imgUrl=URLUtil.getDomainUrl(url)+imgUrl;
// }
// }
// // 判断图片大小
// String fileUrl = download(imgUrl);
// if(fileUrl!=null){
// File picture = new File(fileUrl);
// FileInputStream in = new FileInputStream(picture);
// BufferedImage sourceImg = ImageIO.read(in);
// String weight = String.format("%.1f",picture.length()/1024.0);
// int width = sourceImg.getWidth();
// int height = sourceImg.getHeight();
// // 删除临时文件
// if(picture.exists()){
// in.close();
// picture.delete();
// }
// if(Double.parseDouble(weight) <= 0 || width <=0 || height <= 0){
// logger.info("当前图片大小为0继续获取图片链接");
// imgUrl="";
// }else{
// break;
// }
// }
// }
// } catch (Exception e) {
//
// logger.warn("getPageImg 失败,url:"+url,e.getMessage());
// }
// return imgUrl;
// }
/**
* @auther: Wang
* @date: 2020/02/14 15:35
* 功能描述:查询URL的 最新信息
*/
public static Map<String, String> getCollectFromUrl(String url){
Map<String, String> result = new HashMap<String, String>();
try {
result.put("url", url);
Connection connection = Jsoup.connect(url).userAgent(Const.userAgent);
connection.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
connection.header("Accept-Encoding", "gzip, deflate, sdch");
connection.header("Accept-Language", "zh-CN,zh;q=0.8");
connection.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
Document doc = connection.get();
String title = doc.title();
if(StringUtils.isNotBlank(title)){
result.put("title", title);
}
String charset = doc.charset().name();
if(StringUtils.isBlank(charset)){
Elements eles = doc.select("meta[http-equiv=Content-Type]");
Iterator<Element> itor = eles.iterator();
while (itor.hasNext()){
charset = matchCharset(itor.next().toString().toUpperCase());
}
}
if(StringUtils.isBlank(charset)){
result.put("charset", charset);
}
Elements metas = doc.head().select("meta");
for (Element meta : metas) {
String content = meta.attr("content");
if ("description".equalsIgnoreCase(meta.attr("name"))) {
result.put("description", content);
}
}
//result.put("logoUrl", getImge(url));
} catch (Exception e) {
logger.error("文章解析出错:",e);
}
return result;
}
//
/**
* 一层只输出url及对应的title或描述
* @param in
* @return
*/
public static Map<String, String> parseHtmlOne(InputStream in){
Map<String, String> map = new HashMap<String, String>();
try {
Document doc = Jsoup.parse(in, "UTF-8", "");
Elements metas = doc.select("a");
for (Element meta : metas) {
String url = meta.attr("href");
if(url.startsWith("http")){
map.put(url, meta.text());
}
}
} catch (Exception e) {
logger.error("解析html 文件异常:",e);
}
return map;
}
/**
* 两层(文件夹<url+title或描述>
* @param HTML
* @return
*/
public static Map<String, Map<String, String>> parseHtml(InputStream HTML){
Map<String, Map<String, String>> resultMap = new HashMap<String, Map<String, String>>();
try {
Document doc = Jsoup.parse(HTML, "UTF-8", "");
Elements metasdts = doc.select("dt");
for(Element dt : metasdts){
String favoritesName = "";
Elements dtcs = dt.children();
Map<String, String> map = new HashMap<String, String>();
for(Element dt1 : dtcs){
if("h3".equalsIgnoreCase(dt1.nodeName())){
favoritesName = dt1.text();
}else if("dl".equalsIgnoreCase(dt1.nodeName())){
Elements dts = dt1.children();
for(Element dt11 : dts){
if("dt".equals(dt11.nodeName())){
if("a".equals(dt11.child(0).nodeName())){
String url = dt11.child(0).attr("href");
if(url.startsWith("http")){
map.put(url, dt11.child(0).text());
}
}
}
}
}
}
if(StringUtils.isNotBlank(favoritesName) && map.size() > 0){
resultMap.put(favoritesName, map);
}
}
} catch (Exception e) {
logger.error("解析html文件异常",e);
}
return resultMap;
}
/**
* 按照文档结构输出(TODO)
*/
public static Map<String, List<Map>> importHtmlMore(InputStream in){
Map<String, List<Map>> resultMap = new HashMap<String, List<Map>>();
try {
Document doc = Jsoup.parse(in, "UTF-8", "");
Elements bodys = doc.child(0).children();
for(Element body : bodys){
if("body".equalsIgnoreCase(body.nodeName())){
Elements dls = body.children();
for(Element dl : dls){
if("dl".equalsIgnoreCase(dl.nodeName())){
resultMap = parseElements(dl,resultMap);
System.out.println("resultMap:" + resultMap);
}
}
}
}
} catch (Exception e) {
logger.error("解析html文件异常",e);
}
return resultMap;
}
public static Map<String, List<Map>> parseElements(Element element,Map<String, List<Map>> resultMap){
Map<String, Map> favoritesMap = new HashMap<String, Map>();
Map<String, String> urlMap = new HashMap<String, String>();
String favoritesName = "";
Elements dts = element.children();
for(Element dt : dts){
if("dt".equalsIgnoreCase(dt.nodeName())){
Elements dtas = dt.children();
for(Element a : dtas){
if("a".equalsIgnoreCase(a.nodeName())){
String url = a.attr("href");
if(url.startsWith("http")){
urlMap.put(url, a.text());
favoritesName=a.parent().parent().parent().child(0).text();
System.out.println("目录:"+favoritesName);
logger.error("目录:",favoritesName);
}
}else if("dl".equalsIgnoreCase(a.nodeName())){
resultMap = parseElements(a,resultMap);
}
}
}
}
if(StringUtils.isNotBlank(favoritesName)){
favoritesMap.put(favoritesName, urlMap);
}
List<Map> mapList = null;
Element parment = element.parent().parent().parent().child(0);
if("h3".equalsIgnoreCase(parment.nodeName())){
String name = parment.text();
if(resultMap.containsKey(name)){
mapList = resultMap.get(name);
mapList.add(favoritesMap);
}else{
mapList = new ArrayList<Map>();
mapList.add(favoritesMap);
}
resultMap.put(name, mapList);
}
return resultMap;
}
// public static StringBuilder exportHtml(String title,StringBuilder body){
// StringBuilder sb = new StringBuilder();
// sb.append("<HTML>");
// sb.append("<HEAD>");
// sb.append("<TITLE>"+title+"</TITLE>");
// sb.append("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\" />");
// sb.append("</HEAD>");
// sb.append("<BODY><H1>"+title+"</H1>");
// sb.append(body);
// sb.append("</BODY>");
//
// return sb;
// }
//
public static String matchCharset(String content) {
Pattern p = Pattern.compile("(?<=charset=)(.+)(?=\")");
Matcher m = p.matcher(content);
if (m.find()){
return m.group();
}
return null;
}
//
// // 图片下载
// private static String download(String url) {
// try {
// String imageName = url.substring(url.lastIndexOf("/") + 1,
// url.length());
//
// URL uri = new URL(url);
// InputStream in = uri.openStream();
// String dirName = "static/temp/";
// File dirFile = new File(dirName);
// if(!dirFile.isDirectory()){
// dirFile.mkdir();
// }
// String fileName = dirName+imageName;
// File file = new File(dirFile,imageName);
// FileOutputStream fo = new FileOutputStream(file);
// byte[] buf = new byte[1024];
// int length = 0;
// while ((length = in.read(buf, 0, buf.length)) != -1) {
// fo.write(buf, 0, length);
// }
// in.close();
// fo.close();
// return fileName;
// } catch (Exception e) {
// e.printStackTrace();
// }
// return null;
// }
//
// /**
// * 判断链接是否失效
// * @param url
// * @return
// */
// public static boolean isConnect(String url){
// HttpURLConnection connection;
// int counts = 0;
// boolean flag = false;
// if (url == null || url.length() <= 0) {
// return flag;
// }
// while (counts < 5) {
// try {
// connection = (HttpURLConnection) new URL(url).openConnection();
// int state = connection.getResponseCode();
// if (state == 200) {
// flag = true;
// }
// break;
// } catch (Exception e) {
// counts++;
// continue;
// }
// }
// return flag;
// }
// /**
// * @auther: Wang
// * @date: 2020/02/15 14:44
// * 功能描述:分割书签URL 得到官网主机
// * @return
// */
public static String Urlutils(URL url) throws MalformedURLException {
String host = url.getHost();// 获取主机名
return host;
}
}

View File

@ -159,6 +159,13 @@ export const constantRoutes = [
meta:{ meta:{
requireAuth: false,//加该字段,表示进入这个路由是需要登录的true requireAuth: false,//加该字段,表示进入这个路由是需要登录的true
}, },
},{
path: '/importHtml',
name: 'importHtml',
component: resolve => require(['../views/bookmark/common/ImportHtml.vue'], resolve),
meta:{
requireAuth: true,//加该字段,表示进入这个路由是需要登录的true
},
} }
], ],

View File

@ -0,0 +1,175 @@
<template>
<div>
<!-- 用户导入对话框 -->
<!-- <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>-->
<el-upload
ref="upload"
:limit="1"
accept=".html"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + 222"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
name="htmlFile"
drag
class="upload-demo"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<!-- <div class="el-upload__tip" slot="tip">-->
<!-- <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据-->
<!-- <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>-->
<!-- </div>-->
<div class="el-upload__tip" style="color:red" slot="tip">提示仅允许导入".html"格式文件</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<!-- <el-button @click="upload.open = false"> </el-button>-->
</div>
<!-- </el-dialog>-->
<!-- <el-button-->
<!-- type="info"-->
<!-- icon="el-icon-upload2"-->
<!-- size="mini"-->
<!-- @click="handleImport"-->
<!-- v-hasPermi="['system:user:import']"-->
<!-- >导入</el-button>-->
<!-- <el-upload-->
<!-- ref="upload"-->
<!-- :limit="1"-->
<!-- accept=".html"-->
<!-- class="upload-demo"-->
<!-- :headers="upload.headers"-->
<!-- :action="upload.url + '?updateSupport=' + 22"-->
<!-- name="htmlFile"-->
<!-- :on-progress="handleFileUploadProgress"-->
<!-- :on-success="handleFileSuccess"-->
<!-- :auto-upload="false"-->
<!-- drag-->
<!-- >-->
<!--&lt;!&ndash; v-hasPermi="['bookmark:bookmark:export']"&ndash;&gt;-->
<!-- <i class="el-icon-upload"></i>-->
<!-- <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>-->
<!-- <div class="el-upload__tip" slot="tip">只能上传浏览器导出的.html后缀文件</div>-->
<!-- </el-upload>-->
<!-- <div class="structure">-->
<!-- <el-radio v-model="structure" label="0">按原目录导入</el-radio>-->
<!-- <el-radio v-model="structure" label="1" disabled>全部导入到一个新目录</el-radio>-->
<!-- </div>-->
<!--<el-button type="primary" size="small" @click="submitUpload" plain><i-->
<!--class="el-icon-upload el-icon&#45;&#45;right">开始导入书签</i></el-button>-->
<div class="text">
<ul>
<li>注意事项</li>
<li style="color: #ff2a34">导入300书签需要大约8分钟,请勿短时间重复操作</li>
<li>
1导入的方法是将浏览器里面收藏的网站导出<span style="color: red">HTML文件后缀</span>然后将导出的HTML文件点击上面的<span style="color: red">开始导入书签</span>
<li>2目前只测试过谷歌浏览器书签导入但是因为浏览器的标签是可以支持互相导入的如果不成功可以先将其他浏览器的书签导入谷歌浏览器再导出</li>
<li>3浏览器导出书签方法请百度</li>
<li>4支持浏览器的目录结构,本站目录支持无限级别分类</li>
</ul>
</div>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
export default {
name: "",
data() {
return {
// 用户导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "书签导入",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/IO/import"
},
}
},
methods: {
/** 导入按钮操作 */
handleImport() {
this.upload.title = "书签导入";
this.upload.open = true;
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit();
}
}
}
</script>
<style scoped>
.text{
border: #6f7180 1px solid;
border-radius: 10px;
margin-top: 10px;
background-color: #e9e9e9;
}
.structure {
margin-top: 15px;
}
.Import {
margin-top: 15px;
}
.Import el-button {
width: 200px;
}
ul{
margin-left: 5px;
}
li{
list-style:none;
}
</style>

View File

@ -24,7 +24,11 @@
<div class="reminder">工具箱</div> <div class="reminder">工具箱</div>
<div class="aside-title"><i class="el-icon-s-tools"></i><span>收藏同步</span></div> <div class="aside-title"><i class="el-icon-s-tools"></i><span>收藏同步</span></div>
<div class="aside-title"><i class="el-icon-help"></i><span>发现书签</span></div> <div class="aside-title"><i class="el-icon-help"></i><span>发现书签</span></div>
<div class="aside-title"><i class="el-icon-s-platform"></i><span>任意门</span></div> <router-link :to="{ name: 'importHtml' }">
<div class="aside-title">
<i class="el-icon-s-platform"></i><span>导入书签</span>
</div>
</router-link>
<div class="aside-title"><i class="el-icon-message-solid"></i><span>收件箱</span></div> <div class="aside-title"><i class="el-icon-message-solid"></i><span>收件箱</span></div>
<!-- <el-footer class="aside-navigation">--> <!-- <el-footer class="aside-navigation">-->

File diff suppressed because it is too large Load Diff