优化全局状态读写逻辑,使用 Vuex
This commit is contained in:
85
src/App.vue
85
src/App.vue
@@ -2,7 +2,7 @@
|
||||
<div id="app">
|
||||
|
||||
<!-- 加载动画 -->
|
||||
<div v-show="config.loading.subPage" class="loading-bar">
|
||||
<div v-show="showLoading" class="loading-bar">
|
||||
<div class="bar-content"></div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<!-- 标题 -->
|
||||
<el-menu-item
|
||||
v-show="config.storage.showSiteTitle"
|
||||
v-show="appConfig.showSiteTitle"
|
||||
index="title"
|
||||
class="title"
|
||||
disabled
|
||||
@@ -79,6 +79,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
import FloatingBtn from '@/components/FloatingBtn.vue';
|
||||
|
||||
export default {
|
||||
@@ -88,12 +90,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: this.$root.config,
|
||||
debounce: {
|
||||
saveConfig: null,
|
||||
updateConfig: null
|
||||
},
|
||||
// Header 菜单项
|
||||
|
||||
debounceFontSize: null,
|
||||
|
||||
/** Header 菜单项 */
|
||||
headerMenuItems: [
|
||||
{
|
||||
id: 'home',
|
||||
@@ -116,12 +116,20 @@ export default {
|
||||
routeName: 'About'
|
||||
}
|
||||
],
|
||||
// 显示下拉菜单
|
||||
showHeaderDropdown: false
|
||||
|
||||
/** 显示下拉菜单 */
|
||||
showHeaderDropdown: false,
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Header 默认激活的菜单项
|
||||
|
||||
...mapState({
|
||||
appConfig: 'config',
|
||||
showLoading: 'showLoading',
|
||||
}),
|
||||
|
||||
/** Header 默认激活的菜单项 */
|
||||
headerDefaultActive() {
|
||||
var routeName = this.$route.name;
|
||||
var item = '';
|
||||
@@ -131,10 +139,23 @@ export default {
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
// 更新字体大小
|
||||
'appConfig.fontSize': {
|
||||
handler(value) {
|
||||
|
||||
clearTimeout(this.debounceFontSize);
|
||||
this.debounceFontSize = setTimeout(() => {
|
||||
document.documentElement.style.fontSize = `${value}px`;
|
||||
}, 500);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
// 路由名称
|
||||
'$route.name': {
|
||||
handler() {
|
||||
@@ -143,40 +164,18 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 更新储存的设置
|
||||
'config.storage': {
|
||||
handler(obj) {
|
||||
clearTimeout(this.debounce.saveConfig);
|
||||
|
||||
this.debounce.saveConfig = setTimeout(() => {
|
||||
localStorage.setItem('navConfig', JSON.stringify(obj));
|
||||
}, 2000);
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 初始化 */
|
||||
init() {
|
||||
this.$store.commit('readConfig');
|
||||
},
|
||||
|
||||
// 改变字体大小
|
||||
'config.storage.fontSize': {
|
||||
handler(value) {
|
||||
clearTimeout(this.debounce.updateConfig);
|
||||
|
||||
this.debounce.updateConfig = setTimeout(() => {
|
||||
// 改变字体大小
|
||||
document.documentElement.style.fontSize = value + 'px';
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
var configStr = localStorage.getItem('navConfig');
|
||||
var configObj = {};
|
||||
|
||||
if (configStr != null) {
|
||||
configObj = JSON.parse(configStr);
|
||||
Object.assign(this.config.storage, configObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -2,160 +2,6 @@ let config = {
|
||||
loading: {
|
||||
subPage: false
|
||||
},
|
||||
searchEngines: {
|
||||
search: {
|
||||
title: '搜索',
|
||||
list: [
|
||||
{
|
||||
name: '百度',
|
||||
desc: 'www.baidu.com',
|
||||
url: 'https://www.baidu.com/s?wd=%keyword%',
|
||||
icon: 'website/baidu.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '必应',
|
||||
desc: 'cn.bing.com',
|
||||
url: 'https://cn.bing.com/search?q=%keyword%',
|
||||
icon: 'website/bing.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '秘迹',
|
||||
desc: 'mijisou.com',
|
||||
url: 'https://mijisou.com/?q=%keyword%',
|
||||
icon: 'website/mijisou.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '搜狗',
|
||||
desc: 'www.sogou.com',
|
||||
url: 'https://www.sogou.com/web?query=%keyword%',
|
||||
icon: 'website/sogou.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '360',
|
||||
desc: 'www.so.com',
|
||||
url: 'https://www.so.com/s?q=%keyword%',
|
||||
icon: 'website/360.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Google',
|
||||
desc: 'www.google.com',
|
||||
url: 'https://www.google.com/search?q=%keyword%',
|
||||
icon: 'website/google.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Yandex',
|
||||
desc: 'yandex.com',
|
||||
url: 'https://yandex.com/search/?text=%keyword%',
|
||||
icon: 'website/yandex.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
media: {
|
||||
title: '多媒体',
|
||||
list: [
|
||||
{
|
||||
name: '网易云音乐',
|
||||
desc: 'music.163.com',
|
||||
url: 'https://music.163.com/#/search/m/?s=%keyword%',
|
||||
icon: 'website/netease_music.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'AcFun',
|
||||
desc: 'www.acfun.cn',
|
||||
url: 'https://www.acfun.cn/search/?keyword=%keyword%',
|
||||
icon: 'website/acfun.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'bilibili',
|
||||
desc: 'search.bilibili.com',
|
||||
url: 'https://search.bilibili.com/all?keyword=%keyword%',
|
||||
icon: 'website/bilibili.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'QQ音乐',
|
||||
desc: 'y.qq.com',
|
||||
url: 'https://y.qq.com/n/ryqq/search?w=%keyword%',
|
||||
icon: 'website/qq_music.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
technology: {
|
||||
title: '技术',
|
||||
list: [
|
||||
{
|
||||
name: 'CSDN',
|
||||
desc: 'so.csdn.net',
|
||||
url: 'https://so.csdn.net/so/search/all?q=%keyword%',
|
||||
icon: 'website/csdn.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
desc: 'github.com',
|
||||
url: 'https://github.com/search?q=%keyword%',
|
||||
icon: 'website/github.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'MDN',
|
||||
desc: 'developer.mozilla.org',
|
||||
url: 'https://developer.mozilla.org/zh-CN/search?q=%keyword%',
|
||||
icon: 'website/mdn.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
design: {
|
||||
title: '设计',
|
||||
list: [
|
||||
{
|
||||
name: '站酷',
|
||||
desc: 'www.zcool.com.cn',
|
||||
url: 'https://www.zcool.com.cn/search/content?word=%keyword%',
|
||||
icon: 'website/zcool.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Iconfont',
|
||||
desc: 'www.iconfont.cn',
|
||||
url: 'https://www.iconfont.cn/search/index?searchType=icon&q=%keyword%',
|
||||
icon: 'website/iconfont.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
community: {
|
||||
title: '社区',
|
||||
list: [
|
||||
{
|
||||
name: '天涯',
|
||||
desc: 'search.tianya.cn',
|
||||
url: 'https://search.tianya.cn/bbs?q=%keyword%',
|
||||
icon: 'website/tianya.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '知乎',
|
||||
desc: 'www.zhihu.com',
|
||||
url: 'https://www.zhihu.com/search?type=content&q=%keyword%',
|
||||
icon: 'website/zhihu.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
siteName: 'Frost 网址导航',
|
||||
storage: {
|
||||
fontSize: 16,
|
||||
showSiteTitle: true,
|
||||
|
@@ -1,52 +1,3 @@
|
||||
/**
|
||||
* @description 解析推荐的关键词
|
||||
* @param {string} type 来源类型
|
||||
* @param {*} datas 关键词数据
|
||||
* @param {number} [max] 最大返回数量
|
||||
* @returns {string[]} 关键词列表
|
||||
*/
|
||||
function parseSearchWords(type = '', datas = null, max = 10) {
|
||||
if (!datas) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
let words = [];
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case '360':
|
||||
words = parseSearch360(datas);
|
||||
case 'baidu':
|
||||
words = parseSearchBaidu(datas);
|
||||
case 'bing':
|
||||
words = parseSearchBing(datas);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析失败');
|
||||
return [];
|
||||
}
|
||||
|
||||
const arrLength = words.length;
|
||||
const cutLength = Math.max(arrLength - max, 0);
|
||||
const cutStart = arrLength - cutLength;
|
||||
|
||||
words.splice(cutStart, cutLength);
|
||||
|
||||
let value = '';
|
||||
|
||||
for (value of words) {
|
||||
if (typeof value !== 'string') {
|
||||
console.error('关键词的数据类型错误');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 解析 360 推荐关键词(2021-12-29)
|
||||
* @param {{ result: { word: string }[] }} datas
|
||||
@@ -91,10 +42,41 @@ function parseSearchBing(datas) {
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef MDNSearchIndexDatas
|
||||
* @type {{ title: string, url: string }[]}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 解析 MDN 搜索关键词
|
||||
* @param {MDNSearchIndexDatas} datas 关键词数据
|
||||
* @param {string} keyword 输入的关键词
|
||||
* @param {number} [max] 最多返回的结果数量,默认为 10
|
||||
* @returns {MDNSearchIndexDatas} 匹配到的关键词
|
||||
*/
|
||||
function parseSearchMDN(datas = [], keyword = '', max = 10) {
|
||||
/** @type {MDNSearchIndexDatas} */
|
||||
const result = [];
|
||||
const word = String(keyword).toLowerCase();
|
||||
|
||||
let count = 0;
|
||||
|
||||
for (const item of datas) {
|
||||
if (item.title.toLowerCase().indexOf(word) > -1) {
|
||||
result.push(item);
|
||||
if ((count += 1) >= max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取搜索 URL
|
||||
* @param {string} type 搜索引擎类型
|
||||
* @param {string} word 搜索关键词
|
||||
* @param {string} type 搜索引擎类型
|
||||
* @param {string} word 搜索关键词
|
||||
* @param {string} [cbName] 回调函数名称,用于获取推荐关键词
|
||||
* @returns {(string|null)} 根据参数返回不同的结果
|
||||
* - 无 `cbName` 时返回搜索结果页面 URL
|
||||
@@ -128,6 +110,207 @@ function getSearchURL(type, word, cbName) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析推荐的关键词
|
||||
* @param {string} type 来源类型
|
||||
* @param {*} datas 关键词数据
|
||||
* @param {number} [max] 最大返回数量
|
||||
* @returns {string[]} 关键词列表
|
||||
*/
|
||||
function parseSearchWords(type = '', datas = null, max = 10) {
|
||||
if (!datas) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
let words = [];
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case '360':
|
||||
words = parseSearch360(datas);
|
||||
break;
|
||||
case 'baidu':
|
||||
words = parseSearchBaidu(datas);
|
||||
break;
|
||||
case 'bing':
|
||||
words = parseSearchBing(datas);
|
||||
break;
|
||||
default:
|
||||
console.error('来源类型错误');
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析失败');
|
||||
return [];
|
||||
}
|
||||
|
||||
const arrLength = words.length;
|
||||
const cutLength = Math.max(arrLength - max, 0);
|
||||
const cutStart = arrLength - cutLength;
|
||||
|
||||
words.splice(cutStart, cutLength);
|
||||
|
||||
let value = '';
|
||||
|
||||
for (value of words) {
|
||||
if (typeof value !== 'string') {
|
||||
console.error('关键词的数据类型错误');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return words;
|
||||
}
|
||||
|
||||
/** 搜索引擎列表 */
|
||||
const searchEngines = {
|
||||
search: {
|
||||
title: '搜索',
|
||||
list: [
|
||||
{
|
||||
name: '百度',
|
||||
desc: 'www.baidu.com',
|
||||
url: 'https://www.baidu.com/s?wd=%keyword%',
|
||||
icon: 'website/baidu.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '必应',
|
||||
desc: 'cn.bing.com',
|
||||
url: 'https://cn.bing.com/search?q=%keyword%',
|
||||
icon: 'website/bing.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '搜狗',
|
||||
desc: 'www.sogou.com',
|
||||
url: 'https://www.sogou.com/web?query=%keyword%',
|
||||
icon: 'website/sogou.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '360',
|
||||
desc: 'www.so.com',
|
||||
url: 'https://www.so.com/s?q=%keyword%',
|
||||
icon: 'website/360.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Google',
|
||||
desc: 'www.google.com',
|
||||
url: 'https://www.google.com/search?q=%keyword%',
|
||||
icon: 'website/google.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Yandex',
|
||||
desc: 'yandex.com',
|
||||
url: 'https://yandex.com/search/?text=%keyword%',
|
||||
icon: 'website/yandex.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
media: {
|
||||
title: '多媒体',
|
||||
list: [
|
||||
{
|
||||
name: '网易云音乐',
|
||||
desc: 'music.163.com',
|
||||
url: 'https://music.163.com/#/search/m/?s=%keyword%',
|
||||
icon: 'website/netease_music.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'AcFun',
|
||||
desc: 'www.acfun.cn',
|
||||
url: 'https://www.acfun.cn/search/?keyword=%keyword%',
|
||||
icon: 'website/acfun.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'bilibili',
|
||||
desc: 'search.bilibili.com',
|
||||
url: 'https://search.bilibili.com/all?keyword=%keyword%',
|
||||
icon: 'website/bilibili.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'QQ音乐',
|
||||
desc: 'y.qq.com',
|
||||
url: 'https://y.qq.com/n/ryqq/search?w=%keyword%',
|
||||
icon: 'website/qq_music.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
technology: {
|
||||
title: '技术',
|
||||
list: [
|
||||
{
|
||||
name: 'CSDN',
|
||||
desc: 'so.csdn.net',
|
||||
url: 'https://so.csdn.net/so/search/all?q=%keyword%',
|
||||
icon: 'website/csdn.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
desc: 'github.com',
|
||||
url: 'https://github.com/search?q=%keyword%',
|
||||
icon: 'website/github.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'MDN',
|
||||
desc: 'developer.mozilla.org',
|
||||
url: 'https://developer.mozilla.org/zh-CN/search?q=%keyword%',
|
||||
icon: 'website/mdn.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
design: {
|
||||
title: '设计',
|
||||
list: [
|
||||
{
|
||||
name: '站酷',
|
||||
desc: 'www.zcool.com.cn',
|
||||
url: 'https://www.zcool.com.cn/search/content?word=%keyword%',
|
||||
icon: 'website/zcool.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: 'Iconfont',
|
||||
desc: 'www.iconfont.cn',
|
||||
url: 'https://www.iconfont.cn/search/index?searchType=icon&q=%keyword%',
|
||||
icon: 'website/iconfont.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
},
|
||||
community: {
|
||||
title: '社区',
|
||||
list: [
|
||||
{
|
||||
name: '天涯',
|
||||
desc: 'search.tianya.cn',
|
||||
url: 'https://search.tianya.cn/bbs?q=%keyword%',
|
||||
icon: 'website/tianya.svg',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
name: '知乎',
|
||||
desc: 'www.zhihu.com',
|
||||
url: 'https://www.zhihu.com/search?type=content&q=%keyword%',
|
||||
icon: 'website/zhihu.svg',
|
||||
show: true
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {object} SearchItem
|
||||
* @property {string} name 名称
|
||||
@@ -154,3 +337,10 @@ const searchItems = [
|
||||
// wordURL: 'https://sg1.api.bing.com/qsonhs.aspx?type=cb&cb={cb}&q={w}',
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
getSearchURL,
|
||||
parseSearchMDN,
|
||||
parseSearchWords,
|
||||
searchEngines,
|
||||
};
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import config from './config.js';
|
||||
|
||||
class Utils {
|
||||
|
||||
constructor() { }
|
||||
@@ -9,7 +7,7 @@ class Utils {
|
||||
* @param {string} [value] 新的标题
|
||||
*/
|
||||
changeTitle(value) {
|
||||
document.title = (value ? (value + ' - ') : '') + config.siteName;
|
||||
document.title = ((value ? `${value} - ` : '') + 'Frost 网址导航');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -24,8 +24,7 @@ export default {
|
||||
name: 'FloatingBtn',
|
||||
data() {
|
||||
return {
|
||||
config: this.$root.config.storage,
|
||||
showInner: false
|
||||
showInner: false,
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -33,9 +32,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 设置动画
|
||||
*/
|
||||
/** 设置动画 */
|
||||
initAnimation() {
|
||||
var vm = this;
|
||||
var el = vm.$refs['floatingBtn'];
|
||||
@@ -53,16 +50,12 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换按钮显示
|
||||
*/
|
||||
/** 切换按钮显示 */
|
||||
toggleInnerBtns() {
|
||||
this.showInner = !this.showInner;
|
||||
},
|
||||
|
||||
/**
|
||||
* 返回主页
|
||||
*/
|
||||
/** 返回主页 */
|
||||
backToHome() {
|
||||
const routeName = 'Home';
|
||||
|
||||
@@ -77,19 +70,14 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
/** 刷新 */
|
||||
refreshPage() {
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换侧边菜单折叠状态
|
||||
*/
|
||||
/** 切换侧边菜单折叠状态 */
|
||||
toggleSideCollapse() {
|
||||
var cfg = this.config;
|
||||
cfg.sideMenuCollapse = !cfg.sideMenuCollapse;
|
||||
this.$store.dispatch('toggleSideCollapse');
|
||||
},
|
||||
|
||||
},
|
||||
|
11
src/main.js
11
src/main.js
@@ -4,10 +4,6 @@ import App from './App.vue';
|
||||
import router from './router';
|
||||
import store from './store';
|
||||
|
||||
import config from './assets/js/config';
|
||||
import navLinks from './assets/js/navLinks';
|
||||
import utils from './assets/js/utils';
|
||||
|
||||
import 'ress/ress.css';
|
||||
import 'font-awesome/css/font-awesome.min.css';
|
||||
import './assets/css/element.scss';
|
||||
@@ -20,12 +16,5 @@ Vue.config.productionTip = false;
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
data: function () {
|
||||
return {
|
||||
config,
|
||||
navLinks,
|
||||
utils
|
||||
}
|
||||
},
|
||||
render: h => h(App)
|
||||
}).$mount('#app');
|
||||
|
@@ -2,8 +2,8 @@ import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
|
||||
import routes from './routes';
|
||||
import config from '@/assets/js/config';
|
||||
import utils from '@/assets/js/utils';
|
||||
import store from '@/store/index';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
@@ -11,18 +11,16 @@ const router = new VueRouter({
|
||||
routes
|
||||
});
|
||||
|
||||
// to, from, next
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.meta.loadingBar) {
|
||||
config.loading.subPage = true;
|
||||
store.commit('toggleLoading', true);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// to, from
|
||||
router.afterEach((to) => {
|
||||
if (to.meta.loadingBar) {
|
||||
config.loading.subPage = false;
|
||||
store.commit('toggleLoading', false);
|
||||
}
|
||||
utils.changeTitle(to.meta.title);
|
||||
});
|
||||
|
@@ -4,9 +4,111 @@ import Vuex from 'vuex';
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {},
|
||||
state: {
|
||||
|
||||
/** 设置 */
|
||||
config: {
|
||||
fontSize: 16,
|
||||
showSiteTitle: true,
|
||||
sideMenuCollapse: true,
|
||||
searchEngine: '百度',
|
||||
searchSuggestion: false,
|
||||
},
|
||||
|
||||
/** 显示加载动画 */
|
||||
showLoading: false,
|
||||
|
||||
},
|
||||
getters: {},
|
||||
mutations: {},
|
||||
actions: {},
|
||||
mutations: {
|
||||
|
||||
/** 从本地储存中读取设置 */
|
||||
readConfig(state) {
|
||||
|
||||
let keyName = 'fn_config';
|
||||
let storage = localStorage.getItem(keyName);
|
||||
let parsed = null;
|
||||
let config = state.config;
|
||||
|
||||
try {
|
||||
parsed = JSON.parse(storage);
|
||||
} catch (error) {
|
||||
console.error('解析保存的设置失败。');
|
||||
}
|
||||
|
||||
if (parsed && parsed.constructor === Object) {
|
||||
// [本地储存中有数据,读取]
|
||||
for (let key in config) {
|
||||
let value = parsed[key];
|
||||
|
||||
if (typeof value !== 'undefined') {
|
||||
config[key] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// [本地储存中无数据,写入]
|
||||
localStorage.setItem(keyName, JSON.stringify(config));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/** 重置保存的设置 */
|
||||
resetConfig() {
|
||||
localStorage.removeItem('fn_config');
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 更新设置
|
||||
* @param {object} payload
|
||||
* @param {string} payload.key
|
||||
* @param {any} payload.value
|
||||
*/
|
||||
setConfig(state, payload) {
|
||||
|
||||
const {
|
||||
key: cKey = null,
|
||||
value: cValue = null,
|
||||
} = payload;
|
||||
|
||||
const { config } = state;
|
||||
|
||||
if (cKey === null) {
|
||||
console.error('更新设置失败,key 为空。');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof config[cKey] === 'undefined') {
|
||||
console.error('更新设置失败,key 不存在。');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新设置
|
||||
config[cKey] = cValue;
|
||||
// 保存设置
|
||||
localStorage.setItem('fn_config', JSON.stringify(config));
|
||||
|
||||
},
|
||||
|
||||
/** 切换加载动画显示隐藏 */
|
||||
toggleLoading(state, payload = false) {
|
||||
state.showLoading = payload;
|
||||
},
|
||||
|
||||
},
|
||||
actions: {
|
||||
|
||||
/** 切换导航链接侧边菜单显示隐藏 */
|
||||
toggleSideCollapse(store) {
|
||||
|
||||
const { sideMenuCollapse } = store.state.config;
|
||||
|
||||
store.commit('setConfig', {
|
||||
key: 'sideMenuCollapse',
|
||||
value: !sideMenuCollapse,
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
modules: {},
|
||||
});
|
||||
|
@@ -41,11 +41,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import navLinks from '@/assets/js/navLinks';
|
||||
|
||||
export default {
|
||||
name: 'AboutView',
|
||||
data() {
|
||||
return {
|
||||
navLinks: this.$root.navLinks,
|
||||
navLinks,
|
||||
info: {
|
||||
github: 'https://github.com/Frost-ZX/frost-navigation'
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<el-menu
|
||||
class="side-nav"
|
||||
default-active="search"
|
||||
:collapse="config.sideMenuCollapse"
|
||||
:collapse="appConfig.sideMenuCollapse"
|
||||
:collapse-transition="false"
|
||||
@select="changeCategory"
|
||||
>
|
||||
@@ -41,32 +41,32 @@
|
||||
<div class="wrapper">
|
||||
|
||||
<!-- 搜索引擎 -->
|
||||
<div v-show="show.searchEngine" class="search-engine">
|
||||
<div v-show="show.searchNet" class="search-engine">
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<div :class="['search-bar', 'shadow-3', { suggest: showSES }]">
|
||||
|
||||
<!-- 输入 -->
|
||||
<input
|
||||
v-model="searchEngine.keyword"
|
||||
v-model="searchNet.keyword"
|
||||
class="input"
|
||||
type="text"
|
||||
@blur="searchEngine.isFocus = false"
|
||||
@focus="searchEngine.isFocus = true"
|
||||
@keydown.enter.exact="searchEngineSubmit()"
|
||||
@blur="searchNet.isFocus = false"
|
||||
@focus="searchNet.isFocus = true"
|
||||
@keydown.enter.exact="searchNetSubmit()"
|
||||
/>
|
||||
|
||||
<!-- 清除 -->
|
||||
<div
|
||||
v-show="searchEngine.keyword.length > 0"
|
||||
v-show="searchNet.keyword.length > 0"
|
||||
class="btn btn-clear"
|
||||
@click="searchEngine.keyword = ''"
|
||||
@click="searchNet.keyword = ''"
|
||||
>
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
|
||||
<!-- 搜索 -->
|
||||
<div class="btn btn-search" @click="searchEngineSubmit()">
|
||||
<div class="btn btn-search" @click="searchNetSubmit()">
|
||||
<i class="el-icon-search"></i>
|
||||
</div>
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
<div class="suggestion shadow-3">
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in searchEngine.suggest.list"
|
||||
v-for="item in searchNet.suggest.list"
|
||||
:key="item.id"
|
||||
@click="searchEngine.keyword = item.label"
|
||||
@click="searchNet.keyword = item.label"
|
||||
>
|
||||
<!-- 文本 -->
|
||||
<span class="label">{{ item.label }}</span>
|
||||
@@ -86,7 +86,7 @@
|
||||
effect="plain"
|
||||
size="mini"
|
||||
type="success"
|
||||
>{{ searchEngine.suggest.name }}</el-tag>
|
||||
>{{ searchNet.suggest.name }}</el-tag>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -95,13 +95,13 @@
|
||||
|
||||
<!-- 选择搜索引擎 -->
|
||||
<el-radio-group
|
||||
v-model="config.searchEngine"
|
||||
v-model="configSearchEngine"
|
||||
size="small"
|
||||
:class="['search-type', { fade: searchEngine.isFocus }]"
|
||||
:class="['search-type', { fade: searchNet.isFocus }]"
|
||||
>
|
||||
<!-- 分类 -->
|
||||
<div
|
||||
v-for="(category, cIndex) in searchEngine.list"
|
||||
v-for="(category, cIndex) in searchNet.list"
|
||||
:key="cIndex"
|
||||
class="category"
|
||||
>
|
||||
@@ -230,6 +230,11 @@
|
||||
* @property {string} link
|
||||
*/
|
||||
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
import navLinks from '@/assets/js/navLinks';
|
||||
import searchUtils from '@/assets/js/search-utils';
|
||||
import utils from '@/assets/js/utils';
|
||||
import IconElement from '@/components/IconElement.vue';
|
||||
|
||||
export default {
|
||||
@@ -239,19 +244,19 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: this.$root.config.storage,
|
||||
utils: this.$root.utils,
|
||||
// 显示的内容
|
||||
|
||||
/** 显示的内容 */
|
||||
show: {
|
||||
searchEngine: true,
|
||||
searchNet: true,
|
||||
searchLink: false,
|
||||
linkTree: false,
|
||||
},
|
||||
// 搜索引擎
|
||||
searchEngine: {
|
||||
|
||||
/** 网络搜索 */
|
||||
searchNet: {
|
||||
isFocus: false,
|
||||
keyword: '',
|
||||
list: this.$root.config.searchEngines,
|
||||
list: searchUtils.searchEngines,
|
||||
url: '',
|
||||
debounce: null,
|
||||
suggest: {
|
||||
@@ -259,17 +264,21 @@ export default {
|
||||
name: '',
|
||||
},
|
||||
},
|
||||
// 导航链接
|
||||
navLinks: this.$root.navLinks,
|
||||
// 当前显示的链接
|
||||
|
||||
/** 导航链接数据 */
|
||||
navLinks,
|
||||
|
||||
/** 当前显示的链接 */
|
||||
currentLinks: [],
|
||||
// 搜索链接
|
||||
|
||||
/** 链接搜索 */
|
||||
searchLink: {
|
||||
debounce: null,
|
||||
keyword: '',
|
||||
type: 'all'
|
||||
},
|
||||
// 链接详情
|
||||
|
||||
/** 链接详情 */
|
||||
linkDetail: {
|
||||
show: false,
|
||||
id: '',
|
||||
@@ -279,19 +288,39 @@ export default {
|
||||
desc: '',
|
||||
update: '',
|
||||
},
|
||||
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
...mapState({
|
||||
appConfig: 'config',
|
||||
}),
|
||||
|
||||
/** 设置 - 搜索引擎名称 */
|
||||
configSearchEngine: {
|
||||
/** @this */
|
||||
get() {
|
||||
return this.appConfig.searchEngine;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setConfig', {
|
||||
key: 'searchEngine',
|
||||
value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示搜索引擎关键词建议
|
||||
* @description 显示搜索引擎关键词建议
|
||||
* @this
|
||||
*/
|
||||
showSES() {
|
||||
var se = this.searchEngine;
|
||||
var sn = this.searchNet;
|
||||
var isShow = (
|
||||
(se.isFocus) &&
|
||||
(se.keyword !== '') &&
|
||||
(se.suggest.list.length > 0)
|
||||
(sn.isFocus) &&
|
||||
(sn.keyword !== '') &&
|
||||
(sn.suggest.list.length > 0)
|
||||
);
|
||||
|
||||
return isShow;
|
||||
@@ -299,18 +328,20 @@ export default {
|
||||
|
||||
},
|
||||
watch: {
|
||||
'searchEngine.keyword': {
|
||||
|
||||
'searchNet.keyword': {
|
||||
handler(value) {
|
||||
if (!this.config.searchSuggestion) {
|
||||
if (!this.appConfig.searchSuggestion) {
|
||||
return;
|
||||
}
|
||||
clearInterval(this.searchEngine.debounce);
|
||||
this.searchEngine.debounce = setTimeout(() => {
|
||||
clearInterval(this.searchNet.debounce);
|
||||
this.searchNet.debounce = setTimeout(() => {
|
||||
value = window.encodeURIComponent(value);
|
||||
this.searchEngineGS(value);
|
||||
this.searchNetGS(value);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
'searchLink.keyword': {
|
||||
handler(value) {
|
||||
clearTimeout(this.searchLink.debounce);
|
||||
@@ -319,12 +350,14 @@ export default {
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
'searchLink.type': {
|
||||
handler() {
|
||||
// 更改搜索类型时自动重新搜索
|
||||
this.$refs.linkTree.filter(this.searchLink.keyword);
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -334,17 +367,17 @@ export default {
|
||||
|
||||
if (index === 'search') {
|
||||
this.currentLinks = [];
|
||||
show.searchEngine = true;
|
||||
show.searchNet = true;
|
||||
show.searchLink = false;
|
||||
show.linkTree = false;
|
||||
} else if (index === 'all') {
|
||||
this.currentLinks = navLinks.list;
|
||||
show.searchEngine = false;
|
||||
show.searchNet = false;
|
||||
show.searchLink = true;
|
||||
show.linkTree = true;
|
||||
} else {
|
||||
this.currentLinks = navLinks.list[Number(index)].items;
|
||||
show.searchEngine = false;
|
||||
show.searchNet = false;
|
||||
show.searchLink = true;
|
||||
show.linkTree = true;
|
||||
}
|
||||
@@ -394,45 +427,14 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef MDNSearchIndexDatas
|
||||
* @type {{ title: string, url: string }[]}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 解析 MDN 搜索关键词
|
||||
* @param {MDNSearchIndexDatas} datas 关键词数据
|
||||
* @param {string} keyword 输入的关键词
|
||||
* @param {number} [max] 最多返回的结果数量,默认为 10
|
||||
* @returns {MDNSearchIndexDatas} 匹配到的关键词
|
||||
*/
|
||||
parseMDNSearchWords(datas = [], keyword = '', max = 10) {
|
||||
/** @type {MDNSearchIndexDatas} */
|
||||
const result = [];
|
||||
const word = String(keyword).toLowerCase();
|
||||
|
||||
let count = 0;
|
||||
|
||||
for (const item of datas) {
|
||||
if (item.title.toLowerCase().indexOf(word) > -1) {
|
||||
result.push(item);
|
||||
if ((count += 1) >= max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 搜索引擎(获取关键词建议)
|
||||
* @param {string} keyword 当前输入的关键词
|
||||
*/
|
||||
searchEngineGS(keyword) {
|
||||
var suggest = this.searchEngine.suggest;
|
||||
searchNetGS(keyword) {
|
||||
var suggest = this.searchNet.suggest;
|
||||
// 设置的搜索引擎名称
|
||||
var configSE = this.config.searchEngine;
|
||||
var configSE = this.configSearchEngine;
|
||||
// 关键词建议来源信息(默认使用百度)
|
||||
var reqURLs = [
|
||||
{
|
||||
@@ -506,7 +508,7 @@ export default {
|
||||
};
|
||||
|
||||
// 使用 JSONP 获取
|
||||
this.utils.jsonp({
|
||||
utils.jsonp({
|
||||
url: reqURL,
|
||||
cbName: 'cbSES',
|
||||
cbFunc
|
||||
@@ -514,11 +516,11 @@ export default {
|
||||
},
|
||||
|
||||
/** 搜索引擎(搜索) */
|
||||
searchEngineSubmit() {
|
||||
searchNetSubmit() {
|
||||
var vm = this;
|
||||
var se = this.searchEngine;
|
||||
var selectedSE = this.config.searchEngine;
|
||||
var keyword = se.keyword;
|
||||
var sn = this.searchNet;
|
||||
var selectedSE = this.configSearchEngine;
|
||||
var keyword = sn.keyword;
|
||||
var url = '';
|
||||
|
||||
if (keyword) {
|
||||
@@ -527,8 +529,8 @@ export default {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let category in se.list) {
|
||||
let list = se.list[category].list;
|
||||
for (let category in sn.list) {
|
||||
let list = sn.list[category].list;
|
||||
|
||||
for (let index in list) {
|
||||
if (list[index].name === selectedSE) {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
<el-form-item label="字体大小" class="set-font">
|
||||
<el-input-number
|
||||
v-model="config.fontSize"
|
||||
v-model="configFontSize"
|
||||
:min="12"
|
||||
:max="32"
|
||||
controls-position="right"
|
||||
@@ -16,28 +16,23 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="显示网站标题">
|
||||
<el-switch v-model="config.showSiteTitle"></el-switch>
|
||||
<el-switch v-model="configShowSiteTitle"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="折叠主页侧边菜单">
|
||||
<el-switch v-model="config.sideMenuCollapse"></el-switch>
|
||||
<el-switch v-model="configSideMenuCollapse"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="获取搜索引擎关键词建议">
|
||||
<el-switch v-model="config.searchSuggestion"></el-switch>
|
||||
<el-switch v-model="configSearchSuggestion"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="清除数据">
|
||||
<el-button
|
||||
type="danger"
|
||||
size="medium"
|
||||
@click="resetDatas('settings')"
|
||||
@click="resetDatas()"
|
||||
>清除设置</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="medium"
|
||||
@click="resetDatas('cache')"
|
||||
>清除缓存</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
@@ -47,34 +42,83 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'SettingsView',
|
||||
data() {
|
||||
return {
|
||||
config: this.$root.config.storage,
|
||||
}
|
||||
return { }
|
||||
},
|
||||
computed: {
|
||||
|
||||
...mapState({
|
||||
appConfig: 'config',
|
||||
}),
|
||||
|
||||
configFontSize: {
|
||||
/** @this */
|
||||
get() {
|
||||
return this.appConfig.fontSize;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setConfig', {
|
||||
key: 'fontSize',
|
||||
value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
configShowSiteTitle: {
|
||||
/** @this */
|
||||
get() {
|
||||
return this.appConfig.showSiteTitle;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setConfig', {
|
||||
key: 'showSiteTitle',
|
||||
value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
configSideMenuCollapse: {
|
||||
/** @this */
|
||||
get() {
|
||||
return this.appConfig.sideMenuCollapse;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setConfig', {
|
||||
key: 'sideMenuCollapse',
|
||||
value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
configSearchSuggestion: {
|
||||
/** @this */
|
||||
get() {
|
||||
return this.appConfig.searchSuggestion;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setConfig', {
|
||||
key: 'searchSuggestion',
|
||||
value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 清除数据
|
||||
*
|
||||
* @param {string} type 清除类型(cache、settings)
|
||||
*/
|
||||
resetDatas(type) {
|
||||
/** 清除数据 */
|
||||
resetDatas() {
|
||||
this.$confirm('确定要清除吗?', '', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
|
||||
if (type === 'cache') {
|
||||
localStorage.removeItem('navLinksCache');
|
||||
} else if (type === 'settings') {
|
||||
localStorage.removeItem('navConfig');
|
||||
} else {
|
||||
return
|
||||
}
|
||||
this.$store.commit('resetConfig');
|
||||
|
||||
this.$message({
|
||||
message: '已清除,2s 后自动刷新',
|
||||
|
@@ -43,11 +43,7 @@
|
||||
export default {
|
||||
name: 'ToolsDetail',
|
||||
data() {
|
||||
return {
|
||||
|
||||
|
||||
|
||||
}
|
||||
return { }
|
||||
},
|
||||
computed: {
|
||||
|
||||
|
Reference in New Issue
Block a user