Files
frost-navigation/src/views/Home.vue

827 lines
23 KiB
Vue
Raw Normal View History

2021-02-06 23:36:46 +08:00
<template>
<el-container class="home">
<!-- 侧边栏 -->
<el-aside class="home-aside shadow-1">
2021-05-28 21:49:46 +08:00
<el-menu
class="side-nav"
default-active="search"
:collapse="config.sideMenuCollapse"
2021-07-05 23:06:01 +08:00
:collapse-transition="false"
2021-05-28 21:49:46 +08:00
@select="changeCategory"
>
2021-02-06 23:36:46 +08:00
<!-- 搜索引擎 -->
<el-menu-item index="search">
<i class="el-icon-search"></i>
<span slot="title">搜索</span>
</el-menu-item>
<!-- 全部链接 -->
2021-02-06 23:36:46 +08:00
<el-menu-item index="all">
<i class="el-icon-menu"></i>
<span slot="title">全部链接</span>
2021-02-06 23:36:46 +08:00
</el-menu-item>
<!-- 分类 -->
2021-05-28 21:49:46 +08:00
<el-menu-item
v-for="(item, itemIndex) in navLinks.list"
:key="'list-' + itemIndex"
:index="itemIndex.toString()"
>
2021-05-09 15:54:36 +08:00
<i :class="item.icon || 'el-icon-link'"></i>
2021-02-06 23:36:46 +08:00
<span slot="title">{{ item.title }}</span>
</el-menu-item>
</el-menu>
</el-aside>
<!-- 内容 -->
<el-main class="home-content">
<div class="wrapper">
2021-02-06 23:36:46 +08:00
<!-- 搜索引擎 -->
<div v-show="show.searchEngine" class="search-engine">
<!-- 搜索栏 -->
<div :class="['search-bar', 'shadow-3', { suggest: showSES }]">
<!-- 输入 -->
2021-05-28 21:49:46 +08:00
<input
v-model="searchEngine.keyword"
class="input"
type="text"
2021-05-28 21:49:46 +08:00
@blur="searchEngine.isFocus = false"
@focus="searchEngine.isFocus = true"
@keydown.enter.exact="searchEngineSubmit()"
/>
<!-- 清除 -->
2021-05-28 21:49:46 +08:00
<div
v-show="searchEngine.keyword.length > 0"
class="btn btn-clear"
@click="searchEngine.keyword = ''"
>
<i class="el-icon-close"></i>
</div>
<!-- 搜索 -->
<div class="btn btn-search" @click="searchEngineSubmit()">
<i class="el-icon-search"></i>
</div>
<!-- 关键词建议 -->
<div class="suggestion shadow-3">
<ul>
<li
v-for="item in searchEngine.suggestions"
:key="item.id"
@click="searchEngine.keyword = item.label"
>{{ item.label }}</li>
</ul>
</div>
</div>
<!-- 选择搜索引擎 -->
2021-05-28 21:49:46 +08:00
<el-radio-group
v-model="config.searchEngine"
size="small"
2021-05-09 15:54:36 +08:00
:class="['search-type', { fade: searchEngine.isFocus }]"
>
2021-05-28 21:49:46 +08:00
<!-- 分类 -->
<div
v-for="(category, cIndex) in searchEngine.list"
:key="cIndex"
class="category"
>
2021-05-28 21:49:46 +08:00
<!-- 标题 -->
<div class="title">{{ category.title }}</div>
<!-- -->
<el-radio
v-for="item in category.list"
:key="item.name"
:label="item.name"
class="shadow-2"
2021-05-28 21:49:46 +08:00
>
<Icon :path="item.icon || 'website/default.svg'" size="1.2em" />
<i class="name">{{ item.name }}</i>
<i class="desc limit-line-1">{{ item.desc }}</i>
</el-radio>
2021-05-28 21:49:46 +08:00
</div>
</el-radio-group>
</div>
<!-- 链接搜索框 -->
2021-05-28 21:49:46 +08:00
<el-input
v-show="show.searchLink"
v-model="searchLink.keyword"
class="link-search shadow-2"
placeholder="搜索链接"
clearable
>
2021-05-28 21:49:46 +08:00
<el-select slot="prepend" v-model="searchLink.type" placeholder="类型">
<el-option label="全部" value="all"></el-option>
<el-option label="标题" value="title"></el-option>
<el-option label="链接" value="link"></el-option>
2021-07-05 23:06:01 +08:00
<el-option label="简介" value="desc"></el-option>
</el-select>
</el-input>
<!-- 链接列表树 -->
2021-05-28 21:49:46 +08:00
<el-tree
v-show="show.linkTree"
ref="linkTree"
class="link-tree shadow-2"
:data="currentLinks"
node-key="id"
2021-06-14 00:40:22 +08:00
empty-text="没有查找到内容"
:props="{ label: 'title', children: 'sub' }"
:filter-node-method="searchLinkSubmit"
:default-expand-all="false"
:expand-on-click-node="true"
>
2021-05-28 21:49:46 +08:00
<div
slot-scope="{ node, data }"
class="link-item"
:title="data.update"
@click="openLink(data.link, data.showOnly)"
2021-06-05 00:03:32 +08:00
@contextmenu.prevent="openDetail(data)"
>
<span class="title">{{ node.label }}</span>
<span class="link">{{ data.link }}</span>
</div>
</el-tree>
2021-02-06 23:36:46 +08:00
</div>
2021-02-06 23:36:46 +08:00
</el-main>
<!-- 链接详情 -->
2021-06-05 00:03:32 +08:00
<el-dialog
2021-06-14 00:40:22 +08:00
:append-to-body="true"
2021-06-05 00:03:32 +08:00
custom-class="link-detail"
title="详情"
:visible="linkDetail.show"
@close="linkDetail.show = false"
>
<div class="content">
<div class="row">
<div class="label">ID</div>
<div class="text">{{ linkDetail.id }}</div>
</div>
<div class="row">
<div class="label">标题</div>
<div class="text">{{ linkDetail.title }}</div>
</div>
<div class="row">
<div class="label">链接</div>
<div class="text">
<el-link
v-show="!linkDetail.linkCopy"
type="primary"
:href="linkDetail.link"
target="_blank"
>{{ linkDetail.link }}</el-link>
<input
v-show="linkDetail.linkCopy"
type="text"
:value="linkDetail.link"
/>
2021-06-05 00:03:32 +08:00
</div>
</div>
2021-07-05 23:06:01 +08:00
<div v-show="linkDetail.desc != ''" class="row">
<div class="label">简介</div>
<div class="text">{{ linkDetail.desc }}</div>
</div>
<div v-show="linkDetail.update != ''" class="row">
2021-06-05 00:03:32 +08:00
<div class="label">更新</div>
<div class="text">{{ linkDetail.update }}</div>
</div>
</div>
</el-dialog>
2021-02-06 23:36:46 +08:00
</el-container>
</template>
<script>
import Icon from '@/components/Icon.vue';
2021-02-06 23:36:46 +08:00
export default {
name: 'Home',
components: {
Icon
},
2021-05-23 23:35:58 +08:00
beforeRouteEnter(to, from, next) {
next(vm => {
vm.utils.changeTitle();
});
},
2021-02-06 23:36:46 +08:00
data() {
return {
config: this.$root.config.storage,
utils: this.$root.utils,
// 显示的内容
show: {
searchEngine: true,
2021-05-28 21:49:46 +08:00
searchLink: false,
linkTree: false,
},
// 搜索引擎
searchEngine: {
2021-05-09 15:54:36 +08:00
isFocus: false,
keyword: '',
list: this.$root.config.searchEngines,
2021-05-28 21:49:46 +08:00
url: '',
debounce: null,
suggestions: [],
},
// 导航链接
2021-02-06 23:36:46 +08:00
navLinks: this.$root.navLinks,
// 当前显示的链接
currentLinks: [],
2021-02-07 00:39:03 +08:00
// 搜索链接
2021-05-28 21:49:46 +08:00
searchLink: {
2021-02-07 00:39:03 +08:00
debounce: null,
keyword: '',
type: 'all'
2021-06-05 00:03:32 +08:00
},
// 链接详情
linkDetail: {
show: false,
id: '',
title: '',
link: '',
linkCopy: false,
2021-07-05 23:06:01 +08:00
desc: '',
update: '',
},
2021-02-06 23:36:46 +08:00
};
},
computed: {
/**
* 显示搜索引擎关键词建议
*/
showSES() {
var se = this.searchEngine;
var isShow = (
(se.isFocus) &&
(se.keyword !== '') &&
(se.suggestions.length > 0)
);
return isShow;
},
},
2021-02-06 23:36:46 +08:00
watch: {
'searchEngine.keyword': {
handler(value) {
if (!this.config.searchSuggestion) {
return;
}
clearInterval(this.searchEngine.debounce);
this.searchEngine.debounce = setTimeout(() => {
this.searchEngineGS(value);
}, 500);
}
},
2021-05-28 21:49:46 +08:00
'searchLink.keyword': {
2021-02-06 23:36:46 +08:00
handler(value) {
2021-05-28 21:49:46 +08:00
clearTimeout(this.searchLink.debounce);
this.searchLink.debounce = setTimeout(() => {
2021-02-06 23:36:46 +08:00
this.$refs.linkTree.filter(value);
}, 500);
}
2021-02-07 00:39:03 +08:00
},
2021-05-28 21:49:46 +08:00
'searchLink.type': {
2021-02-07 00:39:03 +08:00
handler() {
// 更改搜索类型时自动重新搜索
2021-05-28 21:49:46 +08:00
this.$refs.linkTree.filter(this.searchLink.keyword);
2021-02-07 00:39:03 +08:00
}
2021-07-05 23:06:01 +08:00
},
2021-02-06 23:36:46 +08:00
},
methods: {
2021-05-22 18:14:46 +08:00
2021-02-06 23:36:46 +08:00
/**
* 更改当前显示的分类
2021-07-05 23:06:01 +08:00
*/
2021-02-06 23:36:46 +08:00
changeCategory(index) {
2021-07-05 23:15:24 +08:00
if (index === 'search') {
this.currentLinks = [];
this.show.searchEngine = true;
2021-05-28 21:49:46 +08:00
this.show.searchLink = false;
this.show.linkTree = false;
2021-07-05 23:15:24 +08:00
} else if (index === 'all') {
this.currentLinks = this.navLinks.list;
this.show.searchEngine = false;
2021-05-28 21:49:46 +08:00
this.show.searchLink = true;
this.show.linkTree = true;
2021-02-06 23:36:46 +08:00
} else {
this.currentLinks = this.navLinks.list[Number(index)].sub;
this.show.searchEngine = false;
2021-05-28 21:49:46 +08:00
this.show.searchLink = true;
this.show.linkTree = true;
2021-02-06 23:36:46 +08:00
}
2021-05-28 21:49:46 +08:00
this.searchLink.keyword = '';
2021-02-06 23:36:46 +08:00
},
2021-05-22 18:14:46 +08:00
2021-06-05 00:03:32 +08:00
/**
* 查看详情
*
* @param {object} datas 当前链接的数据
*/
openDetail(datas) {
// 非链接 / 仅显示
2021-06-05 00:03:32 +08:00
if (datas.link === undefined) {
return;
}
var detail = this.linkDetail;
detail.id = datas.id;
detail.title = datas.title;
detail.link = datas.link;
detail.linkCopy = datas.showOnly || false;
2021-07-05 23:06:01 +08:00
detail.desc = datas.desc || '';
detail.update = datas.update || '';
2021-06-05 00:03:32 +08:00
detail.show = true;
},
2021-02-06 23:36:46 +08:00
/**
* 打开链接
*
* @param {string} link 需要打开的链接
* @param {boolean} showOnly 是否仅显示链接
2021-02-06 23:36:46 +08:00
*/
openLink(link, showOnly) {
if (link === undefined) {
return false;
}
if (showOnly) {
this.$message({
duration: 5000,
message: '请在链接详情中复制后手动打开',
type: 'warning'
});
} else {
2021-02-06 23:36:46 +08:00
window.open(link, '_blank');
}
},
2021-05-22 18:14:46 +08:00
/**
* 搜索引擎获取关键词建议
*
* @param {string} keyword 当前输入的关键词
*/
searchEngineGS(keyword) {
var se = this.searchEngine;
var reqURL = `https://www.baidu.com/sugrec?json=1&prod=pc&wd=${keyword}&cb=cbSES`;
se.suggestions = [];
if (keyword === '') {
return;
}
var cbFunc = (data) => {
var word = (data.q || ''); // 当前关键词
var result = (data.g || []); // 建议
var id = 0; // ID
if (result.length === 0) {
return;
}
result.forEach((item) => {
id += 1;
se.suggestions.push({
id,
label: item.q,
highlight: word
});
});
};
this.utils.jsonp({
url: reqURL,
cbName: 'cbSES',
cbFunc
});
},
/**
* 搜索引擎搜索
*/
searchEngineSubmit() {
var vm = this;
var se = this.searchEngine;
var selectedSE = this.config.searchEngine;
var keyword = se.keyword;
var url = '';
2021-07-05 23:15:24 +08:00
if (keyword === '') {
return false;
2021-05-22 18:14:46 +08:00
} else {
keyword = window.encodeURIComponent(keyword);
}
for (let category in se.list) {
let list = se.list[category].list;
2021-05-28 21:49:46 +08:00
for (let index in list) {
if (list[index].name === selectedSE) {
2021-05-28 21:49:46 +08:00
url = list[index].url.replace(/%keyword%/, keyword);
break;
}
}
}
2021-05-28 21:49:46 +08:00
vm.openLink(url);
},
2021-05-22 18:14:46 +08:00
2021-02-06 23:36:46 +08:00
/**
* 搜索链接搜索
2021-07-05 23:06:01 +08:00
*
* @param {string} value 关键词
* @param {object} data 每一项链接的信息
2021-02-06 23:36:46 +08:00
*/
2021-05-28 21:49:46 +08:00
searchLinkSubmit(value, data) {
2021-02-06 23:36:46 +08:00
// 关键词为空,显示全部
if (value === '') {
return true;
2021-02-06 23:36:46 +08:00
}
// 小写
value = value.toLowerCase();
2021-05-28 21:49:46 +08:00
var searchType = this.searchLink.type;
var title = data.title.toLowerCase();
var link = (data.link || '');
2021-07-05 23:06:01 +08:00
var desc = (data.desc || '');
2021-02-07 00:39:03 +08:00
var result = false;
2021-07-05 23:06:01 +08:00
if (searchType === 'all') {
2021-02-07 00:39:03 +08:00
// 全部
2021-07-05 23:06:01 +08:00
let checks = [
(title.indexOf(value) !== -1),
(link.indexOf(value) !== -1),
(desc.indexOf(value) !== -1),
];
result = (checks[0] || checks[1] || checks[2]);
} else if (searchType === 'title') {
2021-02-07 00:39:03 +08:00
// 标题
result = (title.indexOf(value) !== -1);
2021-07-05 23:06:01 +08:00
} else if (searchType === 'link') {
2021-02-07 00:39:03 +08:00
// 链接
result = (link.indexOf(value) !== -1);
2021-07-05 23:06:01 +08:00
} else if (searchType === 'desc') {
// 简介
result = (desc.indexOf(value) !== -1);
2021-02-07 00:39:03 +08:00
}
2021-02-06 23:36:46 +08:00
return result;
2021-05-22 18:14:46 +08:00
},
2021-02-06 23:36:46 +08:00
},
}
</script>
<style lang="less" scoped>
.home-aside {
position: relative;
z-index: 110;
width: auto !important;
2021-02-06 23:36:46 +08:00
overflow-x: hidden;
.side-nav {
min-height: 100%;
border: none;
2021-02-06 23:36:46 +08:00
}
}
.home-content {
display: flex;
flex-direction: column;
align-items: center;
2021-02-06 23:36:46 +08:00
position: relative;
padding: 2rem;
background-color: @colorWhite;
overflow-x: hidden;
overflow-y: scroll;
.wrapper {
width: 100%;
min-width: 16rem;
2021-06-01 09:12:45 +08:00
max-width: 64rem;
}
2021-05-28 21:49:46 +08:00
}
.search-engine {
display: flex;
align-items: center;
flex-direction: column;
2021-05-28 21:49:46 +08:00
.search-bar {
@barHeight: 2.8rem;
@barRadius: 0.3rem;
display: flex;
align-items: center;
2021-05-28 21:49:46 +08:00
position: sticky;
top: 2.5rem;
2021-05-28 21:49:46 +08:00
z-index: 100;
width: 100%;
max-width: 40rem;
height: @barHeight;
border-radius: @barRadius;
2021-05-28 21:49:46 +08:00
background-color: #FFF;
&.suggest {
border-radius: @barRadius @barRadius 0 0;
}
2021-05-28 21:49:46 +08:00
.input {
flex-grow: 1;
padding-left: 1rem;
width: 0;
height: 100%;
outline: none;
}
.btn {
display: inline-flex;
align-items: center;
2021-05-28 21:49:46 +08:00
justify-content: center;
flex-shrink: 0;
width: 2.8rem;
height: 2.8rem;
2021-05-28 21:49:46 +08:00
background-color: transparent;
font-size: 1.2rem;
2021-05-28 21:49:46 +08:00
cursor: pointer;
}
2021-05-28 21:49:46 +08:00
.btn-clear {
width: 2rem;
opacity: 0.5;
2021-05-28 21:49:46 +08:00
transition: opacity @transitionTime;
&:hover {
opacity: 1;
}
2021-05-28 21:49:46 +08:00
}
2021-05-28 21:49:46 +08:00
.btn-search {
border-radius: 0 @barRadius @barRadius 0;
2021-05-28 21:49:46 +08:00
color: @colorPrimary;
transition: background @transitionTime, color @transitionTime;
2021-05-28 21:49:46 +08:00
&:hover {
background-color: @colorPrimary;
color: #FFF;
}
2021-05-28 21:49:46 +08:00
}
&.suggest .btn-search {
border-bottom-right-radius: 0;
}
.suggestion {
display: block;
visibility: hidden;
position: absolute;
top: @barHeight;
width: 100%;
border-top: 0.1rem solid #EEE;
border-radius: 0 0 @barRadius @barRadius;
background-color: #FFF;
overflow: hidden;
// 延迟隐藏
transition: visibility 0.2s;
ul {
padding: 0.5rem 0;
list-style: none;
line-height: 1.5rem;
font-size: 0.9rem;
color: #000;
}
li {
padding: 0.5rem 1rem;
cursor: pointer;
&:hover {
background-color: @colorWhite;
}
}
}
&.suggest .suggestion {
visibility: visible;
}
2021-05-28 21:49:46 +08:00
}
2021-05-28 21:49:46 +08:00
.search-type {
display: block;
margin: 4.5rem 0;
font-size: 1rem;
transition: opacity calc(@transitionTime * 4);
2021-05-28 21:49:46 +08:00
&.fade {
opacity: 0.5;
}
2021-05-28 21:49:46 +08:00
/deep/ .category {
padding: 0.5rem 0;
2021-05-09 15:54:36 +08:00
2021-05-28 21:49:46 +08:00
.title {
margin: 0.5rem 0;
text-align: left;
2021-05-09 15:54:36 +08:00
}
2021-05-28 21:49:46 +08:00
.el-radio {
margin: 0.5rem;
padding: 0.8rem 1rem;
2021-06-01 09:12:45 +08:00
width: 15rem;
border-radius: 0.25rem;
border-left: solid 0.2rem transparent;
background-color: #FFF;
text-align: left;
font-weight: normal;
transition: border @transitionTime;
2021-05-28 21:49:46 +08:00
&:hover {
border-left-color: @colorSecondary;
color: @colorSecondary;
}
&.is-checked {
2021-05-28 21:49:46 +08:00
border-left-color: @colorPrimary;
color: @colorPrimary;
}
}
.el-radio__input {
display: none;
}
.el-radio__label {
2021-05-28 21:49:46 +08:00
display: flex;
align-items: center;
padding: 0;
transition: color @transitionTime;
i {
2021-05-28 21:49:46 +08:00
display: inline-block;
padding: 0.125rem 0;
font-style: normal;
}
.fn-icon {
2021-05-28 21:49:46 +08:00
flex-shrink: 0;
margin-right: 0.4rem;
2021-05-28 21:49:46 +08:00
}
.name {
flex-shrink: 0;
}
.desc {
2021-05-28 21:49:46 +08:00
flex-grow: 1;
margin-left: 0.5rem;
font-size: 0.8rem;
color: #CCC;
}
}
}
}
2021-05-28 21:49:46 +08:00
}
2021-05-28 21:49:46 +08:00
/deep/ .link-search {
@height: 2.8rem;
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
position: sticky;
z-index: 100;
top: 0;
margin-bottom: 1rem;
border-radius: 0.25rem;
2021-05-28 21:49:46 +08:00
line-height: @height;
overflow: hidden;
2021-02-07 00:39:03 +08:00
> div,
> input {
2021-05-28 21:49:46 +08:00
border: none;
}
2021-05-28 21:49:46 +08:00
.el-input-group__prepend {
background-color: #FFF;
2021-02-07 00:39:03 +08:00
2021-05-28 21:49:46 +08:00
.el-select .el-input {
width: 4.5rem;
2021-02-07 00:39:03 +08:00
2021-05-28 21:49:46 +08:00
input {
padding: 0 0.75rem;
2021-02-07 00:39:03 +08:00
}
}
2021-05-28 21:49:46 +08:00
}
2021-05-28 21:49:46 +08:00
.el-input__inner {
height: @height;
line-height: @height;
2021-02-06 23:36:46 +08:00
}
2021-05-28 21:49:46 +08:00
}
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
.link-tree {
padding: 0.5rem;
border-radius: 0.25rem;
2021-05-28 21:49:46 +08:00
font-size: 14px;
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
/deep/ .el-tree-node__content {
height: 3.6em;
}
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
.link-item {
2021-06-04 23:06:16 +08:00
flex-grow: 1;
width: 0;
2021-05-28 21:49:46 +08:00
> span {
2021-06-04 23:06:16 +08:00
display: block;
overflow: hidden;
text-overflow: ellipsis;
2021-05-28 21:49:46 +08:00
}
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
.title {
2021-06-04 23:06:16 +08:00
opacity: 1;
2021-05-28 21:49:46 +08:00
}
2021-02-06 23:36:46 +08:00
2021-05-28 21:49:46 +08:00
.link {
margin-top: 0.2rem;
opacity: 0.5;
2021-02-06 23:36:46 +08:00
}
}
}
2021-06-05 00:03:32 +08:00
/deep/ .link-detail {
width: 80%;
max-width: 32rem;
.row {
display: flex;
align-items: flex-start;
line-height: 1.5rem;
2021-06-05 00:03:32 +08:00
}
.label {
flex-shrink: 0;
2021-06-05 00:03:32 +08:00
width: 3.5em;
text-align: right;
}
.text {
flex-grow: 1;
}
input[type="text"] {
margin: 0;
padding: 0;
width: 100%;
border: none;
border-radius: 0;
line-height: 1em;
font-size: inherit;
color: @colorSecondary;
}
2021-06-05 00:03:32 +08:00
}
2021-02-06 23:36:46 +08:00
</style>
<style lang="less" scoped>
@media screen and (max-width: 400px) {
.home-content {
padding: 1rem;
}
.search-engine {
.search-bar {
top: 1rem;
}
.search-type {
margin: 2rem 0;
}
.category {
text-align: center;
.el-radio {
width: 90% !important;
}
}
}
}
</style>