├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── admin ├── .env.development ├── .gitignore ├── README.md ├── package.json ├── pnpm-lock.yaml ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ ├── linkimage │ │ │ ├── favicon.png │ │ │ └── zhihu.png │ │ ├── logo.png │ │ └── postimage │ │ │ ├── closure.jpg │ │ │ ├── english.jpg │ │ │ ├── git.jpg │ │ │ ├── learn1.jpg │ │ │ ├── null.jpg │ │ │ └── this.jpg │ ├── components │ │ ├── HelloWorld.vue │ │ └── MTopHandle.vue │ ├── http.js │ ├── main.js │ ├── plugins │ │ └── filters.js │ ├── router │ │ └── index.js │ ├── store │ │ └── index.js │ ├── style.css │ └── views │ │ ├── About.vue │ │ ├── AdminUserEdit.vue │ │ ├── AdminUserList.vue │ │ ├── ArticleEdit.vue │ │ ├── ArticleList.vue │ │ ├── CategoryEdit.vue │ │ ├── CategoryList.vue │ │ ├── CommentList.vue │ │ ├── LinkEdit.vue │ │ ├── LinkList.vue │ │ ├── Login.vue │ │ ├── Main.vue │ │ ├── MessageList.vue │ │ └── UserList.vue └── vue.config.js ├── package.json ├── pnpm-lock.yaml ├── screenshot ├── admin.gif └── web.gif ├── server ├── .gitignore ├── admin │ ├── css │ │ ├── app.b98fe089.css │ │ ├── article_edit.33f6816a.css │ │ ├── chunk-vendors.53794358.css │ │ ├── login.d3cd1897.css │ │ └── main.b0303dc3.css │ ├── favicon.ico │ ├── fonts │ │ ├── element-icons.535877f5.woff │ │ ├── element-icons.732389de.ttf │ │ ├── fontello.068ca2b3.ttf │ │ ├── fontello.8d4a4e6f.woff2 │ │ ├── fontello.a782baa8.woff │ │ └── fontello.e73a0647.eot │ ├── img │ │ └── fontello.9354499c.svg │ ├── index.html │ └── js │ │ ├── admin_edit.e91fae0c.js │ │ ├── admin_edit.e91fae0c.js.map │ │ ├── admin_list.874c83ef.js │ │ ├── admin_list.874c83ef.js.map │ │ ├── app.fed95af8.js │ │ ├── app.fed95af8.js.map │ │ ├── article_edit.dfcc3957.js │ │ ├── article_edit.dfcc3957.js.map │ │ ├── article_list.2a29b43e.js │ │ ├── article_list.2a29b43e.js.map │ │ ├── category_edit.3854f8a3.js │ │ ├── category_edit.3854f8a3.js.map │ │ ├── category_list.ee7cd4b7.js │ │ ├── category_list.ee7cd4b7.js.map │ │ ├── chunk-vendors.64e1808c.js │ │ ├── chunk-vendors.64e1808c.js.map │ │ ├── comment_list.554580fc.js │ │ ├── comment_list.554580fc.js.map │ │ ├── link_edit.2c0e0f99.js │ │ ├── link_edit.2c0e0f99.js.map │ │ ├── link_list.7f84cc17.js │ │ ├── link_list.7f84cc17.js.map │ │ ├── login.109891a5.js │ │ ├── login.109891a5.js.map │ │ ├── main.ba894f64.js │ │ ├── main.ba894f64.js.map │ │ ├── message_list.16168fad.js │ │ └── message_list.16168fad.js.map ├── index.js ├── middleware │ ├── auth.js │ └── resource.js ├── models │ ├── AdminUser.js │ ├── Article.js │ ├── Category.js │ ├── Comment.js │ ├── Link.js │ ├── Message.js │ └── User.js ├── package.json ├── plugins │ ├── db.js │ └── sendEmail.js ├── pnpm-lock.yaml ├── routes │ ├── admin │ │ └── index.js │ └── web │ │ └── index.js └── web │ ├── css │ ├── about.bb042f05.css │ ├── about.bb042f05.css.gz │ ├── app.ffda094d.css │ ├── app.ffda094d.css.gz │ ├── archive.55ff8245.css │ ├── article.9ef0ae68.css │ ├── home.475a08fb.css │ ├── home.475a08fb.css.gz │ ├── link.c7fe52ac.css │ ├── message.208c449f.css │ ├── tag.bcfa3e2a.css │ ├── tag.bcfa3e2a.css.gz │ ├── vendors~app.3a4b7974.css │ ├── vendors~app.3a4b7974.css.gz │ └── vendors~article.1c2c36e5.css │ ├── favicon.ico │ ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf │ ├── img │ └── iconfont.a22d368f.svg │ ├── index.html │ └── js │ ├── about.75b89ddb.js │ ├── about.75b89ddb.js.gz │ ├── app.c4457924.js │ ├── app.c4457924.js.gz │ ├── archive.845f534c.js │ ├── archive.845f534c.js.gz │ ├── article.b4d913a8.js │ ├── article.b4d913a8.js.gz │ ├── home.6e81e34d.js │ ├── home.6e81e34d.js.gz │ ├── link.2206432e.js │ ├── link.2206432e.js.gz │ ├── message.dcf21bf0.js │ ├── message.dcf21bf0.js.gz │ ├── tag.4428a61a.js │ ├── tag.4428a61a.js.gz │ ├── vendors~app.38cd00d7.js │ ├── vendors~app.38cd00d7.js.LICENSE.txt │ ├── vendors~app.38cd00d7.js.gz │ ├── vendors~article.b06189d7.js │ ├── vendors~article.b06189d7.js.gz │ ├── vendors~article~home.db3e17e7.js │ ├── vendors~article~home.db3e17e7.js.LICENSE.txt │ └── vendors~article~home.db3e17e7.js.gz └── web ├── .env.development ├── .gitignore ├── README.md ├── package.json ├── pnpm-lock.yaml ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── iconfont │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.json │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── images │ │ ├── bg-blog.png │ │ ├── git │ │ │ └── git-1.jpg │ │ ├── learn1 │ │ │ ├── learn1-0.jpg │ │ │ ├── learn1-2.jpg │ │ │ ├── learn1-3.jpg │ │ │ └── learn1-4.jpg │ │ ├── logo.png │ │ └── this │ │ │ ├── this-1.jpg │ │ │ └── this-2.png │ └── scss │ │ ├── _variables.scss │ │ └── style.scss ├── commentConfig.js ├── components │ ├── Footer.vue │ ├── Header.vue │ ├── Search.vue │ ├── commentList.vue │ ├── commentTextarea.vue │ ├── formInput.vue │ ├── linkItem.vue │ └── snow.vue ├── http.js ├── main.js ├── plugins │ ├── Toc.js │ ├── filters.js │ ├── global.js │ ├── lineAndCopy.js │ ├── reg.js │ └── validate.js ├── router │ └── index.js ├── store │ └── index.js └── views │ ├── About.vue │ ├── Archive.vue │ ├── Article.vue │ ├── Home.vue │ ├── Link.vue │ ├── Main.vue │ ├── Message.vue │ ├── Search.vue │ └── Tag.vue └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .idea 107 | 108 | .idea/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "javascript.format.insertSpaceBeforeFunctionParenthesis": true, 5 | "eslintIntegration": true, 6 | "htmlWhitespaceSensitivity": "ignore", 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 米淇淋 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 | ### 环境准备 2 | 3 | - Node.js v14.15.5 4 | - MongoDB v4.2.17 5 | 6 | ### 安装MongoDB数据库 7 | 8 | 进入[官网](https://www.mongodb.com/try/download/community)下载,版本是4.2.17,平台是Windows,安装包为msi格式 9 | 10 | ### 项目开发 11 | 12 | #### 1. 克隆项目至本地: 13 | 14 | ```sh 15 | $ git clone git@github.com:miqilin21/vue-blog.git 16 | $ cd vue-blog 17 | $ pnpm i 18 | ``` 19 | 20 | #### 2. 安装前后台(admin、web)及后端(server)项目依赖: 21 | 22 | ```sh 23 | $ pnpm install-all 24 | ``` 25 | 26 | #### 3. 运行前后台(admin、web)及后端(server)项目: 27 | 28 | ```sh 29 | $ pnpm serve-all 30 | ``` 31 | 32 | #### 4. 打开浏览器预览: 33 | 34 | http://localhost:8080 打开前台本地页面;http://localhost:8088 打开后台本地页面。 35 | 36 | 第一次后台admin登陆时请先注册用户,登录后可以将注册按钮隐藏以防登录账户泄露。 37 | 38 | ### 后台管理系统 39 | 40 | - 新增分类 41 | - 分类列表(增删改查) 42 | 43 | - 新增文章 44 | - 文章列表(增删改查) 45 | 46 | - 文章评论列表 47 | 48 | - 新增友链 49 | - 友链列表(增删改查) 50 | 51 | - 留言列表 52 | 53 | - 留言用户列表 54 | 55 | - Markdown 编辑器集成 56 | - 图片上传功能 57 | 58 | - 新增用户 59 | - 用户列表(增删改查) 60 | 61 | - 通用增删改查接口实现(中间件 resourceMiddleware) 62 | 63 | - 登录注册功能 64 | - 登出功能 65 | - 添加 jwt 校验(1. 用户名查找 2. 密码校验 3. 返回 token) 66 | - 添加 http 拦截器(request、response) 67 | 68 | ### 博客系统 69 | 70 | - 博客首页 UI 71 | - 博客首页接口实现 72 | - 博客归档页面 UI 73 | - 博客归档页面接口实现 74 | - 博客标签页面 UI 75 | - 博客标签页面接口实现 76 | - 博客文章页面 UI 77 | - 博客文章页面接口实现 78 | - 博客友链页面 UI 79 | - 博客友链页面接口实现 80 | - 博客留言页面 UI 81 | - 博客留言页面接口实现 82 | 83 | ### 配置注意点 84 | 85 | 1. 评论页面回复他人评论采用的是你的QQ邮箱进行同步通知,需要在 `server/plugins/sendEmail.js` 中进行邮箱配置,如何获取QQ邮箱授权码见下图(在QQ邮箱的设置-账户页面)。 86 | 87 | ![](https://miqilin-blog.oss-cn-shenzhen.aliyuncs.com/qq-shouquanma.png) 88 | 89 | 2. 评论主账户的配置(主账户用于邮箱回复,所以评论建立的账户邮箱要和上一步QQ邮箱授权码的邮箱为同一个),同时需要在 `web/src/commentConfig.js` 中进行评论账户的设置(注意`topNickName`需要和评论建立的主账号昵称一致,`topParentId`可从后台页面获取),主账户被清除的话要重新进行配置。 90 | 3. 关于后台的图片上传:默认是上传到本地,这种方式的话对于服务器要求比较高,自然前端的加载速度会受影响,可以选择用阿里云的oss进行上传及储存,需要将 `server/routes/admin/index.js` 中关于阿里云oss图片上传的代码注释解除,同时将本地图片上传的代码注释掉,最后将你的阿里云oss配置填入即可。 91 | 92 | ### 项目打包部署 93 | 94 | - web、admin 项目打包(根目录下运行 `pnpm build-all` 命令) 95 | - 域名购买 96 | - 域名解析 97 | - linux 云服务器购买 98 | - git 安装, ssh key 添加 99 | - pm2 进程管理 100 | - ngnix 配置 101 | - mongodb 配置 102 | - mongodb 数据从本地迁移至服务器 103 | - 七牛云 cdn 全站免费加速 104 | 105 | 线上服务器部署可参考这份[指南](https://www.yuque.com/lingqian-ceavu/gxhqpr)。 106 | 107 | ### 博客后台管理系统 108 | 109 | ![](https://miqilin-blog.oss-cn-shenzhen.aliyuncs.com/admin.gif) 110 | 111 | ### 博客系统 112 | 113 | ![](https://miqilin-blog.oss-cn-shenzhen.aliyuncs.com/web.gif) 114 | 115 | ### 欢迎 Star,Issues 116 | 117 | - 您的 Star,是我不断更新维护的动力!!! 118 | - 若在使用过程中,存在某些问题,欢迎 Issues 119 | 120 | ### To Do List 121 | 122 | - 性能优化,图片需要压缩处理,预加载处理 123 | - 给博文页面加上搜索功能 124 | - 浏览器兼容问题,移动端适配。只用了自己电脑的谷歌 chrome 浏览器开发和手机适配效果,其他种种都没试,不过不建议移动端使用,电脑登陆效果更佳 125 | - 解决 vue 的 seo 问题及刷新渲染页面抖动问题。先暂时做 vue 预渲染,不排除以后会做 vue 服务端渲染或 nuxt.js 服务端渲染 126 | - 整理代码,拆分出组件。让结构更清晰,代码量更少 127 | - 利用 typescript 重构整个项目 128 | - 想到有意思的功能也会慢慢加上,持续开发中... 129 | -------------------------------------------------------------------------------- /admin/.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL = http://localhost:3000/admin/api -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /admin/README.md: -------------------------------------------------------------------------------- 1 | # admin 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "0.24.0", 12 | "dayjs": "1.10.7", 13 | "element-ui": "2.15.1", 14 | "mavon-editor": "2.10.4", 15 | "vue": "2.6.14", 16 | "vue-router": "3.5.1" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-eslint": "4.5.13", 20 | "@vue/cli-service": "4.5.13", 21 | "babel-eslint": "10.0.3", 22 | "eslint": "6.7.2", 23 | "eslint-plugin-vue": "6.1.2", 24 | "sass": "1.26.3", 25 | "sass-loader": "8.0.2", 26 | "vue-loader": "17.0.0", 27 | "vue-template-compiler": "2.6.14" 28 | }, 29 | "eslintConfig": { 30 | "root": true, 31 | "env": { 32 | "node": true 33 | }, 34 | "extends": [ 35 | "plugin:vue/essential", 36 | "eslint:recommended" 37 | ], 38 | "parserOptions": { 39 | "parser": "babel-eslint" 40 | }, 41 | "rules": {} 42 | }, 43 | "browserslist": [ 44 | "> 1%", 45 | "last 2 versions" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/public/favicon.ico -------------------------------------------------------------------------------- /admin/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 米淇淋的博客-后台管理 10 | 11 | 12 | 13 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /admin/src/assets/linkimage/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/linkimage/favicon.png -------------------------------------------------------------------------------- /admin/src/assets/linkimage/zhihu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/linkimage/zhihu.png -------------------------------------------------------------------------------- /admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/logo.png -------------------------------------------------------------------------------- /admin/src/assets/postimage/closure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/closure.jpg -------------------------------------------------------------------------------- /admin/src/assets/postimage/english.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/english.jpg -------------------------------------------------------------------------------- /admin/src/assets/postimage/git.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/git.jpg -------------------------------------------------------------------------------- /admin/src/assets/postimage/learn1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/learn1.jpg -------------------------------------------------------------------------------- /admin/src/assets/postimage/null.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/null.jpg -------------------------------------------------------------------------------- /admin/src/assets/postimage/this.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/admin/src/assets/postimage/this.jpg -------------------------------------------------------------------------------- /admin/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 94 | 95 | 103 | 104 | 105 | 121 | -------------------------------------------------------------------------------- /admin/src/components/MTopHandle.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 38 | 39 | 48 | -------------------------------------------------------------------------------- /admin/src/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Vue from 'vue' 3 | import router from './router' 4 | 5 | const http = axios.create({ 6 | baseURL: process.env.VUE_APP_API_URL || '/admin/api', 7 | // baseURL: 'http://localhost:3000/admin/api' 8 | }) 9 | 10 | http.interceptors.request.use( 11 | function (config) { 12 | // Do something before request is sent 13 | if (localStorage.token) { 14 | config.headers.Authorization = 'Bearer ' + localStorage.token 15 | } 16 | return config 17 | }, 18 | function (error) { 19 | // Do something with request error 20 | return Promise.reject(error) 21 | } 22 | ) 23 | http.interceptors.response.use( 24 | (res) => { 25 | return res 26 | }, 27 | (err) => { 28 | if (err.response.data.message) { 29 | Vue.prototype.$message({ 30 | type: 'error', 31 | message: err.response.data.message, 32 | }) 33 | 34 | if (err.response.status === 401) { 35 | router.push('/login') 36 | } 37 | } 38 | 39 | return Promise.reject(err) 40 | } 41 | ) 42 | 43 | export default http 44 | -------------------------------------------------------------------------------- /admin/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import ElementUI from "element-ui"; 5 | import "element-ui/lib/theme-chalk/index.css"; 6 | import "./style.css"; 7 | import './plugins/filters' 8 | 9 | Vue.config.productionTip = false; 10 | Vue.use(ElementUI); 11 | 12 | import http from "./http"; 13 | Vue.prototype.$http = http; 14 | import MTopHandle from './components/MTopHandle' 15 | Vue.component('MTopHandle', MTopHandle) 16 | Vue.mixin({ 17 | computed: { 18 | uploadUrl() { 19 | return this.$http.defaults.baseURL + "/upload"; 20 | } 21 | }, 22 | methods: { 23 | getAuthHeaders() { 24 | return { 25 | Authorization: `Bearer ${localStorage.token || ""}` 26 | }; 27 | } 28 | } 29 | }); 30 | 31 | new Vue({ 32 | router, 33 | render: h => h(App), 34 | }).$mount("#app"); 35 | -------------------------------------------------------------------------------- /admin/src/plugins/filters.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import dayjs from 'dayjs' 3 | 4 | Vue.filter('date', (val, type) => { 5 | if (!val) return ''; 6 | 7 | return dayjs(val).format(type) 8 | }) -------------------------------------------------------------------------------- /admin/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const router = new VueRouter({ 7 | routes: [{ 8 | path: "/login", 9 | name: "login", 10 | component: () => import( /* webpackChunkName: "login" */ '../views/Login.vue'), 11 | meta: { 12 | isPublic: true 13 | } 14 | }, 15 | { 16 | path: "/", 17 | name: "main", 18 | component: () => import( /* webpackChunkName: "main" */ '../views/Main.vue'), 19 | children: [{ 20 | path: "/categories/create", 21 | component: () => import( /* webpackChunkName: "category_edit" */ '../views/CategoryEdit.vue'), 22 | }, 23 | { 24 | path: "/categories/edit/:id", 25 | component: () => import( /* webpackChunkName: "category_edit" */ '../views/CategoryEdit.vue'), 26 | props: true 27 | }, 28 | { 29 | path: "/categories/list", 30 | component: () => import( /* webpackChunkName: "category_list" */ '../views/CategoryList.vue'), 31 | }, 32 | { 33 | path: "/articles/create", 34 | component: () => import( /* webpackChunkName: "article_edit" */ '../views/ArticleEdit.vue'), 35 | }, 36 | { 37 | path: "/articles/edit/:id", 38 | component: () => import( /* webpackChunkName: "article_edit" */ '../views/ArticleEdit.vue'), 39 | props: true 40 | }, 41 | { 42 | path: "/articles/list", 43 | component: () => import( /* webpackChunkName: "article_list" */ '../views/ArticleList.vue'), 44 | }, 45 | { 46 | path: "/comments/list", 47 | component: () => import( /* webpackChunkName: "comment_list" */ '../views/CommentList.vue'), 48 | props: true 49 | }, 50 | { 51 | path: "/links/create", 52 | component: () => import( /* webpackChunkName: "link_edit" */ '../views/LinkEdit.vue'), 53 | }, 54 | { 55 | path: "/links/edit/:id", 56 | component: () => import( /* webpackChunkName: "link_edit" */ '../views/LinkEdit.vue'), 57 | props: true 58 | }, 59 | { 60 | path: "/links/list", 61 | component: () => import( /* webpackChunkName: "link_list" */ '../views/LinkList.vue'), 62 | }, 63 | { 64 | path: "/messages/list", 65 | component: () => import( /* webpackChunkName: "message_list" */ '../views/MessageList.vue'), 66 | }, 67 | { 68 | path: "/users/list", 69 | component: () => import( /* webpackChunkName: "link_list" */ '../views/UserList.vue'), 70 | }, 71 | { 72 | path: "/admin_users/create", 73 | component: () => import( /* webpackChunkName: "admin_edit" */ '../views/AdminUserEdit.vue'), 74 | }, 75 | { 76 | path: "/admin_users/edit/:id", 77 | component: () => import( /* webpackChunkName: "admin_edit" */ '../views/AdminUserEdit.vue'), 78 | props: true 79 | }, 80 | { 81 | path: "/admin_users/list", 82 | component: () => import( /* webpackChunkName: "admin_list" */ '../views/AdminUserList.vue'), 83 | } 84 | ] 85 | } 86 | ] 87 | }); 88 | 89 | router.beforeEach((to, from, next) => { 90 | if (!to.meta.isPublic && !localStorage.token) { 91 | return next('/login') 92 | } 93 | next() 94 | }) 95 | export default router; -------------------------------------------------------------------------------- /admin/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: {}, 8 | mutations: {}, 9 | actions: {}, 10 | modules: {} 11 | }) -------------------------------------------------------------------------------- /admin/src/style.css: -------------------------------------------------------------------------------- 1 | .avatar-uploader .el-upload { 2 | border: 1px dashed #d9d9d9; 3 | border-radius: 6px; 4 | cursor: pointer; 5 | position: relative; 6 | overflow: hidden; 7 | } 8 | 9 | .avatar-uploader .el-upload:hover { 10 | border-color: #409eff; 11 | } 12 | 13 | .avatar-uploader-icon { 14 | font-size: 28px; 15 | color: #8c939d; 16 | min-width: 5rem; 17 | min-height: 5rem; 18 | line-height: 5rem !important; 19 | text-align: center; 20 | } 21 | 22 | .avatar { 23 | max-width: 30rem; 24 | max-height: 30rem; 25 | display: block; 26 | } -------------------------------------------------------------------------------- /admin/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /admin/src/views/AdminUserEdit.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 51 | -------------------------------------------------------------------------------- /admin/src/views/AdminUserList.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 70 | -------------------------------------------------------------------------------- /admin/src/views/ArticleEdit.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 108 | -------------------------------------------------------------------------------- /admin/src/views/ArticleList.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 85 | -------------------------------------------------------------------------------- /admin/src/views/CategoryEdit.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 48 | -------------------------------------------------------------------------------- /admin/src/views/CategoryList.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 70 | -------------------------------------------------------------------------------- /admin/src/views/CommentList.vue: -------------------------------------------------------------------------------- 1 | 26 | 66 | -------------------------------------------------------------------------------- /admin/src/views/LinkEdit.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 67 | -------------------------------------------------------------------------------- /admin/src/views/LinkList.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 77 | -------------------------------------------------------------------------------- /admin/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 57 | 58 | 70 | -------------------------------------------------------------------------------- /admin/src/views/Main.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 82 | 83 | 101 | -------------------------------------------------------------------------------- /admin/src/views/MessageList.vue: -------------------------------------------------------------------------------- 1 | 26 | 66 | -------------------------------------------------------------------------------- /admin/src/views/UserList.vue: -------------------------------------------------------------------------------- 1 | 56 | 131 | -------------------------------------------------------------------------------- /admin/vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.json 2 | module.exports = { 3 | devServer: { 4 | port: 8088, 5 | }, 6 | outputDir: __dirname + '/../server/admin', 7 | publicPath: process.env.NODE_ENV === 'production' ? 8 | '/admin/' : '/' 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-node-blog", 3 | "version": "1.0.0", 4 | "description": "- vue-cli v4.5.13\r - Node.js v14.15.5\r - MongoDB v4.2.17", 5 | "scripts": { 6 | "serve-all": "run-p web-serve admin-serve server-serve", 7 | "server-serve": "cd server && pnpm serve", 8 | "web-serve": "cd web && pnpm serve", 9 | "admin-serve": "cd admin && pnpm serve", 10 | "install-all": "run-p web-install admin-install server-install", 11 | "server-install": "cd server && pnpm i", 12 | "web-install": "cd web && pnpm i", 13 | "admin-install": "cd admin && pnpm i", 14 | "build-all": "run-p web-build admin-build", 15 | "web-build": "cd web && pnpm build", 16 | "admin-build": "cd admin && pnpm build" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/zonemeen/vue-node-blog.git" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/zonemeen/vue-node-blog/issues" 26 | }, 27 | "homepage": "https://github.com/zonemeen/vue-node-blog#readme", 28 | "devDependencies": { 29 | "npm-run-all": "4.1.5", 30 | "prettier": "2.5.1" 31 | } 32 | } -------------------------------------------------------------------------------- /screenshot/admin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/screenshot/admin.gif -------------------------------------------------------------------------------- /screenshot/web.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/screenshot/web.gif -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | uploads 4 | /dist 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? -------------------------------------------------------------------------------- /server/admin/css/app.b98fe089.css: -------------------------------------------------------------------------------- 1 | body,html{margin:0;padding:0}.avatar-uploader .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload:hover{border-color:#409eff}.avatar-uploader-icon{font-size:28px;color:#8c939d;min-width:5rem;min-height:5rem;line-height:5rem!important;text-align:center}.avatar{max-width:30rem;max-height:30rem;display:block}.top-box[data-v-00cf1984]{margin-bottom:30px}.top-box .search-input[data-v-00cf1984]{width:300px;margin-right:10px} -------------------------------------------------------------------------------- /server/admin/css/article_edit.33f6816a.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:fontello;src:url(../fonts/fontello.e73a0647.eot);src:url(../fonts/fontello.e73a0647.eot#iefix) format("embedded-opentype"),url(../fonts/fontello.8d4a4e6f.woff2) format("woff2"),url(../fonts/fontello.a782baa8.woff) format("woff"),url(../fonts/fontello.068ca2b3.ttf) format("truetype"),url(../img/fontello.9354499c.svg#fontello) format("svg");font-weight:400;font-style:normal}[class*=" fa-mavon-"]:before,[class^=fa-mavon-]:before{font-family:fontello;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-mavon-bold:before{content:"\E800"}.fa-mavon-italic:before{content:"\E801"}.fa-mavon-thumb-tack:before{content:"\E802"}.fa-mavon-link:before{content:"\E803"}.fa-mavon-picture-o:before{content:"\E804"}.fa-mavon-repeat:before{content:"\E805"}.fa-mavon-undo:before{content:"\E806"}.fa-mavon-trash-o:before{content:"\E807"}.fa-mavon-floppy-o:before{content:"\E808"}.fa-mavon-compress:before{content:"\E809"}.fa-mavon-eye:before{content:"\E80A"}.fa-mavon-eye-slash:before{content:"\E80B"}.fa-mavon-question-circle:before{content:"\E80C"}.fa-mavon-times:before{content:"\E80D"}.fa-mavon-align-left:before{content:"\E80F"}.fa-mavon-align-center:before{content:"\E810"}.fa-mavon-align-right:before{content:"\E811"}.fa-mavon-arrows-alt:before{content:"\F0B2"}.fa-mavon-bars:before{content:"\F0C9"}.fa-mavon-list-ul:before{content:"\F0CA"}.fa-mavon-list-ol:before{content:"\F0CB"}.fa-mavon-strikethrough:before{content:"\F0CC"}.fa-mavon-underline:before{content:"\F0CD"}.fa-mavon-table:before{content:"\F0CE"}.fa-mavon-columns:before{content:"\F0DB"}.fa-mavon-quote-left:before{content:"\F10D"}.fa-mavon-code:before{content:"\F121"}.fa-mavon-superscript:before{content:"\F12B"}.fa-mavon-subscript:before{content:"\F12C"}.fa-mavon-header:before{content:"\F1DC"}.fa-mavon-window-maximize:before{content:"\F2D0"}.markdown-body strong{font-weight:bolder}.markdown-body .hljs-center{text-align:center}.markdown-body .hljs-right{text-align:right}.markdown-body .hljs-left{text-align:left}.markdown-body .hljs{overflow:auto} -------------------------------------------------------------------------------- /server/admin/css/login.d3cd1897.css: -------------------------------------------------------------------------------- 1 | .page-login .card-view[data-v-60cb1fb3]{width:30%;padding:2rem;margin:8% auto}.page-login .card-view .card-body[data-v-60cb1fb3]{margin-top:2rem} -------------------------------------------------------------------------------- /server/admin/css/main.b0303dc3.css: -------------------------------------------------------------------------------- 1 | .el-header{background-color:#69a8e7;color:#333;line-height:60px}.el-aside{color:#333} -------------------------------------------------------------------------------- /server/admin/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/favicon.ico -------------------------------------------------------------------------------- /server/admin/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /server/admin/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /server/admin/fonts/fontello.068ca2b3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/fontello.068ca2b3.ttf -------------------------------------------------------------------------------- /server/admin/fonts/fontello.8d4a4e6f.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/fontello.8d4a4e6f.woff2 -------------------------------------------------------------------------------- /server/admin/fonts/fontello.a782baa8.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/fontello.a782baa8.woff -------------------------------------------------------------------------------- /server/admin/fonts/fontello.e73a0647.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/admin/fonts/fontello.e73a0647.eot -------------------------------------------------------------------------------- /server/admin/index.html: -------------------------------------------------------------------------------- 1 | 米淇淋的博客-后台管理
-------------------------------------------------------------------------------- /server/admin/js/main.ba894f64.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["main"],{"22de":function(e,t,l){"use strict";l("8319")},8319:function(e,t,l){},cd56:function(e,t,l){"use strict";l.r(t);var i=function(){var e=this,t=e.$createElement,l=e._self._c||t;return l("el-container",{staticStyle:{height:"100vh",border:"1px solid #eee"}},[l("el-aside",{staticStyle:{"background-color":"rgb(238, 241, 246)"},attrs:{width:"200px"}},[l("el-menu",{attrs:{router:"","default-active":e.$route.path}},[l("el-submenu",{attrs:{index:"1"}},[l("template",{slot:"title"},[l("i",{staticClass:"el-icon-message"}),e._v("内容管理 ")]),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("分类")]),l("el-menu-item",{attrs:{index:"/categories/create"}},[e._v("新建分类")]),l("el-menu-item",{attrs:{index:"/categories/list"}},[e._v("分类列表")])],2),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("文章")]),l("el-menu-item",{attrs:{index:"/articles/create"}},[e._v("新建文章")]),l("el-menu-item",{attrs:{index:"/articles/list"}},[e._v("文章列表")])],2),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("文章评论")]),l("el-menu-item",{attrs:{index:"/comments/list"}},[e._v("评论列表")])],2),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("友链")]),l("el-menu-item",{attrs:{index:"/links/create"}},[e._v("新建友链")]),l("el-menu-item",{attrs:{index:"/links/list"}},[e._v("友链列表")])],2),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("留言")]),l("el-menu-item",{attrs:{index:"/messages/list"}},[e._v("留言列表")])],2),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("留言评论用户")]),l("el-menu-item",{attrs:{index:"/users/list"}},[e._v("留言评论用户列表")])],2)],2),l("el-submenu",{attrs:{index:"2"}},[l("template",{slot:"title"},[l("i",{staticClass:"el-icon-message"}),e._v("系统设置 ")]),l("el-menu-item-group",[l("template",{slot:"title"},[e._v("管理员")]),l("el-menu-item",{attrs:{index:"/admin_users/create"}},[e._v("新建管理员")]),l("el-menu-item",{attrs:{index:"/admin_users/list"}},[e._v("管理员列表")])],2)],2)],1)],1),l("el-container",[l("el-header",{staticStyle:{"text-align":"right","font-size":"12px"}},[l("span",[e._v(e._s(e.username))]),l("el-dropdown",[l("i",{staticClass:"el-icon-setting",staticStyle:{"margin-left":"10px"}}),l("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[l("el-dropdown-item",[l("a",{on:{click:e.logout}},[e._v("退出")])])],1)],1)],1),l("el-main",[l("router-view",{key:e.$route.path})],1)],1)],1)},s=[],n={data:function(){return{username:""}},methods:{logout:function(){localStorage.clear(),this.$router.push("/login")}},created:function(){this.username=localStorage.getItem("username")||""}},a=n,r=(l("22de"),l("2877")),o=Object(r["a"])(a,i,s,!1,null,null,null);t["default"]=o.exports}}]); 2 | //# sourceMappingURL=main.ba894f64.js.map -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const cors = require('cors') 3 | const app = express() 4 | 5 | app.set('secret', 'i2u34y12oi3u4y8') 6 | 7 | app.use(cors()) 8 | app.use(express.json()) 9 | app.use('/uploads', express.static(__dirname + '/uploads')) 10 | app.use('/', express.static(__dirname + '/web')) 11 | app.use('/admin', express.static(__dirname + '/admin')) 12 | 13 | require('./plugins/db')(app) 14 | require('./routes/admin/index.js')(app) 15 | require('./routes/web/index.js')(app) 16 | 17 | app.listen(3000, async (req, res) => { 18 | console.log('http://localhost:3000') 19 | }) 20 | -------------------------------------------------------------------------------- /server/middleware/auth.js: -------------------------------------------------------------------------------- 1 | module.exports = options => { 2 | const assert = require('http-assert') 3 | const jwt = require('jsonwebtoken') 4 | const AdminUser = require('../models/AdminUser') 5 | 6 | return async (req, res, next) => { 7 | const token = String(req.headers.authorization || '').split(' ').pop() 8 | assert(token, 401, '请先登录') 9 | const { 10 | id 11 | } = jwt.verify(token, req.app.get('secret')) 12 | assert(id, 401, '请先登录') 13 | req.user = await AdminUser.findById(id) 14 | assert(req.user, 401, '请先登录') 15 | await next() 16 | } 17 | } -------------------------------------------------------------------------------- /server/middleware/resource.js: -------------------------------------------------------------------------------- 1 | module.exports = options => { 2 | return async (req, res, next) => { 3 | const modelName = require('inflection').classify(req.params.resource) 4 | req.Model = require(`../models/${modelName}`) 5 | next() 6 | } 7 | } -------------------------------------------------------------------------------- /server/models/AdminUser.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require('bcryptjs') 3 | 4 | const schema = new mongoose.Schema({ 5 | username: { 6 | type: String, 7 | unique: true 8 | }, 9 | password: { 10 | type: String, 11 | select: false, 12 | set(val) { 13 | return bcrypt.hashSync(val, 10); 14 | } 15 | } 16 | }); 17 | 18 | module.exports = mongoose.model("AdminUser", schema); -------------------------------------------------------------------------------- /server/models/Article.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const schema = new mongoose.Schema({ 4 | categories: [{ 5 | type: mongoose.SchemaTypes.ObjectId, 6 | ref: 'Category' 7 | }], 8 | title: { 9 | type: String 10 | }, 11 | icon: { 12 | type: String 13 | }, 14 | body: { 15 | type: String 16 | }, 17 | }, { 18 | timestamps: true 19 | }) 20 | 21 | module.exports = mongoose.model('Article', schema) -------------------------------------------------------------------------------- /server/models/Category.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const schema = new mongoose.Schema({ 4 | name: { 5 | type: String 6 | } 7 | }) 8 | 9 | // schema.virtual('articlesList', { 10 | // localField: '_id', 11 | // foreignField: 'categories', 12 | // justOne: false, 13 | // ref: 'Article' 14 | // }) 15 | 16 | module.exports = mongoose.model('Category', schema) -------------------------------------------------------------------------------- /server/models/Comment.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const schema = new mongoose.Schema({ 3 | nickName: { 4 | type: String 5 | }, 6 | avatarImg: { 7 | type: String 8 | }, 9 | content: { 10 | type: String 11 | }, 12 | userId: { 13 | type: mongoose.SchemaTypes.ObjectId, 14 | ref: 'User' 15 | }, 16 | relateBlogId: { 17 | type: mongoose.SchemaTypes.ObjectId, 18 | ref: 'Article' 19 | }, 20 | parent: { 21 | type: mongoose.SchemaTypes.ObjectId, 22 | ref: 'Comment' 23 | }, 24 | byAiteName: { 25 | type: String 26 | }, 27 | }, { 28 | timestamps: true 29 | }) 30 | module.exports = mongoose.model('Comment', schema) -------------------------------------------------------------------------------- /server/models/Link.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const schema = new mongoose.Schema({ 4 | name: { 5 | type: String 6 | }, 7 | site: { 8 | type: String 9 | }, 10 | description: { 11 | type: String 12 | }, 13 | icon: { 14 | type: String 15 | } 16 | }) 17 | 18 | module.exports = mongoose.model('Link', schema) -------------------------------------------------------------------------------- /server/models/Message.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const schema = new mongoose.Schema({ 3 | nickName: { 4 | type: String 5 | }, 6 | avatarImg: { 7 | type: String 8 | }, 9 | content: { 10 | type: String 11 | }, 12 | userId: { 13 | type: mongoose.SchemaTypes.ObjectId, 14 | ref: 'User' 15 | }, 16 | parent: { 17 | type: mongoose.SchemaTypes.ObjectId, 18 | ref: 'Message' 19 | }, 20 | byAiteName: { 21 | type: String 22 | }, 23 | }, { 24 | timestamps: true 25 | }) 26 | module.exports = mongoose.model('Message', schema) -------------------------------------------------------------------------------- /server/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const schema = new mongoose.Schema({ 3 | nickName: { 4 | type: String, 5 | }, 6 | email: { 7 | type: String 8 | }, 9 | url: { 10 | type: String 11 | }, 12 | avatarImg: { 13 | type: String 14 | } 15 | }, { 16 | timestamps: true 17 | }) 18 | module.exports = mongoose.model('User', schema) -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "serve": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "2.4.3", 14 | "cors": "2.8.5", 15 | "express": "4.17.1", 16 | "http-assert": "1.4.1", 17 | "inflection": "1.12.0", 18 | "jsonwebtoken": "8.5.1", 19 | "mongoose": "5.13.14", 20 | "multer": "1.4.4", 21 | "multer-aliyun-oss": "1.1.1", 22 | "nodemailer": "6.7.2", 23 | "nodemailer-smtp-transport": "2.7.4", 24 | "nodemon": "2.0.15", 25 | "require-all": "3.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/plugins/db.js: -------------------------------------------------------------------------------- 1 | module.exports = (app) => { 2 | const mongoose = require("mongoose"); 3 | mongoose.connect("mongodb://127.0.0.1:27017/blog", { 4 | useNewUrlParser: true, 5 | useFindAndModify: true, 6 | useUnifiedTopology: true, 7 | useCreateIndex: true, 8 | useFindAndModify: true 9 | }); 10 | require("require-all")(__dirname + "/../models"); 11 | }; 12 | -------------------------------------------------------------------------------- /server/plugins/sendEmail.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @Description 邮件发送 4 | * 调用方法:sendMail('994718917@qq.com','这是测试邮件', 'Hi miqilin,这是一封测试邮件'); 5 | * @Author miqilin 6 | * @Created 2020/04/05 15:10 7 | * 技术只是解决问题的选择,而不是解决问题的根本... 8 | * 9 | */ 10 | 11 | const nodemailer = require('nodemailer') 12 | const smtpTransport = require('nodemailer-smtp-transport') 13 | const config = { 14 | email: { 15 | service: 'QQ', // 邮箱类别,我固定QQ 16 | user: 'xxxxxxxxxx@qq.com', // 你的QQ邮箱,用来发送回复邮件 17 | pass: 'xxxxxxxxxxxxx', // 你的QQ邮箱授权码,如何操作获取见README.md所述 18 | }, 19 | } 20 | 21 | sendSmtpTransport = nodemailer.createTransport( 22 | smtpTransport({ 23 | service: config.email.service, 24 | auth: { 25 | user: config.email.user, 26 | pass: config.email.pass, 27 | }, 28 | }) 29 | ) 30 | 31 | /** 32 | * @param {String} recipient 收件人 33 | * @param {String} subject 发送的主题 34 | * @param {String} html 发送的html内容 35 | */ 36 | var sendMail = function (replyObj) { 37 | sendSmtpTransport.sendMail( 38 | { 39 | from: config.email.user, 40 | to: replyObj.recipient, 41 | subject: `${replyObj.subject} 你好! 你有新邮件了, 请查收`, 42 | html: replyObj.html, 43 | }, 44 | function (error, response) { 45 | if (error) { 46 | console.log(error) 47 | } 48 | console.log('发送成功') 49 | } 50 | ) 51 | } 52 | 53 | module.exports = sendMail 54 | -------------------------------------------------------------------------------- /server/routes/admin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (app) => { 2 | const express = require('express') 3 | const assert = require('http-assert') 4 | const jwt = require('jsonwebtoken') 5 | const AdminUser = require('../../models/AdminUser') 6 | const sendEmail = require('../../plugins/sendEmail.js') 7 | const router = express.Router({ 8 | mergeParams: true, 9 | }) 10 | router.post('/', async (req, res) => { 11 | const model = await req.Model.create(req.body) 12 | res.send(model) 13 | }) 14 | router.put('/:id', async (req, res) => { 15 | const model = await req.Model.findByIdAndUpdate(req.params.id, req.body) 16 | res.send(model) 17 | }) 18 | router.delete('/:id', async (req, res) => { 19 | await req.Model.findByIdAndDelete(req.params.id, req.body) 20 | res.send({ 21 | success: true, 22 | }) 23 | }) 24 | router.get('/', async (req, res) => { 25 | const queryOptions = {} 26 | if (req.Model.modelName === 'Category') { 27 | queryOptions.populate = 'parent' 28 | } 29 | const items = await req.Model.find().setOptions(queryOptions).limit(100) 30 | res.send(items) 31 | }) 32 | router.get('/:id', async (req, res) => { 33 | const model = await req.Model.findById(req.params.id) 34 | res.send(model) 35 | }) 36 | 37 | //登录校验中间件 38 | const authMiddleware = require('../../middleware/auth') 39 | 40 | //资源中间件 41 | const resourceMiddleware = require('../../middleware/resource') 42 | 43 | //资源路由 44 | app.use( 45 | '/admin/api/rest/:resource', 46 | authMiddleware(), 47 | resourceMiddleware(), 48 | router 49 | ) 50 | 51 | /* //用于阿里云oss图片上传 52 | const multer = require('multer') 53 | const MAO = require('multer-aliyun-oss') 54 | const upload = multer({ 55 | storage: MAO({ 56 | config: { 57 | region: 'your region', // 阿里云oss的所在区域,比如oss-cn-shenzhen 58 | accessKeyId: 'your accessKeyId', // 阿里云oss的accessKeyId,要自己去创建 59 | accessKeySecret: 'your accessKeySecret', // 阿里云oss的accessKeySecret 60 | bucket: 'your bucket name', // 阿里云oss的bucket's name 61 | }, 62 | }), 63 | }) 64 | app.post( 65 | '/admin/api/upload', 66 | authMiddleware(), 67 | upload.single('file'), 68 | async (req, res) => { 69 | const file = req.file 70 | res.send(file) 71 | } 72 | )*/ 73 | 74 | //本地图片上传 75 | const multer = require('multer') 76 | const upload = multer({ 77 | dest: __dirname + '/../../uploads', 78 | }) 79 | app.post( 80 | '/admin/api/upload', 81 | authMiddleware(), 82 | upload.single('file'), 83 | async (req, res) => { 84 | const file = req.file 85 | file.url = `http://localhost:3000/uploads/${file.filename}` 86 | res.send(file) 87 | } 88 | ) 89 | 90 | // 第一次登录把注册注释取消 91 | app.post('/admin/api/register', async (req, res) => { 92 | const user = await AdminUser.create({ 93 | username: req.body.username, 94 | password: req.body.password, 95 | }) 96 | res.send(user) 97 | }) 98 | 99 | //登录 100 | app.post('/admin/api/login', async (req, res) => { 101 | const { username, password } = req.body 102 | const user = await AdminUser.findOne({ 103 | username, 104 | }).select('+password') 105 | assert(user, 422, '用户不存在') 106 | const isValid = require('bcryptjs').compareSync(password, user.password) 107 | assert(isValid, 422, '密码错误') 108 | const token = jwt.sign( 109 | { 110 | id: user._id, 111 | }, 112 | app.get('secret') 113 | ) 114 | res.send({ 115 | token, 116 | username, 117 | }) 118 | }) 119 | 120 | app.post('/admin/api/email', async (req, res) => { 121 | sendEmail(req.body) 122 | res.send({ 123 | ok: 'ok', 124 | }) 125 | }) 126 | 127 | //错误处理函数 128 | app.use(async (err, req, res, next) => { 129 | res.status(err.statusCode || 500).send({ 130 | message: err.message, 131 | }) 132 | }) 133 | } 134 | -------------------------------------------------------------------------------- /server/routes/web/index.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | const router = require("express").Router(); 3 | const mongoose = require("mongoose"); 4 | const sendEmail = require('../../plugins/sendEmail.js'); 5 | const Article = mongoose.model("Article"); 6 | const Link = mongoose.model("Link"); 7 | const Category = mongoose.model("Category"); 8 | const Comment = mongoose.model("Comment"); 9 | const Message = mongoose.model("Message"); 10 | const User = mongoose.model("User"); 11 | 12 | // 文章列表 13 | router.get("/articles/list", async (req, res) => { 14 | const data = await Article.find().sort({ 15 | 'createdAt': -1 16 | }); 17 | res.send(data); 18 | }); 19 | 20 | // 最近 21 | router.get("/articles/recent", async (req, res) => { 22 | const data = await Article.find() 23 | .sort({ 24 | 'createdAt': -1 25 | }).limit(4); 26 | res.send(data); 27 | }); 28 | 29 | // 获取指定页码的文章 30 | router.get('/articles/:pageNum', async (req, res) => { 31 | const currentPage = req.params.pageNum; 32 | const list = await Article.find().sort({ 33 | 'createdAt': -1 34 | }).skip((currentPage - 1) * 6).limit(6).populate('categories') 35 | const count = await Article.find().lean().countDocuments() 36 | const totalPage = Math.ceil(count / 6) 37 | res.send({ 38 | list, 39 | totalArticles: count, 40 | totalPage, 41 | currentPage 42 | }) 43 | }) 44 | 45 | // 按照年月进行归类后的数据 46 | router.get('/archive', async (req, res) => { 47 | const data = await Article.aggregate([{ 48 | $sort: { 49 | createdAt: -1 50 | } 51 | }, { 52 | $lookup: { 53 | from: 'categories', 54 | localField: 'categories', 55 | foreignField: '_id', 56 | as: 'newList' 57 | } 58 | }, 59 | { 60 | $group: { 61 | _id: { 62 | $month: '$createdAt', 63 | }, 64 | count: { 65 | $sum: 1 66 | }, 67 | list: { 68 | $push: { 69 | _id: '$_id', 70 | title: '$title', 71 | categories: '$newList', 72 | createdAt: '$createdAt', 73 | } 74 | } 75 | } 76 | }, 77 | ]).sort({ 78 | '_id': -1, 79 | }) 80 | res.send(data) 81 | }) 82 | 83 | // 标签 84 | router.get('/tags', async (req, res) => { 85 | const data = await Category.aggregate([{ 86 | $lookup: { 87 | from: 'articles', 88 | localField: '_id', 89 | foreignField: 'categories', 90 | as: 'tagsList' 91 | } 92 | }]).unwind('$tagsList').sort({ 93 | 'tagsList.createdAt': -1 94 | }).group({ 95 | _id: "$name", 96 | count: { 97 | $sum: 1 98 | }, 99 | list: { 100 | $push: { 101 | _id: '$_id', 102 | title: '$title', 103 | categories: '$tagsList', 104 | createdAt: '$createdAt', 105 | } 106 | } 107 | }).sort({ 108 | 'count': -1, 109 | '_id': -1 110 | }) 111 | res.send(data) 112 | }) 113 | 114 | // 文章详情 115 | router.get("/articles/list/:id", async (req, res) => { 116 | const data = await Article.findById(req.params.id).populate('categories'); 117 | res.send(data); 118 | }); 119 | 120 | // links 121 | router.get("/links/list", async (req, res) => { 122 | const data = await Link.find(); 123 | res.send(data); 124 | }); 125 | 126 | // 用户 127 | router.post('/users', async (req, res) => { 128 | const data = await User.create(req.body) 129 | res.send(data) 130 | }) 131 | router.get('/users', async (req, res) => { 132 | const data = await Users.find() 133 | res.send(data) 134 | }) 135 | router.put('/users/:id', async (req, res) => { 136 | const data = await User.findByIdAndUpdate(req.params.id, req.body) 137 | res.send(data) 138 | }) 139 | router.get('/users/:id', async (req, res) => { 140 | const data = await User.findById(req.params.id) 141 | res.send(data) 142 | }) 143 | 144 | router.post('/email', async (req, res) => { 145 | sendEmail(req.body) 146 | res.send({ 147 | ok: 'ok' 148 | }) 149 | }) 150 | 151 | // 评论 152 | router.post('/comments', async (req, res) => { 153 | const data = await Comment.create(req.body) 154 | await Article.findByIdAndUpdate(req.body.relateBlogId, 155 | // { msgs: req.body.msgs }, 156 | { 157 | $inc: { 158 | msgs: 1 159 | } 160 | } 161 | ) 162 | res.send(data) 163 | }) 164 | router.get('/comments/:blogsId', async (req, res) => { 165 | const comments = await Comment.find().where({ 166 | relateBlogId: req.params.blogsId 167 | }) 168 | res.send(comments) 169 | }) 170 | 171 | // 留言 172 | router.post('/messages', async (req, res) => { 173 | const data = await Message.create(req.body) 174 | res.send(data) 175 | }) 176 | router.get('/messages', async (req, res) => { 177 | // console.log("123",await Blogs.findOne({ 178 | // _id: req.params.blogsId})) 179 | const messages = await Message.find() 180 | res.send(messages) 181 | }) 182 | 183 | // 获取服务器时间 184 | router.get('/time', async (req, res) => { 185 | let time = new Date().getTime() 186 | res.send({ 187 | 'data': time 188 | }) 189 | }) 190 | 191 | app.use("/web/api", router); 192 | }; -------------------------------------------------------------------------------- /server/web/css/about.bb042f05.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/css/about.bb042f05.css.gz -------------------------------------------------------------------------------- /server/web/css/app.ffda094d.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/css/app.ffda094d.css.gz -------------------------------------------------------------------------------- /server/web/css/archive.55ff8245.css: -------------------------------------------------------------------------------- 1 | .main-container[data-v-2d95d8ee]{margin-bottom:180px}.main-container .archive-page .categorys-title[data-v-2d95d8ee]{font-size:20px;position:relative;margin:10px auto;padding:0 32px;color:#bbb} -------------------------------------------------------------------------------- /server/web/css/article.9ef0ae68.css: -------------------------------------------------------------------------------- 1 | #content[data-v-7900f6df]{line-height:1.8}.page[data-v-7900f6df]{max-width:650px}.post-tags[data-v-7900f6df]{max-width:60px}.post-tags[data-v-7900f6df]:hover{background-color:#f1f3f7;color:#5f5f5f}.left[data-v-7900f6df]{width:25%}.blogs-menu[data-v-7900f6df]{max-width:200px;line-height:1.3;margin-top:205px}.blogs-menu .menu-title[data-v-7900f6df]{padding:6px 0;color:#a7ce94}.blogs-menu .menu-title[data-v-7900f6df]:hover{text-decoration:underline}.toc-sticky[data-v-7900f6df]{position:sticky!important;top:80px}.art-comment[data-v-7900f6df]{max-width:650px;margin:0 auto 40px}.textarea-box[data-v-7900f6df]{padding:15px}@media screen and (max-width:900px){.d-none[data-v-7900f6df]{display:none}.page[data-v-7900f6df]{margin:0 auto}}@media screen and (max-width:768px){.page[data-v-7900f6df]{width:95%;margin:0 auto}.art-comment[data-v-7900f6df]{width:90%;margin:0 auto}} -------------------------------------------------------------------------------- /server/web/css/home.475a08fb.css: -------------------------------------------------------------------------------- 1 | .bgImg[data-v-686b6344]{background-image:url(https://miqilin-blog.oss-cn-shenzhen.aliyuncs.com/bg-blog.png);width:100%;height:100vh;background-position:50%;background-size:cover;position:relative;overflow:hidden}.bgImg .shouye-text[data-v-686b6344]{font-size:32px;font-family:Roboto,sans-serif;width:13em;white-space:nowrap;-webkit-animation:typing-data-v-686b6344 2.5s steps(13),infinite;animation:typing-data-v-686b6344 2.5s steps(13),infinite;overflow:hidden}.bgImg .line-down[data-v-686b6344]{font-size:32px;font-family:Roboto,sans-serif;-webkit-animation:shine-data-v-686b6344 .5s linear infinite alternate;animation:shine-data-v-686b6344 .5s linear infinite alternate}.bgImg .arrow-down[data-v-686b6344]{position:absolute;bottom:30px;color:#fff;-webkit-animation:arrowDown-data-v-686b6344 .8s linear infinite alternate;animation:arrowDown-data-v-686b6344 .8s linear infinite alternate}.bgImg .arrow-down .icon-down[data-v-686b6344]{cursor:pointer;font-size:45px}@-webkit-keyframes arrowDown-data-v-686b6344{0%{visibility:hidden;opacity:0;-webkit-transform:translatey(-20%)}20%{visibility:visible;opacity:0;-webkit-transform:translatey(10%)}to{visibility:visible;opacity:1;-webkit-transform:translatey(20%)}}@keyframes arrowDown-data-v-686b6344{0%{visibility:hidden;opacity:0;-webkit-transform:translatey(-20%)}20%{visibility:visible;opacity:0;-webkit-transform:translatey(10%)}to{visibility:visible;opacity:1;-webkit-transform:translatey(20%)}}@-webkit-keyframes typing-data-v-686b6344{0%{width:0}to{width:13em}}@keyframes typing-data-v-686b6344{0%{width:0}to{width:13em}}@-webkit-keyframes shine-data-v-686b6344{0%{visibility:hidden;opacity:0}to{visibility:visible;opacity:1}}@keyframes shine-data-v-686b6344{0%{visibility:hidden;opacity:0}to{visibility:visible;opacity:1}}.post-container[data-v-686b6344]{max-width:1024px;margin:0 auto}.post-container .top[data-v-686b6344]{width:270px;height:250px}@media screen and (max-width:768px){.line-down[data-v-686b6344],.shouye-text[data-v-686b6344]{font-size:24px!important}} -------------------------------------------------------------------------------- /server/web/css/home.475a08fb.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/css/home.475a08fb.css.gz -------------------------------------------------------------------------------- /server/web/css/link.c7fe52ac.css: -------------------------------------------------------------------------------- 1 | .link-img{width:32px;height:32px;border-radius:50%}.link-item{float:left;width:33.3333%;height:auto;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.content{line-height:1.4!important}@media screen and (max-width:880px){.link{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.link .link-item{width:45%;-webkit-box-align:center;-ms-flex-align:center;align-items:center}}@media screen and (max-width:480px){.link{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.link .link-item{width:100%;-webkit-box-align:center;-ms-flex-align:center;align-items:center}}.page-link{max-width:650px;margin:90px auto 0}.content{line-height:1.8!important} -------------------------------------------------------------------------------- /server/web/css/message.208c449f.css: -------------------------------------------------------------------------------- 1 | .textarea-box[data-v-2cc96d00]{width:100%;max-width:650px;padding:5px 15px 15px}.content[data-v-2cc96d00]{line-height:1.8!important} -------------------------------------------------------------------------------- /server/web/css/tag.bcfa3e2a.css: -------------------------------------------------------------------------------- 1 | .post-tags[data-v-4ae8544e]{margin:0;padding:5px 0 20px;border-bottom:1px solid #767676}.post-tags a[data-v-4ae8544e]{font-size:13px;font-weight:600;display:inline-block;margin:20px 8px 10px 0;padding:5px 15px}.post-tags a[data-v-4ae8544e]:nth-child(1n){color:#fff;border:1px solid #6fa3ef;border-radius:15px;background:#6fa3ef}.post-tags a[data-v-4ae8544e]:nth-child(2n){color:#fff;border:1px solid #ff9800;border-radius:15px;background:#ff9800}.post-tags a[data-v-4ae8544e]:nth-child(3n){color:#fff;border:1px solid #46c47c;border-radius:15px;background:#46c47c}.post-tags a[data-v-4ae8544e]:nth-child(4n){color:#fff;border:1px solid #f9bb3c;border-radius:15px;background:#f9bb3c}.post-tags a[data-v-4ae8544e]:nth-child(5n){color:#fff;border:1px solid #bc99c4;border-radius:15px;background:#bc99c4}.post-tags a[data-v-4ae8544e]:nth-child(6n){color:#fff;border:1px solid #e8583d;border-radius:15px;background:#e8583d}.anchor[data-v-4ae8544e]{margin-top:-80px;padding-top:80px}.anchor .categorys-title[data-v-4ae8544e]{font-size:14px;position:relative;margin:10px auto;padding:0 30px;color:#bbb} -------------------------------------------------------------------------------- /server/web/css/tag.bcfa3e2a.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/css/tag.bcfa3e2a.css.gz -------------------------------------------------------------------------------- /server/web/css/vendors~app.3a4b7974.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/css/vendors~app.3a4b7974.css.gz -------------------------------------------------------------------------------- /server/web/css/vendors~article.1c2c36e5.css: -------------------------------------------------------------------------------- 1 | .hljs-comment,.hljs-quote{color:#999}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#f2777a}.hljs-built_in,.hljs-builtin-name,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#f99157}.hljs-attribute{color:#fc6}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#9c9}.hljs-section,.hljs-title{color:#69c}.hljs-keyword,.hljs-selector-tag{color:#c9c}.hljs{display:block;overflow-x:auto;background:#2d2d2d;color:#ccc;padding:.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /server/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/favicon.ico -------------------------------------------------------------------------------- /server/web/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /server/web/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /server/web/index.html: -------------------------------------------------------------------------------- 1 | 米淇淋的个人博客
-------------------------------------------------------------------------------- /server/web/js/about.75b89ddb.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["about"],{b062:function(t,s,a){},cc81:function(t,s,a){"use strict";a("b062")},f820:function(t,s,a){"use strict";a.r(s);var e=(a("cc81"),a("2877")),i=Object(e.a)({},(function(){var t=this;t.$createElement;return t._self._c,t._m(0)}),[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"main-container page-about d-flex flex-column ai-center pt-9 content"},[a("div",{staticClass:"page p-5"},[a("div",{staticClass:"text-green fs-xxxxl"},[t._v("About")]),a("div",{staticClass:"text-grey-2 fs-sm mt-5"},[t._v("Published on March 11th 2020")]),a("div",{staticClass:"mt-5 mb-5"}),a("div",{staticClass:"py-4"},[a("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),a("span",{staticClass:"fs-xxl text-grey-1"},[t._v("关于自己")])]),a("div",{staticClass:"pl-5 text-green-1"},[a("p",[t._v("• 转行前端程序员一枚,目前📍:深圳")]),a("p",[t._v("• 未来希望成为一名优秀的前端程序🐵")]),a("p",[t._v("• 业余爱好比较多,喜欢烹饪、烘焙、音乐制作、踢⚽等等")])]),a("div",{staticClass:"py-4"},[a("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),a("span",{staticClass:"fs-xxl text-grey-1"},[t._v("关于博客")])]),a("div",{staticClass:"pl-5 text-green-1"},[a("p",[t._v(" • 该博客用Vue实现,express提供数据接口,mongoDB储存数据,部署在腾讯云的linux服务器上,搭建博客的初衷是希望把自己平常工作学习生活中值得记录的东西记录下来 ")]),a("p",[t._v("• 如有错误、bug的地方烦请指正")])]),a("div",{staticClass:"py-4 mt-6"},[a("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),a("span",{staticClass:"fs-xxl text-grey-1"},[t._v("Side Project (Coding)")])]),a("div",{staticClass:"pl-5 text-green-1 mb-9"},[a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21/myBlog",target:"_blank"}},[t._v("• myBlog(本博客)")]),t._v(" - 基于NodeJs、Vue、MongoDB实现的一款伪全栈博客系统🔥 ")]),a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21/iMoney",target:"_blank"}},[t._v("• 蘑菇记账")]),t._v(" - 基于Vue全家桶、TypeScript、Echarts实现的一款极简的本地记账应用🤑 ")]),a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21/weapp-mogu-bill",target:"_blank"}},[t._v("• 蘑菇记账(小程序版)")]),t._v(" - 原生小程序实现的一款蘑菇记账应用💰 ")]),a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21/wx-todos",target:"_blank"}},[t._v("• TODOS(小程序版)")]),t._v(" - 原生小程序实现的一款TODO应用工具📅 ")]),a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21/cv",target:"_blank"}},[t._v("• cv")]),t._v(" - 原生js实现的米淇淋个人简历📄 ")]),a("p",[a("a",{staticClass:"text-green-1 bd-bottom-about",attrs:{href:"https://github.com/miqilin21",target:"_blank"}},[t._v("• 其他项目")]),t._v(" - 持续开发中... ")])])])])}],!1,null,"ed059e92",null);s.default=i.exports}}]); -------------------------------------------------------------------------------- /server/web/js/about.75b89ddb.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/about.75b89ddb.js.gz -------------------------------------------------------------------------------- /server/web/js/app.c4457924.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/app.c4457924.js.gz -------------------------------------------------------------------------------- /server/web/js/archive.845f534c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["archive"],{"12b6":function(t,e,i){"use strict";i.r(e);var s=i("1da1"),a=(i("96cf"),i("5a0c"),{data:function(){return{model:[]}},methods:{getArchive:function(){var t=this;return Object(s.a)(regeneratorRuntime.mark((function e(){var i;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.$http.get("/archive");case 2:i=e.sent,t.model=i.data;case 4:case"end":return e.stop()}}),e)})))()}},mounted:function(){this.getArchive()}}),n=(i("5e0f"),i("2877")),r=Object(n.a)(a,(function(){var t=this,e=t.$createElement,i=t._self._c||e;return t.model?i("div",{staticClass:"main-container"},[i("div",{staticClass:"main-content archive-page"},t._l(t.model,(function(e){return i("div",{key:e._id,staticClass:"categorys-item mt-6"},[i("div",{staticClass:"categorys-title"},[t._v(t._s(t._f("date")(e.list[0].createdAt,"YYYY"))+"年"+t._s(e._id)+"月")]),i("div",{staticClass:"post-lists"},[i("div",{staticClass:"post-lists-body"},t._l(e.list,(function(e){return i("div",{key:e.createdAt,staticClass:"post-list-item"},[i("div",{staticClass:"post-list-item-container show"},[i("div",[t._v(" "+t._s(e.categories.map((function(t){return t.title})).join("|"))+" ")]),i("div",{staticClass:"item-label bg-postcolor"},[i("div",{staticClass:"item-title pl-4"},[i("router-link",{attrs:{to:"/article/list/"+e._id,title:"访问 "+e.title}},[t._v(t._s(e.title))])],1),i("div",{staticClass:"item-meta"},[i("div",{staticClass:"item-meta-date"},[t._v(" "+t._s(t._f("date")(e.createdAt,"YYYY-MM-DD HH:mm:ss"))+" "),i("router-link",{staticClass:"text-grey-1",attrs:{to:"/tags","data-hover":e.categories.map((function(t){return t.name})).join("|")}},[t._v(" "+t._s(e.categories.map((function(t){return t.name})).join("|"))+" ")])],1)])])])])})),0)])])})),0)]):t._e()}),[],!1,null,"2d95d8ee",null);e.default=r.exports},"5e0f":function(t,e,i){"use strict";i("71bf")},"71bf":function(t,e,i){}}]); -------------------------------------------------------------------------------- /server/web/js/archive.845f534c.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/archive.845f534c.js.gz -------------------------------------------------------------------------------- /server/web/js/article.b4d913a8.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["article"],{"3ad6":function(t,e,n){"use strict";n.r(e);var a=n("1da1"),s=(n("4de4"),n("d3b7"),n("159b"),n("96cf"),n("e0c1")),r=n.n(s),o=n("1487"),i=n.n(o),c=(n("803b"),n("ac1f"),n("1276"),n("1157")),d=n.n(c),l=n("2b0e");n("466d"),n("5319"),n("d81d");var m=function(t){var e=[];t=t.replace(/]+)*>([^<]+)/g,(function(t,n,a){var s=function(t){var e=t.match(/^]*data-id=(?:"|')([^"']+)/),n="";return e?n=e[1]:(n=parseInt(1e3*Math.random(),10)+"_"+parseInt(100*Math.random()*100),t=t.replace(/(^").addClass("pre-numbering"),n=d()('').addClass("el-icon-document-copy code-copy");d()(this).parent().addClass("code").append(e).append(n);for(var a=0;a<=t;a++)e.append(d()("
  • "))})),d()("pre.code i.code-copy").click((function(t){var e=d()(t.target).siblings("code").text(),n=d()("");d()("body").append(n),n[0].select(),document.execCommand("Copy"),n.remove(),l.default.prototype.$message.success({message:"代码复制成功"})}))}));case 8:case"end":return e.stop()}}),e)})))()},scrollTo:function(t){var e=document.querySelector('[data-id="'+t+'"]');e&&e.scrollIntoView({behavior:"smooth",block:"center",inline:"nearest"})},getBlogsComments:function(){var t=this;return Object(a.a)(regeneratorRuntime.mark((function e(){var n,a;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.$http.get("/comments/".concat(t.id));case 2:n=e.sent,a=n.data,t.parentComments=a.filter((function(t){return"5ec884e3fe28d35475b43fb3"==t.parent})),t.parentComments.forEach((function(t){return t.children=a.filter((function(e){return e.parent==t._id}))})),t.Comments=n.data;case 7:case"end":return e.stop()}}),e)})))()}},mounted:function(){this.fetch(),this.getBlogsComments()}},f=(n("ca92"),n("2877")),h=Object(f.a)(p,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"main-container"},[t.model?n("div",{staticClass:"page-article d-flex jc-center"},[n("div",{staticClass:"d-none left"}),n("div",{staticClass:"page"},[n("div",{staticClass:"text-green fs-xxxxl mt-11"},[t._v(t._s(t.model.title))]),n("div",{staticClass:"text-grey-2 d-flex fs-sm my-4"},[n("p",{staticClass:"mr-4"},[t._v(t._s(t._f("date")(t.model.createdAt,"YYYY-MM-DD HH:mm:ss")))]),n("p",{staticClass:"mr-4"},[t._v("字数 "+t._s(t.model.body.length))]),n("p",[t._v("评论 "+t._s(t.Comments.length))])]),n("div",[n("router-link",{staticClass:"p-2 bdr post-tags text-border text-center bg-blue fs-sm hand mb-6",attrs:{tag:"div",to:"/tags"}},[n("span",[n("i",{staticClass:"iconfont icon-tag1"})]),t._v("  "),n("span",{},[t._v(t._s(t.model.categories[0].name))])])],1),n("div",{staticClass:"text-grey-2 fs-md mb-9 container"},[n("div",{staticClass:"markdown-body"},[n("div",{attrs:{id:"content"},domProps:{innerHTML:t._s(t.model.body)}})])])]),n("div",{staticClass:"d-none left"},[n("div",{staticClass:"blogs-menu toc-sticky text-grey-1 pl-9"},[n("div",[n("h2",[t._v("目录")]),t._l(t.articleToc,(function(e){return n("div",{key:e.id,staticClass:"menu-title hand text-ellipsis",style:{paddingLeft:e.indent+"em"},attrs:{title:e.text},on:{click:function(n){return t.scrollTo(e.id)}}},[t._v(t._s(e.text))])}))],2)])])]):t._e(),t.model?n("div",{staticClass:"art-comment"},[t._m(0),n("h3",{staticClass:"fs-xxxxl mt-9"},[t._v("评论")]),n("comment-textarea",{staticClass:"textarea-box bg-postcolor bdr",attrs:{model:"comments",type:"parent",blogsId:t.id,placeholder:"输入留言内容"},on:{toResponse:t.getBlogsComments}}),t.parentComments?n("comment-list",{staticClass:"mt-7 mb-10",attrs:{model:"comments",commentsList:t.parentComments,blogsId:t.id},on:{getCommentList:t.getBlogsComments}}):t._e()],1):t._e(),n("div",{staticClass:"w-100 h-100"},[n("el-backtop",{attrs:{bottom:50}})],1)])}),[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"text-grey border-top"},[n("p",[t._v("• 非特殊说明,本博所有文章均为博主原创。")]),n("p",[t._v("• 本博客所有文章均采用 CC BY-SA 3.0协议 。转载请注明出处!")])])}],!1,null,"7900f6df",null);e.default=h.exports},ca92:function(t,e,n){"use strict";n("f04a")},f04a:function(t,e,n){}}]); -------------------------------------------------------------------------------- /server/web/js/article.b4d913a8.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/article.b4d913a8.js.gz -------------------------------------------------------------------------------- /server/web/js/home.6e81e34d.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["home"],{"225f":function(t,a,e){},"52a5":function(t,a,e){"use strict";e("225f")},bb51:function(t,a,e){"use strict";e.r(a);var n=e("1da1"),s=(e("96cf"),e("1157")),i=e.n(s),r={data:function(){return{articles:[],pagination:{totalPage:1,currentPage:1}}},methods:{fetchData:function(){var t=this;return Object(n.a)(regeneratorRuntime.mark((function a(){var e;return regeneratorRuntime.wrap((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,t.$http.get("/articles/".concat(t.pagination.currentPage));case 2:e=a.sent,t.articles=e.data.list,t.pagination.totalPage=e.data.totalPage,t.pagination.currentPage=e.data.currentPage;case 6:case"end":return a.stop()}}),a)})))()},goToPage:function(t){var a=this;return Object(n.a)(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:a.pagination.currentPage=t,a.fetchData();case 2:case"end":return e.stop()}}),e)})))()},prev:function(){1!=this.pagination.currentPage&&(this.pagination.currentPage--,this.fetchData())},next:function(){this.pagination.currentPage!=this.pagination.totalPage&&(this.pagination.currentPage++,this.fetchData())},downPage:function(){i()("html,body").animate({scrollTop:i()("#header").outerHeight()-56},500)}},mounted:function(){this.fetchData()}},c=(e("52a5"),e("2877")),o=Object(c.a)(r,(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"mb-9"},[e("div",{staticClass:"mb-7"},[e("div",{staticClass:"bgImg d-flex jc-center",attrs:{id:"header"}},[t._m(0),e("div",{staticClass:"arrow-down"},[e("i",{staticClass:"iconfont icon-down",on:{click:t.downPage}})])])]),e("div",{staticClass:"post-container d-flex flex-wrap jc-center ai-center"},t._l(t.articles,(function(a,n){return e("div",{key:n},[e("div",{staticClass:"show home-art"},[e("router-link",{staticClass:"top mt-10 mx-8 mb-0 hand",style:{"background-image":"url("+a.icon+")"},attrs:{tag:"div",to:"/article/list/"+a._id}}),e("div",{staticClass:"bg-postcolor pt-8 mx-8 title"},[e("router-link",{staticClass:"fs-xxl jc-center d-flex flex-wrap hand text-grey-1",attrs:{tag:"span",to:"/article/list/"+a._id}},[t._v(t._s(a.title))]),e("div",{staticClass:"d-flex mt-4 p-7 text-grey-1"},[e("i",{staticClass:"iconfont icon-riqi2 pr-2"}),e("span",{staticClass:"fs-sm"},[t._v(t._s(t._f("date")(a.createdAt,"YYYY-MM-DD")))]),e("i",{staticClass:"iconfont icon-sort pl-9"}),e("router-link",{staticClass:"fs-sm pl-2 mr-6 hand",attrs:{tag:"span",to:"/tags"}},[t._v(t._s(a.categories[0].name))]),e("i",{staticClass:"iconfont icon-love text-red hand"})],1)],1)],1)])})),0),t.articles.length>0?e("div",{staticClass:"my-10"},[e("div",{staticClass:"page-navigator d-flex jc-center"},[e("div",{staticClass:"mx-4 hand fs-md",class:{current:1==t.pagination.currentPage}},[e("a",{attrs:{"data-hover":"首页"},on:{click:function(a){return t.goToPage(1)}}},[e("span",{staticClass:"text-grey-1"},[t._v("首页")])])]),e("div",{staticClass:"mx-4 hand fs-md"},[e("a",{on:{click:function(a){return t.prev()}}},[e("span",{staticClass:"text-grey-1"},[t._v("«")])])]),t._l(t.pagination.totalPage,(function(a){return e("div",{key:a,staticClass:"mx-4 hand fs-md",class:{current:a==t.pagination.currentPage}},[e("a",{staticClass:"text-grey-1",attrs:{"data-hover":a},on:{click:function(e){return t.goToPage(a)}}},[t._v(t._s(a))])])})),e("div",{staticClass:"mx-4 hand fs-md"},[e("a",{on:{click:function(a){return t.next()}}},[e("span",{staticClass:"text-grey-1"},[t._v("»")])])]),e("div",{staticClass:"mx-4 hand fs-md",class:{current:t.pagination.totalPage==t.pagination.currentPage}},[e("a",{attrs:{"data-hover":"末页"},on:{click:function(a){return t.goToPage(t.pagination.totalPage)}}},[e("span",{staticClass:"text-grey-1"},[t._v("末页")])])]),e("div",{staticClass:"current mx-4 fs-md"},[e("span",{staticClass:"text-grey-1"},[t._v("第"+t._s(t.pagination.currentPage)+"页 / 共"+t._s(t.pagination.totalPage)+"页")])])],2)]):t._e()])}),[function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"text-white d-flex jc-center ai-center"},[e("span",{staticClass:"shouye-text"},[t._v("亲亲这边建议您要多喝热水哦")]),e("span",{staticClass:"line-down pl-3"},[t._v("_")])])}],!1,null,"686b6344",null);a.default=o.exports}}]); -------------------------------------------------------------------------------- /server/web/js/home.6e81e34d.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/home.6e81e34d.js.gz -------------------------------------------------------------------------------- /server/web/js/link.2206432e.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["link"],{"11fa":function(t,s,e){"use strict";e("9ede")},"6e1e":function(t,s,e){},"9ede":function(t,s,e){},ef85:function(t,s,e){"use strict";e.r(s);var n=e("1da1"),a=(e("96cf"),{data:function(){return{model:[]}},methods:{fetchLinks:function(){var t=this;return Object(n.a)(regeneratorRuntime.mark((function s(){var e;return regeneratorRuntime.wrap((function(s){for(;;)switch(s.prev=s.next){case 0:return s.next=2,t.$http.get("links/list");case 2:e=s.sent,t.model=e.data;case 4:case"end":return s.stop()}}),s)})))()}},mounted:function(){this.fetchLinks()}}),i=(e("11fa"),e("2877")),l={components:{linkItem:Object(i.a)(a,(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"link d-flex my-6 content"},t._l(t.model,(function(s){return e("div",{key:s.name,staticClass:"link-item show m-4 p-4 bg-postcolor bdr"},[e("el-tooltip",{attrs:{placement:"right-end",effect:"light","popper-class":"text-grey-2 fs-lg"}},[e("div",{staticClass:"text-green",attrs:{slot:"content"},slot:"content"},[t._v(t._s(s.site))]),e("a",{staticClass:"hand",attrs:{href:s.site,target:"_blank"}},[e("div",{staticClass:"ai-center d-flex"},[e("span",[e("img",{staticClass:"link-img",attrs:{src:s.icon}})]),e("span",{staticClass:"fs-lg text-green pl-5 link-font text-ellipsis"},[t._v(" "+t._s(s.name)+" ")])])])]),e("div",{staticClass:"fs-xs text-grey-1 mt-3 text-ellipsis"},[t._v(t._s(s.description))])],1)})),0)}),[],!1,null,null,null).exports},data:function(){return{}}},c=(e("f8e1"),Object(i.a)(l,(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"main-container page-link py-9"},[e("div",{staticClass:"p-5"},[t._m(0),e("linkItem"),t._m(1)],1)])}),[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[e("div",{staticClass:"text-green fs-xxxxl"},[t._v("Links")]),e("div",{staticClass:"text-grey-2 fs-sm mt-5"},[t._v("Published on March 11th 2020")]),e("div",{staticClass:"mt-5 mb-7"}),e("div",[e("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),e("span",{staticClass:"fs-xxl text-grey-1"},[t._v("友情链接")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[e("div",{staticClass:"py-4"},[e("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),e("span",{staticClass:"fs-xxl text-grey-1"},[t._v("链接需知")])]),e("div",{staticClass:"px-5 py-2 text-green-1"},[e("p",[t._v("• 请确定贵站可以稳定运营")]),e("p",[t._v("• 原创博客优先,技术类博客优先,设计、视觉类博客优先")]),e("p",[t._v("• 经常过来访问和评论,眼熟的")])]),e("p",{staticClass:"text-grey-2 my-6"},[t._v("备注:默认申请友情链接均为内页(当前页面)")]),e("div",{staticClass:"py-4 mb-5"},[e("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),e("span",{staticClass:"fs-xxl text-grey-1"},[t._v("基本信息")])]),e("div",{staticClass:"bg-postcolor px-5 py-2 text-green-1 fs-sm bdr"},[e("p",[t._v("• 格式比如:")]),e("p",[t._v("• 网站名称:米淇淋的个人博客")]),e("p",[t._v("• 网站地址:https://amberzqx.com")]),e("p",[t._v("• 描述:我劝你要多喝热水哈哈哈")])]),e("p",{staticClass:"my-9 text-grey-2"},[t._v("暂时先这样,同时欢迎互换友链,到留言页留言即可。 ^_^")])])}],!1,null,null,null));s.default=c.exports},f8e1:function(t,s,e){"use strict";e("6e1e")}}]); -------------------------------------------------------------------------------- /server/web/js/link.2206432e.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/link.2206432e.js.gz -------------------------------------------------------------------------------- /server/web/js/message.dcf21bf0.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["message"],{"3bb2":function(t,e,s){},"61b7":function(t,e,s){"use strict";s("3bb2")},"8e2a":function(t,e,s){"use strict";s.r(e);var a=s("1da1"),n=(s("96cf"),s("4de4"),s("d3b7"),s("159b"),{data:function(){return{parentComments:[]}},mounted:function(){this.getMessagesList()},methods:{getMessagesList:function(){var t=this;return Object(a.a)(regeneratorRuntime.mark((function e(){var s,a;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.$http.get("messages");case 2:s=e.sent,a=s.data,t.parentComments=a.filter((function(t){return"5ec884e3fe28d35475b43fb3"==t.parent})),t.parentComments.forEach((function(t){return t.children=a.filter((function(e){return e.parent==t._id}))}));case 6:case"end":return e.stop()}}),e)})))()}}}),r=(s("61b7"),s("2877")),i=Object(r.a)(n,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"main-container page-message d-flex flex-column ai-center pt-9"},[s("div",{staticClass:"page p-5"},[t._m(0),s("div",[t._m(1),s("div",{class:{"message-box":t.parentComments.length>0}},[s("div",{staticClass:"textarea-box bg-postcolor bdr"},[s("comment-textarea",{attrs:{model:"messages",type:"parent",placeholder:"输入留言内容"},on:{toResponse:t.getMessagesList}})],1),s("div",{staticClass:"my-9",class:{"message-box":t.parentComments.length>0}},[t.parentComments?s("comment-list",{attrs:{model:"messages",commentsList:t.parentComments},on:{getCommentList:t.getMessagesList}}):t._e()],1)])])])])}),[function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[s("div",{staticClass:"text-green fs-xxxxl"},[t._v("Message")]),s("div",{staticClass:"text-grey-2 fs-sm mt-5"},[t._v("Published on March 11th 2020")]),s("div",{staticClass:"mt-5 mb-5"}),s("div",{staticClass:"py-4"},[s("span",{staticClass:"fs-xxl text-green"},[t._v("#")]),t._v("   "),s("span",{staticClass:"fs-xxl text-grey-1"},[t._v("留言前需知")])]),s("div",{staticClass:"pl-5 text-green-1"},[s("p",[t._v("• 烦请各位留言时务必填写自己真实的邮箱")]),s("p",[t._v("• 留言博主基本都会回复,并会邮件通知留言者")]),s("p",[t._v("• 请不要发广告和带有商业推广链接的无用留言")])]),s("p",{staticClass:"my-8 text-grey-2"},[t._v("希望彼此之间有好的交流。 ^_^")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"mb-7"},[s("span",{staticClass:"fs-xxxl text-grey-1 text-center ml-5"},[t._v("留言板")])])}],!1,null,"2cc96d00",null);e.default=i.exports}}]); -------------------------------------------------------------------------------- /server/web/js/message.dcf21bf0.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/message.dcf21bf0.js.gz -------------------------------------------------------------------------------- /server/web/js/tag.4428a61a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["tag"],{2722:function(t,e,s){},"295e":function(t,e,s){"use strict";s("2722")},"8ea7":function(t,e,s){"use strict";s.r(e);var a=s("1da1"),i=(s("96cf"),s("5a0c")),r=s.n(i),n={data:function(){return{model:[]}},methods:{fetch:function(){var t=this;return Object(a.a)(regeneratorRuntime.mark((function e(){var s;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.$http.get("/tags");case 2:s=e.sent,t.model=s.data;case 4:case"end":return e.stop()}}),e)})))()},getDataHover:function(t){return r()(t).format("YYYY-MM-DD HH:mm:ss")}},mounted:function(){this.fetch()}},c=(s("295e"),s("2877")),o=Object(c.a)(n,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return t.model?s("div",{staticClass:"main-container"},[s("div",{staticClass:"main-content archive-page"},[s("div",{staticClass:"post-title text-green fs-xxxxl"},[t._v("标签分类的博文--点击自动查找相应`Tag`")]),s("div",{staticClass:"text-grey-2 fs-sm mt-6"},[t._v("Published on March 11th 2020")]),s("div",[s("div",{staticClass:"post-tags"},t._l(t.model,(function(e){return s("a",{key:e._id,attrs:{href:"#"+e._id}},[t._v(" "+t._s(e._id)+" "),s("span",[t._v("("+t._s(e.count)+")个")])])})),0)])]),s("div",{staticClass:"main-content archive-page"},t._l(t.model,(function(e){return s("div",{key:e._id,staticClass:"post-lists anchor",attrs:{id:""+e._id}},[s("div",[s("div",{staticClass:"categorys-title"},[t._v(t._s(e._id)+" : "+t._s(e.count))]),t._l(e.list,(function(a){return s("div",{key:a.categories._id,staticClass:"post-list-item"},[s("div",{staticClass:"post-list-item-container bg-postcolor show"},[s("div",{staticClass:"item-label"},[s("div",{staticClass:"item-title pl-4"},[s("router-link",{attrs:{"data-hover":""+a.categories.title,to:"/article/list/"+a.categories._id}},[t._v(t._s(a.categories.title))])],1),s("div",{staticClass:"item-meta"},[s("div",{staticClass:"item-meta-date"},[s("router-link",{staticClass:"text-grey-1",attrs:{to:"/archives","data-hover":t.getDataHover(a.categories.createdAt)}},[t._v(t._s(t._f("date")(a.categories.createdAt,"YYYY-MM-DD HH:mm:ss")))]),s("router-link",{staticClass:"text-grey-1",attrs:{"data-hover":""+e._id,to:"/tags"}},[t._v(t._s(e._id))])],1)])])])])}))],2)])})),0)]):t._e()}),[],!1,null,"4ae8544e",null);e.default=o.exports}}]); -------------------------------------------------------------------------------- /server/web/js/tag.4428a61a.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/tag.4428a61a.js.gz -------------------------------------------------------------------------------- /server/web/js/vendors~app.38cd00d7.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 2 | * @license MIT */ 3 | 4 | /*! 5 | * vue-router v3.5.3 6 | * (c) 2021 Evan You 7 | * @license MIT 8 | */ 9 | 10 | /*! 11 | * Vue.js v2.6.14 12 | * (c) 2014-2021 Evan You 13 | * Released under the MIT License. 14 | */ 15 | 16 | /*! 17 | * vuex v3.6.2 18 | * (c) 2021 Evan You 19 | * @license MIT 20 | */ 21 | 22 | /*! ***************************************************************************** 23 | Copyright (c) Microsoft Corporation. 24 | 25 | Permission to use, copy, modify, and/or distribute this software for any 26 | purpose with or without fee is hereby granted. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 29 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 30 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 31 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 32 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 33 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 34 | PERFORMANCE OF THIS SOFTWARE. 35 | ***************************************************************************** */ 36 | 37 | /** 38 | * vue-class-component v7.2.6 39 | * (c) 2015-present Evan You 40 | * @license MIT 41 | */ 42 | 43 | /** 44 | * Checks if an event is supported in the current execution environment. 45 | * 46 | * NOTE: This will not work correctly for non-generic events such as `change`, 47 | * `reset`, `load`, `error`, and `select`. 48 | * 49 | * Borrows from Modernizr. 50 | * 51 | * @param {string} eventNameSuffix Event name, e.g. "click". 52 | * @param {?boolean} capture Check if the capture phase is supported. 53 | * @return {boolean} True if the event is supported. 54 | * @internal 55 | * @license Modernizr 3.0.0pre (Custom Build) | MIT 56 | */ 57 | -------------------------------------------------------------------------------- /server/web/js/vendors~app.38cd00d7.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/vendors~app.38cd00d7.js.gz -------------------------------------------------------------------------------- /server/web/js/vendors~article.b06189d7.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/vendors~article.b06189d7.js.gz -------------------------------------------------------------------------------- /server/web/js/vendors~article~home.db3e17e7.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Sizzle CSS Selector Engine v2.3.6 3 | * https://sizzlejs.com/ 4 | * 5 | * Copyright JS Foundation and other contributors 6 | * Released under the MIT license 7 | * https://js.foundation/ 8 | * 9 | * Date: 2021-02-16 10 | */ 11 | 12 | /*! 13 | * jQuery JavaScript Library v3.6.0 14 | * https://jquery.com/ 15 | * 16 | * Includes Sizzle.js 17 | * https://sizzlejs.com/ 18 | * 19 | * Copyright OpenJS Foundation and other contributors 20 | * Released under the MIT license 21 | * https://jquery.org/license 22 | * 23 | * Date: 2021-03-02T17:08Z 24 | */ 25 | -------------------------------------------------------------------------------- /server/web/js/vendors~article~home.db3e17e7.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/server/web/js/vendors~article~home.db3e17e7.js.gz -------------------------------------------------------------------------------- /web/.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL = http://localhost:3000/web/api -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # web 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "0.24.0", 12 | "dayjs": "1.10.7", 13 | "element-ui": "2.15.1", 14 | "highlight.js": "10.7.3", 15 | "jquery": "3.4.1", 16 | "marked": "4.0.10", 17 | "nprogress": "0.2.0", 18 | "v-emoji-picker": "2.1.7", 19 | "vue": "2.6.14", 20 | "vue-router": "3.5.1", 21 | "vuex": "3.6.2" 22 | }, 23 | "devDependencies": { 24 | "@vue/cli-plugin-eslint": "4.5.13", 25 | "@vue/cli-service": "4.5.13", 26 | "babel-eslint": "10.0.3", 27 | "compression-webpack-plugin": "6.1.1", 28 | "eslint": "6.7.2", 29 | "eslint-plugin-vue": "6.1.2", 30 | "sass": "1.26.3", 31 | "sass-loader": "8.0.2", 32 | "vue-template-compiler": "2.6.14" 33 | }, 34 | "eslintConfig": { 35 | "root": false, 36 | "env": { 37 | "node": false 38 | }, 39 | "extends": [ 40 | "plugin:vue/essential", 41 | "eslint:recommended" 42 | ], 43 | "parserOptions": { 44 | "parser": "babel-eslint" 45 | }, 46 | "rules": {} 47 | }, 48 | "browserslist": [ 49 | "> 1%", 50 | "last 2 versions" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 米淇淋的个人博客 21 | 22 | 23 | 24 | 25 | 32 |
    33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 102 | -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1590638174211'); /* IE9 */ 3 | src: url('iconfont.eot?t=1590638174211#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAdwAAsAAAAADsgAAAckAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCENgqPEIwpATYCJAMsCxgABCAFhG0HeBuBDBFVpK+T/UiwcYvrXxdV1CS9yeDfLZuXFyzB14ztilF1uhWoKARqIqHnUDO2xjpy5xUVpKZwKn9HAwQQtN1casamAUSA/IShXbSbe8iO8kNfWZ9xvAwXoQJYAHvf2q+Kty+hYIM0CwVquznRPR6LiiXXkEhEUa80QoVEtFJoGYiozlLTJyyMvHQ+EAAccmEE6dVngA1ysNBUQrnZM6ZNgDwWArbESCAPlObsy4JsgQRy5jPmFoDN7seTn9CKyAEGEgrtQkOm9p4MUyw5doiKCRGjqQixvygAbysBFIARAPumf5kb5AMa+TFTcFVZrAaQCq1SpOp3So8ZYx1jlbG+sSGxsbHdsUOJBKiWtPEIUvW1AA4yyCGBYvQsFlIwb/88KURTYvtpLgfEkk0uJOlQyCBGKOSQjlBIIJVQKCB9oaCQIVAQyFgoWMhuKKSQQ9QAAwDg7X1SAZQDzCCAVkDTB7grsWDBQA4K/lKIAYFEiad8bZpKxRKtXis5RaXTqtUqKqUqlWlJCoXBeWMTIhF7NOoIh4VQaGXonS38XojGHZHYaK4h4AyW8PV+0TfKvovjXDuSk5mGYu7Qvez999vyRx/mpfJU2Mm703ZXrQqFbKHHwmG+lyrRJ/YjJndRWBWxhUID7oU6T7oQcUT5hODygRH97iChzgAXedg465Qh04ZOHjaK8KBc3als7D9dxDeezSMHzixPm9Phscw62yS7Q7DaA65Sm08sE/zuCkfQOb1OkUOnAPH03DjoKrH0GK7yqbu+JMTp4OsPp0zxzxU99k137kxz7HamTt3VwHE74EwW3I17nVUNrro9oqd+hXq+imQdimZLFHZdysoFK+yrpeYrJQcetLM1BDLJ3qDbD4i+QmWs84hNiy6cpv0y8Tp3wmzOlTswjqfL+MZdVGStsF+MkmnhbPBnaXd0sLCzPlg18fT8r6LLa7c7yeyKRJyocDi/Ab0cdousirhs6cXWpUKiMGWnO21aNDqt8azzjOuUeHqCt83k42F3tPPZrN1VDXXJO3Tvhbdsrkh5Q+quqg11e1I8IyIx+wA4TGzo3cqp4PCVI5GEykdfvaKW7HtFpLASt33oORZgMZ4DJGr8WW82nSw/abL/yJkjiyttCso/UxDHLK/53Mmc7JPnzPdOKqVkZf+KSTLtxpEC8v4yNTB/383xpZ16mHciv7DJ7+OSm+de7M87B41cVbCN76/oVblswMNdTb26Twa3MN1OLjNjxNpmdS/VOjVMLVu63Iksg+kVz3ftejb84TMOfI4NT+Pfx9t8//sJxHHidzdF+wHzfU6Jk5rvNSd/hyGr48ya2hpDFszaS7ouqa21d7fX+idxdJ00/vt4vE0cJw3fG04ijmDlZwqgwx852cIvvwhthRkzftVkPqvsgWemsbcWLhROfKx1L5sxo803U4uirDpbaudi7tk5QpTfPFB1vpx2uVNGJ/uFO6PVEGFkD5l9DHrKYBk/zkz6Wci4ccQiI+Zx4y0orE31fsrJuE+9/bx73HPs8c6Y1MJDDzKeUk/SwWRPi3UxxxL/On7Dk/+MLTwlnhZqJbdQbvCXBJLKk6uIGXnRKGz+qKrku5EAd3rt29q+y362Q2eT7M6dmuZrzLTm0aMaS6avobQmTDSvaV6ev2zA24T7f+snzKFDzCc6I2hJYWUYqPiYNRhY5MeCSHIfg7Uzb5xttc42ft3W2qFkvH5iv34TkyaUtreyW7Dd17ONVqtxNo+Ow55YB78ynM7xtsEvDYOtr7Zftuxm/tOly4bZ8Qq9rm5p1Va3aBK1BV4bw8KtBqgVQvZMeR7FBJ9lYkaxhZl+RlUxAk17p+7M+XK43pLacWhf34OBITYriw0JI2hJYXGV9eg5NpG40hlBkWRl68YDQOIYvUYt7jHO0oe0BwDQ3cwsIHGO6hj3kojTzczLnAB9QEfUiTpPTThfozFanreR/kdXPcqsh1YXNqwfp+nyj5x/4pnx5B/HWHeDhMiCigDkt5VYAjym8DU7sb+ixvXRLibXMo7HnttVFgOA03sAjyQJerzoX0+pzT986SkhMJAhByjkKLRkbY2glOC1ASm57sCp8LQWL4mYI1PcNvVvgND6AYbaWaC0fi1Z2z+QSPUPUtrmwLG3PyavONrdCRpFD/YG5LgPRrjuvPKvGMfWSFR0nT5REqVDlZfJ/CfsUfrYIZ1jrRogCHfweLkYti3DIHxCp/lRdTgURcjbMXfcTXZ6EshQ26sHrJtf4rBeGFvtUu//CkWjliElzf74/YREQosHlVxZw/JJ62s1O5eqyVlUUxUVgFlXsA54VFGo1XMYGPLXOkGOyh1bZA0OCtVdqKvl4+vd9b0AgKM9Bx9DKGGJhEiJjMiJgnCEJ0rIPiZyGa+ejfcKWC08X/pFYtGFmlgthb6oXgTq/UZ5vJLp46LlMy7fOmpxMgE=') format('woff2'), 5 | url('iconfont.woff?t=1590638174211') format('woff'), 6 | url('iconfont.ttf?t=1590638174211') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1590638174211#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-music-o:before { 19 | content: "\e650"; 20 | } 21 | 22 | .icon-Pause1:before { 23 | content: "\e6b4"; 24 | } 25 | 26 | .icon-down:before { 27 | content: "\e649"; 28 | } 29 | 30 | .icon-sort:before { 31 | content: "\e65e"; 32 | } 33 | 34 | .icon-tag1:before { 35 | content: "\e639"; 36 | } 37 | 38 | .icon-riqi2:before { 39 | content: "\e697"; 40 | } 41 | 42 | .icon-find:before { 43 | content: "\e618"; 44 | } 45 | 46 | .icon-touxiang:before { 47 | content: "\e62c"; 48 | } 49 | 50 | .icon-love:before { 51 | content: "\e642"; 52 | } 53 | 54 | .icon-Smile:before { 55 | content: "\e614"; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1845533", 3 | "name": "blog", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "11132369", 10 | "name": "music-o", 11 | "font_class": "music-o", 12 | "unicode": "e650", 13 | "unicode_decimal": 58960 14 | }, 15 | { 16 | "icon_id": "13745135", 17 | "name": "Pause", 18 | "font_class": "Pause1", 19 | "unicode": "e6b4", 20 | "unicode_decimal": 59060 21 | }, 22 | { 23 | "icon_id": "1207827", 24 | "name": "down", 25 | "font_class": "down", 26 | "unicode": "e649", 27 | "unicode_decimal": 58953 28 | }, 29 | { 30 | "icon_id": "5732293", 31 | "name": "分类", 32 | "font_class": "sort", 33 | "unicode": "e65e", 34 | "unicode_decimal": 58974 35 | }, 36 | { 37 | "icon_id": "9512650", 38 | "name": "标签", 39 | "font_class": "tag1", 40 | "unicode": "e639", 41 | "unicode_decimal": 58937 42 | }, 43 | { 44 | "icon_id": "766689", 45 | "name": "date", 46 | "font_class": "riqi2", 47 | "unicode": "e697", 48 | "unicode_decimal": 59031 49 | }, 50 | { 51 | "icon_id": "902003", 52 | "name": "find", 53 | "font_class": "find", 54 | "unicode": "e618", 55 | "unicode_decimal": 58904 56 | }, 57 | { 58 | "icon_id": "10905612", 59 | "name": "头像", 60 | "font_class": "touxiang", 61 | "unicode": "e62c", 62 | "unicode_decimal": 58924 63 | }, 64 | { 65 | "icon_id": "11349144", 66 | "name": "love", 67 | "font_class": "love", 68 | "unicode": "e642", 69 | "unicode_decimal": 58946 70 | }, 71 | { 72 | "icon_id": "2127147", 73 | "name": "微笑", 74 | "font_class": "Smile", 75 | "unicode": "e614", 76 | "unicode_decimal": 58900 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /web/src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /web/src/assets/images/bg-blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/bg-blog.png -------------------------------------------------------------------------------- /web/src/assets/images/git/git-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/git/git-1.jpg -------------------------------------------------------------------------------- /web/src/assets/images/learn1/learn1-0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/learn1/learn1-0.jpg -------------------------------------------------------------------------------- /web/src/assets/images/learn1/learn1-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/learn1/learn1-2.jpg -------------------------------------------------------------------------------- /web/src/assets/images/learn1/learn1-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/learn1/learn1-3.jpg -------------------------------------------------------------------------------- /web/src/assets/images/learn1/learn1-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/learn1/learn1-4.jpg -------------------------------------------------------------------------------- /web/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/logo.png -------------------------------------------------------------------------------- /web/src/assets/images/this/this-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/this/this-1.jpg -------------------------------------------------------------------------------- /web/src/assets/images/this/this-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonemeen/iBlog/2c97326749fcede04f88d28c2b8be8fc3ea4bc9f/web/src/assets/images/this/this-2.png -------------------------------------------------------------------------------- /web/src/assets/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // colors 2 | $colors: ( 3 | 'blue': #6fa3ef, 4 | 'red': #eb5055, 5 | 'green': #34b686, 6 | 'green-1': #a7ce94, 7 | 'postcolor': #363938, 8 | 'orange': #986801, 9 | 'white': #fff, 10 | 'border': #f1f3f7, 11 | 'bgcolor': #1e2020, 12 | 'navcolor': #363938, 13 | 'codecolor': #9b9999, 14 | 'textcolor': #cccccc, 15 | 'grey-1': #bbbbbb, 16 | 'grey-2': #767676, 17 | 'grey-3': #666666, 18 | 'grey': #5f5f5f, 19 | 'dark': #313131, 20 | 'dark-3': #333333, 21 | 'dark-4': #262626, 22 | 'dark-1': #222222, 23 | 'dark-2': #444444 24 | ); 25 | $border-color: map-get($colors, 'dark-2'); 26 | // font size 27 | $base-font-size: 1rem; 28 | $font-sizes: ( 29 | xs: 0.8571, 30 | //12px 31 | sm: 0.9286, 32 | //13px 33 | md: 1, 34 | //14px 35 | lg: 1.0714, 36 | //15px 37 | xl: 1.1429, 38 | //16px 39 | xxl: 1.2143, 40 | //17px 41 | xxxl: 1.2857, 42 | //18px 43 | xxxxl: 1.5, 44 | //21px 45 | ); 46 | 47 | //justify-content 主轴 48 | $flex-jc: ( 49 | start: flex-start, 50 | end: flex-end, 51 | center: center, 52 | between: space-between, 53 | around: space-around 54 | ); 55 | $flex-ai: ( 56 | start: flex-start, 57 | end: flex-end, 58 | center: center, 59 | stretch: stretch 60 | ); 61 | // spacing 62 | // 0-5: 0 63 | // .mt-1 => margin top .pb-2 64 | $spacing-types: ( 65 | m: margin, 66 | p: padding 67 | ); 68 | $spacing-directions: ( 69 | t: top, 70 | r: right, 71 | b: bottom, 72 | l: left 73 | ); 74 | $spacing-base-size: 1rem; 75 | $spacing-sizes: ( 76 | 0: 0, 77 | 1: 0.15, 78 | 2: 0.35, 79 | 3: 0.5, 80 | 4: 0.714, 81 | 5: 1, 82 | 6: 1.5, 83 | 7: 1.8, 84 | 8: 2.14, 85 | 9: 3, 86 | 10: 4.5, 87 | 11: 6 88 | ); 89 | -------------------------------------------------------------------------------- /web/src/commentConfig.js: -------------------------------------------------------------------------------- 1 | const commentConfig = { 2 | topNickName: 'miqilin', 3 | topParentId: '5ec884e3fe28d35475b43fb3', 4 | } 5 | 6 | export default commentConfig 7 | -------------------------------------------------------------------------------- /web/src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 157 | 158 | 165 | -------------------------------------------------------------------------------- /web/src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 132 | 133 | 212 | -------------------------------------------------------------------------------- /web/src/components/Search.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 87 | 88 | 107 | -------------------------------------------------------------------------------- /web/src/components/formInput.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 172 | 173 | 214 | -------------------------------------------------------------------------------- /web/src/components/linkItem.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 50 | 51 | 90 | -------------------------------------------------------------------------------- /web/src/components/snow.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 203 | 204 | 214 | -------------------------------------------------------------------------------- /web/src/http.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import axios from 'axios' 3 | 4 | const http = axios.create({ 5 | baseURL: process.env.VUE_APP_API_URL || '/web/api', 6 | timeout: 20 * 1000, // Timeout 7 | }) 8 | 9 | export default http; -------------------------------------------------------------------------------- /web/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | import ElementUI from 'element-ui' 6 | import 'element-ui/lib/theme-chalk/index.css' 7 | import NProgress from 'nprogress' 8 | import 'nprogress/nprogress.css' 9 | import './assets/scss/style.scss' 10 | import './assets/iconfont/iconfont.css' 11 | import './plugins/filters' 12 | import './plugins/global' 13 | import http from './http' 14 | 15 | Vue.config.productionTip = false 16 | Vue.use(ElementUI) 17 | Vue.prototype.$http = http 18 | 19 | NProgress.configure({ 20 | easing: 'ease', 21 | speed: 500, 22 | showSpinner: false, 23 | trickleSpeed: 200, 24 | minimum: 0.3, 25 | }) 26 | router.beforeEach((to, from, next) => { 27 | NProgress.start() 28 | next() 29 | }) 30 | router.afterEach(() => { 31 | NProgress.done() 32 | }) 33 | // axios请求拦截器 34 | http.interceptors.request.use( 35 | (config) => { 36 | NProgress.start() 37 | return config 38 | }, 39 | (error) => { 40 | return Promise.reject(error) 41 | } 42 | ) 43 | // axios响应拦截器 44 | http.interceptors.response.use( 45 | (response) => { 46 | NProgress.done() 47 | return response.status === 200 48 | ? Promise.resolve(response) 49 | : Promise.reject(response) 50 | }, 51 | (error) => { 52 | return Promise.reject(error) 53 | } 54 | ) 55 | 56 | new Vue({ 57 | router, 58 | store, 59 | render: (h) => h(App), 60 | }).$mount('#app') 61 | -------------------------------------------------------------------------------- /web/src/plugins/Toc.js: -------------------------------------------------------------------------------- 1 | function prefixID(htmlPart) { 2 | let idMatches = htmlPart.match(/^]*data-id=(?:"|')([^"']+)/) 3 | let id = '' 4 | if (idMatches) { 5 | id = idMatches[1] 6 | } else { 7 | id = parseInt(Math.random() * 1000, 10) + '_' + parseInt(Math.random() * 100 * 100) 8 | htmlPart = htmlPart.replace(/(^]+)*>([^<]+)/g, (htmlPart, indent, text) => { 19 | let prefix = prefixID(htmlPart, indent, text) 20 | toc.push({ 21 | indent, 22 | text, 23 | id: prefix.id 24 | }) 25 | return prefix.htmlPart 26 | }) 27 | // debugger 28 | // article = article.replace(/]+href=['"]([^'"]*)['"]>/g, (htmlPart, indent, text) => { 29 | // let replaceA = htmlPart.replace(/(^ item.indent)) 34 | toc.forEach(item => { 35 | item.indent = item.indent - minItendent 36 | }) 37 | return { 38 | article, 39 | toc 40 | } 41 | } 42 | 43 | export default Toc -------------------------------------------------------------------------------- /web/src/plugins/filters.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import dayjs from 'dayjs' 3 | 4 | Vue.filter('date', (val, type) => { 5 | return dayjs(val).format(type) 6 | }) -------------------------------------------------------------------------------- /web/src/plugins/global.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import commentList from '../components/commentList' 4 | import commentTextarea from '../components/commentTextarea' 5 | 6 | Vue.component('commentList', commentList) 7 | Vue.component('commentTextarea', commentTextarea) -------------------------------------------------------------------------------- /web/src/plugins/lineAndCopy.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import Vue from 'vue' 3 | export const addLineAndCopy = () => { 4 | //markdown代码存放在pre code 标签对中 5 | $('pre code').each(function () { 6 | let lines = $(this).text().split('\n').length - 1 7 | //添加有序列表 8 | let $numbering = $('
      ').addClass('pre-numbering') 9 | //添加复制按钮,此处使用的是element-ui icon 图标 10 | let $copy = $('').addClass('el-icon-document-copy code-copy') 11 | $(this) 12 | .parent() 13 | .addClass('code') 14 | .append($numbering) 15 | .append($copy) 16 | for (let i = 0; i <= lines; i++) { 17 | $numbering.append($('
    1. ')) 18 | } 19 | }) 20 | //监听复制按钮点击事件 21 | $('pre.code i.code-copy').click(e => { 22 | let text = $(e.target).siblings('code').text() 23 | let element = $('') 24 | $('body').append(element) 25 | element[0].select() 26 | document.execCommand('Copy') 27 | element.remove() 28 | //这里是自定义的消息通知组件 29 | Vue.prototype.$message.success({ 30 | message: '代码复制成功' 31 | }) 32 | }) 33 | } -------------------------------------------------------------------------------- /web/src/plugins/reg.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | phone: /^(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$/, 4 | tell: /^\d{3,4}\d{7,8}$/, //座机 5 | zh_Name: /^[\u4e00-\u9fa5|a-zA-Z]{2,}$/, 6 | idNumber: /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/, 7 | zhanghu: /^[0-9]*$/, 8 | hanzi: /^[\u2E80-\u9FFF]+$/, 9 | uniformCreditCode: /^[0-9A-Z]{18}$/, 10 | vCode6: /^\d{6}$/, // 6位验证码 11 | email: /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/, 12 | numberFloatTwo: /^\d+(\.\d{1,2})?$/, //正整数或保留两位小数以内 13 | number: /^\d+$/, //正整数 14 | bankCardNumber: /^[0-9]{10,20}$/, //银行卡号 15 | url: /^((https|http){0,1}(:\/\/){0,1})www\.(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/, 16 | qqEmail: /^\d{5,12}@[q][q]\.(com|cn)$/, 17 | }; 18 | -------------------------------------------------------------------------------- /web/src/plugins/validate.js: -------------------------------------------------------------------------------- 1 | import reg from "./reg"; 2 | 3 | //验证手机号码 4 | export const validatePhone = (rule, value, callback) => { 5 | if (!value) { 6 | callback(new Error('请输入手机号码')); 7 | } else if (!reg.phone.test(value)) { 8 | callback(new Error('手机号格式不对')); 9 | } else { 10 | callback(); 11 | } 12 | }; 13 | export const validateQQEmail = (rule, value, callback) => { 14 | if (!value) { 15 | callback(new Error('请输入QQ邮箱')) 16 | } else if (!reg.qqEmail.test(value)) { 17 | callback(new Error('@qq.com')); 18 | } else { 19 | callback(); 20 | } 21 | } 22 | //验证电话号码(座机和手机号) 23 | export const validateTell = (rule, value, callback) => { 24 | if (!value) { 25 | callback(new Error('请输入电话号码')); 26 | } else if (!reg.tell.test(value) && !reg.phone.test(value)) { 27 | callback(new Error('电话号码格式不对')); 28 | } else { 29 | callback(); 30 | } 31 | }; 32 | //验证验证码 33 | export const validateCode = (rule, value, callback) => { 34 | if (!value) { 35 | callback(new Error('请输入验证码')); 36 | } else { 37 | callback(); 38 | } 39 | }; 40 | //验证身份证号码 41 | export const validateIdCard = (rule, value, callback) => { 42 | if (!value) { 43 | callback(new Error("请输入身份证号码")); 44 | } else if (!reg.idNumber.test(value)) { 45 | callback(new Error("请输入正确的身份证号码")); 46 | } else { 47 | callback(); 48 | } 49 | }; 50 | //银行卡号验证 51 | export const validateBankCard = (rule, value, callback) => { 52 | if (!value) { 53 | callback(new Error("请输入账户号码")); 54 | } else if (!reg.bankCardNumber.test(value)) { 55 | callback(new Error("账户号码格式错误")); 56 | } else { 57 | callback(); 58 | } 59 | }; 60 | //银行卡号验证(非必填) 61 | export const validateBankCardNo = (rule, value, callback) => { 62 | if (value && !reg.bankCardNumber.test(value)) { 63 | callback(new Error('账户号码格式错误')) 64 | } else { 65 | callback() 66 | } 67 | }; 68 | //统一信用代码验证 69 | export const validateUniformCreditCode = (rule, value, callback) => { 70 | if (!value) { 71 | callback(new Error("请输入统一信用代码")); 72 | } else if (!reg.uniformCreditCode.test(value)) { 73 | callback(new Error("统一信用代码格式错误")); 74 | } else { 75 | callback(); 76 | } 77 | }; 78 | //图片上传验证 79 | export const validateUploadImg = (rule, value, callback) => { 80 | if (value.length > 0) { 81 | callback(); 82 | } else { 83 | callback(new Error()); 84 | } 85 | }; 86 | //金额验证 87 | export const validateMoney = (rule, value, callback) => { 88 | if (!value) { 89 | callback(new Error("必填")); 90 | return; 91 | } 92 | if (!reg.numberFloatTwo.test(value)) { 93 | callback(new Error("只能为正数,且最多两位小数")); 94 | return; 95 | } 96 | let str = value.toString(); 97 | let strFl = str.substring(0, str.indexOf(".")); 98 | if (strFl.length >= 2 && strFl.substring(0, 1) == "0") { 99 | callback(new Error("小数点前二位以上,则首位不能为0")); 100 | return; 101 | } 102 | if (str.length > 12) { 103 | callback(new Error("最多只能输入12位")); 104 | return; 105 | } 106 | callback(); 107 | }; 108 | //注册资本验证(非必填) 109 | export const validateMoneyNo = (rule, value, callback) => { 110 | if (value) { 111 | if (!reg.numberFloatTwo.test(value)) { 112 | callback(new Error('只能为正数,且最多两位小数')); 113 | return 114 | } 115 | let str = value.toString(); 116 | let strFl = str.substring(0, str.indexOf('.')); 117 | if (strFl.length >= 2 && strFl.substring(0, 1) == '0') { 118 | callback(new Error('小数点前二位以上,则首位不能为0')); 119 | return 120 | } 121 | if (str.length > 12) { 122 | callback(new Error('最多只能输入12位')); 123 | return 124 | } 125 | } 126 | callback() 127 | }; 128 | //邮箱验证 129 | export const validateEmail = (rule, value, callback) => { 130 | if (!value) { 131 | callback(new Error("请输入邮箱")); 132 | } else if (!reg.email.test(value)) { 133 | callback(new Error("邮箱格式错误")); 134 | } else { 135 | callback(); 136 | } 137 | }; 138 | export const validateUrl = (rule, value, callback) => { 139 | if (!value) { 140 | callback(new Error("请输入网址")); 141 | } else if (!reg.url.test(value)) { 142 | callback(new Error("以http/https开头的完整正确格式")); 143 | } else { 144 | callback(); 145 | } 146 | }; 147 | //邮政编码验证(非必填) 148 | export const validateZipCode = (rule, value, callback) => { 149 | if (value && !reg.vCode6.test(value)) { 150 | callback(new Error('邮箱格式错误')) 151 | } else { 152 | callback() 153 | } 154 | }; 155 | //供货标签验证 156 | export const validateTagIds = (rule, value, callback) => { 157 | if (value && value.length > 15) { 158 | callback(new Error("最多只能选择15个标签")); 159 | return 160 | } 161 | callback(); 162 | }; 163 | //公司名称长度验证 164 | export const validateCompanyName = (rule, value, callback) => { 165 | if (!value) { 166 | callback(new Error('请填写公司名称')) 167 | } else if (value.length < 6) { 168 | callback(new Error('公司名称长度需大于6位')) 169 | } else { 170 | callback() 171 | } 172 | }; 173 | //姓名长度验证 174 | export const validateName = (rule, value, callback) => { 175 | if (!value) { 176 | callback(new Error('请填写姓名')) 177 | } else if (value && value.length < 2) { 178 | callback(new Error('姓名长度需大于2位')) 179 | } else { 180 | callback() 181 | } 182 | }; 183 | 184 | export const validatePassword = (rele, value, callback) => { 185 | if (!value) { 186 | callback(new Error('请输入密码')) 187 | } else if (value.length < 6) { 188 | callback(new Error('请输入6位以上的密码')) 189 | } else { 190 | callback() 191 | } 192 | }; -------------------------------------------------------------------------------- /web/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Main from '../views/Main.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [{ 8 | path: '/', 9 | component: Main, 10 | children: [{ 11 | path: '/', 12 | name: 'Home', 13 | component: () => import( /* webpackChunkName: "home" */ '../views/Home.vue'), 14 | meta: { 15 | title: '首页 - MIQILIN' 16 | } 17 | }, 18 | { 19 | path: '/archives', 20 | name: 'Archive', 21 | component: () => import( /* webpackChunkName: "archive" */ '../views/Archive.vue'), 22 | meta: { 23 | title: '归档 - MIQILIN' 24 | } 25 | }, 26 | { 27 | path: '/tags', 28 | name: 'Tag', 29 | component: () => import( /* webpackChunkName: "tag" */ '../views/Tag.vue'), 30 | meta: { 31 | title: '标签 - MIQILIN' 32 | } 33 | }, 34 | { 35 | path: '/links', 36 | name: 'Link', 37 | component: () => import( /* webpackChunkName: "link" */ '../views/Link.vue'), 38 | meta: { 39 | title: '友链 - MIQILIN' 40 | } 41 | }, 42 | { 43 | path: '/message', 44 | name: 'Message', 45 | component: () => import( /* webpackChunkName: "message" */ '../views/Message.vue'), 46 | meta: { 47 | title: '留言 - MIQILIN' 48 | } 49 | }, 50 | { 51 | path: '/about', 52 | name: 'About', 53 | component: () => import( /* webpackChunkName: "about" */ '../views/About.vue'), 54 | meta: { 55 | title: '关于 - MIQILIN' 56 | } 57 | }, 58 | { 59 | path: '/article/list/:id', 60 | name: 'Article', 61 | component: () => import( /* webpackChunkName: "article" */ '../views/Article.vue'), 62 | props: true, 63 | meta: { 64 | title: '文章详情 - MIQILIN' 65 | } 66 | } 67 | ] 68 | }, { 69 | path: '*', 70 | redirect: '/' 71 | }] 72 | 73 | const router = new VueRouter({ 74 | scrollBehavior() { 75 | return { 76 | x: 0, 77 | y: 0 78 | } 79 | }, 80 | // base: process.env.BASE_URL, 81 | routes 82 | }) 83 | router.beforeEach((to, from, next) => { 84 | // to and from are both route objects 85 | if (to.meta.title) { 86 | document.title = to.meta.title 87 | } 88 | next() 89 | }) 90 | 91 | export default router 92 | -------------------------------------------------------------------------------- /web/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | Vue.use(Vuex); 4 | 5 | export default new Vuex.Store({ 6 | state: { 7 | map_user_info: {}, 8 | vuex_skin_obj: { 9 | name: 'miqilin', 10 | chinaName: '米淇淋', 11 | }, 12 | }, 13 | mutations: { 14 | map_set_user_info(state, data) { 15 | state.userInfo = data 16 | }, 17 | vuex_set_skin_obj(state, data) { 18 | state.vuex_skin_obj = data 19 | }, 20 | }, 21 | actions: {}, 22 | modules: {} 23 | }); -------------------------------------------------------------------------------- /web/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 101 | 102 | 115 | -------------------------------------------------------------------------------- /web/src/views/Archive.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 86 | 87 | 99 | -------------------------------------------------------------------------------- /web/src/views/Article.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 164 | 231 | -------------------------------------------------------------------------------- /web/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 154 | 155 | 242 | -------------------------------------------------------------------------------- /web/src/views/Link.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 60 | 61 | 70 | -------------------------------------------------------------------------------- /web/src/views/Main.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | -------------------------------------------------------------------------------- /web/src/views/Message.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 75 | 76 | 86 | -------------------------------------------------------------------------------- /web/src/views/Search.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | -------------------------------------------------------------------------------- /web/src/views/Tag.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 92 | 93 | 154 | -------------------------------------------------------------------------------- /web/vue.config.js: -------------------------------------------------------------------------------- 1 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 2 | const productionGzipExtensions = ['js', 'css'] 3 | 4 | module.exports = { 5 | lintOnSave: false, 6 | outputDir: __dirname + '/../server/web', 7 | // publicPath: process.env.NODE_ENV === 'production' ? '/web' : '/' 8 | productionSourceMap: false, 9 | chainWebpack: (config) => { 10 | //最小化代码 11 | config.optimization.minimize(true) 12 | //分割代码 13 | config.optimization.splitChunks({ 14 | chunks: 'all', 15 | }) 16 | // // 压缩图片 17 | // config.module 18 | // .rule('images') 19 | // .use('image-webpack-loader') 20 | // .loader('image-webpack-loader') 21 | // .options({ 22 | // bypassOnDebug: true, 23 | // }) 24 | // .end() 25 | }, 26 | configureWebpack: (config) => { 27 | if (process.env.NODE_ENV === 'production') { 28 | // 为生产环境修改配置... 29 | return { 30 | plugins: [ 31 | // new PrerenderSPAPlugin({ 32 | // staticDir: path.join(__dirname, '/../serve/web'), 33 | // routes: ['/', '/blogs'], 34 | // // renderer: new Renderer({ 35 | // // inject: { 36 | // // foo: 'bar' 37 | // // }, 38 | // // headless: true, 39 | // // renderAfterDocumentEvent: 'render-event' 40 | // // }) 41 | // }), 42 | new CompressionWebpackPlugin({ 43 | algorithm: 'gzip', 44 | test: new RegExp(`\\.(${productionGzipExtensions.join('|')})$`), 45 | threshold: 1024, 46 | minRatio: 0.8, 47 | }), 48 | ], 49 | } 50 | } else { 51 | // 为开发环境修改配置... 52 | } 53 | }, 54 | } 55 | --------------------------------------------------------------------------------