├── .babelrc ├── .gitignore ├── .gitmodules ├── README.md ├── asserts ├── antd-custom.less └── styles.less ├── build ├── build-manifest.json ├── react-loadable-manifest.json └── server │ ├── pages-manifest.json │ └── ssr-module-cache.js ├── components ├── Ad │ └── index.js ├── ArticleTitle │ ├── index.js │ └── index.less ├── Comments │ ├── index.js │ ├── index.less │ ├── mockData.js │ └── until.js ├── Edit │ └── index.js ├── EditArticle │ ├── index.js │ └── index.less ├── Footer │ └── index.js ├── FormComment │ ├── constants.js │ └── index.js ├── Gallery │ ├── ControllerUnit.js │ ├── ImgFigure.js │ ├── index.js │ ├── index.less │ └── until.js ├── Guitar │ ├── index.js │ └── index.less ├── Header │ ├── index.js │ └── index.less ├── ListTitle │ ├── README.md │ ├── index.js │ └── index.less ├── MyCard │ ├── index.js │ └── index.less ├── MyHead │ └── index.js ├── MyLayout │ └── index.js ├── PrevNextPage │ └── index.js ├── TopTips │ ├── README.md │ ├── index.js │ └── index.less └── loading.js ├── config ├── constantTag.js ├── constantsData.js ├── env.js ├── githubApi.js ├── index.js ├── markdown.js ├── qiniu_config.js └── qiniuyun_cdn.js ├── lib └── with-redux-store.js ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _error.js ├── about │ ├── index.js │ └── index.less ├── admin │ ├── index.js │ ├── req-action.js │ ├── setTable.js │ └── table-data.js ├── adminDetail │ └── index.js ├── blog │ ├── README.md │ ├── index.js │ ├── index.less │ └── searchType.js ├── detail │ ├── index.js │ ├── index.less │ ├── markdown-navbar.less │ ├── pop-tips.less │ ├── util.js │ └── viewer.min.less ├── gallery │ ├── index.js │ └── index.less ├── index.less ├── index │ ├── index.js │ └── index.less ├── life │ └── index.js ├── markdown.less ├── postArticle │ └── index.js └── versions │ ├── index.js │ ├── index.less │ └── mockData.js ├── server.js ├── static ├── 420style.jpg ├── ajax.png ├── all.png ├── blogimg │ ├── 677e4af2jw1f51htgf9ofj20qe0s0tj1.jpg │ ├── IMG_20161119_160013.jpg │ ├── IMG_20170604_205917.jpg │ ├── life1.png │ ├── life2.png │ ├── life3.png │ ├── lkasdfghjk.jpg │ ├── loader.gif │ ├── time.png │ ├── time2.png │ ├── time3.png │ └── time4.png ├── c_1.jpg ├── c_2.jpg ├── copy.png ├── css.png ├── css │ ├── iconfont │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ └── nprogress.css ├── cv │ └── liuweibo_FrontEnd_CV.doc ├── favicon.ico ├── fight.png ├── guitar.png ├── h5.png ├── home │ ├── bg.png │ └── borders1.png ├── interesting.png ├── jianli │ ├── 1072586786_compressed.jpg │ ├── 353938127_compressed.jpg │ ├── IMG_20140329_011043_compressed.jpg │ ├── IMG_20140408_094952_compressed.jpg │ ├── IMG_20140702_002912_compressed.jpg │ ├── IMG_20150306_180108_compressed.jpg │ ├── IMG_20150515_093309_compressed.jpg │ ├── IMG_20150716_130022_compressed.jpg │ ├── IMG_20150723_090614_compressed.jpg │ ├── IMG_20160120_203926_compressed.jpg │ ├── IMG_20160309_151645_compressed.jpg │ ├── IMG_20160312_145558_compressed.jpg │ ├── IMG_20160628_182845_compressed.jpg │ ├── IMG_20161004_101037_compressed.jpg │ ├── IMG_20161103_210243_compressed.jpg │ └── mmexport1448350892762_compressed.jpg ├── js.png ├── loader.gif ├── musicControl.png ├── mv.jpg ├── mysql.png ├── node.png ├── php.png ├── robots.txt ├── server.png ├── sitemap.html ├── sitemap.xml ├── ttround.png └── user │ ├── head-1.jpg │ ├── head-2.jpg │ ├── head-3.jpg │ ├── head-4.jpg │ └── header-my.jpg ├── store ├── action-types.js ├── actions.js ├── reducers.js └── store.js └── until ├── cookie.js ├── getiView.js ├── index.js └── upload-file.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | [ 5 | "import", 6 | { 7 | "libraryName": "antd", 8 | "style": "less" 9 | } 10 | ], 11 | [ 12 | "@babel/plugin-proposal-decorators", 13 | { 14 | "legacy": true 15 | } 16 | ] 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | .idea 6 | .next 7 | 8 | # production 9 | /dist 10 | 11 | # misc 12 | .DS_Store 13 | npm-debug.log* 14 | 15 | # umi 16 | .umi 17 | .umi-production 18 | 19 | .vscode 20 | 21 | package-lock.json 22 | .vscode/settings.json 23 | 24 | yarn.lock 25 | package-lock.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lwbBlogBuild"] 2 | path = lwbBlogBuild 3 | url = https://gitee.com/Weibozzz/lwbBlogBuild.git 4 | -------------------------------------------------------------------------------- /asserts/antd-custom.less: -------------------------------------------------------------------------------- 1 | @primary-color: #017E66; 2 | 3 | @layout-header-height: 40px; 4 | @border-radius-base: 0px; 5 | -------------------------------------------------------------------------------- /asserts/styles.less: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.less"; 2 | @import "./antd-custom.less"; 3 | -------------------------------------------------------------------------------- /build/build-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "devFiles": [ 3 | "static/development/dll/dll_e151f72dbdaf690b2a98.js" 4 | ], 5 | "pages": { 6 | "/_app": [ 7 | "static/runtime/webpack.js", 8 | "static/css/styles.chunk.css", 9 | "static/chunks/styles.js", 10 | "static/runtime/main.js" 11 | ], 12 | "/_error": [ 13 | "static/runtime/webpack.js", 14 | "static/runtime/main.js" 15 | ], 16 | "/about": [ 17 | "static/runtime/webpack.js", 18 | "static/css/styles.chunk.css", 19 | "static/chunks/styles.js", 20 | "static/runtime/main.js" 21 | ] 22 | } 23 | } -------------------------------------------------------------------------------- /build/react-loadable-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "./index.less": [ 3 | { 4 | "id": "./components/Header/index.less", 5 | "name": "./components/Header/index.less", 6 | "file": "static/chunks/styles.js", 7 | "publicPath": "static/chunks/styles.js" 8 | }, 9 | { 10 | "id": "./components/Guitar/index.less", 11 | "name": "./components/Guitar/index.less", 12 | "file": "static/chunks/styles.js", 13 | "publicPath": "static/chunks/styles.js" 14 | }, 15 | { 16 | "id": "./pages/index.less", 17 | "name": "./pages/index.less", 18 | "file": "static/chunks/styles.js", 19 | "publicPath": "static/chunks/styles.js" 20 | }, 21 | { 22 | "id": "./components/MyCard/index.less", 23 | "name": "./components/MyCard/index.less", 24 | "file": "static/chunks/styles.js", 25 | "publicPath": "static/chunks/styles.js" 26 | }, 27 | { 28 | "id": "./components/Comments/index.less", 29 | "name": "./components/Comments/index.less", 30 | "file": "static/chunks/styles.js", 31 | "publicPath": "static/chunks/styles.js" 32 | }, 33 | { 34 | "id": "./pages/about/index.less", 35 | "name": "./pages/about/index.less", 36 | "file": "static/chunks/styles.js", 37 | "publicPath": "static/chunks/styles.js" 38 | } 39 | ], 40 | "undefined": [ 41 | { 42 | "id": 2, 43 | "name": null, 44 | "file": "static/chunks/styles.js", 45 | "publicPath": "static/chunks/styles.js" 46 | }, 47 | { 48 | "id": 3, 49 | "name": null, 50 | "file": "static/chunks/styles.js", 51 | "publicPath": "static/chunks/styles.js" 52 | }, 53 | { 54 | "id": 4, 55 | "name": null, 56 | "file": "static/chunks/styles.js", 57 | "publicPath": "static/chunks/styles.js" 58 | }, 59 | { 60 | "id": 5, 61 | "name": null, 62 | "file": "static/chunks/styles.js", 63 | "publicPath": "static/chunks/styles.js" 64 | }, 65 | { 66 | "id": 6, 67 | "name": null, 68 | "file": "static/chunks/styles.js", 69 | "publicPath": "static/chunks/styles.js" 70 | }, 71 | { 72 | "id": 11, 73 | "name": null, 74 | "file": "static/chunks/styles.js", 75 | "publicPath": "static/chunks/styles.js" 76 | }, 77 | { 78 | "id": 12, 79 | "name": null, 80 | "file": "static/chunks/styles.js", 81 | "publicPath": "static/chunks/styles.js" 82 | }, 83 | { 84 | "id": 13, 85 | "name": null, 86 | "file": "static/chunks/styles.js", 87 | "publicPath": "static/chunks/styles.js" 88 | } 89 | ], 90 | "../asserts/styles.less": [ 91 | { 92 | "id": "./asserts/styles.less", 93 | "name": "./asserts/styles.less", 94 | "file": "static/chunks/styles.js", 95 | "publicPath": "static/chunks/styles.js" 96 | } 97 | ], 98 | "./markdown.less": [ 99 | { 100 | "id": "./pages/markdown.less", 101 | "name": "./pages/markdown.less", 102 | "file": "static/chunks/styles.js", 103 | "publicPath": "static/chunks/styles.js" 104 | } 105 | ], 106 | "./noop": [ 107 | { 108 | "id": "./node_modules/_next@7.0.3@next/dist/client/noop.js", 109 | "name": "./node_modules/_next@7.0.3@next/dist/client/noop.js", 110 | "file": "static/chunks/0.js", 111 | "publicPath": "static/chunks/0.js" 112 | } 113 | ], 114 | "../../components/TopTips": [ 115 | { 116 | "id": "./components/TopTips/index.js", 117 | "name": "./components/TopTips/index.js", 118 | "file": "static/chunks/1.js", 119 | "publicPath": "static/chunks/1.js" 120 | } 121 | ], 122 | "../../config/githubApi": [ 123 | { 124 | "id": "./config/githubApi.js", 125 | "name": "./config/githubApi.js", 126 | "file": "static/chunks/1.js", 127 | "publicPath": "static/chunks/1.js" 128 | } 129 | ], 130 | "../_util/getDataOrAriaProps": [ 131 | { 132 | "id": "./node_modules/_antd@3.26.20@antd/lib/_util/getDataOrAriaProps.js", 133 | "name": "./node_modules/_antd@3.26.20@antd/lib/_util/getDataOrAriaProps.js", 134 | "file": "static/chunks/1.js", 135 | "publicPath": "static/chunks/1.js" 136 | } 137 | ], 138 | "../_util/wave": [ 139 | { 140 | "id": "./node_modules/_antd@3.26.20@antd/lib/_util/wave.js", 141 | "name": "./node_modules/_antd@3.26.20@antd/lib/_util/wave.js", 142 | "file": "static/chunks/1.js", 143 | "publicPath": "static/chunks/1.js" 144 | } 145 | ], 146 | "antd/lib/alert": [ 147 | { 148 | "id": "./node_modules/_antd@3.26.20@antd/lib/alert/index.js", 149 | "name": "./node_modules/_antd@3.26.20@antd/lib/alert/index.js", 150 | "file": "static/chunks/1.js", 151 | "publicPath": "static/chunks/1.js" 152 | } 153 | ], 154 | "./button-group": [ 155 | { 156 | "id": "./node_modules/_antd@3.26.20@antd/lib/button/button-group.js", 157 | "name": "./node_modules/_antd@3.26.20@antd/lib/button/button-group.js", 158 | "file": "static/chunks/1.js", 159 | "publicPath": "static/chunks/1.js" 160 | } 161 | ], 162 | "./button": [ 163 | { 164 | "id": "./node_modules/_antd@3.26.20@antd/lib/button/button.js", 165 | "name": "./node_modules/_antd@3.26.20@antd/lib/button/button.js", 166 | "file": "static/chunks/1.js", 167 | "publicPath": "static/chunks/1.js" 168 | } 169 | ], 170 | "antd/lib/button": [ 171 | { 172 | "id": "./node_modules/_antd@3.26.20@antd/lib/button/index.js", 173 | "name": "./node_modules/_antd@3.26.20@antd/lib/button/index.js", 174 | "file": "static/chunks/1.js", 175 | "publicPath": "static/chunks/1.js" 176 | } 177 | ], 178 | "css-animation/lib/Event": [ 179 | { 180 | "id": "./node_modules/_css-animation@1.6.1@css-animation/lib/Event.js", 181 | "name": "./node_modules/_css-animation@1.6.1@css-animation/lib/Event.js", 182 | "file": "static/chunks/1.js", 183 | "publicPath": "static/chunks/1.js" 184 | } 185 | ], 186 | "whatwg-fetch": [ 187 | { 188 | "id": "./node_modules/_whatwg-fetch@2.0.4@whatwg-fetch/fetch.js", 189 | "name": "./node_modules/_whatwg-fetch@2.0.4@whatwg-fetch/fetch.js", 190 | "file": "static/chunks/1.js", 191 | "publicPath": "static/chunks/1.js" 192 | } 193 | ] 194 | } -------------------------------------------------------------------------------- /build/server/pages-manifest.json: -------------------------------------------------------------------------------- 1 | {"/_app":"static/development/pages/_app.js","/_error":"static/development/pages/_error.js","/_document":"static/development/pages/_document.js","/about":"static/development/pages/about.js"} -------------------------------------------------------------------------------- /build/server/ssr-module-cache.js: -------------------------------------------------------------------------------- 1 | 2 | /* This cache is used by webpack for instantiated modules */ 3 | module.exports = {} 4 | -------------------------------------------------------------------------------- /components/Ad/index.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import { List, Avatar, Icon ,Divider,Row,Col,Tooltip} from 'antd'; 4 | import {COLORS_ARR} from '../../config/constantsData'; 5 | import {getRandomMarginTop,getRandomMarginLeft,getRandomTxt} from '../../config/constantTag'; 6 | 7 | class Ad extends Component { 8 | constructor(props) { 9 | super(props); 10 | this.state={ 11 | dataSource:[] 12 | } 13 | } 14 | componentWillMount (){ 15 | const {dataSource} = this.props; 16 | this.setState({ 17 | dataSource 18 | }) 19 | } 20 | componentWillReceiveProps (){ 21 | const {dataSource} = this.props; 22 | this.setState({ 23 | dataSource 24 | }) 25 | } 26 | shouldComponentUpdate ({dataSource}){ 27 | const {dataSource:hasUrlComment} = this.props; 28 | return dataSource.length!==hasUrlComment.length; 29 | } 30 | render(){ 31 | const {dataSource:hasUrlComment} = this.props; 32 | return ( 33 |
34 |
35 | { 36 | hasUrlComment.map((v,index)=>{ 37 | let ran = Math.random()*COLORS_ARR.length | 0; 38 | let color = COLORS_ARR[ran]; 39 | return ( 40 | 41 | 44 | {getRandomTxt(v.user)} 45 | 46 | 47 | ); 48 | }) 49 | } 50 |
51 |
52 | 53 | 想展示你的友情链接? 54 | 55 | 56 |
57 |
58 | ); 59 | } 60 | } 61 | 62 | export default Ad 63 | -------------------------------------------------------------------------------- /components/ArticleTitle/index.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import { List, Avatar, Icon ,Divider,Row,Col} from 'antd'; 4 | import {formatTime,regUrl} from '../../until'; 5 | import {POST_ARTICLE_TYPE_MAPPING} from '../../config/constantsData'; 6 | import {DEFAULT_TAG_ARR,getRandomMarginTop,getRandomMarginLeft,getRandomTxt} from '../../config/constantTag'; 7 | import Ad from '../Ad'; 8 | import './index.less' 9 | 10 | 11 | const ArticleTitle = (...args)=>{ 12 | const {detailArticle={},commentsData=[],htmlStr=''} = args[0]; 13 | const READING_TIME = Math.ceil(htmlStr.length/20677*29); 14 | let hasUrlComment = commentsData.filter(v=>regUrl.test(v.website)).slice(0,10) 15 | if(hasUrlComment.length<10){ 16 | hasUrlComment=[...hasUrlComment,...DEFAULT_TAG_ARR].slice(0,10) 17 | } 18 | let {title,createTime,user,visitor,lastModify,modifyCount,type=['js'],url='',like=0} = detailArticle 19 | if(lastModify===0){ 20 | lastModify=createTime 21 | } 22 | return ( 23 |
24 | 25 | 28 | 29 |

{title}

30 |

31 | { 32 | url===''? 33 | :'' 34 | } 35 | { 36 | type.split(',').map(v=>({POST_ARTICLE_TYPE_MAPPING[v] || v})) 37 | } 38 | 读完大概需要{READING_TIME}分钟 39 | 40 |

41 | 48 | 49 | 50 | 55 | 56 | 57 |
58 | 59 | 66 |
67 | ) 68 | } 69 | const mapStateToProps = state => { 70 | const {getCommentsData} = state; 71 | return {getCommentsData}; 72 | } 73 | export default connect(mapStateToProps)(ArticleTitle) 74 | -------------------------------------------------------------------------------- /components/ArticleTitle/index.less: -------------------------------------------------------------------------------- 1 | .article-title-component{ 2 | padding: 24px; 3 | background-color: #fff; 4 | .tag-wrapper{ 5 | margin: 30px 0 50px 0; 6 | } 7 | .origin-article{ 8 | display: inline-block; 9 | font-size: 13px; 10 | height: 22px; 11 | margin-right: 10px; 12 | line-height: 22px; 13 | padding: 0 9px; 14 | border-radius: 11px; 15 | font-weight: normal; 16 | background-color: rgba(242,174,67,0.25); 17 | color: #F2AE43; 18 | } 19 | } 20 | 21 | 22 | .advertisement-wrapper{ 23 | .content{ 24 | width: 255px; 25 | height: 255px; 26 | background-color: #f5f5f5; 27 | margin-left: 20px; 28 | overflow: hidden; 29 | .link-friends-a{ 30 | display: inline-block; 31 | } 32 | span{ 33 | display: inline-block; 34 | padding: 3px 5px; 35 | margin-top: 3px; 36 | margin-left: 3px; 37 | background-color: #000; 38 | color: #fff; 39 | font-size: 12px; 40 | } 41 | } 42 | .remark{ 43 | text-align: center; 44 | width: 255px; 45 | margin-left: 20px; 46 | .my-link{ 47 | color: #9E9E9E; 48 | font-size: 12px; 49 | &:hover{ 50 | text-decoration: underline; 51 | } 52 | } 53 | } 54 | } 55 | .read-time{ 56 | margin-left: 20px; 57 | font-size: 14px; 58 | color: #999; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /components/Comments/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import { 4 | Layout, Menu, Breadcrumb, Row, Col, BackTop, Card, Form, 5 | Input, Tooltip, Cascader, Select, Checkbox, Button, 6 | AutoComplete, List, Avatar, Icon, Divider, message 7 | } from 'antd'; 8 | 9 | import {COMMENT_IMAGES} from "../../config/constantsData"; 10 | import {default_comment, DEFAULT_TAG_ARR} from "../../config/constantTag"; 11 | import MyCard from '../MyCard'; 12 | import FormComment from '../FormComment'; 13 | import {mockCommentsData} from './mockData'; 14 | import {getMyCommenData} from './until'; 15 | import './index.less' 16 | 17 | 18 | class Comments extends Component { 19 | constructor(props) { 20 | super(props); 21 | this.state = { 22 | autoCompleteResult: [], 23 | articleID: '' 24 | } 25 | } 26 | 27 | 28 | componentWillMount() { 29 | const {blogData = []} = this.props; 30 | let {id: articleID} = blogData[0] || {}; 31 | this.setState({ 32 | articleID 33 | }) 34 | } 35 | 36 | 37 | render() { 38 | const {dataSource = {}, commentIndex = -1} = this.props; 39 | let {commentsData = [], commentTitle = '发表评论', commentRow = 8} = dataSource; 40 | commentsData = [...default_comment, ...getMyCommenData(commentsData)]; 41 | return ( 42 |
43 |

{commentTitle}:

44 | 45 | 50 | { 51 | commentIndex === -1 ? : '' 52 | } 53 | 54 | 55 | 56 | { 57 | commentsData.map((v, i) => { 58 | let index = i % COMMENT_IMAGES.length; 59 | return ( 60 | 61 | 62 | 63 | ) 64 | } 65 | ) 66 | } 67 |
68 | ) 69 | } 70 | } 71 | 72 | const mapStateToProps = state => { 73 | const {commentIndex} = state; 74 | return {commentIndex}; 75 | } 76 | 77 | const WrappedRegistrationForm = Form.create()(Comments); 78 | export default connect(mapStateToProps)(WrappedRegistrationForm); 79 | -------------------------------------------------------------------------------- /components/Comments/index.less: -------------------------------------------------------------------------------- 1 | .css-move-top{ 2 | .ant-card-body{ 3 | position: relative; 4 | .msg-p{ 5 | padding-right: 50px; 6 | word-break: break-all; 7 | } 8 | .icon-comment{ 9 | position: absolute; 10 | top: 50%; 11 | right: -10px; 12 | width: 50px; 13 | height: 50px; 14 | transform: translateY(-50%); 15 | overflow:hidden; 16 | .icon-posi{ 17 | position: relative; 18 | img{ 19 | position: absolute; 20 | left: 50%; 21 | height: 50px; 22 | transform: translateX(-50%); 23 | border-radius: 50%; 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /components/Comments/mockData.js: -------------------------------------------------------------------------------- 1 | export const mockCommentsData = [ 2 | { 3 | "id": 52, 4 | "a_id": 205, 5 | "user": "测试 grade 0", 6 | "remark": "", 7 | "website": null, 8 | "msg": "测试 grade 0", 9 | "createTime": "1533272366", 10 | grade: 0 11 | }, 12 | { 13 | "id": 53, 14 | "a_id": 205, 15 | "user": "测试 grade 1", 16 | "remark": "测试 grade 0", 17 | "website": null, 18 | "msg": "测试 grade 1", 19 | "createTime": '1533272368', 20 | grade: 1 21 | }, 22 | { 23 | "id": 54, 24 | "a_id": 205, 25 | "user": "测试 grade 2", 26 | "remark": "测试 grade 0", 27 | "website": null, 28 | "msg": "测试 grade 2", 29 | "createTime": '1533272369', 30 | grade: 2 31 | }, 32 | { 33 | "id": 55, 34 | "a_id": 205, 35 | "user": "测试1 grade 0", 36 | "remark": "", 37 | "website": null, 38 | "msg": "测试1 grade 0", 39 | "createTime": '1533272379', 40 | grade: 0 41 | }, 42 | ] 43 | export const resultMockCommentsData = { 44 | 0: [ 45 | { 46 | "id": 52, 47 | "a_id": 205, 48 | "user": "测试 grade 0", 49 | "remark": "", 50 | "website": null, 51 | "msg": "测试 grade 0", 52 | "createTime": "1533272366", 53 | grade: 0 54 | }, 55 | { 56 | "id": 54, 57 | "a_id": 205, 58 | "user": "测试1 grade 0", 59 | "remark": "", 60 | "website": null, 61 | "msg": "测试1 grade 0", 62 | "createTime": '1533272379', 63 | grade: 0 64 | }, 65 | ] 66 | } -------------------------------------------------------------------------------- /components/Comments/until.js: -------------------------------------------------------------------------------- 1 | export const getMyCommenData=(COMMENT_MOCK_DATA_ORIGIN)=> { 2 | let mapping = {answerId: []} 3 | let grande_one = COMMENT_MOCK_DATA_ORIGIN.filter(v => { 4 | let {answerId = ''} = v; 5 | if (answerId) { 6 | mapping[answerId] = [] 7 | } 8 | return answerId === '' || answerId==null||answerId===0; 9 | }) 10 | COMMENT_MOCK_DATA_ORIGIN.forEach(v => { 11 | let {answerId = ''} = v; 12 | if (answerId in mapping) { 13 | mapping[answerId].push(v) 14 | } 15 | }) 16 | return grande_one.map(v => { 17 | let {id} = v; 18 | if (id in mapping) { 19 | v.answerArr = mapping[id] 20 | } 21 | return v; 22 | }); 23 | } -------------------------------------------------------------------------------- /components/Edit/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | import {Row, Col,Input} from 'antd'; 5 | 6 | import marked from 'marked' 7 | import hljs from 'highlight.js'; 8 | 9 | import {getHtml, OldTime} from '../../until'; 10 | import {markdownConfig} from "../../config/markdown"; 11 | // import './index.less'; 12 | 13 | const { TextArea } = Input; 14 | 15 | const {options,config} = markdownConfig 16 | hljs.configure(config) 17 | marked.setOptions({ 18 | highlight: (code) => hljs.highlightAuto(code).value, 19 | ...options 20 | }); 21 | 22 | class Edit extends Component { 23 | constructor(props) { 24 | super(props) 25 | this.state = { 26 | previewContent: '', 27 | aceBoxH: null, 28 | originContent: '', 29 | inputValue:'', 30 | } 31 | 32 | this.cacheValue() 33 | this.containerScroll = this.containerScroll.bind(this) 34 | this.onContentChange = this.onContentChange.bind(this) 35 | } 36 | 37 | componentWillMount() { 38 | const {editCont = '', createTime = ''} = this.props; 39 | let decodeOrigin; 40 | try { 41 | decodeOrigin = getHtml(decodeURIComponent(editCont)) 42 | } catch (err) { 43 | decodeOrigin = editCont 44 | } 45 | let markedContent = marked(decodeOrigin); 46 | this.setState({ 47 | previewContent: markedContent, 48 | originContent: decodeOrigin, 49 | }) 50 | } 51 | 52 | componentDidMount() { 53 | this.setState({ 54 | aceBoxH: document.documentElement.clientHeight - document.querySelector('.editor-main-a').offsetHeight + 'px' 55 | }) 56 | } 57 | cacheValue() { 58 | this.currentTabIndex = 1 59 | this.hasContentChanged = false 60 | this.scale = 1 61 | } 62 | 63 | setCurrentIndex(index) { 64 | this.currentTabIndex = index 65 | } 66 | 67 | containerScroll(e) { 68 | this.hasContentChanged && this.setScrollValue() 69 | if (this.currentTabIndex === 1) { 70 | this.previewContainer.scrollTop = this.editContainer.scrollTop * this.scale 71 | } else { 72 | this.editContainer.scrollTop = this.previewContainer.scrollTop / this.scale 73 | } 74 | } 75 | 76 | onContentChange(e) { 77 | let innerText = e.target.innerText; 78 | innerText=innerText.replace(/script>/img,'script\\>') 79 | let markCont = marked(innerText) 80 | const {handleChangeMarkEdit} = this.props; 81 | handleChangeMarkEdit(innerText) 82 | this.setState({ 83 | previewContent: markCont 84 | }) 85 | !this.hasContentChanged && (this.hasContentChanged = true) 86 | } 87 | 88 | onOldArticleContentChange(e) { 89 | const {handleChangeMarkEdit} = this.props; 90 | handleChangeMarkEdit(e.target.innerHTML) 91 | } 92 | 93 | setScrollValue() { 94 | // 设置值,方便 scrollBy 操作 95 | this.scale = (this.previewWrap.offsetHeight - this.previewContainer.offsetHeight) / (this.editWrap.offsetHeight - this.editContainer.offsetHeight) 96 | this.hasContentChanged = false 97 | } 98 | onPaste(e){ 99 | 100 | } 101 | 102 | render() { 103 | 104 | let {aceBoxH, previewContent, originContent} = this.state; 105 | const {createTime = '',id=''} = this.props; 106 | 107 | return ( 108 |
109 | 110 |
112 | 113 | { 114 | createTime > OldTime || createTime === '' || id===1? 115 |
116 | 117 |
118 |
this.editContainer = node}> 120 |
this.editWrap = node}> 124 | {originContent} 125 |
126 |
127 |
128 | 129 | 130 |
131 |
this.previewContainer = node} 133 | onMouseOver={this.setCurrentIndex.bind(this, 2)} onScroll={this.containerScroll}> 134 |
this.previewWrap = node} 135 | dangerouslySetInnerHTML={{__html: previewContent}}>
136 |
137 |
138 | 139 |
140 | : 141 |
142 | 143 |
144 |
148 |
149 | 150 |
151 | } 152 |
153 | 154 |
155 | 170 |
171 | 172 | ); 173 | } 174 | 175 | } 176 | 177 | export default connect()(Edit); 178 | -------------------------------------------------------------------------------- /components/EditArticle/index.less: -------------------------------------------------------------------------------- 1 | .save-status{ 2 | margin-left: 15px; 3 | font-size: 14px; 4 | color: #888; 5 | } -------------------------------------------------------------------------------- /components/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import Link from 'next/link'; 3 | import { Layout,BackTop} from 'antd'; 4 | 5 | import {MIIT_BEIAN,MY_BLOG,LINK_ABOUT_ME} from '../../config/constantsData'; 6 | const {Footer } = Layout; 7 | 8 | class blogFooter extends Component { 9 | constructor() { 10 | super(); 11 | 12 | } 13 | render (){ 14 | return ( 15 | 38 | ); 39 | } 40 | } 41 | 42 | export default blogFooter; 43 | -------------------------------------------------------------------------------- /components/FormComment/constants.js: -------------------------------------------------------------------------------- 1 | export const formItemLayout = { 2 | labelCol: { 3 | xs: {span: 24}, 4 | sm: {span: 24}, 5 | lg: {span: 8}, 6 | }, 7 | wrapperCol: { 8 | xs: {span: 24}, 9 | sm: {span: 24}, 10 | lg: {span: 16}, 11 | }, 12 | }; 13 | export const tailFormItemLayout = { 14 | wrapperCol: { 15 | xs: { 16 | span: 24, 17 | offset: 0, 18 | }, 19 | sm: { 20 | span: 24, 21 | offset: 0, 22 | }, 23 | lg: { 24 | span: 16, 25 | offset: 8, 26 | }, 27 | }, 28 | }; 29 | export const tailCommentsTip = { 30 | xs: { 31 | span: 24, 32 | offset: 0, 33 | }, 34 | sm: { 35 | span: 24, 36 | offset: 0, 37 | }, 38 | lg: { 39 | span: 16, 40 | offset: 8, 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /components/FormComment/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import { 4 | Layout, Menu, Breadcrumb, Row, Col, BackTop, Card, Form, 5 | Input, Tooltip, Cascader, Select, Checkbox, Button, Spin, 6 | AutoComplete, List, Avatar, Icon, Divider, message, Tag, Alert 7 | } from 'antd'; 8 | 9 | import Loading from '../loading' 10 | import {regUrl, real_ip} from "../../until"; 11 | import {getCookie, setCookie} from "../../until/cookie"; 12 | import {postComments, postUserComments, setAnswerId, setCommentIndex} from "../../store/actions"; 13 | import {postCommentUrl, postUserCommentUrl} from "../../config"; 14 | import {COMMENT_TIPS, COMMENT_LIMIT, commentPlaceHolder, BILILI_ADDRESS} from "../../config/constantsData"; 15 | import {DEFAULT_TAG_ARR} from "../../config/constantTag"; 16 | import {tailFormItemLayout, tailCommentsTip, formItemLayout} from './constants'; 17 | 18 | const FormItem = Form.Item; 19 | const {TextArea} = Input; 20 | const AutoCompleteOption = AutoComplete.Option; 21 | 22 | class FormComment extends Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | isLoading: false, 27 | nickname: '', 28 | email: '', 29 | website: '', 30 | autoCompleteResult: [], 31 | } 32 | } 33 | 34 | handleWebsiteChange = (value) => { 35 | let autoCompleteResult; 36 | if (!value) { 37 | autoCompleteResult = []; 38 | } else { 39 | autoCompleteResult = ['.com', '.cn', '.org', '.net'].map(domain => `${value}${domain}`); 40 | } 41 | this.setState({autoCompleteResult}); 42 | } 43 | // 存储 cookie 44 | handleSetCookie(nickname,email,website){ 45 | setCookie('nickname', nickname, 180) 46 | setCookie('email', email, 180) 47 | setCookie('website', website, 180) 48 | } 49 | // 获取 cookie 50 | handleGetCookie(){ 51 | return { 52 | nickname:getCookie('nickname') || '', 53 | email: getCookie('email') || '', 54 | website: getCookie('website') || '' 55 | }; 56 | } 57 | componentDidMount(){ 58 | this.setState(this.handleGetCookie()) 59 | } 60 | handleSubmit = (e) => { 61 | e.preventDefault(); 62 | const {dispatch, dataSource = {}, answerId} = this.props; 63 | const { password } = sessionStorage; 64 | const {commentsData: commentsDataOrigin = [], articleID: id, isUserSubmit = false} = dataSource; 65 | if (!id && !isUserSubmit) { 66 | return; 67 | } 68 | this.props.form.validateFieldsAndScroll(async (err, values) => { 69 | if (!err) { 70 | const {comment = '', email = '', nickname = '', website = ''} = values; 71 | if (website !== '' && !regUrl.test(website)) { 72 | message.warning('url不正确,示例:"http://www.xxx.com"') 73 | return; 74 | } 75 | if (nickname.length < 2 || comment.length < 2) { 76 | message.warning('用户名或者评论内容过少') 77 | return; 78 | } 79 | if (comment.length > COMMENT_LIMIT.value) { 80 | message.warning(COMMENT_LIMIT.key) 81 | return; 82 | } 83 | 84 | this.setState({ 85 | isLoading: true 86 | }) 87 | let realIp = '' 88 | try { 89 | realIp = await real_ip() 90 | } catch (err) { 91 | } 92 | let queryParamsObj = {}; 93 | try { 94 | queryParamsObj = {real_ip: realIp, ip: returnCitySN['cip'], address: returnCitySN['cname']}; 95 | } catch (err) { 96 | } 97 | const isExist = commentsDataOrigin.findIndex(v => { 98 | const {user, name} = v; 99 | return ((user || name) === nickname) && ((user || name) !== '刘伟波'); 100 | }) !== -1; 101 | if (isExist && !answerId) { 102 | message.warning('用户名已存在') 103 | return; 104 | } 105 | const queryStringComment = { 106 | id, 107 | comment: comment.trim(), 108 | email: email.trim(), 109 | nickname: nickname.trim(), 110 | website: website.trim(), 111 | name: nickname.trim(), 112 | token: password, 113 | answerId, 114 | ...queryParamsObj 115 | } 116 | //如果是详情页提交它,如果是关于我,则不用关心id 117 | isUserSubmit ? 118 | postUserComments(dispatch, postUserCommentUrl(), queryStringComment).then(res => { 119 | const {getUserCommentsData} = res; 120 | if (getUserCommentsData.length) { 121 | this.handleSetCookie(nickname,email,website) 122 | message.success(`留言发表成功,感谢支持!`); 123 | }else { 124 | message.warning('您可能没权限'); 125 | } 126 | this.setState({ 127 | isLoading: false 128 | }) 129 | }) 130 | : 131 | postComments(dispatch, postCommentUrl(), queryStringComment).then(res => { 132 | if ((res.getCommentsData.length)) { 133 | this.handleSetCookie(nickname,email,website) 134 | message.success(`评论发表成功,感谢支持!`); 135 | }else { 136 | message.warning('您可能没权限'); 137 | } 138 | this.setState({ 139 | isLoading: false 140 | }) 141 | }) 142 | 143 | setCommentIndex(dispatch, -1) 144 | setAnswerId(dispatch, '') 145 | } 146 | }); 147 | } 148 | 149 | render() { 150 | const {dataSource = {}} = this.props; 151 | const {commentBtnMsg = '提交评论'} = dataSource; 152 | const {nickname,email,website: commentWebsite} = this.state 153 | const {autoCompleteResult, isLoading} = this.state; 154 | const websiteOptions = autoCompleteResult.map(website => ( 155 | {website} 156 | )); 157 | const {getFieldDecorator} = this.props.form; 158 | const sf = DEFAULT_TAG_ARR.find(v => v.user === 'segmentfault'); 159 | const {user, website} = sf; 160 | const tipsRender = 161 | {COMMENT_TIPS()} 162 | ; 163 | return
164 | { 165 | isLoading && 166 | } 167 | 168 | 169 | 170 | 171 | 172 |
173 | 177 | Nickname  178 | 179 | 180 | 181 | 182 | )} 183 | > 184 | {getFieldDecorator('nickname', { 185 | initialValue: nickname, 186 | rules: [{ 187 | required: true, 188 | max: 15, 189 | whitespace: true 190 | }], 191 | })( 192 | 195 | )} 196 | 197 | 201 | {getFieldDecorator('email', { 202 | initialValue: email, 203 | rules: [{ 204 | type: 'email', message: 'The input is not valid E-mail!', 205 | }, { 206 | required: false, message: 'Please input your E-mail!', 207 | }], 208 | })( 209 | 212 | )} 213 | 214 | 218 | {getFieldDecorator('website', { 219 | initialValue: commentWebsite, 220 | rules: [{required: false, message: 'Please input website!'}], 221 | })( 222 | 227 | 229 | 230 | )} 231 | 232 | 236 | {getFieldDecorator('comment', { 237 | rules: [{ 238 | required: true, message: 'Please input your comment!', 239 | }], 240 | })( 241 |