├── .babelrc ├── .gitignore ├── PreviewImg ├── admin01.png ├── admin02.png ├── admin3.png ├── admin4.png ├── blog01.png └── blog02.png ├── Procfile ├── README.md ├── admin.html ├── admin ├── .babelrc ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ ├── add.png │ │ ├── adminlabel.png │ │ ├── article-title.png │ │ ├── article.png │ │ ├── atom-one-dark.css │ │ ├── atom-one-light.css │ │ ├── dark.css │ │ ├── default.css │ │ ├── head-portrait.jpg │ │ ├── labels.png │ │ ├── left.png │ │ ├── logo.png │ │ ├── right.png │ │ ├── simplemde.css │ │ ├── simplemde.min.css │ │ ├── tag.png │ │ └── use.png │ ├── component │ │ ├── AddArticLabel.vue │ │ ├── ArticleEdit.vue │ │ ├── ArticleLabel.vue │ │ ├── ArticleList.vue │ │ ├── ArticlePreview.vue │ │ └── PersonalCenter.vue │ └── main.js ├── webpack.config.js └── yarn.lock ├── app.js ├── dist ├── add.png ├── admin.js ├── admin.js.map ├── adminlabel.png ├── article-title.png ├── article.png ├── b02bdc1b846fd65473922f5f62832108.ttf ├── build.js ├── build.js.gz ├── build.js.map ├── d2f69a92faa6fe990d2e613c358be705.woff ├── labels.png ├── logo.png ├── tag.png └── use.png ├── index.html ├── login.html ├── package.json ├── server ├── db.js ├── router.js └── user.js ├── src ├── App.vue ├── assets │ ├── atom-one-light.css │ ├── blueprint.png │ ├── gitment │ │ ├── constants.js │ │ ├── constants.js.map │ │ ├── default.css │ │ ├── gitment.browser.js │ │ ├── gitment.browser.js.map │ │ ├── gitment.js │ │ ├── gitment.js.map │ │ ├── icons.js │ │ ├── icons.js.map │ │ ├── theme │ │ │ ├── default.js │ │ │ └── default.js.map │ │ ├── utils.js │ │ └── utils.js.map │ └── logo.png ├── component │ ├── About.vue │ ├── archives.vue │ ├── articlesDetails.vue │ ├── comment.vue │ ├── latestArticles.vue │ └── tag.vue ├── main.js ├── style │ ├── common.css │ └── latestArticles.css └── utils │ └── utils.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ], 5 | "plugins": [["component", [ 6 | { 7 | "libraryName": "element-ui", 8 | "styleLibraryName": "theme-default" 9 | } 10 | ]]] 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | 5 | *.xml 6 | 7 | *.iml 8 | 9 | .vscode/launch.json 10 | 11 | .vscode/settings.json 12 | -------------------------------------------------------------------------------- /PreviewImg/admin01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/admin01.png -------------------------------------------------------------------------------- /PreviewImg/admin02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/admin02.png -------------------------------------------------------------------------------- /PreviewImg/admin3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/admin3.png -------------------------------------------------------------------------------- /PreviewImg/admin4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/admin4.png -------------------------------------------------------------------------------- /PreviewImg/blog01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/blog01.png -------------------------------------------------------------------------------- /PreviewImg/blog02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/PreviewImg/blog02.png -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue+node 实现的一套简易博客系统,包括博客前端展示页和博客后台管理页 2 | 3 | 2017-09-05更新,该博客已用`Angular4重构`,界面ui也重新设计了,目前线上跑的版本已经是`Angular4`重构后的了,`vue`版的已不再维护,新版[在线地址](https://lweiwei.com), 源码[地址](https://github.com/linguowei/blog-angular) 4 |
5 | 6 | 分割线——————————————————————————————————————————————————————————————————————————— 7 |
8 | 9 | 2017-04-27更新如下,调整博客前端展示页面样式,支持响应式,添加了评论功能,UI参照[美团点评技术团队](http://tech.meituan.com/)的,莫怪 10 | 11 | > 简要技术栈: 12 | > 13 | > vue2.0+vue-router+vue-resource 14 | > 15 | > node+express+mongodb 16 | > 17 | > 后台管理页的编辑器用的simplemde,支持markdown语法 18 | > 19 | > markdown解析部分使用了marked这个库,语法高亮用highlight 20 | 21 | ### [博客在线地址](https://weiweiblog.herokuapp.com)(仅供临时预览) 22 | 23 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/blog01.png) 24 | --- 25 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/blog02.png) 26 | --- 27 | 28 | ### [博客后台管理页在线地址](https://weiweiblog.herokuapp.com/admin)(仅供临时预览) 29 | 30 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/admin01.png) 31 | --- 32 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/admin02.png) 33 | --- 34 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/admin3.png) 35 | --- 36 | ![](https://github.com/linguowei/myblog/blob/master/PreviewImg/admin4.png) 37 | --- 38 | ### 本地查看方法 39 | 40 | ``` bash 41 | # git clone https://github.com/linguowei/myblog.git 42 | # cd myblog 43 | # npm install 44 | # node app.js 45 | # localhost:9000 46 | # localhost:9000/admin 47 | ``` 48 | ### License 49 | [MIT](https://www.oschina.net/question/54100_9455) 50 | -------------------------------------------------------------------------------- /admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | admin 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /admin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /admin/README.md: -------------------------------------------------------------------------------- 1 | # admin 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | ``` 17 | 18 | For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). 19 | -------------------------------------------------------------------------------- /admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | admin 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "description": "A Vue.js project", 4 | "version": "1.0.0", 5 | "author": "linguowei <1368033036@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --inline --hot", 9 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 10 | }, 11 | "dependencies": { 12 | "body-parser": "^1.15.2", 13 | "element-ui": "^1.0.5", 14 | "express": "^4.14.0", 15 | "highlight.js": "^9.8.0", 16 | "http-proxy-middleware": "^0.17.3", 17 | "marked": "^0.3.6", 18 | "mime": "^1.3.4", 19 | "mongoose": "^4.7.6", 20 | "simplemde": "^1.11.2", 21 | "vue": "^2.1.0", 22 | "vue-resource": "^1.0.3", 23 | "vue-router": "^2.1.1", 24 | "vuex": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "babel-core": "^6.0.0", 28 | "babel-loader": "^6.0.0", 29 | "babel-plugin-component": "^0.6.0", 30 | "babel-preset-es2015": "^6.0.0", 31 | "cross-env": "^3.0.0", 32 | "css-loader": "^0.25.0", 33 | "file-loader": "^0.9.0", 34 | "style-loader": "^0.13.1", 35 | "vue-loader": "^10.0.0", 36 | "vue-template-compiler": "^2.1.0", 37 | "webpack": "^2.1.0-beta.25", 38 | "webpack-dev-server": "^2.1.0-beta.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 55 | 56 | 153 | -------------------------------------------------------------------------------- /admin/src/assets/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/add.png -------------------------------------------------------------------------------- /admin/src/assets/adminlabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/adminlabel.png -------------------------------------------------------------------------------- /admin/src/assets/article-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/article-title.png -------------------------------------------------------------------------------- /admin/src/assets/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/article.png -------------------------------------------------------------------------------- /admin/src/assets/atom-one-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Atom One Dark by Daniel Gamage 4 | Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax 5 | 6 | base: #282c34 7 | mono-1: #abb2bf 8 | mono-2: #818896 9 | mono-3: #5c6370 10 | hue-1: #56b6c2 11 | hue-2: #61aeee 12 | hue-3: #c678dd 13 | hue-4: #98c379 14 | hue-5: #e06c75 15 | hue-5-2: #be5046 16 | hue-6: #d19a66 17 | hue-6-2: #e6c07b 18 | 19 | */ 20 | 21 | .hljs { 22 | display: block; 23 | overflow-x: auto; 24 | padding: 0.5em; 25 | color: #abb2bf; 26 | background: #282c34; 27 | } 28 | 29 | .hljs-comment, 30 | .hljs-quote { 31 | color: #5c6370; 32 | font-style: italic; 33 | } 34 | 35 | .hljs-doctag, 36 | .hljs-keyword, 37 | .hljs-formula { 38 | color: #c678dd; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name, 43 | .hljs-selector-tag, 44 | .hljs-deletion, 45 | .hljs-subst { 46 | color: #e06c75; 47 | } 48 | 49 | .hljs-literal { 50 | color: #56b6c2; 51 | } 52 | 53 | .hljs-string, 54 | .hljs-regexp, 55 | .hljs-addition, 56 | .hljs-attribute, 57 | .hljs-meta-string { 58 | color: #98c379; 59 | } 60 | 61 | .hljs-built_in, 62 | .hljs-class .hljs-title { 63 | color: #e6c07b; 64 | } 65 | 66 | .hljs-attr, 67 | .hljs-variable, 68 | .hljs-template-variable, 69 | .hljs-type, 70 | .hljs-selector-class, 71 | .hljs-selector-attr, 72 | .hljs-selector-pseudo, 73 | .hljs-number { 74 | color: #d19a66; 75 | } 76 | 77 | .hljs-symbol, 78 | .hljs-bullet, 79 | .hljs-link, 80 | .hljs-meta, 81 | .hljs-selector-id, 82 | .hljs-title { 83 | color: #61aeee; 84 | } 85 | 86 | .hljs-emphasis { 87 | font-style: italic; 88 | } 89 | 90 | .hljs-strong { 91 | font-weight: bold; 92 | } 93 | 94 | .hljs-link { 95 | text-decoration: underline; 96 | } 97 | -------------------------------------------------------------------------------- /admin/src/assets/atom-one-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Atom One Light by Daniel Gamage 4 | Original One Light Syntax theme from https://github.com/atom/one-light-syntax 5 | 6 | base: #fafafa 7 | mono-1: #383a42 8 | mono-2: #686b77 9 | mono-3: #a0a1a7 10 | hue-1: #0184bb 11 | hue-2: #4078f2 12 | hue-3: #a626a4 13 | hue-4: #50a14f 14 | hue-5: #e45649 15 | hue-5-2: #c91243 16 | hue-6: #986801 17 | hue-6-2: #c18401 18 | 19 | */ 20 | 21 | .hljs { 22 | display: block; 23 | overflow-x: auto; 24 | padding: 0.5em; 25 | color: #383a42; 26 | background: #fafafa; 27 | } 28 | 29 | .hljs-comment, 30 | .hljs-quote { 31 | color: #a0a1a7; 32 | font-style: italic; 33 | } 34 | 35 | .hljs-doctag, 36 | .hljs-keyword, 37 | .hljs-formula { 38 | color: #a626a4; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name, 43 | .hljs-selector-tag, 44 | .hljs-deletion, 45 | .hljs-subst { 46 | color: #e45649; 47 | } 48 | 49 | .hljs-literal { 50 | color: #0184bb; 51 | } 52 | 53 | .hljs-string, 54 | .hljs-regexp, 55 | .hljs-addition, 56 | .hljs-attribute, 57 | .hljs-meta-string { 58 | color: #50a14f; 59 | } 60 | 61 | .hljs-built_in, 62 | .hljs-class .hljs-title { 63 | color: #c18401; 64 | } 65 | 66 | .hljs-attr, 67 | .hljs-variable, 68 | .hljs-template-variable, 69 | .hljs-type, 70 | .hljs-selector-class, 71 | .hljs-selector-attr, 72 | .hljs-selector-pseudo, 73 | .hljs-number { 74 | color: #986801; 75 | } 76 | 77 | .hljs-symbol, 78 | .hljs-bullet, 79 | .hljs-link, 80 | .hljs-meta, 81 | .hljs-selector-id, 82 | .hljs-title { 83 | color: #4078f2; 84 | } 85 | 86 | .hljs-emphasis { 87 | font-style: italic; 88 | } 89 | 90 | .hljs-strong { 91 | font-weight: bold; 92 | } 93 | 94 | .hljs-link { 95 | text-decoration: underline; 96 | } 97 | -------------------------------------------------------------------------------- /admin/src/assets/dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Dark style from softwaremaniacs.org (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #444; 12 | } 13 | 14 | .hljs-keyword, 15 | .hljs-selector-tag, 16 | .hljs-literal, 17 | .hljs-section, 18 | .hljs-link { 19 | color: white; 20 | } 21 | 22 | .hljs, 23 | .hljs-subst { 24 | color: #ddd; 25 | } 26 | 27 | .hljs-string, 28 | .hljs-title, 29 | .hljs-name, 30 | .hljs-type, 31 | .hljs-attribute, 32 | .hljs-symbol, 33 | .hljs-bullet, 34 | .hljs-built_in, 35 | .hljs-addition, 36 | .hljs-variable, 37 | .hljs-template-tag, 38 | .hljs-template-variable { 39 | color: #d88; 40 | } 41 | 42 | .hljs-comment, 43 | .hljs-quote, 44 | .hljs-deletion, 45 | .hljs-meta { 46 | color: #777; 47 | } 48 | 49 | .hljs-keyword, 50 | .hljs-selector-tag, 51 | .hljs-literal, 52 | .hljs-title, 53 | .hljs-section, 54 | .hljs-doctag, 55 | .hljs-type, 56 | .hljs-name, 57 | .hljs-strong { 58 | font-weight: bold; 59 | } 60 | 61 | .hljs-emphasis { 62 | font-style: italic; 63 | } 64 | -------------------------------------------------------------------------------- /admin/src/assets/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original highlight.js style (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #F0F0F0; 12 | } 13 | 14 | 15 | /* Base color: saturation 0; */ 16 | 17 | .hljs, 18 | .hljs-subst { 19 | color: #444; 20 | } 21 | 22 | .hljs-comment { 23 | color: #888888; 24 | } 25 | 26 | .hljs-keyword, 27 | .hljs-attribute, 28 | .hljs-selector-tag, 29 | .hljs-meta-keyword, 30 | .hljs-doctag, 31 | .hljs-name { 32 | font-weight: bold; 33 | } 34 | 35 | 36 | /* User color: hue: 0 */ 37 | 38 | .hljs-type, 39 | .hljs-string, 40 | .hljs-number, 41 | .hljs-selector-id, 42 | .hljs-selector-class, 43 | .hljs-quote, 44 | .hljs-template-tag, 45 | .hljs-deletion { 46 | color: #880000; 47 | } 48 | 49 | .hljs-title, 50 | .hljs-section { 51 | color: #880000; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-regexp, 56 | .hljs-symbol, 57 | .hljs-variable, 58 | .hljs-template-variable, 59 | .hljs-link, 60 | .hljs-selector-attr, 61 | .hljs-selector-pseudo { 62 | color: #BC6060; 63 | } 64 | 65 | 66 | /* Language color: hue: 90; */ 67 | 68 | .hljs-literal { 69 | color: #78A960; 70 | } 71 | 72 | .hljs-built_in, 73 | .hljs-bullet, 74 | .hljs-code, 75 | .hljs-addition { 76 | color: #397300; 77 | } 78 | 79 | 80 | /* Meta color: hue: 200 */ 81 | 82 | .hljs-meta { 83 | color: #1f7199; 84 | } 85 | 86 | .hljs-meta-string { 87 | color: #4d99bf; 88 | } 89 | 90 | 91 | /* Misc effects */ 92 | 93 | .hljs-emphasis { 94 | font-style: italic; 95 | } 96 | 97 | .hljs-strong { 98 | font-weight: bold; 99 | } 100 | -------------------------------------------------------------------------------- /admin/src/assets/head-portrait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/head-portrait.jpg -------------------------------------------------------------------------------- /admin/src/assets/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/labels.png -------------------------------------------------------------------------------- /admin/src/assets/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/left.png -------------------------------------------------------------------------------- /admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/logo.png -------------------------------------------------------------------------------- /admin/src/assets/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/right.png -------------------------------------------------------------------------------- /admin/src/assets/simplemde.css: -------------------------------------------------------------------------------- 1 | /** 2 | * simplemde v1.11.2 3 | * Copyright Next Step Webs, Inc. 4 | * @link https://github.com/NextStepWebs/simplemde-markdown-editor 5 | * @license MIT 6 | */ 7 | /* BASICS */ 8 | 9 | .CodeMirror { 10 | /* Set height, width, borders, and global font properties here */ 11 | font-family: monospace; 12 | height: 300px; 13 | color: black; 14 | } 15 | 16 | /* PADDING */ 17 | 18 | .CodeMirror-lines { 19 | padding: 4px 0; /* Vertical padding around content */ 20 | } 21 | .CodeMirror pre { 22 | padding: 0 4px; /* Horizontal padding of content */ 23 | } 24 | 25 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 26 | background-color: white; /* The little square between H and V scrollbars */ 27 | } 28 | 29 | /* GUTTER */ 30 | 31 | .CodeMirror-gutters { 32 | border-right: 1px solid #ddd; 33 | background-color: #f7f7f7; 34 | white-space: nowrap; 35 | } 36 | .CodeMirror-linenumbers {} 37 | .CodeMirror-linenumber { 38 | padding: 0 3px 0 5px; 39 | min-width: 20px; 40 | text-align: right; 41 | color: #999; 42 | white-space: nowrap; 43 | } 44 | 45 | .CodeMirror-guttermarker { color: black; } 46 | .CodeMirror-guttermarker-subtle { color: #999; } 47 | 48 | /* CURSOR */ 49 | 50 | .CodeMirror-cursor { 51 | border-left: 1px solid black; 52 | border-right: none; 53 | width: 0; 54 | } 55 | /* Shown when moving in bi-directional text */ 56 | .CodeMirror div.CodeMirror-secondarycursor { 57 | border-left: 1px solid silver; 58 | } 59 | .cm-fat-cursor .CodeMirror-cursor { 60 | width: auto; 61 | border: 0 !important; 62 | background: #7e7; 63 | } 64 | .cm-fat-cursor div.CodeMirror-cursors { 65 | z-index: 1; 66 | } 67 | 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-ruler { 98 | border-left: 1px solid #ccc; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3 {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 30px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -30px; margin-right: -30px; 163 | padding-bottom: 30px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | } 168 | .CodeMirror-sizer { 169 | position: relative; 170 | border-right: 30px solid transparent; 171 | } 172 | 173 | /* The fake, visible scrollbars. Used to force redraw during scrolling 174 | before actual scrolling happens, thus preventing shaking and 175 | flickering artifacts. */ 176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 | position: absolute; 178 | z-index: 6; 179 | display: none; 180 | } 181 | .CodeMirror-vscrollbar { 182 | right: 0; top: 0; 183 | overflow-x: hidden; 184 | overflow-y: scroll; 185 | } 186 | .CodeMirror-hscrollbar { 187 | bottom: 0; left: 0; 188 | overflow-y: hidden; 189 | overflow-x: scroll; 190 | } 191 | .CodeMirror-scrollbar-filler { 192 | right: 0; bottom: 0; 193 | } 194 | .CodeMirror-gutter-filler { 195 | left: 0; bottom: 0; 196 | } 197 | 198 | .CodeMirror-gutters { 199 | position: absolute; left: 0; top: 0; 200 | min-height: 100%; 201 | z-index: 3; 202 | } 203 | .CodeMirror-gutter { 204 | white-space: normal; 205 | height: 100%; 206 | display: inline-block; 207 | vertical-align: top; 208 | margin-bottom: -30px; 209 | /* Hack to make IE7 behave */ 210 | *zoom:1; 211 | *display:inline; 212 | } 213 | .CodeMirror-gutter-wrapper { 214 | position: absolute; 215 | z-index: 4; 216 | background: none !important; 217 | border: none !important; 218 | } 219 | .CodeMirror-gutter-background { 220 | position: absolute; 221 | top: 0; bottom: 0; 222 | z-index: 4; 223 | } 224 | .CodeMirror-gutter-elt { 225 | position: absolute; 226 | cursor: default; 227 | z-index: 4; 228 | } 229 | .CodeMirror-gutter-wrapper { 230 | -webkit-user-select: none; 231 | -moz-user-select: none; 232 | user-select: none; 233 | } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: none; 256 | font-variant-ligatures: none; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | overflow: auto; 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-code { 279 | outline: none; 280 | } 281 | 282 | /* Force content-box sizing for the elements where we expect it */ 283 | .CodeMirror-scroll, 284 | .CodeMirror-sizer, 285 | .CodeMirror-gutter, 286 | .CodeMirror-gutters, 287 | .CodeMirror-linenumber { 288 | -moz-box-sizing: content-box; 289 | box-sizing: content-box; 290 | } 291 | 292 | .CodeMirror-measure { 293 | position: absolute; 294 | width: 100%; 295 | height: 0; 296 | overflow: hidden; 297 | visibility: hidden; 298 | } 299 | 300 | .CodeMirror-cursor { position: absolute; } 301 | .CodeMirror-measure pre { position: static; } 302 | 303 | div.CodeMirror-cursors { 304 | visibility: hidden; 305 | position: relative; 306 | z-index: 3; 307 | } 308 | div.CodeMirror-dragcursors { 309 | visibility: visible; 310 | } 311 | 312 | .CodeMirror-focused div.CodeMirror-cursors { 313 | visibility: visible; 314 | } 315 | 316 | .CodeMirror-selected { background: #d9d9d9; } 317 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 318 | .CodeMirror-crosshair { cursor: crosshair; } 319 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 320 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 321 | 322 | .cm-searching { 323 | background: #ffa; 324 | background: rgba(255, 255, 0, .4); 325 | } 326 | 327 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 328 | .CodeMirror span { *vertical-align: text-bottom; } 329 | 330 | /* Used to force a border model for a node */ 331 | .cm-force-border { padding-right: .1px; } 332 | 333 | @media print { 334 | /* Hide the cursor when printing */ 335 | .CodeMirror div.CodeMirror-cursors { 336 | visibility: hidden; 337 | } 338 | } 339 | 340 | /* See issue #2901 */ 341 | .cm-tab-wrap-hack:after { content: ''; } 342 | 343 | /* Help users use markselection to safely style text background */ 344 | span.CodeMirror-selectedtext { background: none; } 345 | 346 | .CodeMirror { 347 | height: auto; 348 | min-height: 300px; 349 | border: 1px solid #ddd; 350 | border-bottom-left-radius: 4px; 351 | border-bottom-right-radius: 4px; 352 | padding: 10px; 353 | font: inherit; 354 | z-index: 1; 355 | } 356 | 357 | .CodeMirror-scroll { 358 | min-height: 300px 359 | } 360 | 361 | .CodeMirror-fullscreen { 362 | background: #fff; 363 | position: fixed !important; 364 | top: 50px; 365 | left: 0; 366 | right: 0; 367 | bottom: 0; 368 | height: auto; 369 | z-index: 9; 370 | } 371 | 372 | .CodeMirror-sided { 373 | width: 50% !important; 374 | } 375 | 376 | .editor-toolbar { 377 | position: relative; 378 | opacity: .6; 379 | -webkit-user-select: none; 380 | -moz-user-select: none; 381 | -ms-user-select: none; 382 | -o-user-select: none; 383 | user-select: none; 384 | padding: 0 10px; 385 | border-top: 1px solid #bbb; 386 | border-left: 1px solid #bbb; 387 | border-right: 1px solid #bbb; 388 | border-top-left-radius: 4px; 389 | border-top-right-radius: 4px; 390 | } 391 | 392 | .editor-toolbar:after, 393 | .editor-toolbar:before { 394 | display: block; 395 | content: ' '; 396 | height: 1px; 397 | } 398 | 399 | .editor-toolbar:before { 400 | margin-bottom: 8px 401 | } 402 | 403 | .editor-toolbar:after { 404 | margin-top: 8px 405 | } 406 | 407 | .editor-toolbar:hover, 408 | .editor-wrapper input.title:focus, 409 | .editor-wrapper input.title:hover { 410 | opacity: .8 411 | } 412 | 413 | .editor-toolbar.fullscreen { 414 | width: 100%; 415 | height: 50px; 416 | overflow-x: auto; 417 | overflow-y: hidden; 418 | white-space: nowrap; 419 | padding-top: 10px; 420 | padding-bottom: 10px; 421 | box-sizing: border-box; 422 | background: #fff; 423 | border: 0; 424 | position: fixed; 425 | top: 0; 426 | left: 0; 427 | opacity: 1; 428 | z-index: 9; 429 | } 430 | 431 | .editor-toolbar.fullscreen::before { 432 | width: 20px; 433 | height: 50px; 434 | background: -moz-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); 435 | background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 1)), color-stop(100%, rgba(255, 255, 255, 0))); 436 | background: -webkit-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); 437 | background: -o-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); 438 | background: -ms-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); 439 | background: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); 440 | position: fixed; 441 | top: 0; 442 | left: 0; 443 | margin: 0; 444 | padding: 0; 445 | } 446 | 447 | .editor-toolbar.fullscreen::after { 448 | width: 20px; 449 | height: 50px; 450 | background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); 451 | background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(255, 255, 255, 1))); 452 | background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); 453 | background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); 454 | background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); 455 | background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); 456 | position: fixed; 457 | top: 0; 458 | right: 0; 459 | margin: 0; 460 | padding: 0; 461 | } 462 | 463 | .editor-toolbar a { 464 | display: inline-block; 465 | text-align: center; 466 | text-decoration: none!important; 467 | color: #2c3e50!important; 468 | width: 30px; 469 | height: 30px; 470 | margin: 0; 471 | border: 1px solid transparent; 472 | border-radius: 3px; 473 | cursor: pointer; 474 | } 475 | 476 | .editor-toolbar a.active, 477 | .editor-toolbar a:hover { 478 | background: #fcfcfc; 479 | border-color: #95a5a6; 480 | } 481 | 482 | .editor-toolbar a:before { 483 | line-height: 30px 484 | } 485 | 486 | .editor-toolbar i.separator { 487 | display: inline-block; 488 | width: 0; 489 | border-left: 1px solid #d9d9d9; 490 | border-right: 1px solid #fff; 491 | color: transparent; 492 | text-indent: -10px; 493 | margin: 0 6px; 494 | } 495 | 496 | .editor-toolbar a.fa-header-x:after { 497 | font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; 498 | font-size: 65%; 499 | vertical-align: text-bottom; 500 | position: relative; 501 | top: 2px; 502 | } 503 | 504 | .editor-toolbar a.fa-header-1:after { 505 | content: "1"; 506 | } 507 | 508 | .editor-toolbar a.fa-header-2:after { 509 | content: "2"; 510 | } 511 | 512 | .editor-toolbar a.fa-header-3:after { 513 | content: "3"; 514 | } 515 | 516 | .editor-toolbar a.fa-header-bigger:after { 517 | content: "▲"; 518 | } 519 | 520 | .editor-toolbar a.fa-header-smaller:after { 521 | content: "▼"; 522 | } 523 | 524 | .editor-toolbar.disabled-for-preview a:not(.no-disable) { 525 | pointer-events: none; 526 | background: #fff; 527 | border-color: transparent; 528 | text-shadow: inherit; 529 | } 530 | 531 | @media only screen and (max-width: 700px) { 532 | .editor-toolbar a.no-mobile { 533 | display: none; 534 | } 535 | } 536 | 537 | .editor-statusbar { 538 | padding: 8px 10px; 539 | font-size: 12px; 540 | color: #959694; 541 | text-align: right; 542 | } 543 | 544 | .editor-statusbar span { 545 | display: inline-block; 546 | min-width: 4em; 547 | margin-left: 1em; 548 | } 549 | 550 | .editor-statusbar .lines:before { 551 | content: 'lines: ' 552 | } 553 | 554 | .editor-statusbar .words:before { 555 | content: 'words: ' 556 | } 557 | 558 | .editor-statusbar .characters:before { 559 | content: 'characters: ' 560 | } 561 | 562 | .editor-preview { 563 | padding: 10px; 564 | position: absolute; 565 | width: 100%; 566 | height: 100%; 567 | top: 0; 568 | left: 0; 569 | background: #fafafa; 570 | z-index: 7; 571 | overflow: auto; 572 | display: none; 573 | box-sizing: border-box; 574 | } 575 | 576 | .editor-preview-side { 577 | padding: 10px; 578 | position: fixed; 579 | bottom: 0; 580 | width: 50%; 581 | top: 50px; 582 | right: 0; 583 | background: #fafafa; 584 | z-index: 9; 585 | overflow: auto; 586 | display: none; 587 | box-sizing: border-box; 588 | border: 1px solid #ddd; 589 | } 590 | 591 | .editor-preview-active-side { 592 | display: block 593 | } 594 | 595 | .editor-preview-active { 596 | display: block 597 | } 598 | 599 | .editor-preview>p, 600 | .editor-preview-side>p { 601 | margin-top: 0 602 | } 603 | 604 | .editor-preview pre, 605 | .editor-preview-side pre { 606 | background: #eee; 607 | margin-bottom: 10px; 608 | } 609 | 610 | .editor-preview table td, 611 | .editor-preview table th, 612 | .editor-preview-side table td, 613 | .editor-preview-side table th { 614 | border: 1px solid #ddd; 615 | padding: 5px; 616 | } 617 | 618 | .CodeMirror .CodeMirror-code .cm-tag { 619 | color: #63a35c; 620 | } 621 | 622 | .CodeMirror .CodeMirror-code .cm-attribute { 623 | color: #795da3; 624 | } 625 | 626 | .CodeMirror .CodeMirror-code .cm-string { 627 | color: #183691; 628 | } 629 | 630 | .CodeMirror .CodeMirror-selected { 631 | background: #d9d9d9; 632 | } 633 | 634 | .CodeMirror .CodeMirror-code .cm-header-1 { 635 | font-size: 200%; 636 | line-height: 200%; 637 | } 638 | 639 | .CodeMirror .CodeMirror-code .cm-header-2 { 640 | font-size: 160%; 641 | line-height: 160%; 642 | } 643 | 644 | .CodeMirror .CodeMirror-code .cm-header-3 { 645 | font-size: 125%; 646 | line-height: 125%; 647 | } 648 | 649 | .CodeMirror .CodeMirror-code .cm-header-4 { 650 | font-size: 110%; 651 | line-height: 110%; 652 | } 653 | 654 | .CodeMirror .CodeMirror-code .cm-comment { 655 | background: rgba(0, 0, 0, .05); 656 | border-radius: 2px; 657 | } 658 | 659 | .CodeMirror .CodeMirror-code .cm-link { 660 | color: #7f8c8d; 661 | } 662 | 663 | .CodeMirror .CodeMirror-code .cm-url { 664 | color: #aab2b3; 665 | } 666 | 667 | .CodeMirror .CodeMirror-code .cm-strikethrough { 668 | text-decoration: line-through; 669 | } 670 | 671 | .CodeMirror .CodeMirror-placeholder { 672 | opacity: .5; 673 | } 674 | .CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) { 675 | background: rgba(255, 0, 0, .15); 676 | } -------------------------------------------------------------------------------- /admin/src/assets/simplemde.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * simplemde v1.11.2 3 | * Copyright Next Step Webs, Inc. 4 | * @link https://github.com/NextStepWebs/simplemde-markdown-editor 5 | * @license MIT 6 | */ 7 | .CodeMirror{color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected,.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;min-height:300px;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:1}.CodeMirror-scroll{min-height:300px}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-code .cm-tag{color:#63a35c}.CodeMirror .CodeMirror-code .cm-attribute{color:#795da3}.CodeMirror .CodeMirror-code .cm-string{color:#183691}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .CodeMirror-placeholder{opacity:.5}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)} -------------------------------------------------------------------------------- /admin/src/assets/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/tag.png -------------------------------------------------------------------------------- /admin/src/assets/use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/admin/src/assets/use.png -------------------------------------------------------------------------------- /admin/src/component/AddArticLabel.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /admin/src/component/ArticleEdit.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 263 | 264 | 345 | -------------------------------------------------------------------------------- /admin/src/component/ArticleLabel.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 86 | 87 | 137 | -------------------------------------------------------------------------------- /admin/src/component/ArticleList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 114 | 115 | 180 | -------------------------------------------------------------------------------- /admin/src/component/ArticlePreview.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 72 | 73 | -------------------------------------------------------------------------------- /admin/src/component/PersonalCenter.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 53 | 54 | -------------------------------------------------------------------------------- /admin/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import VueResource from 'vue-resource' 4 | import VueRouter from 'vue-router' 5 | import 'element-ui/lib/theme-default/index.css' 6 | import ElementUI from 'element-ui' 7 | import articleList from './component/ArticleList.vue' 8 | import articleEdit from './component/ArticleEdit.vue' 9 | import atricleLabel from './component/ArticleLabel.vue' 10 | import personalCenter from './component/PersonalCenter.vue' 11 | import articlePreview from './component/ArticlePreview.vue' 12 | 13 | Vue.use(VueResource) 14 | Vue.use(VueRouter) 15 | Vue.use(ElementUI) 16 | 17 | const router = new VueRouter({ 18 | routes: [ 19 | {path: '/', component: personalCenter }, 20 | {path: '/articleList', component: articleList, 21 | children: [ 22 | {path: 'articleEdit', component: articleEdit}, 23 | {path: 'articlePreview:id', component: articlePreview}, 24 | ] 25 | }, 26 | {path: '/atricleLabel', component: atricleLabel}, 27 | {path: '/personalCenter', component: personalCenter}, 28 | // {path: '/about', component: about}, 29 | // {path: '/articleDetails:id', component: articleDetails}, 30 | // {path: '/classify', component: classify}, 31 | // {path: '/label', component: label}, 32 | ] 33 | }) 34 | 35 | new Vue({ 36 | el: '#app', 37 | router: router, 38 | render: h => h(App) 39 | }) 40 | -------------------------------------------------------------------------------- /admin/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: './src/main.js', 6 | output: { 7 | path: path.resolve(__dirname, '../dist'), 8 | publicPath: '/dist/', 9 | filename: 'admin.js' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.vue$/, 15 | loader: 'vue-loader', 16 | options: { 17 | // vue-loader options go here 18 | } 19 | }, 20 | { 21 | test: /\.js$/, 22 | loader: 'babel-loader', 23 | exclude: /node_modules/ 24 | }, 25 | { 26 | test: /\.css$/, 27 | loader: 'style-loader!css-loader' 28 | }, 29 | { 30 | test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/, 31 | loader: 'file-loader' 32 | }, 33 | { 34 | test: /\.(png|jpe?g|gif|svg)(\?\S*)?$/, 35 | loader: 'file-loader', 36 | query: { 37 | name: '[name].[ext]?[hash]' 38 | } 39 | } 40 | ] 41 | }, 42 | resolve: { 43 | alias: { 44 | 'vue$': 'vue/dist/vue.common.js' 45 | } 46 | }, 47 | devServer: { 48 | historyApiFallback: true, 49 | noInfo: true, 50 | proxy: [ 51 | { 52 | context: ['/api'], 53 | target: 'http://localhost:7000', 54 | changeOrigin: true, 55 | secure: false 56 | } 57 | ] 58 | }, 59 | devtool: '#eval-source-map' 60 | } 61 | 62 | if (process.env.NODE_ENV === 'production') { 63 | module.exports.devtool = '#source-map' 64 | // http://vue-loader.vuejs.org/en/workflow/production.html 65 | module.exports.plugins = (module.exports.plugins || []).concat([ 66 | new webpack.DefinePlugin({ 67 | 'process.env': { 68 | NODE_ENV: '"production"' 69 | } 70 | }), 71 | new webpack.optimize.UglifyJsPlugin({ 72 | sourceMap: true, 73 | compress: { 74 | warnings: false 75 | } 76 | }), 77 | new webpack.LoaderOptionsPlugin({ 78 | minimize: true 79 | }) 80 | ]) 81 | } 82 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var bodyParser = require('body-parser'); 5 | var mime = require('mime'); 6 | var router = require('./server/router'); 7 | var compression = require('compression'); 8 | var session = require('express-session'); 9 | var FileStore = require('session-file-store')(session); 10 | var app = express(); 11 | 12 | var resolve = file => path.resolve(__dirname, file); 13 | app.use(compression()); 14 | app.use('/dist', express.static(resolve('./dist'))); 15 | 16 | app.use(bodyParser.json()); 17 | app.use(bodyParser.urlencoded({ extended: true })); 18 | 19 | var identityKey = 'skey'; 20 | 21 | app.use(session({ 22 | name: identityKey, 23 | secret: 'chyingp', // 用来对session id相关的cookie进行签名 24 | store: new FileStore(), // 本地存储session(文本文件,也可以选择其他store,比如redis的) 25 | saveUninitialized: false, // 是否自动保存未初始化的会话,建议false 26 | resave: false, // 是否每次都重新保存会话,建议false 27 | cookie: { 28 | maxAge: 15 * 60 * 1000 // 有效期,单位是毫秒, 这里设置的是15分钟 29 | } 30 | })); 31 | 32 | app.use(router) 33 | 34 | // 后台管理页 35 | app.get('/admin', function(req, res) { 36 | var sess = req.session; 37 | var loginUser = sess.loginUser; 38 | var isLogined = !!loginUser; 39 | if (isLogined){ 40 | console.log('已登录') 41 | var html = fs.readFileSync(resolve('./' + 'admin.html'), 'utf-8'); 42 | }else{ 43 | console.log('未登录') 44 | var html = fs.readFileSync(resolve('./' + 'login.html'), 'utf-8'); 45 | } 46 | res.send(html) 47 | }); 48 | 49 | // 博客首页 50 | app.get('*', function(req, res) { 51 | var html = fs.readFileSync(resolve('./' + 'index.html'), 'utf-8'); 52 | res.send(html) 53 | }); 54 | 55 | app.listen(process.env.PORT || 7000, function() { 56 | console.log("应用实例,访问地址为 localhost:7000") 57 | }); 58 | -------------------------------------------------------------------------------- /dist/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/add.png -------------------------------------------------------------------------------- /dist/adminlabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/adminlabel.png -------------------------------------------------------------------------------- /dist/article-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/article-title.png -------------------------------------------------------------------------------- /dist/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/article.png -------------------------------------------------------------------------------- /dist/b02bdc1b846fd65473922f5f62832108.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/b02bdc1b846fd65473922f5f62832108.ttf -------------------------------------------------------------------------------- /dist/build.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/build.js.gz -------------------------------------------------------------------------------- /dist/d2f69a92faa6fe990d2e613c358be705.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/d2f69a92faa6fe990d2e613c358be705.woff -------------------------------------------------------------------------------- /dist/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/labels.png -------------------------------------------------------------------------------- /dist/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/logo.png -------------------------------------------------------------------------------- /dist/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/tag.png -------------------------------------------------------------------------------- /dist/use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/dist/use.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | myapp 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 105 | 106 |
107 | 116 |
117 | 143 | 144 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myapp", 3 | "description": "this is weiwei blog", 4 | "version": "1.0.0", 5 | "author": "linguowei <1368033036@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --inline --hot", 9 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules", 10 | "server": "supervisor ./app.js" 11 | }, 12 | "dependencies": { 13 | "bluebird": "^3.4.7", 14 | "body-parser": "^1.15.2", 15 | "compression-webpack-plugin": "^0.4.0", 16 | "element-ui": "^1.0.5", 17 | "express": "^4.14.0", 18 | "express-session": "^1.15.2", 19 | "extract-text-webpack-plugin": "^2.1.0", 20 | "gitment": "0.0.3", 21 | "highlight.js": "^9.8.0", 22 | "marked": "^0.3.6", 23 | "mime": "^1.3.4", 24 | "mongoose": "^4.7.6", 25 | "session-file-store": "^1.0.0", 26 | "supervisor": "^0.12.0", 27 | "vue": "^2.1.0", 28 | "vue-resource": "^1.0.3", 29 | "vue-router": "^2.1.1", 30 | "vuex": "^2.0.0" 31 | }, 32 | "devDependencies": { 33 | "babel-core": "^6.0.0", 34 | "babel-loader": "^6.0.0", 35 | "babel-plugin-component": "^0.6.0", 36 | "babel-preset-es2015": "^6.0.0", 37 | "cross-env": "^3.0.0", 38 | "css-loader": "^0.25.0", 39 | "file-loader": "^0.9.0", 40 | "style-loader": "^0.13.1", 41 | "vue-loader": "^10.0.0", 42 | "vue-template-compiler": "^2.1.0", 43 | "webpack": "^2.1.0-beta.25", 44 | "webpack-dev-server": "^2.1.0-beta.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/db.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | mongoose.Promise = require('bluebird') 3 | 4 | // mongoose.connect('mongodb://wei1:0987654321@ds161018.mlab.com:61018/weiwei') 5 | mongoose.connect('mongodb://localhost:27017/weiweiblog') 6 | 7 | var userSchema = new mongoose.Schema({ 8 | name: String, 9 | pwd: String, 10 | }) 11 | 12 | var articleSchema = new mongoose.Schema({ 13 | title: String, 14 | date: Date, 15 | articleContent: String, 16 | state: String, 17 | label: String, 18 | }) 19 | 20 | var tagSchema = new mongoose.Schema({ 21 | tagName: String, 22 | tagNumber: Number, 23 | }) 24 | 25 | var personalInformationSchema = new mongoose.Schema({ 26 | name: String, 27 | individualitySignature: String, 28 | introduce: String, 29 | }) 30 | 31 | var Models = { 32 | User: mongoose.model('User', userSchema), 33 | Article: mongoose.model('Article', articleSchema), 34 | TagList: mongoose.model('TagList', tagSchema), 35 | PersonalInformation: mongoose.model('PersonalInformation', personalInformationSchema), 36 | } 37 | 38 | module.exports = Models 39 | -------------------------------------------------------------------------------- /server/router.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var users = require('./user').items; 4 | var db = require('./db'); 5 | 6 | var findUser = function(name, password){ 7 | return users.find(function(item){ 8 | return item.name === name && item.password === password; 9 | }); 10 | }; 11 | // 登录接口 12 | router.post('/api/login', function(req, res){ 13 | var sess = req.session; 14 | var user = findUser(req.body.name, req.body.pwd); 15 | 16 | if(user){ 17 | req.session.regenerate(function(err) { 18 | if(err){ 19 | return res.json({code: 2, msg: '登录失败'}); 20 | } 21 | req.session.loginUser = user.name; 22 | res.json({code: 0, msg: '登录成功'}); 23 | }); 24 | }else{ 25 | res.json({code: 1, msg: '账号或密码错误'}); 26 | } 27 | }) 28 | // 查询文章列表路由 用于博客前端展示数据不包含草稿内容 29 | router.get('/api/articleList', function(req, res){ 30 | db.Article.find({state: "publish"}, function(err, docs){ 31 | if (err) { 32 | console.log('出错'+ err); 33 | return 34 | } 35 | res.json(docs) 36 | }) 37 | }); 38 | // 按标签ID查询文章列表路由 用于博客前端展示数据不包含草稿内容 39 | router.post('/api/articleList', function(req, res){ 40 | db.TagList.find({_id: req.body.tagId}, function(err, docs){ 41 | if (err) { 42 | res.status(500).send(); 43 | return 44 | } 45 | db.Article.find({label: docs[0].tagName,state: "publish"}, function(err, docs){ 46 | if (err) { 47 | res.status(500).send(); 48 | return 49 | } 50 | res.json(docs) 51 | }) 52 | }) 53 | }); 54 | // 查询文章列表路由 用于博客后端管理系统包含草稿和已发布文章数据 55 | router.get('/api/admin/articleList', function(req, res){ 56 | db.Article.find({}, function(err, docs){ 57 | if (err) { 58 | console.log('出错'+ err); 59 | return 60 | } 61 | res.json(docs) 62 | }) 63 | }); 64 | // 查询文章列表路由(根据标签返回对应的文章列表) 用于博客后端管理系统包含草稿和已发布文章数据 65 | router.post('/api/admin/articleList', function(req, res){ 66 | db.Article.find({label: req.body.label}, function(err, docs){ 67 | if (err) { 68 | console.log('出错'+ err); 69 | return 70 | } 71 | res.json(docs) 72 | }) 73 | }); 74 | // 查询文章详情路由 75 | router.get('/api/articleDetails/:id', function(req, res){ 76 | db.Article.findOne({_id: req.params.id}, function(err, docs){ 77 | if (err) { 78 | return 79 | } 80 | res.send(docs) 81 | }) 82 | }); 83 | router.post('/api/articleDetails', function(req, res){ 84 | db.Article.findOne({_id: req.body.id}, function(err, docs){ 85 | if (err) { 86 | return 87 | } 88 | res.send(docs) 89 | }) 90 | }); 91 | // 文章保存路由 92 | router.post('/api/saveArticle', function(req, res){ 93 | new db.Article(req.body.articleInformation).save(function(error){ 94 | if (error) { 95 | res.status(500).send() 96 | return 97 | } 98 | if (req.body.articleInformation.state != 'draft') { 99 | db.Article.find({label:req.body.articleInformation.label},function(err, ArticleList){ 100 | if (err) { 101 | return 102 | } 103 | db.TagList.find({tagName:req.body.articleInformation.label}, function(err, docs){ 104 | if(docs.length>0){ 105 | docs[0].tagNumber = ArticleList.length 106 | db.TagList(docs[0]).save(function(error){}) 107 | } 108 | }) 109 | }) 110 | } 111 | res.send() 112 | }) 113 | }); 114 | 115 | // 文章更新路由 116 | router.post('/api/updateArticle', function(req, res){ 117 | db.Article.find({_id: req.body.obj._id}, function(err, docs){ 118 | if(err){ 119 | return 120 | } 121 | docs[0].title = req.body.obj.title 122 | docs[0].articleContent = req.body.obj.articleContent 123 | // 不更新文章更改时间 124 | docs[0].date = docs[0].date 125 | docs[0].state = req.body.obj.state 126 | docs[0].label = req.body.obj.label 127 | db.Article(docs[0]).save(function(err){ 128 | if (err){ 129 | res.status(500).send(); 130 | return 131 | } 132 | res.send() 133 | }) 134 | }) 135 | }); 136 | 137 | // 删除文章 138 | router.post('/api/delect/article', function(req, res){ 139 | db.Article.remove({_id: req.body._id}, function(err, docs){ 140 | if (err) { 141 | res.status(500).send(); 142 | return 143 | } 144 | res.send() 145 | }) 146 | }) 147 | 148 | // 文章标签查询路由 149 | router.get('/api/getArticleLabel', function(req, res){ 150 | // db.Article.find({},function(err, docs){ 151 | // if (err) { 152 | // return 153 | // } 154 | // var tagList = []; 155 | // var tagObj = {}; 156 | // for (let i=0; i0){ 220 | docs[0].name = req.body.form.name 221 | docs[0].individualitySignature = req.body.form.individualitySignature 222 | docs[0].introduce = req.body.form.introduce 223 | db.PersonalInformation(docs[0]).save(function(err){ 224 | if (err) { 225 | res.status(500).send(); 226 | return 227 | } 228 | res.send(); 229 | }) 230 | } else { 231 | new db.PersonalInformation(req.body.from).save(function(err){ 232 | if (err){ 233 | res.status(500).send(); 234 | return 235 | } 236 | res.send(); 237 | }) 238 | } 239 | }) 240 | }) 241 | 242 | router.get('/api/personalInformation', function(req, res){ 243 | db.PersonalInformation.find({}, function(err, docs){ 244 | if (err) { 245 | res.status(500).send(); 246 | return 247 | } 248 | res.json(docs) 249 | }) 250 | }) 251 | 252 | module.exports = router -------------------------------------------------------------------------------- /server/user.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | items: [ 3 | {name: 'admin', password: '123456'} 4 | ] 5 | } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 98 | 99 | 102 | -------------------------------------------------------------------------------- /src/assets/atom-one-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Atom One Light by Daniel Gamage 4 | Original One Light Syntax theme from https://github.com/atom/one-light-syntax 5 | 6 | base: #fafafa 7 | mono-1: #383a42 8 | mono-2: #686b77 9 | mono-3: #a0a1a7 10 | hue-1: #0184bb 11 | hue-2: #4078f2 12 | hue-3: #a626a4 13 | hue-4: #50a14f 14 | hue-5: #e45649 15 | hue-5-2: #c91243 16 | hue-6: #986801 17 | hue-6-2: #c18401 18 | 19 | */ 20 | 21 | .hljs { 22 | display: block; 23 | overflow-x: auto; 24 | padding: 0.5em; 25 | color: #383a42; 26 | background: #fafafa; 27 | } 28 | 29 | .hljs-comment, 30 | .hljs-quote { 31 | color: #a0a1a7; 32 | font-style: italic; 33 | } 34 | 35 | .hljs-doctag, 36 | .hljs-keyword, 37 | .hljs-formula { 38 | color: #a626a4; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name, 43 | .hljs-selector-tag, 44 | .hljs-deletion, 45 | .hljs-subst { 46 | color: #e45649; 47 | } 48 | 49 | .hljs-literal { 50 | color: #0184bb; 51 | } 52 | 53 | .hljs-string, 54 | .hljs-regexp, 55 | .hljs-addition, 56 | .hljs-attribute, 57 | .hljs-meta-string { 58 | color: #50a14f; 59 | } 60 | 61 | .hljs-built_in, 62 | .hljs-class .hljs-title { 63 | color: #c18401; 64 | } 65 | 66 | .hljs-attr, 67 | .hljs-variable, 68 | .hljs-template-variable, 69 | .hljs-type, 70 | .hljs-selector-class, 71 | .hljs-selector-attr, 72 | .hljs-selector-pseudo, 73 | .hljs-number { 74 | color: #986801; 75 | } 76 | 77 | .hljs-symbol, 78 | .hljs-bullet, 79 | .hljs-link, 80 | .hljs-meta, 81 | .hljs-selector-id, 82 | .hljs-title { 83 | color: #4078f2; 84 | } 85 | 86 | .hljs-emphasis { 87 | font-style: italic; 88 | } 89 | 90 | .hljs-strong { 91 | font-weight: bold; 92 | } 93 | 94 | .hljs-link { 95 | text-decoration: underline; 96 | } 97 | -------------------------------------------------------------------------------- /src/assets/blueprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/src/assets/blueprint.png -------------------------------------------------------------------------------- /src/assets/gitment/constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var LS_ACCESS_TOKEN_KEY = exports.LS_ACCESS_TOKEN_KEY = 'gitment-comments-token'; 7 | var LS_USER_KEY = exports.LS_USER_KEY = 'gitment-user-info'; 8 | 9 | var NOT_INITIALIZED_ERROR = exports.NOT_INITIALIZED_ERROR = new Error('Comments Not Initialized'); 10 | //# sourceMappingURL=constants.js.map -------------------------------------------------------------------------------- /src/assets/gitment/constants.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/constants.js"],"names":["LS_ACCESS_TOKEN_KEY","LS_USER_KEY","NOT_INITIALIZED_ERROR","Error"],"mappings":";;;;;AAAO,IAAMA,oDAAsB,wBAA5B;AACA,IAAMC,oCAAc,mBAApB;;AAEA,IAAMC,wDAAwB,IAAIC,KAAJ,CAAU,0BAAV,CAA9B","file":"constants.js","sourcesContent":["export const LS_ACCESS_TOKEN_KEY = 'gitment-comments-token'\nexport const LS_USER_KEY = 'gitment-user-info'\n\nexport const NOT_INITIALIZED_ERROR = new Error('Comments Not Initialized')\n"]} -------------------------------------------------------------------------------- /src/assets/gitment/gitment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 | 5 | var _mobx = require('mobx'); 6 | 7 | var _constants = require('./constants'); 8 | 9 | var _utils = require('./utils'); 10 | 11 | var _default = require('./theme/default'); 12 | 13 | var _default2 = _interopRequireDefault(_default); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 18 | 19 | var scope = 'public_repo'; 20 | 21 | function extendRenderer(instance, renderer) { 22 | instance[renderer] = function (container) { 23 | var targetContainer = (0, _utils.getTargetContainer)(container); 24 | var render = instance.theme[renderer] || instance.defaultTheme[renderer]; 25 | 26 | (0, _mobx.autorun)(function () { 27 | var e = render(instance.state, instance); 28 | if (targetContainer.firstChild) { 29 | targetContainer.replaceChild(e, targetContainer.firstChild); 30 | } else { 31 | targetContainer.appendChild(e); 32 | } 33 | }); 34 | 35 | return targetContainer; 36 | }; 37 | } 38 | 39 | var Gitment = function () { 40 | _createClass(Gitment, [{ 41 | key: 'accessToken', 42 | get: function get() { 43 | return localStorage.getItem(_constants.LS_ACCESS_TOKEN_KEY); 44 | }, 45 | set: function set(token) { 46 | localStorage.setItem(_constants.LS_ACCESS_TOKEN_KEY, token); 47 | } 48 | }, { 49 | key: 'loginLink', 50 | get: function get() { 51 | var oauthUri = 'https://github.com/login/oauth/authorize'; 52 | var redirect_uri = this.oauth.redirect_uri || window.location.href; 53 | 54 | var oauthParams = Object.assign({ 55 | scope: scope, 56 | redirect_uri: redirect_uri 57 | }, this.oauth); 58 | 59 | return '' + oauthUri + _utils.Query.stringify(oauthParams); 60 | } 61 | }]); 62 | 63 | function Gitment() { 64 | var _this = this; 65 | 66 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 67 | 68 | _classCallCheck(this, Gitment); 69 | 70 | this.defaultTheme = _default2.default; 71 | this.useTheme(_default2.default); 72 | 73 | Object.assign(this, { 74 | id: window.location.href, 75 | title: window.document.title, 76 | link: window.location.href, 77 | desc: '', 78 | labels: [], 79 | theme: _default2.default, 80 | oauth: {}, 81 | perPage: 20, 82 | maxCommentHeight: 250 83 | }, options); 84 | 85 | this.useTheme(this.theme); 86 | 87 | var user = {}; 88 | try { 89 | var userInfo = localStorage.getItem(_constants.LS_USER_KEY); 90 | if (this.accessToken && userInfo) { 91 | Object.assign(user, JSON.parse(userInfo), { 92 | fromCache: true 93 | }); 94 | } 95 | } catch (e) { 96 | localStorage.removeItem(_constants.LS_USER_KEY); 97 | } 98 | 99 | this.state = (0, _mobx.observable)({ 100 | user: user, 101 | error: null, 102 | meta: {}, 103 | comments: undefined, 104 | reactions: [], 105 | commentReactions: {}, 106 | currentPage: 1 107 | }); 108 | 109 | var query = _utils.Query.parse(); 110 | if (query.code) { 111 | var _oauth = this.oauth, 112 | client_id = _oauth.client_id, 113 | client_secret = _oauth.client_secret; 114 | 115 | var code = query.code; 116 | delete query.code; 117 | var search = _utils.Query.stringify(query); 118 | var replacedUrl = '' + window.location.origin + window.location.pathname + search + window.location.hash; 119 | history.replaceState({}, '', replacedUrl); 120 | 121 | Object.assign(this, { 122 | id: replacedUrl, 123 | link: replacedUrl 124 | }, options); 125 | 126 | this.state.user.isLoggingIn = true; 127 | _utils.http.post('https://gh-oauth.imsun.net', { 128 | code: code, 129 | client_id: client_id, 130 | client_secret: client_secret 131 | }, '').then(function (data) { 132 | _this.accessToken = data.access_token; 133 | _this.update(); 134 | }).catch(function (e) { 135 | _this.state.user.isLoggingIn = false; 136 | alert(e); 137 | }); 138 | } else { 139 | this.update(); 140 | } 141 | } 142 | 143 | _createClass(Gitment, [{ 144 | key: 'init', 145 | value: function init() { 146 | var _this2 = this; 147 | 148 | return this.createIssue().then(function () { 149 | return _this2.loadComments(); 150 | }).then(function (comments) { 151 | _this2.state.error = null; 152 | return comments; 153 | }); 154 | } 155 | }, { 156 | key: 'useTheme', 157 | value: function useTheme() { 158 | var _this3 = this; 159 | 160 | var theme = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 161 | 162 | this.theme = theme; 163 | 164 | var renderers = Object.keys(this.theme); 165 | renderers.forEach(function (renderer) { 166 | return extendRenderer(_this3, renderer); 167 | }); 168 | } 169 | }, { 170 | key: 'update', 171 | value: function update() { 172 | var _this4 = this; 173 | 174 | return Promise.all([this.loadMeta(), this.loadUserInfo()]).then(function () { 175 | return Promise.all([_this4.loadComments().then(function () { 176 | return _this4.loadCommentReactions(); 177 | }), _this4.loadReactions()]); 178 | }).catch(function (e) { 179 | return _this4.state.error = e; 180 | }); 181 | } 182 | }, { 183 | key: 'markdown', 184 | value: function markdown(text) { 185 | return _utils.http.post('/markdown', { 186 | text: text, 187 | mode: 'gfm' 188 | }); 189 | } 190 | }, { 191 | key: 'createIssue', 192 | value: function createIssue() { 193 | var _this5 = this; 194 | 195 | var id = this.id, 196 | owner = this.owner, 197 | repo = this.repo, 198 | title = this.title, 199 | link = this.link, 200 | desc = this.desc, 201 | labels = this.labels; 202 | 203 | 204 | return _utils.http.post('/repos/' + owner + '/' + repo + '/issues', { 205 | title: title, 206 | labels: labels.concat(['gitment', id]), 207 | body: link + '\n\n' + desc 208 | }).then(function (meta) { 209 | _this5.state.meta = meta; 210 | return meta; 211 | }); 212 | } 213 | }, { 214 | key: 'getIssue', 215 | value: function getIssue() { 216 | if (this.state.meta.id) return Promise.resolve(this.state.meta); 217 | 218 | return this.loadMeta(); 219 | } 220 | }, { 221 | key: 'post', 222 | value: function post(body) { 223 | var _this6 = this; 224 | 225 | return this.getIssue().then(function (issue) { 226 | return _utils.http.post(issue.comments_url, { body: body }, ''); 227 | }).then(function (data) { 228 | _this6.state.meta.comments++; 229 | var pageCount = Math.ceil(_this6.state.meta.comments / _this6.perPage); 230 | if (_this6.state.currentPage === pageCount) { 231 | _this6.state.comments.push(data); 232 | } 233 | return data; 234 | }); 235 | } 236 | }, { 237 | key: 'loadMeta', 238 | value: function loadMeta() { 239 | var _this7 = this; 240 | 241 | var id = this.id, 242 | owner = this.owner, 243 | repo = this.repo; 244 | 245 | return _utils.http.get('/repos/' + owner + '/' + repo + '/issues', { 246 | creator: owner, 247 | labels: id 248 | }).then(function (issues) { 249 | if (!issues.length) return Promise.reject(_constants.NOT_INITIALIZED_ERROR); 250 | _this7.state.meta = issues[0]; 251 | return issues[0]; 252 | }); 253 | } 254 | }, { 255 | key: 'loadComments', 256 | value: function loadComments() { 257 | var _this8 = this; 258 | 259 | var page = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.state.currentPage; 260 | 261 | return this.getIssue().then(function (issue) { 262 | return _utils.http.get(issue.comments_url, { page: page, per_page: _this8.perPage }, ''); 263 | }).then(function (comments) { 264 | _this8.state.comments = comments; 265 | return comments; 266 | }); 267 | } 268 | }, { 269 | key: 'loadUserInfo', 270 | value: function loadUserInfo() { 271 | var _this9 = this; 272 | 273 | if (!this.accessToken) { 274 | this.logout(); 275 | return Promise.resolve({}); 276 | } 277 | 278 | return _utils.http.get('/user').then(function (user) { 279 | _this9.state.user = user; 280 | localStorage.setItem(_constants.LS_USER_KEY, JSON.stringify(user)); 281 | return user; 282 | }); 283 | } 284 | }, { 285 | key: 'loadReactions', 286 | value: function loadReactions() { 287 | var _this10 = this; 288 | 289 | if (!this.accessToken) { 290 | this.state.reactions = []; 291 | return Promise.resolve([]); 292 | } 293 | 294 | return this.getIssue().then(function (issue) { 295 | if (!issue.reactions.total_count) return []; 296 | return _utils.http.get(issue.reactions.url, {}, ''); 297 | }).then(function (reactions) { 298 | _this10.state.reactions = reactions; 299 | return reactions; 300 | }); 301 | } 302 | }, { 303 | key: 'loadCommentReactions', 304 | value: function loadCommentReactions() { 305 | var _this11 = this; 306 | 307 | if (!this.accessToken) { 308 | this.state.commentReactions = {}; 309 | return Promise.resolve([]); 310 | } 311 | 312 | var comments = this.state.comments; 313 | var comentReactions = {}; 314 | 315 | return Promise.all(comments.map(function (comment) { 316 | if (!comment.reactions.total_count) return []; 317 | 318 | var owner = _this11.owner, 319 | repo = _this11.repo; 320 | 321 | return _utils.http.get('/repos/' + owner + '/' + repo + '/issues/comments/' + comment.id + '/reactions', {}); 322 | })).then(function (reactionsArray) { 323 | comments.forEach(function (comment, index) { 324 | comentReactions[comment.id] = reactionsArray[index]; 325 | }); 326 | _this11.state.commentReactions = comentReactions; 327 | 328 | return comentReactions; 329 | }); 330 | } 331 | }, { 332 | key: 'login', 333 | value: function login() { 334 | window.location.href = this.loginLink; 335 | } 336 | }, { 337 | key: 'logout', 338 | value: function logout() { 339 | localStorage.removeItem(_constants.LS_ACCESS_TOKEN_KEY); 340 | localStorage.removeItem(_constants.LS_USER_KEY); 341 | this.state.user = {}; 342 | } 343 | }, { 344 | key: 'goto', 345 | value: function goto(page) { 346 | this.state.currentPage = page; 347 | this.state.comments = undefined; 348 | return this.loadComments(page); 349 | } 350 | }, { 351 | key: 'like', 352 | value: function like() { 353 | var _this12 = this; 354 | 355 | if (!this.accessToken) { 356 | alert('Login to Like'); 357 | return Promise.reject(); 358 | } 359 | 360 | var owner = this.owner, 361 | repo = this.repo; 362 | 363 | 364 | return _utils.http.post('/repos/' + owner + '/' + repo + '/issues/' + this.state.meta.number + '/reactions', { 365 | content: 'heart' 366 | }).then(function (reaction) { 367 | _this12.state.reactions.push(reaction); 368 | _this12.state.meta.reactions.heart++; 369 | }); 370 | } 371 | }, { 372 | key: 'unlike', 373 | value: function unlike() { 374 | var _this13 = this; 375 | 376 | if (!this.accessToken) return Promise.reject(); 377 | 378 | var _state = this.state, 379 | user = _state.user, 380 | reactions = _state.reactions; 381 | 382 | var index = reactions.findIndex(function (reaction) { 383 | return reaction.user.login === user.login; 384 | }); 385 | return _utils.http.delete('/reactions/' + reactions[index].id).then(function () { 386 | reactions.splice(index, 1); 387 | _this13.state.meta.reactions.heart--; 388 | }); 389 | } 390 | }, { 391 | key: 'likeAComment', 392 | value: function likeAComment(commentId) { 393 | var _this14 = this; 394 | 395 | if (!this.accessToken) { 396 | alert('Login to Like'); 397 | return Promise.reject(); 398 | } 399 | 400 | var owner = this.owner, 401 | repo = this.repo; 402 | 403 | var comment = this.state.comments.find(function (comment) { 404 | return comment.id === commentId; 405 | }); 406 | 407 | return _utils.http.post('/repos/' + owner + '/' + repo + '/issues/comments/' + commentId + '/reactions', { 408 | content: 'heart' 409 | }).then(function (reaction) { 410 | _this14.state.commentReactions[commentId].push(reaction); 411 | comment.reactions.heart++; 412 | }); 413 | } 414 | }, { 415 | key: 'unlikeAComment', 416 | value: function unlikeAComment(commentId) { 417 | if (!this.accessToken) return Promise.reject(); 418 | 419 | var reactions = this.state.commentReactions[commentId]; 420 | var comment = this.state.comments.find(function (comment) { 421 | return comment.id === commentId; 422 | }); 423 | var user = this.state.user; 424 | 425 | var index = reactions.findIndex(function (reaction) { 426 | return reaction.user.login === user.login; 427 | }); 428 | 429 | return _utils.http.delete('/reactions/' + reactions[index].id).then(function () { 430 | reactions.splice(index, 1); 431 | comment.reactions.heart--; 432 | }); 433 | } 434 | }]); 435 | 436 | return Gitment; 437 | }(); 438 | 439 | module.exports = Gitment; 440 | //# sourceMappingURL=gitment.js.map -------------------------------------------------------------------------------- /src/assets/gitment/gitment.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/gitment.js"],"names":["scope","extendRenderer","instance","renderer","container","targetContainer","render","theme","defaultTheme","e","state","firstChild","replaceChild","appendChild","Gitment","localStorage","getItem","token","setItem","oauthUri","redirect_uri","oauth","window","location","href","oauthParams","Object","assign","stringify","options","useTheme","id","title","document","link","desc","labels","perPage","maxCommentHeight","user","userInfo","accessToken","JSON","parse","fromCache","removeItem","error","meta","comments","undefined","reactions","commentReactions","currentPage","query","code","client_id","client_secret","search","replacedUrl","origin","pathname","hash","history","replaceState","isLoggingIn","post","then","data","access_token","update","catch","alert","createIssue","loadComments","renderers","keys","forEach","Promise","all","loadMeta","loadUserInfo","loadCommentReactions","loadReactions","text","mode","owner","repo","concat","body","resolve","getIssue","issue","comments_url","pageCount","Math","ceil","push","get","creator","issues","length","reject","page","per_page","logout","total_count","url","comentReactions","map","comment","index","reactionsArray","loginLink","number","content","reaction","heart","findIndex","login","delete","splice","commentId","find","module","exports"],"mappings":"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","file":"gitment.js","sourcesContent":["import { autorun, observable } from 'mobx'\n\nimport { LS_ACCESS_TOKEN_KEY, LS_USER_KEY, NOT_INITIALIZED_ERROR } from './constants'\nimport { getTargetContainer, http, Query } from './utils'\nimport defaultTheme from './theme/default'\n\nconst scope = 'public_repo'\n\nfunction extendRenderer(instance, renderer) {\n instance[renderer] = (container) => {\n const targetContainer = getTargetContainer(container)\n const render = instance.theme[renderer] || instance.defaultTheme[renderer]\n\n autorun(() => {\n const e = render(instance.state, instance)\n if (targetContainer.firstChild) {\n targetContainer.replaceChild(e, targetContainer.firstChild)\n } else {\n targetContainer.appendChild(e)\n }\n })\n\n return targetContainer\n }\n}\n\nclass Gitment {\n get accessToken() {\n return localStorage.getItem(LS_ACCESS_TOKEN_KEY)\n }\n set accessToken(token) {\n localStorage.setItem(LS_ACCESS_TOKEN_KEY, token)\n }\n\n get loginLink() {\n const oauthUri = 'https://github.com/login/oauth/authorize'\n const redirect_uri = this.oauth.redirect_uri || window.location.href\n\n const oauthParams = Object.assign({\n scope,\n redirect_uri,\n }, this.oauth)\n\n return `${oauthUri}${Query.stringify(oauthParams)}`\n }\n\n constructor(options = {}) {\n this.defaultTheme = defaultTheme\n this.useTheme(defaultTheme)\n\n Object.assign(this, {\n id: window.location.href,\n title: window.document.title,\n link: window.location.href,\n desc: '',\n labels: [],\n theme: defaultTheme,\n oauth: {},\n perPage: 20,\n maxCommentHeight: 250,\n }, options)\n\n this.useTheme(this.theme)\n\n const user = {}\n try {\n const userInfo = localStorage.getItem(LS_USER_KEY)\n if (this.accessToken && userInfo) {\n Object.assign(user, JSON.parse(userInfo), {\n fromCache: true,\n })\n }\n } catch (e) {\n localStorage.removeItem(LS_USER_KEY)\n }\n\n this.state = observable({\n user,\n error: null,\n meta: {},\n comments: undefined,\n reactions: [],\n commentReactions: {},\n currentPage: 1,\n })\n\n const query = Query.parse()\n if (query.code) {\n const { client_id, client_secret } = this.oauth\n const code = query.code\n delete query.code\n const search = Query.stringify(query)\n const replacedUrl = `${window.location.origin}${window.location.pathname}${search}${window.location.hash}`\n history.replaceState({}, '', replacedUrl)\n\n Object.assign(this, {\n id: replacedUrl,\n link: replacedUrl,\n }, options)\n\n this.state.user.isLoggingIn = true\n http.post('https://gh-oauth.imsun.net', {\n code,\n client_id,\n client_secret,\n }, '')\n .then(data => {\n this.accessToken = data.access_token\n this.update()\n })\n .catch(e => {\n this.state.user.isLoggingIn = false\n alert(e)\n })\n } else {\n this.update()\n }\n }\n\n init() {\n return this.createIssue()\n .then(() => this.loadComments())\n .then(comments => {\n this.state.error = null\n return comments\n })\n }\n\n useTheme(theme = {}) {\n this.theme = theme\n\n const renderers = Object.keys(this.theme)\n renderers.forEach(renderer => extendRenderer(this, renderer))\n }\n\n update() {\n return Promise.all([this.loadMeta(), this.loadUserInfo()])\n .then(() => Promise.all([\n this.loadComments().then(() => this.loadCommentReactions()),\n this.loadReactions(),\n ]))\n .catch(e => this.state.error = e)\n }\n\n markdown(text) {\n return http.post('/markdown', {\n text,\n mode: 'gfm',\n })\n }\n\n createIssue() {\n const { id, owner, repo, title, link, desc, labels } = this\n\n return http.post(`/repos/${owner}/${repo}/issues`, {\n title,\n labels: labels.concat(['gitment', id]),\n body: `${link}\\n\\n${desc}`,\n })\n .then((meta) => {\n this.state.meta = meta\n return meta\n })\n }\n\n getIssue() {\n if (this.state.meta.id) return Promise.resolve(this.state.meta)\n\n return this.loadMeta()\n }\n\n post(body) {\n return this.getIssue()\n .then(issue => http.post(issue.comments_url, { body }, ''))\n .then(data => {\n this.state.meta.comments++\n const pageCount = Math.ceil(this.state.meta.comments / this.perPage)\n if (this.state.currentPage === pageCount) {\n this.state.comments.push(data)\n }\n return data\n })\n }\n\n loadMeta() {\n const { id, owner, repo } = this\n return http.get(`/repos/${owner}/${repo}/issues`, {\n creator: owner,\n labels: id,\n })\n .then(issues => {\n if (!issues.length) return Promise.reject(NOT_INITIALIZED_ERROR)\n this.state.meta = issues[0]\n return issues[0]\n })\n }\n\n loadComments(page = this.state.currentPage) {\n return this.getIssue()\n .then(issue => http.get(issue.comments_url, { page, per_page: this.perPage }, ''))\n .then((comments) => {\n this.state.comments = comments\n return comments\n })\n }\n\n loadUserInfo() {\n if (!this.accessToken) {\n this.logout()\n return Promise.resolve({})\n }\n\n return http.get('/user')\n .then((user) => {\n this.state.user = user\n localStorage.setItem(LS_USER_KEY, JSON.stringify(user))\n return user\n })\n }\n\n loadReactions() {\n if (!this.accessToken) {\n this.state.reactions = []\n return Promise.resolve([])\n }\n\n return this.getIssue()\n .then((issue) => {\n if (!issue.reactions.total_count) return []\n return http.get(issue.reactions.url, {}, '')\n })\n .then((reactions) => {\n this.state.reactions = reactions\n return reactions\n })\n }\n\n loadCommentReactions() {\n if (!this.accessToken) {\n this.state.commentReactions = {}\n return Promise.resolve([])\n }\n\n const comments = this.state.comments\n const comentReactions = {}\n\n return Promise.all(comments.map((comment) => {\n if (!comment.reactions.total_count) return []\n\n const { owner, repo } = this\n return http.get(`/repos/${owner}/${repo}/issues/comments/${comment.id}/reactions`, {})\n }))\n .then(reactionsArray => {\n comments.forEach((comment, index) => {\n comentReactions[comment.id] = reactionsArray[index]\n })\n this.state.commentReactions = comentReactions\n\n return comentReactions\n })\n }\n\n login() {\n window.location.href = this.loginLink\n }\n\n logout() {\n localStorage.removeItem(LS_ACCESS_TOKEN_KEY)\n localStorage.removeItem(LS_USER_KEY)\n this.state.user = {}\n }\n\n goto(page) {\n this.state.currentPage = page\n this.state.comments = undefined\n return this.loadComments(page)\n }\n\n like() {\n if (!this.accessToken) {\n alert('Login to Like')\n return Promise.reject()\n }\n\n const { owner, repo } = this\n\n return http.post(`/repos/${owner}/${repo}/issues/${this.state.meta.number}/reactions`, {\n content: 'heart',\n })\n .then(reaction => {\n this.state.reactions.push(reaction)\n this.state.meta.reactions.heart++\n })\n }\n\n unlike() {\n if (!this.accessToken) return Promise.reject()\n\n\n const { user, reactions } = this.state\n const index = reactions.findIndex(reaction => reaction.user.login === user.login)\n return http.delete(`/reactions/${reactions[index].id}`)\n .then(() => {\n reactions.splice(index, 1)\n this.state.meta.reactions.heart--\n })\n }\n\n likeAComment(commentId) {\n if (!this.accessToken) {\n alert('Login to Like')\n return Promise.reject()\n }\n\n const { owner, repo } = this\n const comment = this.state.comments.find(comment => comment.id === commentId)\n\n return http.post(`/repos/${owner}/${repo}/issues/comments/${commentId}/reactions`, {\n content: 'heart',\n })\n .then(reaction => {\n this.state.commentReactions[commentId].push(reaction)\n comment.reactions.heart++\n })\n }\n\n unlikeAComment(commentId) {\n if (!this.accessToken) return Promise.reject()\n\n const reactions = this.state.commentReactions[commentId]\n const comment = this.state.comments.find(comment => comment.id === commentId)\n const { user } = this.state\n const index = reactions.findIndex(reaction => reaction.user.login === user.login)\n\n return http.delete(`/reactions/${reactions[index].id}`)\n .then(() => {\n reactions.splice(index, 1)\n comment.reactions.heart--\n })\n }\n}\n\nmodule.exports = Gitment\n"]} -------------------------------------------------------------------------------- /src/assets/gitment/icons.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | /** 7 | * Modified from https://github.com/evil-icons/evil-icons 8 | */ 9 | 10 | var close = exports.close = ''; 11 | var github = exports.github = ''; 12 | var heart = exports.heart = ''; 13 | var spinner = exports.spinner = ''; 14 | //# sourceMappingURL=icons.js.map -------------------------------------------------------------------------------- /src/assets/gitment/icons.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/icons.js"],"names":["close","github","heart","spinner"],"mappings":";;;;;AAAA;;;;AAIO,IAAMA,wBAAQ,gOAAd;AACA,IAAMC,0BAAS,0jBAAf;AACA,IAAMC,wBAAQ,iYAAd;AACA,IAAMC,4BAAU,48CAAhB","file":"icons.js","sourcesContent":["/**\n * Modified from https://github.com/evil-icons/evil-icons\n */\n\nexport const close = ''\nexport const github = ''\nexport const heart = ''\nexport const spinner = ''\n"]} -------------------------------------------------------------------------------- /src/assets/gitment/theme/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 8 | 9 | var _icons = require('../icons'); 10 | 11 | var _constants = require('../constants'); 12 | 13 | var _mobx = require('mobx'); 14 | 15 | Date.prototype.format = function (format) { 16 | var o = { 17 | "M+": this.getMonth() + 1, //month 18 | "d+": this.getDate(), //day 19 | "h+": this.getHours(), //hour 20 | "m+": this.getMinutes(), //minute 21 | "s+": this.getSeconds(), //second 22 | "q+": Math.floor((this.getMonth() + 3) / 3), //quarter 23 | "S": this.getMilliseconds() //millisecond 24 | }; 25 | if (/(y+)/.test(format)) { 26 | format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 27 | } 28 | for (var k in o) { 29 | if (new RegExp("(" + k + ")").test(format)) { 30 | format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); 31 | } 32 | } 33 | return format; 34 | }; 35 | 36 | function renderHeader(_ref, instance) { 37 | var meta = _ref.meta, 38 | user = _ref.user, 39 | reactions = _ref.reactions; 40 | 41 | var container = document.createElement('div'); 42 | container.lang = "en-US"; 43 | container.className = 'gitment-container gitment-header-container'; 44 | 45 | var likeButton = document.createElement('span'); 46 | var likedReaction = reactions.find(function (reaction) { 47 | return reaction.content === 'heart' && reaction.user.login === user.login; 48 | }); 49 | likeButton.className = 'gitment-header-like-btn'; 50 | likeButton.innerHTML = '\n ' + _icons.heart + '\n ' + (likedReaction ? 'Unlike' : 'Like') + '\n ' + (meta.reactions && meta.reactions.heart ? ' \u2022 ' + meta.reactions.heart + ' Liked' : '') + '\n '; 51 | 52 | if (likedReaction) { 53 | likeButton.classList.add('liked'); 54 | likeButton.onclick = function () { 55 | return instance.unlike(); 56 | }; 57 | } else { 58 | likeButton.classList.remove('liked'); 59 | likeButton.onclick = function () { 60 | return instance.like(); 61 | }; 62 | } 63 | container.appendChild(likeButton); 64 | 65 | var commentsCount = document.createElement('span'); 66 | commentsCount.innerHTML = '\n ' + (meta.comments ? ' \u2022 ' + meta.comments + ' Comments' : '') + '\n '; 67 | container.appendChild(commentsCount); 68 | 69 | var issueLink = document.createElement('a'); 70 | issueLink.className = 'gitment-header-issue-link'; 71 | issueLink.href = meta.html_url; 72 | issueLink.target = '_blank'; 73 | issueLink.innerText = 'Issue Page'; 74 | container.appendChild(issueLink); 75 | 76 | return container; 77 | } 78 | 79 | function renderComments(_ref2, instance) { 80 | var meta = _ref2.meta, 81 | comments = _ref2.comments, 82 | commentReactions = _ref2.commentReactions, 83 | currentPage = _ref2.currentPage, 84 | user = _ref2.user, 85 | error = _ref2.error; 86 | 87 | var container = document.createElement('div'); 88 | container.lang = "en-US"; 89 | container.className = 'gitment-container gitment-comments-container'; 90 | 91 | if (error) { 92 | var errorBlock = document.createElement('div'); 93 | errorBlock.className = 'gitment-comments-error'; 94 | 95 | if (error === _constants.NOT_INITIALIZED_ERROR && user.login && user.login.toLowerCase() === instance.owner.toLowerCase()) { 96 | var initHint = document.createElement('div'); 97 | var initButton = document.createElement('button'); 98 | initButton.className = 'gitment-comments-init-btn'; 99 | initButton.onclick = function () { 100 | initButton.setAttribute('disabled', true); 101 | instance.init().catch(function (e) { 102 | initButton.removeAttribute('disabled'); 103 | alert(e); 104 | }); 105 | }; 106 | initButton.innerText = '初始化评论'; 107 | initHint.appendChild(initButton); 108 | errorBlock.appendChild(initHint); 109 | } else { 110 | errorBlock.innerText = error; 111 | } 112 | container.appendChild(errorBlock); 113 | return container; 114 | } else if (comments === undefined) { 115 | var loading = document.createElement('div'); 116 | loading.innerText = '加载评论中...'; 117 | loading.className = 'gitment-comments-loading'; 118 | container.appendChild(loading); 119 | return container; 120 | } else if (!comments.length) { 121 | var emptyBlock = document.createElement('div'); 122 | emptyBlock.className = 'gitment-comments-empty'; 123 | emptyBlock.innerText = '暂无评论'; 124 | container.appendChild(emptyBlock); 125 | return container; 126 | } 127 | 128 | var commentsList = document.createElement('ul'); 129 | commentsList.className = 'gitment-comments-list'; 130 | 131 | comments.forEach(function (comment) { 132 | var createDate = new Date(comment.created_at); 133 | var updateDate = new Date(comment.updated_at); 134 | var commentItem = document.createElement('li'); 135 | commentItem.className = 'gitment-comment'; 136 | commentItem.innerHTML = '\n \n \n \n
\n
\n \n ' + comment.user.login + '\n \n \u521B\u5EFA\u4E8E\n ' + new Date(createDate).format('yyyy-MM-dd hh:mm:ss') + '\n ' + (createDate.toString() !== updateDate.toString() ? ' \u2022 edited' : '') + '\n
' + _icons.heart + ' ' + (comment.reactions.heart || '') + '
\n
\n
' + comment.body_html + '
\n
\n '; 137 | var likeButton = commentItem.querySelector('.gitment-comment-like-btn'); 138 | var likedReaction = commentReactions[comment.id] && commentReactions[comment.id].find(function (reaction) { 139 | return reaction.content === 'heart' && reaction.user.login === user.login; 140 | }); 141 | if (likedReaction) { 142 | likeButton.classList.add('liked'); 143 | likeButton.onclick = function () { 144 | return instance.unlikeAComment(comment.id); 145 | }; 146 | } else { 147 | likeButton.classList.remove('liked'); 148 | likeButton.onclick = function () { 149 | return instance.likeAComment(comment.id); 150 | }; 151 | } 152 | 153 | // dirty 154 | // use a blank image to trigger height calculating when element rendered 155 | var imgTrigger = document.createElement('img'); 156 | var markdownBody = commentItem.querySelector('.gitment-comment-body'); 157 | imgTrigger.className = 'gitment-hidden'; 158 | imgTrigger.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; 159 | imgTrigger.onload = function () { 160 | if (markdownBody.clientHeight > instance.maxCommentHeight) { 161 | markdownBody.classList.add('gitment-comment-body-folded'); 162 | markdownBody.style.maxHeight = instance.maxCommentHeight + 'px'; 163 | markdownBody.title = 'Click to Expand'; 164 | markdownBody.onclick = function () { 165 | markdownBody.classList.remove('gitment-comment-body-folded'); 166 | markdownBody.style.maxHeight = ''; 167 | markdownBody.title = ''; 168 | markdownBody.onclick = null; 169 | }; 170 | } 171 | }; 172 | commentItem.appendChild(imgTrigger); 173 | 174 | commentsList.appendChild(commentItem); 175 | }); 176 | 177 | container.appendChild(commentsList); 178 | 179 | if (meta) { 180 | var pageCount = Math.ceil(meta.comments / instance.perPage); 181 | if (pageCount > 1) { 182 | var pagination = document.createElement('ul'); 183 | pagination.className = 'gitment-comments-pagination'; 184 | 185 | if (currentPage > 1) { 186 | var previousButton = document.createElement('li'); 187 | previousButton.className = 'gitment-comments-page-item'; 188 | previousButton.innerText = 'Previous'; 189 | previousButton.onclick = function () { 190 | return instance.goto(currentPage - 1); 191 | }; 192 | pagination.appendChild(previousButton); 193 | } 194 | 195 | var _loop = function _loop(i) { 196 | var pageItem = document.createElement('li'); 197 | pageItem.className = 'gitment-comments-page-item'; 198 | pageItem.innerText = i; 199 | pageItem.onclick = function () { 200 | return instance.goto(i); 201 | }; 202 | if (currentPage === i) pageItem.classList.add('gitment-selected'); 203 | pagination.appendChild(pageItem); 204 | }; 205 | 206 | for (var i = 1; i <= pageCount; i++) { 207 | _loop(i); 208 | } 209 | 210 | if (currentPage < pageCount) { 211 | var nextButton = document.createElement('li'); 212 | nextButton.className = 'gitment-comments-page-item'; 213 | nextButton.innerText = 'Next'; 214 | nextButton.onclick = function () { 215 | return instance.goto(currentPage + 1); 216 | }; 217 | pagination.appendChild(nextButton); 218 | } 219 | 220 | container.appendChild(pagination); 221 | } 222 | } 223 | 224 | return container; 225 | } 226 | 227 | function renderEditor(_ref3, instance) { 228 | var user = _ref3.user, 229 | error = _ref3.error; 230 | 231 | var container = document.createElement('div'); 232 | container.lang = "en-US"; 233 | container.className = 'gitment-container gitment-editor-container'; 234 | 235 | var shouldDisable = user.login && !error ? '' : 'disabled'; 236 | var disabledTip = user.login ? '' : 'Login to Comment'; 237 | container.innerHTML = '\n ' + (user.login ? '\n \n ' : user.isLoggingIn ? '
' + _icons.spinner + '
' : '\n ' + _icons.github + '\n ') + '\n \n
\n
\n \n \n
\n
\n
\n \n
\n
\n
\n
\n
\n
\n \n '; 238 | if (user.login) { 239 | container.querySelector('.gitment-editor-logout-link').onclick = function () { 240 | return instance.logout(); 241 | }; 242 | } 243 | 244 | var writeField = container.querySelector('.gitment-editor-write-field'); 245 | var previewField = container.querySelector('.gitment-editor-preview-field'); 246 | 247 | var textarea = writeField.querySelector('textarea'); 248 | textarea.oninput = function () { 249 | textarea.style.height = 'auto'; 250 | var style = window.getComputedStyle(textarea, null); 251 | var height = parseInt(style.height, 10); 252 | var clientHeight = textarea.clientHeight; 253 | var scrollHeight = textarea.scrollHeight; 254 | if (clientHeight < scrollHeight) { 255 | textarea.style.height = height + scrollHeight - clientHeight + 'px'; 256 | } 257 | }; 258 | 259 | var _container$querySelec = container.querySelectorAll('.gitment-editor-tab'), 260 | _container$querySelec2 = _slicedToArray(_container$querySelec, 2), 261 | writeTab = _container$querySelec2[0], 262 | previewTab = _container$querySelec2[1]; 263 | 264 | writeTab.onclick = function () { 265 | writeTab.classList.add('gitment-selected'); 266 | previewTab.classList.remove('gitment-selected'); 267 | writeField.classList.remove('gitment-hidden'); 268 | previewField.classList.add('gitment-hidden'); 269 | 270 | textarea.focus(); 271 | }; 272 | previewTab.onclick = function () { 273 | previewTab.classList.add('gitment-selected'); 274 | writeTab.classList.remove('gitment-selected'); 275 | previewField.classList.remove('gitment-hidden'); 276 | writeField.classList.add('gitment-hidden'); 277 | 278 | var preview = previewField.querySelector('.gitment-editor-preview'); 279 | var content = textarea.value.trim(); 280 | if (!content) { 281 | preview.innerText = '暂无内容可预览'; 282 | return; 283 | } 284 | 285 | preview.innerText = 'Loading preview...'; 286 | instance.markdown(content).then(function (html) { 287 | return preview.innerHTML = html; 288 | }); 289 | }; 290 | 291 | var submitButton = container.querySelector('.gitment-editor-submit'); 292 | submitButton.onclick = function () { 293 | submitButton.innerText = 'Submitting...'; 294 | submitButton.setAttribute('disabled', true); 295 | instance.post(textarea.value.trim()).then(function (data) { 296 | textarea.value = ''; 297 | textarea.style.height = 'auto'; 298 | submitButton.removeAttribute('disabled'); 299 | submitButton.innerText = 'Comment'; 300 | }).catch(function (e) { 301 | alert(e); 302 | submitButton.removeAttribute('disabled'); 303 | submitButton.innerText = 'Comment'; 304 | }); 305 | }; 306 | 307 | return container; 308 | } 309 | 310 | function renderFooter() { 311 | var container = document.createElement('div'); 312 | container.lang = "en-US"; 313 | container.className = 'gitment-container gitment-footer-container'; 314 | container.innerHTML = '\n Powered by\n \n Gitment\n \n '; 315 | return container; 316 | } 317 | 318 | function render(state, instance) { 319 | var container = document.createElement('div'); 320 | container.lang = "en-US"; 321 | container.className = 'gitment-container gitment-root-container'; 322 | container.appendChild(instance.renderHeader(state, instance)); 323 | container.appendChild(instance.renderComments(state, instance)); 324 | container.appendChild(instance.renderEditor(state, instance)); 325 | container.appendChild(instance.renderFooter(state, instance)); 326 | return container; 327 | } 328 | 329 | exports.default = { render: render, renderHeader: renderHeader, renderComments: renderComments, renderEditor: renderEditor, renderFooter: renderFooter }; 330 | //# sourceMappingURL=default.js.map -------------------------------------------------------------------------------- /src/assets/gitment/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.http = exports.Query = exports.isString = undefined; 7 | 8 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 9 | 10 | exports.getTargetContainer = getTargetContainer; 11 | 12 | var _constants = require('./constants'); 13 | 14 | var isString = exports.isString = function isString(s) { 15 | return toString.call(s) === '[object String]'; 16 | }; 17 | 18 | function getTargetContainer(container) { 19 | var targetContainer = void 0; 20 | if (container instanceof Element) { 21 | targetContainer = container; 22 | } else if (isString(container)) { 23 | targetContainer = document.getElementById(container); 24 | } else { 25 | targetContainer = document.createElement('div'); 26 | } 27 | 28 | return targetContainer; 29 | } 30 | 31 | var Query = exports.Query = { 32 | parse: function parse() { 33 | var search = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.location.search; 34 | 35 | if (!search) return {}; 36 | var queryString = search[0] === '?' ? search.substring(1) : search; 37 | var query = {}; 38 | queryString.split('&').forEach(function (queryStr) { 39 | var _queryStr$split = queryStr.split('='), 40 | _queryStr$split2 = _slicedToArray(_queryStr$split, 2), 41 | key = _queryStr$split2[0], 42 | value = _queryStr$split2[1]; 43 | 44 | if (key) query[key] = value; 45 | }); 46 | 47 | return query; 48 | }, 49 | stringify: function stringify(query) { 50 | var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '?'; 51 | 52 | var queryString = Object.keys(query).map(function (key) { 53 | return key + '=' + encodeURIComponent(query[key] || ''); 54 | }).join('&'); 55 | return queryString ? prefix + queryString : ''; 56 | } 57 | }; 58 | 59 | function ajaxFactory(method) { 60 | return function (apiPath) { 61 | var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 62 | var base = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'https://api.github.com'; 63 | 64 | var req = new XMLHttpRequest(); 65 | var token = localStorage.getItem(_constants.LS_ACCESS_TOKEN_KEY); 66 | 67 | var url = '' + base + apiPath; 68 | var body = null; 69 | if (method === 'GET' || method === 'DELETE') { 70 | url += Query.stringify(data); 71 | } 72 | 73 | var p = new Promise(function (resolve, reject) { 74 | req.addEventListener('load', function () { 75 | var contentType = req.getResponseHeader('content-type'); 76 | var res = req.responseText; 77 | if (!/json/.test(contentType)) { 78 | resolve(res); 79 | return; 80 | } 81 | var data = req.responseText ? JSON.parse(res) : {}; 82 | if (data.message) { 83 | reject(new Error(data.message)); 84 | } else { 85 | resolve(data); 86 | } 87 | }); 88 | req.addEventListener('error', function (error) { 89 | return reject(error); 90 | }); 91 | }); 92 | req.open(method, url, true); 93 | 94 | req.setRequestHeader('Accept', 'application/vnd.github.squirrel-girl-preview, application/vnd.github.html+json'); 95 | if (token) { 96 | req.setRequestHeader('Authorization', 'token ' + token); 97 | } 98 | if (method !== 'GET' && method !== 'DELETE') { 99 | body = JSON.stringify(data); 100 | req.setRequestHeader('Content-Type', 'application/json'); 101 | } 102 | 103 | req.send(body); 104 | return p; 105 | }; 106 | } 107 | 108 | var http = exports.http = { 109 | get: ajaxFactory('GET'), 110 | post: ajaxFactory('POST'), 111 | delete: ajaxFactory('DELETE'), 112 | put: ajaxFactory('PUT') 113 | }; 114 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /src/assets/gitment/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/utils.js"],"names":["getTargetContainer","isString","toString","call","s","container","targetContainer","Element","document","getElementById","createElement","Query","parse","search","window","location","queryString","substring","query","split","forEach","queryStr","key","value","stringify","prefix","Object","keys","map","encodeURIComponent","join","ajaxFactory","method","apiPath","data","base","req","XMLHttpRequest","token","localStorage","getItem","url","body","p","Promise","resolve","reject","addEventListener","contentType","getResponseHeader","res","responseText","test","JSON","message","Error","error","open","setRequestHeader","send","http","get","post","delete","put"],"mappings":"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","file":"utils.js","sourcesContent":["import { LS_ACCESS_TOKEN_KEY } from './constants'\n\nexport const isString = s => toString.call(s) === '[object String]'\n\nexport function getTargetContainer(container) {\n let targetContainer\n if (container instanceof Element) {\n targetContainer = container\n } else if (isString(container)) {\n targetContainer = document.getElementById(container)\n } else {\n targetContainer = document.createElement('div')\n }\n\n return targetContainer\n}\n\nexport const Query = {\n parse(search = window.location.search) {\n if (!search) return {}\n const queryString = search[0] === '?' ? search.substring(1) : search\n const query = {}\n queryString.split('&')\n .forEach(queryStr => {\n const [key, value] = queryStr.split('=')\n if (key) query[key] = value\n })\n\n return query\n },\n stringify(query, prefix = '?') {\n const queryString = Object.keys(query)\n .map(key => `${key}=${encodeURIComponent(query[key] || '')}`)\n .join('&')\n return queryString ? prefix + queryString : ''\n },\n}\n\nfunction ajaxFactory(method) {\n return function(apiPath, data = {}, base = 'https://api.github.com') {\n const req = new XMLHttpRequest()\n const token = localStorage.getItem(LS_ACCESS_TOKEN_KEY)\n\n let url = `${base}${apiPath}`\n let body = null\n if (method === 'GET' || method === 'DELETE') {\n url += Query.stringify(data)\n }\n\n const p = new Promise((resolve, reject) => {\n req.addEventListener('load', () => {\n const contentType = req.getResponseHeader('content-type')\n const res = req.responseText\n if (!/json/.test(contentType)) {\n resolve(res)\n return\n }\n const data = req.responseText ? JSON.parse(res) : {}\n if (data.message) {\n reject(new Error(data.message))\n } else {\n resolve(data)\n }\n })\n req.addEventListener('error', error => reject(error))\n })\n req.open(method, url, true)\n\n req.setRequestHeader('Accept', 'application/vnd.github.squirrel-girl-preview, application/vnd.github.html+json')\n if (token) {\n req.setRequestHeader('Authorization', `token ${token}`)\n }\n if (method !== 'GET' && method !== 'DELETE') {\n body = JSON.stringify(data)\n req.setRequestHeader('Content-Type', 'application/json')\n }\n\n req.send(body)\n return p\n }\n}\n\nexport const http = {\n get: ajaxFactory('GET'),\n post: ajaxFactory('POST'),\n delete: ajaxFactory('DELETE'),\n put: ajaxFactory('PUT'),\n}\n"]} -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linguowei/myblog/760284a1d70eaced651dee23f05eda2e4da149c8/src/assets/logo.png -------------------------------------------------------------------------------- /src/component/About.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 34 | 35 | -------------------------------------------------------------------------------- /src/component/archives.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 109 | 110 | -------------------------------------------------------------------------------- /src/component/articlesDetails.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 130 | 131 | -------------------------------------------------------------------------------- /src/component/comment.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 52 | 53 | -------------------------------------------------------------------------------- /src/component/latestArticles.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 110 | 111 | 121 | -------------------------------------------------------------------------------- /src/component/tag.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 32 | 33 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import VueResource from 'vue-resource' 4 | import ElementUI from 'element-ui' 5 | import 'element-ui/lib/theme-default/index.css' 6 | import App from './App.vue' 7 | import LatestArticles from './component/latestArticles.vue' 8 | import Archiver from './component/archives.vue' 9 | import Tag from './component/tag.vue' 10 | import About from './component/about.vue' 11 | import ArticlesDetails from './component/articlesDetails.vue' 12 | 13 | Vue.use(ElementUI); 14 | Vue.use(VueRouter); 15 | Vue.use(VueResource); 16 | 17 | const router = new VueRouter({ 18 | mode: 'history', 19 | routes: [ 20 | {path: '/', component: LatestArticles, mate: { keepAlive: true }}, 21 | {path: '/archives', component: Archiver}, 22 | {path: '/tag', component: Tag}, 23 | {path: '/about', component: About}, 24 | {path: '/articlesDetails:id', name: 'articlesDetails', component: ArticlesDetails} 25 | ] 26 | }) 27 | 28 | new Vue({ 29 | el: '#app', 30 | router: router, 31 | render: h => h(App) 32 | }) 33 | -------------------------------------------------------------------------------- /src/style/common.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin:0; 3 | padding: 0; 4 | } 5 | h1, h2 { 6 | font-weight: normal; 7 | } 8 | ul li { 9 | list-style-type: none; 10 | padding: 0; 11 | } 12 | ol { 13 | list-style-type: none; 14 | } 15 | #app { 16 | font-family: -apple-system,"Helvetica Neue",Helvetica,Arial,"PingFang SC","Hiragino Sans GB","WenQuanYi Micro Hei","Microsoft Yahei",sans-serif; 17 | -webkit-font-smoothing: antialiased; 18 | -moz-osx-font-smoothing: grayscale; 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden; 22 | } 23 | 24 | .fade-enter-active, .fade-leave-active { 25 | transition: opacity .5s 26 | } 27 | .fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ { 28 | opacity: 0 29 | } 30 | /*侧栏导航*/ 31 | 32 | @media screen and (max-width: 768px){ 33 | #app .nav-bar { 34 | height: 8vh; 35 | } 36 | .mobile-nav-bar { 37 | background-color: #fff; 38 | position: relative; 39 | z-index: 10; 40 | height: 12vh; 41 | } 42 | .mobile-nav-bar > ul > li { 43 | width: 50%; 44 | text-align: center; 45 | float: left; 46 | box-sizing: border-box; 47 | display: inline-block; 48 | padding: 1vh 0; 49 | border-bottom: 1px solid #ddd; 50 | } 51 | .mobile-nav-bar > ul > li > span { 52 | display: block; 53 | color: #999; 54 | text-align: center; 55 | line-height: 4vh; 56 | text-decoration: none; 57 | } 58 | .current { 59 | border-bottom-color: #32D3C3!important; 60 | } 61 | .current > span { 62 | color: #32D3C3!important; 63 | } 64 | .nav-bar-inner { 65 | height: 8vh!important; 66 | line-height: 8vh!important; 67 | text-align: left!important; 68 | padding: 0 10px; 69 | background-color: #1e1d26; 70 | color: #32D3C3; 71 | font-size: 18px; 72 | } 73 | .nav-bar-inner > div { 74 | float: right; 75 | } 76 | .nav-bar-body { 77 | display: none; 78 | } 79 | .nav-bar > .nav-bar-footer { 80 | display: none; 81 | } 82 | .bg-purple .el-tabs__header { 83 | display: none; 84 | } 85 | } 86 | .nav-bar { 87 | height: 100vh; 88 | background-color: #2a2935; 89 | } 90 | .mobile-nav-bar { 91 | /*display: none;*/ 92 | } 93 | .nav-bar-inner { 94 | height: 6vh; 95 | line-height: 6vh; 96 | text-align: center; 97 | background-color: #1e1d26; 98 | color: #32D3C3; 99 | font-size: 18px; 100 | } 101 | .nav-bar-inner > span { 102 | width: 85%; 103 | float: left; 104 | } 105 | .nav-bar-body { 106 | height: 81vh; 107 | } 108 | .nav-bar-body > ul { 109 | margin-top: 5vh; 110 | } 111 | .nav-bar-body > ul > li { 112 | height: 8vh; 113 | line-height: 8vh; 114 | text-align: center; 115 | color: #666; 116 | font-size: 18px; 117 | padding-right: 20px; 118 | } 119 | .nav-bar-body .borderRightActive { 120 | border-right:4px solid #32D3C3; 121 | color: #32D3C3; 122 | background-color: #26252f; 123 | } 124 | .nav-bar-body > ul > li:hover { 125 | height: 8vh; 126 | line-height: 8vh; 127 | text-align: center; 128 | color: #32D3C3; 129 | cursor: pointer; 130 | background-color: #26252f; 131 | } 132 | .nav-bar-footer { 133 | height: 8vh; 134 | line-height: 8vh; 135 | text-align: center; 136 | color: #666; 137 | font-size: 13px; 138 | } 139 | /*页面主体*/ 140 | .main { 141 | background-color: #f4f4f4; 142 | height: 100vh; 143 | overflow-y: scroll; 144 | } 145 | @media screen and (max-width: 768px){ 146 | .main { 147 | background-color: #f4f4f4; 148 | /*max-height: 92vh!important;*/ 149 | overflow-y: scroll; 150 | /*margin-top: 8vh;*/ 151 | -webkit-overflow-scrolling: touch; 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/style/latestArticles.css: -------------------------------------------------------------------------------- 1 | .articles-box { 2 | width:100%; 3 | margin-top:10px; 4 | margin-bottom:5px; 5 | border-color: #fff; 6 | border: 1px solid #fff; 7 | border-radius: 0; 8 | box-shadow: 0; 9 | text-align: center; 10 | } 11 | .el-tabs__active-bar { 12 | background-color: #32D3C3; 13 | } 14 | .el-tabs__item { 15 | color: #666!important; 16 | } 17 | 18 | @media screen and (min-width: 768px){ 19 | .articles-box { 20 | box-shadow: none; 21 | } 22 | .articles-box:hover { 23 | /*border-color: #32D3C3;*/ 24 | cursor: pointer; 25 | } 26 | .articles-box .post-title { 27 | font-size: 1.3rem; 28 | color: #32D3C3; 29 | } 30 | .articles-box .post-time { 31 | color: #AAA; 32 | font-size: 0.8rem; 33 | margin-bottom: 0.12rem; 34 | padding: 1rem 0; 35 | border-bottom: 1px dashed #DDD; 36 | } 37 | .articles-box .post-abstract { 38 | text-align: left; 39 | padding: 1rem 0; 40 | color: #666; 41 | min-height: 3.7rem; 42 | max-height: 3.7rem; 43 | line-height: 1.2rem; 44 | margin-bottom: 1rem; 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | display: -webkit-box; 48 | -webkit-line-clamp: 4; 49 | -webkit-box-orient: vertical; 50 | font-size: 0.9rem; 51 | } 52 | .articles-box .post-label { 53 | border: 1px solid #32D3C3; 54 | border-radius: 0.3rem; 55 | color: #32D3C3; 56 | padding: 0.1rem 0.5rem; 57 | font-size: 0.5rem; 58 | } 59 | .main-header { 60 | display: block; 61 | font-size: 0; 62 | color: #32D3C3; 63 | height: 6vh; 64 | border-bottom: 1px solid #ddd; 65 | position: relative; 66 | } 67 | .main-header span { 68 | display: inline-block; 69 | font-size: 14px; 70 | margin-left: 0; 71 | height: 5.8vh; 72 | line-height: 6vh; 73 | border-bottom: 0.3vh solid #32D3C3; 74 | color: #666; 75 | } 76 | .main-header .search { 77 | float:right; 78 | width:200px; 79 | } 80 | .main-header .search > input:focus { 81 | border-color: #32D3C3; 82 | } 83 | .main-header .search > input:hover { 84 | border-color: #32D3C3; 85 | } 86 | .main-header .search input { 87 | height: 4vh; 88 | margin-top: 1vh; 89 | } 90 | .main-header .search i { 91 | margin-top: 1vh; 92 | height: 4vh; 93 | } 94 | } 95 | 96 | @media screen and (max-width: 768px){ 97 | .main-header { 98 | display: none!important; 99 | } 100 | .post-title { 101 | font-size: 1.3rem; 102 | color:#32D3C3; 103 | } 104 | .articles-box .post-label { 105 | border: 1px solid #32D3C3; 106 | border-radius: 0.3rem; 107 | color: #32D3C3; 108 | padding: 0.1rem 0.5rem; 109 | font-size: 0.5rem; 110 | } 111 | .post-time { 112 | color: #AAA; 113 | font-size: 0.8rem; 114 | margin-bottom: 0.12rem; 115 | padding: 1rem 0; 116 | border-bottom: 1px dashed #DDD; 117 | } 118 | .post-abstract { 119 | text-align: left; 120 | padding: 1rem 0; 121 | color: #666; 122 | max-height: 3.7rem; 123 | line-height: 1.2rem; 124 | overflow: hidden; 125 | text-overflow: ellipsis; 126 | display: -webkit-box; 127 | -webkit-line-clamp: 4; 128 | -webkit-box-orient: vertical; 129 | } 130 | } 131 | 132 | -------------------------------------------------------------------------------- /src/utils/utils.js: -------------------------------------------------------------------------------- 1 | export const close = '' 2 | export const github = '' 3 | export const heart = '' 4 | export const spinner = '' 5 | 6 | export function dateFormat(){ 7 | Date.prototype.format = function(format) { 8 | var o = { 9 | "M+": this.getMonth() + 1, //month 10 | "d+": this.getDate(), //day 11 | "h+": this.getHours(), //hour 12 | "m+": this.getMinutes(), //minute 13 | "s+": this.getSeconds(), //second 14 | "q+": Math.floor((this.getMonth() + 3) / 3), //quarter 15 | "S": this.getMilliseconds() //millisecond 16 | } 17 | if (/(y+)/.test(format)) { 18 | format = format.replace(RegExp.$1,(this.getFullYear() + "").substr(4 - RegExp.$1.length)); 19 | } 20 | for (var k in o){ 21 | if (new RegExp("(" + k + ")").test(format)){ 22 | format = format.replace(RegExp.$1,RegExp.$1.length == 1 ? o[k] :("00" + o[k]).substr(("" + o[k]).length)); 23 | } 24 | } 25 | return format; 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var CompressionWebpackPlugin = require('compression-webpack-plugin'); 4 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: './src/main.js', 8 | output: { 9 | path: path.resolve(__dirname, './dist'), 10 | publicPath: '/dist/', 11 | filename: 'build.js' 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.vue$/, 17 | loader: 'vue-loader', 18 | options: { 19 | loaders: { 20 | } 21 | // other vue-loader options go here 22 | } 23 | }, 24 | { 25 | test: /\.js$/, 26 | loader: 'babel-loader', 27 | exclude: /node_modules/ 28 | }, 29 | { 30 | test: /\.css$/, 31 | loader: 'style-loader!css-loader' 32 | }, 33 | { 34 | test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/, 35 | loader: 'file-loader' 36 | }, 37 | { 38 | test: /\.(png|jpg|gif|svg)$/, 39 | loader: 'file-loader', 40 | options: { 41 | name: '[name].[ext]?[hash]' 42 | } 43 | } 44 | ] 45 | }, 46 | resolve: { 47 | alias: { 48 | 'vue$': 'vue/dist/vue.esm.js' 49 | } 50 | }, 51 | devServer: { 52 | historyApiFallback: true, 53 | noInfo: true, 54 | proxy: [ 55 | { 56 | context: ['/api'], 57 | target: 'http://localhost:7000', 58 | changeOrigin: true, 59 | secure: false 60 | } 61 | ] 62 | }, 63 | performance: { 64 | hints: false 65 | }, 66 | devtool: '#source-map' 67 | } 68 | 69 | if (process.env.NODE_ENV === 'production') { 70 | module.exports.devtool = '#source-map' 71 | // http://vue-loader.vuejs.org/en/workflow/production.html 72 | module.exports.plugins = (module.exports.plugins || []).concat([ 73 | new webpack.DefinePlugin({ 74 | 'process.env': { 75 | NODE_ENV: '"production"' 76 | } 77 | }), 78 | new webpack.optimize.UglifyJsPlugin({ 79 | sourceMap: false, 80 | comments: false, 81 | compress: { 82 | warnings: false 83 | } 84 | }), 85 | new webpack.LoaderOptionsPlugin({ 86 | minimize: true 87 | }), 88 | new CompressionWebpackPlugin({ //gzip 压缩 89 | asset: '[path].gz[query]', 90 | algorithm: 'gzip', 91 | test: new RegExp( 92 | '\\.(js|css)$' //压缩 js 与 css 93 | ), 94 | threshold: 10240, 95 | minRatio: 0.8 96 | }), 97 | new ExtractTextPlugin('[name].[contenthash].css') 98 | ]) 99 | } 100 | --------------------------------------------------------------------------------