├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── components ├── About.vue ├── All.vue ├── Home.vue ├── MyVssue.vue ├── Posts.vue ├── Tags.vue └── TocBtn.vue ├── data └── .gitkeep ├── enhanceApp.js ├── global-components ├── ContentHeader.vue ├── GoTop.vue ├── MyAside.vue ├── MyFooter.vue ├── MyHeader.vue └── MyMain.vue ├── index.js ├── layouts └── Layout.vue ├── package.json ├── router └── routes.js ├── styles ├── code.styl ├── content.styl ├── element-variables.scss ├── iconfont.css ├── iconfont │ ├── iconfont.eot │ ├── iconfont.svg │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 ├── index.styl ├── palette.styl └── vssue.styl ├── templates ├── dev.html └── ssr.html └── utils ├── Bus.js └── importElement.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # Log files 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directory and files 10 | .vscode 11 | .idea 12 | 13 | data/*.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | # Log files 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 zhhlwd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

vuepress-theme-indigo-material

2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | ## 最近更新 18 | 19 | - 优化:升级 vuepress 到 1.0.2 20 | - 优化:优化首页搜索数据的加载,解决资源二次重复加载 21 | - 修复:文章样式没有选中 22 | - 修复归档页文章摘要显示,fix #11 23 | - 目录显示实际标题文字而不是 id 24 | - 更新 vuepress , node-sass 和 vssue 到最新版本,并锁死版本号 25 | - 完善了文章的样式 26 | - 优化了摘要 27 | - 彻底改善 SEO,文本内容全部直出到 HTML 28 | 29 | ### 对于老版本升级 30 | 31 | - 已经发布到 npm,只需要把 package.json 中的主题版本号改为 **1.0.22**,然后删除 node_modules 文件夹,重新运行 npm i 或者最好用 cnpm i 即可 32 | 33 | ## 介绍 34 | 35 | [vuepress-theme-indigo-material](https://github.com/zhhlwd/vuepress-theme-indigo-material) 的原主题是[hexo-theme-indigo](https://github.com/yscoder/hexo-theme-indigo), github 的 star 数高达**2042**, fork 的有**451**个, 它在静态博客网站中的应用处处可见.在这里首先感谢原作者. 36 | 37 | 然而它的定位是仅支持 IE10+ 等现代浏览器。既然不需要支持老版本浏览器, 在前端发展迅猛的今天, 已经有许多技术能够让网址更快, 所以我用[VuePress](https://github.com/vuejs/vuepress)来重写了它. 其实 indigo 的原作者已经在用 vuepress 重写了, 但是项目一直没有进展, 所以我接过了这个任务, 最后重写的效果是挺满意的. 38 | 39 | 本主题在我的有 51 篇博客笔记下, 部署在 github page,网页加载速度非常快,更快的是,网页加载完成后,此后每个页面的打开速度,都是仿佛在点击本地文件. 40 | 41 | 具体感受,国内请点击码云的 [博客网站(国内 gitee)](https://zhhlwd.gitee.io/),国外请点击我的[博客网站(国外 github)](https://zhhlwd.github.io/)来亲身感受, 部署在 github page 下 42 | 43 | 正如 VuePress 文档所说: 44 | 45 | > 每一个由 VuePress 生成的页面都带有预渲染好的 HTML,也因此具有非常好的加载性能和搜索引擎优化(SEO)。 46 | 47 | 用 Lighthouse 来测试网站的截图 48 | 49 | [![k29TJO.md.png](https://user-gold-cdn.xitu.io/2019/2/21/1690ccfe90e24db3?w=680&h=437&f=png&s=179256)](https://imgchr.com/i/k29TJO) 50 | 51 | ### 您换主题的理由 52 | 53 | - 更快更小, 54 | - 改进我在一年的的使用中感受到的原主题一些交互和外观, 例如删除分享功能, 移除了打赏功能, 增加移动端文章目录导航等等 55 | - 更加容易自定义, vuepress 的主题和插件很灵活,只要您会 Vue, 就能利用相关知识修改原主题, 和平时工作中写的页面和 APP 差不多 56 | - 基于本地数据的全文搜索 57 | - 拥抱 vue, 然后能享用它的生态, 例如组件库, 本主题就利用了 element ui 58 | - 享用 vuepress 的拓展 , 例如在 Markdown 中 使用 Vue,自定义文章摘要 59 | - 兼容 hexo 原主题写的 Markdown 文件, 不用修改即可搬迁移到本主题, 前提是您原来文件有遵守原主题的规则, 例如在文件顶部有包含 YAML front matter 60 | 61 | ``` 62 | --- 63 | title: 【读书笔记】《JavaScript权威指南》第7章数组 64 | date: 2018-11-08 04:10:03 65 | tags: [读书笔记, 《JavaScript权威指南》] 66 | --- 67 | ``` 68 | 69 | - 理论上很好的 SEO 70 | - 已经发布到 npm, 只需要下载即可, 还提供一套模板文件结构, 下载依赖后立即可以使用, 还提供了相关操作的 shell 文件,双击即可,几分钟就能有自己的博客 71 | - 内置评论功能 72 | - ...... 73 | 74 | ### 浏览器兼容性 75 | 76 | 由于博客面向的群体比较都是技术人员, 所以本主题只在最新版的谷歌浏览器和火狐浏览器测试无误 77 | 78 | vuepress 默认有编译一些对老版本浏览器的兼容, 具体控制请看它[官网配置](https://vuepress.vuejs.org/zh/config/#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7) 79 | 80 | ## 快速上手 81 | 82 | ### 注意 83 | 84 | 请确保您的 Node.js 版本 >= 8 85 | 86 | 安装 git 87 | 88 | 最好 npm 下载改用[cnpm](http://npm.taobao.org/) 89 | 90 | 以下的操作已经假设您已经会基本的 npm, git 和 vue 常识和操作 91 | 92 | ### 使用模板分支 93 | 94 | 由于文件较琐碎, 配置又容易出错,很容易出错,为此我已经准备好了一套模板, 部署在 template 分支下,您只需要的是**下载下来**, 双击目录下的 init.sh 文件,等它完成就行了, 95 | 96 | > 前提是您已经安装 node, git , npm 和 cnpm, 并且把它们加进系统的 path 中, 97 | > 特别是 git 安装目录的 bin 文件夹,把它们加进系统的 path 中是保证脚本能运行的首先依赖 98 | 99 | 下载分支的命令: 100 | 101 | ```sh 102 | git clone -b template https://github.com/zhhlwd/vuepress-theme-indigo-material.git 103 | ``` 104 | 105 | 这是 template 的目录说明 106 | 107 | **一定要注意!** 模板 docs 目录下的文件夹除了 posts 和 about 可以动,其他都不能动, 原因是 vuepress 在编译时会注册这些文件夹和文件为路由, 如果您把它们删了,即使您后面用 addRouter 再注册成功也不行,编译时没有注册的当跳转到时会让 vue 从根实例重新渲染, 这会导致应用很多状态没了 ,例如折叠的侧边栏重新打开,文章列表每打开一次就重新渲染一次, 108 | 109 | 使用本主题在内容上,您只要把新文章放在 posts 目录下, 文件顶部写好相关信息, 和自由定义 about 目录下的 index.md 文件就行了 110 | 111 | ```sh 112 | |-- template 113 | |-- .babelrc // 主题的babel配置, 按需加载element ui所需 114 | |-- .gitignore // 让git忽略跟踪dist文件夹等等, 不要把docs文件夹加进去 115 | |-- deploy.sh // 部署到git 远程仓库的shell文件, 要部署时双击即可, 前提是配置的构建目录位置没变 116 | |-- init.sh // (只要执行一次)克隆template分支到本地后, 双击它, 一步完成所有操作, 等他完成下载, 开启测试服务器, 打开http://localhost:8080/看到效果 117 | |-- package-lock.json 118 | |-- package.json 119 | |-- 目录说明.md 120 | |-- docs // 存放所有开发环境的目录 121 | |-- index.md // 首页,不用改 122 | |-- .vuepress 123 | | |-- config.js // 主题的配置 124 | | |-- public // 存放静态文件的目录, 例如img, ico ... 125 | | |-- avatar.jpg 126 | | |-- brand.jpg 127 | | |-- favicon.ico 128 | |-- about // 展示在自我介绍页面的内容 129 | | |-- index.md // 不能删除, 可以添加内容 130 | |-- tags // 不能删除, 不能动 131 | | |-- index.md // 不能删除, 不能动 132 | |-- all // 不能删除, 不能动 133 | | |-- index.md // 不能删除, 不能动 134 | |-- posts // 存放所有文章的目录 135 | ``` 136 | 137 | > 两个 sh 文件如果要自定义,很有可能会报错"No such file or directory", 如果能运行下去则不用理睬,运行中断,可能因为 window 和 Linux 的换行不一样,可以运行命令解决: 138 | 139 | ``` 140 | > 用 vim 打开该 sh 文件,输入: 141 | > :set ff 142 | > 回车,显示 fileformat=dos,重新设置下文件格式: 143 | > :set ff=unix 144 | > 保存退出: 145 | > :wq 146 | > 再执行, 147 | ``` 148 | 149 | 最后 init.sh 文件 **只运行一次** 即可, 以后使用的是 package.json 中提供的两个命令, 150 | 151 | ```sh 152 | npm run dev 153 | npm run build 154 | ``` 155 | 156 | 分别是运行本地环境开启服务器预览和运行编译打包, 它和平时的 vue 项目流程一模一样 157 | 158 | ## 配置 159 | 160 | 请去看[vuepress 的文档](https://vuepress.vuejs.org/zh/config/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE) 161 | **填写 docs/.vuepress/config.js**的相关字段.具体作用我都有相关注释, 没写的更多配置在[vuepress 文档](https://vuepress.vuejs.org/zh/config/)中查询 162 | 163 | ## 静态资源 164 | 165 | 您可以将它们放在 .vuepress/public 中, 具体请看[vuepress 文档](https://vuepress.vuejs.org/zh/guide/assets.html#%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84), 值得一提的是 166 | 167 | > 如果您的网站会被部署到一个非根路径,您将需要在 .vuepress/config.js 中设置 base,举例来说,如果您打算将您的网站部署到 https://foo.github.io/bar/,那么 base 的值就应该被设置为 "/bar/" (应当总是以斜杠开始,并以斜杠结束)。 168 | 169 | > 有了基础路径(Base URL),如果您希望引用一张放在 .vuepress/public 中的图片,您需要使用这样路径:/bar/image.png 170 | 171 | ## 开始写作 172 | 173 | 和原主题的新建一个 md 文件不一样, vuepress 没有内置像这些命令 174 | 175 | ```js 176 | hexo n '新md文件名' 177 | hexo d //push 到远程分支 178 | ``` 179 | 180 | 由于没有自动生成 md 文件的命令,需要手动创建 Markdown 文件,而且要放在 **./docs/posts/** 下, 然后还需要文件顶部像原主题那样写上信息 181 | 182 | ``` 183 | --- 184 | title: 【读书笔记】《JavaScript权威指南》第7章数组 185 | date: 2018-11-08 04:10:03 186 | tags: [读书笔记, 《JavaScript权威指南》] 187 | categories: [读书笔记] 188 | --- 189 | ``` 190 | 191 | **上下的---符号不能少**, **字段后面空一格**再写值 不然会报错. 192 | 193 | ### markdown 文件的元信息 194 | 195 | **title**: 文章名只读取这里写的名字,所以**一定要写**, 我的建议是最好文章名和文件名保持一致 196 | 197 | **date**: 主题用了 vuepress 的内置插件[@vuepress/plugin-last-updated](https://vuepress.vuejs.org/zh/plugin/official/plugin-last-updated.html#%E9%80%89%E9%A1%B9), 也可以不用写了, 198 | 199 | 然而此插件依赖的是在 git 仓库中的提交时间,这就是为什么 init.sh 中要进行 git init 的原因 200 | 201 | 在本主题中一篇文章的创建时间的确定顺序是 202 | 203 | **md 文件顶部的信息中的 date 字段==>git 仓库的提交时间===>都没有那就是当前时间** 204 | 205 | **注意**的是这只是一种容错机制, 这不意味着不用管时间了, 最后条件的当前时间**并没有写进对应文件**, 206 | 如果每次编译都匹配的是最后一条条件会导致这个文章**永远排序在最开头**, 因为每次编译都是取的当前时间, 207 | 208 | 所以对于以前的文章, 开头信息没有 date 的, **要么手动加上**, 格式一定要是 YYYY-MM-DD HH:mm:ss, 才能计算出正确的时间顺序,**要么把文章迁移到 209 | ./docs/posts/ 目录下后, 运行** 210 | 211 | ``` 212 | git add -A 213 | git commit -m '初始化文章时间' 214 | ``` 215 | 216 | 这样退求其次的以当前时间重置那些没写 date 的文章 217 | 218 | 而对于使用本主题之后的文章**最好不用写 date 字段**了,以免出错, 而是每次新建文章后进行一次 commit, 让主题取 git 仓库的时间, 并且这种工作流能让您的提交历史对应对应文章的时间 219 | 220 | ``` 221 | git add -A 222 | git commit -m '新建文章xxx' 223 | ``` 224 | 225 | **tags**: 字段**必须是数组**,如果没有则要写上一个空数组 _[]_,这样此文章会被分类到 _'未分类'_,我的建议是最好写上内容, 它是文章的内容标签, 是一种分类 226 | 227 | **categories**字段已经放弃, 因为它和**tags**字段的作用重复 228 | 229 | **摘要\*** :vuepress 内置了文章内容摘抄功能,这也是本主题首先判断收录的,如果没有才去从文章内容中截取一段,所以您可以完全自定义文章在首页列表的摘要了,例如一段简单明了的介绍,会让人更加想点击进去,这功能开启方式是 \<\!\-\- more \-\-\> 注释,该注释之前的内容会被抓取为文章的摘要 230 | 231 | > 如果一个 markdown 文件中有一个 \<\!\-\- more \-\-\> 注释,则该注释之前的内容会被抓取并暴露在 \$page.excerpt 属性中。如果您在开发一个博客主题,您可以用这个属性来渲染一个带摘抄的文章列表。 232 | 233 | ## 文章评论功能 234 | 235 | 评论功能用的是[Vssue](https://vssue.js.org/zh/guide/#vssue-%E6%98%AF%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%E7%9A%84) 236 | 237 | 虽然 Vssue 支持多个平台,但是本主题只选用的是 github 的 V3 api, 这是一个为了减少构建大小的决策. 238 | 239 | 在**docs/.vuepress/config.js**的**vssue**字段 240 | 241 | 如果需要评论功能,**需要把它的 need 改为 true**, 不需要就改为 false, 不要不去填写 242 | 243 | 如果需要则需要**创建两个新的 github OAuth App**,Vssue 的[文档](https://vssue.js.org/zh/guide/github.html)有简单明了的操作过程, 244 | 245 | 填写的 url 一个是**http://localhost:8080/**, 一个是您**将要部署到的网站地址**, 它们分别用在本地测试环境和线上环境,对应的字段分别是**development**和**production** 246 | 247 | 还有一个字段是**option**,是一个对象,存放**development**和**production**共有的属性,具体可以参考 template 分支的模板配置 248 | 249 | 拿到两组**clientId**和**clientSecret**后,还要一个存放 issue 评论的 github 仓库,得到**owner**和**repo**, 也就是仓库所有者名和仓库名, 存放 issue 的仓库在本地环境和线上环境中可以相同, 除了这四个字段是必须的,其它字段可以自行去[Vssue 的文档](https://vssue.js.org/zh/options/)中查询 250 | 251 | **一个问题是**, 在打开一篇新文章编译的页面时,Vssue 会去创建一个新 issue, 就会让您的页面从定向到 github 登录页, 您不登录就一直重定向 ,您不登录, 您的访客也会重定向,这非常影响网页体验, 所以您需要去打开文章,登录了让 Vssue 创建,这样只要一次, 您和访客在这个页面以后都不会重定向了 252 | 253 | 还有如果点击登录 github 不成功,请在点击一次 254 | 255 | ## 全站搜索 256 | 257 | 关于 搜索, 搜索的是文章内容, 也就是说假如文章标题不在内容里面您搜索标题关键词是没有匹配的, 搜索不区分大小写,但是区分空格和标点符号, 一个技巧是找到一个关键词, 例如我搜索 flex-w 就能唯一的搜到我关于 flex 的博客 258 | 259 | ## 部署 260 | 261 | 写完文章, 在本地预览无误后, 可以直接双击 deploy,sh 文件, 然后脚本运行编译打包 push 到远程仓库 262 | 263 | > 前提是首先 deploy.sh 文件的位置不要变了, 它使用的是相对路径, 然后还要在 deploy.sh 文件中改好 push 的地址 264 | 265 | 模板中构建输出位置是 **./dist** 266 | 267 | 如果不用 deploy.sh 文件,而是手打命令,要注意的是,**一定要在 dist 目录下运行这三步** 268 | 269 | ``` 270 | git init 271 | git add -A 272 | git commit -m 'deploy' 273 | ``` 274 | 275 | 因为根目录有一个仓库用来保存每个文章的时间, 不能删除, 当您 dist 目录没有一个仓库时,运行 push 命令就会往上一个文件夹找,**从而把根目录 push 到远程仓库**, 而我们只要的是 dist 目录下的文件 276 | 277 | 还有一个地方就是 vuepress 每次 build **都会清空覆盖 dist 文件夹,** 会把上一次的仓库也清空,所以**每次 build 后都要进行上面的三步才去 push**, 这也是我提供脚本的原因 278 | 279 | vuepress 的构建的确麻烦不少, 但是相当于它的网页运行速度, 这是可以忍受的,我用了我的主题,就不想用回原来的 hexo 了, 迷上了网页一点就开的速度... 280 | 281 | ## 自定义 282 | 283 | 主题的文字已经暴露出去,在 config.js 中能够自定义,如果您需要完全的自定义. 284 | 285 | 您可以下载 template 分支, 然后把 master 分支下的内容**放在模板的./docs/.vuepress/theme/下,** 286 | 287 | 把 config.js 中的**theme 字段去掉**,再在项目根文件夹下(docs 文件夹的上一级)执行 cnpm i,剩下的和 vuepress [开发主题](https://vuepress.vuejs.org/zh/theme/writing-a-theme.html)差不多. 288 | 289 | ## UI 变化 290 | 291 | - 删除了分享和打赏功能, 因为在我一年多的使用中没有一次用到 292 | - 文章目录改为 H1,H2,H3....,H 不代表为是一个 html 的 h1 标题,而是此标题在当前文章所有标题中的等级,H1 最高,H6 最低 293 | - 标签放在卡片中,更加直接的看到和点击 294 | - 增加了自我介绍页,可以用来展示一些除了文章博客以外的内容,例如,求职,自我介绍,开源项目,交流群,版权声明 295 | - 加强了我最依赖的搜索功能, 在每一个匹配中搜索词的高亮,高亮后面接着的是它的一段句子,因为常常一个词在多个文章中匹配,扰乱了判断 296 | - vuepress 内置了文章内容摘抄功能,这也是本主题首先判断收录的,如果没有才去从文章内容中截取一段,所以您可以完全自定义文章在首页列表的摘要了,例如一段简单明了的介绍,会让人更加想点击进去 297 | > 如果一个 markdown 文件中有一个 \<\!\-\- more \-\-\> 注释,则该注释之前的内容会被抓取并暴露在 \$page.excerpt 属性中。如果您在开发一个博客主题,您可以用这个属性来渲染一个带摘抄的文章列表。 298 | 299 | ## 计划 300 | 301 | - [ ] 内置一些命令, 例如 hexo 的 hexo n 和 hexo d 302 | - [ ] 整理 css 文件,暴露出一套变量, 让用户能够自定义样式 303 | - [ ] 看用户人群, 需不需要完善英文方面的配置 304 | - [ ] 本来想支持 pwa, 但是有一个 bug 没解决,就放弃, 后续看能不能解决, 非常欢迎一起来完成 305 | - [ ] 优化代码, 开发太仓促, 还没到最好, 继续优化性能 306 | 307 | ## 已知问题 308 | 309 | - 顶部的菜单按钮在屏幕大小发生变化到了断点时会在一些情况出乎意料,本主题的媒体查询断点是宽 1190px,比 iPad pro 宽, 因为本主题定位的是博客,不应该有在浏览的时候有太大的窗口大小变化,所以也不是至关重要,后面会尝试解决 310 | - 在首次加载时侧边栏上部分会闪, 初步判断是样式文件加载慢的问题, 毕竟打包成了一个 css,网页一大部分时间在加载它 311 | 312 | ## 提问 313 | 314 | 可以在 issue 提问, 也可以是加入 qq 群, 我们一起讨论 315 | 316 | ``` 317 | qq群: 668387596 318 | ``` 319 | 320 | 最后感谢您能看到这里, 您都看到这里了, 您能给我一个 star 吗? 321 | 322 | github 地址:https://github.com/zhhlwd/vuepress-theme-indigo-material 323 | -------------------------------------------------------------------------------- /components/About.vue: -------------------------------------------------------------------------------- 1 | 20 | 31 | -------------------------------------------------------------------------------- /components/All.vue: -------------------------------------------------------------------------------- 1 | 60 | 82 | -------------------------------------------------------------------------------- /components/Home.vue: -------------------------------------------------------------------------------- 1 | 74 | 110 | 111 | -------------------------------------------------------------------------------- /components/MyVssue.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 52 | 68 | -------------------------------------------------------------------------------- /components/Posts.vue: -------------------------------------------------------------------------------- 1 | 108 | 299 | -------------------------------------------------------------------------------- /components/Tags.vue: -------------------------------------------------------------------------------- 1 | 42 | 83 | 131 | -------------------------------------------------------------------------------- /components/TocBtn.vue: -------------------------------------------------------------------------------- 1 | 22 | 66 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhhlwd/vuepress-theme-indigo-material/dc0bb1b8f849c61fbd467bbaecc8166f3249f034/data/.gitkeep -------------------------------------------------------------------------------- /enhanceApp.js: -------------------------------------------------------------------------------- 1 | import routes from 'imRouter/routes'; 2 | import importElement from 'imUtils/importElement'; 3 | 4 | import 'imStyles/palette.styl'; 5 | import 'imStyles/index.styl'; 6 | import 'imStyles/element-variables.scss'; 7 | import 'imStyles/iconfont.css'; 8 | import 'imStyles/code.styl'; 9 | import 'imStyles/content.styl'; 10 | 11 | export default ({ Vue, router }) => { 12 | Vue.use(routes, { router }); 13 | Vue.use(importElement); 14 | }; 15 | -------------------------------------------------------------------------------- /global-components/ContentHeader.vue: -------------------------------------------------------------------------------- 1 | 9 | 78 | -------------------------------------------------------------------------------- /global-components/GoTop.vue: -------------------------------------------------------------------------------- 1 | 13 | 59 | -------------------------------------------------------------------------------- /global-components/MyAside.vue: -------------------------------------------------------------------------------- 1 | 76 | 96 | -------------------------------------------------------------------------------- /global-components/MyFooter.vue: -------------------------------------------------------------------------------- 1 | 54 | 69 | -------------------------------------------------------------------------------- /global-components/MyHeader.vue: -------------------------------------------------------------------------------- 1 | 88 | 214 | 235 | 236 | -------------------------------------------------------------------------------- /global-components/MyMain.vue: -------------------------------------------------------------------------------- 1 | 15 | 107 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const moment = require('moment'); 3 | const fs = require('fs'); 4 | 5 | module.exports = (options, ctx) => ({ 6 | plugins: [ 7 | [ 8 | '@vuepress/last-updated', 9 | { 10 | transformer: timestamp => { 11 | const moment = require('moment'); 12 | moment.locale('zh-CN'); 13 | return moment(timestamp).format( 14 | 'YYYY-MM-DD HH:mm:ss' 15 | ); 16 | } 17 | } 18 | ] 19 | ], 20 | enhanceAppFiles: path.resolve(__dirname, 'enhanceApp.js'), 21 | chainMarkdown(config) { 22 | config 23 | .plugin('anchor') 24 | .tap(([options]) => [ 25 | Object.assign(options, { 26 | level: [1, 2, 3, 4, 5, 6] 27 | }) 28 | ]); 29 | }, 30 | alias: { 31 | imStyles: path.resolve(__dirname, 'styles'), 32 | imRouter: path.resolve(__dirname, 'router'), 33 | imComponents: path.resolve(__dirname, 'components'), 34 | imData: path.resolve(__dirname, 'data'), 35 | imUtils: path.resolve(__dirname, 'utils') 36 | }, 37 | async ready() { 38 | //生成客户端所需的数据 39 | //只处理posts文件夹下的文件 40 | const postsFilter = val => 41 | val.path.slice(1, 6) === 'posts'; 42 | //排序函数 43 | const postsSorter = (prev, next) => { 44 | const prevTime = 45 | new Date(prev.frontmatter.date).getTime() || 46 | new Date(prev.lastUpdated).getTime() || 47 | new Date().getTime(); 48 | const nextTime = 49 | new Date(next.frontmatter.date).getTime() || 50 | new Date(next.lastUpdated).getTime() || 51 | new Date().getTime(); 52 | return prevTime - nextTime > 0 ? -1 : 1; 53 | }; 54 | const { pages } = ctx; 55 | //格式化 lastUpdated 56 | function changeDate(dateStr) { 57 | if (dateStr.length === undefined) { 58 | let str = JSON.stringify(dateStr, null, 2); 59 | return str.slice(1, 11) + ' ' + str.slice(12, -6); 60 | } else { 61 | return dateStr; 62 | } 63 | } 64 | //进一步个性化 lastUpdated,全部文章页中使用 65 | function changeTime(dateStr) { 66 | let str = ''; 67 | str = dateStr.slice(0, 7); 68 | const arr = str.split('-'); 69 | let result = [ 70 | arr[0] + '-' + arr[1], 71 | Number(arr[0]) + Number(arr[1]) 72 | ]; 73 | return result; 74 | } 75 | //开始格式化和排序 76 | const posts = pages.filter(postsFilter); 77 | posts.sort(postsSorter); 78 | 79 | //存放最终数据的变量 80 | let archived = []; 81 | let tagsList = {}; 82 | let poList = []; 83 | let search = []; 84 | 85 | posts.forEach((val, index) => { 86 | //遍历posts目录生成包含所有文章信息的 archived 87 | let page = {}; 88 | let sear = {}; 89 | let { 90 | excerpt, 91 | lastUpdated, 92 | path, 93 | _strippedContent 94 | } = val; 95 | let { tags, title } = val.frontmatter; 96 | if (_strippedContent) { 97 | _strippedContent = _strippedContent 98 | .replace(/[\n\r]/g, ' ') 99 | .replace(/\s+/, ' '); 100 | } 101 | if (_strippedContent) { 102 | excerpt = 103 | excerpt || 104 | (_strippedContent.slice(0, 200) 105 | ? _strippedContent.slice(0, 200) + '......' 106 | : false) || 107 | ''; 108 | excerpt = excerpt.replace(/#/g, ''); 109 | } else { 110 | excerpt = ''; 111 | } 112 | 113 | lastUpdated = 114 | val.frontmatter.date || 115 | lastUpdated || 116 | moment().format('YYYY-MM-DD HH:mm:ss'); 117 | lastUpdated = changeDate(lastUpdated); 118 | tags = tags || ''; 119 | title = title || '你忘记写title字段了'; 120 | 121 | page.excerpt = excerpt; 122 | page.tags = tags; 123 | page.id = index; 124 | page.title = title; 125 | page.lastUpdated = lastUpdated; 126 | page.path = path; 127 | 128 | sear.title = title; 129 | sear.path = path; 130 | sear.strippedContent = _strippedContent; 131 | 132 | archived.push(page); 133 | search.push(sear); 134 | 135 | //生成标签页的数据 136 | //剔除不需要的数据 137 | const t = {}; 138 | t.lastUpdated = lastUpdated; 139 | t.tags = tags; 140 | t.id = index; 141 | t.title = title; 142 | t.path = path; 143 | 144 | if (!tags) { 145 | if (!tagsList['未分类']) { 146 | tagsList['未分类'] = [{ name: '未分类' }]; 147 | } 148 | tagsList['未分类'].push(t); 149 | } else { 150 | tags.forEach(tag => { 151 | if (tag === undefined) { 152 | if (!tagsList['未分类']) { 153 | tagsList['未分类'] = [{ name: '未分类' }]; 154 | } 155 | tagsList['未分类'].push(t); 156 | } 157 | if (!tagsList[tag]) { 158 | tagsList[tag] = [{ name: tag }]; 159 | } 160 | tagsList[tag].push(t); 161 | }); 162 | } 163 | }); 164 | 165 | //生成全部文章页所需要的数据 166 | let index = 0; 167 | archived.forEach((val, i) => { 168 | let result1 = changeTime(val.lastUpdated); 169 | if (archived.length === 1) { 170 | poList[0] = [result1[0]]; 171 | return poList[0].push(val); 172 | } 173 | if (i + 1 !== archived.length) { 174 | var result2 = changeTime( 175 | archived[i + 1].lastUpdated 176 | ); 177 | } else { 178 | var result2 = changeTime( 179 | archived[i - 1].lastUpdated 180 | ); 181 | } 182 | if (!poList[index]) { 183 | poList[index] = [result1[0]]; 184 | } 185 | if (!poList[index]) { 186 | poList[index] = [result2[0]]; 187 | } 188 | poList[index].push(val); 189 | if (result1[1] !== result2[1]) { 190 | index++; 191 | } 192 | }); 193 | 194 | const dataPath = path.resolve(__dirname, 'data'); 195 | console.log('正在写入本地数据,加快在客户端的速度~~'); 196 | 197 | fs.writeFile( 198 | `${dataPath}/content.js`, 199 | `export default ${JSON.stringify( 200 | archived, 201 | null, 202 | 2 203 | )};`, 204 | error => { 205 | if (error) 206 | return console.log( 207 | '写入首页content文件失败,原因是' + error.message 208 | ); 209 | console.log('写入首页content文件成功'); 210 | } 211 | ); 212 | 213 | fs.writeFile( 214 | `${dataPath}/tagsList.js`, 215 | `export default ${JSON.stringify( 216 | tagsList, 217 | null, 218 | 2 219 | )};`, 220 | error => { 221 | if (error) 222 | return console.log( 223 | '写入标签页tagsList文件失败,原因是' + 224 | error.message 225 | ); 226 | console.log('写入标签页tagsList文件成功'); 227 | } 228 | ); 229 | 230 | fs.writeFile( 231 | `${dataPath}/search.js`, 232 | `export default ${JSON.stringify(search, null, 2)};`, 233 | error => { 234 | if (error) 235 | return console.log( 236 | '写入搜索search文件失败,原因是' + error.message 237 | ); 238 | console.log('写入搜索search文件成功'); 239 | } 240 | ); 241 | 242 | fs.writeFile( 243 | `${dataPath}/poList.js`, 244 | `export default ${JSON.stringify(poList, null, 2)};`, 245 | error => { 246 | if (error) 247 | return console.log( 248 | '写入归档页poList文件失败,原因是' + 249 | error.message 250 | ); 251 | console.log('写入归档页poList文件成功'); 252 | } 253 | ); 254 | } 255 | }); 256 | -------------------------------------------------------------------------------- /layouts/Layout.vue: -------------------------------------------------------------------------------- 1 | 29 | 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuepress-theme-indigo-material", 3 | "version": "1.0.22", 4 | "description": "Material Design theme for vuepress", 5 | "main": "index.js", 6 | "private": false, 7 | "keywords": [ 8 | "vue", 9 | "vuepress", 10 | "theme", 11 | "material design" 12 | ], 13 | "author": { 14 | "name": "zhhlwd", 15 | "email": "1962421071@qq.com" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/zhhlwd/vuepress-theme-indigo-material" 20 | }, 21 | "license": "MIT", 22 | "dependencies": { 23 | "@vssue/api-github-v3": "1.0.0", 24 | "element-ui": "2.5.4", 25 | "vssue": "1.0.0" 26 | }, 27 | "devDependencies": { 28 | "babel-eslint": "8.2.6", 29 | "babel-plugin-component": "1.1.1", 30 | "eslint": "5.0.0", 31 | "eslint-plugin-vue": "5.2.2", 32 | "moment": "2.24.0", 33 | "node-sass": "4.12.0", 34 | "sass-loader": "7.1.0", 35 | "vuepress": "1.0.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /router/routes.js: -------------------------------------------------------------------------------- 1 | const Layout = () => import('../layouts/Layout.vue'); 2 | 3 | const install = (Vue, { router }) => { 4 | let ru = ['/', '/all/', '/about/', '/tags/', '/tags/:tag', '/posts/:post']; 5 | const routes = []; 6 | 7 | for (var i = 0, len = ru.length; i < len; i++) { 8 | routes.push({ 9 | name: ru[i], 10 | path: ru[i], 11 | component: Layout 12 | }); 13 | } 14 | 15 | router.addRoutes(routes); 16 | router.beforeEach((to, from, next) => { 17 | if (typeof window === 'undefined') { 18 | return next(); 19 | } 20 | document.getElementById('loader-wrapper').style.display = 'block'; 21 | document.getElementById('loader-wrapper').style.opacity = '1'; 22 | next(); 23 | }); 24 | router.afterEach(() => { 25 | if (typeof window === 'undefined') return; 26 | document.getElementById('loader-wrapper').style.opacity = '0'; 27 | 28 | setTimeout(() => { 29 | document.getElementById('loader-wrapper').style.display = 'none'; 30 | }, 200); 31 | }); 32 | }; 33 | 34 | export default { 35 | install 36 | }; 37 | -------------------------------------------------------------------------------- /styles/code.styl: -------------------------------------------------------------------------------- 1 | pre, pre[class*="language-"] 2 | padding 1.25rem 1.5rem 3 | margin 0.85rem 0 4 | overflow auto 5 | 6 | div[class*="language-"] 7 | position relative 8 | 9 | .highlight-lines 10 | user-select none 11 | padding-top 1.3rem 12 | position absolute 13 | top 0 14 | left 0 15 | width 100% 16 | line-height 1.4 17 | .highlighted 18 | background-color rgba(0, 0, 0, 0.5) 19 | pre, pre[class*="language-"] 20 | position relative 21 | z-index 1 22 | &::before 23 | position absolute 24 | z-index 3 25 | top 0.8em 26 | right 1em 27 | font-size 0.75rem 28 | color rgba(255, 255, 255, 0.4) 29 | &:not(.line-numbers-mode) 30 | .line-numbers-wrapper 31 | display none 32 | &.line-numbers-mode 33 | .highlight-lines .highlighted 34 | position relative 35 | &:before 36 | content '' 37 | position absolute 38 | z-index 3 39 | left 0 40 | top 0 41 | display block 42 | width 3.5rem 43 | height 100% 44 | pre 45 | padding-left 3.5rem + 1 rem 46 | vertical-align middle 47 | z-index 0 48 | line-height 1.6 49 | .line-numbers-wrapper 50 | position absolute 51 | top 0 52 | width 3.5rem 53 | text-align center 54 | color rgba(255, 255, 255, 0.3) 55 | padding 1.4rem 0 56 | line-height 1.4 57 | br 58 | user-select none 59 | .line-number 60 | position relative 61 | z-index 1 62 | user-select none 63 | font-size 0.85em 64 | &::after 65 | content '' 66 | position absolute 67 | z-index 0 68 | top 0 69 | left 0 70 | width: 3.5rem; 71 | height 100% 72 | border-radius 6px 0 0 6px 73 | border-right 1px solid rgba(0, 0, 0, 66%) 74 | background-color $codeBgColor 75 | code, kbd, pre, samp 76 | font-family: $font-code 77 | background-color: #fff5f5; 78 | color: #ff502c; 79 | font-size: 0.9em; 80 | padding: .065em .4em; 81 | 82 | code, kbd { 83 | display: inline; 84 | margin: 0 0.3em; 85 | padding: .125em .25em; 86 | box-shadow: none; 87 | } 88 | 89 | pre, 90 | pre[class*="language-"] { 91 | border-radius: 6px; 92 | padding: 1.5em 1.75em 1.5em; 93 | white-space: pre-wrap; 94 | word-break: break-word; 95 | line-height: 1.4; 96 | background: #282c34; 97 | } 98 | div[class*='language-'] { 99 | position relative 100 | &:before { 101 | position: absolute; 102 | top: 0.4em; 103 | right: 1em; 104 | font-size: .86em; 105 | color: #fff; 106 | z-index: 3; 107 | } 108 | } 109 | 110 | pre { 111 | code { 112 | margin: 0; 113 | padding: 0; 114 | background-color: transparent ; 115 | white-space: pre; 116 | font-size: .85em; 117 | font-weight: 400; 118 | color: #03A9F4; 119 | .token.selector, .token.important, .token.atrule, .token.keyword, .token.builtin { 120 | color: #C678DD; 121 | } 122 | .token.boolean, .token.number, .token.function { 123 | color: #61AFEF; 124 | } 125 | .token.punctuation { 126 | color: #FFD700; 127 | } 128 | .token.attr-name,.token.deleted,.token.namespace,.token.tag { 129 | color: #e06c75; 130 | } 131 | .token.attr-value,.token.char,.token.regex,.token.string,.token.variable { 132 | color: #69B779; 133 | } 134 | .token.property { 135 | color: #E2DDDD 136 | } 137 | .token.function-variable.function { 138 | color: #49f581; 139 | } 140 | .token.function-name { 141 | color: #6196cc 142 | } 143 | .token.operator { 144 | color: #DA6FBC; 145 | } 146 | .token.boolean,.token.function,.token.number { 147 | color: #f08d49; 148 | } 149 | .token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog { 150 | color: #999 151 | } 152 | 153 | &:before, &:after { 154 | content: ''; 155 | letter-spacing: 0; 156 | } 157 | } 158 | } 159 | 160 | for type, exts in $code-languages 161 | div 162 | &.language-{exts[0]}:before 163 | content type 164 | if exts[1] 165 | &.language-{exts[1]}:before 166 | content type 167 | 168 | blockquote { 169 | margin 2em 0 2em 0 170 | color #dad7d7 171 | border-left .5em solid $primary-color 172 | padding: 1.25rem 1.5rem; 173 | background-color: #282c34; 174 | font-size: 15px; 175 | border-radius: 4px; 176 | p { 177 | margin-top: 0 178 | &:last-child { 179 | margin-bottom: 0 180 | 181 | } 182 | code { 183 | background-color $primary-color 184 | padding: 3px 5px; 185 | border-radius: 3px; 186 | color: #000; 187 | vertical-align: bottom; 188 | } 189 | } 190 | footer { 191 | font-size 80% 192 | } 193 | } 194 | 195 | strong { 196 | font-weight: 600; 197 | margin: 0 0.2em; 198 | } 199 | 200 | ol, ul { 201 | padding-left: 2em; 202 | } 203 | 204 | table { 205 | width: 100%; 206 | border: 1px solid #dedede; 207 | border-collapse: collapse; 208 | border-spacing: 0; 209 | 210 | thead { 211 | tr { 212 | background: #f6f8fa; 213 | } 214 | } 215 | 216 | tbody { 217 | tr { 218 | &:hover { 219 | background: #efefef; 220 | } 221 | } 222 | } 223 | 224 | td, th { 225 | border: 1px solid #dedede; 226 | padding: .375em .75em; 227 | text-align: center !important; 228 | } 229 | } 230 | 231 | video, audio { 232 | max-width: 100%; 233 | } 234 | 235 | .highlighted-line { 236 | display: block; 237 | margin: 0 -1.5rem; 238 | padding: 0 1.5rem; 239 | background-color: rgba(0, 0, 0, 66%); 240 | } 241 | 242 | *:first-child { 243 | margin-top 0 244 | } -------------------------------------------------------------------------------- /styles/content.styl: -------------------------------------------------------------------------------- 1 | .post-sign{ 2 | display: block; 3 | min-height: 150px; 4 | font-size: 16px; 5 | line-height: 1.8; 6 | color: $content-color; 7 | 8 | ::-webkit-scrollbar { 9 | -webkit-appearance: none; 10 | width: 4px; 11 | height: 5px; 12 | } 13 | 14 | p { 15 | word-wrap: break-word; 16 | } 17 | 18 | h1, h2, h3, h4, h5, h6, ol, ul, pre, table, figure { 19 | margin-bottom: 1.2em 20 | } 21 | 22 | h1, h2, h3, h4, h5, h6 { 23 | padding-top: 2em; 24 | margin-top: -1.8em; 25 | text-transform: none; 26 | 27 | .header-anchor { 28 | margin-left: -1em; 29 | padding-right: 4px; 30 | color: $light-primary-color; 31 | opacity: 0; 32 | text-decoration: none; 33 | } 34 | 35 | &:hover { 36 | .header-anchor { 37 | opacity: 1; 38 | } 39 | } 40 | } 41 | 42 | a { 43 | position relative 44 | font-weight: 500 45 | text-decoration none 46 | color: $primary-color 47 | &:after { 48 | content '' 49 | position absolute 50 | left 0 51 | bottom 0 52 | height 1px 53 | width 100% 54 | background rgba($primary-color, .2) 55 | transition height 200ms ease-in 56 | } 57 | &:hover:after { 58 | height 100% 59 | } 60 | } 61 | } 62 | 63 | 64 | .custom-block 65 | margin-bottom 1.2em 66 | padding 1em 1.5em 0.2em 67 | border-left-width .5em 68 | border-left-style solid 69 | border-radius 4px 70 | &-title 71 | font-weight 600 72 | for type, color in $custom-blocks 73 | &.{type} 74 | color darken(color, 20%) 75 | border-color color!important 76 | background lighten(color, 90%)!important 77 | border-radius: 4px; 78 | 79 | .line-numbers-wrapper 80 | position absolute 81 | top 0 82 | width $lineNumbersWrapperWidth 83 | text-align center 84 | color rgba(255, 255, 255, 0.3) 85 | padding 1.25rem 0 86 | line-height 1.4 87 | br 88 | user-select none 89 | .line-number 90 | position relative 91 | z-index 4 92 | user-select none 93 | font-size 0.85em -------------------------------------------------------------------------------- /styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 改变主题色变量 */ 2 | $--color-primary: #3f51b5; 3 | 4 | /* 改变 icon 字体路径变量,必需 */ 5 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 6 | @import '~element-ui/lib/theme-chalk/display.css'; 7 | @import '~element-ui/packages/theme-chalk/src/index'; 8 | -------------------------------------------------------------------------------- /styles/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'iconfont'; 3 | src: url('iconfont/iconfont.eot?t=1550165856909'); /* IE9 */ 4 | src: url('iconfont/iconfont.eot?t=1550165856909#iefix') 5 | format('embedded-opentype'), 6 | /* IE6-IE8 */ 7 | url('data:application/x-font-woff2;charset=utf-8;base64,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') 8 | format('woff2'), 9 | url('iconfont/iconfont.woff?t=1550165856909') format('woff'), 10 | url('iconfont/iconfont.ttf?t=1550165856909') format('truetype'), 11 | /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 12 | url('iconfont/iconfont.svg?t=1550165856909#iconfont') format('svg'); /* iOS 4.1- */ 13 | } 14 | 15 | .iconfont { 16 | font-family: 'iconfont' !important; 17 | font-size: 16px; 18 | font-style: normal; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | .icon-github:before { 24 | content: '\e7ab'; 25 | } 26 | 27 | .icon-huidaodingbu:before { 28 | content: '\e61c'; 29 | } 30 | 31 | .icon-biaoqian:before { 32 | content: '\e629'; 33 | } 34 | 35 | .icon-wenzhang:before { 36 | content: '\e610'; 37 | } 38 | 39 | .icon-guanbi:before { 40 | content: '\e654'; 41 | } 42 | 43 | .icon-home:before { 44 | content: '\e6b7'; 45 | } 46 | 47 | .icon-service-directory:before { 48 | content: '\e661'; 49 | } 50 | 51 | .icon-aboutme:before { 52 | content: '\e63e'; 53 | } 54 | 55 | .icon-caidan:before { 56 | content: '\e653'; 57 | } 58 | -------------------------------------------------------------------------------- /styles/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhhlwd/vuepress-theme-indigo-material/dc0bb1b8f849c61fbd467bbaecc8166f3249f034/styles/iconfont/iconfont.eot -------------------------------------------------------------------------------- /styles/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /styles/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhhlwd/vuepress-theme-indigo-material/dc0bb1b8f849c61fbd467bbaecc8166f3249f034/styles/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /styles/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhhlwd/vuepress-theme-indigo-material/dc0bb1b8f849c61fbd467bbaecc8166f3249f034/styles/iconfont/iconfont.woff -------------------------------------------------------------------------------- /styles/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhhlwd/vuepress-theme-indigo-material/dc0bb1b8f849c61fbd467bbaecc8166f3249f034/styles/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /styles/index.styl: -------------------------------------------------------------------------------- 1 | html 2 | overflow-y: auto 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | background $body-background 6 | color: #2c3e50; 7 | font-family $body-font-family 8 | 9 | #app{ 10 | height: 100%!important; 11 | } 12 | html,body { 13 | position: relative; 14 | min-height: 100%; 15 | height: 100% 16 | } 17 | 18 | body,h1,h2,h3,h4,h5,h6,p,figure,pre,dl,dd,blockquote,button,input { 19 | margin: 0 20 | } 21 | 22 | input,legend,input,textarea,button { 23 | padding: 0 24 | } 25 | 26 | form,fieldset,th,td { 27 | margin: 0; 28 | padding: 0 29 | } 30 | 31 | ol,ul { 32 | margin: 12px 0; 33 | padding: 0 0 0 32px 34 | } 35 | 36 | ol ol,ol ul,ul ol,ul ul { 37 | margin-top: 0; 38 | margin-bottom: 0 39 | } 40 | 41 | p { 42 | margin: 12px 0 43 | } 44 | 45 | small { 46 | font-size: 75%; 47 | line-height: 1 48 | } 49 | 50 | main { 51 | display: block 52 | } 53 | 54 | template { 55 | display: none 56 | } 57 | 58 | img { 59 | max-width: 100%; 60 | border: none 61 | } 62 | 63 | img,input,button,label { 64 | vertical-align: middle 65 | } 66 | 67 | i { 68 | font-style: normal 69 | } 70 | 71 | input[type="button"],input[type="submit"],input[type="search"],input[type="reset"] { 72 | cursor: pointer; 73 | -webkit-appearance: button 74 | } 75 | 76 | input:focus,select:focus,button:focus,textarea:focus { 77 | outline: none 78 | } 79 | 80 | textarea { 81 | resize: none 82 | } 83 | 84 | a { 85 | background-color: transparent; 86 | background-image: none; 87 | color: #3f51b5; 88 | text-decoration: none; 89 | outline: 0 90 | } 91 | 92 | a:hover { 93 | color: #303f9f; 94 | text-decoration: underline 95 | } 96 | 97 | hr,.hr { 98 | border: 0; 99 | border-top: 1px solid #dadada; 100 | box-sizing: content-box; 101 | display: block; 102 | height: 0; 103 | margin-top: 24px; 104 | margin-bottom: 24px 105 | } 106 | 107 | ::-webkit-scrollbar { 108 | -webkit-appearance: none; 109 | width: 8px; 110 | height: 8px 111 | } 112 | 113 | ::-webkit-scrollbar-track { 114 | background-color: inherit 115 | } 116 | 117 | ::-webkit-scrollbar-thumb { 118 | background-color: #b6b6b6; 119 | border: 1px solid #fff; 120 | border-radius: 10px; 121 | } 122 | 123 | ::-webkit-scrollbar-thumb:hover { 124 | background-color: #9d9d9d 125 | } 126 | 127 | ::-webkit-scrollbar-thumb:active { 128 | background-color: #838383 129 | } 130 | 131 | .clearfix:after { 132 | content: ""; 133 | display: table; 134 | clear: both; 135 | overflow: hidden 136 | } 137 | 138 | .ellipsis { 139 | overflow: hidden; 140 | text-overflow: ellipsis; 141 | white-space: nowrap 142 | } 143 | 144 | .el-tag { 145 | color: rgba(255, 255, 255, 0.8); 146 | margin: 5px; 147 | cursor: pointer; 148 | } 149 | .el-tag:hover { 150 | opacity: 0.6; 151 | } 152 | .el-tag:nth-child(1n + 0) { 153 | background: #3f51b5; 154 | } 155 | .el-tag:nth-child(2n + 0) { 156 | background: #36c978; 157 | } 158 | .el-tag:nth-child(3n + 0) { 159 | background: #673ab7; 160 | } 161 | 162 | .el-tag:nth-child(4n + 0) { 163 | background: #ce24c6; 164 | } 165 | 166 | .el-tag:nth-child(5n + 0) { 167 | background: #dd5c53; 168 | } 169 | 170 | .el-tag:nth-child(6n + 0) { 171 | background: #00abc0; 172 | } 173 | 174 | .el-tag:nth-child(7n + 0) { 175 | background: #2196f3; 176 | } 177 | .el-tag:nth-child(8n + 0) { 178 | background: rgb(148, 8, 213); 179 | } 180 | .el-tag:nth-child(9n + 0) { 181 | background: #34dad4; 182 | } 183 | 184 | 185 | 186 | .post-title-time { 187 | font-weight: 500; 188 | font-size: 15px; 189 | color: #727272; 190 | margin: 8px 0 0 191 | } 192 | 193 | .post-title-link { 194 | color: #3f51b5; 195 | font-size: 24px; 196 | } 197 | @media (max-width: 1190px) { 198 | .post-title-link { 199 | font-size: 20px; 200 | } 201 | } 202 | .el-card__body { 203 | padding: 0px; 204 | } 205 | .el-card__header { 206 | padding: 0 14px 6px; 207 | } 208 | .post-excerpt { 209 | color: #888; 210 | font-size: 16px; 211 | margin: 10px 0; 212 | padding: 0 14px; 213 | } 214 | 215 | 216 | .post-footer { 217 | border-top: 1px solid #ddd; 218 | padding: 4px 8px 5px; 219 | display: flex; 220 | flex-wrap: wrap; 221 | justify-content: flex-start; 222 | } 223 | 224 | 225 | 226 | #loader-wrapper { 227 | position: fixed; 228 | top: 0; 229 | left: 0; 230 | width: 100%; 231 | height: 100%; 232 | z-index: 2000; 233 | } 234 | #loader { 235 | display: block; 236 | position: relative; 237 | left: 50%; 238 | top: 50%; 239 | width: 150px; 240 | height: 150px; 241 | margin: -75px 0 0 -75px; 242 | border-radius: 50%; 243 | border: 3px solid transparent; 244 | border-top-color: #3498db; 245 | -webkit-animation: spin 2s linear infinite; 246 | animation: spin 2s linear infinite; 247 | z-index: 2001; 248 | } 249 | #loader:before { 250 | content: ''; 251 | position: absolute; 252 | top: 5px; 253 | left: 5px; 254 | right: 5px; 255 | bottom: 5px; 256 | border-radius: 50%; 257 | border: 3px solid transparent; 258 | border-top-color: #e74c3c; 259 | -webkit-animation: spin 3s linear infinite; 260 | animation: spin 3s linear infinite; 261 | } 262 | #loader:after { 263 | content: ''; 264 | position: absolute; 265 | top: 15px; 266 | left: 15px; 267 | right: 15px; 268 | bottom: 15px; 269 | border-radius: 50%; 270 | border: 3px solid transparent; 271 | border-top-color: #f9c922; 272 | -webkit-animation: spin 1.5s linear infinite; 273 | animation: spin 1.5s linear infinite; 274 | } 275 | @-webkit-keyframes spin { 276 | 0% { 277 | -webkit-transform: rotate(0deg); 278 | transform: rotate(0deg); 279 | } 280 | to { 281 | -webkit-transform: rotate(1turn); 282 | transform: rotate(1turn); 283 | } 284 | } 285 | @keyframes spin { 286 | 0% { 287 | -webkit-transform: rotate(0deg); 288 | transform: rotate(0deg); 289 | } 290 | to { 291 | -webkit-transform: rotate(1turn); 292 | transform: rotate(1turn); 293 | } 294 | } 295 | #loader-wrapper .loader-section { 296 | position: fixed; 297 | top: 0; 298 | width: 51%; 299 | height: 100%; 300 | background: transparent; 301 | z-index: 2000; 302 | -webkit-transform: translateX(0); 303 | transform: translateX(0); 304 | transition: 0.2s all ease-in-out; 305 | } 306 | #loader-wrapper .loader-section.section-left { 307 | left: 0; 308 | } 309 | #loader-wrapper .loader-section.section-right { 310 | right: 0; 311 | } 312 | .loaded #loader-wrapper .loader-section.section-left { 313 | -webkit-transform: translateX(-100%); 314 | transform: translateX(-100%); 315 | } 316 | .loaded #loader-wrapper .loader-section.section-left, 317 | .loaded #loader-wrapper .loader-section.section-right { 318 | -webkit-transition: all 0.7s cubic-bezier(0.645, 0.045, 0.355, 1) 0.3s; 319 | transition: all 0.7s cubic-bezier(0.645, 0.045, 0.355, 1) 0.3s; 320 | } 321 | .loaded #loader-wrapper .loader-section.section-right { 322 | -webkit-transform: translateX(100%); 323 | transform: translateX(100%); 324 | } 325 | .loaded #loader { 326 | opacity: 0; 327 | -webkit-transition: all 0.3s ease-out; 328 | transition: all 0.3s ease-out; 329 | } 330 | .loaded #loader-wrapper { 331 | -webkit-transform: translateY(-100%); 332 | transform: translateY(-100%); 333 | -webkit-transition: all 0.3s ease-out 1s; 334 | transition: all 0.3s ease-out 1s; 335 | } 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /styles/palette.styl: -------------------------------------------------------------------------------- 1 | $body-font-family = -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; 2 | $font-size-root = 16px 3 | $line-height-root = 1.5 4 | $card-border-radius = 4px 5 | 6 | $font-roboto = './fonts/roboto' 7 | $font-code = Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace 8 | 9 | $container-max-width = 1000px 10 | $body-background = #f6f6f6 11 | $primary-color = #3F51B5 12 | $light-primary-color = lighten($primary-color, 50%) 13 | $content-color = #4d4d4d 14 | $gray-color = #fff 15 | 16 | $custom-blocks = { 17 | tip: #4caf50 18 | warning: #FF9800 19 | danger: #ff5722 20 | } 21 | 22 | $code-languages = { 23 | js: 'js' 'javascript' 24 | html: 'html' 'markup' 25 | md: 'md' 'markdown' 26 | vue: 'vue' 27 | css: 'css' 28 | less: 'less' 29 | sass: 'sass' 30 | scss: 'scss' 31 | stylus: 'stylus' 32 | jsx: 'jsx' 33 | ts: 'ts' 'typescript' 34 | json: 'json' 35 | yaml: 'yaml' 36 | bash: 'bash' 37 | c: 'c' 38 | java: 'java' 39 | kotlin: 'kotlin' 40 | python: 'py' 'python' 41 | csharp: 'c#' 'csharp' 42 | go: 'go' 43 | sql: 'sql' 44 | } 45 | 46 | // colors 47 | $accentColor = #3eaf7c 48 | $textColor = #2c3e50 49 | $borderColor = #eaecef 50 | $codeBgColor = #282c34 51 | $arrowBgColor = #ccc 52 | 53 | // layout 54 | $navbarHeight = 3.6rem 55 | $sidebarWidth = 20rem 56 | $contentWidth = 740px 57 | 58 | -------------------------------------------------------------------------------- /styles/vssue.styl: -------------------------------------------------------------------------------- 1 | // 先设置变量 2 | $vssue-theme-color = #3f51b5 3 | 4 | $vssue-border-color = #3f51b5 5 | 6 | // 引入 Vssue 的样式主文件和 github-markdown-css 7 | @import '~vssue/src/styles/index' 8 | @import '~github-markdown-css/github-markdown.css' -------------------------------------------------------------------------------- /templates/dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /templates/ssr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | {{ title }} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{{ userHeadTags 18 | 19 | 20 | 21 | }}} 22 | {{{ pageMeta 23 | 24 | 25 | 26 | }}} 27 | {{{ renderResourceHints() 28 | 29 | 30 | 31 | }}} 32 | {{{ renderStyles() 33 | 34 | 35 | 36 | }}} 37 | 38 | 39 | 44 | 45 | {{{ renderScripts() }}} 46 | 47 | 48 | -------------------------------------------------------------------------------- /utils/Bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | export default new Vue(); -------------------------------------------------------------------------------- /utils/importElement.js: -------------------------------------------------------------------------------- 1 | import { 2 | Pagination, 3 | Autocomplete, 4 | Menu, 5 | MenuItem, 6 | Button, 7 | Tag, 8 | Row, 9 | Col, 10 | Card, 11 | Container, 12 | Header, 13 | Aside, 14 | Main, 15 | Footer 16 | } from 'element-ui'; 17 | const install = Vue => { 18 | Vue.use(Pagination); 19 | Vue.use(Autocomplete); 20 | Vue.use(Menu); 21 | Vue.use(MenuItem); 22 | Vue.use(Button); 23 | Vue.use(Tag); 24 | Vue.use(Row); 25 | Vue.use(Col); 26 | Vue.use(Card); 27 | Vue.use(Container); 28 | Vue.use(Header); 29 | Vue.use(Aside); 30 | Vue.use(Main); 31 | Vue.use(Footer); 32 | }; 33 | export default { 34 | install 35 | }; 36 | --------------------------------------------------------------------------------