├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── asserts ├── antd-custom.less ├── editor-markedown.less └── styles.less ├── components ├── Edit │ ├── SimpleMde.js │ └── index.js ├── ErrorPage.js ├── Header.js ├── Home │ └── index.js ├── Layout.js ├── Normal │ ├── SimpleMde.js │ └── index.js └── Preview │ └── index.js ├── constants ├── ActionTypes.js ├── ApiUrlForBE.js ├── ConstTypes.js └── CustomTheme.js ├── containers ├── home │ └── counter.js └── user │ └── UserList.js ├── core ├── nextFetch.js └── util.js ├── middlewares ├── client │ └── user.js └── server │ └── README.md ├── next.config.js ├── out ├── _next │ └── static │ │ ├── NSy5qE4UvMw4dCrM10co1 │ │ └── pages │ │ │ ├── _app.js │ │ │ ├── _error.js │ │ │ ├── edit.js │ │ │ ├── index.js │ │ │ ├── normal.js │ │ │ └── preview.js │ │ ├── chunks │ │ ├── 0.js │ │ ├── 1.js │ │ ├── 10.08b5d53e0813005250ac.js │ │ ├── 11.0b2141b385e7370d7db6.js │ │ ├── 2.js │ │ ├── commons.1f8d74ed5ebdd5c01cf6.js │ │ ├── styles.c57e4bb0e5d39697731a.js │ │ └── styles.js │ │ ├── css │ │ ├── commons.7a17fd1f.chunk.css │ │ ├── commons.7a17fd1f.chunk.css.map │ │ ├── styles.1c350e82.chunk.css │ │ ├── styles.1c350e82.chunk.css.map │ │ └── styles.chunk.css │ │ ├── development │ │ ├── dll │ │ │ └── dll_5456783f4b96fc9b3e3a.js │ │ └── pages │ │ │ ├── _app.js │ │ │ ├── _error.js │ │ │ ├── index.js │ │ │ └── preview.js │ │ ├── runtime │ │ ├── main-9d9b5feb884c2ff45459.js │ │ ├── main.js │ │ ├── webpack-b71bd49ef0ed4f12a8c1.js │ │ └── webpack.js │ │ └── webpack │ │ ├── 052c5077de3094569559.hot-update.json │ │ ├── 0f8744ee697c70dab960.hot-update.json │ │ ├── 1516d8ccf198cea54b10.hot-update.json │ │ ├── 6a1fe21aaf5cb4fe529a.hot-update.json │ │ ├── 70577301787b6e0e7497.hot-update.json │ │ ├── 74eb4dbfc4d60836b3a8.hot-update.json │ │ ├── 7db70758f5c5c4335604.hot-update.json │ │ ├── e5d126e4670caddb4c2f.hot-update.json │ │ ├── e9002a3ce1f2ce304d4e.hot-update.json │ │ ├── static │ │ └── development │ │ │ └── pages │ │ │ ├── normal.js.0f8744ee697c70dab960.hot-update.js │ │ │ └── normal.js.e5d126e4670caddb4c2f.hot-update.js │ │ ├── styles.6a1fe21aaf5cb4fe529a.hot-update.js │ │ ├── styles.70577301787b6e0e7497.hot-update.js │ │ └── styles.74eb4dbfc4d60836b3a8.hot-update.js ├── edit │ └── index.html ├── index.html ├── normal │ └── index.html ├── preview │ └── index.html └── static │ ├── empty.png │ ├── favicon.ico │ ├── logo.png │ ├── tags │ ├── Algorithm.png │ ├── Angular.png │ ├── Back.png │ ├── CSS.png │ ├── Database.png │ ├── HTML.png │ ├── Http.png │ ├── Java.png │ ├── JavaScript.png │ ├── Nodejs.png │ ├── Product.png │ ├── Python.png │ ├── React.png │ ├── Vue.png │ ├── WEB.png │ └── Webpack.png │ ├── unknown_error.png │ └── users.json ├── package.json ├── pages ├── _app.js ├── _document.js ├── _error.js ├── edit │ └── index.js ├── index.js ├── normal │ └── index.js └── preview │ └── index.js ├── redux ├── actions │ ├── home.js │ └── user.js ├── reducers │ ├── home │ │ ├── counter.js │ │ └── index.js │ ├── index.js │ └── user │ │ ├── index.js │ │ └── list.js ├── sagas │ ├── index.js │ └── user │ │ ├── index.js │ │ └── userList.js └── store.js ├── server.js ├── static ├── empty.png ├── favicon.ico ├── logo.png ├── tags │ ├── Algorithm.png │ ├── Angular.png │ ├── Back.png │ ├── CSS.png │ ├── Database.png │ ├── HTML.png │ ├── Http.png │ ├── Java.png │ ├── JavaScript.png │ ├── Nodejs.png │ ├── Product.png │ ├── Python.png │ ├── React.png │ ├── Vue.png │ ├── WEB.png │ └── Webpack.png ├── unknown_error.png └── users.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "presets": ["next/babel"], 4 | "plugins": [ 5 | [ 6 | "@babel/plugin-proposal-decorators", 7 | { 8 | "decoratorsBeforeExport": true 9 | } 10 | ], 11 | [ 12 | "import", 13 | { 14 | "libraryName": "antd", 15 | "style": true 16 | } 17 | ], 18 | ["lodash"] 19 | ] 20 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig文件使用INI格式。斜杠(/)作为路径分隔符,#或者;作为注释。路径支持通配符: 2 | # 表明是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件 3 | root = true 4 | 5 | # * 匹配除/之外的任意字符 6 | # ** 匹配任意字符串 7 | # ? 匹配任意单个字符 8 | # [name] 匹配name字符 9 | # [!name] 不匹配name字符 10 | # [s1,s2,s3] 匹配给定的字符串 11 | # [num1..num2] 匹配num1到mun2直接的整数 12 | [*] 13 | # 文件的charset。有以下几种类型:latin1, utf-8, utf-8-bom, utf-16be, utf-16le 14 | charset = utf-8 15 | # 缩进使用 tab 或者 space 16 | indent_style = space 17 | # 缩进为 space 时,缩进的字符数 18 | indent_size = 2 19 | # 缩进为 tab 时,缩进的宽度 20 | # tab_width = 2 21 | # 换行符的类型。lf, cr, crlf三种 22 | end_of_line = lf 23 | # 是否将行尾空格自动删除 24 | trim_trailing_whitespace = true 25 | # 是否使文件以一个空白行结尾 26 | insert_final_newline = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .next/ 3 | .editorconfig 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "jsx": true 8 | } 9 | }, 10 | "env": { 11 | "es6": true, 12 | "browser": true, 13 | "node": true 14 | }, 15 | "rules":{ 16 | "no-console":0, 17 | "indent": [ 18 | 2, 19 | 2, 20 | { 21 | "SwitchCase": 1, 22 | "ObjectExpression": 1 23 | } 24 | ], 25 | "react/jsx-uses-vars": [2], 26 | "semi": [1, "always"], 27 | "linebreak-style": 0, 28 | "consistent-return": 0, 29 | "no-use-before-define": 0, 30 | "no-multi-assign": 0, 31 | "no-lonely-if": 1, 32 | "no-nested-ternary": 0, 33 | "wrap-iife": [2, "inside"], 34 | "jsx-quotes": [2, "prefer-single"], 35 | "generator-star-spacing": 0, 36 | "react/forbid-prop-types": 0, 37 | "react/sort-comp": 1, 38 | "react/no-string-refs": 0, 39 | "react/prefer-stateless-function": 0, 40 | "react/prop-types": 2, 41 | "react/require-default-props": [2, { "forbidDefaultForRequired": true }], 42 | "jsx-a11y/no-static-element-interactions": 0, 43 | "keyword-spacing": [2, { "before": true }], 44 | "eqeqeq": [2, "always"], 45 | "space-infix-ops": [2, {"int32Hint": false}], 46 | "comma-spacing": [2, { "before": false, "after": true }], 47 | "block-spacing": 2, 48 | "no-else-return": 2 49 | }, 50 | "extends": "eslint:recommended", 51 | "plugins": [ 52 | "react" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | node_modules/ 5 | 6 | # Compiled output 7 | build 8 | 9 | # Runtime data 10 | database.sqlite 11 | 12 | # Test coverage 13 | coverage 14 | 15 | # Logs 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | logs/ 20 | 21 | # Editors and IDEs 22 | .idea 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # Misc 30 | .DS_Store 31 | 32 | # custom 33 | .next 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 luffyZhou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # next-markdown-editor 2 | A demo for edit & priview markdown bases on next-antd-scafflod quickly! 3 | 4 | ## Features 5 | - next.js 6 | - simplemde-markdown-editor 7 | - react-markdown 8 | 9 | ## Demo Image 10 | - home page 11 | ![](https://user-gold-cdn.xitu.io/2018/12/13/167a642030ace38f?imageView2/0/w/1280/h/960/ignore-error/1) 12 | - normal editor 13 | ![](https://user-gold-cdn.xitu.io/2018/12/13/167a612a7c76b3f7?imageView2/0/w/1280/h/960/ignore-error/1) 14 | - side-by-side editor 15 | ![](https://user-gold-cdn.xitu.io/2018/12/13/167a63e1f4763db2?imageView2/0/w/1280/h/960/ignore-error/1) 16 | - preview markdown 17 | ![](https://user-gold-cdn.xitu.io/2018/12/13/167a6767c65fb398?imageView2/0/w/1280/h/960/ignore-error/1) 18 | -------------------------------------------------------------------------------- /asserts/antd-custom.less: -------------------------------------------------------------------------------- 1 | /* 系统主题颜色 */ 2 | @color-primary: #52f; 3 | 4 | @primary-color: @color-primary; 5 | 6 | @layout-header-height: 40px; 7 | @border-radius-base: 2px; 8 | -------------------------------------------------------------------------------- /asserts/editor-markedown.less: -------------------------------------------------------------------------------- 1 | /* simple-markdown-editor */ 2 | .CodeMirror { 3 | width: 100% !important; 4 | top: 50px !important; 5 | bottom: 40px !important; 6 | background-color: #fafafa !important; 7 | } 8 | .editor-toolbar { 9 | display: none; 10 | } 11 | .editor-preview-side { 12 | background-color: #fff !important; 13 | bottom: 40px !important; 14 | } 15 | /* markdown css */ 16 | 17 | code, pre { 18 | border-radius: 3px; 19 | background-color:#f7f7f7; 20 | color: inherit; 21 | } 22 | 23 | code { 24 | font-family: Consolas, Monaco, Andale Mono, monospace; 25 | margin: 0 2px; 26 | } 27 | 28 | pre { 29 | line-height: 1.7em; 30 | overflow: auto; 31 | padding: 6px 10px; 32 | border-left: 5px solid #6CE26C; 33 | } 34 | 35 | pre > code { 36 | border: 0; 37 | display: inline; 38 | max-width: initial; 39 | padding: 0; 40 | margin: 0; 41 | overflow: initial; 42 | line-height: inherit; 43 | font-size: .85em; 44 | white-space: pre; 45 | background: 0 0; 46 | 47 | } 48 | 49 | code { 50 | color: #666555; 51 | } 52 | 53 | aside { 54 | display: block; 55 | float: right; 56 | width: 390px; 57 | } 58 | blockquote { 59 | border-left:.5em solid #eee; 60 | padding: 0 0 0 2em; 61 | margin-left:0; 62 | } 63 | blockquote cite { 64 | font-size:14px; 65 | line-height:20px; 66 | color:#bfbfbf; 67 | } 68 | blockquote cite:before { 69 | content: '\2014 \00A0'; 70 | } 71 | 72 | blockquote p { 73 | color: #666; 74 | } 75 | hr { 76 | text-align: left; 77 | color: #999; 78 | height: 2px; 79 | padding: 0; 80 | margin: 16px 0; 81 | background-color: #e7e7e7; 82 | border: 0 none; 83 | } 84 | 85 | dl { 86 | padding: 0; 87 | } 88 | 89 | dl dt { 90 | padding: 10px 0; 91 | margin-top: 16px; 92 | font-size: 1em; 93 | font-style: italic; 94 | font-weight: bold; 95 | } 96 | 97 | dl dd { 98 | padding: 0 16px; 99 | margin-bottom: 16px; 100 | } 101 | 102 | dd { 103 | margin-left: 0; 104 | } 105 | table { 106 | *border-collapse: collapse; /* IE7 and lower */ 107 | border-spacing: 0; 108 | width: 100%; 109 | } 110 | table { 111 | border: solid #ccc 1px; 112 | -moz-border-radius: 6px; 113 | -webkit-border-radius: 6px; 114 | border-radius: 6px; 115 | } 116 | table tr:hover { 117 | background: #fbf8e9; 118 | -o-transition: all 0.1s ease-in-out; 119 | -webkit-transition: all 0.1s ease-in-out; 120 | -moz-transition: all 0.1s ease-in-out; 121 | -ms-transition: all 0.1s ease-in-out; 122 | transition: all 0.1s ease-in-out; 123 | } 124 | table td, .table th { 125 | border-left: 1px solid #ccc; 126 | border-top: 1px solid #ccc; 127 | padding: 10px; 128 | text-align: left; 129 | } 130 | 131 | table th { 132 | background-color: #dce9f9; 133 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebf3fc), to(#dce9f9)); 134 | background-image: -webkit-linear-gradient(top, #ebf3fc, #dce9f9); 135 | background-image: -moz-linear-gradient(top, #ebf3fc, #dce9f9); 136 | background-image: -ms-linear-gradient(top, #ebf3fc, #dce9f9); 137 | background-image: -o-linear-gradient(top, #ebf3fc, #dce9f9); 138 | background-image: linear-gradient(top, #ebf3fc, #dce9f9); 139 | border-top: none; 140 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 141 | padding: 5px; 142 | } 143 | 144 | table td:first-child, table th:first-child { 145 | border-left: none; 146 | } 147 | 148 | table th:first-child { 149 | -moz-border-radius: 6px 0 0 0; 150 | -webkit-border-radius: 6px 0 0 0; 151 | border-radius: 6px 0 0 0; 152 | } 153 | table th:last-child { 154 | -moz-border-radius: 0 6px 0 0; 155 | -webkit-border-radius: 0 6px 0 0; 156 | border-radius: 0 6px 0 0; 157 | } 158 | table th:only-child{ 159 | -moz-border-radius: 6px 6px 0 0; 160 | -webkit-border-radius: 6px 6px 0 0; 161 | border-radius: 6px 6px 0 0; 162 | } 163 | table tr:last-child td:first-child { 164 | -moz-border-radius: 0 0 0 6px; 165 | -webkit-border-radius: 0 0 0 6px; 166 | border-radius: 0 0 0 6px; 167 | } 168 | table tr:last-child td:last-child { 169 | -moz-border-radius: 0 0 6px 0; 170 | -webkit-border-radius: 0 0 6px 0; 171 | border-radius: 0 0 6px 0; 172 | } -------------------------------------------------------------------------------- /asserts/styles.less: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.less"; 2 | @import "./editor-markedown.less"; 3 | @import "./antd-custom.less"; 4 | -------------------------------------------------------------------------------- /components/Edit/SimpleMde.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import SimpleMDE from 'simplemde'; 3 | import marked from 'marked'; 4 | import hljs from 'highlight.js'; 5 | import 'simplemde/dist/simplemde.min.css'; 6 | import 'highlight.js/styles/github.css'; 7 | 8 | class SimpleMDEditor extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { }; 12 | this.smde = null; 13 | } 14 | 15 | componentDidMount() { 16 | // 编辑器内容点击不弹文字 17 | document.getElementById('markdownEditor') 18 | .parentNode // 获取编辑器container 19 | .addEventListener('click', e => e.stopPropagation()); 20 | this.smde = new SimpleMDE({ 21 | element: document.getElementById('markdownEditor').childElementCount, 22 | autofocus: true, 23 | autosave: true, 24 | initialValue: '', 25 | indentWithTabs: false, 26 | placeholder: '## 输入试题内容...', 27 | renderingConfig: { 28 | singleLineBreaks: false, 29 | codeSyntaxHighlighting: true, 30 | }, 31 | insertTexts: { 32 | horizontalRule: ["", "\n\n-----\n\n"], 33 | image: ["![](http://", ")"], 34 | link: ["[", "](http://)"], 35 | table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"], 36 | }, 37 | previewRender: function(plainText) { 38 | return marked(plainText, { 39 | renderer: new marked.Renderer(), 40 | gfm: true, 41 | pedantic: false, 42 | sanitize: false, 43 | tables: true, 44 | breaks: true, 45 | smartLists: true, 46 | smartypants: true, 47 | highlight: function (code) { 48 | return hljs.highlightAuto(code).value; 49 | } 50 | }); 51 | }, 52 | spellChecker: false 53 | }); 54 | SimpleMDE.toggleSideBySide(this.smde); 55 | } 56 | 57 | render() { 58 | return ( 59 |
60 | 61 |
62 | ); 63 | } 64 | } 65 | 66 | export default SimpleMDEditor; -------------------------------------------------------------------------------- /components/Edit/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import dynamic from 'next/dynamic'; 3 | import { Input } from 'antd'; 4 | 5 | const SimpleMDEditor = dynamic(import('./SimpleMde'), { 6 | ssr: false 7 | }); 8 | 9 | class Edit extends Component { 10 | 11 | constructor(props) { 12 | super(props); 13 | this.state = { }; 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 | 58 | 62 |
63 |
64 | 65 |
66 |
67 |
68 | 编辑页面底部内容 69 |
70 |
71 | ); 72 | } 73 | } 74 | 75 | export default Edit; -------------------------------------------------------------------------------- /components/ErrorPage.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Button } from 'antd'; 4 | import Router from 'next/router'; 5 | 6 | class ErrorPage extends Component { 7 | static propTypes = { 8 | statusCode: PropTypes.number.isRequired 9 | } 10 | render() { 11 | let RenderComp; 12 | switch (this.props.statusCode) { 13 | case 200: 14 | case 404: { 15 | RenderComp = () => ( 16 |
17 | 34 | error-img 35 |

您访问的页面不存在,请确认地址准确~

36 | 37 |
38 | ); 39 | break; 40 | } 41 | case 500: { 42 | RenderComp = () => ( 43 |
44 | 61 | error-img 62 |

您访问的页面出现未知错误,程序员小哥正在加紧修复~

63 | 64 |
65 | ); 66 | break; 67 | } 68 | default: 69 | break; 70 | } 71 | return ( 72 | 73 | ); 74 | } 75 | } 76 | 77 | export default ErrorPage; -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Link from 'next/link'; 4 | import { color_primary } from '../constants/CustomTheme'; 5 | 6 | class Header extends Component { 7 | static propTypes = { 8 | title: PropTypes.string 9 | } 10 | static defaultProps = { 11 | title: '' 12 | } 13 | constructor(props) { 14 | super(props); 15 | const { title } = props; 16 | this.state = { title }; 17 | } 18 | 19 | static getDerivedStateFromProps(nextProps, prevState) { 20 | if (nextProps.title !== prevState.title) { 21 | return { 22 | title: nextProps.title 23 | }; 24 | } 25 | return null; 26 | } 27 | 28 | render() { 29 | const { title } = this.state; 30 | return ( 31 |
32 | 69 | 70 |
71 | logo 72 | XXX系统 73 |
74 | 75 |

{title}

76 |
77 | ); 78 | } 79 | } 80 | 81 | export default Header; 82 | -------------------------------------------------------------------------------- /components/Home/index.js: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd'; 2 | import Link from 'next/link'; 3 | 4 | const Home = () => ( 5 |
6 | 26 |

Next-Markdown-Editor

27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 |
45 | ); 46 | export default Home; 47 | -------------------------------------------------------------------------------- /components/Layout.js: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import IfComp from 'if-comp'; 4 | import _ from 'lodash'; 5 | import Header from './Header'; 6 | 7 | const Layout = ({ pathname, children }) => ( 8 | 9 | 22 | 26 |
27 |
28 | {children} 29 |
30 | 31 | } 32 | falseComp={ 33 | 40 | {children} 41 | 42 | } 43 | /> 44 | } 45 | /> 46 | 47 | ); 48 | 49 | export default Layout; 50 | 51 | Layout.propTypes = { 52 | pathname: PropTypes.string.isRequired, 53 | children: PropTypes.object.isRequired 54 | }; 55 | 56 | 57 | -------------------------------------------------------------------------------- /components/Normal/SimpleMde.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import SimpleMDE from 'simplemde'; 3 | import marked from 'marked'; 4 | import hljs from 'highlight.js'; 5 | import 'simplemde/dist/simplemde.min.css'; 6 | import 'highlight.js/styles/github.css'; 7 | 8 | class SimpleMDEditor extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { }; 12 | this.smde = null; 13 | } 14 | 15 | componentDidMount() { 16 | // 编辑器内容点击不弹文字 17 | document.getElementById('normalMarkdownEditor') 18 | .parentNode // 获取编辑器container 19 | .addEventListener('click', e => e.stopPropagation()); 20 | this.smde = new SimpleMDE({ 21 | element: document.getElementById('normalMarkdownEditor').childElementCount, 22 | autofocus: true, 23 | autosave: true, 24 | initialValue: '', 25 | indentWithTabs: false, 26 | placeholder: '## 输入试题内容...', 27 | renderingConfig: { 28 | singleLineBreaks: false, 29 | codeSyntaxHighlighting: true, 30 | }, 31 | insertTexts: { 32 | horizontalRule: ["", "\n\n-----\n\n"], 33 | image: ["![](http://", ")"], 34 | link: ["[", "](http://)"], 35 | table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"], 36 | }, 37 | previewRender: function(plainText) { 38 | return marked(plainText, { 39 | renderer: new marked.Renderer(), 40 | gfm: true, 41 | pedantic: false, 42 | sanitize: false, 43 | tables: true, 44 | breaks: true, 45 | smartLists: true, 46 | smartypants: true, 47 | highlight: function (code) { 48 | return hljs.highlightAuto(code).value; 49 | } 50 | }); 51 | }, 52 | spellChecker: false 53 | }); 54 | } 55 | 56 | render() { 57 | return ( 58 |
59 | 60 |
61 | ); 62 | } 63 | } 64 | 65 | export default SimpleMDEditor; -------------------------------------------------------------------------------- /components/Normal/index.js: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import dynamic from 'next/dynamic'; 3 | 4 | const SimpleMDEditor = dynamic(import('./SimpleMde'), { 5 | ssr: false 6 | }); 7 | 8 | const NormalEditor = () => ( 9 | 10 | 26 |

普通Markdown编辑器

27 |
28 | 29 |
30 |
31 | ); 32 | 33 | export default NormalEditor; -------------------------------------------------------------------------------- /components/Preview/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import MarkDown from 'react-markdown'; 4 | import marked from 'marked'; 5 | import hljs from 'highlight.js'; 6 | import 'highlight.js/styles/github.css'; 7 | 8 | const initialValue = ` 9 | ## Next-Markdown-Editor 10 | \`\`\` 11 | let a; 12 | a = 1; 13 | \`\`\` 14 | 这是一个预览页的Demo 15 | > var a = 1; 16 | `; 17 | class Markdown extends Component { 18 | static propTypes = { 19 | markdownValue: PropTypes.string 20 | } 21 | static defaultProps = { 22 | markdownValue: initialValue 23 | } 24 | constructor(props) { 25 | super(props); 26 | const { markdownValue } = props; 27 | this.state = { markdownValue }; 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 | 45 | 63 |
64 | ); 65 | } 66 | } 67 | 68 | export default Markdown; -------------------------------------------------------------------------------- /constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | // ================= 首页部分 ==================== // 2 | export const INCREMENT = 'INCREMENT'; 3 | export const DECREMENT = 'DECREMENT'; 4 | export const RESET = 'RESET'; 5 | 6 | // ================= 用户部分 ==================== // 7 | 8 | export const FETCH_USER_LIST = 'FETCH_USER_LIST'; 9 | export const FETCH_USER_LIST_FAIL = 'FETCH_USER_LIST_FAIL'; 10 | export const FETCH_USER_LIST_SUCCESS = 'FETCH_USER_LIST_SUCCESS'; -------------------------------------------------------------------------------- /constants/ApiUrlForBE.js: -------------------------------------------------------------------------------- 1 | // const API url 2 | 3 | export default { 4 | /** 5 | * 获取用户列表数据 6 | * @method GET 7 | */ 8 | getUserList: '../static/users.json' 9 | }; -------------------------------------------------------------------------------- /constants/ConstTypes.js: -------------------------------------------------------------------------------- 1 | // 用户级别 2 | export const RoleType = { 3 | 1: '管理员', 4 | 10: '普通用户' 5 | }; 6 | 7 | // 路由对应页面标题 8 | export const RouterTitle = { 9 | '/': '首页', 10 | '/user/userList': '用户列表', 11 | '/user/userDetail': '用户详情' 12 | }; 13 | 14 | export const TagsArray = [ 15 | { 16 | name: 'HTML', 17 | url: '/static/tags/HTML.png' 18 | }, { 19 | name: 'JavaScript', 20 | url: '/static/tags/JavaScript.png' 21 | }, { 22 | name: 'CSS', 23 | url: '/static/tags/CSS.png' 24 | }, { 25 | name: '前端', 26 | url: '/static/tags/WEB.png' 27 | }, { 28 | name: 'React', 29 | url: '/static/tags/React.png' 30 | }, { 31 | name: 'Vue', 32 | url: '/static/tags/Vue.png' 33 | }, { 34 | name: 'Angular', 35 | url: '/static/tags/Angular.png' 36 | }, { 37 | name: 'Webpack', 38 | url: '/static/tags/Webpack.png' 39 | }, { 40 | name: 'Nodejs', 41 | url: '/static/tags/Nodejs.png' 42 | }, { 43 | name: '后端', 44 | url: '/static/tags/Back.png' 45 | }, { 46 | name: 'Java', 47 | url: '/static/tags/Java.png' 48 | }, { 49 | name: '产品', 50 | url: '/static/tags/Product.png' 51 | }, { 52 | name: '数据库', 53 | url: '/static/tags/Database.png' 54 | }, { 55 | name: '算法', 56 | url: '/static/tags/Algorithm.png' 57 | }, { 58 | name: 'Python', 59 | url: '/static/tags/Python.png' 60 | }, { 61 | name: 'Http', 62 | url: '/static/tags/Http.png' 63 | }, 64 | ]; -------------------------------------------------------------------------------- /constants/CustomTheme.js: -------------------------------------------------------------------------------- 1 | /* 各种颜色常量 */ 2 | export const color_youdao = '#E93D34'; 3 | export const color_youdao_border = '#c20c0c'; 4 | export const color_primary = '#53f'; 5 | 6 | -------------------------------------------------------------------------------- /containers/home/counter.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { increment, decrement, reset } from '../../redux/actions/home'; 3 | import Counter from '../../components/Home/Counter'; 4 | 5 | const mapStateToProps = state => ({ 6 | count: state.home.counter.count, 7 | }); 8 | 9 | const mapDispatchToProps = dispatch => ({ 10 | increment() { 11 | dispatch(increment()); 12 | }, 13 | decrement() { 14 | dispatch(decrement()); 15 | }, 16 | reset() { 17 | dispatch(reset()); 18 | } 19 | }); 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(Counter); 22 | -------------------------------------------------------------------------------- /containers/user/UserList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { fetchUserListData } from '../../redux/actions/user'; 3 | import UserList from '../../components/User/UserList'; 4 | 5 | const mapStateToProps = state => ({ 6 | list: state.user.list.list, 7 | }); 8 | 9 | const mapDispatchToProps = dispatch => ({ 10 | fetchUserListData() { 11 | dispatch(fetchUserListData()); 12 | } 13 | }); 14 | 15 | export default connect(mapStateToProps, mapDispatchToProps)(UserList); 16 | -------------------------------------------------------------------------------- /core/nextFetch.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-unfetch'; 2 | import qs from 'query-string'; 3 | import { filterObject } from './util'; 4 | 5 | // initial fetch 6 | const nextFetch = Object.create(null); 7 | // browser support methods 8 | // ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PATCH', 'PUT'] 9 | const HTTP_METHOD = ['get', 'post', 'put', 'patch', 'delete']; 10 | // can send data method 11 | const CAN_SEND_METHOD = ['post', 'put', 'delete', 'patch']; 12 | 13 | HTTP_METHOD.forEach(method => { 14 | // is can send data in opt.body 15 | const canSend = CAN_SEND_METHOD.includes(method); 16 | nextFetch[method] = (path, { data, query, timeout = 10000 } = {}) => { 17 | let url = path; 18 | const opts = { 19 | method, 20 | headers: { 21 | 'Content-Type': 'application/x-www-form-urlencoded', 22 | Accept: 'application/json' 23 | }, 24 | credentials: 'include', 25 | timeout, 26 | mode: 'same-origin', 27 | cache: 'no-cache' 28 | }; 29 | 30 | if (query) { 31 | url += `${url.includes('?') ? '&' : '?'}${qs.stringify( 32 | filterObject(query, Boolean), 33 | )}`; 34 | } 35 | 36 | if (canSend && data) { 37 | opts.body = qs.stringify(filterObject(data, Boolean)); 38 | } 39 | 40 | console.info('Request Url:', url); 41 | 42 | return fetch(url, opts) 43 | .then(res => res.json()) 44 | .then(({ errcode = 0, errmsg, data }) => { 45 | if (errcode !== 0) { 46 | const err = new Error(errmsg); 47 | err.message = errmsg; 48 | err.code = errcode; 49 | err.data = data; 50 | throw err; 51 | } 52 | return data; 53 | }); 54 | }; 55 | }); 56 | 57 | export default nextFetch; -------------------------------------------------------------------------------- /core/util.js: -------------------------------------------------------------------------------- 1 | // transform the http query & params 2 | export const filterObject = (o, filter) => { 3 | const r = {}; 4 | Object.keys(o).forEach(k => { 5 | if (filter(o[k], k)) { 6 | r[k] = o[k]; 7 | } 8 | }); 9 | return r; 10 | }; -------------------------------------------------------------------------------- /middlewares/client/user.js: -------------------------------------------------------------------------------- 1 | import { message } from 'antd'; 2 | import { 3 | FETCH_USER_LIST_FAIL 4 | } from '../../constants/ActionTypes'; 5 | 6 | export default ({ getState }) => next => action => { 7 | console.log(getState()); 8 | const ret = next(action); 9 | switch (action.type) { 10 | case FETCH_USER_LIST_FAIL: { 11 | message.error('获取用户列表失败, 请稍后重试'); 12 | break; 13 | } 14 | default: 15 | } 16 | return ret; 17 | }; 18 | -------------------------------------------------------------------------------- /middlewares/server/README.md: -------------------------------------------------------------------------------- 1 | ### deal request in node, like download file stream etc... -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const withLess = require('@zeit/next-less'); 3 | const withCss = require('@zeit/next-css'); 4 | const lessToJS = require('less-vars-to-js'); 5 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); 6 | const TerserPlugin = require('terser-webpack-plugin'); 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | 10 | // Where your antd-custom.less file lives 11 | const themeVariables = lessToJS( 12 | fs.readFileSync( 13 | path.resolve(__dirname, './asserts/antd-custom.less'), 14 | 'utf8', 15 | ), 16 | ); 17 | 18 | // fix: prevents error when .css files are required by node 19 | if (typeof require !== 'undefined') { 20 | require.extensions['.less'] = (file) => {} 21 | } 22 | 23 | module.exports = withCss(withLess({ 24 | exportPathMap: async function (defaultPathMap) { 25 | return { 26 | '/': { page: '/' }, 27 | '/normal': { page: '/normal' }, 28 | '/edit': { page: '/edit' }, 29 | '/preview': { page: '/preview' } 30 | } 31 | }, 32 | lessLoaderOptions: { 33 | javascriptEnabled: true, 34 | modifyVars: themeVariables, 35 | localIdentName: '[local]___[hash:base64:5]', 36 | }, 37 | webpack: (config, { buildId, dev, isServer, defaultLoaders }) => { 38 | if (!dev) { 39 | config.plugins.push( 40 | ...[ 41 | new BundleAnalyzerPlugin({ 42 | analyzerMode: 'disabled', 43 | // For all options see https://github.com/th0r/webpack-bundle-analyzer#as-plugin 44 | generateStatsFile: true, 45 | // Will be available at `.next/stats.json` 46 | statsFilename: 'stats.json' 47 | }), 48 | // 代替uglyJsPlugin 49 | new TerserPlugin({ 50 | terserOptions: { 51 | ecma: 6, 52 | warnings: false, 53 | extractComments: false, // remove comment 54 | compress: { 55 | drop_console: true // remove console 56 | }, 57 | ie8: false 58 | } 59 | }), 60 | ]); 61 | config.devtool = 'source-map'; 62 | } else { 63 | config.module.rules.push({ 64 | test: /\.js$/, 65 | enforce: 'pre', 66 | include: [ 67 | path.resolve('components'), 68 | path.resolve('pages'), 69 | path.resolve('utils'), 70 | path.resolve('constants'), 71 | path.resolve('redux'), 72 | path.resolve('containers') 73 | ], 74 | options: { 75 | configFile: path.resolve('.eslintrc'), 76 | eslint: { 77 | configFile: path.resolve(__dirname, '.eslintrc') 78 | } 79 | }, 80 | loader: 'eslint-loader' 81 | }); 82 | config.devtool = 'cheap-module-inline-source-map'; 83 | } 84 | return config; 85 | }, 86 | webpackDevMiddleware: config => { 87 | // Perform customizations to webpack dev middleware config 88 | // console.log(config, '@@') 89 | // Important: return the modified config 90 | return config; 91 | } 92 | })); -------------------------------------------------------------------------------- /out/_next/static/NSy5qE4UvMw4dCrM10co1/pages/_error.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{123:function(e,t,n){e.exports=n(46)},339:function(e,t,n){__NEXT_REGISTER_PAGE("/_error",function(){return e.exports=n(632),{page:e.exports.default}})},632:function(e,t,n){"use strict";n.r(t);var r=n(0),o=n.n(r),i=(n(1),n(110),n(34)),c=n.n(i),a=n(3),u=n.n(a),s=n(123),l=n.n(s);function f(e){return(f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function p(e,t){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;i||(i=document.createElement("textarea"),document.body.appendChild(i)),e.getAttribute("wrap")?i.setAttribute("wrap",e.getAttribute("wrap")):i.removeAttribute("wrap");var u=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=e.getAttribute("id")||e.getAttribute("data-reactid")||e.getAttribute("name");if(t&&o[n])return o[n];var r=window.getComputedStyle(e),i=r.getPropertyValue("box-sizing")||r.getPropertyValue("-moz-box-sizing")||r.getPropertyValue("-webkit-box-sizing"),l=parseFloat(r.getPropertyValue("padding-bottom"))+parseFloat(r.getPropertyValue("padding-top")),u=parseFloat(r.getPropertyValue("border-bottom-width"))+parseFloat(r.getPropertyValue("border-top-width")),s={sizingStyle:a.map(function(e){return e+":"+r.getPropertyValue(e)}).join(";"),paddingSize:l,borderSize:u,boxSizing:i};return t&&n&&(o[n]=s),s}(e,t),s=u.paddingSize,f=u.borderSize,d=u.boxSizing,p=u.sizingStyle;i.setAttribute("style",p+";"+r),i.value=e.value||e.placeholder||"";var c=Number.MIN_SAFE_INTEGER,m=Number.MAX_SAFE_INTEGER,y=i.scrollHeight,h=void 0;if("border-box"===d?y+=f:"content-box"===d&&(y-=s),null!==n||null!==l){i.value=" ";var b=i.scrollHeight-s;null!==n&&(c=b*n,"border-box"===d&&(c=c+s+f),y=Math.max(c,y)),null!==l&&(m=b*l,"border-box"===d&&(m=m+s+f),h=y>m?"":"hidden",y=Math.min(m,y))}return l||(h="hidden"),{height:y,minHeight:c,maxHeight:m,overflowY:h}};var r="\n min-height:0 !important;\n max-height:none !important;\n height:0 !important;\n visibility:hidden !important;\n overflow:hidden !important;\n position:absolute !important;\n z-index:-1000 !important;\n top:0 !important;\n right:0 !important\n",a=["letter-spacing","line-height","padding-top","padding-bottom","font-family","font-weight","font-size","text-rendering","text-transform","width","text-indent","padding-left","padding-right","border-width","box-sizing"],o={},i=void 0;e.exports=t.default},60:function(e,t,n){e.exports=n(111)},631:function(e,t,n){"use strict";n.r(t),n(347);var r=n(185),a=n.n(r),o=n(3),i=n.n(o),l=n(0),u=n.n(l),s=n(60);function f(e){return(f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function d(e,t){for(var n=0;n0&&void 0!==n[0]?n[0]:{}).webpackHMR,e.next=4,N.loadPage("/_error");case 4:return t.ErrorComponent=L=e.sent,e.next=7,N.loadPage("/_app");case 7:return H=e.sent,r=C,e.prev=9,e.next=12,N.loadPage(P);case 12:if("function"==typeof(G=e.sent)){e.next=15;break}throw new Error('The default export is not a React Component in page: "'.concat(P,'"'));case 15:e.next=20;break;case 17:e.prev=17,e.t0=e.catch(9),r=e.t0;case 20:return e.next=22,_.default.preloadReady(T||[]);case 22:return t.router=j=(0,f.createRouter)(P,b,M,{initialProps:x,pageLoader:N,App:H,Component:G,ErrorComponent:L,err:r}),j.subscribe(function(e){z({App:e.App,Component:e.Component,props:e.props,err:e.err,emitter:D})}),z({App:H,Component:G,props:x,err:r,emitter:D}),e.abrupt("return",D);case 26:case"end":return e.stop()}},e,this,[[9,17]])}));function z(e){return function(){return(0,i.default)(u.default.mark(function e(t){return u.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!t.err){e.next=4;break}return e.next=3,B(t);case 3:return e.abrupt("return");case 4:return e.prev=4,e.next=7,J(t);case 7:e.next=13;break;case 9:return e.prev=9,e.t0=e.catch(4),e.next=13,B((0,o.default)({},t,{err:e.t0}));case 13:case"end":return e.stop()}},e,this,[[4,9]])})).apply(this,arguments)}.apply(this,arguments)}function B(e){return function(){return(0,i.default)(u.default.mark(function e(t){var r,n,a;return u.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:r=t.App,n=t.err,e.next=3;break;case 3:if(!t.props){e.next=8;break}e.t0=t.props,e.next=11;break;case 8:return e.next=10,(0,v.loadGetInitialProps)(r,{Component:L,router:j,ctx:{err:n,pathname:P,query:b,asPath:M}});case 10:e.t0=e.sent;case 11:return a=e.t0,e.next=14,J((0,o.default)({},t,{err:n,Component:L,props:a}));case 14:case"end":return e.stop()}},e,this)})).apply(this,arguments)}.apply(this,arguments)}t.default=X;var U=!0;function J(e){return function(){return(0,i.default)(u.default.mark(function e(t){var r,n,a,s,c,l,f,h,m,g,y,_;return u.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(r=t.App,n=t.Component,a=t.props,s=t.err,c=t.emitter,l=void 0===c?D:c,a||!n||n===L||O.Component!==L){e.next=6;break}return h=(f=j).pathname,m=f.query,g=f.asPath,e.next=5,(0,v.loadGetInitialProps)(r,{Component:n,router:j,ctx:{err:s,pathname:h,query:m,asPath:g}});case 5:a=e.sent;case 6:n=n||O.Component,a=a||O.props,y=(0,o.default)({Component:n,err:s,router:j,headManager:S},a),O=y,l.emit("before-reactdom-render",{Component:n,ErrorComponent:L,appProps:y}),_=function(){var e=(0,i.default)(u.default.mark(function e(t){return u.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,B({App:r,err:t});case 3:e.next=8;break;case 5:e.prev=5,e.t0=e.catch(0);case 8:case"end":return e.stop()}},e,this,[[0,5]])}));return function(t){return e.apply(this,arguments)}}(),w=p.default.createElement(E.default,{onError:_},p.default.createElement(r,y)),x=q,U&&"function"==typeof d.default.hydrate?(d.default.hydrate(w,x),U=!1):d.default.render(w,x),l.emit("after-reactdom-render",{Component:n,ErrorComponent:L,appProps:y});case 13:case"end":return e.stop()}var w,x},e,this)})).apply(this,arguments)}.apply(this,arguments)}},225:function(e,t,r){"use strict";var n=r(5);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=n(r(43)),o=n(r(11)),u=n(r(12)),i={acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},s=function(){function e(){(0,o.default)(this,e),this.updatePromise=null}return(0,u.default)(e,[{key:"updateHead",value:function(e){var t=this,r=this.updatePromise=a.default.resolve().then(function(){r===t.updatePromise&&(t.updatePromise=null,t.doUpdateHead(e))})}},{key:"doUpdateHead",value:function(e){var t=this,r={};e.forEach(function(e){var t=r[e.type]||[];t.push(e),r[e.type]=t}),this.updateTitle(r.title?r.title[0]:null),["meta","base","link","style","script"].forEach(function(e){t.updateElements(e,r[e]||[])})}},{key:"updateTitle",value:function(e){var t;if(e){var r=e.props.children;t="string"==typeof r?r:r.join("")}else t="";t!==document.title&&(document.title=t)}},{key:"updateElements",value:function(e,t){var r=document.getElementsByTagName("head")[0],n=Array.prototype.slice.call(r.querySelectorAll(e+".next-head")),a=t.map(c).filter(function(e){for(var t=0,r=n.length;tNext-Markdown-Editor

loading...

编辑页面底部内容
-------------------------------------------------------------------------------- /out/index.html: -------------------------------------------------------------------------------- 1 | Next-Markdown-Editor

Next-Markdown-Editor

-------------------------------------------------------------------------------- /out/normal/index.html: -------------------------------------------------------------------------------- 1 | Next-Markdown-Editor

普通Markdown编辑器

loading...

-------------------------------------------------------------------------------- /out/preview/index.html: -------------------------------------------------------------------------------- 1 | Next-Markdown-Editor
XXX系统

Next-Markdown-Editor

Next-Markdown-Editor

16 |
    let a;
17 |     a = 1;

这是一个预览页的Demo

18 |
19 |

var a = 1;

20 |
-------------------------------------------------------------------------------- /out/static/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/empty.png -------------------------------------------------------------------------------- /out/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/favicon.ico -------------------------------------------------------------------------------- /out/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/logo.png -------------------------------------------------------------------------------- /out/static/tags/Algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Algorithm.png -------------------------------------------------------------------------------- /out/static/tags/Angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Angular.png -------------------------------------------------------------------------------- /out/static/tags/Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Back.png -------------------------------------------------------------------------------- /out/static/tags/CSS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/CSS.png -------------------------------------------------------------------------------- /out/static/tags/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Database.png -------------------------------------------------------------------------------- /out/static/tags/HTML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/HTML.png -------------------------------------------------------------------------------- /out/static/tags/Http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Http.png -------------------------------------------------------------------------------- /out/static/tags/Java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Java.png -------------------------------------------------------------------------------- /out/static/tags/JavaScript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/JavaScript.png -------------------------------------------------------------------------------- /out/static/tags/Nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Nodejs.png -------------------------------------------------------------------------------- /out/static/tags/Product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Product.png -------------------------------------------------------------------------------- /out/static/tags/Python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Python.png -------------------------------------------------------------------------------- /out/static/tags/React.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/React.png -------------------------------------------------------------------------------- /out/static/tags/Vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Vue.png -------------------------------------------------------------------------------- /out/static/tags/WEB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/WEB.png -------------------------------------------------------------------------------- /out/static/tags/Webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/tags/Webpack.png -------------------------------------------------------------------------------- /out/static/unknown_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/out/static/unknown_error.png -------------------------------------------------------------------------------- /out/static/users.json: -------------------------------------------------------------------------------- 1 | { 2 | "errcode": 500, 3 | "errmsg": "服务器内部错误", 4 | "data": [ 5 | { 6 | "id": 1, 7 | "name": "Leanne Graham", 8 | "username": "Bret", 9 | "email": "Sincere@april.biz", 10 | "address": { 11 | "street": "Kulas Light", 12 | "suite": "Apt. 556", 13 | "city": "Gwenborough", 14 | "zipcode": "92998-3874", 15 | "geo": { 16 | "lat": "-37.3159", 17 | "lng": "81.1496" 18 | } 19 | }, 20 | "phone": "1-770-736-8031 x56442", 21 | "website": "hildegard.org", 22 | "company": { 23 | "name": "Romaguera-Crona", 24 | "catchPhrase": "Multi-layered client-server neural-net", 25 | "bs": "harness real-time e-markets" 26 | } 27 | }, 28 | { 29 | "id": 2, 30 | "name": "Ervin Howell", 31 | "username": "Antonette", 32 | "email": "Shanna@melissa.tv", 33 | "address": { 34 | "street": "Victor Plains", 35 | "suite": "Suite 879", 36 | "city": "Wisokyburgh", 37 | "zipcode": "90566-7771", 38 | "geo": { 39 | "lat": "-43.9509", 40 | "lng": "-34.4618" 41 | } 42 | }, 43 | "phone": "010-692-6593 x09125", 44 | "website": "anastasia.net", 45 | "company": { 46 | "name": "Deckow-Crist", 47 | "catchPhrase": "Proactive didactic contingency", 48 | "bs": "synergize scalable supply-chains" 49 | } 50 | }, 51 | { 52 | "id": 3, 53 | "name": "Clementine Bauch", 54 | "username": "Samantha", 55 | "email": "Nathan@yesenia.net", 56 | "address": { 57 | "street": "Douglas Extension", 58 | "suite": "Suite 847", 59 | "city": "McKenziehaven", 60 | "zipcode": "59590-4157", 61 | "geo": { 62 | "lat": "-68.6102", 63 | "lng": "-47.0653" 64 | } 65 | }, 66 | "phone": "1-463-123-4447", 67 | "website": "ramiro.info", 68 | "company": { 69 | "name": "Romaguera-Jacobson", 70 | "catchPhrase": "Face to face bifurcated interface", 71 | "bs": "e-enable strategic applications" 72 | } 73 | }, 74 | { 75 | "id": 4, 76 | "name": "Patricia Lebsack", 77 | "username": "Karianne", 78 | "email": "Julianne.OConner@kory.org", 79 | "address": { 80 | "street": "Hoeger Mall", 81 | "suite": "Apt. 692", 82 | "city": "South Elvis", 83 | "zipcode": "53919-4257", 84 | "geo": { 85 | "lat": "29.4572", 86 | "lng": "-164.2990" 87 | } 88 | }, 89 | "phone": "493-170-9623 x156", 90 | "website": "kale.biz", 91 | "company": { 92 | "name": "Robel-Corkery", 93 | "catchPhrase": "Multi-tiered zero tolerance productivity", 94 | "bs": "transition cutting-edge web services" 95 | } 96 | }, 97 | { 98 | "id": 5, 99 | "name": "Chelsey Dietrich", 100 | "username": "Kamren", 101 | "email": "Lucio_Hettinger@annie.ca", 102 | "address": { 103 | "street": "Skiles Walks", 104 | "suite": "Suite 351", 105 | "city": "Roscoeview", 106 | "zipcode": "33263", 107 | "geo": { 108 | "lat": "-31.8129", 109 | "lng": "62.5342" 110 | } 111 | }, 112 | "phone": "(254)954-1289", 113 | "website": "demarco.info", 114 | "company": { 115 | "name": "Keebler LLC", 116 | "catchPhrase": "User-centric fault-tolerant solution", 117 | "bs": "revolutionize end-to-end systems" 118 | } 119 | }, 120 | { 121 | "id": 6, 122 | "name": "Mrs. Dennis Schulist", 123 | "username": "Leopoldo_Corkery", 124 | "email": "Karley_Dach@jasper.info", 125 | "address": { 126 | "street": "Norberto Crossing", 127 | "suite": "Apt. 950", 128 | "city": "South Christy", 129 | "zipcode": "23505-1337", 130 | "geo": { 131 | "lat": "-71.4197", 132 | "lng": "71.7478" 133 | } 134 | }, 135 | "phone": "1-477-935-8478 x6430", 136 | "website": "ola.org", 137 | "company": { 138 | "name": "Considine-Lockman", 139 | "catchPhrase": "Synchronised bottom-line interface", 140 | "bs": "e-enable innovative applications" 141 | } 142 | }, 143 | { 144 | "id": 7, 145 | "name": "Kurtis Weissnat", 146 | "username": "Elwyn.Skiles", 147 | "email": "Telly.Hoeger@billy.biz", 148 | "address": { 149 | "street": "Rex Trail", 150 | "suite": "Suite 280", 151 | "city": "Howemouth", 152 | "zipcode": "58804-1099", 153 | "geo": { 154 | "lat": "24.8918", 155 | "lng": "21.8984" 156 | } 157 | }, 158 | "phone": "210.067.6132", 159 | "website": "elvis.io", 160 | "company": { 161 | "name": "Johns Group", 162 | "catchPhrase": "Configurable multimedia task-force", 163 | "bs": "generate enterprise e-tailers" 164 | } 165 | }, 166 | { 167 | "id": 8, 168 | "name": "Nicholas Runolfsdottir V", 169 | "username": "Maxime_Nienow", 170 | "email": "Sherwood@rosamond.me", 171 | "address": { 172 | "street": "Ellsworth Summit", 173 | "suite": "Suite 729", 174 | "city": "Aliyaview", 175 | "zipcode": "45169", 176 | "geo": { 177 | "lat": "-14.3990", 178 | "lng": "-120.7677" 179 | } 180 | }, 181 | "phone": "586.493.6943 x140", 182 | "website": "jacynthe.com", 183 | "company": { 184 | "name": "Abernathy Group", 185 | "catchPhrase": "Implemented secondary concept", 186 | "bs": "e-enable extensible e-tailers" 187 | } 188 | }, 189 | { 190 | "id": 9, 191 | "name": "Glenna Reichert", 192 | "username": "Delphine", 193 | "email": "Chaim_McDermott@dana.io", 194 | "address": { 195 | "street": "Dayna Park", 196 | "suite": "Suite 449", 197 | "city": "Bartholomebury", 198 | "zipcode": "76495-3109", 199 | "geo": { 200 | "lat": "24.6463", 201 | "lng": "-168.8889" 202 | } 203 | }, 204 | "phone": "(775)976-6794 x41206", 205 | "website": "conrad.com", 206 | "company": { 207 | "name": "Yost and Sons", 208 | "catchPhrase": "Switchable contextually-based project", 209 | "bs": "aggregate real-time technologies" 210 | } 211 | }, 212 | { 213 | "id": 10, 214 | "name": "Clementina DuBuque", 215 | "username": "Moriah.Stanton", 216 | "email": "Rey.Padberg@karina.biz", 217 | "address": { 218 | "street": "Kattie Turnpike", 219 | "suite": "Suite 198", 220 | "city": "Lebsackbury", 221 | "zipcode": "31428-2261", 222 | "geo": { 223 | "lat": "-38.2386", 224 | "lng": "57.2232" 225 | } 226 | }, 227 | "phone": "024-648-3804", 228 | "website": "ambrose.net", 229 | "company": { 230 | "name": "Hoeger LLC", 231 | "catchPhrase": "Centralized empowering task-force", 232 | "bs": "target end-to-end models" 233 | } 234 | } 235 | ] 236 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "first-next-demo", 3 | "version": "1.0.0", 4 | "description": "first nextjs demo", 5 | "main": "index.js", 6 | "author": "luffyZhou", 7 | "license": "MIT", 8 | "scripts": { 9 | "start": "node server.js", 10 | "build": "next build", 11 | "prod": "NODE_ENV=production node server.js", 12 | "preexport": "next build", 13 | "export": "next export", 14 | "static-run": "next export && serve out", 15 | "deploy": "rm -rf node_modules/.cache && next build && next export && touch out/.nojekyll && git add out/ && git commit -m \"Deploy Next.js to gh-pages\" && git subtree push --prefix out origin gh-pages" 16 | }, 17 | "dependencies": { 18 | "@zeit/next-css": "^1.0.1", 19 | "@zeit/next-less": "^1.0.1", 20 | "antd": "^3.8.4", 21 | "babel-plugin-import": "^1.9.0", 22 | "es6-promise": "^4.2.5", 23 | "express": "^4.16.3", 24 | "highlight.js": "^9.13.1", 25 | "if-comp": "^0.0.8", 26 | "isomorphic-unfetch": "^3.0.0", 27 | "less": "^3.8.1", 28 | "less-vars-to-js": "^1.3.0", 29 | "lodash": "^4.17.11", 30 | "marked": "^0.6.2", 31 | "next": "^7.0.2", 32 | "next-redux-saga": "^3.0.0", 33 | "next-redux-wrapper": "^2.0.0", 34 | "prop-types": "^15.6.2", 35 | "query-string": "^6.2.0", 36 | "react": "^16.5.0", 37 | "react-dom": "^16.5.0", 38 | "react-markdown": "^4.0.4", 39 | "react-redux": "^5.0.7", 40 | "redux": "^4.0.0", 41 | "redux-logger": "^3.0.6", 42 | "redux-saga": "^0.16.0", 43 | "serve": "^10.1.1", 44 | "simplemde": "^1.11.2" 45 | }, 46 | "devDependencies": { 47 | "@babel/plugin-proposal-decorators": "^7.1.0", 48 | "babel-eslint": "^9.0.0", 49 | "babel-plugin-lodash": "^3.3.4", 50 | "eslint": "^5.4.0", 51 | "eslint-config-react-app": "^2.1.0", 52 | "eslint-loader": "^2.1.0", 53 | "eslint-plugin-flowtype": "^2.50.0", 54 | "eslint-plugin-import": "^2.14.0", 55 | "eslint-plugin-jsx-a11y": "^6.1.1", 56 | "eslint-plugin-react": "^7.11.1", 57 | "redux-devtools-extension": "^2.13.5", 58 | "terser-webpack-plugin": "^1.1.0", 59 | "webpack-bundle-analyzer": "^3.3.2" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import App, { Container } from 'next/app'; 3 | import Head from 'next/head'; 4 | import { Provider } from 'react-redux'; 5 | import withRedux from 'next-redux-wrapper'; 6 | import withReduxSaga from 'next-redux-saga'; 7 | import createStore from '../redux/store'; 8 | import Layout from '../components/Layout'; 9 | import '../asserts/styles.less'; 10 | class InTerViewSystem extends App { 11 | 12 | static async getInitialProps ({ Component, ctx }) { 13 | let pageProps = {}; 14 | 15 | if (Component.getInitialProps) { 16 | pageProps = await Component.getInitialProps({ ctx }); 17 | } 18 | 19 | return { pageProps }; 20 | } 21 | 22 | render () { 23 | const { Component, pageProps, store, router } = this.props; 24 | return ( 25 | 26 | 27 | 28 | 29 | Next-Markdown-Editor 30 | 31 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | } 53 | 54 | export default withRedux(createStore)(withReduxSaga({ async: true })(InTerViewSystem)); -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | // _document is only rendered on the server side and not on the client side 2 | // Event handlers like onClick can't be added to this file 3 | 4 | // ./pages/_document.js 5 | import Document, { Head, Main, NextScript } from 'next/document'; 6 | 7 | export default class MyDocument extends Document { 8 | static async getInitialProps(ctx) { 9 | const initialProps = await Document.getInitialProps(ctx); 10 | return { ...initialProps }; 11 | } 12 | 13 | render() { 14 | return ( 15 | 16 | 17 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | ); 34 | } 35 | } -------------------------------------------------------------------------------- /pages/_error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ErrorPage from '../components/ErrorPage'; 4 | export default class Error extends React.Component { 5 | static propTypes = { 6 | statusCode: PropTypes.number 7 | } 8 | static defaultProps = { 9 | statusCode: 200 10 | } 11 | static getInitialProps({ res, err }) { 12 | const statusCode = res ? res.statusCode : err ? err.statusCode : null; 13 | return { statusCode }; 14 | } 15 | 16 | render() { 17 | return ( 18 | 19 | ); 20 | } 21 | } -------------------------------------------------------------------------------- /pages/edit/index.js: -------------------------------------------------------------------------------- 1 | import Edit from '../../components/Edit'; 2 | 3 | export default Edit; -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Home from '../components/Home'; 2 | 3 | export default Home; -------------------------------------------------------------------------------- /pages/normal/index.js: -------------------------------------------------------------------------------- 1 | import Normal from '../../components/Normal'; 2 | 3 | export default Normal; -------------------------------------------------------------------------------- /pages/preview/index.js: -------------------------------------------------------------------------------- 1 | import Preview from '../../components/Preview'; 2 | 3 | export default Preview; -------------------------------------------------------------------------------- /redux/actions/home.js: -------------------------------------------------------------------------------- 1 | import { 2 | INCREMENT, 3 | DECREMENT, 4 | RESET 5 | } from '../../constants/ActionTypes'; 6 | 7 | export function increment () { 8 | return { 9 | type: INCREMENT 10 | }; 11 | } 12 | 13 | export function decrement () { 14 | return { 15 | type: DECREMENT 16 | }; 17 | } 18 | 19 | export function reset () { 20 | return { 21 | type: RESET 22 | }; 23 | } -------------------------------------------------------------------------------- /redux/actions/user.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_USER_LIST, 3 | FETCH_USER_LIST_FAIL, 4 | FETCH_USER_LIST_SUCCESS 5 | } from '../../constants/ActionTypes'; 6 | 7 | export function fetchUserListData () { 8 | return { 9 | type: FETCH_USER_LIST 10 | }; 11 | } 12 | 13 | export function fetchUserListDataSuccess (payload) { 14 | return { 15 | type: FETCH_USER_LIST_SUCCESS, 16 | payload 17 | }; 18 | } 19 | 20 | export function fetchUserListDataFali () { 21 | return { 22 | type: FETCH_USER_LIST_FAIL, 23 | }; 24 | } -------------------------------------------------------------------------------- /redux/reducers/home/counter.js: -------------------------------------------------------------------------------- 1 | import { 2 | INCREMENT, 3 | DECREMENT, 4 | RESET 5 | } from '../../../constants/ActionTypes'; 6 | 7 | const initialState = { 8 | count: 0 9 | }; 10 | 11 | const counter = (state = initialState, { type }) => { 12 | switch (type) { 13 | case INCREMENT: 14 | return { 15 | ...state, 16 | count: state.count + 1 17 | }; 18 | case DECREMENT: 19 | return { 20 | ...state, 21 | count: state.count - 1 22 | }; 23 | case RESET: 24 | return { 25 | ...state, 26 | count: 0 27 | }; 28 | default: 29 | return state; 30 | } 31 | }; 32 | 33 | export default counter; 34 | -------------------------------------------------------------------------------- /redux/reducers/home/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import counter from './counter'; 3 | 4 | export default combineReducers({ 5 | counter 6 | }); -------------------------------------------------------------------------------- /redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import home from './home'; 3 | import user from './user'; 4 | 5 | export default combineReducers({ 6 | home, 7 | user 8 | }); -------------------------------------------------------------------------------- /redux/reducers/user/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import list from './list'; 3 | 4 | export default combineReducers({ 5 | list 6 | }); -------------------------------------------------------------------------------- /redux/reducers/user/list.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_USER_LIST, 3 | FETCH_USER_LIST_FAIL, 4 | FETCH_USER_LIST_SUCCESS 5 | } from '../../../constants/ActionTypes'; 6 | 7 | const initialState = { 8 | list: [] 9 | }; 10 | 11 | const list = (state = initialState, { type, payload }) => { 12 | switch (type) { 13 | case FETCH_USER_LIST: 14 | case FETCH_USER_LIST_FAIL: 15 | return initialState; 16 | case FETCH_USER_LIST_SUCCESS: 17 | return { 18 | ...state, 19 | list: payload 20 | }; 21 | default: 22 | return state; 23 | } 24 | }; 25 | 26 | export default list; 27 | -------------------------------------------------------------------------------- /redux/sagas/index.js: -------------------------------------------------------------------------------- 1 | import { all } from 'redux-saga/effects'; 2 | import userSagas from './user/index'; 3 | 4 | 5 | export default function* rootSagas() { 6 | yield all([ 7 | ...userSagas 8 | ]); 9 | } 10 | -------------------------------------------------------------------------------- /redux/sagas/user/index.js: -------------------------------------------------------------------------------- 1 | 2 | import userList from './userList'; 3 | 4 | const userSagas = [ 5 | ...userList, 6 | ]; 7 | 8 | export default userSagas; -------------------------------------------------------------------------------- /redux/sagas/user/userList.js: -------------------------------------------------------------------------------- 1 | import { take, put, fork } from 'redux-saga/effects'; 2 | import { 3 | FETCH_USER_LIST, 4 | } from '../../../constants/ActionTypes'; 5 | import { fetchUserListDataFali, fetchUserListDataSuccess } from '../../actions/user'; 6 | import api from '../../../constants/ApiUrlForBE'; 7 | import nextFetch from '../../../core/nextFetch'; 8 | /** 9 | * 简洁的实际写法, 把worker saga和watcher saga结合在一起。写起来方便 10 | */ 11 | export function* userList() { 12 | while (true) { 13 | yield take(FETCH_USER_LIST); 14 | try { 15 | const data = yield nextFetch.get(api.getUserList); 16 | // const res = yield fetch(api.getUserList); 17 | // const { data } = yield res.json(); 18 | yield put(fetchUserListDataSuccess(data)); 19 | } catch (error) { 20 | console.log(error.code, error.message, error.data); 21 | yield put(fetchUserListDataFali(error)); 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * 合理的官方写法,分离了watcher saga和worker saga 28 | */ 29 | // // worker saga 30 | // export function* getUserList() { 31 | // try { 32 | // const { data } = yield call(http.get, apiUrl.USER_LIST); 33 | // yield put({ type: FETCH_ALL_USER_LIST_SUCCESS, payload: data }); 34 | // } catch(error) { 35 | // yield put({ type: FETCH_ALL_USER_LIST_FAIL, payload: error }); 36 | // } 37 | // } 38 | // // watcher saga 39 | // export function* watchGetUserList() { 40 | // yield takeEvery(FETCH_ALL_USER_LIST, getUserList); 41 | // } 42 | 43 | export default [ 44 | fork(userList) 45 | ]; 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import createSagaMiddleware from 'redux-saga'; 3 | import rootReducer from './reducers/index'; 4 | import rootSaga from './sagas/index'; 5 | import userMiddleware from '../middlewares/client/user'; 6 | 7 | const sagaMiddleware = createSagaMiddleware(); 8 | 9 | const bindMiddleware = (middleware) => { 10 | // add route middleware 11 | middleware.push(userMiddleware); 12 | if (process.env.NODE_ENV !== 'production') { 13 | const { composeWithDevTools } = require('redux-devtools-extension'); 14 | // 开发模式打印redux信息 15 | const { logger } = require('redux-logger'); 16 | middleware.push(logger); 17 | return composeWithDevTools(applyMiddleware(...middleware)); 18 | } 19 | return applyMiddleware(...middleware); 20 | }; 21 | 22 | function configureStore (initialState) { 23 | const store = createStore( 24 | rootReducer, 25 | initialState, 26 | bindMiddleware([sagaMiddleware]) 27 | ); 28 | 29 | store.runSagaTask = () => { 30 | store.sagaTask = sagaMiddleware.run(rootSaga); 31 | }; 32 | 33 | store.runSagaTask(); 34 | return store; 35 | } 36 | 37 | export default configureStore; 38 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cp = require('child_process'); 3 | const { parse } = require('url'); 4 | const next = require('next'); 5 | 6 | const port = parseInt(process.env.PORT, 10) || 3006; 7 | // 判断开发环境和生产环境 8 | process.env.NODE_ENV = (typeof process.env.NODE_ENV !== 'undefined') 9 | ? process.env.NODE_ENV.trim() 10 | : 'development'; 11 | 12 | const dev = process.env.NODE_ENV !== 'production'; 13 | 14 | const app = next({ dev }); 15 | const handle = app.getRequestHandler(); 16 | 17 | app.prepare() 18 | .then(() => { 19 | const server = express(); 20 | 21 | server.get('*', (req, res) => { 22 | return handle(req, res); 23 | }); 24 | 25 | server.listen(port, (err) => { 26 | if (err) throw err; 27 | const serverUrl = `http:127.0.0.1:${port}`; 28 | console.log(`> Ready on ${serverUrl}`); 29 | // 开发环境自动启动 30 | if (dev) { 31 | switch (process.platform) { 32 | //mac系统使用 一下命令打开url在浏览器 33 | case 'darwin': 34 | cp.exec(`open ${serverUrl}`); 35 | break; 36 | //win系统使用 一下命令打开url在浏览器 37 | case 'win32': 38 | cp.exec(`start ${serverUrl}`); 39 | break; 40 | // 默认mac系统 41 | default: 42 | cp.exec(`open ${serverUrl}`); 43 | } 44 | } 45 | }); 46 | }); 47 | 48 | -------------------------------------------------------------------------------- /static/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/empty.png -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/favicon.ico -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/logo.png -------------------------------------------------------------------------------- /static/tags/Algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Algorithm.png -------------------------------------------------------------------------------- /static/tags/Angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Angular.png -------------------------------------------------------------------------------- /static/tags/Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Back.png -------------------------------------------------------------------------------- /static/tags/CSS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/CSS.png -------------------------------------------------------------------------------- /static/tags/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Database.png -------------------------------------------------------------------------------- /static/tags/HTML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/HTML.png -------------------------------------------------------------------------------- /static/tags/Http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Http.png -------------------------------------------------------------------------------- /static/tags/Java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Java.png -------------------------------------------------------------------------------- /static/tags/JavaScript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/JavaScript.png -------------------------------------------------------------------------------- /static/tags/Nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Nodejs.png -------------------------------------------------------------------------------- /static/tags/Product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Product.png -------------------------------------------------------------------------------- /static/tags/Python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Python.png -------------------------------------------------------------------------------- /static/tags/React.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/React.png -------------------------------------------------------------------------------- /static/tags/Vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Vue.png -------------------------------------------------------------------------------- /static/tags/WEB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/WEB.png -------------------------------------------------------------------------------- /static/tags/Webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/tags/Webpack.png -------------------------------------------------------------------------------- /static/unknown_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luffyZh/next-markdown-editor/42ae943a30b945d3b52bbdc5bd4fc06ab5884c4c/static/unknown_error.png -------------------------------------------------------------------------------- /static/users.json: -------------------------------------------------------------------------------- 1 | { 2 | "errcode": 500, 3 | "errmsg": "服务器内部错误", 4 | "data": [ 5 | { 6 | "id": 1, 7 | "name": "Leanne Graham", 8 | "username": "Bret", 9 | "email": "Sincere@april.biz", 10 | "address": { 11 | "street": "Kulas Light", 12 | "suite": "Apt. 556", 13 | "city": "Gwenborough", 14 | "zipcode": "92998-3874", 15 | "geo": { 16 | "lat": "-37.3159", 17 | "lng": "81.1496" 18 | } 19 | }, 20 | "phone": "1-770-736-8031 x56442", 21 | "website": "hildegard.org", 22 | "company": { 23 | "name": "Romaguera-Crona", 24 | "catchPhrase": "Multi-layered client-server neural-net", 25 | "bs": "harness real-time e-markets" 26 | } 27 | }, 28 | { 29 | "id": 2, 30 | "name": "Ervin Howell", 31 | "username": "Antonette", 32 | "email": "Shanna@melissa.tv", 33 | "address": { 34 | "street": "Victor Plains", 35 | "suite": "Suite 879", 36 | "city": "Wisokyburgh", 37 | "zipcode": "90566-7771", 38 | "geo": { 39 | "lat": "-43.9509", 40 | "lng": "-34.4618" 41 | } 42 | }, 43 | "phone": "010-692-6593 x09125", 44 | "website": "anastasia.net", 45 | "company": { 46 | "name": "Deckow-Crist", 47 | "catchPhrase": "Proactive didactic contingency", 48 | "bs": "synergize scalable supply-chains" 49 | } 50 | }, 51 | { 52 | "id": 3, 53 | "name": "Clementine Bauch", 54 | "username": "Samantha", 55 | "email": "Nathan@yesenia.net", 56 | "address": { 57 | "street": "Douglas Extension", 58 | "suite": "Suite 847", 59 | "city": "McKenziehaven", 60 | "zipcode": "59590-4157", 61 | "geo": { 62 | "lat": "-68.6102", 63 | "lng": "-47.0653" 64 | } 65 | }, 66 | "phone": "1-463-123-4447", 67 | "website": "ramiro.info", 68 | "company": { 69 | "name": "Romaguera-Jacobson", 70 | "catchPhrase": "Face to face bifurcated interface", 71 | "bs": "e-enable strategic applications" 72 | } 73 | }, 74 | { 75 | "id": 4, 76 | "name": "Patricia Lebsack", 77 | "username": "Karianne", 78 | "email": "Julianne.OConner@kory.org", 79 | "address": { 80 | "street": "Hoeger Mall", 81 | "suite": "Apt. 692", 82 | "city": "South Elvis", 83 | "zipcode": "53919-4257", 84 | "geo": { 85 | "lat": "29.4572", 86 | "lng": "-164.2990" 87 | } 88 | }, 89 | "phone": "493-170-9623 x156", 90 | "website": "kale.biz", 91 | "company": { 92 | "name": "Robel-Corkery", 93 | "catchPhrase": "Multi-tiered zero tolerance productivity", 94 | "bs": "transition cutting-edge web services" 95 | } 96 | }, 97 | { 98 | "id": 5, 99 | "name": "Chelsey Dietrich", 100 | "username": "Kamren", 101 | "email": "Lucio_Hettinger@annie.ca", 102 | "address": { 103 | "street": "Skiles Walks", 104 | "suite": "Suite 351", 105 | "city": "Roscoeview", 106 | "zipcode": "33263", 107 | "geo": { 108 | "lat": "-31.8129", 109 | "lng": "62.5342" 110 | } 111 | }, 112 | "phone": "(254)954-1289", 113 | "website": "demarco.info", 114 | "company": { 115 | "name": "Keebler LLC", 116 | "catchPhrase": "User-centric fault-tolerant solution", 117 | "bs": "revolutionize end-to-end systems" 118 | } 119 | }, 120 | { 121 | "id": 6, 122 | "name": "Mrs. Dennis Schulist", 123 | "username": "Leopoldo_Corkery", 124 | "email": "Karley_Dach@jasper.info", 125 | "address": { 126 | "street": "Norberto Crossing", 127 | "suite": "Apt. 950", 128 | "city": "South Christy", 129 | "zipcode": "23505-1337", 130 | "geo": { 131 | "lat": "-71.4197", 132 | "lng": "71.7478" 133 | } 134 | }, 135 | "phone": "1-477-935-8478 x6430", 136 | "website": "ola.org", 137 | "company": { 138 | "name": "Considine-Lockman", 139 | "catchPhrase": "Synchronised bottom-line interface", 140 | "bs": "e-enable innovative applications" 141 | } 142 | }, 143 | { 144 | "id": 7, 145 | "name": "Kurtis Weissnat", 146 | "username": "Elwyn.Skiles", 147 | "email": "Telly.Hoeger@billy.biz", 148 | "address": { 149 | "street": "Rex Trail", 150 | "suite": "Suite 280", 151 | "city": "Howemouth", 152 | "zipcode": "58804-1099", 153 | "geo": { 154 | "lat": "24.8918", 155 | "lng": "21.8984" 156 | } 157 | }, 158 | "phone": "210.067.6132", 159 | "website": "elvis.io", 160 | "company": { 161 | "name": "Johns Group", 162 | "catchPhrase": "Configurable multimedia task-force", 163 | "bs": "generate enterprise e-tailers" 164 | } 165 | }, 166 | { 167 | "id": 8, 168 | "name": "Nicholas Runolfsdottir V", 169 | "username": "Maxime_Nienow", 170 | "email": "Sherwood@rosamond.me", 171 | "address": { 172 | "street": "Ellsworth Summit", 173 | "suite": "Suite 729", 174 | "city": "Aliyaview", 175 | "zipcode": "45169", 176 | "geo": { 177 | "lat": "-14.3990", 178 | "lng": "-120.7677" 179 | } 180 | }, 181 | "phone": "586.493.6943 x140", 182 | "website": "jacynthe.com", 183 | "company": { 184 | "name": "Abernathy Group", 185 | "catchPhrase": "Implemented secondary concept", 186 | "bs": "e-enable extensible e-tailers" 187 | } 188 | }, 189 | { 190 | "id": 9, 191 | "name": "Glenna Reichert", 192 | "username": "Delphine", 193 | "email": "Chaim_McDermott@dana.io", 194 | "address": { 195 | "street": "Dayna Park", 196 | "suite": "Suite 449", 197 | "city": "Bartholomebury", 198 | "zipcode": "76495-3109", 199 | "geo": { 200 | "lat": "24.6463", 201 | "lng": "-168.8889" 202 | } 203 | }, 204 | "phone": "(775)976-6794 x41206", 205 | "website": "conrad.com", 206 | "company": { 207 | "name": "Yost and Sons", 208 | "catchPhrase": "Switchable contextually-based project", 209 | "bs": "aggregate real-time technologies" 210 | } 211 | }, 212 | { 213 | "id": 10, 214 | "name": "Clementina DuBuque", 215 | "username": "Moriah.Stanton", 216 | "email": "Rey.Padberg@karina.biz", 217 | "address": { 218 | "street": "Kattie Turnpike", 219 | "suite": "Suite 198", 220 | "city": "Lebsackbury", 221 | "zipcode": "31428-2261", 222 | "geo": { 223 | "lat": "-38.2386", 224 | "lng": "57.2232" 225 | } 226 | }, 227 | "phone": "024-648-3804", 228 | "website": "ambrose.net", 229 | "company": { 230 | "name": "Hoeger LLC", 231 | "catchPhrase": "Centralized empowering task-force", 232 | "bs": "target end-to-end models" 233 | } 234 | } 235 | ] 236 | } --------------------------------------------------------------------------------