更新1.x
This commit is contained in:
parent
257174f800
commit
2b75680031
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 reco_luan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# vuepress-theme-reco
|
||||
|
||||
![vuepress](https://img.shields.io/badge/vuepress-0.14.8-brightgreen.svg)
|
||||
![leancloud-storage](https://img.shields.io/badge/leancloud--storage-3.10.1-orange.svg)
|
||||
![valine](https://img.shields.io/badge/valine-1.3.4-blue.svg)
|
||||
|
||||
> 1. 这是一个vuepress主题,旨在添加博客所需的分类、TAB墙、分页、评论等能;
|
||||
> 2. 主题追求极简,根据 vuepress 的默认主题修改而成,官方的主题配置仍然适用;
|
||||
> 3. 效果:[午后南杂](http://recoluan.gitlab.io)
|
||||
> 4. 文档:[vuepress-theme-reco-doc](https://recoluan.gitlab.io/vuepress-theme-reco-doc)
|
||||
|
||||
## 预览
|
||||
|
||||
### 首页
|
||||
![home.png](https://upload-images.jianshu.io/upload_images/4660406-0bf9d91e9d289f75.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
|
||||
### 分类页面
|
||||
![category.png](https://upload-images.jianshu.io/upload_images/4660406-8c7995d750c58536.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
|
||||
### 标签页
|
||||
![tag.png](https://upload-images.jianshu.io/upload_images/4660406-39c0d47627869e3a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
|
||||
### 文章页面
|
||||
![article.png](https://upload-images.jianshu.io/upload_images/4660406-a19cad487991409d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
### 加密登录页
|
||||
|
||||
![password.png](https://upload-images.jianshu.io/upload_images/4660406-7d38dc78c16b7d48.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
### 时间轴
|
||||
|
||||
![timeline.png](https://upload-images.jianshu.io/upload_images/4660406-1dc5a5fe6c03952e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
### 移动端
|
||||
![m.png](https://upload-images.jianshu.io/upload_images/4660406-7e2c78c48dd78284.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
|
||||
|
||||
## License
|
||||
[MIT](https://github.com/recoluan/vuepress-theme-reco/blob/master/LICENSE)
|
155
components/AlgoliaSearchBox.vue
Normal file
155
components/AlgoliaSearchBox.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<form
|
||||
id="search-form"
|
||||
class="algolia-search-wrapper search-box"
|
||||
role="search"
|
||||
>
|
||||
<input
|
||||
id="algolia-search-input"
|
||||
class="search-query"
|
||||
>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['options'],
|
||||
|
||||
mounted () {
|
||||
this.initialize(this.options, this.$lang)
|
||||
},
|
||||
|
||||
methods: {
|
||||
initialize (userOptions, lang) {
|
||||
Promise.all([
|
||||
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
|
||||
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
|
||||
]).then(([docsearch]) => {
|
||||
docsearch = docsearch.default
|
||||
const { algoliaOptions = {}} = userOptions
|
||||
docsearch(Object.assign(
|
||||
{},
|
||||
userOptions,
|
||||
{
|
||||
inputSelector: '#algolia-search-input',
|
||||
// #697 Make docsearch work well at i18n mode.
|
||||
algoliaOptions: Object.assign({
|
||||
'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
|
||||
}, algoliaOptions)
|
||||
}
|
||||
))
|
||||
})
|
||||
},
|
||||
|
||||
update (options, lang) {
|
||||
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
|
||||
this.initialize(options, lang)
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
$lang (newValue) {
|
||||
this.update(this.options, newValue)
|
||||
},
|
||||
|
||||
options (newValue) {
|
||||
this.update(newValue, this.$lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.algolia-search-wrapper
|
||||
& > span
|
||||
vertical-align middle
|
||||
.algolia-autocomplete
|
||||
line-height normal
|
||||
.ds-dropdown-menu
|
||||
background-color #fff
|
||||
border 1px solid #999
|
||||
border-radius 4px
|
||||
font-size 16px
|
||||
margin 6px 0 0
|
||||
padding 4px
|
||||
text-align left
|
||||
&:before
|
||||
border-color #999
|
||||
[class*=ds-dataset-]
|
||||
border none
|
||||
padding 0
|
||||
.ds-suggestions
|
||||
margin-top 0
|
||||
.ds-suggestion
|
||||
border-bottom 1px solid $borderColor
|
||||
.algolia-docsearch-suggestion--highlight
|
||||
color #2c815b
|
||||
.algolia-docsearch-suggestion
|
||||
border-color $borderColor
|
||||
padding 0
|
||||
.algolia-docsearch-suggestion--category-header
|
||||
padding 5px 10px
|
||||
margin-top 0
|
||||
background $accentColor
|
||||
color #fff
|
||||
font-weight 600
|
||||
.algolia-docsearch-suggestion--highlight
|
||||
background rgba(255, 255, 255, 0.6)
|
||||
.algolia-docsearch-suggestion--wrapper
|
||||
padding 0
|
||||
.algolia-docsearch-suggestion--title
|
||||
font-weight 600
|
||||
margin-bottom 0
|
||||
color $textColor
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
vertical-align top
|
||||
padding 5px 7px 5px 5px
|
||||
border-color $borderColor
|
||||
background #f1f3f5
|
||||
&:after
|
||||
display none
|
||||
.algolia-docsearch-suggestion--subcategory-column-text
|
||||
color #555
|
||||
.algolia-docsearch-footer
|
||||
border-color $borderColor
|
||||
.ds-cursor .algolia-docsearch-suggestion--content
|
||||
background-color #e7edf3 !important
|
||||
color $textColor
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.algolia-search-wrapper
|
||||
.algolia-autocomplete
|
||||
.algolia-docsearch-suggestion
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
float none
|
||||
width 150px
|
||||
min-width 150px
|
||||
display table-cell
|
||||
.algolia-docsearch-suggestion--content
|
||||
float none
|
||||
display table-cell
|
||||
width 100%
|
||||
vertical-align top
|
||||
.ds-dropdown-menu
|
||||
min-width 515px !important
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.algolia-search-wrapper
|
||||
.ds-dropdown-menu
|
||||
min-width calc(100vw - 4rem) !important
|
||||
max-width calc(100vw - 4rem) !important
|
||||
.algolia-docsearch-suggestion--wrapper
|
||||
padding 5px 7px 5px 5px !important
|
||||
.algolia-docsearch-suggestion--subcategory-column
|
||||
padding 0 !important
|
||||
background white !important
|
||||
.algolia-docsearch-suggestion--subcategory-column-text:after
|
||||
content " > "
|
||||
font-size 10px
|
||||
line-height 14.4px
|
||||
display inline-block
|
||||
width 5px
|
||||
margin -3px 3px 0
|
||||
vertical-align middle
|
||||
|
||||
</style>
|
109
components/BackToTop.vue
Normal file
109
components/BackToTop.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<transition :name="transitionName">
|
||||
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
|
||||
<i class="iconfont reco-up"></i>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BackToTop',
|
||||
props: {
|
||||
visibilityHeight: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
backPosition: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
right: '1rem',
|
||||
bottom: '3rem',
|
||||
width: '2.5rem',
|
||||
height: '2.5rem',
|
||||
'border-radius': '.25rem',
|
||||
'line-height': '2.5rem',
|
||||
backgroundColor: 'rgba(231, 234, 241,.5)'
|
||||
}
|
||||
}
|
||||
},
|
||||
transitionName: {
|
||||
type: String,
|
||||
default: 'fade'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
interval: null,
|
||||
isMoving: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
this.visible = window.pageYOffset > this.visibilityHeight
|
||||
},
|
||||
backToTop() {
|
||||
if (this.isMoving) return
|
||||
const start = window.pageYOffset
|
||||
let i = 0
|
||||
this.isMoving = true
|
||||
this.interval = setInterval(() => {
|
||||
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
|
||||
if (next <= this.backPosition) {
|
||||
window.scrollTo(0, this.backPosition)
|
||||
clearInterval(this.interval)
|
||||
this.isMoving = false
|
||||
} else {
|
||||
window.scrollTo(0, next)
|
||||
}
|
||||
i++
|
||||
}, 16.7)
|
||||
},
|
||||
easeInOutQuad(t, b, c, d) {
|
||||
if ((t /= d / 2) < 1) return c / 2 * t * t + b
|
||||
return -c / 2 * (--t * (t - 2) - 1) + b
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.back-to-ceiling
|
||||
position: fixed;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
i
|
||||
font-size 1.6rem
|
||||
color $accentColor
|
||||
.back-to-ceiling:hover {
|
||||
background: #d5dbe7;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0
|
||||
}
|
||||
.back-to-ceiling .Icon {
|
||||
fill: #9aaabf;
|
||||
background: none;
|
||||
}
|
||||
</style>
|
118
components/Background.vue
Normal file
118
components/Background.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div id="particles-oli-wrapper"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
const keyPage = this.$site.themeConfig.keyPage
|
||||
let color = '#fb9b5f'
|
||||
let lineColor = '#fb9b5f'
|
||||
if (keyPage) {
|
||||
color = keyPage.color || color
|
||||
lineColor = keyPage.lineColor || lineColor
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js";
|
||||
|
||||
document.body.append(script);
|
||||
script.onload = function() {
|
||||
particlesJS("particles-oli-wrapper", {
|
||||
particles: {
|
||||
number: {
|
||||
value: 40,
|
||||
density: {
|
||||
enable: true,
|
||||
value_area: 800
|
||||
}
|
||||
},
|
||||
color: {
|
||||
value: color
|
||||
},
|
||||
shape: {
|
||||
type: "circle",
|
||||
stroke: {
|
||||
width: 0,
|
||||
color: "#000000"
|
||||
},
|
||||
polygon: {
|
||||
nb_sides: 5
|
||||
}
|
||||
},
|
||||
opacity: {
|
||||
value: 0.3,
|
||||
random: false,
|
||||
anim: {
|
||||
enable: false,
|
||||
speed: 1,
|
||||
opacity_min: 0.1,
|
||||
sync: false
|
||||
}
|
||||
},
|
||||
size: {
|
||||
value: 20,
|
||||
random: true,
|
||||
anim: {
|
||||
enable: false,
|
||||
speed: 30,
|
||||
size_min: 0.1,
|
||||
sync: false
|
||||
}
|
||||
},
|
||||
line_linked: {
|
||||
enable: true,
|
||||
distance: 250,
|
||||
color: lineColor,
|
||||
opacity: 0.2,
|
||||
width: 1
|
||||
},
|
||||
move: {
|
||||
enable: true,
|
||||
speed: 3,
|
||||
direction: "none",
|
||||
random: true,
|
||||
straight: false,
|
||||
out_mode: "out",
|
||||
bounce: true,
|
||||
attract: {
|
||||
enable: false,
|
||||
rotateX: 600,
|
||||
rotateY: 1200
|
||||
}
|
||||
}
|
||||
},
|
||||
interactivity: {
|
||||
detect_on: "canvas",
|
||||
events: {
|
||||
onhover: {
|
||||
enable: true,
|
||||
mode: "grab"
|
||||
},
|
||||
onclick: {
|
||||
enable: true,
|
||||
mode: "push"
|
||||
},
|
||||
resize: true
|
||||
},
|
||||
modes: {
|
||||
grab: {
|
||||
distance: 100,
|
||||
line_linked: {
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
retina_detect: true
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
#particles-oli-wrapper {
|
||||
height: 99vh;
|
||||
}
|
||||
</style>
|
||||
|
152
components/Common.vue
Normal file
152
components/Common.vue
Normal file
@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div
|
||||
class="theme-container"
|
||||
:class="pageClasses"
|
||||
@touchstart="onTouchStart"
|
||||
@touchend="onTouchEnd">
|
||||
<Password v-if="!isHasKey"></Password>
|
||||
<div v-else>
|
||||
<Navbar
|
||||
v-if="shouldShowNavbar"
|
||||
@toggle-sidebar="toggleSidebar"/>
|
||||
|
||||
<div
|
||||
class="sidebar-mask"
|
||||
@click="toggleSidebar(false)"></div>
|
||||
|
||||
<Sidebar
|
||||
:items="sidebarItems"
|
||||
@toggle-sidebar="toggleSidebar">
|
||||
<slot
|
||||
name="sidebar-top"
|
||||
slot="top"/>
|
||||
<slot
|
||||
name="sidebar-bottom"
|
||||
slot="bottom"/>
|
||||
</Sidebar>
|
||||
<slot></slot>
|
||||
<Valine :isComment="isComment"></Valine>
|
||||
<BackToTop></BackToTop>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Navbar from '@theme/components/Navbar.vue'
|
||||
import Sidebar from '@theme/components/Sidebar.vue'
|
||||
import { resolveSidebarItems } from '../util'
|
||||
import Password from '@theme/components/Password'
|
||||
import Valine from '@theme/components/Valine/'
|
||||
import BackToTop from "@theme/components/BackToTop"
|
||||
|
||||
export default {
|
||||
components: { Sidebar, Navbar, Password, Valine, BackToTop },
|
||||
|
||||
props: ['sidebar', 'isComment'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
isSidebarOpen: false,
|
||||
isHasKey: true,
|
||||
nightMode: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
shouldShowNavbar () {
|
||||
const { themeConfig } = this.$site
|
||||
const { frontmatter } = this.$page
|
||||
if (
|
||||
frontmatter.navbar === false
|
||||
|| themeConfig.navbar === false) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
this.$title
|
||||
|| themeConfig.logo
|
||||
|| themeConfig.repo
|
||||
|| themeConfig.nav
|
||||
|| this.$themeLocaleConfig.nav
|
||||
)
|
||||
},
|
||||
|
||||
shouldShowSidebar () {
|
||||
const { frontmatter } = this.$page
|
||||
return (
|
||||
this.sidebar !== false
|
||||
&& !frontmatter.home
|
||||
&& frontmatter.sidebar !== false
|
||||
&& this.sidebarItems.length
|
||||
)
|
||||
},
|
||||
|
||||
sidebarItems () {
|
||||
return resolveSidebarItems(
|
||||
this.$page,
|
||||
this.$page.regularPath,
|
||||
this.$site,
|
||||
this.$localePath
|
||||
)
|
||||
},
|
||||
|
||||
pageClasses () {
|
||||
const userPageClass = this.$page.frontmatter.pageClass
|
||||
return [
|
||||
{
|
||||
'no-navbar': !this.shouldShowNavbar,
|
||||
'sidebar-open': this.isSidebarOpen,
|
||||
'no-sidebar': !this.shouldShowSidebar,
|
||||
'night-mode': this.nightMode
|
||||
},
|
||||
userPageClass
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.$router.afterEach(() => {
|
||||
this.isSidebarOpen = false
|
||||
})
|
||||
|
||||
if (localStorage.getItem('nightMode')) {
|
||||
document.documentElement.style.background = "#000"
|
||||
this.nightMode = true
|
||||
}
|
||||
|
||||
const keyPage = this.$site.themeConfig.keyPage
|
||||
if (!keyPage) {
|
||||
this.isHasKey = true
|
||||
return
|
||||
}
|
||||
|
||||
const keys = keyPage.keys
|
||||
this.isHasKey = keys && keys.indexOf(sessionStorage.getItem('key')) > -1
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleSidebar (to) {
|
||||
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
|
||||
},
|
||||
|
||||
// side swipe
|
||||
onTouchStart (e) {
|
||||
this.touchStart = {
|
||||
x: e.changedTouches[0].clientX,
|
||||
y: e.changedTouches[0].clientY
|
||||
}
|
||||
},
|
||||
|
||||
onTouchEnd (e) {
|
||||
const dx = e.changedTouches[0].clientX - this.touchStart.x
|
||||
const dy = e.changedTouches[0].clientY - this.touchStart.y
|
||||
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
|
||||
if (dx > 0 && this.touchStart.x <= 80) {
|
||||
this.toggleSidebar(true)
|
||||
} else {
|
||||
this.toggleSidebar(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
181
components/DropdownLink.vue
Normal file
181
components/DropdownLink.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div
|
||||
class="dropdown-wrapper"
|
||||
:class="{ open }"
|
||||
>
|
||||
<a
|
||||
class="dropdown-title"
|
||||
@click="toggle"
|
||||
>
|
||||
<span class="title">
|
||||
<i :class="`iconfont ${item.icon}`"></i>
|
||||
{{ item.text }}
|
||||
</span>
|
||||
<span
|
||||
class="arrow"
|
||||
:class="open ? 'down' : 'right'"
|
||||
></span>
|
||||
</a>
|
||||
|
||||
<DropdownTransition>
|
||||
<ul
|
||||
class="nav-dropdown"
|
||||
v-show="open"
|
||||
>
|
||||
<li
|
||||
class="dropdown-item"
|
||||
:key="subItem.link || index"
|
||||
v-for="(subItem, index) in item.items"
|
||||
>
|
||||
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
|
||||
|
||||
<ul
|
||||
class="dropdown-subitem-wrapper"
|
||||
v-if="subItem.type === 'links'"
|
||||
>
|
||||
<li
|
||||
class="dropdown-subitem"
|
||||
:key="childSubItem.link"
|
||||
v-for="childSubItem in subItem.items"
|
||||
>
|
||||
<NavLink :item="childSubItem"/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<NavLink
|
||||
v-else
|
||||
:item="subItem"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</DropdownTransition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavLink from '@theme/components/NavLink.vue'
|
||||
import DropdownTransition from '@theme/components/DropdownTransition.vue'
|
||||
|
||||
export default {
|
||||
components: { NavLink, DropdownTransition },
|
||||
|
||||
data () {
|
||||
return {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggle () {
|
||||
this.open = !this.open
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.dropdown-wrapper
|
||||
cursor pointer
|
||||
.dropdown-title
|
||||
display block
|
||||
&:hover
|
||||
border-color transparent
|
||||
.arrow
|
||||
vertical-align middle
|
||||
margin-top -1px
|
||||
margin-left 0.4rem
|
||||
.nav-dropdown
|
||||
.dropdown-item
|
||||
color inherit
|
||||
line-height 1.7rem
|
||||
h4
|
||||
margin 0.45rem 0 0
|
||||
border-top 1px solid #eee
|
||||
padding 0.45rem 1.5rem 0 1.25rem
|
||||
.dropdown-subitem-wrapper
|
||||
padding 0
|
||||
list-style none
|
||||
.dropdown-subitem
|
||||
font-size 0.9em
|
||||
a
|
||||
display block
|
||||
line-height 1.7rem
|
||||
position relative
|
||||
border-bottom none
|
||||
font-weight 400
|
||||
margin-bottom 0
|
||||
padding 0 1.5rem 0 1.25rem
|
||||
&:hover
|
||||
color $accentColor
|
||||
&.router-link-active
|
||||
color $accentColor
|
||||
&::after
|
||||
content ""
|
||||
width 0
|
||||
height 0
|
||||
border-left 5px solid $accentColor
|
||||
border-top 3px solid transparent
|
||||
border-bottom 3px solid transparent
|
||||
position absolute
|
||||
top calc(50% - 2px)
|
||||
left 9px
|
||||
&:first-child h4
|
||||
margin-top 0
|
||||
padding-top 0
|
||||
border-top 0
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.dropdown-wrapper
|
||||
&.open .dropdown-title
|
||||
margin-bottom 0.5rem
|
||||
.nav-dropdown
|
||||
transition height .1s ease-out
|
||||
overflow hidden
|
||||
.dropdown-item
|
||||
h4
|
||||
border-top 0
|
||||
margin-top 0
|
||||
padding-top 0
|
||||
h4, & > a
|
||||
font-size 15px
|
||||
line-height 2rem
|
||||
.dropdown-subitem
|
||||
font-size 14px
|
||||
padding-left 1rem
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.dropdown-wrapper
|
||||
height 1.8rem
|
||||
&:hover .nav-dropdown
|
||||
// override the inline style.
|
||||
display block !important
|
||||
.dropdown-title .arrow
|
||||
// make the arrow always down at desktop
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-top 6px solid $arrowBgColor
|
||||
border-bottom 0
|
||||
.nav-dropdown
|
||||
display none
|
||||
// Avoid height shaked by clicking
|
||||
height auto !important
|
||||
box-sizing border-box;
|
||||
max-height calc(100vh - 2.7rem)
|
||||
overflow-y auto
|
||||
position absolute
|
||||
top 100%
|
||||
right 0
|
||||
background-color #fff
|
||||
padding 0.6rem 0
|
||||
box-shadow: 0 4px 20px 0 rgba(0,0,0,.2);
|
||||
text-align left
|
||||
border-radius 0.25rem
|
||||
white-space nowrap
|
||||
margin 0
|
||||
</style>
|
33
components/DropdownTransition.vue
Normal file
33
components/DropdownTransition.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<transition
|
||||
name="dropdown"
|
||||
@enter="setHeight"
|
||||
@after-enter="unsetHeight"
|
||||
@before-leave="setHeight"
|
||||
>
|
||||
<slot/>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DropdownTransition',
|
||||
|
||||
methods: {
|
||||
setHeight (items) {
|
||||
// explicitly set height so that it can be transitioned
|
||||
items.style.height = items.scrollHeight + 'px'
|
||||
},
|
||||
|
||||
unsetHeight (items) {
|
||||
items.style.height = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.dropdown-enter, .dropdown-leave-to
|
||||
height 0 !important
|
||||
|
||||
</style>
|
285
components/Home.vue
Normal file
285
components/Home.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div class="home" :class="recoShow?'reco-show': 'reco-hide'">
|
||||
<div class="hero">
|
||||
<img v-if="data.heroImage" :style="heroImageStyle" :src="$withBase(data.heroImage)" alt="hero">
|
||||
|
||||
<h1 v-if="data.isShowTitleInHome !== false">{{ data.heroText || $title || '午后南杂' }}</h1>
|
||||
|
||||
<p class="description">{{ data.tagline || $description || 'Welcome to your vuePress-theme-reco site' }}</p>
|
||||
|
||||
<p class="action" v-if="data.actionText && data.actionLink">
|
||||
<NavLink class="action-button" :item="actionLink"/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="features" v-if="data.features && data.features.length">
|
||||
<div v-for="(feature, index) in data.features" :key="index" class="feature">
|
||||
<h2>{{ feature.title }}</h2>
|
||||
<p>{{ feature.details }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Content class="home-center" custom/>
|
||||
|
||||
<div class="footer">
|
||||
<span>
|
||||
<i class="iconfont reco-theme"></i>
|
||||
<a target="blank" href="https://recoluan.gitlab.io/vuepress-theme-reco-doc/">VuePress-theme-reco</a>
|
||||
</span>
|
||||
<span>
|
||||
<i class="iconfont reco-other"></i>
|
||||
<a>{{ $site.themeConfig.author || $site.title }}</a>
|
||||
</span>
|
||||
<span>
|
||||
<i class="iconfont reco-copyright"></i>
|
||||
<a>{{ year }}</a>
|
||||
</span>
|
||||
<span>
|
||||
<AccessNumber idVal="/"></AccessNumber>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavLink from "@theme/components/NavLink/";
|
||||
import AccessNumber from '@theme/components/Valine/AccessNumber'
|
||||
|
||||
export default {
|
||||
components: { NavLink, AccessNumber },
|
||||
data () {
|
||||
return {
|
||||
recoShow: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
year () {
|
||||
return new Date().getFullYear()
|
||||
},
|
||||
data() {
|
||||
return this.$page.frontmatter;
|
||||
},
|
||||
|
||||
actionLink() {
|
||||
return {
|
||||
link: this.data.actionLink,
|
||||
text: this.data.actionText
|
||||
};
|
||||
},
|
||||
|
||||
heroImageStyle () {
|
||||
return this.data.heroImageStyle || {
|
||||
maxHeight: '200px',
|
||||
margin: '6rem auto 1.5rem'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.recoShow = true
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@require '../styles/loadMixin.styl'
|
||||
|
||||
.home {
|
||||
padding: $navbarHeight 2rem 0;
|
||||
max-width: 960px;
|
||||
margin: 0px auto;
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
img {
|
||||
background-color: $accentColor;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
h1, .description, .action {
|
||||
margin: 1.8rem auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
max-width: 35rem;
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.3;
|
||||
color: lighten($textColor, 20%);
|
||||
}
|
||||
|
||||
.action-button {
|
||||
display: inline-block;
|
||||
font-size: 1.2rem;
|
||||
color: #fff;
|
||||
background-color: $accentColor;
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.1s ease;
|
||||
box-sizing: border-box;
|
||||
load-start()
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($accentColor, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.features {
|
||||
border-top: 1px solid $borderColor;
|
||||
padding: 1.2rem 0;
|
||||
margin-top: 2.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
align-content: stretch;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.feature {
|
||||
flex-grow: 1;
|
||||
flex-basis: 30%;
|
||||
max-width: 30%;
|
||||
transition: all .5s
|
||||
h2 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
color: lighten($textColor, 10%);
|
||||
}
|
||||
|
||||
p {
|
||||
color: lighten($textColor, 20%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform scale(1.05)
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 2.5rem;
|
||||
border-top: 1px solid $borderColor;
|
||||
text-align: center;
|
||||
color: lighten($textColor, 25%);
|
||||
load-start()
|
||||
> span {
|
||||
margin-left 1rem
|
||||
> i {
|
||||
margin-right .5rem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.reco-hide {
|
||||
.hero {
|
||||
img {
|
||||
load-start()
|
||||
}
|
||||
.h1 {
|
||||
load-start()
|
||||
}
|
||||
.description {
|
||||
load-start()
|
||||
}
|
||||
.action-button {
|
||||
load-start()
|
||||
}
|
||||
}
|
||||
.features {
|
||||
load-start()
|
||||
}
|
||||
.home-center {
|
||||
load-start()
|
||||
}
|
||||
.footer {
|
||||
load-start()
|
||||
}
|
||||
}
|
||||
|
||||
&.reco-show {
|
||||
.hero {
|
||||
img {
|
||||
load-end(0.08s)
|
||||
}
|
||||
.h1 {
|
||||
load-end(0.16s)
|
||||
}
|
||||
.description {
|
||||
load-end(0.24s)
|
||||
}
|
||||
.action-button {
|
||||
load-end(0.32s)
|
||||
}
|
||||
}
|
||||
.features {
|
||||
load-end(0.40s)
|
||||
}
|
||||
.home-center {
|
||||
load-end(0.48s)
|
||||
}
|
||||
.footer {
|
||||
load-end(0.56s)
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobile) {
|
||||
.home {
|
||||
.features {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.feature {
|
||||
max-width: 100%;
|
||||
padding: 0 2.5rem;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
text-align: left!important;
|
||||
> span {
|
||||
display block
|
||||
margin-left 0
|
||||
line-height 2rem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobileNarrow) {
|
||||
.home {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
|
||||
.hero {
|
||||
img {
|
||||
max-height: 210px;
|
||||
margin: 2rem auto 1.2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h1, .description, .action {
|
||||
margin: 1.2rem auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
font-size: 1rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.feature {
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
52
components/NavLink.vue
Normal file
52
components/NavLink.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="link"
|
||||
v-if="!isExternal(link)"
|
||||
:exact="exact">
|
||||
<i :class="`iconfont ${item.icon}`"></i>
|
||||
{{ item.text }}
|
||||
</router-link>
|
||||
<a
|
||||
v-else
|
||||
:href="link"
|
||||
class="nav-link external"
|
||||
:target="isMailto(link) || isTel(link) ? null : '_blank'"
|
||||
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
|
||||
>
|
||||
<i :class="`iconfont ${item.icon}`"></i>
|
||||
{{ item.text }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal, isMailto, isTel, ensureExt } from '../util'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
link () {
|
||||
return ensureExt(this.item.link)
|
||||
},
|
||||
|
||||
exact () {
|
||||
if (this.$site.locales) {
|
||||
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
|
||||
}
|
||||
return this.link === '/'
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
isExternal,
|
||||
isMailto,
|
||||
isTel
|
||||
}
|
||||
}
|
||||
</script>
|
181
components/NavLinks.vue
Normal file
181
components/NavLinks.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<nav
|
||||
class="nav-links"
|
||||
v-if="userLinks.length || repoLink"
|
||||
>
|
||||
<!-- user links -->
|
||||
<div
|
||||
class="nav-item"
|
||||
v-for="item in userLinks"
|
||||
:key="item.link">
|
||||
<DropdownLink
|
||||
v-if="item.type === 'links'"
|
||||
:item="item"/>
|
||||
<NavLink
|
||||
v-else
|
||||
:item="item"/>
|
||||
</div>
|
||||
|
||||
<!-- repo link -->
|
||||
<a
|
||||
v-if="repoLink"
|
||||
:href="repoLink"
|
||||
class="repo-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{{ repoLabel }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DropdownLink from '@theme/components/DropdownLink.vue'
|
||||
import { resolveNavLinkItem } from '../util'
|
||||
import NavLink from '@theme/components/NavLink.vue'
|
||||
|
||||
export default {
|
||||
components: { NavLink, DropdownLink },
|
||||
|
||||
computed: {
|
||||
userNav () {
|
||||
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
|
||||
},
|
||||
|
||||
nav () {
|
||||
const { locales } = this.$site
|
||||
if (locales && Object.keys(locales).length > 1) {
|
||||
const currentLink = this.$page.path
|
||||
const routes = this.$router.options.routes
|
||||
const themeLocales = this.$site.themeConfig.locales || {}
|
||||
const languageDropdown = {
|
||||
text: this.$themeLocaleConfig.selectText || 'Languages',
|
||||
items: Object.keys(locales).map(path => {
|
||||
const locale = locales[path]
|
||||
const text = themeLocales[path] && themeLocales[path].label || locale.lang
|
||||
let link
|
||||
// Stay on the current page
|
||||
if (locale.lang === this.$lang) {
|
||||
link = currentLink
|
||||
} else {
|
||||
// Try to stay on the same page
|
||||
link = currentLink.replace(this.$localeConfig.path, path)
|
||||
// fallback to homepage
|
||||
if (!routes.some(route => route.path === link)) {
|
||||
link = path
|
||||
}
|
||||
}
|
||||
return { text, link }
|
||||
})
|
||||
}
|
||||
return [...this.userNav, languageDropdown]
|
||||
}
|
||||
|
||||
// blogConfig 的处理,根绝配置自动添加分类和标签
|
||||
const blogConfig = this.$themeConfig.blogConfig || {},
|
||||
isHasCategory = this.userNav.some(item => {
|
||||
return item.text === 'Category'
|
||||
}),
|
||||
isHasTag = this.userNav.some(item => {
|
||||
return item.text === 'Tag'
|
||||
})
|
||||
|
||||
if (!isHasCategory && blogConfig.hasOwnProperty('category')) {
|
||||
const category = blogConfig.category
|
||||
const $categories = this.$categories
|
||||
this.userNav.splice( parseInt(category.location || 2) - 1, 0, {
|
||||
items: $categories.list.map(item => {
|
||||
item.link = item.path
|
||||
item.text = item.name
|
||||
return item
|
||||
}),
|
||||
text: category.text || '分类',
|
||||
type: "links",
|
||||
icon: "reco-category"
|
||||
})
|
||||
}
|
||||
if (!isHasTag && blogConfig.hasOwnProperty('tag')) {
|
||||
const tag = blogConfig.tag
|
||||
this.userNav.splice(parseInt(tag.location || 3) - 1, 0, {
|
||||
link: '/tag/',
|
||||
text: tag.text || '标签',
|
||||
type: "links",
|
||||
icon: "reco-tag"
|
||||
})
|
||||
}
|
||||
|
||||
return this.userNav
|
||||
},
|
||||
|
||||
userLinks () {
|
||||
return (this.nav || []).map(link => {
|
||||
return Object.assign(resolveNavLinkItem(link), {
|
||||
items: (link.items || []).map(resolveNavLinkItem)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
repoLink () {
|
||||
const { repo } = this.$site.themeConfig
|
||||
if (repo) {
|
||||
return /^https?:/.test(repo)
|
||||
? repo
|
||||
: `https://github.com/${repo}`
|
||||
}
|
||||
},
|
||||
|
||||
repoLabel () {
|
||||
if (!this.repoLink) return
|
||||
if (this.$site.themeConfig.repoLabel) {
|
||||
return this.$site.themeConfig.repoLabel
|
||||
}
|
||||
|
||||
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
|
||||
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
|
||||
for (let i = 0; i < platforms.length; i++) {
|
||||
const platform = platforms[i]
|
||||
if (new RegExp(platform, 'i').test(repoHost)) {
|
||||
return platform
|
||||
}
|
||||
}
|
||||
|
||||
return 'Source'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.nav-links
|
||||
display inline-block
|
||||
a
|
||||
line-height 1.4rem
|
||||
color inherit
|
||||
&:hover, &.router-link-active
|
||||
color $accentColor
|
||||
.iconfont
|
||||
color $accentColor
|
||||
.nav-item
|
||||
position relative
|
||||
display inline-block
|
||||
margin-left 1.5rem
|
||||
line-height 2rem
|
||||
&:first-child
|
||||
margin-left 0
|
||||
.repo-link
|
||||
margin-left 1.5rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.nav-links
|
||||
.nav-item, .repo-link
|
||||
margin-left 0
|
||||
|
||||
@media (min-width: $MQMobile)
|
||||
.nav-links a
|
||||
&:hover, &.router-link-active
|
||||
color $textColor
|
||||
.nav-item > a:not(.external)
|
||||
&:hover, &.router-link-active
|
||||
margin-bottom -2px
|
||||
border-bottom 2px solid lighten($accentColor, 8%)
|
||||
</style>
|
125
components/Navbar.vue
Normal file
125
components/Navbar.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<header class="navbar">
|
||||
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
|
||||
|
||||
<router-link
|
||||
:to="$localePath"
|
||||
class="home-link">
|
||||
<img
|
||||
class="logo"
|
||||
v-if="$site.themeConfig.logo"
|
||||
:src="$withBase($site.themeConfig.logo)"
|
||||
:alt="$siteTitle">
|
||||
<span
|
||||
ref="siteName"
|
||||
class="site-name"
|
||||
v-if="$siteTitle">{{ $siteTitle }}</span>
|
||||
</router-link>
|
||||
|
||||
<div
|
||||
class="links"
|
||||
:style="linksWrapMaxWidth ? {
|
||||
'max-width': linksWrapMaxWidth + 'px'
|
||||
} : {}">
|
||||
<Theme />
|
||||
<AlgoliaSearchBox
|
||||
v-if="isAlgoliaSearch"
|
||||
:options="algolia"/>
|
||||
<SearchBox v-else-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false"/>
|
||||
<NavLinks class="can-hide"/>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AlgoliaSearchBox from '@AlgoliaSearchBox'
|
||||
import SearchBox from '@SearchBox'
|
||||
import SidebarButton from '@theme/components/SidebarButton.vue'
|
||||
import NavLinks from '@theme/components/NavLinks.vue'
|
||||
import Theme from '@theme/components/Theme'
|
||||
|
||||
export default {
|
||||
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox, Theme },
|
||||
|
||||
data () {
|
||||
return {
|
||||
linksWrapMaxWidth: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
|
||||
const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))
|
||||
const handleLinksWrapWidth = () => {
|
||||
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
|
||||
this.linksWrapMaxWidth = null
|
||||
} else {
|
||||
this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING
|
||||
- (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0)
|
||||
}
|
||||
}
|
||||
handleLinksWrapWidth()
|
||||
window.addEventListener('resize', handleLinksWrapWidth, false)
|
||||
},
|
||||
|
||||
computed: {
|
||||
algolia () {
|
||||
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
|
||||
},
|
||||
|
||||
isAlgoliaSearch () {
|
||||
return this.algolia && this.algolia.apiKey && this.algolia.indexName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function css (el, property) {
|
||||
// NOTE: Known bug, will return 'auto' if style value is 'auto'
|
||||
const win = el.ownerDocument.defaultView
|
||||
// null means not to return pseudo styles
|
||||
return win.getComputedStyle(el, null)[property]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
$navbar-vertical-padding = 0.7rem
|
||||
$navbar-horizontal-padding = 1.5rem
|
||||
|
||||
.navbar
|
||||
padding $navbar-vertical-padding $navbar-horizontal-padding
|
||||
line-height $navbarHeight - 1.4rem
|
||||
box-shadow 0 1px 6px 0 rgba(32,33,36,.28)
|
||||
a, span, img
|
||||
display inline-block
|
||||
.logo
|
||||
height $navbarHeight - 1.4rem
|
||||
min-width $navbarHeight - 1.4rem
|
||||
margin-right 0.8rem
|
||||
vertical-align top
|
||||
.site-name
|
||||
font-size 1.2rem
|
||||
font-weight 600
|
||||
color $textColor
|
||||
position relative
|
||||
.links
|
||||
padding-left 1.5rem
|
||||
box-sizing border-box
|
||||
background-color white
|
||||
white-space nowrap
|
||||
font-size 0.9rem
|
||||
position absolute
|
||||
right $navbar-horizontal-padding
|
||||
top $navbar-vertical-padding
|
||||
display flex
|
||||
.search-box
|
||||
flex: 0 0 auto
|
||||
vertical-align top
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.navbar
|
||||
padding-left 4rem
|
||||
.can-hide
|
||||
display none
|
||||
.links
|
||||
padding-left 1.5rem
|
||||
</style>
|
84
components/NoteAbstract.vue
Normal file
84
components/NoteAbstract.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="abstract-wrapper">
|
||||
<div
|
||||
v-for="item in formatData"
|
||||
:key="item.path"
|
||||
class="abstract-item">
|
||||
<div class="title">
|
||||
<router-link
|
||||
:to="item.path">{{item.title}}</router-link>
|
||||
</div>
|
||||
<div class="abstract" v-html="item.excerpt"></div>
|
||||
<hr>
|
||||
<PageInfo :pageInfo="item" :currentTag="currentTag"></PageInfo>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageInfo from './PageInfo'
|
||||
|
||||
export default {
|
||||
components: { PageInfo },
|
||||
props: ['data', 'currentPage', 'currentTag'],
|
||||
computed: {
|
||||
formatData () {
|
||||
const data = this.data
|
||||
const currentPage = this.currentPage
|
||||
return data.slice(currentPage * 10 - 10, currentPage * 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
.abstract-wrapper
|
||||
width 100%
|
||||
.abstract-item
|
||||
margin: 0 auto 20px;
|
||||
padding: 16px 20px;
|
||||
width 100%
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||
box-sizing: border-box;
|
||||
transition all .3s
|
||||
background-color $bgColor
|
||||
&:hover
|
||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.2);
|
||||
.title
|
||||
position: relative;
|
||||
font-size: 1.28rem;
|
||||
line-height: 36px;
|
||||
display: inline-block;
|
||||
:after
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: $accentColor;
|
||||
visibility: hidden;
|
||||
-webkit-transform: scaleX(0);
|
||||
transform: scaleX(0);
|
||||
transition: .3s ease-in-out;
|
||||
:hover:after
|
||||
visibility visible
|
||||
-webkit-transform: scaleX(1);
|
||||
transform: scaleX(1);
|
||||
.tags
|
||||
.tag-item
|
||||
cursor: pointer;
|
||||
&.active
|
||||
color $accentColor
|
||||
&:hover
|
||||
color $accentColor
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.tags
|
||||
display block
|
||||
margin-top 1rem;
|
||||
margin-left: 0!important;
|
||||
</style>
|
157
components/Pagation.vue
Normal file
157
components/Pagation.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="pagation" v-show="show">
|
||||
<div class="pagation-list">
|
||||
<span class="jump" v-show="currentPage>1" @click="goPrev" unselectable="on">Prev</span>
|
||||
<span v-show="efont" class="jump" @click="jumpPage(1)">1</span>
|
||||
<span class="ellipsis" v-show="efont">...</span>
|
||||
<span class="jump" v-for="num in indexs" :key="num" :class="{bgprimary:currentPage==num}" @click="jumpPage(num)">{{num}}</span>
|
||||
<span class="ellipsis" v-show="efont&¤tPage<pages-4">...</span>
|
||||
<span v-show="efont&¤tPage<pages-4" class="jump" @click="jumpPage(pages)">{{pages}}</span>
|
||||
<span class="jump" v-show="currentPage < pages" @click="goNext">Next</span>
|
||||
<span class="jumppoint">跳转到:</span>
|
||||
<span class="jumpinp"><input type="text" v-model="changePage"></span>
|
||||
<span class="jump gobtn" @click="jumpPage(changePage)">GO</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
changePage:'', // 跳转页
|
||||
}
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perPage: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
pages () {
|
||||
return Math.ceil(this.data.length / this.perPage)
|
||||
},
|
||||
show:function(){
|
||||
return this.pages && this.pages !=1
|
||||
},
|
||||
efont: function() {
|
||||
if (this.pages <= 7) return false;
|
||||
return this.currentPage > 5
|
||||
},
|
||||
indexs: function() {
|
||||
var left = 1,
|
||||
right = this.pages,
|
||||
ar = [];
|
||||
if (this.pages >= 7) {
|
||||
if (this.currentPage > 5 && this.currentPage < this.pages - 4) {
|
||||
left = Number(this.currentPage) - 3;
|
||||
right = Number(this.currentPage) + 3;
|
||||
} else {
|
||||
if (this.currentPage <= 5) {
|
||||
left = 1;
|
||||
right = 7;
|
||||
} else {
|
||||
right = this.pages;
|
||||
|
||||
left = this.pages - 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (left <= right) {
|
||||
ar.push(left);
|
||||
left++;
|
||||
}
|
||||
return ar;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goPrev () {
|
||||
let currentPage = this.currentPage
|
||||
if (this.currentPage > 1) {
|
||||
this.emit(--currentPage)
|
||||
}
|
||||
},
|
||||
goNext () {
|
||||
let currentPage = this.currentPage
|
||||
if (currentPage < this.pages) {
|
||||
this.emit(++currentPage)
|
||||
}
|
||||
},
|
||||
jumpPage: function(id) {
|
||||
if(id == ''){
|
||||
alert(`请输入页码!`)
|
||||
return
|
||||
}
|
||||
if (id <= this.pages) {
|
||||
this.emit(id)
|
||||
return
|
||||
}
|
||||
alert(`请输入小于${this.pages}的页码!`)
|
||||
},
|
||||
emit (id) {
|
||||
this.$emit('getCurrentPage', id)
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
.pagation
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
margin: 20px auto 0;
|
||||
background: #f2f2f2;
|
||||
.pagation-list
|
||||
font-size: 0;
|
||||
background: #fff;
|
||||
line-height: 50px;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
span
|
||||
font-size: 14px;
|
||||
&.jump
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 8px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
&.jumpinp
|
||||
input
|
||||
width: 55px;
|
||||
height: 26px;
|
||||
font-size: 13px;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
outline none
|
||||
&.bgprimary
|
||||
cursor: default;
|
||||
color: #fff;
|
||||
background: $accentColor;
|
||||
border-color: $accentColor;
|
||||
&.ellipsis
|
||||
padding: 0px 8px;
|
||||
&.jumppoint
|
||||
margin-left: 30px;
|
||||
</style>
|
284
components/Page.vue
Normal file
284
components/Page.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<main class="page" :class="recoShow?'reco-show': 'reco-hide'">
|
||||
<slot name="top"/>
|
||||
|
||||
<div class="page-title" v-if="!(isTimeLine)">
|
||||
<h1>{{$page.title}}</h1>
|
||||
<hr>
|
||||
<PageInfo :pageInfo="$page"></PageInfo>
|
||||
</div>
|
||||
|
||||
<Content/>
|
||||
|
||||
<TimeLine v-if="isTimeLine"></TimeLine>
|
||||
|
||||
<footer class="page-edit">
|
||||
<div
|
||||
class="edit-link"
|
||||
v-if="editLink"
|
||||
>
|
||||
<a
|
||||
:href="editLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{{ editLinkText }}</a>
|
||||
<OutboundLink/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="last-updated"
|
||||
v-if="lastUpdated"
|
||||
>
|
||||
<span class="prefix">{{ lastUpdatedText }}: </span>
|
||||
<span class="time">{{ lastUpdated }}</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="page-nav" v-if="prev || next">
|
||||
<p class="inner">
|
||||
<span
|
||||
v-if="prev"
|
||||
class="prev"
|
||||
>
|
||||
←
|
||||
<router-link
|
||||
v-if="prev"
|
||||
class="prev"
|
||||
:to="prev.path"
|
||||
>
|
||||
{{ prev.title || prev.path }}
|
||||
</router-link>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="next"
|
||||
class="next"
|
||||
>
|
||||
<router-link
|
||||
v-if="next"
|
||||
:to="next.path"
|
||||
>
|
||||
{{ next.title || next.path }}
|
||||
</router-link>
|
||||
→
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<slot name="bottom"/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageInfo from '@theme/components/PageInfo'
|
||||
import { resolvePage, outboundRE, endingSlashRE } from '../util'
|
||||
import TimeLine from '@theme/components/TimeLine'
|
||||
|
||||
export default {
|
||||
components: { PageInfo, TimeLine},
|
||||
|
||||
props: ['sidebarItems'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
recoShow: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isTimeLine () {
|
||||
return this.$page.frontmatter.isTimeLine
|
||||
},
|
||||
lastUpdated () {
|
||||
return this.$page.lastUpdated
|
||||
},
|
||||
|
||||
lastUpdatedText () {
|
||||
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
|
||||
return this.$themeLocaleConfig.lastUpdated
|
||||
}
|
||||
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
|
||||
return this.$site.themeConfig.lastUpdated
|
||||
}
|
||||
return 'Last Updated'
|
||||
},
|
||||
|
||||
prev () {
|
||||
const prev = this.$page.frontmatter.prev
|
||||
if (prev === false) {
|
||||
return
|
||||
} else if (prev) {
|
||||
return resolvePage(this.$site.pages, prev, this.$route.path)
|
||||
} else {
|
||||
return resolvePrev(this.$page, this.sidebarItems)
|
||||
}
|
||||
},
|
||||
|
||||
next () {
|
||||
const next = this.$page.frontmatter.next
|
||||
if (next === false) {
|
||||
return
|
||||
} else if (next) {
|
||||
return resolvePage(this.$site.pages, next, this.$route.path)
|
||||
} else {
|
||||
return resolveNext(this.$page, this.sidebarItems)
|
||||
}
|
||||
},
|
||||
|
||||
editLink () {
|
||||
if (this.$page.frontmatter.editLink === false) {
|
||||
return
|
||||
}
|
||||
const {
|
||||
repo,
|
||||
editLinks,
|
||||
docsDir = '',
|
||||
docsBranch = 'master',
|
||||
docsRepo = repo
|
||||
} = this.$site.themeConfig
|
||||
|
||||
if (docsRepo && editLinks && this.$page.relativePath) {
|
||||
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, this.$page.relativePath)
|
||||
}
|
||||
},
|
||||
|
||||
editLinkText () {
|
||||
return (
|
||||
this.$themeLocaleConfig.editLinkText
|
||||
|| this.$site.themeConfig.editLinkText
|
||||
|| `Edit this page`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.recoShow = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
|
||||
const bitbucket = /bitbucket.org/
|
||||
if (bitbucket.test(repo)) {
|
||||
const base = outboundRE.test(docsRepo)
|
||||
? docsRepo
|
||||
: repo
|
||||
return (
|
||||
base.replace(endingSlashRE, '')
|
||||
+ `/src`
|
||||
+ `/${docsBranch}/`
|
||||
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
|
||||
+ path
|
||||
+ `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||
)
|
||||
}
|
||||
|
||||
const base = outboundRE.test(docsRepo)
|
||||
? docsRepo
|
||||
: `https://github.com/${docsRepo}`
|
||||
return (
|
||||
base.replace(endingSlashRE, '')
|
||||
+ `/edit`
|
||||
+ `/${docsBranch}/`
|
||||
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
|
||||
+ path
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePrev (page, items) {
|
||||
return find(page, items, -1)
|
||||
}
|
||||
|
||||
function resolveNext (page, items) {
|
||||
return find(page, items, 1)
|
||||
}
|
||||
|
||||
function find (page, items, offset) {
|
||||
const res = []
|
||||
flatten(items, res)
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const cur = res[i]
|
||||
if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
|
||||
return res[i + offset]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function flatten (items, res) {
|
||||
for (let i = 0, l = items.length; i < l; i++) {
|
||||
if (items[i].type === 'group') {
|
||||
flatten(items[i].children || [], res)
|
||||
} else {
|
||||
res.push(items[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@require '../styles/wrapper.styl'
|
||||
@require '../styles/loadMixin.styl'
|
||||
|
||||
.page
|
||||
padding-top 6rem
|
||||
padding-bottom 2rem
|
||||
display block
|
||||
.page-title
|
||||
max-width: 740px;
|
||||
margin: 0 auto;
|
||||
padding: 0rem 2.5rem;
|
||||
|
||||
.page-edit
|
||||
@extend $wrapper
|
||||
padding-top 1rem
|
||||
padding-bottom 1rem
|
||||
overflow auto
|
||||
.edit-link
|
||||
display inline-block
|
||||
a
|
||||
color lighten($textColor, 25%)
|
||||
margin-right 0.25rem
|
||||
.last-updated
|
||||
float right
|
||||
font-size 0.9em
|
||||
.prefix
|
||||
font-weight 500
|
||||
color lighten($textColor, 25%)
|
||||
.time
|
||||
font-weight 400
|
||||
color #aaa
|
||||
|
||||
.page-nav
|
||||
@extend $wrapper
|
||||
padding-top 1rem
|
||||
padding-bottom 0
|
||||
.inner
|
||||
min-height 2rem
|
||||
margin-top 0
|
||||
border-top 1px solid $borderColor
|
||||
padding-top 1rem
|
||||
overflow auto // clear float
|
||||
.next
|
||||
float right
|
||||
|
||||
.reco-hide.page {
|
||||
load-start()
|
||||
}
|
||||
.reco-show.page {
|
||||
load-end(0.08s)
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.page-title
|
||||
padding: 0 1rem;
|
||||
.page-edit
|
||||
.edit-link
|
||||
margin-bottom .5rem
|
||||
.last-updated
|
||||
font-size .8em
|
||||
float none
|
||||
text-align left
|
||||
|
||||
</style>
|
68
components/PageInfo.vue
Normal file
68
components/PageInfo.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<i
|
||||
class="iconfont reco-account"
|
||||
v-if="pageInfo.frontmatter.author || $site.themeConfig.author || $site.title">
|
||||
<span>{{ pageInfo.frontmatter.author || $site.themeConfig.author || $site.title }}</span>
|
||||
</i>
|
||||
<i class="iconfont reco-date" v-if="pageInfo.frontmatter.date"><span>{{ new Date(pageInfo.frontmatter.date).toLocaleDateString() }}</span></i>
|
||||
<AccessNumber :idVal="pageInfo.path" :numStyle="numStyle"></AccessNumber>
|
||||
<i class="iconfont reco-tag tags" v-if="pageInfo.frontmatter.tags">
|
||||
<span
|
||||
v-for="(subItem, subIndex) in pageInfo.frontmatter.tags"
|
||||
:key="subIndex"
|
||||
class="tag-item"
|
||||
:class="{ 'active': currentTag == subItem }"
|
||||
@click="goTags(subItem)">
|
||||
{{subItem}}
|
||||
</span>
|
||||
</i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AccessNumber from './Valine/AccessNumber'
|
||||
|
||||
export default {
|
||||
components: { AccessNumber },
|
||||
props: ['pageInfo', 'currentTag'],
|
||||
data () {
|
||||
return {
|
||||
numStyle: {
|
||||
fontSize: '.9rem',
|
||||
fontWeight: 'normal',
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
goTags (tag) {
|
||||
window.location.href = `/tag/?tag=${tag}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
.iconfont
|
||||
display inline-block
|
||||
line-height 1.5rem
|
||||
&:not(:last-child)
|
||||
margin-right 1rem
|
||||
span
|
||||
margin-left .5rem
|
||||
.tags
|
||||
.tag-item
|
||||
cursor: pointer;
|
||||
&.active
|
||||
color $accentColor
|
||||
&:hover
|
||||
color $accentColor
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.tags
|
||||
display block
|
||||
margin-left: 0!important;
|
||||
</style>
|
283
components/Password.vue
Normal file
283
components/Password.vue
Normal file
@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<div class="password-shadow">
|
||||
<Background></Background>
|
||||
<h3 class="title">{{$site.title}}</h3>
|
||||
<p class="description">{{$site.description}}</p>
|
||||
<label class="inputBox" id="box">
|
||||
<input
|
||||
v-model="key"
|
||||
type="password"
|
||||
@keyup.enter="inter"
|
||||
@focus="inputFocus"
|
||||
@blur="inputBlur">
|
||||
<span>{{warningText}}</span>
|
||||
<button ref="passwordBtn" @click="inter">OK</button>
|
||||
</label>
|
||||
|
||||
<div class="footer">
|
||||
<span>
|
||||
<i class="iconfont reco-theme"></i>
|
||||
<a target="blank" href="https://www.npmjs.com/package/vuepress-theme-reco">vuePress-theme-reco</a>
|
||||
</span>
|
||||
<span>
|
||||
<i class="iconfont reco-other"></i>
|
||||
<a>{{ $site.themeConfig.author || $site.title }}</a>
|
||||
</span>
|
||||
<span>
|
||||
<i class="iconfont reco-copyright"></i>
|
||||
<a>{{ year }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Background from '@theme/components/Background'
|
||||
|
||||
export default {
|
||||
components: {Background},
|
||||
name: 'Password',
|
||||
data() {
|
||||
return {
|
||||
warningText: 'Konck! Knock!',
|
||||
key: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
year () {
|
||||
return new Date().getFullYear()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
inter () {
|
||||
const keyVal = this.key.trim()
|
||||
sessionStorage.setItem('key', keyVal)
|
||||
if (!this.isHasKey()) {
|
||||
this.warningText = 'Key Error'
|
||||
return
|
||||
}
|
||||
const passwordBtn = this.$refs.passwordBtn
|
||||
const width = document.getElementById('box').getClientRects()[0].width
|
||||
|
||||
passwordBtn.style.width = `${width - 2}px`
|
||||
passwordBtn.style.opacity = 1
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 800)
|
||||
},
|
||||
isHasKey () {
|
||||
const keyPage = this.$site.themeConfig.keyPage
|
||||
const keys = keyPage.keys
|
||||
return keys && keys.indexOf(sessionStorage.getItem('key')) > -1
|
||||
},
|
||||
inputFocus () {
|
||||
this.warningText = 'Input Your Key'
|
||||
},
|
||||
inputBlur () {
|
||||
this.warningText = 'Konck! Knock!'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
.password-shadow {
|
||||
width 100vw;
|
||||
height 100vh;
|
||||
background #fff
|
||||
position relative
|
||||
.title {
|
||||
position: absolute;
|
||||
left 0
|
||||
right 0
|
||||
top 12%
|
||||
margin auto
|
||||
text-align center
|
||||
color #666
|
||||
font-size 30px
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.description {
|
||||
position: absolute;
|
||||
left 0
|
||||
right 0
|
||||
top 20%
|
||||
margin auto
|
||||
text-align center
|
||||
color #999
|
||||
font-size 22px
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.inputBox{
|
||||
max-width:700px;
|
||||
height: 100px;
|
||||
background: $accentColor;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
left 0
|
||||
right 0
|
||||
top 36%
|
||||
margin auto
|
||||
padding-left 20px
|
||||
box-sizing border-box
|
||||
opacity 0.9
|
||||
input{
|
||||
width:600px;
|
||||
height:100%;
|
||||
border:none;
|
||||
padding:0;
|
||||
padding-left:5px;
|
||||
color: #fff;
|
||||
background: none;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
bottom:0;
|
||||
opacity 0
|
||||
font-size 50px
|
||||
&:focus {
|
||||
opacity 1
|
||||
}
|
||||
&:focus~span{
|
||||
transform: translateY(-80px);
|
||||
color $accentColor
|
||||
font-size 30px
|
||||
opacity:0.8;
|
||||
}
|
||||
&:focus~button{
|
||||
opacity:1;
|
||||
width:100px;
|
||||
}
|
||||
}
|
||||
span{
|
||||
width:200px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
line-height:100px;
|
||||
top:0;
|
||||
left:20px;
|
||||
color: #fff;
|
||||
cursor: text;
|
||||
transition: 0.5s;
|
||||
transform-origin: left top;
|
||||
font-size 30px
|
||||
}
|
||||
button{
|
||||
width:0px;
|
||||
height:98px;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
border 1px solid $accentColor
|
||||
right:1px;
|
||||
top 1px
|
||||
border:0;
|
||||
padding:0;
|
||||
background: #fff;
|
||||
color: $accentColor;
|
||||
font-size:18px;
|
||||
outline:none;
|
||||
cursor: pointer;
|
||||
opacity:0;
|
||||
transition: 0.5s;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
position: absolute;
|
||||
left 0
|
||||
right 0
|
||||
bottom 10%
|
||||
margin auto
|
||||
padding: 2.5rem;
|
||||
text-align: center;
|
||||
color: lighten($textColor, 25%);
|
||||
> span {
|
||||
margin-left 1rem
|
||||
> i {
|
||||
margin-right .5rem
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: $MQMobile) {
|
||||
.inputBox{
|
||||
max-width:700px;
|
||||
height: 60px;
|
||||
background: $accentColor;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
left 0
|
||||
right 0
|
||||
top 43%
|
||||
margin auto
|
||||
padding-left 20px
|
||||
box-sizing border-box
|
||||
opacity 0.9
|
||||
input{
|
||||
width:600px;
|
||||
height:100%;
|
||||
border:none;
|
||||
padding:0;
|
||||
padding-left:5px;
|
||||
color: #fff;
|
||||
background: none;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
bottom:0;
|
||||
opacity 0
|
||||
font-size 30px
|
||||
&:focus {
|
||||
opacity 1
|
||||
}
|
||||
&:focus~span{
|
||||
transform: translateY(-60px);
|
||||
color $accentColor
|
||||
font-size 20px
|
||||
opacity:0.8;
|
||||
}
|
||||
&:focus~button{
|
||||
opacity:1;
|
||||
width:60px;
|
||||
}
|
||||
}
|
||||
span{
|
||||
width:200px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
line-height:60px;
|
||||
top:0;
|
||||
left:20px;
|
||||
color: #fff;
|
||||
cursor: text;
|
||||
transition: 0.5s;
|
||||
transform-origin: left top;
|
||||
font-size 20px
|
||||
}
|
||||
button{
|
||||
width:0px;
|
||||
height:58px;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
border 1px solid $accentColor
|
||||
right:1px;
|
||||
top 1px
|
||||
border:0;
|
||||
padding:0;
|
||||
background: #fff;
|
||||
color: $accentColor;
|
||||
font-size:18px;
|
||||
outline:none;
|
||||
cursor: pointer;
|
||||
opacity:0;
|
||||
transition: 0.5s;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
59
components/Sidebar.vue
Normal file
59
components/Sidebar.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<aside class="sidebar">
|
||||
<NavLinks/>
|
||||
<slot name="top"/>
|
||||
<SidebarLinks :depth="0" :items="items"/>
|
||||
<slot name="bottom"/>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarLinks from '@theme/components/SidebarLinks.vue'
|
||||
import NavLinks from '@theme/components/NavLinks.vue'
|
||||
|
||||
export default {
|
||||
name: 'Sidebar',
|
||||
|
||||
components: { SidebarLinks, NavLinks },
|
||||
|
||||
props: ['items']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.sidebar
|
||||
ul
|
||||
padding 0
|
||||
margin 0
|
||||
list-style-type none
|
||||
a
|
||||
display inline-block
|
||||
.nav-links
|
||||
display none
|
||||
border-bottom 1px solid $borderColor
|
||||
padding 0.5rem 0 0.75rem 0
|
||||
a
|
||||
font-weight 600
|
||||
.nav-item, .repo-link
|
||||
display block
|
||||
line-height 1.25rem
|
||||
font-size 1.1em
|
||||
padding 0.5rem 0 0.5rem 1.5rem
|
||||
& > .sidebar-links
|
||||
padding 1.5rem 0
|
||||
& > li > a.sidebar-link
|
||||
font-size 1.1em
|
||||
line-height 1.7
|
||||
font-weight bold
|
||||
& > li:not(:first-child)
|
||||
margin-top .75rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar
|
||||
.nav-links
|
||||
display block
|
||||
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
|
||||
top calc(1rem - 2px)
|
||||
& > .sidebar-links
|
||||
padding 1rem 0
|
||||
</style>
|
27
components/SidebarButton.vue
Normal file
27
components/SidebarButton.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="sidebar-button" @click="$emit('toggle-sidebar')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
|
||||
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.sidebar-button
|
||||
cursor pointer
|
||||
display none
|
||||
width 1.25rem
|
||||
height 1.25rem
|
||||
position absolute
|
||||
padding 0.6rem
|
||||
top 0.6rem
|
||||
left 1rem
|
||||
.icon
|
||||
display block
|
||||
width 1.25rem
|
||||
height 1.25rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar-button
|
||||
display block
|
||||
</style>
|
129
components/SidebarGroup.vue
Normal file
129
components/SidebarGroup.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<section
|
||||
class="sidebar-group"
|
||||
:class="[
|
||||
{
|
||||
collapsable,
|
||||
'is-sub-group': depth !== 0
|
||||
},
|
||||
`depth-${depth}`
|
||||
]"
|
||||
>
|
||||
<router-link
|
||||
v-if="item.path"
|
||||
class="sidebar-heading clickable"
|
||||
:class="{
|
||||
open,
|
||||
'active': isActive($route, item.path)
|
||||
}"
|
||||
:to="item.path"
|
||||
@click.native="$emit('toggle')"
|
||||
>
|
||||
<span>{{ item.title }}</span>
|
||||
<span
|
||||
class="arrow"
|
||||
v-if="collapsable"
|
||||
:class="open ? 'down' : 'right'">
|
||||
</span>
|
||||
</router-link>
|
||||
|
||||
<p
|
||||
v-else
|
||||
class="sidebar-heading"
|
||||
:class="{ open }"
|
||||
@click="$emit('toggle')"
|
||||
>
|
||||
<span>{{ item.title }}</span>
|
||||
<span
|
||||
class="arrow"
|
||||
v-if="collapsable"
|
||||
:class="open ? 'down' : 'right'">
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<DropdownTransition>
|
||||
<SidebarLinks
|
||||
class="sidebar-group-items"
|
||||
:items="item.children"
|
||||
v-if="open || !collapsable"
|
||||
:sidebarDepth="item.sidebarDepth"
|
||||
:depth="depth + 1"
|
||||
/>
|
||||
</DropdownTransition>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isActive } from '../util'
|
||||
import DropdownTransition from '@theme/components/DropdownTransition.vue'
|
||||
|
||||
export default {
|
||||
name: 'SidebarGroup',
|
||||
props: ['item', 'open', 'collapsable', 'depth'],
|
||||
components: { DropdownTransition },
|
||||
// ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
|
||||
beforeCreate () {
|
||||
this.$options.components.SidebarLinks = require('./SidebarLinks.vue').default
|
||||
},
|
||||
methods: { isActive }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.sidebar-group
|
||||
.sidebar-group
|
||||
padding-left 0.5em
|
||||
&:not(.collapsable)
|
||||
.sidebar-heading:not(.clickable)
|
||||
cursor auto
|
||||
color inherit
|
||||
// refine styles of nested sidebar groups
|
||||
&.is-sub-group
|
||||
padding-left 0
|
||||
& > .sidebar-heading
|
||||
font-size 0.95em
|
||||
line-height 1.4
|
||||
font-weight normal
|
||||
padding-left 2rem
|
||||
&:not(.clickable)
|
||||
opacity 0.5
|
||||
& > .sidebar-group-items
|
||||
padding-left 1rem
|
||||
& > li > .sidebar-link
|
||||
font-size: 0.95em;
|
||||
border-left none
|
||||
&.depth-2
|
||||
& > .sidebar-heading
|
||||
border-left none
|
||||
|
||||
.sidebar-heading
|
||||
color $textColor
|
||||
transition color .15s ease
|
||||
cursor pointer
|
||||
font-size 1.1em
|
||||
font-weight bold
|
||||
// text-transform uppercase
|
||||
padding 0.35rem 1.5rem 0.35rem 1.25rem
|
||||
width 100%
|
||||
box-sizing border-box
|
||||
margin 0
|
||||
border-left 0.25rem solid transparent
|
||||
&.open, &:hover
|
||||
color inherit
|
||||
.arrow
|
||||
position relative
|
||||
top -0.12em
|
||||
left 0.5em
|
||||
&.clickable
|
||||
&.active
|
||||
font-weight 600
|
||||
color $accentColor
|
||||
border-left-color $accentColor
|
||||
&:hover
|
||||
color $accentColor
|
||||
|
||||
.sidebar-group-items
|
||||
transition height .1s ease-out
|
||||
font-size 0.95em
|
||||
overflow hidden
|
||||
</style>
|
109
components/SidebarLink.vue
Normal file
109
components/SidebarLink.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<script>
|
||||
import { isActive, hashRE, groupHeaders } from '../util'
|
||||
|
||||
export default {
|
||||
functional: true,
|
||||
|
||||
props: ['item', 'sidebarDepth'],
|
||||
|
||||
render (h,
|
||||
{
|
||||
parent: {
|
||||
$page,
|
||||
$site,
|
||||
$route,
|
||||
$themeConfig,
|
||||
$themeLocaleConfig
|
||||
},
|
||||
props: {
|
||||
item,
|
||||
sidebarDepth
|
||||
}
|
||||
}) {
|
||||
// use custom active class matching logic
|
||||
// due to edge case of paths ending with / + hash
|
||||
const selfActive = isActive($route, item.path)
|
||||
// for sidebar: auto pages, a hash link should be active if one of its child
|
||||
// matches
|
||||
const active = item.type === 'auto'
|
||||
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
|
||||
: selfActive
|
||||
const link = renderLink(h, item.path, item.title || item.path, active)
|
||||
|
||||
const configDepth = $page.frontmatter.sidebarDepth
|
||||
|| sidebarDepth
|
||||
|| $themeLocaleConfig.sidebarDepth
|
||||
|| $themeConfig.sidebarDepth
|
||||
|
||||
const maxDepth = configDepth == null ? 1 : configDepth
|
||||
|
||||
const displayAllHeaders = $themeLocaleConfig.displayAllHeaders
|
||||
|| $themeConfig.displayAllHeaders
|
||||
|
||||
if (item.type === 'auto') {
|
||||
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
|
||||
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
|
||||
const children = groupHeaders(item.headers)
|
||||
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
|
||||
} else {
|
||||
return link
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderLink (h, to, text, active) {
|
||||
return h('router-link', {
|
||||
props: {
|
||||
to,
|
||||
activeClass: '',
|
||||
exactActiveClass: ''
|
||||
},
|
||||
class: {
|
||||
active,
|
||||
'sidebar-link': true
|
||||
}
|
||||
}, text)
|
||||
}
|
||||
|
||||
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
|
||||
if (!children || depth > maxDepth) return null
|
||||
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
|
||||
const active = isActive(route, path + '#' + c.slug)
|
||||
return h('li', { class: 'sidebar-sub-header' }, [
|
||||
renderLink(h, path + '#' + c.slug, c.title, active),
|
||||
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
|
||||
])
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.sidebar .sidebar-sub-headers
|
||||
padding-left 1rem
|
||||
font-size 0.95em
|
||||
|
||||
a.sidebar-link
|
||||
font-size 1em
|
||||
font-weight 400
|
||||
display inline-block
|
||||
color $textColor
|
||||
border-left 0.25rem solid transparent
|
||||
padding 0.35rem 1rem 0.35rem 1.25rem
|
||||
line-height 1.4
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
&:hover
|
||||
color $accentColor
|
||||
&.active
|
||||
font-weight 600
|
||||
color $accentColor
|
||||
border-left-color $accentColor
|
||||
.sidebar-group &
|
||||
padding-left 2rem
|
||||
.sidebar-sub-headers &
|
||||
padding-top 0.25rem
|
||||
padding-bottom 0.25rem
|
||||
border-left none
|
||||
&.active
|
||||
font-weight 500
|
||||
</style>
|
86
components/SidebarLinks.vue
Normal file
86
components/SidebarLinks.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<ul
|
||||
class="sidebar-links"
|
||||
v-if="items.length"
|
||||
>
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<SidebarGroup
|
||||
v-if="item.type === 'group'"
|
||||
:item="item"
|
||||
:open="i === openGroupIndex"
|
||||
:collapsable="item.collapsable || item.collapsible"
|
||||
:depth="depth"
|
||||
@toggle="toggleGroup(i)"
|
||||
/>
|
||||
<SidebarLink
|
||||
v-else
|
||||
:sidebarDepth="sidebarDepth"
|
||||
:item="item"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarGroup from '@theme/components/SidebarGroup.vue'
|
||||
import SidebarLink from '@theme/components/SidebarLink.vue'
|
||||
import { isActive } from '../util'
|
||||
|
||||
export default {
|
||||
name: 'SidebarLinks',
|
||||
|
||||
components: { SidebarGroup, SidebarLink },
|
||||
|
||||
props: [
|
||||
'items',
|
||||
'depth', // depth of current sidebar links
|
||||
'sidebarDepth' // depth of headers to be extracted
|
||||
],
|
||||
|
||||
data () {
|
||||
return {
|
||||
openGroupIndex: 0
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.refreshIndex()
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$route' () {
|
||||
this.refreshIndex()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
refreshIndex () {
|
||||
const index = resolveOpenGroupIndex(
|
||||
this.$route,
|
||||
this.items
|
||||
)
|
||||
if (index > -1) {
|
||||
this.openGroupIndex = index
|
||||
}
|
||||
},
|
||||
|
||||
toggleGroup (index) {
|
||||
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
|
||||
},
|
||||
|
||||
isActive (page) {
|
||||
return isActive(this.$route, page.regularPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOpenGroupIndex (route, items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
if (item.type === 'group' && item.children.some(c => c.type === 'page' && isActive(route, c.path))) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
</script>
|
124
components/Theme/ThemeOptions.vue
Executable file
124
components/Theme/ThemeOptions.vue
Executable file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="theme-options">
|
||||
<ul v-if="reco.hasThemes" class="color-theme-options">
|
||||
<li>
|
||||
<a href="#" class="default-theme" @click.prevent="setTheme()"></a>
|
||||
</li>
|
||||
<li v-for="color in reco.themes" :key="color">
|
||||
<a href="#" :class="`${color}-theme`" :style="{background: colors[color]}" @click.prevent="setTheme(color)"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="!reco.disableDarkTheme" class="dark-theme-options toggle-option">
|
||||
<label for="dark-theme-toggle">Enable Dark Theme?</label>
|
||||
<input id="dark-theme-toggle" v-model="darkTheme" type="checkbox" @change="toggleDarkTheme" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import recoConfig from './recoConfig.js';
|
||||
|
||||
export default {
|
||||
name: 'ThemeOptions',
|
||||
|
||||
mixins: [recoConfig],
|
||||
|
||||
data() {
|
||||
return {
|
||||
darkTheme: 'false'
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const theme = localStorage.getItem('reco-theme')
|
||||
const night = localStorage.getItem('reco-night')
|
||||
const classes = document.body.classList;
|
||||
|
||||
this.darkTheme = night === 'true' ? true : false
|
||||
if (night === 'true') classes.add(`reco-theme-night`)
|
||||
if (theme) this.setTheme(theme)
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleDarkTheme() {
|
||||
console.log(123, this.darkTheme)
|
||||
localStorage.setItem('reco-night', this.darkTheme)
|
||||
console.log(456, localStorage.getItem('reco-night'))
|
||||
const classes = document.body.classList;
|
||||
if (this.darkTheme) {
|
||||
const oldColor = [...classes]
|
||||
classes.value = ''
|
||||
classes.add(`reco-theme-night`)
|
||||
oldColor.forEach(item => {
|
||||
classes.add(item)
|
||||
})
|
||||
}
|
||||
else {
|
||||
classes.remove(`reco-theme-night`)
|
||||
}
|
||||
},
|
||||
setTheme(theme, moveClass = true) {
|
||||
const colorThemes = this.reco.themes;
|
||||
|
||||
const classes = document.body.classList;
|
||||
const themes = colorThemes.map(colorTheme => `reco-theme-${colorTheme}`);
|
||||
|
||||
if (!theme) {
|
||||
if (moveClass) localStorage.removeItem('reco-theme');
|
||||
classes.remove(...themes);
|
||||
return
|
||||
}
|
||||
|
||||
classes.remove(...themes.filter(t => t !== `reco-theme-${theme}`));
|
||||
|
||||
if (moveClass) {
|
||||
classes.add(`reco-theme-${theme}`);
|
||||
localStorage.setItem('reco-theme', theme);
|
||||
} else {
|
||||
localStorage.removeItem('reco-theme')
|
||||
classes.remove(`reco-theme-${theme}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
.color-theme-options {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
li {
|
||||
width: 33%;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 2px;
|
||||
|
||||
&.default-theme {
|
||||
background-color: $accentColor;
|
||||
}
|
||||
|
||||
&.blue-theme {
|
||||
background-color: $blueAccentColor;
|
||||
}
|
||||
|
||||
&.red-theme {
|
||||
background-color: $redAccentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
padding-right: 0.25em;
|
||||
}
|
||||
}
|
||||
</style>
|
140
components/Theme/index.vue
Executable file
140
components/Theme/index.vue
Executable file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div v-if="showSettings" v-click-outside="hideMenu" class="color-picker">
|
||||
<a class="color-button" href="#" @click.prevent="showMenu = !showMenu">
|
||||
<i class="iconfont reco-color"></i>
|
||||
</a>
|
||||
<transition name="menu-transition" mode="out-in">
|
||||
<div v-show="showMenu" class="color-picker-menu">
|
||||
<ThemeOptions />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClickOutside from 'vue-click-outside';
|
||||
import ThemeOptions from './ThemeOptions.vue';
|
||||
import recoConfig from './recoConfig.js';
|
||||
|
||||
export default {
|
||||
name: 'UserSettings',
|
||||
|
||||
directives: {
|
||||
'click-outside': ClickOutside,
|
||||
},
|
||||
|
||||
components: {
|
||||
ThemeOptions
|
||||
},
|
||||
|
||||
mixins: [recoConfig],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showMenu: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
showSettings() {
|
||||
const { reco } = this;
|
||||
return reco.hasThemes || reco.disableDarkTheme !== true || reco.disableThemeIgnore !== true;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
hideMenu() {
|
||||
this.showMenu = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
.color-picker {
|
||||
position: relative;
|
||||
margin-right: 1em;
|
||||
|
||||
.color-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
.iconfont {
|
||||
font-size 1.4rem
|
||||
color: $accentColor
|
||||
}
|
||||
.settings-icon {
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.color-picker-menu {
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
min-width: 100px;
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
transform: translateX(-50%);
|
||||
z-index: 150;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 50%;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent $borderColor;
|
||||
border-width: 0 7px 7px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.menu-transition-enter-active,
|
||||
&.menu-transition-leave-active {
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
&.menu-transition-enter,
|
||||
&.menu-transition-leave-to {
|
||||
top: 50px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reco-theme-dark {
|
||||
.color-picker-menu {
|
||||
background-color: $darkPrimaryBg;
|
||||
border-color: $darkBorderColor;
|
||||
|
||||
&::before {
|
||||
border-bottom-color: $darkBorderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobile) {
|
||||
.color-picker {
|
||||
margin-right: 0;
|
||||
|
||||
.color-picker-menu {
|
||||
left: calc(50% - 35px);
|
||||
|
||||
&::before {
|
||||
left: calc(50% + 35px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
22
components/Theme/recoConfig.js
Executable file
22
components/Theme/recoConfig.js
Executable file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
reco: {},
|
||||
colors: {
|
||||
blue: '#2196f3',
|
||||
red: '#f26d6d',
|
||||
green: '#3eaf7c',
|
||||
orange: '#fb9b5f'
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.reco = {
|
||||
themes: ['blue', 'red', 'green', 'orange'],
|
||||
disableDarkTheme: false
|
||||
};
|
||||
|
||||
this.reco.hasThemes = this.$site.themeConfig.themePicker || true
|
||||
},
|
||||
};
|
189
components/TimeLine.vue
Normal file
189
components/TimeLine.vue
Normal file
@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="timeline-wrapper">
|
||||
<li class="desc">Yesterday Once More!</li>
|
||||
<li v-for="(item, index) in formatPagesArr" :key="index">
|
||||
<h3 class="year">{{item.year}}</h3>
|
||||
<ul class="year-wrapper">
|
||||
<li v-for="(subItem, subIndex) in item.data" :key="subIndex">
|
||||
<span class="date">{{dateFormat(new Date(subItem.frontmatter.date))}}</span>
|
||||
<span class="title" @click="go(subItem.path)">{{subItem.title}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'TimeLine',
|
||||
data () {
|
||||
return {
|
||||
pages: [],
|
||||
tags: [],
|
||||
currentTag: '',
|
||||
currentPage: 1,
|
||||
formatPages: {},
|
||||
formatPagesArr: []
|
||||
}
|
||||
},
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
trueCurrentTag () {
|
||||
return this.currentTag
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getPages()
|
||||
},
|
||||
methods: {
|
||||
// 根据分类获取页面数据
|
||||
getPages (tag) {
|
||||
let pages = this.$site.pages
|
||||
pages = pages.filter(item => {
|
||||
const { home, isTimeLine, date } = item.frontmatter
|
||||
return !(home == true || isTimeLine == true || date === undefined)
|
||||
})
|
||||
// reverse()是为了按时间最近排序排序
|
||||
this.pages = pages.length == 0 ? [] : pages
|
||||
for (let i = 0, length = pages.length; i < length; i++) {
|
||||
const page = pages[i]
|
||||
const pageDateYear = this.dateFormat(page.frontmatter.date, 'year')
|
||||
if (this.formatPages[pageDateYear]) this.formatPages[pageDateYear].push(page)
|
||||
else {
|
||||
this.formatPages[pageDateYear] = [page]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(let key in this.formatPages) {
|
||||
this.formatPagesArr.unshift({
|
||||
year: key,
|
||||
data: this.formatPages[key].sort((a, b) => {
|
||||
return this._getTimeNum(b) - this._getTimeNum(a)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
// 时间格式化
|
||||
dateFormat (date, type) {
|
||||
const dateObj = new Date(date)
|
||||
const year = dateObj.getFullYear()
|
||||
const mon = dateObj.getMonth() + 1
|
||||
const day = dateObj.getDate()
|
||||
if (type == 'year') return year
|
||||
else return `${mon}-${day}`
|
||||
},
|
||||
// 跳转
|
||||
go (url) {
|
||||
this.$router.push({path: url})
|
||||
},
|
||||
// 获取时间的数字类型
|
||||
_getTimeNum (date) {
|
||||
return parseInt(new Date(date.frontmatter.date).getTime())
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@require '../styles/wrapper.styl'
|
||||
|
||||
.timeline-wrapper
|
||||
box-sizing border-box
|
||||
max-width: 740px;
|
||||
margin: 0 auto;
|
||||
position relative
|
||||
list-style none
|
||||
&::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 0;
|
||||
margin-left: -2px;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.desc, .year {
|
||||
position: relative;
|
||||
color #666
|
||||
font-size 16px
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
z-index 2;
|
||||
left: -19px;
|
||||
top: 50%;
|
||||
margin-left: -4px;
|
||||
margin-top: -4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ddd;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.year {
|
||||
margin: 80px 0 0px;
|
||||
color #555
|
||||
font-weight: 700;
|
||||
font-size 26px
|
||||
}
|
||||
.year-wrapper {
|
||||
padding-left 0!important
|
||||
li {
|
||||
display flex
|
||||
padding 30px 0 10px
|
||||
list-style none
|
||||
border-bottom: 1px dashed #ccc;
|
||||
position relative
|
||||
&:hover {
|
||||
.date {
|
||||
color $accentColor
|
||||
&::before {
|
||||
background $accentColor
|
||||
}
|
||||
}
|
||||
.title {
|
||||
color $accentColor
|
||||
}
|
||||
}
|
||||
.date {
|
||||
width 40px
|
||||
line-height 30px
|
||||
color: #555;
|
||||
font-size 12px
|
||||
&::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: -19px;
|
||||
top: 41px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-left: -4px;
|
||||
background: #ddd;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #fff;
|
||||
z-index 2
|
||||
}
|
||||
}
|
||||
.title {
|
||||
line-height 30px
|
||||
color: #555;
|
||||
font-size 16px
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: $MQMobile)
|
||||
.timeline-wrapper
|
||||
margin: 0 1.2rem;
|
||||
</style>
|
24
components/Valine/AccessNumber.vue
Normal file
24
components/Valine/AccessNumber.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<span v-if="$site.themeConfig.valineConfig" :id="getIdVal(idVal)" class="leancloud-visitors" data-flag-title="Your Article Title">
|
||||
<i class="iconfont reco-eye" style="margin-right: .5rem"></i>
|
||||
<a class="leancloud-visitors-count" :style="numStyle"></a>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['idVal', 'numStyle'],
|
||||
methods: {
|
||||
getIdVal (path) {
|
||||
const base = this.$site.base
|
||||
return base.slice(0, base.length - 1) + path
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.leancloud-visitors
|
||||
margin-right .5rem
|
||||
</style>
|
||||
|
83
components/Valine/index.vue
Normal file
83
components/Valine/index.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="valine-wrapper" v-show="isShowComment">
|
||||
<div id="valine"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
export default {
|
||||
props: ['isComment'],
|
||||
computed: {
|
||||
// 是否显示评论
|
||||
isShowComment () {
|
||||
const frontmatter = this.$page.frontmatter
|
||||
return this.isComment == false || frontmatter.isComment == false || frontmatter.home == true ? false : true
|
||||
}
|
||||
},
|
||||
mounted: function(){
|
||||
this.createValine()
|
||||
},
|
||||
methods: {
|
||||
createValine () {
|
||||
const valineConfig = this.$site.themeConfig.valineConfig
|
||||
if (valineConfig) {
|
||||
const Valine = require('valine');
|
||||
const AV = require('leancloud-storage')
|
||||
if (typeof window !== 'undefined') {
|
||||
this.window = window
|
||||
window.AV = AV
|
||||
}
|
||||
|
||||
new Valine({
|
||||
el: '#valine' ,
|
||||
appId: valineConfig.appId,// your appId
|
||||
appKey: valineConfig.appKey, // your appKey
|
||||
verify:false,
|
||||
visitor: true,
|
||||
avatar:'retro',
|
||||
path: window.location.pathname,
|
||||
placeholder: 'just go go'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route' (to, from) {
|
||||
if(to.path !== from.path){
|
||||
// 切换页面时刷新评论
|
||||
// this.$router.go(0)
|
||||
setTimeout(() => {
|
||||
this.createValine()
|
||||
}, 300)
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
.theme-container.no-sidebar
|
||||
.valine-wrapper
|
||||
padding-left 0
|
||||
|
||||
.valine-wrapper
|
||||
background-color $bgColor
|
||||
padding-left: 20rem;
|
||||
#valine
|
||||
padding 2rem
|
||||
max-width: 740px;
|
||||
margin: 0 auto;
|
||||
@media (max-width: $MQNarrow)
|
||||
.theme-container.no-sidebar
|
||||
.valine-wrapper
|
||||
padding-left 0
|
||||
.valine-wrapper
|
||||
padding-left: 16.4rem;
|
||||
@media (max-width: $MQMobile)
|
||||
.valine-wrapper
|
||||
padding-left: 0;
|
||||
</style>
|
109
fonts/iconfont.css
Normal file
109
fonts/iconfont.css
Normal file
@ -0,0 +1,109 @@
|
||||
@font-face {font-family: "iconfont";
|
||||
src: url('iconfont.eot?t=1555230336940'); /* IE9 */
|
||||
src: url('iconfont.eot?t=1555230336940#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAA3kAAsAAAAAGEAAAA2VAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCGPAqeSJg+ATYCJANgCzIABCAFhG0HgWQbPBQzo8LGAQBRPg/Z/+lAC7nKrp+BQ65GtdFY67Wu2OkOR2Pn6WsdTQIS7RWsH8yaDghWQvqrhu4OgLBjp/bu8FcELrA+Bw+fzvKPZiQ2wQEQVWgfIHbEJQFo9yBJlS4v9nvXrAjCRqiSrOoRKLT30uwPLM12Mim0OSAz2/ncXu8Sv0ucDzGVt2NR3SrOAq8brPCjUjM7eR1bBDac0/6hKuEB/xs/X9/fEG3z7v7FigAxGp80ChMUE6MKXeHMBbGEZbOObkAAytAmO3IZcDMvLfbgtoOk1lcDBPPN/ap6/M04TEOT12nxtq/bDjn8ULEIoVLChqlEKjQakUZIkUrOWMS2mPUrLpJQD9qI2BgCcChKZUTr58+f1ggFYwXKTA5Ot0aZEFSBtcAKMAILKkdsQMfSVmoH1tuPF7+pKSzQ0CVsU93HPmWtg88/y2W5GCyzZLsjwNtCIIHKgAItHUwWUaRnKlvOCVVW+EBFitP5Oj6+yREUkVBUWeXV1khrg0wwz+q8/Py1v37+szw3VPNrC/iqVluwaEFF7rcyVX2qvn9IEMfCwyaAwkAniksYjQgpgvP2Dy9EEhNBDIQBlfYARGnkpMBHOiMBvumMOPi2MyzwHWd44AdYilAQLIMoAowCSgBjABUFRgcqC0wUqDwwLlAtYMJAtYHRgBqBpVvUGvLpRYPAMm40ARgJNA8YB2g1nBGCvHxnJCF/rTNM+PWTINiDFKhGDIA0FqgOtKWlToAxsMXM9sDovzSntzqiIcBJTSw0YjRwyrN2gLGZOBHzvIAoFhLhhJRtR5CorDoF3yp02LbLep6wOW+ZcAXbjdte1LNjcutlw57QyxYNR3VTt5pajtK5wy+s5/ZT85nmDJCBMuO88w8gyLGfgswzkHka/UR2/roRde5nStDGt1qs9sMXf6QHb/4eiFh12bqCA84l86L++R0GmQoLjW+dLnWKQKGmcurMNfD6JWcV7SsU9jT+dXpVgg71XJEGss8zdj3Pi9h/mhCw8iw38sKL5Kmsd8HeTZPTw7mtKFoUVvR77/Dq/E34itrBFNLTyiqK/g88WqbOhQ/5atYPTgXD1EO32lArPvyMo6OgiOBo47Y8eZP+tfdlVtcAm4kNK1/NMGnjIRNB84x9tCrvlsFf8Xo69AX+9nVL/NHq7F2i/A+d72mH9ir3GXoJhRhezevHl5pOaHQoaF8GqZcQmPmreo4kbZcgvPv+8WoxbdvjQ+lLn1e6VcxewqzM9AK9ZPphNXvpCs+l1X+BKM8AGZ/pefjVgFnGjOwrBQ3mIj9hggo5h0TxP1VdfQIu1T1a6v8K9kr1eDm+RiV+TbS+Czc4fMGz5xxKKwO95rSV9X2LnPcv8+6SksvfEZiGNZWQYJbTdSxS63/Cq66zvrv0zFv0PWbVN3j/PTKM9v5yLluX7AuT/s4Mbcp/374rzKxc93kH2PU8oIlobfQDDuXSkoSdC9LS5szyxRx0eBkK60o+PriUaEiFZV0g1ROuEmBmXbmXfKfd92I1T3pWufIgeWvmIsv8EgRf+sD6T/uSddm5ONBd4gOevrpRb9CY2H9YMLPSLJxd5RTPr7mRPKz4PXCxzECze6sTGHFzbFGTJsG1lb46QB9vxWA1g+RM2ni8NNiXN0OFNy7q9mURfkNDVq9IzULA7H75i+5aYVdwDXKUDZvavAl7o8uYKqC6PwVqPs8TlNn7op/1EQBU/UBp6cCnSszjT4l9rW1SEsHV9byuUcSeoJg2gUAVHx43OVsQtiyuXQNnNuyTQb0Lbq4K9r6w/zHH9VtL0oUbycmu400m5FaxXHHzUJbf/6ZD3tLakzvJ1gXsTmxd8a24dXVFt62KonvBmzSJsPwQOVn44IIkyQgYeQQtDE++6BqSkQraEAHcrReSoDbXgn13Pc5D7r4gCFdYfppjP05KwhLFXXm2jeIQ2Q8TlNnmjCQcSAFYufNFLl2QcKBXWJdZhjuzfr1dw8oCxaFrZ2z4OQtXymmC7KyFp65stM1WnbHIkC+yvHrV01JXZs5TXKMsEtmtJFZG7CB2NB+wXROxmseuuWLnqH5qKBORu2J3lQo8PlgyNlKa4TBsa5lk7SB22Pbv/8liJoIx832EI65hQuPe7qzk873kJuMauOGNfUF60Arq6eiIxU9SK3sSMLQyBM2hCMswu/FE675tdR3PIkjQz/DMNrmOZf1/HBwehzUEKb4cViQzVcBcBuTlQDZk8QQMQJB2AlmQzcthQEAAF4Hg1Km5YnISOKgAxA8Cg7JSEMxEZ/LQylOnnSc5DqHt21GkmU4U6ARCMwSIxQCAhtBBK0AJlLW0gB1q//++Xh3IPVWgqmoILeuOT58GpejUEWwQxMV3MPycqntopNWOhuBwOdSxWkFUN7wjv+eV3j2qs3hHeUeWM74ywGrXz+xBg2j1TFSL6vT8g0iZLRUGWvErWBUjJYaZThS7BQHPC0QAPIwQgQOo30AECLRX/F3+Ul2O0TvXxekCH7W1Qr4lZ5B0b1ZSwT7NUN/stQeYOt4YTCxsiNOfamNinFKRwoFVk/ziLQfZPosp2U5a/nXgVlDnzkkQqqQkSvSqQTAW8TNyxP7p3K8pMG5Yv9fi7IjMOHExfDbHaJbOlC2DKoZU/wwT0vUD9J9Jzk737Gjq5YJZy/dpyAK5GoZUA0ythClHwxFPx0LtyixpAqMME/mdZpP6gTxevSlhHXMwxurQivFxxYnPdLNNVpzofl5e42CT8eqoAQhJaq0a8oBDWisquHvF4r3cCwIaGK8EhHP/GKbFF1Dw2L+OZrdfZYeFSWgzHHXVYimBkzBKizar41Qge/Zr4hYl428T4NaaPbJ7VGPGbQJ/hfKK+D381WMxFaZ1gNEgsioB6VJaSytNmm5uEECYuRRbCYkW06M5KRP7OExsJsPPNfpDmU0pDrrh4xpzQJNNvQ+foejuUTCerKMDah21WH/o0fmcyHYePFKTq/OgRg2cpSd+PLC68g78yMfCMq0h3lCWmCde5VWmNScrvscsOYhMPV5VbPrvfYFnwZ12Id/fJZRsGqzco0n2pNIPooM16J6+mqczISbRXhkgroJ0ZviOwnOmf1J2xzhw+Dzn+XMhrfUMmO/MsOS3W+UH+a0TUmfI3/LZ+IioyH8rn0EVrvMLGjpPe6wmwxnMbz1jbK3O4vKC6/e7+2338pIrJuk8a/sM1HyspqE940tJgaAjusPEpJ2Y5gWdUrR01NUMlILbSauId04ScMCIvfifP2pvBDVUon34799z7P39n1Ny1kBv5dy+22jntspvuGKk6ZQfqLv41QKJXFhJFshVvuDMmcZ31c9VkDd795a1nqEejmILCzlA30mX+qAETpsGS3Bd5g4zPSc9NPv40VmYXoC208VN2Ovf2CbdOT3AAWu4hJub40pz6ZWCOnlZZhvN65Mq8AE0fxHshT2KhT2oBC+nm2Xqc7xZlYxpiOkpZMGkDDotPj8WLeH4ow5uJYXBMQBPFzVXuHdUek4usKqWgjI2teOY4GWD4lKJZzUaH4LlHGpSjap61qgchVSh/fBvy+gtuKkne3NoWkD8ARBjQR0moBQuF5G8VtkrShcHO9ce91adPuNpe4is568KLZq/r+eDm8pHYqF+Jkg4lwk5thMFn+aTjKy8iC3B6/LWrWE4LElZGzLueeHJ5bcTKC1r6QVyULgTYVJ+hpdLW4W7nUqanVQ1rjbJuEutyc/ydLajiCma6xLyoE/HqgeBKKojEOcfqGQTERfI1kAgo07lActKySCJZOIyQcHUkAEkW5QcuHZ1ru1cqUTeuFyAwmyUtzbqhGE5kiKxVDLXFtR5qG0a/0ViZUxzS3Vl6KY1hhkgutEd1llnRKb/mKinG/yYnkG8R6Wu1enzhHYZUQ25G6+64Pn/0Tmgbf9vALnTtmJBNvm73IldqwsqgHYCkHtAiTlA8s4vzDPqfpB1AFKGOjJHQqwFwpesxnL/m/OtHEDmbvdkLCQyNlif5KopGKnAfgcl/T9j+pvQVSQTPmANkg1kWCrzCNp2uSLprJu5y5+IlpsGq/9l4387fhiQ9vpNKItzupGPT36nQLMYAXfwWOUVaKX+VfVC0+q7NXJwTnakvJzF3d5MPjZZmiXYmRrg8AFw5x4f5eM0+ScqK55l+CcSoGFSBEgsSmsr/cpAx6U2MLBoBhwq0W5xlxgz6SKUDVRkaQQEYU4CjSCXgCTMA22l/wHoJPkLGISFBA59RWyFLmXFhEkrhhZk6nhTwVBtRbhMXNkvuFRqTqus+A8cQ5raVq3en/eCGtzFLPHsdiJWWaZKPUebQ1mSaphyGFl5kWa/rjY2NOvKUNWa0AoGaEQ7zFCM5OZ1Agaixg4vTAbe/wvgSJRouKDJH69/ACYSohU1HrA0WUH7hasrNTktlWdxxrFjNhGLkrUZokLRlggoeTahmB4eLwcYhBXjG2Q27KlGXY2tyq3G71/tEhwbX7NslekBoiQrqqYbJrPL3luxWG12h9Pl9nh9/lAMlcTDCjFqhy5uGERoNn5kVtZpO+Lb4ALjtXTrphrKJYiAeyZmda9CnQYuiE/HzunUJ/FBYU8VOqnpina9rPGJjI5U4JZ0PTbU3Dg4L0NtDKVahnnQdfSpHxFU0FkMtAAAAA==') format('woff2'),
|
||||
url('iconfont.woff?t=1555230336940') format('woff'),
|
||||
url('iconfont.ttf?t=1555230336940') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||
url('iconfont.svg?t=1555230336940#iconfont') format('svg'); /* iOS 4.1- */
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.reco-color:before {
|
||||
content: "\eae9";
|
||||
}
|
||||
|
||||
.reco-message:before {
|
||||
content: "\e634";
|
||||
}
|
||||
|
||||
.reco-eye:before {
|
||||
content: "\e669";
|
||||
}
|
||||
|
||||
.reco-search:before {
|
||||
content: "\e611";
|
||||
}
|
||||
|
||||
.reco-category:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.reco-wechat:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.reco-npm:before {
|
||||
content: "\e88d";
|
||||
}
|
||||
|
||||
.reco-twitter:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.reco-csdn:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.reco-menu:before {
|
||||
content: "\e67c";
|
||||
}
|
||||
|
||||
.reco-github:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.reco-qq:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
|
||||
.reco-other:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.reco-home:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.reco-up:before {
|
||||
content: "\e68b";
|
||||
}
|
||||
|
||||
.reco-tag:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.reco-date:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.reco-bokeyuan:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.reco-copyright:before {
|
||||
content: "\ef87";
|
||||
}
|
||||
|
||||
.reco-account:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.reco-jianshu:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.reco-theme:before {
|
||||
content: "\e7e8";
|
||||
}
|
||||
|
||||
.reco-three:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
BIN
fonts/iconfont.eot
Normal file
BIN
fonts/iconfont.eot
Normal file
Binary file not shown.
95
fonts/iconfont.svg
Normal file
95
fonts/iconfont.svg
Normal file
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-9-30: Created.
|
||||
-->
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by iconfont
|
||||
</metadata>
|
||||
<defs>
|
||||
|
||||
<font id="iconfont" horiz-adv-x="1024" >
|
||||
<font-face
|
||||
font-family="iconfont"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1024"
|
||||
ascent="896"
|
||||
descent="-128"
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="color" unicode="" d="M778.22179555 384q29.56401778 0 51.21517 20.48606777t21.60260778 52.33171001-21.60260778 52.33171001-51.21517 20.48606776-51.21516998-20.48606776-21.60260779-52.33171001 21.60260779-52.33171001 51.21516998-20.48606777zM632.58624 577.40401778q29.56401778 0 51.21516999 21.60260779t21.60260779 51.21516998-21.60260779 51.21517-51.21516999 21.60260778-51.21516999-21.60260778-21.60260779-51.21517 21.60260779-51.21516998 51.21516999-21.60260779zM391.41376 577.40401778q29.56401778 0 51.21516999 21.60260779t21.60260779 51.21516998-21.60260779 51.21517-51.21516999 21.60260778-51.21516999-21.60260778-21.60260779-51.21517 21.60260779-51.21516998 51.21516999-21.60260779zM245.77820445 384q29.56401778 0 51.21516998 20.48606777t21.60260779 52.33171001-21.60260779 52.33171001-51.21516998 20.48606776-51.21517-20.48606776-21.60260778-52.33171001 21.60260778-52.33171001 51.21517-20.48606777zM512 820.90666667q179.76282112 0 308.35901667-113.78991446t128.54765-275.34828999q0-100.14871666-71.7012389-170.68487111t-171.80140999-70.53615445l-84.17735111 0q-31.8456411 0-52.33171001-21.60260779t-20.48606777-51.21516999q0-25.04931555 18.20444444-47.76846222t18.20444445-50.05008555q0-31.8456411-20.48606777-52.33171001t-52.33171001-20.48606777q-182.04444445 0-309.47555555 127.43111112t-127.43111112 309.47555555 127.43111112 309.47555555 309.47555555 127.43111112z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="message" unicode="" d="M864.227 791.373h-702c-48.988 0-88.732-39.621-88.732-88.487v-519.688c0-48.866 42.607-84.866 91.616-84.866h218.638l122.113-106.65 122.134 106.65h233.366c48.988 0 91.596 36.021 91.596 84.866v519.688c0 48.866-39.763 88.487-88.732 88.487zM685.393 342.72299999999996h-309.702c-17.202 0-31.152 13.95-31.152 31.152s13.95 31.152 31.152 31.152h309.702c17.223 0 31.152-13.95 31.152-31.152s-13.95-31.152-31.152-31.152zM759.029 505.848h-484.343c-17.202 0-31.152 13.95-31.152 31.152 0 17.223 13.95 31.173 31.152 31.173h484.343c17.202 0 31.152-13.95 31.152-31.173 0.021-17.202-13.95-31.152-31.152-31.152z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="eye" unicode="" d="M1023.997 384.169c0-99.79-211.129-312.67-511.999-312.67C218.787 71.49900000000002 0 280.875 0 384.169c0 103.279 219.73 312.334 511.999 312.334 303.234-0.001 512.071-209.057 511.999-312.335L1023.998 384.169zM512.45 639.471C376.184 639.471 265.72 525.087 265.72 384c0-141.089 110.463-255.471 246.729-255.471 136.307 0 246.774 114.383 246.774 255.471C759.223 525.087 648.756 639.471 512.45 639.471L512.45 639.471zM511.999 547.0989999999999c-86.778 0-157.126-72.827-157.126-162.673 0-89.836 70.348-162.677 157.126-162.677s157.097 72.841 157.097 162.677C669.096 474.273 598.776 547.0989999999999 511.999 547.0989999999999L511.999 547.0989999999999z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="search" unicode="" d="M1023.991873-35.817713000000026l-92.147972-92.180481L728.542586 75.23810300000002c-76.088314-56.998056-169.675731-91.994455-272.138222-91.994455C204.34252-16.693138999999974 0.073146 187.606938 0.073146 439.605569c0 252.078099 204.269374 456.345667 456.392625 456.345667 251.984183 0 456.251751-204.269374 456.251751-456.345667 0-102.446236-34.965695-196.002949-91.963751-272.091263L1023.991873-35.817713000000026 1023.991873-35.817713000000026 1023.991873-35.817713000000026 1023.991873-35.817713000000026zM456.465771 765.490904c-180.066105 0-325.932293-145.896891-325.932293-325.948548 0-180.002893 145.866188-325.932293 325.932293-325.932293 179.927037 0 325.916038 145.929401 325.916038 325.932293C782.381809 619.657226 636.392808 765.490904 456.465771 765.490904L456.465771 765.490904 456.465771 765.490904 456.465771 765.490904zM456.465771 765.490904" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="category" unicode="" d="M928 288C928 323.36 899.36 352 864 352 792.96 352 679.04 352 608 352 572.64 352 544 323.36 544 288 544 216.96000000000004 544 103.03999999999996 544 32 544-3.3600000000000136 572.64-32 608-32 679.04-32 792.96-32 864-32 899.36-32 928-3.3600000000000136 928 32 928 103.03999999999996 928 216.96000000000004 928 288ZM480 288C480 323.36 451.36 352 416 352 344.96 352 231.04 352 160 352 124.64 352 96 323.36 96 288 96 216.96000000000004 96 103.03999999999996 96 32 96-3.3600000000000136 124.64-32 160-32 231.04-32 344.96-32 416-32 451.36-32 480-3.3600000000000136 480 32 480 103.03999999999996 480 216.96000000000004 480 288ZM480 736C480 771.36 451.36 800 416 800 344.96 800 231.04 800 160 800 124.64 800 96 771.36 96 736 96 664.96 96 551.04 96 480 96 444.64 124.64 416 160 416 231.04 416 344.96 416 416 416 451.36 416 480 444.64 480 480 480 551.04 480 664.96 480 736ZM928 736C928 771.36 899.36 800 864 800 792.96 800 679.04 800 608 800 572.64 800 544 771.36 544 736 544 664.96 544 551.04 544 480 544 444.64 572.64 416 608 416 679.04 416 792.96 416 864 416 899.36 416 928 444.64 928 480 928 551.04 928 664.96 928 736Z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="wechat" unicode="" d="M692.699238 559.112294c11.619123 0 23.117414-0.831898 34.517504-2.108006C696.19497 701.4500350000001 541.769728 808.7724029999999 365.488742 808.7724029999999 168.405197 808.7724029999999 6.977229 674.464461 6.977229 503.892582c0-98.493235 53.707366-179.306803 143.459123-242.033357l-35.857101-107.840102 125.329408 62.837146c44.84311-8.861798 80.827085-18.002022 125.578138-18.002022 11.250688 0 22.40215 0.561766 33.469645 1.428582-7.001702 23.95351-11.06647 49.054208-11.06647 75.120947C387.891917 432.023757 522.3936 559.112294 692.699238 559.112294zM497.405542 663.593882c30.611456 0 55.425536-24.815206 55.425536-55.427379s-24.814182-55.426355-55.425536-55.426355c-30.613504 0-55.427584 24.815206-55.427584 55.426355S466.794086 663.593882 497.405542 663.593882zM246.567526 551.622656c-30.611456 0-55.427584 24.815206-55.427584 55.426355 0 30.611149 24.81623 55.426355 55.427584 55.426355 30.613504 0 55.428608-24.815206 55.428608-55.426355C301.996134 576.438886 277.18103 551.622656 246.567526 551.622656zM1017.379942 278.544179c0 143.330406-143.423283 260.165325-304.515686 260.165325-170.58089 0-304.924979-116.834918-304.924979-260.165325 0-143.57801 134.34409-260.158157 304.924979-260.158157 35.697459 0 71.712154 9.0112 107.569254 17.99895l98.340659-53.861683-26.969293 89.592525C963.769856 126.10232299999996 1017.379942 197.69077800000002 1017.379942 278.544179zM619.184947 318.72430099999997c-21.799322 0-39.469466 17.673523-39.469466 39.471002 0 21.799526 17.671168 39.468954 39.469466 39.468954s39.469466-17.670451 39.469466-39.468954C658.656563 336.3968 640.983347 318.72430099999997 619.184947 318.72430099999997zM816.270541 316.485325c-21.80137 0-39.47049 17.672499-39.47049 39.468954 0 21.80055 17.670144 39.468954 39.47049 39.468954 21.798298 0 39.469466-17.669427 39.469466-39.468954C855.741133 334.157824 838.068941 316.485325 816.270541 316.485325z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="npm" unicode="" d="M0 896l0-1024 1024 0 0 1024-1024 0zM832 64l-128 0 0 512-192 0 0-512-320 0 0 640 640 0 0-640z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="twitter" unicode="" d="M1023.975952 701.055053c-37.679158-16.679891-78.102822-27.991518-120.592542-33.05279 43.367717 25.931602 76.668147 67.167772 92.285846 116.151393-40.551578-24.050766-85.536119-41.484833-133.394103-50.980091-38.304398 40.800241-92.849688 66.355267-153.272849 66.355267-116.02348 0-210.06225-94.039793-210.06225-210.004945 0-16.430204 1.874697-32.489972 5.438872-47.857984-174.508499 8.810666-329.278445 92.40762-432.870802 219.500203C53.388443 730.1087620000001 43.074539 693.99731 43.074539 655.571139c0-72.855308 32.520671-137.149637 88.880283-174.821631-34.428114 1.119497-99.69049 10.556427-99.69049 26.303063 0-0.876973 0-1.689479 0-2.622734 0-101.723799 77.008908-186.632631 173.045171-205.942419-17.619286-4.811586-33.908274-7.370875-53.090149-7.370875-13.563924 0-25.546839 1.375324-38.419008 3.812839 26.744107-83.476203 104.847953-144.149051 196.757223-145.895835-71.850421-56.297191-162.17152-89.912799-260.573667-89.912799-17.000186 0-33.537837 1.062192-49.973158 2.936889 92.976578-59.547211 203.388247-94.346785 322.041624-94.346785 386.323673 0 597.605703 320.032874 597.605703 597.570911 0 9.123798-0.167822 18.183128-0.602727 27.179013 41.050951 29.616528 76.606749 66.611094 104.785531 108.723214L1023.975952 701.055053z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="csdn" unicode="" d="M906.475693-47.728792c-49.491181-17.325597-152.164608-28.913517-295.537993-28.913517-412.545894 0-635.223462 193.912431-608.80579 450.249611C33.545364 679.03555 363.386627 850.974527 724.595296 850.974527c139.829675 0 222.1301-11.388375 299.383578-30.263258l-24.720008-205.822692c-51.434439 17.35118-171.688267 33.208333-269.12032 33.208333-212.388225 0-392.96493-63.425541-413.440264-263.878946-18.429745-179.328271 108.114346-265.024026 347.120812-265.024026 83.251076 0 205.89944 11.884678 262.580371 29.214369L906.475693-47.728792z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="menu" unicode="" d="M105.931034 595.862069C105.931034 615.363434 121.789652 631.172414 141.3498 631.172414L882.6502 631.172414C902.211443 631.172414 918.068966 615.499395 918.068966 595.862069 918.068966 576.3607039999999 902.210348 560.5517239999999 882.6502 560.5517239999999L141.3498 560.5517239999999C121.788557 560.5517239999999 105.931034 576.224743 105.931034 595.862069ZM105.931034 384C105.931034 403.501365 121.789652 419.310345 141.3498 419.310345L882.6502 419.310345C902.211443 419.310345 918.068966 403.637326 918.068966 384 918.068966 364.49863500000004 902.210348 348.689655 882.6502 348.689655L141.3498 348.689655C121.788557 348.689655 105.931034 364.36267399999997 105.931034 384ZM105.931034 172.13793099999998C105.931034 191.63929599999994 121.789652 207.44827599999996 141.3498 207.44827599999996L882.6502 207.44827599999996C902.211443 207.44827599999996 918.068966 191.775257 918.068966 172.13793099999998 918.068966 152.63656600000002 902.210348 136.827586 882.6502 136.827586L141.3498 136.827586C121.788557 136.827586 105.931034 152.50060499999995 105.931034 172.13793099999998Z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="github" unicode="" d="M0 371.008q0-166.016 95.488-298.496t247.488-185.504q6.016-0.992 10.016-0.992t6.496 1.504 4 3.008 2.016 4.992 0.512 4.992v100.512q-36.992-4-66.016 0.512t-45.504 14.016-28.992 23.488-16.992 25.504-8.992 24-5.504 14.496q-8.992 15.008-27.008 27.488t-27.008 20-2.016 14.496q50.016 26.016 112.992-66.016 34.016-51.008 119.008-30.016 10.016 40.992 40 70.016Q293.984 160 237.984 225.024t-56 158.016q0 87.008 55.008 151.008-22.016 64.992 6.016 136.992 28.992 2.016 64.992-11.488t50.496-23.008 25.504-17.504q56.992 16 128.512 16t129.504-16q12.992 8.992 28.992 19.008t48.992 21.504 60.992 9.504q27.008-71.008 7.008-135.008 56-64 56-151.008 0-92.992-56.992-158.496t-172-85.504q43.008-43.008 43.008-104v-128.992q0-0.992 0.992-3.008 0-6.016 0.512-8.992t4.512-6.016 12-3.008q152.992 52 250.496 185.504t97.504 300.512q0 104-40.512 199.008t-108.992 163.488-163.488 108.992T512.032 883.04 313.024 842.528 149.536 733.536t-108.992-163.488-40.512-199.008z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="qq" unicode="" d="M933.446 252.138c-18.784 108.945-97.673 180.323-97.673 180.323 11.268 98.92-30.056 116.461-30.056 116.461-8.701 306.05-272.335 300.692-277.87 300.546-5.542 0.146-269.211 5.505-277.872-300.548 0 0-41.325-17.54-30.055-116.461 0 0-78.895-71.378-97.676-180.323 0 0-10.034-184.083 90.16-22.544 0 0 22.544-61.344 63.867-116.455 0 0-73.897-25.062-67.621-90.165 0 0-2.518-72.618 157.784-67.625 0 0 112.702 8.757 146.516 56.35h29.795c33.809-47.595 146.514-56.35 146.514-56.35 160.262-4.993 157.781 67.625 157.781 67.625 6.238 65.103-67.62 90.165-67.62 90.165 41.324 55.11 63.862 116.455 63.862 116.455 100.156-161.537 90.164 22.546 90.164 22.546z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="other" unicode="" d="M1063.443927-128zM640.505087 896a414.805401 414.805401 0 0 0 414.805401-414.805401v-22.77363q-17.080222-308.257347-17.080223-372.511517a325.337569 325.337569 0 0 1 73.200953-197.642574A612.447975 612.447975 0 0 1 895.081735-24.705322000000024c-81.334392 0-143.148531-93.534551-178.12232-87.0278-81.334392 14.640191-124.44162 87.0278-148.028594 87.0278a154.535346 154.535346 0 0 1-127.694996-87.841143 139.895155 139.895155 0 0 1-121.188244 64.25417A251.323272 251.323272 0 0 1 163.072203-112.54646500000001a919.078634 919.078634 0 0 1 67.507546 297.683876 904.438443 904.438443 0 0 1-29.280381 226.922954v43.920572A439.205719 439.205719 0 0 0 640.505087 896zM1037.416921 209.53772800000002c94.347895 11.386815 92.721207-125.254964 168.362193-108.174742 52.054011 18.70691 65.880858 147.21525 24.400317 209.029389a244.003177 244.003177 0 0 1-175.682287 116.308181c-54.494043 8.946783-39.853852-219.602859-17.080223-217.162828zM213.499527 209.53772800000002c-91.094519 11.386815-89.467832-122.814932-162.668785-107.361398-54.494043 18.70691-68.32089 147.21525-26.027006 209.029389a244.003177 244.003177 0 0 0 175.682288 116.308181c54.494043 8.133439 39.853852-220.416203 13.013503-217.976172zM457.502704 574.72915m-101.667991 0a101.66799 101.66799 0 1 1 203.335981 0 101.66799 101.66799 0 1 1-203.335981 0ZM782.840273 574.72915m-101.66799 0a101.66799 101.66799 0 1 1 203.335981 0 101.66799 101.66799 0 1 1-203.335981 0Z" horiz-adv-x="1255" />
|
||||
|
||||
|
||||
<glyph glyph-name="home" unicode="" d="M964.835556 454.542222L857.884444 554.666667 580.266667 809.528889c-18.204444 18.204444-45.511111 29.582222-72.817778 29.582222-27.306667 0-54.613333-11.377778-72.817778-29.582222L59.164444 454.542222c-31.857778-31.857778-25.031111-56.888889-22.755555-63.715555 4.551111-11.377778 15.928889-25.031111 43.235555-25.031111h54.613334c13.653333 0 22.755556-11.377778 22.755555-22.755556v-302.648889c0-52.337778 36.408889-109.226667 95.573334-109.226667H766.862222c59.164444 0 95.573333 56.888889 95.573334 109.226667V340.764444c0 13.653333 11.377778 22.755556 22.755555 22.755556h54.613333c27.306667 0 38.684444 13.653333 43.235556 27.306667 6.826667 6.826667 13.653333 29.582222-18.204444 63.715555z m-307.2-386.844444c0-29.582222-22.755556-52.337778-52.337778-52.337778h-184.32c-29.582222 0-52.337778 25.031111-52.337778 52.337778V320.284444c0 29.582222 22.755556 52.337778 52.337778 52.337778h184.32c29.582222 0 52.337778-25.031111 52.337778-52.337778v-252.586666z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="up" unicode="" d="M526.60727968 885.09814884a27.675 27.675 0 0 1-29.21455937 0c-131.36607665-82.28402758-218.69155461-228.01873535-218.69155402-394.07834331a462.20625001 462.20625001 0 0 1 5.36959153-69.94390903c1.00431239-6.55289093-0.34802892-13.13561351-3.76865779-18.80351572-32.63518765-54.11355614-51.75690182-118.55860487-51.7569018-187.94566865a371.06718723 371.06718723 0 0 1 11.50484808-91.98906777c6.53300375-25.50556257 41.68394495-28.14064038 52.69160883-4.22606766 17.37162448 37.73630017 42.14135425 72.50938081 72.80769204 103.21549295 2.18761121-3.04276886 4.15646224-6.24463696 6.40373557-9.22774369a1871.4375 1871.4375 0 0 1 140.04691725-5.34970492 1866.36093723 1866.36093723 0 0 1 140.04691723 5.34970492c2.24727335 2.98310674 4.21612437 6.18497483 6.3937923 9.2178004 30.66633723-30.70611158 55.4360664-65.4791928 72.80769147-103.21549355 11.00766384-23.91457269 46.15860503-21.27949489 52.69160879 4.22606768a371.15156223 371.15156223 0 0 1 11.514792 91.99901164c0 69.36717486-19.13165746 133.82216804-51.75690182 187.92578088-3.42062944 5.66790279-4.76302748 12.26056868-3.76865837 18.80351632a462.20625001 462.20625001 0 0 1 5.36959269 69.943909c-0.00994388 166.08943902-87.32547796 311.81420293-218.6915546 394.09823051zM605.93803103 538.12306142a93.93749974 93.93749974 0 1 0-187.89594924-6.1e-7 93.93749974 93.93749974 0 0 0 187.89594924 6.1e-7zM429.50777625 130.36139452999998C429.50777625 92.60644993000005 466.44236686-104.39046097000005 512.00932183-104.39046097000005c45.56695499 0 82.4922232 197.00623328 82.5015456 234.7518555 0 37.75494459-36.9345906 68.35043303-82.4922232 68.34111062-45.57627738 0.00932239-82.52019037-30.59548842-82.51086798-68.34111062z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="tag" unicode="" d="M814 773.2l-264.6-12.7c-35.1-1.7-68.3-16.4-93.1-41.2L107.7 370.70000000000005c-31.1-31.1-31.1-81.6 0-112.7l277.2-277.2c31.1-31.1 81.6-31.1 112.7 0l14.3 14.3 92.1 92.1 242.2 242.2c24.8 24.8 39.5 58 41.2 93.1l12.7 264.6c2.3 48.5-37.6 88.4-86.1 86.1zM689.6 426c-37.8-37.8-99-37.8-136.8 0s-37.8 99 0 136.8c37.8 37.8 99 37.8 136.8 0 37.8-37.8 37.8-99.1 0-136.8z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="date" unicode="" d="M512 832.1C264.5 832.1 63.9 631.5 63.9 384c0-247.5 200.7-448.2 448.2-448.2S960.2 136.39999999999998 960.2 384c0 247.5-200.6 448.1-448.2 448.1z m179.5-618.2c-7.8-9.5-19.1-14.5-30.6-14.5-8.8 0-17.6 2.9-25 8.9l-155 126.8a39.34 39.34 0 0 0-14.5 30.5V550.8c0 21.8 17.7 39.5 39.5 39.5s39.5-17.7 39.5-39.5v-166.4l140.6-115c16.8-13.8 19.3-38.7 5.5-55.5z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="bokeyuan" unicode="" d="M874.03867197 746.03867197C674.01230571 946.06503823 349.98769429 946.06503823 149.96132803 746.03867197s-200.02636626-524.05097767 0-724.07734394 524.05097767-200.02636626 724.07734394 0S1074.06503823 546.01230571 874.03867197 746.03867197z m-508.66433412-561.15994155c-12.67135352 0-25.34270704 5.43058008-35.29877051 15.38664356-9.95606348 9.95606348-14.48154688 21.72232032-15.38664356 35.29877051 0 12.67135352 5.43058008 25.34270704 14.48154688 36.2038672 9.95606348 9.95606348 21.72232032 14.48154688 36.20386719 14.48154688 12.67135352 0 25.34270704-5.43058008 35.29877052-15.38664356 9.95606348-9.95606348 14.48154688-21.72232032 15.38664356-35.29877052 0-12.67135352-5.43058008-25.34270704-15.38664356-35.29877051-9.95606348-9.95606348-21.72232032-14.48154688-35.29877052-15.38664356z m220.8435899 28.96309376c-9.0509668 0-17.19683692 2.71529004-23.53251367 9.05096679s-9.0509668 14.48154688-9.95606348 24.43761036l0 3.62038672c-1.81019336 48.87522072-18.1019336 88.69947463-50.68541408 121.28295511-32.58348048 32.58348048-72.40773439 48.87522072-120.37785843 49.7803174-9.0509668 0-18.1019336 3.62038672-24.43761036 9.95606348s-9.0509668 14.48154688-9.95606348 24.43761035c0 9.0509668 3.62038672 18.1019336 9.95606348 24.43761036s14.48154688 9.0509668 24.43761036 9.95606348c66.97715431-1.81019336 123.99824515-24.43761036 169.25307915-69.69244435s68.78734767-101.37082815 69.69244435-169.25307915l0-3.62038672c0-9.0509668-4.5254834-17.19683692-10.86116016-23.53251368-7.24077344-7.24077344-14.48154688-10.86116016-23.53251368-10.86116015z m197.31107623 23.53251367c-9.0509668 0-17.19683692 2.71529004-23.53251368 9.0509668s-9.0509668 14.48154688-9.95606348 24.43761036c-1.81019336 99.56063479-37.10896388 181.92443266-103.18102151 247.9964903-66.07205763 66.07205763-149.34095219 100.46573147-247.9964903 103.18102151-9.0509668 0-18.1019336 3.62038672-24.43761036 9.95606348s-9.0509668 14.48154688-9.0509668 23.53251368 3.62038672 18.1019336 9.95606348 24.43761035 14.48154688 9.0509668 23.53251368 10.86116016c118.56766507-2.71529004 217.22320318-43.44464064 295.96661433-122.18805179 78.74341115-78.74341115 119.47276175-177.39894926 122.18805179-295.96661433 0-9.0509668-4.5254834-17.19683692-10.86116016-23.53251368-5.43058008-9.0509668-13.5764502-11.76625684-22.62741699-11.76625684z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="copyright" unicode="" d="M512 880C238.066 880 16 657.934 16 384s222.066-496 496-496 496 222.066 496 496S785.934 880 512 880z m234.268-693.506c-3.184-3.734-79.552-91.462-219.702-91.462-169.384 0-288.968 126.52-288.968 291.134 0 162.606 124.008 286.802 287.524 286.802 133.914 0 203.93-74.63 206.844-77.808a24 24 0 0 0 2.476-29.246l-44.76-69.31c-8.098-12.534-25.548-14.702-36.468-4.59-0.466 0.428-53.058 47.76-123.76 47.76-92.232 0-147.832-67.15-147.832-152.164 0-79.204 51.028-159.384 148.554-159.384 77.394 0 130.56 56.676 131.088 57.25 10.264 11.13 28.118 10.066 37.016-2.106l49.094-67.144a24.002 24.002 0 0 0-1.106-29.732z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="account" unicode="" d="M723.4304 387.39968c-54.12352-47.74912-125.9776-77.056-205.16352-77.056-80.36352 0-153.40032 30.2592-207.76448 79.42144-126.45376-33.09056-204.69248-146.0736-204.69248-281.04192v-68.78208c0-160.96768 812.3904-160.96768 812.3904 0v68.78208c-0.00512 131.41504-74.21952 242.5088-194.76992 278.67648z m-205.16352-28.12928c140.16512 0 254.09536 109.44 254.09536 244.63872S658.66752 848.78336 518.26688 848.78336c-139.9296 0-253.85472-109.67552-253.85472-244.87424 0-135.20384 113.92512-244.63872 253.85472-244.63872z m0 0" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="jianshu" unicode="" d="M0 384v-512h1024V896H0v-512z m356.864 331.776c-1.024-6.656-4.096-15.872-6.144-20.48-3.584-8.704 2.048-9.216 78.848-9.216H512v-51.2h-43.52c-24.064 0-43.52-0.512-43.52-1.024 0-1.024 4.608-16.384 10.24-34.816 5.632-18.432 10.24-33.792 10.24-34.304 0-1.024-18.432-1.536-40.448-1.536h-40.448l-3.584 16.896c-9.216 45.056-13.824 54.784-27.136 54.784-7.68 0-21.504-8.192-35.328-20.992-22.016-19.968-71.168-46.592-99.84-53.76-14.336-3.072-14.336-3.072-14.336 22.528 0 23.552 1.024 26.112 19.968 38.4 25.088 16.384 41.984 38.912 59.392 76.8l13.312 29.184h41.472c39.936 0 40.96-0.512 38.4-11.264z m306.688-6.656l-3.584-17.92h179.712v-56.32h-96.768l10.24-29.184c14.848-43.008 15.36-42.496-28.16-42.496h-37.888l-3.584 16.896c-2.048 8.704-6.144 25.088-8.704 35.84-5.12 17.92-7.168 18.944-25.6 18.944-16.384 0-23.552-3.584-41.472-20.992-19.456-18.944-76.288-50.176-104.448-58.368-10.752-3.072-11.776-1.536-11.776 18.944 0 20.992 2.048 24.576 27.648 46.592 25.6 22.016 49.152 59.392 61.44 97.28 2.048 7.168 9.216 8.704 44.544 8.704h41.472l-3.072-17.92z m-317.44-162.304c4.608-8.704 13.824-32.256 20.48-51.2 13.824-41.472 14.336-40.96-36.352-38.912l-34.816 1.536-11.264 35.84c-6.144 19.968-14.848 43.52-19.968 52.736L255.488 563.2h40.96c40.96 0 41.472 0 49.664-16.384z m462.848-225.792c0-200.192-0.512-217.6-9.216-233.984-15.872-28.672-40.448-37.888-103.424-38.4h-53.76l-1.536 29.696-1.536 29.184h33.792c29.184 0 35.328 1.536 46.08 12.8l12.8 12.288V476.16H384V537.6h424.96v-216.576zM291.84 243.2v-202.24h-76.8V445.44h76.8V243.2z m363.52 57.344c0-182.784 4.608-177.664-167.424-177.664H373.76V424.96h281.6v-124.416zM450.56 335.36v-33.28h128V368.64h-128v-33.28zM450.56 215.04v-35.84h55.808c65.024 0 69.12 2.56 69.632 43.008v26.112l-62.464 1.536-62.976 1.536v-36.352z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="theme" unicode="" d="M978.77333333 545.49333333s-0.10666667 0-0.10666666 0.10666667L771.30666667 752.42666667c-16.64 26.98666667-46.29333333 45.22666667-80.42666667 45.22666666h-59.2c-17.28 0-31.68-12.37333333-34.88-28.8l-0.64-0.96v1.28c-0.85333333-2.13333333-2.13333333-4.16-3.62666667-6.29333333-10.02666667-34.45333333-41.49333333-59.73333333-79.25333333-59.73333333-33.06666667 0-61.44 19.52-74.77333333 47.46666666l-0.42666667 0.32c-0.10666667 0.42666667-0.32 0.85333333-0.42666667 1.28-0.96 2.13333333-1.6 4.37333333-2.45333333 6.61333334-1.49333333 3.84-3.2 7.57333333-4.90666667 11.30666666v-5.65333333h-0.21333333c-0.21333333 3.84-1.06666667 7.57333333-2.34666667 10.98666667-0.74666667 1.6-1.6 3.2-2.45333333 4.8-6.18666667 10.45333333-17.38666667 17.49333333-30.4 17.49333333-2.13333333 0-4.16-0.32-6.29333333-0.74666667-1.06666667 0.53333333-2.24 0.74666667-3.41333334 0.74666667-0.74666667 0-1.49333333-0.21333333-2.24-0.42666667v0.42666667h-47.36c-30.82666667 0-57.92-14.93333333-75.30666666-37.65333333l-214.82666667-214.4s-0.10666667 0-0.10666667-0.10666667c-13.86666667-13.86666667-13.86666667-36.26666667 0-50.13333333l150.72-150.4c12.26666667-12.26666667 30.82666667-13.01333333 44.69333334-3.62666667v-347.30666667h2.34666666c4.90666667-13.76 17.70666667-23.68 33.17333334-23.68h473.81333333c15.46666667 0 28.26666667 9.92 33.17333333 23.68h2.34666667v345.6c13.54666667-7.68 30.82666667-6.29333333 42.34666667 5.22666667l150.72 150.4c13.97333333 13.86666667 13.97333333 36.26666667 0.10666666 50.13333333z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="three" unicode="" d="M861.798876 266.798732C835.998959 320.998557 830.598976 331.29852400000004 806.999052 385.398351c-12.59996 28.799907-38.399877 46.79985-61.899801 52.69983l62.499799 294.499054c9.29997 43.899859-18.299941 86.99972-61.699801 96.39969-43.399861 9.39997-86.099723-18.59994-95.399694-62.499799l-58.099813-273.79912-3.699988 322.598963C587.999756 860.296825 551.599873 896.396709 507.200015 895.99671c-44.499857-0.299999-79.999743-36.999881-79.499744-81.799737l3.499989-304.299022-66.199788 248.999199c-11.499963 43.299861-55.599821 68.999778-98.499683 57.399816-42.899862-11.599963-68.299781-56.199819-56.799818-99.49968L295.000697 395.898317c-62.899798-21.69993-92.899702-43.899859-103.399668-72.899766l-37.999878-108.399652c-8.499973-23.399925 3.19999-75.299758 11.499964-98.799682 39.699872-112.799638 86.399722-187.699397 114.499632-226.699272 4.899984-6.799978 9.099971-12.29996 12.799959-16.899946H760.9992c1.699995 3.09999 3.19999 6.29998 4.499986 9.699969l94.999694 256.299177c22.099929 52.099833 10.799965 108.499651 1.299996 128.599587z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
</defs></svg>
|
After Width: | Height: | Size: 25 KiB |
BIN
fonts/iconfont.ttf
Normal file
BIN
fonts/iconfont.ttf
Normal file
Binary file not shown.
BIN
fonts/iconfont.woff
Normal file
BIN
fonts/iconfont.woff
Normal file
Binary file not shown.
BIN
fonts/iconfont.woff2
Normal file
BIN
fonts/iconfont.woff2
Normal file
Binary file not shown.
44
global-components/Badge.vue
Normal file
44
global-components/Badge.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script>
|
||||
export default {
|
||||
functional: true,
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'tip'
|
||||
},
|
||||
text: String,
|
||||
vertical: {
|
||||
type: String,
|
||||
default: 'top'
|
||||
}
|
||||
},
|
||||
render (h, { props, slots }) {
|
||||
return h('span', {
|
||||
class: ['badge', props.type],
|
||||
style: {
|
||||
verticalAlign: props.vertical
|
||||
}
|
||||
}, props.text || slots().default)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.badge
|
||||
display inline-block
|
||||
font-size 14px
|
||||
height 18px
|
||||
line-height 18px
|
||||
border-radius 3px
|
||||
padding 0 6px
|
||||
color white
|
||||
background-color #42b983
|
||||
&.tip, &.green
|
||||
background-color #42b983
|
||||
&.error
|
||||
background-color #DA5961 //#f66
|
||||
&.warning, &.warn, &.yellow
|
||||
background-color darken(#ffe564, 35%)
|
||||
& + &
|
||||
margin-left 5px
|
||||
</style>
|
46
index.js
Normal file
46
index.js
Normal file
@ -0,0 +1,46 @@
|
||||
const path = require('path')
|
||||
|
||||
// Theme API.
|
||||
module.exports = (options, ctx) => ({
|
||||
alias () {
|
||||
const { themeConfig, siteConfig } = ctx
|
||||
// resolve algolia
|
||||
const isAlgoliaSearch = (
|
||||
themeConfig.algolia
|
||||
|| Object.keys(siteConfig.locales && themeConfig.locales || {})
|
||||
.some(base => themeConfig.locales[base].algolia)
|
||||
)
|
||||
return {
|
||||
'@AlgoliaSearchBox': isAlgoliaSearch
|
||||
? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue')
|
||||
: path.resolve(__dirname, 'noopModule.js')
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [
|
||||
'@vuepress/active-header-links',
|
||||
['@vuepress/plugin-blog', {
|
||||
permalink: '/:regular'
|
||||
}],
|
||||
'@vuepress/search',
|
||||
'@vuepress/plugin-nprogress',
|
||||
['container', {
|
||||
type: 'tip',
|
||||
defaultTitle: {
|
||||
'/zh/': '提示'
|
||||
}
|
||||
}],
|
||||
['container', {
|
||||
type: 'warning',
|
||||
defaultTitle: {
|
||||
'/zh/': '注意'
|
||||
}
|
||||
}],
|
||||
['container', {
|
||||
type: 'danger',
|
||||
defaultTitle: {
|
||||
'/zh/': '警告'
|
||||
}
|
||||
}]
|
||||
]
|
||||
})
|
28
layouts/404.vue
Normal file
28
layouts/404.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="theme-container">
|
||||
<div class="content">
|
||||
<h1>404</h1>
|
||||
<blockquote>{{ getMsg() }}</blockquote>
|
||||
<router-link to="/">Take me home.</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const msgs = [
|
||||
`There's nothing here.`,
|
||||
`How did we get here?`,
|
||||
`That's a Four-Oh-Four.`,
|
||||
`Looks like we've got some broken links.`
|
||||
]
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
getMsg () {
|
||||
return msgs[Math.floor(Math.random() * msgs.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="../styles/theme.styl" lang="stylus"></style>
|
93
layouts/Category.vue
Normal file
93
layouts/Category.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="categories-wrapper">
|
||||
<!-- 公共布局 -->
|
||||
<Common :sidebar="false" :isComment="false">
|
||||
<!-- 页面标题 -->
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
|
||||
<!-- 博客列表 -->
|
||||
<note-abstract
|
||||
:data="posts"
|
||||
:currentPage="currentPage"
|
||||
@currentTag="getCurrentTag"></note-abstract>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagation
|
||||
:data="posts"
|
||||
:currentPage="currentPage"
|
||||
@getCurrentPage="getCurrentPage"></pagation>
|
||||
</Common>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Common from '@theme/components/Common.vue'
|
||||
import NoteAbstract from '../components//NoteAbstract.vue'
|
||||
import Pagation from '../components//Pagation.vue'
|
||||
|
||||
export default {
|
||||
components: { Common, NoteAbstract, Pagation },
|
||||
|
||||
data () {
|
||||
return {
|
||||
// 当前页码
|
||||
currentPage: 1
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
// 时间降序后的博客列表
|
||||
posts () {
|
||||
let posts = this.$category.posts
|
||||
posts.sort((a, b) => {
|
||||
return this._getTimeNum(b) - this._getTimeNum(a)
|
||||
})
|
||||
return posts
|
||||
},
|
||||
// 标题只显示分类名称
|
||||
title () {
|
||||
return this.$page.frontmatter.title.split('|')[0]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 获取当前tag
|
||||
getCurrentTag (tag) {
|
||||
this.$emit('currentTag', tag)
|
||||
},
|
||||
// 获取当前页码
|
||||
getCurrentPage (page) {
|
||||
this.currentPage = page
|
||||
this.$page.currentPage = page
|
||||
},
|
||||
// 获取时间的数字类型
|
||||
_getTimeNum (date) {
|
||||
return parseInt(new Date(date.frontmatter.date).getTime())
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="../styles/theme.styl" lang="stylus"></style>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.categories-wrapper
|
||||
max-width: 740px;
|
||||
margin: 0 auto;
|
||||
padding: 4.6rem 2.5rem 0;
|
||||
.title
|
||||
margin-bottom 3rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.categories-wrapper
|
||||
padding: 4.6rem 1rem 0;
|
||||
.page-edit
|
||||
.edit-link
|
||||
margin-bottom .5rem
|
||||
.last-updated
|
||||
font-size .8em
|
||||
float none
|
||||
text-align left
|
||||
</style>
|
||||
|
||||
|
42
layouts/Layout.vue
Normal file
42
layouts/Layout.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<Common>
|
||||
<Home v-if="$page.frontmatter.home"/>
|
||||
<Page
|
||||
v-else
|
||||
:sidebar-items="sidebarItems">
|
||||
<slot
|
||||
name="page-top"
|
||||
slot="top"/>
|
||||
<slot
|
||||
name="page-bottom"
|
||||
slot="bottom"/>
|
||||
</Page>
|
||||
</Common>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Home from '@theme/components/Home.vue'
|
||||
import Page from '@theme/components/Page.vue'
|
||||
import Common from '@theme/components/Common.vue'
|
||||
import { resolveSidebarItems } from '../util'
|
||||
|
||||
export default {
|
||||
components: { Home, Page, Common },
|
||||
|
||||
computed: {
|
||||
sidebarItems () {
|
||||
return resolveSidebarItems(
|
||||
this.$page,
|
||||
this.$page.regularPath,
|
||||
this.$site,
|
||||
this.$localePath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
||||
<style src="../styles/theme.styl" lang="stylus"></style>
|
129
layouts/Tags.vue
Normal file
129
layouts/Tags.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="tags-wrapper">
|
||||
<Common :sidebar="false" :isComment="false"></Common>
|
||||
<div class="tags">
|
||||
<span
|
||||
v-for="(item, index) in tags"
|
||||
:key="index"
|
||||
:class="{'active': item.name == currentTag}"
|
||||
:style="{ 'backgroundColor': item.color }"
|
||||
@click="getPagesByTags(item.name)">{{item.name}}</span>
|
||||
</div>
|
||||
<note-abstract
|
||||
:data="posts"
|
||||
:currentPage="currentPage"
|
||||
:currentTag="currentTag"
|
||||
@currentTag="getCurrentTag"></note-abstract>
|
||||
|
||||
<pagation
|
||||
:data="posts"
|
||||
:currentPage="currentPage"
|
||||
@getCurrentPage="getCurrentPage"></pagation>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Common from '@theme/components/Common.vue'
|
||||
import NoteAbstract from '../components//NoteAbstract.vue'
|
||||
import Pagation from '../components//Pagation.vue'
|
||||
|
||||
export default {
|
||||
components: { Common, NoteAbstract, Pagation },
|
||||
|
||||
data () {
|
||||
return {
|
||||
posts: [],
|
||||
tags: [],
|
||||
currentTag: '',
|
||||
currentPage: 1
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (this.$tags.list.length > 0) {
|
||||
const currentTag = this.$route.query.tag ? this.$route.query.tag : this.$tags.list[0].name
|
||||
let tags = this.$tags.list
|
||||
tags.map(item => {
|
||||
const color = this._tagColor()
|
||||
item.color = color
|
||||
return tags
|
||||
})
|
||||
this.tags = tags
|
||||
|
||||
this.getPagesByTags(currentTag)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 根据分类获取页面数据
|
||||
getPagesByTags (currentTag) {
|
||||
|
||||
this.currentTag = currentTag
|
||||
|
||||
let posts = this.$tags.map[currentTag].posts
|
||||
posts.sort((a, b) => {
|
||||
return this._getTimeNum(b) - this._getTimeNum(a)
|
||||
})
|
||||
// reverse()是为了按时间最近排序排序
|
||||
this.posts = posts.length == 0 ? [] : posts
|
||||
|
||||
this.getCurrentPage(1);
|
||||
},
|
||||
|
||||
getCurrentTag (tag) {
|
||||
this.$emit('currentTag', tag)
|
||||
},
|
||||
|
||||
getCurrentPage (page) {
|
||||
this.currentPage = page
|
||||
this.$page.currentPage = page
|
||||
},
|
||||
|
||||
_tagColor () {
|
||||
// 红、蓝、绿、橙、灰
|
||||
const tagColorArr = ['#f26d6d', '#3498db', '#67cc86', '#fb9b5f', '#838282']
|
||||
const index = Math.floor(Math.random() * tagColorArr.length)
|
||||
return tagColorArr[index]
|
||||
},
|
||||
|
||||
// 获取时间的数字类型
|
||||
_getTimeNum (date) {
|
||||
return parseInt(new Date(date.frontmatter.date).getTime())
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="../styles/theme.styl" lang="stylus"></style>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.tags-wrapper
|
||||
max-width: 740px;
|
||||
margin: 0 auto;
|
||||
padding: 4.6rem 2.5rem 0;
|
||||
.tags
|
||||
margin-bottom 30px
|
||||
span
|
||||
vertical-align: middle;
|
||||
margin: 4px 4px 10px;
|
||||
padding: 4px 8px;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
background: #fff;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
box-shadow 0 1px 4px 0 rgba(0,0,0,0.2)
|
||||
transition: all .5s
|
||||
&:hover
|
||||
transform scale(1.04)
|
||||
&.active
|
||||
transform scale(1.2)
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.tags-wrapper
|
||||
padding: 0 0.6rem;
|
||||
</style>
|
||||
|
||||
|
1
noopModule.js
Normal file
1
noopModule.js
Normal file
@ -0,0 +1 @@
|
||||
export default {}
|
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "vuepress-theme-reco",
|
||||
"version": "1.0.0-alpha.11",
|
||||
"description": "this is a vuepress theme",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/recoluan/vuepress-theme-reco.git"
|
||||
},
|
||||
"keywords": [
|
||||
"vuepress",
|
||||
"vue",
|
||||
"theme"
|
||||
],
|
||||
"author": "reco_luan",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/recoluan/vuepress-theme-reco/issues"
|
||||
},
|
||||
"homepage": "https://recoluan.gitlab.io/vuepress-theme-reco-doc/",
|
||||
"_from": "vuepress-theme-reco@0.2.1",
|
||||
"_resolved": "http://registry.npm.taobao.org/vuepress-theme-reco/download/vuepress-theme-reco-0.2.1.tgz",
|
||||
"dependencies": {
|
||||
"leancloud-storage": "^3.10.1",
|
||||
"valine": "^1.3.4",
|
||||
"@vuepress/plugin-blog": "^1.0.0-alpha.46",
|
||||
"vue-click-outside": "^1.0.7"
|
||||
}
|
||||
}
|
22
styles/arrow.styl
Normal file
22
styles/arrow.styl
Normal file
@ -0,0 +1,22 @@
|
||||
@require './config'
|
||||
|
||||
.arrow
|
||||
display inline-block
|
||||
width 0
|
||||
height 0
|
||||
&.up
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-bottom 6px solid $arrowBgColor
|
||||
&.down
|
||||
border-left 4px solid transparent
|
||||
border-right 4px solid transparent
|
||||
border-top 6px solid $arrowBgColor
|
||||
&.right
|
||||
border-top 4px solid transparent
|
||||
border-bottom 4px solid transparent
|
||||
border-left 6px solid $arrowBgColor
|
||||
&.left
|
||||
border-top 4px solid transparent
|
||||
border-bottom 4px solid transparent
|
||||
border-right 6px solid $arrowBgColor
|
135
styles/code.styl
Normal file
135
styles/code.styl
Normal file
@ -0,0 +1,135 @@
|
||||
.content
|
||||
code
|
||||
color lighten($textColor, 20%)
|
||||
padding 0.25rem 0.5rem
|
||||
margin 0
|
||||
font-size 0.85em
|
||||
background-color rgba(27,31,35,0.05)
|
||||
border-radius 3px
|
||||
.token
|
||||
&.deleted
|
||||
color #EC5975
|
||||
&.inserted
|
||||
color $accentColor
|
||||
|
||||
.content
|
||||
pre, pre[class*="language-"]
|
||||
line-height 1.4
|
||||
padding 1.25rem 1.5rem
|
||||
margin 0.85rem 0
|
||||
background-color $codeBgColor
|
||||
border-radius 6px
|
||||
overflow auto
|
||||
code
|
||||
color #fff
|
||||
padding 0
|
||||
background-color transparent
|
||||
border-radius 0
|
||||
|
||||
div[class*="language-"]
|
||||
position relative
|
||||
background-color $codeBgColor
|
||||
border-radius 6px
|
||||
.highlight-lines
|
||||
user-select none
|
||||
padding-top 1.3rem
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
line-height 1.4
|
||||
.highlighted
|
||||
background-color rgba(0, 0, 0, 66%)
|
||||
pre, pre[class*="language-"]
|
||||
background transparent
|
||||
position relative
|
||||
z-index 1
|
||||
&::before
|
||||
position absolute
|
||||
z-index 3
|
||||
top 0.8em
|
||||
right 1em
|
||||
font-size 0.75rem
|
||||
color rgba(255, 255, 255, 0.4)
|
||||
&:not(.line-numbers-mode)
|
||||
.line-numbers-wrapper
|
||||
display none
|
||||
&.line-numbers-mode
|
||||
.highlight-lines .highlighted
|
||||
position relative
|
||||
&:before
|
||||
content ' '
|
||||
position absolute
|
||||
z-index 3
|
||||
left 0
|
||||
top 0
|
||||
display block
|
||||
width $lineNumbersWrapperWidth
|
||||
height 100%
|
||||
background-color rgba(0, 0, 0, 66%)
|
||||
pre
|
||||
padding-left $lineNumbersWrapperWidth + 1 rem
|
||||
vertical-align middle
|
||||
.line-numbers-wrapper
|
||||
position absolute
|
||||
top 0
|
||||
width $lineNumbersWrapperWidth
|
||||
text-align center
|
||||
color rgba(255, 255, 255, 0.3)
|
||||
padding 1.25rem 0
|
||||
line-height 1.4
|
||||
br
|
||||
user-select none
|
||||
.line-number
|
||||
position relative
|
||||
z-index 4
|
||||
user-select none
|
||||
font-size 0.85em
|
||||
&::after
|
||||
content ''
|
||||
position absolute
|
||||
z-index 2
|
||||
top 0
|
||||
left 0
|
||||
width $lineNumbersWrapperWidth
|
||||
height 100%
|
||||
border-radius 6px 0 0 6px
|
||||
border-right 1px solid rgba(0, 0, 0, 66%)
|
||||
background-color $codeBgColor
|
||||
|
||||
|
||||
for lang in $codeLang
|
||||
div{'[class~="language-' + lang + '"]'}
|
||||
&:before
|
||||
content ('' + lang)
|
||||
|
||||
div[class~="language-javascript"]
|
||||
&:before
|
||||
content "js"
|
||||
|
||||
div[class~="language-typescript"]
|
||||
&:before
|
||||
content "ts"
|
||||
|
||||
div[class~="language-markup"]
|
||||
&:before
|
||||
content "html"
|
||||
|
||||
div[class~="language-markdown"]
|
||||
&:before
|
||||
content "md"
|
||||
|
||||
div[class~="language-json"]:before
|
||||
content "json"
|
||||
|
||||
div[class~="language-ruby"]:before
|
||||
content "rb"
|
||||
|
||||
div[class~="language-python"]:before
|
||||
content "py"
|
||||
|
||||
div[class~="language-bash"]:before
|
||||
content "sh"
|
||||
|
||||
div[class~="language-php"]:before
|
||||
content "php"
|
55
styles/colorMode/colorMixin.styl
Normal file
55
styles/colorMode/colorMixin.styl
Normal file
@ -0,0 +1,55 @@
|
||||
color-mode(accountColor , colorName)
|
||||
.reco-theme-{colorName}
|
||||
input
|
||||
color accountColor!important;
|
||||
.search-box input
|
||||
border-color: accountColor!important;
|
||||
&:focus
|
||||
border-color: accountColor!important;
|
||||
.navbar
|
||||
box-shadow: 0 1px 6px 0 rgba(32,33,36,.28)
|
||||
.nav-links a:hover,
|
||||
.nav-links a.router-link-active,
|
||||
.nav-links a:hover .iconfont,
|
||||
.nav-links a.router-link-active .iconfont
|
||||
color accountColor!important;
|
||||
.home
|
||||
.hero
|
||||
.description
|
||||
color accountColor!important;
|
||||
.action-button, img
|
||||
background-color accountColor!important;
|
||||
color: #fff!important
|
||||
.features
|
||||
.feature
|
||||
h2, p
|
||||
color accountColor!important;
|
||||
.footer
|
||||
a
|
||||
color accountColor!important;
|
||||
.abstract-wrapper .abstract-item .title
|
||||
a
|
||||
color accountColor!important;
|
||||
a:after
|
||||
background-color accountColor!important;
|
||||
.back-to-ceiling i
|
||||
color accountColor!important;
|
||||
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
|
||||
border-left: 5px solid accountColor!important;
|
||||
.tags
|
||||
.tag-item.active, .tag-item:hover
|
||||
color accountColor!important;
|
||||
.timeline-wrapper .year-wrapper li:hover
|
||||
.title
|
||||
color accountColor!important;
|
||||
.date::before
|
||||
background: accountColor!important;
|
||||
.date
|
||||
color accountColor!important;
|
||||
@media (min-width: 765px)
|
||||
.nav-item > a:not(.external):hover,
|
||||
.nav-item > a:not(.external).router-link-active
|
||||
border-color: accountColor!important;
|
||||
@media (max-width: 959px)
|
||||
.search-box input
|
||||
border-color: transparent!important;
|
17
styles/colorMode/colorMode.styl
Normal file
17
styles/colorMode/colorMode.styl
Normal file
@ -0,0 +1,17 @@
|
||||
@require './colorMixin.styl'
|
||||
|
||||
$fc-red = #f26d6d
|
||||
$fc-red-name = 'red'
|
||||
color-mode($fc-red, $fc-red-name)
|
||||
|
||||
$fc-blue = #2196f3
|
||||
$fc-blue-name = 'blue'
|
||||
color-mode($fc-blue, $fc-blue-name)
|
||||
|
||||
$fc-green = #3eaf7c
|
||||
$fc-green-name = 'green'
|
||||
color-mode($fc-green, $fc-green-name)
|
||||
|
||||
$fc-orange = #fb9b5f
|
||||
$fc-orange-name = 'orange'
|
||||
color-mode($fc-orange, $fc-orange-name)
|
2
styles/colorMode/index.styl
Normal file
2
styles/colorMode/index.styl
Normal file
@ -0,0 +1,2 @@
|
||||
@require './nightMode.styl'
|
||||
@require './colorMode.styl'
|
114
styles/colorMode/nightMode.styl
Normal file
114
styles/colorMode/nightMode.styl
Normal file
@ -0,0 +1,114 @@
|
||||
$bc = #000
|
||||
$fc = #9e9e9e
|
||||
$bdc = #9e9e9e
|
||||
$fcActive = #fff
|
||||
|
||||
|
||||
bc() {
|
||||
background-color: $bc!important
|
||||
}
|
||||
fc() {
|
||||
color: $fc!important
|
||||
}
|
||||
bdc() {
|
||||
border-color: $bdc!important
|
||||
}
|
||||
fc-active() {
|
||||
color: $fcActive!important
|
||||
}
|
||||
|
||||
.reco-theme-night
|
||||
bc()
|
||||
fc()
|
||||
bdc()
|
||||
h1, h2, h3, h4, h5, h6, a, hr
|
||||
bdc()
|
||||
fc()
|
||||
input
|
||||
bc()
|
||||
fc()
|
||||
.search-box input
|
||||
bdc()
|
||||
&:focus
|
||||
bdc()
|
||||
.navbar
|
||||
bc()
|
||||
box-shadow: 0 2px 20px 0 rgba(255,255,255,0.4);
|
||||
border-bottom: 1px solid $bdc
|
||||
.site-name, .links
|
||||
bc()
|
||||
fc()
|
||||
.nav-links a:hover,
|
||||
.nav-links a.router-link-active,
|
||||
.nav-links a:hover .iconfont,
|
||||
.nav-links a.router-link-active .iconfont
|
||||
fc-active()
|
||||
.tags .tag-item:hover
|
||||
fc-active()
|
||||
@media (min-width: 765px)
|
||||
.nav-item > a:not(.external):hover,
|
||||
.nav-item > a:not(.external).router-link-active
|
||||
border-color: $fcActive;
|
||||
.dropdown-wrapper .nav-dropdown
|
||||
bc()
|
||||
box-shadow: 0 2px 20px 0 rgba(255,255,255,0.4);
|
||||
border: 1px solid $bdc
|
||||
@media (min-width: 719px)
|
||||
.nav-item > a:not(.external):hover,
|
||||
.nav-item > a:not(.external).router-link-active
|
||||
border-bottom: 2px solid $fcActive!important;
|
||||
@media (max-width: 959px)
|
||||
.search-box input
|
||||
border-color: transparent!important;
|
||||
.home
|
||||
.hero
|
||||
.description
|
||||
fc()
|
||||
.features
|
||||
bdc()
|
||||
.feature
|
||||
h2, p
|
||||
fc()
|
||||
.footer
|
||||
bdc()
|
||||
a
|
||||
fc()
|
||||
.abstract-wrapper .abstract-item
|
||||
background-color: lighten($bc, 20%)
|
||||
.custom-block.tip, .custom-block.warning, .custom-block.danger
|
||||
background-color: lighten($bc, 15%)
|
||||
.pagation .pagation-list
|
||||
bc()
|
||||
.sidebar
|
||||
bc()
|
||||
bdc()
|
||||
.valine-wrapper
|
||||
bc()
|
||||
|
||||
.v .vbtn, .v .vwrap
|
||||
bc()
|
||||
bdc()
|
||||
.v .vlist .vcard
|
||||
.vhead .vsys, .vh
|
||||
bdc()
|
||||
bc()
|
||||
.timeline-wrapper
|
||||
&::after
|
||||
background: $fc
|
||||
.year-wrapper li
|
||||
bdc()
|
||||
.back-to-ceiling
|
||||
background-color: lighten($bc, 15%)!important
|
||||
i
|
||||
fc()
|
||||
.color-picker .color-button .iconfont
|
||||
fc()
|
||||
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
|
||||
border-left: 5px solid $fcActive
|
||||
.timeline-wrapper .year-wrapper li:hover
|
||||
.title
|
||||
fc()
|
||||
.date::before
|
||||
background: $fcActive
|
||||
.date
|
||||
fc()
|
30
styles/custom-blocks.styl
Normal file
30
styles/custom-blocks.styl
Normal file
@ -0,0 +1,30 @@
|
||||
.custom-block
|
||||
.custom-block-title
|
||||
font-weight 600
|
||||
margin-bottom -0.4rem
|
||||
&.tip, &.warning, &.danger
|
||||
padding .1rem 1.5rem
|
||||
border-left-width .5rem
|
||||
border-left-style solid
|
||||
margin 1rem 0
|
||||
&.tip
|
||||
background-color #f3f5f7
|
||||
border-color #67cc86
|
||||
&.warning
|
||||
background-color #fbf0ea
|
||||
border-color #fb9b5f
|
||||
// color darken(#ffe564, 70%)
|
||||
.custom-block-title
|
||||
color darken(#ffe564, 50%)
|
||||
a
|
||||
color $textColor
|
||||
&.danger
|
||||
background-color #fceaea
|
||||
border-color #f26d6d
|
||||
// color darken(red, 70%)
|
||||
.custom-block-title
|
||||
color darken(red, 40%)
|
||||
a
|
||||
color $textColor
|
||||
|
||||
|
11
styles/loadMixin.styl
Normal file
11
styles/loadMixin.styl
Normal file
@ -0,0 +1,11 @@
|
||||
load-start(){
|
||||
transition:all .25s;
|
||||
transform:translateY(-20px);
|
||||
opacity 0
|
||||
}
|
||||
|
||||
load-end($delayTime){
|
||||
transform:translateY(0);
|
||||
transition-delay: $delayTime
|
||||
opacity 1
|
||||
}
|
37
styles/mobile.styl
Normal file
37
styles/mobile.styl
Normal file
@ -0,0 +1,37 @@
|
||||
// @require './config'
|
||||
|
||||
$mobileSidebarWidth = $sidebarWidth * 0.82
|
||||
|
||||
// narrow desktop / iPad
|
||||
@media (max-width: $MQNarrow)
|
||||
.sidebar
|
||||
font-size 15px
|
||||
width $mobileSidebarWidth
|
||||
.page
|
||||
padding-left $mobileSidebarWidth
|
||||
|
||||
// wide mobile
|
||||
@media (max-width: $MQMobile)
|
||||
.sidebar
|
||||
top 0
|
||||
padding-top $navbarHeight
|
||||
transform translateX(-100%)
|
||||
transition transform .2s ease
|
||||
.page
|
||||
padding-left 0
|
||||
.theme-container
|
||||
&.sidebar-open
|
||||
.sidebar
|
||||
transform translateX(0)
|
||||
&.no-navbar
|
||||
.sidebar
|
||||
padding-top: 0
|
||||
|
||||
// narrow mobile
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
h1
|
||||
font-size 1.9rem
|
||||
.content
|
||||
div[class*="language-"]
|
||||
margin 0.85rem -1.5rem
|
||||
border-radius 0
|
3
styles/recoConfig.styl
Normal file
3
styles/recoConfig.styl
Normal file
@ -0,0 +1,3 @@
|
||||
// colors
|
||||
$accentColor = #424242
|
||||
$textColor = #232321
|
215
styles/theme.styl
Normal file
215
styles/theme.styl
Normal file
@ -0,0 +1,215 @@
|
||||
@require './recoConfig'
|
||||
@require './code'
|
||||
@require './custom-blocks'
|
||||
@require './arrow'
|
||||
@require './wrapper'
|
||||
@require './toc'
|
||||
@require './colorMode/index'
|
||||
@require '../fonts/iconfont.css'
|
||||
|
||||
html, body
|
||||
padding 0
|
||||
margin 0
|
||||
body
|
||||
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
|
||||
-webkit-font-smoothing antialiased
|
||||
-moz-osx-font-smoothing grayscale
|
||||
font-size 16px
|
||||
color $textColor
|
||||
background-color #fff
|
||||
|
||||
.page
|
||||
padding-left $sidebarWidth
|
||||
|
||||
.navbar
|
||||
position fixed
|
||||
z-index 20
|
||||
top 0
|
||||
left 0
|
||||
right 0
|
||||
height $navbarHeight
|
||||
background-color #fff
|
||||
box-sizing border-box
|
||||
|
||||
.sidebar-mask
|
||||
position fixed
|
||||
z-index 9
|
||||
top 0
|
||||
left 0
|
||||
width 100vw
|
||||
height 100vh
|
||||
display none
|
||||
background-color: rgba(0,0,0,.65);
|
||||
|
||||
.sidebar
|
||||
font-size 16px
|
||||
background-color #fff
|
||||
width $sidebarWidth
|
||||
position fixed
|
||||
z-index 10
|
||||
margin 0
|
||||
top $navbarHeight
|
||||
left 0
|
||||
bottom 0
|
||||
box-sizing border-box
|
||||
border-right 1px solid $borderColor
|
||||
overflow-y auto
|
||||
|
||||
.content:not(.custom)
|
||||
@extend $wrapper
|
||||
// > *:first-child
|
||||
// margin-top 1.6rem
|
||||
a:hover
|
||||
text-decoration underline
|
||||
p.demo
|
||||
padding 1rem 1.5rem
|
||||
border 1px solid #ddd
|
||||
border-radius 4px
|
||||
img
|
||||
max-width 100%
|
||||
|
||||
.content.custom
|
||||
padding 0
|
||||
margin 0
|
||||
img
|
||||
max-width 100%
|
||||
|
||||
a
|
||||
font-weight 500
|
||||
color $accentColor
|
||||
text-decoration none
|
||||
|
||||
p a code
|
||||
font-weight 400
|
||||
color $accentColor
|
||||
|
||||
kbd
|
||||
background #eee
|
||||
border solid 0.15rem #ddd
|
||||
border-bottom solid 0.25rem #ddd
|
||||
border-radius 0.15rem
|
||||
padding 0 0.15em
|
||||
|
||||
blockquote
|
||||
font-size .9rem
|
||||
color #999
|
||||
border-left .25rem solid #dfe2e5
|
||||
margin 0.5rem 0
|
||||
padding .25rem 0 .25rem 1rem
|
||||
& > p
|
||||
margin 0
|
||||
|
||||
ul, ol
|
||||
padding-left 1.2em
|
||||
|
||||
strong
|
||||
font-weight 600
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
font-weight 500
|
||||
line-height 1.25
|
||||
.content:not(.custom) > &
|
||||
margin-top (0.5rem - $navbarHeight)
|
||||
padding-top ($navbarHeight + 1rem)
|
||||
margin-bottom 0
|
||||
&:first-child
|
||||
margin-top -1.5rem
|
||||
margin-bottom 1rem
|
||||
+ p, + pre, + .custom-block
|
||||
margin-top 2rem
|
||||
&:hover .header-anchor
|
||||
opacity: 1
|
||||
|
||||
h1
|
||||
font-size 2.2rem
|
||||
|
||||
h2
|
||||
font-size 1.65rem
|
||||
padding-bottom .3rem
|
||||
border-bottom 1px solid $borderColor
|
||||
|
||||
h3
|
||||
font-size 1.35rem
|
||||
|
||||
a.header-anchor
|
||||
font-size 0.85em
|
||||
float left
|
||||
margin-left -0.87em
|
||||
padding-right 0.23em
|
||||
margin-top 0.125em
|
||||
opacity 0
|
||||
&:hover
|
||||
text-decoration none
|
||||
|
||||
code, kbd, .line-number
|
||||
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
|
||||
p, ul, ol
|
||||
line-height 1.7
|
||||
|
||||
hr
|
||||
border 0
|
||||
border-top 1px solid $borderColor
|
||||
|
||||
table
|
||||
border-collapse collapse
|
||||
margin 1rem 0
|
||||
display: block
|
||||
overflow-x: auto
|
||||
|
||||
tr
|
||||
border-top 1px solid #dfe2e5
|
||||
&:nth-child(2n)
|
||||
background-color #f6f8fa
|
||||
|
||||
th, td
|
||||
border 1px solid #dfe2e5
|
||||
padding .6em 1em
|
||||
|
||||
.theme-container
|
||||
&.sidebar-open
|
||||
.sidebar-mask
|
||||
display: block
|
||||
&.no-navbar
|
||||
.content:not(.custom) > h1, h2, h3, h4, h5, h6
|
||||
margin-top 1.5rem
|
||||
padding-top 0
|
||||
.sidebar
|
||||
top 0
|
||||
|
||||
@media (min-width: ($MQMobile + 1px))
|
||||
.theme-container.no-sidebar
|
||||
.sidebar
|
||||
display none
|
||||
.page
|
||||
padding-left 0
|
||||
|
||||
@require 'mobile.styl'
|
||||
|
||||
.iconfont
|
||||
font-size: 0.9rem;
|
||||
color: #999;
|
||||
// &:not(:first-child)
|
||||
// margin-left: 1rem
|
||||
// span
|
||||
// margin-left: .5rem
|
||||
|
||||
/************** 滚动条 **************/
|
||||
::-webkit-scrollbar
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
::-webkit-scrollbar-track-piece
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
-webkit-border-radius: 6px;
|
||||
|
||||
::-webkit-scrollbar-thumb:vertical
|
||||
height: 5px;
|
||||
background-color: rgba(125, 125, 125, 0.7);
|
||||
-webkit-border-radius: 6px;
|
||||
|
||||
::-webkit-scrollbar-thumb:horizontal
|
||||
width: 5px;
|
||||
background-color: rgba(125, 125, 125, 0.7);
|
||||
-webkit-border-radius: 6px;
|
||||
|
3
styles/toc.styl
Normal file
3
styles/toc.styl
Normal file
@ -0,0 +1,3 @@
|
||||
.table-of-contents
|
||||
.badge
|
||||
vertical-align middle
|
9
styles/wrapper.styl
Normal file
9
styles/wrapper.styl
Normal file
@ -0,0 +1,9 @@
|
||||
$wrapper
|
||||
max-width $contentWidth
|
||||
margin 0 auto
|
||||
padding 2rem 2.5rem
|
||||
@media (max-width: $MQNarrow)
|
||||
padding 2rem
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
padding 1.5rem
|
||||
|
239
util/index.js
Normal file
239
util/index.js
Normal file
@ -0,0 +1,239 @@
|
||||
export const hashRE = /#.*$/
|
||||
export const extRE = /\.(md|html)$/
|
||||
export const endingSlashRE = /\/$/
|
||||
export const outboundRE = /^(https?:|mailto:|tel:)/
|
||||
|
||||
export function normalize (path) {
|
||||
return decodeURI(path)
|
||||
.replace(hashRE, '')
|
||||
.replace(extRE, '')
|
||||
}
|
||||
|
||||
export function getHash (path) {
|
||||
const match = path.match(hashRE)
|
||||
if (match) {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
export function isExternal (path) {
|
||||
return outboundRE.test(path)
|
||||
}
|
||||
|
||||
export function isMailto (path) {
|
||||
return /^mailto:/.test(path)
|
||||
}
|
||||
|
||||
export function isTel (path) {
|
||||
return /^tel:/.test(path)
|
||||
}
|
||||
|
||||
export function ensureExt (path) {
|
||||
if (isExternal(path)) {
|
||||
return path
|
||||
}
|
||||
const hashMatch = path.match(hashRE)
|
||||
const hash = hashMatch ? hashMatch[0] : ''
|
||||
const normalized = normalize(path)
|
||||
|
||||
if (endingSlashRE.test(normalized)) {
|
||||
return path
|
||||
}
|
||||
return normalized + '.html' + hash
|
||||
}
|
||||
|
||||
export function isActive (route, path) {
|
||||
const routeHash = route.hash
|
||||
const linkHash = getHash(path)
|
||||
if (linkHash && routeHash !== linkHash) {
|
||||
return false
|
||||
}
|
||||
const routePath = normalize(route.path)
|
||||
const pagePath = normalize(path)
|
||||
return routePath === pagePath
|
||||
}
|
||||
|
||||
export function resolvePage (pages, rawPath, base) {
|
||||
if (base) {
|
||||
rawPath = resolvePath(rawPath, base)
|
||||
}
|
||||
const path = normalize(rawPath)
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
if (normalize(pages[i].regularPath) === path) {
|
||||
return Object.assign({}, pages[i], {
|
||||
type: 'page',
|
||||
path: ensureExt(pages[i].path)
|
||||
})
|
||||
}
|
||||
}
|
||||
console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
|
||||
return {}
|
||||
}
|
||||
|
||||
function resolvePath (relative, base, append) {
|
||||
const firstChar = relative.charAt(0)
|
||||
if (firstChar === '/') {
|
||||
return relative
|
||||
}
|
||||
|
||||
if (firstChar === '?' || firstChar === '#') {
|
||||
return base + relative
|
||||
}
|
||||
|
||||
const stack = base.split('/')
|
||||
|
||||
// remove trailing segment if:
|
||||
// - not appending
|
||||
// - appending to trailing slash (last segment is empty)
|
||||
if (!append || !stack[stack.length - 1]) {
|
||||
stack.pop()
|
||||
}
|
||||
|
||||
// resolve relative path
|
||||
const segments = relative.replace(/^\//, '').split('/')
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i]
|
||||
if (segment === '..') {
|
||||
stack.pop()
|
||||
} else if (segment !== '.') {
|
||||
stack.push(segment)
|
||||
}
|
||||
}
|
||||
|
||||
// ensure leading slash
|
||||
if (stack[0] !== '') {
|
||||
stack.unshift('')
|
||||
}
|
||||
|
||||
return stack.join('/')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Page } page
|
||||
* @param { string } regularPath
|
||||
* @param { SiteData } site
|
||||
* @param { string } localePath
|
||||
* @returns { SidebarGroup }
|
||||
*/
|
||||
export function resolveSidebarItems (page, regularPath, site, localePath) {
|
||||
const { pages, themeConfig } = site
|
||||
|
||||
const localeConfig = localePath && themeConfig.locales
|
||||
? themeConfig.locales[localePath] || themeConfig
|
||||
: themeConfig
|
||||
|
||||
const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
|
||||
if (pageSidebarConfig === 'auto') {
|
||||
return resolveHeaders(page)
|
||||
}
|
||||
|
||||
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
|
||||
if (!sidebarConfig) {
|
||||
return []
|
||||
} else {
|
||||
const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig)
|
||||
return config
|
||||
? config.map(item => resolveItem(item, pages, base))
|
||||
: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Page } page
|
||||
* @returns { SidebarGroup }
|
||||
*/
|
||||
function resolveHeaders (page) {
|
||||
const headers = groupHeaders(page.headers || [])
|
||||
return [{
|
||||
type: 'group',
|
||||
collapsable: false,
|
||||
title: page.title,
|
||||
path: null,
|
||||
children: headers.map(h => ({
|
||||
type: 'auto',
|
||||
title: h.title,
|
||||
basePath: page.path,
|
||||
path: page.path + '#' + h.slug,
|
||||
children: h.children || []
|
||||
}))
|
||||
}]
|
||||
}
|
||||
|
||||
export function groupHeaders (headers) {
|
||||
// group h3s under h2
|
||||
headers = headers.map(h => Object.assign({}, h))
|
||||
let lastH2
|
||||
headers.forEach(h => {
|
||||
if (h.level === 2) {
|
||||
lastH2 = h
|
||||
} else if (lastH2) {
|
||||
(lastH2.children || (lastH2.children = [])).push(h)
|
||||
}
|
||||
})
|
||||
return headers.filter(h => h.level === 2)
|
||||
}
|
||||
|
||||
export function resolveNavLinkItem (linkItem) {
|
||||
return Object.assign(linkItem, {
|
||||
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Route } route
|
||||
* @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config
|
||||
* @returns { base: string, config: SidebarConfig }
|
||||
*/
|
||||
export function resolveMatchingConfig (regularPath, config) {
|
||||
if (Array.isArray(config)) {
|
||||
return {
|
||||
base: '/',
|
||||
config: config
|
||||
}
|
||||
}
|
||||
for (const base in config) {
|
||||
if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) {
|
||||
return {
|
||||
base,
|
||||
config: config[base]
|
||||
}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
function ensureEndingSlash (path) {
|
||||
return /(\.html|\/)$/.test(path)
|
||||
? path
|
||||
: path + '/'
|
||||
}
|
||||
|
||||
function resolveItem (item, pages, base, groupDepth = 1) {
|
||||
if (typeof item === 'string') {
|
||||
return resolvePage(pages, item, base)
|
||||
} else if (Array.isArray(item)) {
|
||||
return Object.assign(resolvePage(pages, item[0], base), {
|
||||
title: item[1]
|
||||
})
|
||||
} else {
|
||||
if (groupDepth > 3) {
|
||||
console.error(
|
||||
'[vuepress] detected a too deep nested sidebar group.'
|
||||
)
|
||||
}
|
||||
const children = item.children || []
|
||||
if (children.length === 0 && item.path) {
|
||||
return Object.assign(resolvePage(pages, item.path, base), {
|
||||
title: item.title
|
||||
})
|
||||
}
|
||||
return {
|
||||
type: 'group',
|
||||
path: item.path,
|
||||
title: item.title,
|
||||
sidebarDepth: item.sidebarDepth,
|
||||
children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)),
|
||||
collapsable: item.collapsable !== false
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user