优化代码

This commit is contained in:
2022-03-17 13:09:58 +08:00
parent fcd91876ce
commit 58386fd28c
13 changed files with 1931 additions and 1887 deletions

View File

@@ -1,307 +1,321 @@
<template>
<div id="app">
<!-- 加载动画 -->
<div v-show="config.loading.subPage" class="loading-bar">
<div class="bar-content"></div>
</div>
<!-- 内容区域 -->
<el-container>
<!-- Header -->
<el-header class="main-header shadow-1">
<!-- LOGO -->
<div class="logo"></div>
<!-- 菜单 -->
<el-menu class="menu" :default-active="headerDefaultActive" mode="horizontal" router>
<!-- 标题 -->
<el-menu-item v-show="config.storage.showSiteTitle"
index="title" class="title" disabled
>Frost 网址导航</el-menu-item>
<!-- 菜单项 -->
<el-menu-item v-for="item in headerMenuItems" :key="item.id"
class="item-normal" :index="item.id" :route="{ name: item.routeName }"
>{{ item.label }}</el-menu-item>
<!-- 切换下拉菜单 -->
<el-menu-item :class="['item-dropdown', { active: showHeaderDropdown }]"
@click="showHeaderDropdown = !showHeaderDropdown"
>
<i class="fa fa-bars"></i>
</el-menu-item>
</el-menu>
</el-header>
<!-- 下拉菜单 -->
<el-menu :class="['header-dropdown', 'shadow-2', { show: showHeaderDropdown }]"
:default-active="headerDefaultActive" router
>
<!-- 菜单项 -->
<el-menu-item v-for="item in headerMenuItems" :key="item.id"
class="item-normal" :index="item.id" :route="{ name: item.routeName }"
>{{ item.label }}</el-menu-item>
</el-menu>
<!-- Container -->
<keep-alive>
<router-view class="main-container" />
</keep-alive>
</el-container>
<!-- 悬浮按钮 -->
<FloatingBtn />
<div id="app">
<!-- 加载动画 -->
<div v-show="config.loading.subPage" class="loading-bar">
<div class="bar-content"></div>
</div>
<!-- 内容区域 -->
<el-container>
<!-- Header -->
<el-header class="main-header shadow-1">
<!-- LOGO -->
<div class="logo"></div>
<!-- 菜单 -->
<el-menu class="menu" :default-active="headerDefaultActive" mode="horizontal" router>
<!-- 标题 -->
<el-menu-item
v-show="config.storage.showSiteTitle"
index="title"
class="title"
disabled
>Frost 网址导航</el-menu-item>
<!-- 菜单项 -->
<el-menu-item
v-for="item in headerMenuItems"
:key="item.id"
class="item-normal"
:index="item.id"
:route="{ name: item.routeName }"
>{{ item.label }}</el-menu-item>
<!-- 切换下拉菜单 -->
<el-menu-item
:class="['item-dropdown', { active: showHeaderDropdown }]"
@click="showHeaderDropdown = !showHeaderDropdown"
>
<i class="fa fa-bars"></i>
</el-menu-item>
</el-menu>
</el-header>
<!-- 下拉菜单 -->
<el-menu
:class="['header-dropdown', 'shadow-2', { show: showHeaderDropdown }]"
:default-active="headerDefaultActive"
router
>
<!-- 菜单项 -->
<el-menu-item
v-for="item in headerMenuItems"
:key="item.id"
class="item-normal"
:index="item.id"
:route="{ name: item.routeName }"
>{{ item.label }}</el-menu-item>
</el-menu>
<!-- Container -->
<keep-alive>
<router-view class="main-container" />
</keep-alive>
</el-container>
<!-- 悬浮按钮 -->
<FloatingBtn />
</div>
</template>
<script>
import FloatingBtn from '@/components/FloatingBtn.vue';
export default {
name: 'App',
components: {
FloatingBtn,
},
data() {
return {
config: this.$root.config,
debounce: {
saveConfig: null,
updateConfig: null
},
// Header 菜单项
headerMenuItems: [
{
id: 'home',
label: '主页',
routeName: 'Home'
},
{
id: 'tools',
label: '小工具',
routeName: 'Tools'
},
{
id: 'settings',
label: '设置',
routeName: 'Settings'
},
{
id: 'about',
label: '关于',
routeName: 'About'
}
],
// 显示下拉菜单
showHeaderDropdown: false
}
},
computed: {
// Header 默认激活的菜单项
headerDefaultActive() {
var routeName = this.$route.name;
var item = '';
if (routeName) {
item = routeName.toLowerCase();
}
return item;
}
},
watch: {
// 路由名称
'$route.name': {
handler() {
// 切换路由时隐藏下拉菜单
this.showHeaderDropdown = false;
}
name: 'App',
components: {
FloatingBtn,
},
data() {
return {
config: this.$root.config,
debounce: {
saveConfig: null,
updateConfig: null
},
// Header 菜单项
headerMenuItems: [
{
id: 'home',
label: '主页',
routeName: 'Home'
},
// 更新储存的设置
'config.storage': {
handler(obj) {
clearTimeout(this.debounce.saveConfig);
this.debounce.saveConfig = setTimeout(() => {
localStorage.setItem('navConfig', JSON.stringify(obj));
}, 2000);
},
deep: true
{
id: 'tools',
label: '小工具',
routeName: 'Tools'
},
// 改变字体大小
'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);
{
id: 'settings',
label: '设置',
routeName: 'Settings'
},
{
id: 'about',
label: '关于',
routeName: 'About'
}
],
// 显示下拉菜单
showHeaderDropdown: false
}
},
computed: {
// Header 默认激活的菜单项
headerDefaultActive() {
var routeName = this.$route.name;
var item = '';
if (routeName) {
item = routeName.toLowerCase();
}
return item;
}
},
watch: {
// 路由名称
'$route.name': {
handler() {
// 切换路由时隐藏下拉菜单
this.showHeaderDropdown = false;
}
},
// 更新储存的设置
'config.storage': {
handler(obj) {
clearTimeout(this.debounce.saveConfig);
this.debounce.saveConfig = setTimeout(() => {
localStorage.setItem('navConfig', JSON.stringify(obj));
}, 2000);
},
deep: true
},
// 改变字体大小
'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>
<style lang="less">
.loading-bar {
position: absolute;
z-index: 1000;
top: 0;
left: 0;
width: 100%;
height: 0.2rem;
.bar-content {
position: absolute;
z-index: 1000;
top: 0;
left: 0;
width: 100%;
height: 0.2rem;
width: 0;
height: 100%;
background-color: @colorPrimary;
animation: loadingBar 4s ease-in-out infinite;
}
.bar-content {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 100%;
background-color: @colorPrimary;
animation: loadingBar 4s ease-in-out infinite;
@keyframes loadingBar {
0% {
left: 0;
width: 0;
}
@keyframes loadingBar {
0% {
left: 0;
width: 0;
}
50% {
left: 0;
width: 100%;
}
100% {
left: 100%;
width: 0;
}
50% {
left: 0;
width: 100%;
}
100% {
left: 100%;
width: 0;
}
}
}
.main-header {
display: flex;
align-items: center;
z-index: 200;
height: @headerHeight !important;
background-color: #FFF;
display: flex;
align-items: center;
z-index: 200;
height: @headerHeight !important;
background-color: #FFF;
@media screen and (min-width: 30rem) {
.menu .item-dropdown {
display: none;
}
}
@media screen and (max-width: 30rem) {
.logo {
display: none;
}
.menu {
.item-normal {
display: none;
}
.item-dropdown {
position: absolute;
right: 0;
margin: 0;
padding: 0;
i {
font-size: 1.75em;
}
}
}
@media screen and (min-width: 30rem) {
.menu .item-dropdown {
display: none;
}
}
@media screen and (max-width: 30rem) {
.logo {
flex-shrink: 0;
margin-right: 1rem;
width: 2rem;
height: 2rem;
background-image: url("./assets/icon/favicon.svg");
background-position: center;
background-repeat: no-repeat;
background-size: contain;
display: none;
}
.menu {
flex-grow: 1;
height: 2.5rem;
border: none !important;
.item-normal {
display: none;
}
> li {
padding: 0 1rem;
height: 100%;
line-height: 2.5rem;
.item-dropdown {
position: absolute;
right: 0;
margin: 0;
padding: 0;
&.is-active {
border-bottom-color: transparent;
color: @colorPrimary !important;
}
}
.title {
padding-left: 0;
font-size: 1.2rem;
color: @colorPrimary;
opacity: 1;
cursor: default;
i {
font-size: 1.75em;
}
}
}
}
.logo {
flex-shrink: 0;
margin-right: 1rem;
width: 2rem;
height: 2rem;
background-image: url("./assets/icon/favicon.svg");
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
.menu {
flex-grow: 1;
height: 2.5rem;
border: none !important;
> li {
padding: 0 1rem;
height: 100%;
line-height: 2.5rem;
&.is-active {
border-bottom-color: transparent;
color: @colorPrimary !important;
}
}
.title {
padding-left: 0;
font-size: 1.2rem;
color: @colorPrimary;
opacity: 1;
cursor: default;
}
}
}
.main-container {
height: calc(100vh - @headerHeight);
height: calc(100vh - @headerHeight);
}
</style>
<style lang="less" scoped>
.header-dropdown {
position: absolute !important;
z-index: 150;
top: @headerHeight;
left: 0;
width: 100%;
overflow: hidden;
transform: translateY(-100%);
transition: transform @transitionTime;
position: absolute !important;
z-index: 150;
top: @headerHeight;
left: 0;
width: 100%;
overflow: hidden;
transform: translateY(-100%);
transition: transform @transitionTime;
&.show {
transform: translateY(0);
}
&.show {
transform: translateY(0);
}
}
.menu .item-dropdown {
i {
transition: color @transitionTime;
}
i {
transition: color @transitionTime;
}
&.active i {
color: @colorPrimary;
}
&.active i {
color: @colorPrimary;
}
}
</style>

View File

@@ -1,24 +1,30 @@
// 工具信息列表
let navTools = {
// 分类
/**
* @typedef {object} ToolItem
* @property {string} title 工具标题
* @property {string} [desc] 工具简介
* @property {string} component 组件名称
* @property {string} [update] 更新日期
* @property {string} [version] 版本号
* @property {boolean} enabled 启用状态
*/
/**
* @typedef {object} ToolCategory
* @property {string} title 分类标题
* @property {Object.<string, ToolItem>} list 工具列表
*/
/** @type {Object.<string, ToolCategory>} */
const navTools = {
calculation: {
// 分类标题
title: '计算',
// 分类列表
list: {
// 工具
'download-time': {
// 工具标题
title: '下载用时计算',
// 工具简介
desc: '根据设定的文件大小和下载速度简单计算大约下载完成所需的时间。',
// 组件名称
component: 'CalcDownloadTime',
// 更新时间
update: '2021-12-06',
// 版本
version: '1',
// 启用状态
enabled: true
},
'ratio': {

View File

@@ -5,8 +5,7 @@ class Utils {
constructor() { }
/**
* 改变网页标题
*
* @description 改变网页标题
* @param {string} [value] 新的标题
*/
changeTitle(value) {
@@ -14,8 +13,7 @@ class Utils {
}
/**
* JSONP
*
* @description JSONP
* @param {object} options 配置选项
*/
jsonp(options) {
@@ -40,11 +38,9 @@ class Utils {
}
/**
* 以文本方式读取文件(异步)
*
* @description 以文本方式读取文件(异步)
* @param {Event} ev 输入框 change 事件对象
* @param {boolean} resetValue 是否自动重置输入框 value 属性
*
* @returns {Promise<string[]>} `{ name: 文件名, content: 文件内容 }`
*/
readFileAsText(ev, resetValue) {

View File

@@ -1,214 +1,214 @@
<template>
<div ref="floatingBtn" class="floating-btn">
<div :class="['btns-inner', { show: showInner }]">
<button class="btn" type="button" title="折叠侧边菜单" @click="toggleSideCollapse()">
<i class="el-icon-menu" aria-hidden="true"></i>
</button>
<button class="btn" type="button" title="刷新" @click="refreshPage()">
<i class="el-icon-refresh-right" aria-hidden="true"></i>
</button>
<button class="btn" type="button" title="返回主页" @click="backToHome()">
<i class="el-icon-s-home" aria-hidden="true"></i>
</button>
</div>
<div class="btns-outer">
<button class="btn" type="button" title="菜单">
<i class="fa fa-bars" aria-hidden="true"></i>
</button>
</div>
<div ref="floatingBtn" class="floating-btn">
<div :class="['btns-inner', { show: showInner }]">
<button class="btn" type="button" title="折叠侧边菜单" @click="toggleSideCollapse()">
<i class="el-icon-menu" aria-hidden="true"></i>
</button>
<button class="btn" type="button" title="刷新" @click="refreshPage()">
<i class="el-icon-refresh-right" aria-hidden="true"></i>
</button>
<button class="btn" type="button" title="返回主页" @click="backToHome()">
<i class="el-icon-s-home" aria-hidden="true"></i>
</button>
</div>
<div class="btns-outer">
<button class="btn" type="button" title="菜单">
<i class="fa fa-bars" aria-hidden="true"></i>
</button>
</div>
</div>
</template>
<script>
export default {
name: 'FloatingBtn',
data() {
return {
config: this.$root.config.storage,
showInner: false
}
name: 'FloatingBtn',
data() {
return {
config: this.$root.config.storage,
showInner: false
}
},
mounted () {
this.initAnimation();
},
methods: {
/**
* 设置动画
*/
initAnimation() {
var vm = this;
var el = vm.$refs['floatingBtn'];
var btns = el.querySelectorAll('.btn');
var className = 'animate';
btns.forEach((elem) => {
elem.addEventListener('click', function () {
this.classList.remove(className);
setTimeout(() => {
this.classList.add(className);
vm.toggleInnerBtns();
}, 20);
});
});
},
mounted () {
this.initAnimation();
/**
* 切换按钮显示
*/
toggleInnerBtns() {
this.showInner = !this.showInner;
},
methods: {
/**
* 设置动画
*/
initAnimation() {
var vm = this;
var el = vm.$refs['floatingBtn'];
var btns = el.querySelectorAll('.btn');
var className = 'animate';
btns.forEach((elem) => {
elem.addEventListener('click', function () {
this.classList.remove(className);
setTimeout(() => {
this.classList.add(className);
vm.toggleInnerBtns();
}, 20);
});
});
},
/**
* 切换按钮显示
*/
toggleInnerBtns() {
this.showInner = !this.showInner;
},
/**
* 返回主页
*/
backToHome() {
var routeName = 'Home';
if (this.$route.name != routeName) {
this.$router.push({
name: routeName
}).then(() => {
window.location.reload();
});
} else {
this.$message({
duration: 2000,
message: '已经在主页啦~',
type: 'warning'
});
}
},
/**
* 刷新
*/
refreshPage() {
window.location.reload();
},
/**
* 切换侧边菜单折叠状态
*/
toggleSideCollapse() {
var cfg = this.config;
cfg.sideMenuCollapse = !cfg.sideMenuCollapse;
},
/**
* 返回主页
*/
backToHome() {
var routeName = 'Home';
if (this.$route.name != routeName) {
this.$router.push({
name: routeName
}).then(() => {
window.location.reload();
});
} else {
this.$message({
duration: 2000,
message: '已经在主页啦~',
type: 'warning'
});
}
},
/**
* 刷新
*/
refreshPage() {
window.location.reload();
},
/**
* 切换侧边菜单折叠状态
*/
toggleSideCollapse() {
var cfg = this.config;
cfg.sideMenuCollapse = !cfg.sideMenuCollapse;
},
},
}
</script>
<style lang="less" scoped>
.floating-btn {
position: fixed;
z-index: 5000;
right: 2rem;
bottom: 2rem;
text-align: center;
position: fixed;
z-index: 5000;
right: 2rem;
bottom: 2rem;
text-align: center;
.btns-inner.show .btn {
width: 2.6rem;
height: 2.6rem;
font-size: 1rem;
color: #FFF;
}
.btns-inner.show .btn {
width: 2.6rem;
height: 2.6rem;
font-size: 1rem;
color: #FFF;
}
@media screen and (max-width: 400px) {
right: 1.5rem;
bottom: 1.5rem;
}
@media screen and (max-width: 400px) {
right: 1.5rem;
bottom: 1.5rem;
}
}
.btns-inner {
display: inline-block;
display: inline-block;
.btn {
width: 0;
height: 0;
background-color: @colorSecondary;
font-size: 0;
color: transparent;
transition: all @transitionTime;
.btn {
width: 0;
height: 0;
background-color: @colorSecondary;
font-size: 0;
color: transparent;
transition: all @transitionTime;
&:not(:first-child) {
margin-top: 0.75rem;
}
&:not(:first-child) {
margin-top: 0.75rem;
}
}
}
.btns-outer {
display: block;
display: block;
.btn {
margin-top: 1rem;
width: 3.2rem;
height: 3.2rem;
background-color: @colorPrimary;
}
.btn {
margin-top: 1rem;
width: 3.2rem;
height: 3.2rem;
background-color: @colorPrimary;
}
}
.btn {
display: flex;
align-items: center;
justify-content: center;
position: relative;
right: 0;
bottom: 0;
box-shadow: 0 0.15rem 0.15rem 0 rgba(0, 0, 0, 0.14),
0 0.2rem 0.1rem -0.15rem rgba(0, 0, 0, 0.12),
0 0.1rem 0.3rem 0 rgba(0, 0, 0, 0.20);
display: flex;
align-items: center;
justify-content: center;
position: relative;
right: 0;
bottom: 0;
box-shadow: 0 0.15rem 0.15rem 0 rgba(0, 0, 0, 0.14),
0 0.2rem 0.1rem -0.15rem rgba(0, 0, 0, 0.12),
0 0.1rem 0.3rem 0 rgba(0, 0, 0, 0.20);
border-radius: 50%;
font-size: 1rem;
color: #FFF;
overflow: hidden;
cursor: pointer;
&::before, &::after {
content: "";
display: block;
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
font-size: 1rem;
color: #FFF;
overflow: hidden;
cursor: pointer;
background-color: #FFF;
opacity: 0;
transition: opacity @transitionTime;
transform: translate(-50%, -50%);
}
&::before, &::after {
content: "";
display: block;
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #FFF;
opacity: 0;
transition: opacity @transitionTime;
transform: translate(-50%, -50%);
}
&:hover::before {
opacity: 0.1;
}
&:hover::before {
opacity: 0.1;
}
&.animate::after {
animation: floatingBtnClick calc(@transitionTime * 2) linear;
}
&.animate::after {
animation: floatingBtnClick calc(@transitionTime * 2) linear;
}
}
@keyframes floatingBtnClick {
0% {
width: 0;
height: 0;
opacity: 0;
}
25% {
opacity: 0.2;
}
50% {
width: 100%;
height: 100%;
}
75% {
opacity: 0.2;
}
100% {
width: 100%;
height: 100%;
opacity: 0;
}
0% {
width: 0;
height: 0;
opacity: 0;
}
25% {
opacity: 0.2;
}
50% {
width: 100%;
height: 100%;
}
75% {
opacity: 0.2;
}
100% {
width: 100%;
height: 100%;
opacity: 0;
}
}
</style>

View File

@@ -1,67 +1,67 @@
<template>
<i class="icon-element bg-center-contain" :style="iconStyle"></i>
<i class="icon-element bg-center-contain" :style="iconStyle"></i>
</template>
<script>
export default {
name: 'IconElement',
props: {
// 来源inner、outer
from: {
type: String,
default: 'inner'
},
// 路径
// 若为 inner自动添加 @/assets/icon/
path: {
type: String,
default: 'unknown.svg'
},
// 尺寸
size: {
type: String,
default: '1em'
},
// 显示图标
showIcon: {
type: Boolean,
default: true
}
name: 'IconElement',
props: {
// 来源inner、outer
from: {
type: String,
default: 'inner'
},
computed: {
// 图标样式
iconStyle() {
var style = {
width: this.size,
height: this.size
};
var iconPath = '';
// 不显示图标内容
if (!this.showIcon) {
return style;
}
if (this.from === 'inner') {
// 内部
iconPath = require(`@/assets/icon/${this.path}`);
} else if (this.from === 'outer') {
// 外部
iconPath = this.path;
}
style.backgroundImage = `url('${iconPath}')`;
return style;
}
// 路径
// 若为 inner自动添加 @/assets/icon/
path: {
type: String,
default: 'unknown.svg'
},
// 尺寸
size: {
type: String,
default: '1em'
},
// 显示图标
showIcon: {
type: Boolean,
default: true
}
},
computed: {
// 图标样式
iconStyle() {
var style = {
width: this.size,
height: this.size
};
var iconPath = '';
// 不显示图标内容
if (!this.showIcon) {
return style;
}
if (this.from === 'inner') {
// 内部
iconPath = require(`@/assets/icon/${this.path}`);
} else if (this.from === 'outer') {
// 外部
iconPath = this.path;
}
style.backgroundImage = `url('${iconPath}')`;
return style;
}
}
}
</script>
<style lang="less" scoped>
.icon-element {
display: inline-block;
vertical-align: middle;
font-style: normal;
display: inline-block;
vertical-align: middle;
font-style: normal;
}
</style>

View File

@@ -1,164 +1,190 @@
<template>
<div class="tool-page">
<div class="info">
<div class="title">配置选项 &amp; 结果</div>
<div class="content">
<el-form label-width="6rem">
<!-- 字符串长度 -->
<el-form-item label="字符串长度">
<el-input v-model.number="info.strLength" type="number" min="1" max="1024"></el-input>
</el-form-item>
<!-- 字符串格式 -->
<el-form-item label="字符串格式">
<el-checkbox v-model="info.option.hasNum"
name="option" label="hasNum"
>数字</el-checkbox>
<el-checkbox v-model="info.option.hasChar"
name="option" label="hasChar"
>字母</el-checkbox>
<el-checkbox v-model="info.option.hasSymbol"
name="option" label="hasSymbol"
>其他符号</el-checkbox>
<el-checkbox v-model="info.option.caseSensitive"
name="option" label="caseSensitive"
>大小写</el-checkbox>
<el-checkbox v-model="info.option.lowerCase"
name="option" label="lowerCase"
>全小写需关闭大小写</el-checkbox>
</el-form-item>
<!-- 生成结果 -->
<el-form-item label="生成结果">
<el-input v-model="info.result" type="text" readonly></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div class="action">
<div class="title">操作</div>
<div class="content">
<el-button type="primary" @click="btnGenerate()">生成</el-button>
</div>
</div>
<div class="reference">
<div class="title">参考资料</div>
<div class="content">
<el-link href="https://www.cnblogs.com/hankuksui/p/9892729.html" target="_blank" type="primary">博客园 - hankuksui</el-link>
</div>
</div>
<div class="tool-page">
<div class="info">
<div class="title">配置选项 &amp; 结果</div>
<div class="content">
<el-form label-width="6rem">
<!-- 字符串长度 -->
<el-form-item label="字符串长度">
<el-input
v-model.number="info.strLength"
type="number"
min="1"
max="1024"
></el-input>
</el-form-item>
<!-- 字符串格式 -->
<el-form-item label="字符串格式">
<el-checkbox
v-model="info.option.hasNum"
name="option"
label="hasNum"
>数字</el-checkbox>
<el-checkbox
v-model="info.option.hasChar"
name="option"
label="hasChar"
>字母</el-checkbox>
<el-checkbox
v-model="info.option.hasSymbol"
name="option"
label="hasSymbol"
>其他符号</el-checkbox>
<el-checkbox
v-model="info.option.caseSensitive"
name="option"
label="caseSensitive"
>大小写</el-checkbox>
<el-checkbox
v-model="info.option.lowerCase"
name="option"
label="lowerCase"
>全小写需关闭大小写</el-checkbox>
</el-form-item>
<!-- 生成结果 -->
<el-form-item label="生成结果">
<el-input
v-model="info.result"
type="text"
readonly
></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div class="action">
<div class="title">操作</div>
<div class="content">
<el-button
type="primary"
@click="btnGenerate()"
>生成</el-button>
</div>
</div>
<div class="reference">
<div class="title">参考资料</div>
<div class="content">
<el-link
href="https://www.cnblogs.com/hankuksui/p/9892729.html"
target="_blank"
type="primary"
>博客园 - hankuksui</el-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'GenRandomStr',
data() {
return {
info: {
strLength: 8,
option: {
caseSensitive: true,
hasNum: true,
hasChar: true,
hasSymbol: false,
lowerCase: false
},
result: ''
}
}
},
methods: {
/**
* 生成
*/
btnGenerate() {
var len = this.info.strLength;
var op = this.info.option;
var r = this.genRandomStr(len, op.hasNum, op.hasChar, op.hasSymbol, op.caseSensitive, op.lowerCase);
this.info.result = r;
name: 'GenRandomStr',
data() {
return {
info: {
strLength: 8,
option: {
caseSensitive: true,
hasNum: true,
hasChar: true,
hasSymbol: false,
lowerCase: false
},
result: ''
}
}
},
methods: {
/**
* 生成随机字符串
*
* @param {number} strLength 长度
* @param {boolean} hasNum 是否包含数字
* @param {boolean} hasChar 是否包含字母
* @param {boolean} hasSymbol 是否包含其他符号
* @param {boolean} caseSensitive 是否包含大小写
* @param {boolean} lowerCase 是否全小写(当 caseSensitive 为 false 时起作用)
*
* @returns {string} 生成的字符串
*/
genRandomStr(strLength, hasNum, hasChar, hasSymbol, caseSensitive, lowerCase) {
var result = '请选中数字、字母或其他符号的其中一项!';
/**
* 生成
*/
btnGenerate() {
var len = this.info.strLength;
var op = this.info.option;
var r = this.genRandomStr(len, op.hasNum, op.hasChar, op.hasSymbol, op.caseSensitive, op.lowerCase);
if (hasNum === false && hasChar === false && hasSymbol === false) {
return result;
}
this.info.result = r;
},
result = '';
/**
* 生成随机字符串
*
* @param {number} strLength 长度
* @param {boolean} hasNum 是否包含数字
* @param {boolean} hasChar 是否包含字母
* @param {boolean} hasSymbol 是否包含其他符号
* @param {boolean} caseSensitive 是否包含大小写
* @param {boolean} lowerCase 是否全小写(当 caseSensitive 为 false 时起作用)
*
* @returns {string} 生成的字符串
*/
genRandomStr(strLength, hasNum, hasChar, hasSymbol, caseSensitive, lowerCase) {
var result = '请选中数字、字母或其他符号的其中一项!';
for (var i = strLength; i > 0; i--) {
let num = Math.floor((Math.random() * 94) + 33);
let flag = ((
(hasNum === false) && ((num >= 48) && (num <= 57))
) || (
(hasChar === false) && ((
(num >= 65) && (num <= 90)
) || (
(num >= 97) && (num <= 122)
))
) || (
(hasSymbol === false) && ((
(num >= 33) && (num <= 47)
) || (
(num >= 58) && (num <= 64)
) || (
(num >= 91) && (num <= 96)
) || (
(num >= 123) && (num <= 127)
)
)));
if (hasNum === false && hasChar === false && hasSymbol === false) {
return result;
}
if (flag) {
i++;
continue;
}
result = '';
/**
* | CharCode | 符号 |
* | :--------- | :----- |
* | 033 -> 047 | ! -> / |
* | 048 -> 057 | 0 -> 9 |
* | 058 -> 064 | : -> @ |
* | 065 -> 090 | A -> Z |
* | 091 -> 096 | [ -> ` |
* | 097 -> 122 | a -> z |
* | 123 -> 127 | { -> |
*/
for (var i = strLength; i > 0; i--) {
let num = Math.floor((Math.random() * 94) + 33);
let flag = ((
(hasNum === false) && ((num >= 48) && (num <= 57))
) || (
(hasChar === false) && ((
(num >= 65) && (num <= 90)
) || (
(num >= 97) && (num <= 122)
))
) || (
(hasSymbol === false) && ((
(num >= 33) && (num <= 47)
) || (
(num >= 58) && (num <= 64)
) || (
(num >= 91) && (num <= 96)
) || (
(num >= 123) && (num <= 127)
)
)));
result += String.fromCharCode(num);
}
if (caseSensitive === false) {
result = (lowerCase ? result.toLocaleLowerCase() : result.toUpperCase());
}
return result;
if (flag) {
i++;
continue;
}
},
/**
* | CharCode | 符号 |
* | :--------- | :----- |
* | 033 -> 047 | ! -> / |
* | 048 -> 057 | 0 -> 9 |
* | 058 -> 064 | : -> @ |
* | 065 -> 090 | A -> Z |
* | 091 -> 096 | [ -> ` |
* | 097 -> 122 | a -> z |
* | 123 -> 127 | { -> |
*/
result += String.fromCharCode(num);
}
if (caseSensitive === false) {
result = (lowerCase ? result.toLocaleLowerCase() : result.toUpperCase());
}
return result;
}
},
}
</script>
<style lang="less" scoped>
.content {
max-width: 30rem;
max-width: 30rem;
}
</style>

View File

@@ -1,93 +1,93 @@
<template>
<div class="tool-page">
<div class="tool-page">
<div class="info">
<div class="title">信息</div>
<div class="content">
<div class="link">
<span class="label">目标链接</span>
<el-input v-model="info.link" type="text" placeholder="需要包含协议(例如 https://" />
</div>
<div class="size">
<span class="label">窗口大小</span>
<el-input v-model="info.size.width" type="number" min="1" placeholder="宽度(默认 400" />
<el-input v-model="info.size.height" type="number" min="1" placeholder="高度(默认 300" />
</div>
</div>
<div class="info">
<div class="title">信息</div>
<div class="content">
<div class="link">
<span class="label">目标链接</span>
<el-input v-model="info.link" type="text" placeholder="需要包含协议(例如 https://" />
</div>
<div class="action">
<div class="title">操作</div>
<div class="content">
<el-button type="primary" @click="btnOpen()">打开</el-button>
<el-button type="danger" @click="btnClear()">清空</el-button>
</div>
<div class="size">
<span class="label">窗口大小</span>
<el-input v-model="info.size.width" type="number" min="1" placeholder="宽度(默认 400" />
<el-input v-model="info.size.height" type="number" min="1" placeholder="高度(默认 300" />
</div>
</div>
</div>
<div class="action">
<div class="title">操作</div>
<div class="content">
<el-button type="primary" @click="btnOpen()">打开</el-button>
<el-button type="danger" @click="btnClear()">清空</el-button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'OtherNewWindow',
data() {
return {
info: {
link: '',
size: {
width: '',
height: ''
}
}
name: 'OtherNewWindow',
data() {
return {
info: {
link: '',
size: {
width: '',
height: ''
}
}
}
},
methods: {
// 打开
btnOpen() {
var link = this.info.link || 'https://github.com/Frost-ZX';
var width = this.info.size.width || '400';
var height = this.info.size.height || '300';
var features = `height=${height}, width=${width}, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=yes, status=yes`;
window.open(link, '_blank', features);
},
methods: {
// 打开
btnOpen() {
var link = this.info.link || 'https://github.com/Frost-ZX';
var width = this.info.size.width || '400';
var height = this.info.size.height || '300';
var features = `height=${height}, width=${width}, toolbar=no, menubar=no, scrollbars=yes, resizable=yes, location=yes, status=yes`;
window.open(link, '_blank', features);
},
// 清空
btnClear() {
// 重置 info
this.info = {
link: '',
size: {
width: '',
height: ''
}
};
// 清空
btnClear() {
// 重置 info
this.info = {
link: '',
size: {
width: '',
height: ''
}
};
}
},
},
}
</script>
<style lang="less" scoped>
.content {
max-width: 40rem;
max-width: 40rem;
> div {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 0.5rem 0;
}
> div {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 0.5rem 0;
}
.label {
display: inline-block;
flex-shrink: 0;
width: 5rem;
}
.label {
display: inline-block;
flex-shrink: 0;
width: 5rem;
}
.el-input {
margin: 0 0.5rem;
}
.el-input {
margin: 0 0.5rem;
}
}
</style>

View File

@@ -1,34 +1,36 @@
<template>
<div class="tool-page">
<!-- 输入 -->
<div class="input">
<div class="title">代码</div>
<div class="text">
<textarea ref="jsInput" placeholder="代码"></textarea>
</div>
</div>
<!-- 操作 -->
<div class="action">
<div class="title">操作</div>
<div class="btns">
<el-button type="primary" @click="btnRun()">执行</el-button>
<el-button type="danger" @click="btnClear()">清空</el-button>
</div>
</div>
<!-- 输出 -->
<div class="output">
<div class="title">输出</div>
<div class="text">
<p v-for="line in output.lines" :key="line.id"
:class="['line', line.type]"
>{{ line.message }}</p>
</div>
</div>
<div class="tool-page">
<!-- 输入 -->
<div class="input">
<div class="title">代码</div>
<div class="text">
<textarea ref="jsInput" placeholder="代码"></textarea>
</div>
</div>
<!-- 操作 -->
<div class="action">
<div class="title">操作</div>
<div class="btns">
<el-button type="primary" @click="btnRun()">执行</el-button>
<el-button type="danger" @click="btnClear()">清空</el-button>
</div>
</div>
<!-- 输出 -->
<div class="output">
<div class="title">输出</div>
<div class="text">
<p
v-for="line in output.lines"
:key="line.id"
:class="['line', line.type]"
>{{ line.message }}</p>
</div>
</div>
</div>
</template>
<script>
@@ -37,144 +39,144 @@ import 'codemirror/mode/javascript/javascript';
import 'codemirror/lib/codemirror.css';
export default {
name: 'OtherRunJS',
data() {
return {
input: {
editor: null,
value: ''
},
output: {
id: 0,
lines: []
}
name: 'OtherRunJS',
data() {
return {
input: {
editor: null,
value: ''
},
output: {
id: 0,
lines: []
}
}
},
mounted () {
this.init();
},
methods: {
// 初始化
init() {
var jsInput = this.$refs['jsInput'];
var datas = this.input;
datas.editor = CodeMirror.fromTextArea(jsInput, {
indentWithTabs: false,
indentUnit: 4,
lineNumbers: true,
mode: 'javascript',
theme: 'default'
});
datas.editor.setOption('extraKeys', {
// Tab 转空格
Tab: function(cm) {
var spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
cm.replaceSelection(spaces);
}
});
datas.editor.on('change', function (instance) {
// 同步 value
datas.value = instance.doc.getValue();
});
},
mounted () {
this.init();
// 运行
btnRun() {
var output = this.output;
output.lines = [];
try {
window.eval(this.input.value);
} catch (err) {
let time = new Date();
output.id += 1;
output.lines.push({
id: output.id + '_' + time.getTime(),
message: err.message,
stack: err.stack,
type: 'error'
});
}
},
methods: {
// 初始化
init() {
var jsInput = this.$refs['jsInput'];
var datas = this.input;
// 清空
btnClear() {
this.$confirm('确定要清空输入和输出的内容吗?', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
datas.editor = CodeMirror.fromTextArea(jsInput, {
indentWithTabs: false,
indentUnit: 4,
lineNumbers: true,
mode: 'javascript',
theme: 'default'
});
this.input.editor.doc.setValue('');
this.output.lines = [];
this.$message({
message: '已清除',
type: 'success'
});
datas.editor.setOption('extraKeys', {
// Tab 转空格
Tab: function(cm) {
var spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
cm.replaceSelection(spaces);
}
});
}).catch(() => {
datas.editor.on('change', function (instance) {
// 同步 value
datas.value = instance.doc.getValue();
});
},
this.$message({
message: '取消清除',
type: 'info'
});
// 运行
btnRun() {
var output = this.output;
});
}
output.lines = [];
try {
window.eval(this.input.value);
} catch (err) {
let time = new Date();
output.id += 1;
output.lines.push({
id: output.id + '_' + time.getTime(),
message: err.message,
stack: err.stack,
type: 'error'
});
}
},
// 清空
btnClear() {
this.$confirm('确定要清空输入和输出的内容吗?', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.input.editor.doc.setValue('');
this.output.lines = [];
this.$message({
message: '已清除',
type: 'success'
});
}).catch(() => {
this.$message({
message: '取消清除',
type: 'info'
});
});
}
},
},
}
</script>
<style lang="less" scoped>
.tool-page {
@lineHeight: 1.25rem;
@lineHeight: 1.25rem;
padding-bottom: 5rem;
padding-bottom: 5rem;
> div {
.text {
line-height: @lineHeight;
font-family: monospace;
font-size: 0.8rem;
}
> div {
.text {
line-height: @lineHeight;
font-family: monospace;
font-size: 0.8rem;
}
}
.input {
/deep/ .CodeMirror {
width: 100%;
height: calc(@lineHeight * 20 + 0.5rem);
box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1);
line-height: @lineHeight;
}
}
.output {
.text {
padding: 1rem;
height: calc(@lineHeight * 10);
border: 0.1rem solid #EEE;
border-radius: 0.32rem;
overflow: auto;
user-select: text;
resize: vertical;
}
.input {
/deep/ .CodeMirror {
width: 100%;
height: calc(@lineHeight * 20 + 0.5rem);
box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1);
line-height: @lineHeight;
}
}
.output {
.text {
padding: 1rem;
height: calc(@lineHeight * 10);
border: 0.1rem solid #EEE;
border-radius: 0.32rem;
overflow: auto;
user-select: text;
resize: vertical;
}
.line {
&.error {
color: @colorRed;
}
}
.line {
&.error {
color: @colorRed;
}
}
}
}
</style>

View File

@@ -1,95 +1,95 @@
<template>
<el-container class="about">
<div class="wrapper shadow-2">
<div class="text">
<div class="title">简介</div>
<div class="content">
<p>一个多功能的网址导航绿色无广告</p>
<p>右键点击PC端或长按移动端链接项可查看链接详情</p>
<p>当前版本2.0.0</p>
<p>链接版本{{ navLinks.version }}</p>
</div>
</div>
<div class="text">
<div class="title">开发人员</div>
<div class="content">
<p>Frost-ZX</p>
</div>
</div>
<div class="text">
<div class="title">注意事项</div>
<div class="content">
<p>部分链接由于不能及时更新可能已过期不存在指向了错误的网站等访问时请注意</p>
<p>若您在使用时发现相关情况欢迎进行反馈</p>
</div>
</div>
<div class="text">
<div class="title">GitHub</div>
<div class="content">
<p>
<el-link :href="info.github" target="_blank" type="primary">{{ info.github }}</el-link>
</p>
</div>
</div>
<el-container class="about">
<div class="wrapper shadow-2">
<div class="text">
<div class="title">简介</div>
<div class="content">
<p>一个多功能的网址导航绿色无广告</p>
<p>右键点击PC端或长按移动端链接项可查看链接详情</p>
<p>当前版本2.0.0</p>
<p>链接版本{{ navLinks.version }}</p>
</div>
</el-container>
</div>
<div class="text">
<div class="title">开发人员</div>
<div class="content">
<p>Frost-ZX</p>
</div>
</div>
<div class="text">
<div class="title">注意事项</div>
<div class="content">
<p>部分链接由于不能及时更新可能已过期不存在指向了错误的网站等访问时请注意</p>
<p>若您在使用时发现相关情况欢迎进行反馈</p>
</div>
</div>
<div class="text">
<div class="title">GitHub</div>
<div class="content">
<p>
<el-link :href="info.github" target="_blank" type="primary">{{ info.github }}</el-link>
</p>
</div>
</div>
</div>
</el-container>
</template>
<script>
export default {
name: 'AboutView',
beforeRouteEnter(to, from, next) {
next(vm => {
vm.utils.changeTitle('关于');
});
},
data() {
return {
navLinks: this.$root.navLinks,
utils: this.$root.utils,
info: {
github: 'https://github.com/Frost-ZX/frost-navigation'
}
}
},
name: 'AboutView',
beforeRouteEnter(to, from, next) {
next(vm => {
vm.utils.changeTitle('关于');
});
},
data() {
return {
navLinks: this.$root.navLinks,
utils: this.$root.utils,
info: {
github: 'https://github.com/Frost-ZX/frost-navigation'
}
}
},
}
</script>
<style lang="less" scoped>
.about {
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
background-color: @colorWhite;
overflow-y: auto;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
background-color: @colorWhite;
overflow-y: auto;
.wrapper {
padding: 1.5rem 2rem;
width: 100%;
max-width: 50rem;
background-color: #FFF;
.wrapper {
padding: 1.5rem 2rem;
width: 100%;
max-width: 50rem;
background-color: #FFF;
}
.text {
padding: 0.5rem 0;
.title {
line-height: 2.5rem;
font-size: 1rem;
color: @textPrimary;
}
.text {
padding: 0.5rem 0;
.title {
line-height: 2.5rem;
font-size: 1rem;
color: @textPrimary;
}
.content {
line-height: 1.8rem;
font-size: 0.85rem;
color: @textSecondary;
}
.content {
line-height: 1.8rem;
font-size: 0.85rem;
color: @textSecondary;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,141 +1,141 @@
<template>
<el-container class="settings">
<div class="wrapper shadow-2">
<el-container class="settings">
<div class="wrapper shadow-2">
<el-form label-position="left" label-width="12rem">
<el-form label-position="left" label-width="12rem">
<el-form-item label="字体大小" class="set-font">
<el-input-number
v-model="config.fontSize"
:min="12"
:max="32"
controls-position="right"
label="字体大小"
size="small"
></el-input-number>
</el-form-item>
<el-form-item label="字体大小" class="set-font">
<el-input-number
v-model="config.fontSize"
:min="12"
:max="32"
controls-position="right"
label="字体大小"
size="small"
></el-input-number>
</el-form-item>
<el-form-item label="显示网站标题">
<el-switch v-model="config.showSiteTitle"></el-switch>
</el-form-item>
<el-form-item label="显示网站标题">
<el-switch v-model="config.showSiteTitle"></el-switch>
</el-form-item>
<el-form-item label="折叠主页侧边菜单">
<el-switch v-model="config.sideMenuCollapse"></el-switch>
</el-form-item>
<el-form-item label="折叠主页侧边菜单">
<el-switch v-model="config.sideMenuCollapse"></el-switch>
</el-form-item>
<el-form-item label="获取搜索引擎关键词建议">
<el-switch v-model="config.searchSuggestion"></el-switch>
</el-form-item>
<el-form-item label="获取搜索引擎关键词建议">
<el-switch v-model="config.searchSuggestion"></el-switch>
</el-form-item>
<el-form-item label="清除数据">
<el-button
type="danger"
size="medium"
@click="resetDatas('settings')"
>清除设置</el-button>
<el-button
type="danger"
size="medium"
@click="resetDatas('cache')"
>清除缓存</el-button>
</el-form-item>
<el-form-item label="清除数据">
<el-button
type="danger"
size="medium"
@click="resetDatas('settings')"
>清除设置</el-button>
<el-button
type="danger"
size="medium"
@click="resetDatas('cache')"
>清除缓存</el-button>
</el-form-item>
</el-form>
</el-form>
</div>
</el-container>
</div>
</el-container>
</template>
<script>
export default {
name: 'SettingsView',
beforeRouteEnter(to, from, next) {
next(vm => {
vm.utils.changeTitle('设置');
});
},
data() {
return {
config: this.$root.config.storage,
utils: this.$root.utils
}
},
methods: {
/**
* 清除数据
*
* @param {string} type 清除类型cache、settings
*/
resetDatas(type) {
this.$confirm('确定要清除吗?', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (type === 'cache') {
localStorage.removeItem('navLinksCache');
} else if (type === 'settings') {
localStorage.removeItem('navConfig');
} else {
return
}
this.$message({
message: '已清除2s 后自动刷新',
type: 'success'
});
setTimeout(() => {
location.reload();
}, 2000);
}).catch(() => {
this.$message({
message: '取消清除',
type: 'info'
});
});
}
name: 'SettingsView',
beforeRouteEnter(to, from, next) {
next(vm => {
vm.utils.changeTitle('设置');
});
},
data() {
return {
config: this.$root.config.storage,
utils: this.$root.utils
}
},
methods: {
/**
* 清除数据
*
* @param {string} type 清除类型cache、settings
*/
resetDatas(type) {
this.$confirm('确定要清除吗?', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (type === 'cache') {
localStorage.removeItem('navLinksCache');
} else if (type === 'settings') {
localStorage.removeItem('navConfig');
} else {
return
}
this.$message({
message: '已清除2s 后自动刷新',
type: 'success'
});
setTimeout(() => {
location.reload();
}, 2000);
}).catch(() => {
this.$message({
message: '取消清除',
type: 'info'
});
});
}
}
}
</script>
<style lang="less" scoped>
.settings {
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
background-color: @colorWhite;
overflow-y: auto;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
background-color: @colorWhite;
overflow-y: auto;
.wrapper {
padding: 1.5rem 2rem;
width: 100%;
max-width: 50rem;
background-color: #FFF;
}
.wrapper {
padding: 1.5rem 2rem;
width: 100%;
max-width: 50rem;
background-color: #FFF;
}
}
.set-font {
/deep/ .el-input-number {
width: 7rem;
}
/deep/ .el-input-number {
width: 7rem;
}
}
/deep/ .el-form-item {
@media screen and (max-width: 520px) {
.el-form-item__label {
float: unset !important;
}
.el-form-item__content {
margin-left: 0 !important;
}
@media screen and (max-width: 520px) {
.el-form-item__label {
float: unset !important;
}
.el-form-item__content {
margin-left: 0 !important;
}
}
}
</style>

View File

@@ -9,13 +9,6 @@ import navTools from '@/assets/js/navTools.js';
export default {
name: 'ToolsDetail',
data() {
return {
utils: this.$root.utils,
toolList: navTools,
toolPage: null,
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
const { params, query } = vm.$route;
@@ -52,6 +45,13 @@ export default {
});
});
},
data() {
return {
utils: this.$root.utils,
toolList: navTools,
toolPage: null,
}
},
}
</script>

View File

@@ -61,16 +61,6 @@ import navTools from '@/assets/js/navTools.js';
export default {
name: 'ToolsView',
data() {
return {
utils: this.$root.utils,
toolList: navTools,
detail: {
show: false,
title: ''
},
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
const { name: rName, params: rParams } = vm.$route;
@@ -85,6 +75,16 @@ export default {
}
});
},
data() {
return {
utils: this.$root.utils,
toolList: navTools,
detail: {
show: false,
title: ''
},
}
},
methods: {
/**