├── .gitignore ├── LICENSE ├── README.md ├── README_Img ├── create.png ├── create_sm.jpg ├── index.jpg ├── index_sm.jpg ├── list.png ├── list_sm.jpg ├── login.png ├── login_sm.jpg ├── register.png └── register_sm.jpg ├── WebStorm_run_nei.js ├── mock.data ├── interface │ ├── delete │ │ └── api │ │ │ └── works │ │ │ └── _ │ │ │ └── id │ │ │ ├── data.js │ │ │ └── data.json │ ├── get │ │ ├── api │ │ │ ├── tags │ │ │ │ └── _ │ │ │ │ │ └── recommend │ │ │ │ │ ├── data.js │ │ │ │ │ └── data.json │ │ │ ├── users │ │ │ │ └── _ │ │ │ │ │ ├── getloginuser │ │ │ │ │ ├── data.js │ │ │ │ │ └── data.json │ │ │ │ │ └── getstarlist │ │ │ │ │ ├── data.js │ │ │ │ │ └── data.json │ │ │ └── works │ │ │ │ ├── _ │ │ │ │ └── id │ │ │ │ │ ├── data.js │ │ │ │ │ └── data.json │ │ │ │ ├── data.js │ │ │ │ └── data.json │ │ ├── captcha │ │ │ ├── data.js │ │ │ └── data.json │ │ └── uploads │ │ │ └── _ │ │ │ └── filename │ │ │ ├── data.js │ │ │ └── data.json │ ├── patch │ │ └── api │ │ │ └── works │ │ │ └── _ │ │ │ └── id │ │ │ ├── data.js │ │ │ └── data.json │ └── post │ │ └── api │ │ ├── login │ │ ├── data.js │ │ └── data.json │ │ ├── logout │ │ ├── data.js │ │ └── data.json │ │ ├── register │ │ ├── data.js │ │ └── data.json │ │ ├── users │ │ └── _ │ │ │ ├── follow │ │ │ ├── data.js │ │ │ └── data.json │ │ │ └── unfollow │ │ │ ├── data.js │ │ │ └── data.json │ │ └── works │ │ ├── _ │ │ └── upload │ │ │ ├── data.js │ │ │ └── data.json │ │ ├── data.js │ │ └── data.json └── template │ ├── index.js │ ├── index.json │ ├── search.js │ ├── search.json │ └── works │ ├── create.js │ ├── create.json │ ├── detail.js │ ├── detail.json │ ├── list.js │ └── list.json ├── nei.15044.e389c52125abdd607c4455e4d448e5d3 ├── json │ ├── nei-2017-09-12-17-38-34-763.json │ ├── nei-2017-10-12-13-23-21-578-diff.txt │ ├── nei-2017-10-12-13-23-21-578.json │ └── nei-latest.json ├── nei.json └── server.config.js ├── package.json └── public ├── common ├── base.css ├── data │ └── address_codes.js ├── font │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.svg │ ├── iconfont.ttf │ └── iconfont.woff └── js │ ├── emitter.js │ ├── template.js │ ├── util.js │ └── validator.js ├── component ├── AlertModal │ ├── AlertModal.css │ └── AlertModal.js ├── Aside │ ├── Aside.css │ └── Aside.js ├── CascadeSelect │ ├── CascadeSelect.css │ └── CascadeSelect.js ├── ClassificWorks │ ├── ClassificWorks.css │ └── ClassificWorks.js ├── ConfirmModal │ ├── ConfirmModal.css │ └── ConfirmModal.js ├── Guest │ ├── Guest.css │ └── Guest.js ├── LoginModal │ ├── LoginModal.css │ └── LoginModal.js ├── Modal │ ├── Modal.css │ └── Modal.js ├── Nav │ ├── Nav.css │ └── Nav.js ├── Pagination │ ├── Pagination.css │ └── Pagination.js ├── Profile │ ├── Profile.css │ └── Profile.js ├── Radio │ ├── Radio.css │ └── Radio.js ├── RegisterModal │ ├── RegisterModal.css │ └── RegisterModal.js ├── Search │ ├── Search.css │ └── Search.js ├── Section │ ├── Section.css │ └── Section.js ├── Select │ ├── Select.css │ └── Select.js ├── Slider │ ├── Slider.css │ └── Slider.js ├── StarList │ ├── StarList.css │ └── StarList.js ├── Tabs │ ├── Tabs.css │ └── Tabs.js ├── Tag │ ├── Tag.css │ └── Tag.js ├── UploadPictures │ ├── UploadPictures.css │ └── UploadPictures.js ├── User │ ├── User.css │ └── User.js ├── WorkDescription │ ├── WorkDescription.css │ └── WorkDescription.js ├── WorkPermission │ ├── WorkPermission.css │ └── WorkPermission.js └── WorksList │ ├── WorksList.css │ └── WorksList.js ├── html ├── index.html ├── search.html └── works │ ├── create.html │ ├── detail.html │ └── list.html ├── lib ├── handlebars-v4.0.10.js └── md5.js ├── pages ├── index.css ├── index.js ├── search.css ├── search.js └── works │ ├── create.css │ ├── create.js │ ├── detail.css │ ├── detail.js │ ├── list.css │ └── list.js └── res └── images ├── Slider ├── banner0.jpg ├── banner1.jpg ├── banner2.jpg └── banner3.jpg ├── activity1.png ├── activity2.png ├── avatar.png ├── avatar0.jpg ├── avatar1.jpg ├── avatar2.jpg ├── avatar3.jpg ├── avatar4.jpg ├── avatar5.jpg ├── banner4.jpg ├── circle1.jpg ├── circle2.jpg ├── circle3.jpg ├── iconfont ├── u-icon-pencle.png ├── u-icon-people.png └── u-icon-work.png ├── loading.gif ├── logo.png ├── my_work.png ├── page-active.png ├── work1.jpg ├── work10.jpg ├── work2.jpg ├── work3.jpg ├── work4.jpg ├── work5.jpg ├── work6.jpg ├── work7.jpg ├── work8.jpg └── work9.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Yibay 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 |

网易-Mini项目实战:Ego社区

2 |

项目介绍

3 |

网站类型:二次元社区

4 | 9 |

终端命令

10 | 14 |

WebStorm Run/Debug配置

15 |

如要使用 WebStorm Run/Debug 来运行项目,配置如下:

16 | 24 |

页面地址

25 | 30 |

线上Demo地址

31 |

因为线上Demo 是无后台的mock数据,所以只支持部分项目功能。完整版,还需在本地环境,通过nei服务器体验

32 | 37 |

项目展示

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
首页登录框注册框作品页编辑页
-------------------------------------------------------------------------------- /README_Img/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/create.png -------------------------------------------------------------------------------- /README_Img/create_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/create_sm.jpg -------------------------------------------------------------------------------- /README_Img/index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/index.jpg -------------------------------------------------------------------------------- /README_Img/index_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/index_sm.jpg -------------------------------------------------------------------------------- /README_Img/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/list.png -------------------------------------------------------------------------------- /README_Img/list_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/list_sm.jpg -------------------------------------------------------------------------------- /README_Img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/login.png -------------------------------------------------------------------------------- /README_Img/login_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/login_sm.jpg -------------------------------------------------------------------------------- /README_Img/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/register.png -------------------------------------------------------------------------------- /README_Img/register_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/README_Img/register_sm.jpg -------------------------------------------------------------------------------- /WebStorm_run_nei.js: -------------------------------------------------------------------------------- 1 | var process = require('child_process'); 2 | process.exec('nei server', function(error, stdout, stderr){ 3 | if(error !== null){ 4 | console.log('exec error: ' + error); 5 | } 6 | }); -------------------------------------------------------------------------------- /mock.data/interface/delete/api/works/_/id/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/delete/api/works/_/id/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "dZoTYrIEaP", 4 | "result": { 5 | "id": 82743, 6 | "name": "QEWUug4v0o", 7 | "tag": "少女系,萌神,萝莉,未来日记", 8 | "coverId": 82894, 9 | "coverUrl": "http://via.placeholder.com/102x158", 10 | "pictures": [ 11 | { 12 | "id": 83468, 13 | "name": "TGoxnFjtIS", 14 | "url": "http://via.placeholder.com/100x140", 15 | "position": 67422, 16 | "worksId": 83762, 17 | "creatorId": 83895, 18 | "createTime": 1505208252619, 19 | "updateTime": 1505206746965 20 | }, 21 | { 22 | "id": 83945, 23 | "name": "09YHtPQajP", 24 | "url": "http://via.placeholder.com/100x140", 25 | "position": 29184, 26 | "worksId": 84280, 27 | "creatorId": 84593, 28 | "createTime": 1505207141733, 29 | "updateTime": 1505208221655 30 | }, 31 | { 32 | "id": 84883, 33 | "name": "CqjOCoBt3Z", 34 | "url": "http://via.placeholder.com/100x140", 35 | "position": 89525, 36 | "worksId": 85417, 37 | "creatorId": 86376, 38 | "createTime": 1505208325844, 39 | "updateTime": 1505208554448 40 | } 41 | ], 42 | "category": 1, 43 | "description": "aC2MS0iXzW", 44 | "privilege": 2, 45 | "authorization": 0, 46 | "createTime": 1505208606658, 47 | "updateTime": 1505207818309 48 | } 49 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/tags/_/recommend/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/tags/_/recommend/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "AbDUtG0Ols", 4 | "result": "少女系,萌神,萝莉" 5 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/users/_/getloginuser/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/users/_/getloginuser/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "w3F1ehclM7", 4 | "result": { 5 | "id": 84198, 6 | "username": "9CxJSqsdd4", 7 | "nickname": "hAwn2xZnjf", 8 | "sex": 1, 9 | "province": 52232, 10 | "city": 330200, 11 | "district": 330000, 12 | "birthday": "2003-12-03", 13 | "createTime": 1505208114789, 14 | "updateTime": 1505208680016 15 | } 16 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/users/_/getstarlist/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/users/_/getstarlist/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "qsABksCXLo", 4 | "result": [ 5 | { 6 | "id": 72592, 7 | "nickname": "cqh2TOFlDF", 8 | "isFollow": true, 9 | "workCount": 75929, 10 | "followCount": 41268 11 | }, 12 | { 13 | "id": 72744, 14 | "nickname": "xIHIjM1eRD", 15 | "isFollow": false, 16 | "workCount": 16878, 17 | "followCount": 65757 18 | }, 19 | { 20 | "id": 73101, 21 | "nickname": "2Y9hCd1xbJ", 22 | "isFollow": false, 23 | "workCount": 95399, 24 | "followCount": 97303 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/works/_/id/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/works/_/id/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "73KxFKM9bi", 4 | "result": { 5 | "id": 91299, 6 | "name": "wyGqmCph4z", 7 | "tag": "少女系,萌神,萝莉,未来日记", 8 | "coverId": 92019, 9 | "coverUrl": "http://via.placeholder.com/102x158", 10 | "pictures": [ 11 | { 12 | "id": 92329, 13 | "name": "nRQLYr56jR", 14 | "url": "http://via.placeholder.com/100x140", 15 | "position": 80843, 16 | "worksId": 92389, 17 | "creatorId": 92426, 18 | "createTime": 1505207705457, 19 | "updateTime": 1505208377040 20 | }, 21 | { 22 | "id": 92482, 23 | "name": "EH5wQPg5fM", 24 | "url": "http://via.placeholder.com/100x140", 25 | "position": 61414, 26 | "worksId": 92723, 27 | "creatorId": 93120, 28 | "createTime": 1505207088712, 29 | "updateTime": 1505207834556 30 | }, 31 | { 32 | "id": 93822, 33 | "name": "aXjqH5SOMy", 34 | "url": "http://via.placeholder.com/100x140", 35 | "position": 57239, 36 | "worksId": 94283, 37 | "creatorId": 94672, 38 | "createTime": 1505207743601, 39 | "updateTime": 1505206824250 40 | } 41 | ], 42 | "category": 1, 43 | "description": "zcY4ydIQug", 44 | "privilege": 0, 45 | "authorization": 1, 46 | "createTime": 1505208730225, 47 | "updateTime": 1505206653712 48 | } 49 | } -------------------------------------------------------------------------------- /mock.data/interface/get/api/works/data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 模拟分页逻辑 3 | */ 4 | module.exports = function (json, req) { 5 | // 请求参数 6 | let query = req.query; 7 | // 返回的 json结果 8 | let result = json.result; 9 | 10 | if(query.total === '1'){ 11 | result.total = result.data.length 12 | } 13 | else{ 14 | delete result.total; 15 | } 16 | 17 | // 过滤逻辑 18 | result.data = result.data.splice(query.offset, query.limit || 10); 19 | 20 | return json; 21 | } -------------------------------------------------------------------------------- /mock.data/interface/get/captcha/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/captcha/data.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /mock.data/interface/get/uploads/_/filename/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/get/uploads/_/filename/data.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /mock.data/interface/patch/api/works/_/id/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/patch/api/works/_/id/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "NXMJKR7wHs", 4 | "result": { 5 | "id": 66427, 6 | "name": "0pPSO9PkWU", 7 | "tag": "少女系,萌神,萝莉,未来日记", 8 | "coverId": 66932, 9 | "coverUrl": "http://via.placeholder.com/102x158", 10 | "pictures": [ 11 | { 12 | "id": 67056, 13 | "name": "SPYpsZGOJy", 14 | "url": "http://via.placeholder.com/100x140", 15 | "position": 36996, 16 | "worksId": 67909, 17 | "creatorId": 68370, 18 | "createTime": 1505209091440, 19 | "updateTime": 1505207369926 20 | }, 21 | { 22 | "id": 68611, 23 | "name": "7cghZwMRzZ", 24 | "url": "http://via.placeholder.com/100x140", 25 | "position": 28972, 26 | "worksId": 69021, 27 | "creatorId": 69782, 28 | "createTime": 1505208726557, 29 | "updateTime": 1505208501669 30 | }, 31 | { 32 | "id": 70610, 33 | "name": "YBlocYbVYn", 34 | "url": "http://via.placeholder.com/100x140", 35 | "position": 97681, 36 | "worksId": 71341, 37 | "creatorId": 71820, 38 | "createTime": 1505206633919, 39 | "updateTime": 1505207885296 40 | } 41 | ], 42 | "category": 0, 43 | "description": "xA2Uh1eoba", 44 | "privilege": 2, 45 | "authorization": 0, 46 | "createTime": 1505207387673, 47 | "updateTime": 1505207434259 48 | } 49 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/login/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/login/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "YJxCof6Ga4", 4 | "result": { 5 | "id": 58401, 6 | "username": "kofNNZBEMF", 7 | "nickname": "ZlEF95XF3f", 8 | "sex": 0, 9 | "province": 65505, 10 | "city": 330200, 11 | "district": 330000, 12 | "birthday": "2003-12-03", 13 | "createTime": 1505207879814, 14 | "updateTime": 1505208950246 15 | } 16 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/logout/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/logout/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "EzWivYOnNo", 4 | "result": false 5 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/register/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/register/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "a5J788EhRu", 4 | "result": { 5 | "id": 73085, 6 | "username": "UilCq76i7E", 7 | "nickname": "OLZcB1KZZ0", 8 | "sex": 1, 9 | "province": 30421, 10 | "city": 330200, 11 | "district": 330000, 12 | "birthday": "2003-12-03", 13 | "createTime": 1505207701832, 14 | "updateTime": 1505208445178 15 | } 16 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/users/_/follow/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/users/_/follow/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "jwsT44bhYP", 4 | "result": true 5 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/users/_/unfollow/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/users/_/unfollow/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "4Byd5lEhOa", 4 | "result": false 5 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/works/_/upload/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/works/_/upload/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "zAZ3hRkhfs", 4 | "result": { 5 | "id": 78535, 6 | "name": "P6jytkR9NX", 7 | "url": "http://via.placeholder.com/100x140", 8 | "position": 95525, 9 | "worksId": 79098, 10 | "creatorId": 79796, 11 | "createTime": 1505207656021, 12 | "updateTime": 1505208988009 13 | } 14 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/works/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/interface/post/api/works/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "bl17XP8Q81", 4 | "result": { 5 | "id": 99743, 6 | "name": "nZePKg6uEz", 7 | "tag": "少女系,萌神,萝莉,未来日记", 8 | "coverId": 100628, 9 | "coverUrl": "http://via.placeholder.com/102x158", 10 | "pictures": [ 11 | { 12 | "id": 100949, 13 | "name": "9kIjbjkQ24", 14 | "url": "http://via.placeholder.com/100x140", 15 | "position": 17631, 16 | "worksId": 101402, 17 | "creatorId": 102116, 18 | "createTime": 1505208953154, 19 | "updateTime": 1505207227582 20 | }, 21 | { 22 | "id": 102794, 23 | "name": "u1tWkmyK0x", 24 | "url": "http://via.placeholder.com/100x140", 25 | "position": 32246, 26 | "worksId": 103294, 27 | "creatorId": 103575, 28 | "createTime": 1505208326647, 29 | "updateTime": 1505209088354 30 | }, 31 | { 32 | "id": 103580, 33 | "name": "lPyCRq8AAn", 34 | "url": "http://via.placeholder.com/100x140", 35 | "position": 49648, 36 | "worksId": 104062, 37 | "creatorId": 104888, 38 | "createTime": 1505208577032, 39 | "updateTime": 1505207214532 40 | } 41 | ], 42 | "category": 1, 43 | "description": "RZI6JaL3hc", 44 | "privilege": 2, 45 | "authorization": 1, 46 | "createTime": 1505208090968, 47 | "updateTime": 1505208131010 48 | } 49 | } -------------------------------------------------------------------------------- /mock.data/template/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/template/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "首页", 3 | "description": "页面模板" 4 | } -------------------------------------------------------------------------------- /mock.data/template/search.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/template/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "4W5ou1Ubep", 3 | "title": "搜索结果页面", 4 | "description": "搜索结果页面" 5 | } -------------------------------------------------------------------------------- /mock.data/template/works/create.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/template/works/create.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "作品创建页面", 3 | "description": "作品新建页面" 4 | } -------------------------------------------------------------------------------- /mock.data/template/works/detail.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/template/works/detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "作品详情页面", 3 | "description": "作品新建页面" 4 | } -------------------------------------------------------------------------------- /mock.data/template/works/list.js: -------------------------------------------------------------------------------- 1 | module.exports = function (json, req) { 2 | return json; 3 | } -------------------------------------------------------------------------------- /mock.data/template/works/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "作品列表页面", 3 | "description": "作品列表页面" 4 | } -------------------------------------------------------------------------------- /nei.15044.e389c52125abdd607c4455e4d448e5d3/json/nei-2017-10-12-13-23-21-578-diff.txt: -------------------------------------------------------------------------------- 1 | 项目基本信息有更新 2 | -------------------------------------------------- 3 | 规范的文档有更新 -------------------------------------------------------------------------------- /nei.15044.e389c52125abdd607c4455e4d448e5d3/nei.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": { 3 | "specType": "web", 4 | "pid": 15044, 5 | "key": "e389c52125abdd607c4455e4d448e5d3" 6 | } 7 | } -------------------------------------------------------------------------------- /nei.15044.e389c52125abdd607c4455e4d448e5d3/server.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * config file for nei server command 3 | * @author 4 | * Auto build by NEI Builder 5 | */ 6 | var path = require('path'); 7 | 8 | module.exports = { 9 | /* 根目录 */ 10 | webRoot: __dirname + '/../public/', 11 | /* 视图目录 */ 12 | viewRoot: __dirname + '/../public/html/', 13 | /* 路由 */ 14 | routes: { 15 | "ALL /api/*": "http://59.111.99.234/", 16 | "GET /captcha": "http://59.111.99.234/", 17 | "GET /uploads/:filename": "http://59.111.99.234/", 18 | "GET /works/detail/:id": { name: '作品详情页面', index: 0, list: [{"id":13096,"path":"works/detail"}] }, 19 | "GET /works/create": { name: '作品创建页面', index: 0, list: [{"id":13081,"path":"works/create"}] }, 20 | "GET /works": { name: '作品列表页面', index: 0, list: [{"id":13079,"path":"works/list"}] }, 21 | // "GET /uploads/:filename": { path: 'get/uploads/_/filename/data', id: 36427, group: '图画',isFile: true }, 22 | "GET /search": { name: '搜索结果页面', index: 0, list: [{"id":13080,"path":"search"}] }, 23 | "GET /index": { name: '首页', index: 0, list: [{"id":13078,"path":"index"}] }, 24 | // "GET /captcha": { path: 'get/captcha/data', id: 29450, group: '验证码',isFile: true }, 25 | // "POST /api/works?upload": { path: 'post/api/works/_/upload/data', id: 28719, group: '作品' }, 26 | // "PATCH /api/works/:id": { path: 'patch/api/works/_/id/data', id: 28580, group: '作品' }, 27 | // "DELETE /api/works/:id": { path: 'delete/api/works/_/id/data', id: 28581, group: '作品' }, 28 | // "GET /api/works/:id": { path: 'get/api/works/_/id/data', id: 28577, group: '作品' }, 29 | // "POST /api/works": { path: 'post/api/works/data', id: 28579, group: '作品' }, 30 | // "GET /api/works": { path: 'get/api/works/data', id: 28578, group: '作品' }, 31 | // "POST /api/users?unfollow": { path: 'post/api/users/_/unfollow/data', id: 28971, group: '用户' }, 32 | // "GET /api/users?getstarlist": { path: 'get/api/users/_/getstarlist/data', id: 30936, group: '用户' }, 33 | // "GET /api/users?getloginuser": { path: 'get/api/users/_/getloginuser/data', id: 29446, group: '用户' }, 34 | // "POST /api/users?follow": { path: 'post/api/users/_/follow/data', id: 28970, group: '用户' }, 35 | // "GET /api/tags?recommend": { path: 'get/api/tags/_/recommend/data', id: 35276, group: '标签' }, 36 | // "POST /api/register": { path: 'post/api/register/data', id: 28507, group: '用户' }, 37 | // "POST /api/logout": { path: 'post/api/logout/data', id: 28525, group: '用户' }, 38 | // "POST /api/login": { path: 'post/api/login/data', id: 28516, group: '用户' }, 39 | }, 40 | /* 注入给页面的模型数据的服务器配置 */ 41 | // modelServer: { 42 | // // 完整的主机地址,包括协议、主机名、端口 43 | // host: '', 44 | // // 查询参数,键值对 45 | // queries: {}, 46 | // // 自定义请求头,键值对 47 | // headers: {}, 48 | // // path 可以是字符串,也可以是函数;默认不用传,即使用 host + 页面path + queries 的值 49 | // // 如果是函数,则使用函数的返回值,传给函数的参数 options 是一个对象,它包含 host、path(页面path)、queries、headers 等参数 50 | // // 如果 path 的值为假值,则使用 host + 页面path + queries 的值; 51 | // // 如果 path 的值是相对地址,则会在前面加上 host 52 | // // path: '', 53 | // }, 54 | /* api 响应头 */ 55 | apiResHeaders: { 56 | }, 57 | /* 是否自动打开浏览器 */ 58 | launch: true, 59 | /* 自动打开的页面地址 */ 60 | openUrl: '', 61 | /* 端口 */ 62 | port: 8002, 63 | /* 是否使用 https 协议,设为true的时候表示启用 */ 64 | https: false, 65 | /* 是否使用 nei 提供的在线 mock 数据 */ 66 | online: false, 67 | /* 是否监听静态文件和模板文件的变化并自动刷新浏览器 */ 68 | reload: true, 69 | /* 文件监听的选项 */ 70 | watchingFiles: { 71 | /* 需要即时编译的文件, 前提是 reload 为 true */ 72 | compilers: { 73 | /* 值为 mcss 的配置选项, 默认为 false,即不编译 mcss 文件 */ 74 | mcss: false 75 | }, 76 | /* 不用监听的文件,支持通配符 */ 77 | //ignored: '**/*.css' 78 | }, 79 | /* 项目的 key */ 80 | projectKey: 'e389c52125abdd607c4455e4d448e5d3', 81 | /* 同步模块mock数据路径 */ 82 | mockTpl: __dirname + '/../mock.data/template/', 83 | /* 异步接口mock数据路径 */ 84 | mockApi: __dirname + '/../mock.data/interface/', 85 | /* 模板后缀 */ 86 | viewExt: '.html', 87 | /* 模板引擎 */ 88 | engine: 'ejs', 89 | /* 打开下面的 fmpp 配置,可以在模板中调用自定义 jar 包中的类 */ 90 | //fmpp: { 91 | // /* 存放自定义 jar 的目录, 绝对路径 */ 92 | // jarDir: '', 93 | // /* 暴露给模板的类实例名称和 jar 中的类名(带包名)的对应关系 */ 94 | // jarConfig: { 95 | // [暴露给模板的类实例名称]: [类名] // 比如: HighlightUtil: 'yueduutil.HighlightUtil' 96 | // } 97 | //} 98 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-netease-ego", 3 | "version": "1.0.0", 4 | "description": "动漫社区网站Ego(网易Mini项目)", 5 | "main": "", 6 | "scripts": { 7 | "test": "nei server" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Yibay/Web-netease-Ego.git" 12 | }, 13 | "keywords": [ 14 | "Mini", 15 | "Ego", 16 | "netease" 17 | ], 18 | "author": "Yibay", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/Yibay/Web-netease-Ego/issues" 22 | }, 23 | "homepage": "https://github.com/Yibay/Web-netease-Ego#readme" 24 | } 25 | -------------------------------------------------------------------------------- /public/common/base.css: -------------------------------------------------------------------------------- 1 | /* 字体图标 */ 2 | @import url(./font/iconfont.css); 3 | /* Reset */ 4 | html,body,ul,h3,h4{margin:0;padding:0;font:12px "Microsoft YaHei", "Helvetica", "sans-serif";} 5 | body{background:#efefef;} 6 | ul{list-style:none;} 7 | a{text-decoration:none;color:inherit;} 8 | input,textarea{outline:none;} 9 | button{margin:0;padding:0;outline:none;cursor:pointer;} 10 | em{font-style:normal;} 11 | /* 布局样式 */ 12 | .g-header{height:90px;border: 1px solid #fff;box-sizing:border-box;background-color:#fff;} 13 | /* 元件样式 */ 14 | /* 字体图标 */ 15 | .u-icon-female{color:#e55f48;} 16 | .u-icon-male{color:#4b91fe;} 17 | .u-icon-down{color:#5ed0ba;} 18 | .u-icon-work{width:32px;height:32px;background:url(../res/images/iconfont/u-icon-work.png) center/100% 100% no-repeat;} 19 | .u-icon-pencle{width:32px;height:32px;background:url(../res/images/iconfont/u-icon-pencle.png) center/100% 100% no-repeat;} 20 | .u-icon-people{width:40px;height:40px;background:url(../res/images/iconfont/u-icon-people.png) center/100% 100% no-repeat;} 21 | .u-icon-errorsquare{color:red;} 22 | .u-icon-address{color:#e15f48;} 23 | /* 表单样式 */ 24 | .u-formitem{margin:10px 0;white-space:nowrap;} 25 | .u-formitem .formitem_tt{float:left;width:60px;font-size:12px;line-height:40px;} 26 | .u-formitem .formitem_ct{display:inline-block;width:100%;height:40px;box-sizing:border-box;font-size:12px;} 27 | /* 扩展样式 */ 28 | .u-formitem-2{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;justify-content:space-between;} 29 | /* 输入框 */ 30 | .u-ipt{border:1px solid #5ed0ba;-webkit-border-radius:4px;border-radius:4px;font-size:12px;text-indent:15px;} 31 | .u-ipt.error{border-color:red;} 32 | /* 按钮 */ 33 | .u-btn{display:inline-block;width:100%;height:38px;border:1px solid #5ed0ba;border-radius:3px;line-height:38px;text-align:center;text-decoration:none;cursor:pointer;} 34 | .u-btn-primary{color:#fff;background-color:#5ed0ba;} 35 | .u-btn-link{color:#5ed0ba;background-color:#fff;} 36 | .u-link{cursor:pointer;} 37 | /* 其它 */ 38 | .u-error{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;font-size:12px;line-height:2em;cursor:pointer;} 39 | .u-error span + span{margin-left:8px;} 40 | /* 功能 */ 41 | .f-cb::before,.f-cb::after{display:table;content:'';clear:both;} 42 | .f-dn{display:none!important;} 43 | .f-tac{text-align:center;} 44 | .f-vh{visibility:hidden;} 45 | .f-oh{overflow:hidden;} 46 | .f-pen{pointer-events:none;}/* 点击穿透 */ 47 | .f-fr{float:right;} 48 | .f-fl{float:left;} -------------------------------------------------------------------------------- /public/common/font/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "u-icon"; 3 | src: url('iconfont.eot?t=1508720432584'); /* IE9*/ 4 | src: url('iconfont.eot?t=1508720432584#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA/QAAsAAAAAGBQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW90npY21hcAAAAYAAAAEhAAADTnIEcyNnbHlmAAACpAAACj4AAA7EncJZHGhlYWQAAAzkAAAAMQAAADYP2ARxaGhlYQAADRgAAAAgAAAAJAhwBTVobXR4AAANOAAAACIAAABsbhn//mxvY2EAAA1cAAAAOAAAADgznjcSbWF4cAAADZQAAAAfAAAAIAEuAHhuYW1lAAANtAAAAUsAAAJVtlIFh3Bvc3QAAA8AAAAAzQAAAS1JqbvTeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkEWGcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGBwYKp4fY27438AQwzyXoRMozAiSAwDqCwx2eJzFkk1OAkEQRt/AzAAjIqDOKCsXLgkhXoGQeBh2bNhwM5bICTxD9THwqyk2JiS6MXbn9fRP9VR11QcUQFfMRQ6dkkwzH7rtbK5v1e7nfGr9yotmOTvDulbZyKbW2MJWtraN7e2QijRO23Q6n2UdVoWsJlZfsTq2Vj+1TF6jL9SXvF3pbjVmyLNe1PDEo+LOmXLLhJoRA72iT4+SO27ocM8DM10qf+H/j1r2f66/t6EP2ftlpaywu6AQjUDZVS0Dv2FFoIxjZeBqsl6gKmD9QPXABoEqg1WBK89GgavPJoHqhl1QBbE6wM+bAN9bBPg/VgHuZx24cm0T4LHsAzzGQyBFkIpA2iCNA6mEtA2kF9IxkHJIH4E0RDoFzL4AdT5q3wAAAHichVd9bBTXEX/z3u7t7d35vnZv985fd3vL7YJtzvh8vsN2OdtgEiA4IEMDGAWLlIbylYp8lHxAbKpgoCJguyqNlNJIIYVCpOSPqPmoIIGq+QBFCiqNAsEhDa3S/pGmTVVVDfjWnXe2w6VpE8uaee/NvLfz5jdvZo6IhEx8xE6xKFHITNJEuslyQsBVD0k/rQHDzqZpPUQMMaKrfmabtiGZyTSbB3rSpWqZXNbSXZIrAH6ohWYjk7PT1IaWbIG2Q0arAYhVVa4Ip6rDbBg8Ubt2j7OEPg2RuFkdKMx2Fjd0qJmE4t7hC4dj4fABt0sU3ZQKAT9s0zVZlD0u5xkxUBk5FZ9F4+CL2ZVL11QkqsLr92XvqUnpMsDgIChVCf/xjlBlCP93VmpKOCYFK9zRygpzhgo7/uSNKr4a648E/yje9W36D+YiSZImJGW2FCCfBtsPUi3oEdMPqqY3R/i95kFzAbIWoAKKUCEN9EerVm6ibN/GjfsY3bRylVZTo62qaajhA+f3dFl7+zJaom/1K7tXb9xP6f6Nq3cr/VBTXwPrufb60nDptB7AMkK4/z9kB9ks0kbmkQ6ylKxB/xstWctOShaaVAOmlZRKtrVDCI1pjqTQqlAaki5J8YOtG3mX1aKbIYPDwSJmqgXhkBR+Da5mT09acMI2Ohe0oEeWPUHtiugRF4YikdAsqE/YcD5RD4h0BACM+pnOskyDAUDXo8JucIKwKogSLYEk8k6oNNQgQq/MxGVRhEhwJpR2F4/wYwDqEjTKz3bSdVV8Xj17Mf0O7ikOdywW4DPc4GSDmhaEt/m55TO8PgeLPU5PcsQUA+5nUed+eqK4fgrDf7MhJhIv0VF1ErsSjgzvOo0XJcfGBGHs2CR94rwgnH+iRNnN1WNjxRibWkY6FR+si24lYVLLUWC2pE2Czw/W9DxMfwudis9jBmxYlwA2vG3bMEO6pH2dc4QuyuUXASzK5xbRrc7lDf1zb5mUIoUZ65w3cX1aB+8jTUxMDAo/Znn8ukhk4iNBohIL7w0MFMPOm5IMZsqwTbHZNvOK5pLAlbQhDXklhy/uIdjwLOhvOduXPXD4GXorbHjZScHQAz+DDW/84vDrY26Qi1c8hrc4JlP3mEehowqccx6Dnc6t8MrisTGnHXYiG2tzrniq5OKLXi9dIld5IBWY8vU1NsbuQbvq0B/5RgiALuV1UAOgaBHVTNop0ZW0WrJ5xeY0peQyuiJq9LnnP9u69bPn6y69B4L/QhNEPT5nwpnwaT5ouuBPQO1ub9K7G2oTMP+keeWKefLv27fDKV/gvHNNcFdLe/dKQZ/ghtrzAV/ENQuEaNQZn+Wasukqe5olSYDUc4wQHv4qjMk3wTQ/JBtBciXT0AG2VYBMHPK5TC1azmYXj2W6KO3K0L4Sf06GcEzes0eOhUEeGvrSjCVRr1y/eBkFXAuV9u4tG2Oook2vsbOsC22Kk2991Sp8ppKBQRPCqYHLkpUMoHFJP8TR1FrIdICuZQrQiKZa3MyOFQArOmhfieswJ1U8lpoDyGkf8ovy/7MaZ6wTd5afUFTLtiJ/RP5ft8AxokwmfsnOsNUkREwyg+RIniwmS8jtZAveSXWZBkLdlOVhp0VaIfTVFcBpCKdNGU0P+cFspcbNlQjf0ErzRsgALZNXgNcPWwbMWkkMcgsrCqZ0l8rOqIHxNwKqGmBtnN4cjx8NKEogoAAoOPerqp9LguPHWF+x2hf3TgDzxjWv44DHq8V9zr+c6z6cgzRBvKxlWn/8MKd8xjaP34cnb765enPchmd6vF6gjsPPAgoufpbzOUils687ji8+if0wu8HuJ27STDrRTwgnv5FLSaZpNpfPJfRa9B1GpJXN85piWwmpVGlqEe1aQImIEWtlcxipmuoCfHxzLWtu6rU7ntrV1VUYgrVDBeSeqOppyc/N3uu8dx/ni1+9q0KTfBXtcNnn+VhWopzQb1XzraniS527fv78kcLAQIEzLs0NHR7Kbd1aYrdmYkJFpSZUDMgfe6JKiZBS3p04g/h3kmreA5RjgnjJZXix7yc155xzTu+erUMe+exu5Jgiu9O6cw7yehrDcHKEEq7JJSXNdLfGNXHnVJ6/l35MsliNk5aNTwTzWnlRxnSHAz+Pq9Jyy2SCR3+WhKzf7QVxV6ssuM0UsAfX9O1gkDLdgty6SwRv0Cv8oNNI0kObNz/OzETHDgG81V537kSNoLrjvY2ojXvSvQkpItScyLm9Xnfh+Gxz1ZxNBxk9uHnOKnP28YLbO9U7oK0/pH2YnbFEpfIcY973NJacFEfY8030wAexzILK3/mT/ouV85tjV295bMlfr8YWZKIXvbTiYjSzIHZ1EV1SOgp9vYPtY0fwrVkkU6pkGBGloAijC2xrhv7F/Ut+4VfPadKkGh9b8MD2/ZK05Y7eddFZvyp0VFW1OR8+OX+h6NKbbhk2jOHuuhpYs2Lhi6daDUPtNc1eNX+EbWi8sL3zo92P9AfjAPc9/JOeI/uG6x/syd9+597XRfG3e5avnrl2uC7pedbY1Z7IAGQS258k0zV5EM328vwWwnYjYoSaQwYbdN496bwL9SehHpBNzbi6MLWH7/WRGK9qYJZ1VEpZ8bZDZdVbouQPdKC//1HGHu3vH7h69LIgXD569JIgXDo6ckYQzowMn2Xs7DAM9g/QaUX4sKQjipeOon6xAeVf6E7WjcnY5jn6qxladdmGy8qHrJyOuUxincXBzFJKl2boJPfPW+CQlY1MqOujZHU9Y50oK9cpzl/VwGBeNyXd86Awfxrjm99MffNXwZZ0sL/x207dwTuuwRMrv8kG58Qh1PvpyiksJt5nx5mNnuD9bwOPYTtlgy6WFafm/JcslLCqY3OvYzxC7u1R+POoc2YULEibjoI5CxqT/+x1nmrrobSnjZrtPdC7bq0vUdEw2uCDX7eN/GbEOTuy3+SKJnxipqEXbm8rfjCpD+t7YVmfF3z1I/UViRI+E+wSukzF3yEEJNviKcAV1pNNuTDPDEkX9hXYbdgzKE8RGb0ULVTXtLtW37YpWH3hmvDRO9Wh791WfyfcGz/nFEWfyymer1V39X13S3ZTU99OOhoIdgOsXT7q/KVvLcSGl/VBcuCg8+ZWSjdB26F122BpKgV0y1Sf8T47xiyMd4wXsSwpKaVEntF4ug4Az+sd2G++8KkofvpCibYpihypjsinFeW00mQrI9a0BCnNKE2q7PHIp9U56mnFblJGyPT7Erro4/wXgUyxxxI6nRXOq1veg/nUcHphAR9hTGG/OMA+Z7tLPWoTWoZlpsWaqjOS6qes7FWBlesALL+8HEsqQumyYPzuVw710D07Hgb/Qw8+Bj3DL288dUMUb5w6fV0Urzvfdsneq6FYCEB2/82rD6L87gOfLFq4cNEnB+5+ebiHvSReP33qhiDcQFo84dPFJdBaEQpVOC8JckBYLrr/23dKWdMsYn/IyyH323TR6/t678Dg17kWvfYfxLIksAAAeJxjYGRgYADiHWUCG+P5bb4ycLMwgMA14QQDGP3///9ylsnMc4FcDgYmkCgALDMLRwAAAHicY2BkYGBu+N/AEMM68/9/BgaWyQxAERQgDQCP5gWveJxjYWBgYH7JwMDCgAVP/v8fqzgJmHUmkGaGmAMATt0GgQAAAAAAAAB2AMABSgFYAYYBwAIOAlgCogMGA6AEBgQ+BJQEvAUUBS4FdAWuBfQGRAaMBsQG2AcqB2J4nGNgZGBgkGbIYeBkAAEmIOYCQgaG/2A+AwAWfgGnAHicXZA9TgMxEIXf5g+xQRQgaGgsCoRA2fx0pKGIlNSkSJ9svPnR7nrlOJFScB4OwAk4AAeg4QocgIaX3QGR2PLom+c3npEBnOETHop1wVOwhxqzgks4wpVwmfq1cIV8K1xFHYFwjftB2Mc9HoXrOMeSL3iVY2Z3eBb2cIIX4RJO8Spcpv4mXCG/C1dxiQ/hGvUvYR8jfAvXceM9+T2rx05P1WSrFqFJI5M6f93Y4VDP1vHYFkkRR9quFiZV7aBVCAOdavtbv9rMOs5FKrImUX0+pOPYqMyapQ5dMHcu6zabkehBaBKO04OFxhiOcQqFCbaMC4QwSBHl0dG3RuNPHdI7oxKzzu7d/OcRXRYr5rtMoc3Pb+05BnSkueuw/wobduhQdZxB8VhWJKS+TKTZPSYrZPndkkpIPcA8r8rQRZM7OvAHee/kB4wPZ1IAeJxtTdtOwzAUi7ek7VqgjDuMn0uTwxot6YGTTNvnsxYh8YAfbMuybLVSP2jV/9hhhTU0DCrUaLBBiw5XuMYNetxiizvc4wGPeMIzXvCKN+zwrnCuPijZSJWnSIUaL/zp+TR1Yn1gF8RF0nOh9sEmnrzOxUpHIiz562iF+j8+hom2Q0jB8XQSnvZDtO5QZbLiRuMiZ9LHTKLni9p6L5Tz+qJmuWvcSO4w8Ln/NYuS1+RDMeNlpmwSC0nYj8UsrEtI1M7hwKVwUuobDMtPRwAAAA==') format('woff'), 6 | url('iconfont.ttf?t=1508720432584') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1508720432584#u-icon') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .u-icon { 11 | font-family:"u-icon" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .u-icon-female:before { content: "\e773"; } 19 | 20 | .u-icon-delete:before { content: "\e60a"; } 21 | 22 | .u-icon-dropdown:before { content: "\e60e"; } 23 | 24 | .u-icon-radiocircle:before { content: "\e606"; } 25 | 26 | .u-icon-male:before { content: "\e6bc"; } 27 | 28 | .u-icon-diamond:before { content: "\e67d"; } 29 | 30 | .u-icon-star:before { content: "\e66c"; } 31 | 32 | .u-icon-errorsquare:before { content: "\e643"; } 33 | 34 | .u-icon-errorsquareline:before { content: "\e645"; } 35 | 36 | .u-icon-bimiconwrongblack:before { content: "\e710"; } 37 | 38 | .u-icon-search:before { content: "\e603"; } 39 | 40 | .u-icon-close:before { content: "\e612"; } 41 | 42 | .u-icon-user:before { content: "\e62e"; } 43 | 44 | .u-icon-down:before { content: "\e705"; } 45 | 46 | .u-icon-address:before { content: "\e600"; } 47 | 48 | .u-icon-add:before { content: "\e616"; } 49 | 50 | .u-icon-radio:before { content: "\e611"; } 51 | 52 | .u-icon-checkbox:before { content: "\e7c4"; } 53 | 54 | .u-icon-checkboxchecked:before { content: "\e7c5"; } 55 | 56 | .u-icon-edit:before { content: "\e609"; } 57 | 58 | .u-icon-heart:before { content: "\e617"; } 59 | 60 | .u-icon-moreright:before { content: "\e607"; } 61 | 62 | .u-icon-right:before { content: "\e608"; } 63 | 64 | .u-icon-time:before { content: "\e605"; } 65 | 66 | .u-icon-morebottom:before { content: "\e7c6"; } 67 | 68 | -------------------------------------------------------------------------------- /public/common/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/common/font/iconfont.eot -------------------------------------------------------------------------------- /public/common/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/common/font/iconfont.ttf -------------------------------------------------------------------------------- /public/common/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/common/font/iconfont.woff -------------------------------------------------------------------------------- /public/common/js/emitter.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | // 事件管理器(发布-订阅模式) 7 | (function(App){ 8 | 9 | // 事件列表对象 10 | var client_list = {}; 11 | 12 | // 订阅事件 函数 13 | function on(key, fn){ 14 | // 若该事件,还没有事件列表 15 | if(!client_list[key]){ 16 | // 则新建一个事件列表 17 | client_list[key] = []; 18 | } 19 | // 向该事件列表,存入回调函数 20 | client_list[key].push(fn); 21 | } 22 | 23 | // 发布事件 函数 24 | function emit(){ 25 | // 获取第一个参数 事件名称 26 | var key = Array.prototype.shift.call(arguments); 27 | // 该事件 对应的回调函数集合 28 | var fns = client_list[key]; 29 | // 若该事件无回调函数 30 | if(!fns || fns.length === 0){ 31 | // 则直接返回 32 | return false; 33 | } 34 | // 否则,遍历数组,执行回调函数 35 | for(var i=0;i函数连用,不然会污染this,arguments 36 | fns[i].apply(this, arguments);// emit将被混入组件,所以emit将被作为组件方法调用,预测this指向组件本身 37 | } 38 | } 39 | 40 | // 取消订阅 41 | function remove(key, fn){ 42 | // 该事件 对应的回调函数集合 43 | var fns = client_list[key]; 44 | // 若该事件无回调函数 45 | if(!fns){ 46 | // 则直接返回 47 | return false; 48 | } 49 | // 若未指定具体函数 50 | if(!fn){ 51 | // 清空全部 回调函数 52 | fns && (fns.length = 0); 53 | } 54 | else{ 55 | for(var i = fns.length - 1;i>=0;i--){ 56 | if(fns[i] === fn){ 57 | fns.splice(i,1); 58 | } 59 | } 60 | } 61 | 62 | } 63 | 64 | // 揭示接口 65 | App.emitter = { 66 | on: on, 67 | emit: emit, 68 | romove: remove 69 | } 70 | 71 | })(window.App); -------------------------------------------------------------------------------- /public/common/js/template.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | // 模板储存对象 7 | (function(App){ 8 | 9 | var template = { 10 | 11 | /** Section 模板 12 | * 上下文 { 13 | * icon, str类型 (用于 图标样式) 14 | * title, str类型 (用于 标题内容) 15 | * cnt, str类型 (用于 替换section_cnt结构) 16 | * } 17 | */ 18 | m_section: Handlebars.compile(`
19 |

20 | {{title}} 21 | 更 多 22 |

23 | {{#if cnt}} 24 | {{!-- 使用3个{},可以不转译<>等字符 --}} 25 | {{{cnt}}} 26 | {{else}} 27 |
28 | {{/if}} 29 |
`), 30 | 31 | /** 图片列表 模板 32 | * 上下文 { 33 | * list_type, str类型 (用于 列表整体样式,如每行几列。) 34 | * list, array类型, 35 | * 其中单个元素为obj{ 36 | * img: str类型 (url地址) 37 | * img_alt: str类型 (图片加载失败时,显示的文案) 38 | * img_name: str类型 (图片的名字)[可选] 39 | * } 40 | * } 41 | */ 42 | list_img: Handlebars.compile(`
43 |
    44 | {{#each list}} 45 |
  • 46 | {{img_alt}} 47 | {{#if img_name}} 48 |
    {{img_name}}
    49 | {{/if}} 50 |
  • 51 | {{/each}} 52 |
53 |
`), 54 | 55 | /** 活动进行时 模板 56 | * 上下文 { 57 | * list_type, str类型 (用于 列表整体样式,如每行几列。) 58 | * list, array类型, 59 | * 其中单个元素为obj{ 60 | * img: str类型 (url地址) 61 | * times: str类型 (活动时间段) 62 | * status: str类型 (活动状态) 63 | * } 64 | * } 65 | */ 66 | list_activity: Handlebars.compile(`
67 |
    68 | {{#each list}} 69 |
  • 70 |
    71 |
    快来PK
    72 |
    邀请小伙伴
    73 |
    74 |
    75 | 76 |
    77 |
  • 78 | {{/each}} 79 |
80 |
`), 81 | 82 | /** 首页 侧边栏排行榜 83 | * 上下文 { 84 | * list, array类型, 85 | * 其中单个元素为obj{ 86 | * img_url: str类型 (作品图片地址) 87 | * work_name: str类型 (作品名字) 88 | * author_name: str类型 (作者名称) 89 | * visit_num: num类型 (查看人数) 90 | * collection_num: num类型 (收藏人数) 91 | * } 92 | * } 93 | */ 94 | aside_ranking: Handlebars.compile(`
95 |
96 |
排 行
97 |
98 |
99 |
    100 | {{#each list}} 101 |
  • 102 | 103 |
    104 |
    {{work_name}}
    105 |

    {{author_name}}

    106 |

    查看 {{visit_num}}收藏 {{collection_num}}

    107 |
    108 |
  • 109 | {{/each}} 110 | 111 |
112 |
`), 113 | 114 | /** 首页 侧边栏达人排行榜 115 | * 上下文 { 116 | * list, array类型, 117 | * 其中单个元素为obj{ 118 | * img_url: str类型 (作品图片地址) 119 | * author_name: str类型 (作者名称) 120 | * works_num: num类型 (查看人数) 121 | * fans_num: num类型 (收藏人数) 122 | * } 123 | * } 124 | */ 125 | aside_authorranking: Handlebars.compile(`
126 |
127 |
达 人 排 行 榜
128 |
129 |
    130 | {{#each list}} 131 |
  • 132 | 133 |
    134 |
    {{author_name}}
    135 |

    作品 {{works_num}}粉丝 {{fans_num}}

    136 |
    137 |
  • 138 | {{/each}} 139 | 140 |
141 |
`), 142 | 143 | /** 首页 侧边栏热门话题 144 | * 上下文 { 145 | * list, array类型, 146 | * 其中单个元素为obj{ 147 | * title: str类型 (热门话题 标题) 148 | * } 149 | * } 150 | */ 151 | aside_hottopic: Handlebars.compile(`
152 |
153 |
热 门 话 题
154 |
155 |
    156 | {{#each list}} 157 |
  • 158 |
    159 |
    {{title}}
    160 |
    161 |
  • 162 | {{/each}} 163 |
164 |

更 多

165 |
`), 166 | 167 | /** 首页 侧边栏圈子 168 | * 上下文 { 169 | * list, array类型, 170 | * 其中单个元素为obj{ 171 | * circle_name: str类型 (圈子 名称) 172 | * circle_members (圈子 成员数) 173 | * } 174 | * } 175 | */ 176 | aside_circle: Handlebars.compile(`
177 |
178 | 179 |
180 |
    181 | {{#each list}} 182 |
  • 183 | 184 |
    185 |
    {{circle_name}}
    186 |

    已圈 {{circle_members}}人

    187 |
    188 |
  • 189 | {{/each}} 190 |
191 |

更 多

192 |
`) 193 | 194 | } 195 | 196 | App.template = template; 197 | 198 | })(window.App); -------------------------------------------------------------------------------- /public/common/js/util.js: -------------------------------------------------------------------------------- 1 | // 封装工具函数 2 | (function(global){ 3 | // 依赖 4 | var document = global.document; 5 | 6 | // 工具函数对象 7 | var _ = Object.create(null); 8 | 9 | // 1. 继承属性 10 | _.extend = function(obj, options){ 11 | // 若options对象存在 12 | if(options && typeof options == 'object'){ 13 | 14 | // 若浏览器支持ES6 15 | if(typeof Object.assign !== 'undefined'){ 16 | // obj 继承 options中的属性 17 | return Object.assign(obj, options); 18 | } 19 | // 若浏览器不支持ES6 20 | else{ 21 | for(var key in options){ 22 | obj[key] = options[key]; 23 | } 24 | return obj; 25 | } 26 | } 27 | }; 28 | // 8. 判断是否含有该class 29 | _.hasClassName = function(ele, className){ 30 | var current_className = ` ${ele.className} `,has_className; 31 | if(current_className.indexOf(` ${className} `) === -1){ 32 | has_className = false; 33 | } 34 | else{ 35 | has_className = true; 36 | } 37 | return has_className; 38 | }; 39 | // 2. 为元素添加css类 40 | _.addClassName = function(ele, className){ 41 | var new_className = ` ${ele.className} `; 42 | // 若 该元素没有此class,才添加 43 | if(new_className.indexOf(` ${className} `) === -1){ 44 | new_className += className; 45 | // 更新className 46 | ele.className = new_className.trim(); 47 | } 48 | }; 49 | // 3. 删除元素的css类 50 | _.delClassName = function(ele, className){ 51 | // 前后加上空格,防止误伤 52 | var new_className = ` ${ele.className} `; 53 | new_className = new_className.replace(new RegExp(` ${className} `, 'g'), ' '); 54 | // 更新className 55 | ele.className = new_className.trim(); 56 | }; 57 | // 4. 生成dom元素 58 | _.html2node = function(str){ 59 | var container = document.createElement('div'); 60 | container.innerHTML = str; 61 | return container.children[0]; 62 | }; 63 | // 5. 按className获取dom元素 64 | _.getElementsByClassName = function(ele, className){ 65 | // 若浏览器支持getElementsByClassName, 则使用 66 | if(document.getElementsByClassName){ 67 | return ele.getElementsByClassName(className); 68 | } 69 | }; 70 | // 6. 数据请求 71 | /* options = { 72 | * url: str, 请求地址, 如:'/api/logout', 73 | * method: str, 请求方法, 如:'POST', 74 | * data: obj, 请求数据,(选填) 75 | * success: func, 请求成功后, 执行函数 76 | * fail: func, 请求失败后, 执行函数 77 | * header: {} 请求头(选填) 78 | * withCredentials: boolean 是否带cookie身份认证(选填,默认false) 79 | * finish_data: obj 直接xhr.send(finish_data),不做处理 80 | * } 81 | */ 82 | _.ajax = function(options){ 83 | var xhr = new XMLHttpRequest(); 84 | 85 | // 0. 监听状态变化 86 | xhr.onreadystatechange = function(){ 87 | if(xhr.readyState == 4){ 88 | if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 89 | options.success( 90 | // JSON.parse(xhr.responseText) 91 | xhr.responseText 92 | ); 93 | } 94 | else{ 95 | options.fail(xhr); 96 | } 97 | } 98 | } 99 | 100 | var data; 101 | 102 | // 若需身份认证,带上cookie 103 | if(options.withCredentials){ 104 | // 跨域请求 105 | xhr.withCredentials = true; 106 | } 107 | 108 | // 若是 GET 请求 109 | if(options.method.toUpperCase() === 'GET'){ 110 | var search = options.data ? ('?' + _.serialize(options.data)) : ''; 111 | options.url += search; 112 | data = null; 113 | } 114 | 115 | // 1. 开启请求 116 | xhr.open(options.method, options.url, true); 117 | 118 | // 若是 POST 请求 或 PATCH请求 119 | if(options.method.toUpperCase() === 'POST' || options.method.toUpperCase() === 'PATCH'){ 120 | data = options.data ? JSON.stringify(options.data) : null; 121 | } 122 | 123 | // 若有请求头,则设置请求头 124 | if(options.header){ 125 | for(var key in options.header){ 126 | xhr.setRequestHeader(key, options.header[key]); 127 | } 128 | } 129 | 130 | // 若有finish_data, 131 | if(options.finish_data){ 132 | data = options.finish_data; 133 | } 134 | 135 | // 2. 发送请求 136 | xhr.send(data); 137 | }; 138 | // 7. 数据序列化 139 | _.serialize = function(data){ 140 | // 若无数据 141 | if(!data){return '';} 142 | var pairs = []; 143 | for(key in data){ 144 | // 过滤掉 继承来的属性 145 | if(!data.hasOwnProperty(key)){continue;} 146 | // 过滤掉 方法类的属性 147 | if(typeof data[key] === 'function'){continue;} 148 | var value = data[key].toString(); 149 | key = encodeURIComponent(key); 150 | value = encodeURIComponent(value); 151 | pairs.push(`${key}=${value}`); 152 | } 153 | return pairs.join('&'); 154 | }; 155 | // 9. 将地址data,转成 选择器data格式 {name:,value:,list:} 156 | _.toSelectData = function(data){ 157 | // 映射函数 158 | function mapping(data){ 159 | // 对data数组映射 160 | return data.map(function(item){ 161 | // 设置对应对象的 name,value 162 | var result_data = { 163 | name: item[1], 164 | value: item[0] 165 | } 166 | // 若存在第3项, 则递归映射第3项 167 | item[2] && (result_data.list = mapping(item[2])); 168 | return result_data; 169 | }); 170 | } 171 | var select_data = mapping(data); 172 | return select_data; 173 | }; 174 | // 10. 以选择器data格式,生成日期数据 {name:,value:,list:} 175 | _.createDateData = function(start_year, end_year){ 176 | // 默认值 177 | start_year = start_year || 1970; 178 | end_year = end_year || new Date().getFullYear(); 179 | // 日模板 (用模板,可以省去第3重循环,提升效率) 180 | var day_template = []; 181 | for(var k=1;k<=31;k++){ 182 | day_template.push({name:k,value:k}); 183 | } 184 | var select_data = []; 185 | for(var i=start_year;i<=end_year;i++){ 186 | var year = { 187 | name: i, 188 | value: i, 189 | list: [] 190 | } 191 | for(var j=1;j<=12;j++){ 192 | var month = { 193 | name: j, 194 | value: j, 195 | list: day_template.slice(0,new Date(i,j,0).getDate()) 196 | } 197 | year.list.push(month); 198 | } 199 | select_data.unshift(year); 200 | } 201 | return select_data; 202 | }; 203 | // 11. 计算年龄 204 | _.calculateAge = function(birthday){ 205 | birthday = birthday.replace(/-/g,'/'); // Safari Date 不识别 1990-1-1,只识别 1990/1/1 206 | return parseInt((new Date().getTime() - new Date(birthday).getTime()) / 1000 / 3600 / 24 / 365, 10); 207 | }; 208 | // 12. 查找城市名 209 | _.searchCity = function(src, target){ 210 | src.some(function(item){ 211 | if(item[0] == target.province){ 212 | item[2].some(function(item){ 213 | if(item[0] == target.city){ 214 | target.city_name = item[1]; 215 | return true; 216 | } 217 | }); 218 | return true; 219 | } 220 | }); 221 | return target.city_name; 222 | }; 223 | // 13. 计算星座 224 | _.calculateZodiac = function(birthday){ 225 | birthday = birthday.split('-'); 226 | var month = Number(birthday[1]); 227 | var day = Number(birthday[2]); 228 | var user_zodiac; 229 | var zodiac = [ 230 | [12,22,1,19,'摩羯'],[1,20,2,18,'水瓶'],[2,19,3,20,'双鱼'],[3,21,4,20,'白羊'],[4,21,5,20,'金牛'],[5,21,6,21,'双子'], 231 | [6,22,7,22,'巨蟹'],[7,23,8,22,'狮子'],[8,23,9,22,'处女'],[9,23,10,22,'天秤'],[10,23,11,21,'天蝎'],[11,22,12,21,'射手'] 232 | ]; 233 | zodiac.some(function(item){ 234 | if( (item[0] === month && item[1] <= day) || (item[2] === month && item[3] >= day) ){ 235 | user_zodiac = item[4]; 236 | return true; 237 | } 238 | }); 239 | return user_zodiac; 240 | }; 241 | 242 | // 把工具函数对象 绑定到 全局变量上。 243 | global._ = _; 244 | 245 | })(window); -------------------------------------------------------------------------------- /public/common/js/validator.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | // 验证器(验证数据格式) 7 | (function(App){ 8 | 9 | var validator = { 10 | // 1. 验证是否为空 11 | isEmpty: function(value){ 12 | return typeof value === 'undefined' || !value.trim(); 13 | }, 14 | // 2. 验证电话号码 15 | isPhone: function(value){ 16 | return /^\d{11}$/.test(value); 17 | }, 18 | // 3. 验证昵称 19 | isNickName: function(value){ 20 | // 中英文数字均可,至少8个字符 21 | return /^[\u4e00-\u9fa5a-zA-Z0-9]{8}[\u4e00-\u9fa5a-zA-Z0-9]*$/.test(value); 22 | }, 23 | // 4. 长度限制 24 | isLength: function(value, min, max){ 25 | var length = value.toString().length; 26 | // 验证结果 27 | var result = true; 28 | // 长度 大于等于最小值,小于等于最大值 29 | typeof min !== 'undefined' && (result = result && length >= min); 30 | typeof max !== 'undefined' && (result = result && length <= max); 31 | return result; 32 | } 33 | }; 34 | 35 | App.validator = validator; 36 | 37 | })(window.App); -------------------------------------------------------------------------------- /public/component/AlertModal/AlertModal.css: -------------------------------------------------------------------------------- 1 | /* 警告弹窗组件 样式 */ 2 | .m-alertmodal{position:relative;width:408px;padding:10px 12px;border:1px solid #a8a8a8;box-sizing:border-box;} 3 | /* 关闭弹窗按钮 */ 4 | .m-alertmodal .close_btn{position:absolute;right:9px;top:9px;font-size:15px;color:#c0c0c1;cursor:pointer;} 5 | /* 标题 */ 6 | .m-alertmodal .modal_tt{margin-bottom:10px;line-height:1em;color:#5ed0ba;font-size:15px;} 7 | /* 内容 */ 8 | .m-alertmodal .modal_ct{padding-top:5px;border-top:1px solid #e9eaea;text-align:center;} 9 | .m-alertmodal .modal_ct .alert_msg{font-size:14px;} 10 | .m-alertmodal .modal_ct .u-btn{width:90px;margin-top:24px;margin-bottom:5px;} -------------------------------------------------------------------------------- /public/component/AlertModal/AlertModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 警告弹窗组件 3 | * 继承自Modal组件 4 | */ 5 | (function(App){ 6 | 7 | var template = `
8 | 9 | 12 | 16 |
`; 17 | 18 | function AlertModal(options){ 19 | 20 | this.content = template; 21 | 22 | App.Modal.call(this, options); 23 | 24 | // 缓存节点 25 | this.alert_msg = _.getElementsByClassName(this.container, 'alert_msg')[0]; 26 | this.submit_btn = _.getElementsByClassName(this.container, 'submit_btn')[0]; 27 | this.close_btn = _.getElementsByClassName(this.container, 'close_btn')[0]; 28 | 29 | this.init(); 30 | } 31 | 32 | AlertModal.prototype = Object.create(App.Modal.prototype); 33 | 34 | // 混入事件管理器 35 | _.extend(AlertModal.prototype, App.emitter); 36 | 37 | AlertModal.prototype.init = function(){ 38 | this.on('alert', this.showMsg.bind(this)); 39 | // close按钮,关闭弹窗 40 | this.close_btn.addEventListener('click', this.hide.bind(this)); 41 | // 确定,关闭弹窗 42 | this.submit_btn.addEventListener('click', this.hide.bind(this)); 43 | }; 44 | AlertModal.prototype.showMsg = function(msg){ 45 | // 显示弹窗 46 | this.alert_msg.innerText = msg; 47 | this.show(); 48 | }; 49 | 50 | App.AlertModal = AlertModal; 51 | 52 | })(window.App); -------------------------------------------------------------------------------- /public/component/Aside/Aside.css: -------------------------------------------------------------------------------- 1 | /* 侧边栏组件 样式 */ 2 | .m-aside li{width:101px;height:46px;line-height:46px;text-align:center;font-size:11px;color:#5ed0ba;background:#fdfdfd;} 3 | .m-aside li.z-active{background:#5ed0ba;color:#fff;} 4 | .m-aside li.msg{position:relative;} 5 | .m-aside li.msg::before{content:'';position:absolute;top:5px;right:8px;z-index:99;width:8px;height:8px;-webkit-border-radius:50%;border-radius:50%;background:#e55f48;} -------------------------------------------------------------------------------- /public/component/Aside/Aside.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 侧边栏组件 3 | */ 4 | 5 | (function(App){ 6 | 7 | var template = `
    8 |
  • 个人中心
  • 9 |
  • 我的作品
  • 10 |
  • 我关注的
  • 11 |
  • 我的圈子
  • 12 |
  • 消息提醒
  • 13 |
  • 隐私设置
  • 14 |
`; 15 | 16 | var Aside = { 17 | // 初始化 18 | init: function(options){ 19 | // 继承配置 20 | _.extend(this, options); 21 | // 缓存节点 22 | this.container = _.html2node(template); 23 | this.parent.appendChild(this.container); 24 | } 25 | }; 26 | 27 | App.Aside = Aside; 28 | 29 | })(window.App); -------------------------------------------------------------------------------- /public/component/CascadeSelect/CascadeSelect.css: -------------------------------------------------------------------------------- 1 | /* 级联选择器 组件 */ 2 | .m-cascadeselect{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;justify-content:space-between;height:100%;} 3 | .m-cascadeselect > .m-select{width:29%;} 4 | .m-cascadeselect > .m-select + .m-select{margin-left:16px;} -------------------------------------------------------------------------------- /public/component/CascadeSelect/CascadeSelect.js: -------------------------------------------------------------------------------- 1 | // 级联选择器组件 2 | (function(App){ 3 | 4 | /* options 参数说明 5 | *{ 6 | * parent: dom节点, 父容器(必填) 7 | * data: [{name:,value:,list:}] 8 | *} 9 | */ 10 | function CascadeSelect(options){ 11 | // 继承配置 12 | _.extend(this, options); 13 | // 缓存 各级选择器节点 列表 14 | this.selectList = []; 15 | // 初始化 16 | this.init(); 17 | } 18 | 19 | // 初始化 20 | CascadeSelect.prototype.init = function(){ 21 | for(var i=0;i<3;i++){ 22 | // 创建 选择级组件 (Select中,完成挂载) 23 | var select = new App.Select({ 24 | parent: this.parent 25 | }); 26 | // 订阅 select事件,用于触发级联关系 27 | select.on('select',this.onChange.bind(this, i));//绑定this,绑定参数i 28 | // 缓存选择器节点 29 | this.selectList[i] = select; 30 | } 31 | // 1级下拉菜单,初始化数据 32 | this.selectList[0].render(this.data); 33 | } 34 | /* 响应select事件,渲染下一个Select数据 35 | * 参数:index: 选择器在 级联数组中的序号 36 | * data{ 37 | * value: 地址编码, 38 | * target: 触发事件的选择器, 39 | * index: 选中选项的序号 40 | * } 41 | */ 42 | CascadeSelect.prototype.onChange = function(index, data){ 43 | // 无关的选择器,不做任何操作,直接退出。 44 | if(this.selectList[index] !== data.target){return;} 45 | // 下级选择器,若是末级选择器,则退出 46 | var next = index + 1; 47 | if(next === this.selectList.length){return;} 48 | // 否则,更新渲染下级 选择菜单; 49 | this.selectList[next].render(this.getList(next, data.index)); 50 | } 51 | // 获取第N个Select的下拉列表数据 52 | CascadeSelect.prototype.getList = function(n, index){ 53 | return this.selectList[n - 1].options[index].list; 54 | } 55 | CascadeSelect.prototype.getValue = function(){ 56 | var value = []; 57 | for(var i=0;i<3;i++){ 58 | value.push(this.selectList[i].getValue()); 59 | } 60 | return value; 61 | } 62 | 63 | App.CascadeSelect = CascadeSelect; 64 | })(window.App); -------------------------------------------------------------------------------- /public/component/ClassificWorks/ClassificWorks.css: -------------------------------------------------------------------------------- 1 | /* 作品分类组件 样式 */ 2 | .m-classificworks{margin:0 13px;padding: 15px 0;border-bottom:2px solid #f2f2f2;} 3 | .m-classificworks label{float:left;font-size:11px;} 4 | /* 组件标题 */ 5 | .m-classificworks .classific_title{margin-left:17px;width:88px;line-height:40px;font-weight:700;} 6 | /* 隐藏组件单选按钮 */ 7 | .m-classificworks input[type=radio]{display:none;} 8 | /* 自定义组件单选按钮 */ 9 | .m-classificworks .u-btn{padding:0 30px;width:auto;color:#5ed0ba;cursor:pointer;} 10 | .m-classificworks input[type=radio]:checked + .u-btn{background:#5ed0ba;color:#fff;} 11 | .m-classificworks .classific_radio + .classific_radio{margin-left:34px;} -------------------------------------------------------------------------------- /public/component/ClassificWorks/ClassificWorks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作品分类组件 3 | */ 4 | 5 | (function(App){ 6 | 7 | // 模板 8 | var template = `
9 | 10 | 11 | 12 |
`; 13 | 14 | // 单例组件 15 | var ClassificWorks = { 16 | /** 17 | * options = { 18 | * parent: dom节点 (父容器节点) 19 | * } 20 | */ 21 | // 初始化 22 | init: function(options){ 23 | // 继承配置 24 | _.extend(this, options); 25 | 26 | // 缓存节点 27 | this.container = _.html2node(template); 28 | 29 | // 挂载组件 30 | this.parent.appendChild(this.container); 31 | return this; 32 | }, 33 | // 获取 作品分类信息 34 | getValue: function(){ 35 | var input = this.container.getElementsByTagName('input'); 36 | var value = 0; 37 | Array.prototype.some.call(input, function(item){ 38 | var found = false; 39 | if(item.checked){ 40 | value = item.value; 41 | found = true; 42 | } 43 | return found; 44 | }); 45 | return value; 46 | } 47 | }; 48 | 49 | App.ClassificWorks = ClassificWorks; 50 | 51 | })(window.App); -------------------------------------------------------------------------------- /public/component/ConfirmModal/ConfirmModal.css: -------------------------------------------------------------------------------- 1 | /* 确认弹窗 组件样式 */ 2 | .m-confirmmodal{position:relative;width:408px;padding:10px 12px;border:1px solid #a8a8a8;box-sizing:border-box;} 3 | /* 关闭弹窗按钮 */ 4 | .m-confirmmodal .close_btn{position:absolute;right:9px;top:9px;font-size:15px;color:#c0c0c1;cursor:pointer;} 5 | /* 标题 */ 6 | .m-confirmmodal .modal_tt{margin-bottom:10px;line-height:1em;color:#5ed0ba;font-size:15px;} 7 | /* 内容 */ 8 | .m-confirmmodal .modal_ct{border-top:1px solid #e9eaea;text-align:center;} 9 | .m-confirmmodal .modal_ct .confirm_msg{margin-top:24px;font-size:14px;} 10 | .m-confirmmodal .modal_ct .confirm_msg span{color:#5ed0ba;} 11 | .m-confirmmodal .modal_ct .confirm_msg input{width:200px;height:35px;box-sizing:border-box;line-height:35px;border-color:#d7d7d7;} 12 | .m-confirmmodal .modal_ct .u-btn{width:90px;margin-top:24px;margin-bottom:5px;} 13 | .m-confirmmodal .modal_ct .u-btn + .u-btn{margin-left:20px;} -------------------------------------------------------------------------------- /public/component/ConfirmModal/ConfirmModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 确认弹窗组件 3 | * 继承自Modal组件 4 | */ 5 | 6 | (function(App){ 7 | 8 | var template = `
9 | 10 | 13 | 17 |
`; 18 | 19 | /* options 参数说明 20 | *{ 21 | * parent: dom节点, 父容器(必填) 22 | * content: 内容str (选填) 23 | *} 24 | */ 25 | function ConfirmModal(options){ 26 | 27 | // 固定架构 28 | this.content = template; 29 | 30 | // 继承父类Modal 31 | App.Modal.call(this, options); 32 | 33 | // 缓存节点 34 | this.title = _.getElementsByClassName(this.container, 'modal_tt')[0].getElementsByTagName('strong')[0]; // 标题 35 | this.confirm_msg = _.getElementsByClassName(this.container, 'confirm_msg')[0]; // 确认信息 36 | this.submit_btn = _.getElementsByClassName(this.container, 'submit_btn')[0]; // 确认按钮 37 | this.cancel_btn = _.getElementsByClassName(this.container, 'cancel_btn')[0]; // 取消按钮 38 | this.close_btn = _.getElementsByClassName(this.container, 'close_btn')[0]; // 关闭按钮 39 | 40 | // 初始化 41 | this.init(); 42 | } 43 | 44 | // 继承父类Modal原型 45 | ConfirmModal.prototype = Object.create(App.Modal.prototype); 46 | 47 | // 混入事件管理器 48 | _.extend(ConfirmModal.prototype, App.emitter); 49 | 50 | // 初始化 51 | ConfirmModal.prototype.init = function(){ 52 | // 订阅事件 53 | this.on('confirm', this.showMsg.bind(this)); // 触发时,执行showMsg 54 | 55 | // 绑定事件 56 | this.close_btn.addEventListener('click', this.hide.bind(this)); // 关闭弹窗 57 | this.cancel_btn.addEventListener('click', this.hide.bind(this)); // 取消 58 | this.submit_btn.addEventListener('click', function(evt){ // 确认 59 | // 执行回调函数 60 | this.confirmCallBack(evt); 61 | // 关闭弹窗 62 | this.hide(); 63 | }.bind(this)); 64 | }; 65 | 66 | // 显示确认组件 67 | /** 68 | * options = { 69 | * title : str (弹窗标题) 70 | * content : str (确认消息内容) 71 | * confirmCallBack : func (确认后回调函数) 72 | * } 73 | */ 74 | ConfirmModal.prototype.showMsg = function(options){ 75 | // 标题变更 76 | options.title && (this.title.innerHTML = options.title); 77 | // confirm信息变更 78 | this.confirm_msg.innerHTML = options.content || ''; 79 | // 点击确定后,回调函数 80 | this.confirmCallBack = options.confirmCallBack || function(){console.log('未绑定回调事件');}; 81 | // 显示弹窗 82 | this.show(); 83 | } 84 | 85 | App.ConfirmModal = ConfirmModal; 86 | 87 | })(window.App); -------------------------------------------------------------------------------- /public/component/Guest/Guest.css: -------------------------------------------------------------------------------- 1 | /* Guest 组件样式 */ 2 | .m-guest{float:left;margin-left: 29px;} 3 | .m-guest .u-btn{float:left;width: 90px;font-size:12px;} 4 | .m-guest .u-btn + .u-btn{margin-left:5px;} 5 | .m-guest .u-btn.u-btn-icon{text-align:left;} 6 | .m-guest .u-btn .u-icon{float:left;height:100%;margin-left:15px;margin-right: 10px;font-size:15px;font-weight:900;} -------------------------------------------------------------------------------- /public/component/Guest/Guest.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | (function(App){ 7 | 8 | //模板 9 | var template = `
10 | 13 | 14 |
`; 15 | 16 | // options 参数说明 17 | // { 18 | // parent: dom节点, 父容器 (必填) 19 | // }) 20 | function Guest(options){ 21 | // 继承配置 22 | _.extend(this, options); 23 | 24 | // 缓存节点 25 | this.container = this._template.cloneNode(true); 26 | this.nLogin = this.container.getElementsByTagName('button')[0]; 27 | this.nRegister = this.container.getElementsByTagName('button')[1]; 28 | 29 | // 初始化 30 | this.init(); 31 | } 32 | 33 | // 混入事件管理器 34 | _.extend(Guest.prototype, App.emitter); 35 | 36 | // 用于复用的dom节点 37 | Guest.prototype._template = _.html2node(template); 38 | 39 | // 初始化(绑定事件,将组件载入页面) 40 | Guest.prototype.init = function(){ 41 | // 订阅事件 42 | this.on('login', this.hide.bind(this)); // 登录事件 43 | this.on('notLogin', this.show.bind(this)); // 未登录事件 44 | // 绑定事件 45 | this.nLogin.addEventListener('click', (function(){ 46 | // 弹出登录弹窗 47 | this.emit('showLoginModal'); 48 | }).bind(this)); 49 | this.nRegister.addEventListener('click', (function(){ 50 | // 弹出注册弹窗 51 | this.emit('showRegisterModal'); 52 | }).bind(this)); 53 | 54 | // 挂载组件 55 | this.parent.appendChild(this.container); 56 | }; 57 | // 显示此组件 58 | Guest.prototype.show = function(){ 59 | _.delClassName(this.container, 'f-dn'); 60 | }; 61 | // 隐藏此组件 62 | Guest.prototype.hide = function(){ 63 | _.addClassName(this.container, 'f-dn'); 64 | }; 65 | 66 | App.Guest = Guest; 67 | 68 | })(window.App); -------------------------------------------------------------------------------- /public/component/LoginModal/LoginModal.css: -------------------------------------------------------------------------------- 1 | /* 注册登录弹窗 组件样式 */ 2 | .m-loginmodal{position:relative;width:408px;padding:34px 31px;border:1px solid #a8a8a8;box-sizing:border-box;} 3 | /* 关闭弹窗按钮 */ 4 | .m-loginmodal .close_btn{position:absolute;right:9px;top:9px;font-size:15px;color:#c0c0c1;cursor:pointer;} 5 | /* 头部 */ 6 | .m-loginmodal .modal_tt{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:flex-end;margin-bottom:30px;} 7 | .m-loginmodal .modal_tt > *{line-height:1em;} 8 | .m-loginmodal .modal_tt strong{font-size:20px;color:#5ed0ba;font-weight:400;} 9 | .m-loginmodal .modal_tt span{font-size:12px;} 10 | .m-loginmodal .modal_tt .u-link{color:#5ed0ba;} 11 | /* 保持登录 */ 12 | .m-loginmodal .remember_box{margin-bottom:0;font-size:12px;line-height:2em;cursor:pointer;} 13 | .m-loginmodal .remember_box label{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;line-height:1em;} 14 | .m-loginmodal .remember_box label .u-icon{margin-right:8px;color:#5ed0ba;} 15 | /* 多选框 */ 16 | .m-loginmodal input[type=checkbox]{display:none;} 17 | .m-loginmodal input[type=checkbox] ~ .u-icon-checkboxchecked{display:none;} 18 | .m-loginmodal input[type=checkbox]:checked ~ .u-icon-checkbox{display:none;} 19 | .m-loginmodal input[type=checkbox]:checked ~ .u-icon-checkboxchecked{display:inline-block;} 20 | /* 提交按钮 */ 21 | .m-loginmodal button[type=submit]{margin-top:10px;} 22 | /* 表单样式 */ 23 | .m-form-1 ::placeholder{color:#7ad7c4;} -------------------------------------------------------------------------------- /public/component/LoginModal/LoginModal.js: -------------------------------------------------------------------------------- 1 | 2 | /* 登录弹窗组件 3 | * 使用登录弹窗组件前,要先引入 Modal组件 4 | * 以及 事件管理器、工具函数模块、md5加密库 5 | */ 6 | (function(App){ 7 | 8 | // 模板 9 | var template = `
10 | 11 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 | 28 | 忘记密码? 29 |
30 |
31 | 32 |
33 |
`; 34 | 35 | /* options 参数说明 36 | * { 37 | * parent: dom节点, 父容器(必填) 38 | * } 39 | */ 40 | function LoginModal(options){ 41 | // 弹窗内容模块 42 | this.content = template; 43 | // 继承父类Modal (挂载工作在Modal中完成) 44 | App.Modal.call(this, options); 45 | 46 | // 缓存节点 47 | this.closeBtn = _.getElementsByClassName(this.container, 'close_btn')[0]; // 关闭弹窗按钮 48 | this.goregister = document.getElementById('goregister'); // 立即注册按钮 49 | this.nUsername = _.getElementsByClassName(this.container, 'username_input')[0]; // 用户名输入框 50 | this.nPassword = _.getElementsByClassName(this.container, 'password_input')[0]; // 密码输入框 51 | this.nRemember = _.getElementsByClassName(this.container, 'remember')[0]; // 记住登录状态选框 52 | this.nErrorBox = _.getElementsByClassName(this.container, 'u-error')[0]; // 错误盒子 53 | this.nError = _.getElementsByClassName(this.container, 'errormsg')[0]; // 错误消息提示 54 | this.submitBtn = _.getElementsByClassName(this.container, 'submit_btn')[0]; // 登录按钮 55 | 56 | // 初始化 57 | this.initLoginEvent(); 58 | } 59 | 60 | // 继承父类Modal的原型 61 | LoginModal.prototype = Object.create(App.Modal.prototype); 62 | LoginModal.prototype.constructor = LoginModal; 63 | 64 | // 混入事件管理器 65 | _.extend(LoginModal.prototype, App.emitter); 66 | 67 | // 初始化事件 68 | LoginModal.prototype.initLoginEvent = function(){ 69 | // 订阅显示登录弹窗事件 70 | this.on('showLoginModal', this.show.bind(this)); 71 | // 为关闭按钮,绑定关闭弹窗事件 72 | this.closeBtn.addEventListener('click', this.hide.bind(this)); 73 | // 去注册 74 | this.goregister.addEventListener('click', function(){ 75 | // 关闭登录弹窗 76 | this.hide(); 77 | // 显示注册弹窗 78 | this.emit('showRegisterModal'); 79 | }.bind(this)); 80 | // 绑定提交表单事件 81 | this.submitBtn.addEventListener('click', this.submit.bind(this)); 82 | }; 83 | // 表单验证 84 | LoginModal.prototype.check = function(){ 85 | // 载入 数据验证器 86 | var validator = App.validator; 87 | 88 | var isValid = true, 89 | flag = true; 90 | // 验证用户名 91 | flag = flag && !validator.isEmpty(this.nUsername.value); 92 | flag = flag && validator.isPhone(this.nUsername.value); 93 | // 显示错误 94 | flag ? _.delClassName(this.nUsername, 'error') : _.addClassName(this.nUsername, 'error'); 95 | isValid = isValid && flag; 96 | 97 | // 验证密码 98 | flag = true; 99 | flag = flag && !validator.isEmpty(this.nPassword.value); 100 | // 显示错误 101 | flag ? _.delClassName(this.nPassword, 'error') : _.addClassName(this.nPassword, 'error'); 102 | isValid = isValid && flag; 103 | 104 | isValid || (this.nError.innerText = '账号或密码不正常,请重新输入'); 105 | // 显示错误 106 | this.showError(); 107 | isValid ? _.addClassName(this.nErrorBox, 'f-dn') : this.showError(); 108 | 109 | 110 | return isValid; 111 | }; 112 | // 显示错误 113 | LoginModal.prototype.showError = function(){ 114 | _.delClassName(this.nErrorBox, 'f-dn'); 115 | }; 116 | // 表单提交 117 | LoginModal.prototype.submit = function(event){ 118 | event.preventDefault(); 119 | if(this.check()){ 120 | var data = { 121 | username: this.nUsername.value.trim(), 122 | password: hex_md5(this.nPassword.value), 123 | remember: !!this.nRemember.checked 124 | }; 125 | _.ajax({ 126 | url: '/api/login', 127 | method: 'POST', 128 | header: { 129 | 'content-type': 'application/json' 130 | }, 131 | data: data, 132 | success: function(data){ 133 | data = JSON.parse(data); 134 | console.log(data); 135 | if(data.code === 200){ 136 | this.hide(); 137 | this.emit('login', data.result); 138 | } 139 | else{ 140 | // 根据不同代码错误,显示不同的错误提示 141 | switch(data.code){ 142 | case 400: 143 | this.nError.innerText = '密码错误,请重新输入'; 144 | break; 145 | case 404: 146 | this.nError.innerText = '用户不存在,请重新输入'; 147 | break; 148 | } 149 | this.showError(); 150 | } 151 | }.bind(this), 152 | fail: function(){} 153 | }); 154 | } 155 | }; 156 | 157 | App.LoginModal = LoginModal; 158 | 159 | })(window.App); -------------------------------------------------------------------------------- /public/component/Modal/Modal.css: -------------------------------------------------------------------------------- 1 | /* Modal 组件样式 */ 2 | .m-modal{position:fixed;z-index:999;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);} 3 | /* 内容区 */ 4 | .m-modal > div{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border:1px solid #e4e4e2;background:#fff;} -------------------------------------------------------------------------------- /public/component/Modal/Modal.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | /* 弹窗组件, 7 | * 作为一个基础组件,不能单独使用, 8 | * 继承它的子组件中,才做初始化,绑定事件的操作。 9 | * Modal组件,只提供基本的外框样式,显示、隐藏功能. 10 | */ 11 | (function(App){ 12 | 13 | var template = '
'; 14 | 15 | /* options 参数说明 16 | *{ 17 | * parent: dom节点, 父容器(必填) 18 | * content: 内容str (选填) 19 | *} 20 | */ 21 | function Modal(option){ 22 | // 继承配置 23 | _.extend(this, option); 24 | 25 | // 缓存节点 26 | this.container = _.html2node(template); 27 | // 挂载内容模块 28 | this.container.innerHTML = this.content || ''; 29 | // 挂载到父节点 30 | this.parent.appendChild(this.container); 31 | } 32 | 33 | // 展示 弹窗 34 | Modal.prototype.show = function(){ 35 | _.delClassName(this.container, 'f-dn'); 36 | }; 37 | // 关闭 弹窗 38 | Modal.prototype.hide = function(){ 39 | _.addClassName(this.container, 'f-dn'); 40 | }; 41 | 42 | App.Modal = Modal; 43 | 44 | })(window.App); -------------------------------------------------------------------------------- /public/component/Nav/Nav.css: -------------------------------------------------------------------------------- 1 | /* Nav 组件样式 */ 2 | .m-nav{margin:25px auto 0;width:1080px;} 3 | .m-nav .logo{float:left;margin-top:-3px;margin-right:46px;} 4 | /* 调整User组件相对位置 */ 5 | .m-nav .m-user{margin-top:-25px;padding-top:14px;} 6 | .m-nav .m-user .user_list{padding: 88px 0 0;}/*g-header 高90px - user_list border宽 2px*/ -------------------------------------------------------------------------------- /public/component/Nav/Nav.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | (function(App){ 7 | 8 | // 模板 9 | var template = ``; 12 | 13 | // options 参数说明 14 | // { 15 | // parent: dom节点, 父容器 (必填) 16 | // }) 17 | function Nav(options){ 18 | // 继承配置 19 | _.extend(this, options); 20 | 21 | // 缓存节点 22 | this.container = this._template.cloneNode(true); 23 | 24 | // 初始化 25 | this.init(); 26 | } 27 | 28 | // 混入事件管理器 29 | _.extend(Nav.prototype, App.emitter); 30 | 31 | Nav.prototype._template = _.html2node(template); 32 | 33 | Nav.prototype.init = function(){ 34 | // 挂载组件 35 | // 先挂载 容器组件,不然Tabs的滑块offset定位不准确 36 | this.parent.appendChild(this.container); 37 | // 1.顶栏选项卡 组件 38 | this.hdtab = new App.Tabs({ 39 | parent: this.container, 40 | index:this.getTabIndex(), 41 | nTabData:[ 42 | {name:'首页',url:'/index'}, 43 | {name:'作品',url:'/works'}, 44 | {name:'圈子',url:'javascript:;'}, 45 | {name:'奇思妙想',url:'javascript:;'} 46 | ] 47 | }); 48 | // 2.搜索框 组件 49 | this.search = new App.Search({parent: this.container}); 50 | // 3.未登录显示的客人 组件 51 | this.nGuest = new App.Guest({parent: this.container}); 52 | // 4.已登录显示的用户 组件 53 | this.nUser = new App.User({parent: this.container}); 54 | 55 | // 初始化登录状态 56 | this.initLoginStatus(); 57 | }; 58 | // 初始化登录状态 59 | Nav.prototype.initLoginStatus = function(){ 60 | _.ajax({ 61 | url: '/api/users?getloginuser', 62 | method: 'GET', 63 | success: (function(data){ 64 | data = JSON.parse(data); 65 | console.log(data); 66 | if(data.code === 200){ 67 | // 触发登录事件 68 | this.emit('login', data.result); 69 | } 70 | // 如果不是200,则隐藏User,显示Guest,默认就是如此,无需操作 71 | else{ 72 | // 触发未登录事件 73 | this.emit('notLogin'); 74 | } 75 | }).bind(this), 76 | fail: function(){ 77 | console.log('api/users?getloginuser 失败'); 78 | } 79 | }) 80 | }; 81 | // 获取 tab的选中项的序号 82 | Nav.prototype.getTabIndex = function(){ 83 | // 根据url 的path,决定 tab的index 84 | if(/\/([^\/]+)/.test(location.pathname)){ 85 | switch (RegExp.$1){ 86 | // 首页 87 | case 'index': 88 | return 0; 89 | // 作品页 90 | case 'works': 91 | return 1; 92 | } 93 | } 94 | }; 95 | 96 | App.Nav = Nav; 97 | })(window.App); -------------------------------------------------------------------------------- /public/component/Pagination/Pagination.css: -------------------------------------------------------------------------------- 1 | /* 分页器组件 */ 2 | .m-pagination{margin-top:27px;text-align:center;} 3 | .m-pagination li{display:inline-block;margin-left:25px;font-size:11px;line-height:32px;text-align:center;color:#5eccb6;cursor:pointer;} 4 | /* 当前选中状态 */ 5 | .m-pagination li.z-active{width:29px;height:32px;margin-left:10px;background:url(../../res/images/page-active.png) 50% 50%/100% 100% no-repeat;color:#fff;} 6 | .m-pagination li.z-active + li{margin-left:14px;} 7 | /* 禁止点击状态 */ 8 | .m-pagination li.disable{cursor:no-drop;} -------------------------------------------------------------------------------- /public/component/Pagination/Pagination.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 分页器组件 3 | */ 4 | (function(App){ 5 | 6 | // 默认选中页码 7 | var DEFAULT_CURRENT_PAGE = 1; 8 | // 默认显示页码个数 9 | var DEFAULT_SHOW_NUM = 8; 10 | // 默认每页显示数量 11 | var DEFAULT_ITEMS_LIMIT = 10; 12 | 13 | /** 14 | * options = { 15 | * parent: dom节点 16 | * total: num 总共作品数 17 | * } 18 | */ 19 | function Pagination(options){ 20 | 21 | // 继承配置 22 | _.extend(this, options); 23 | // 当前页 24 | this.current = options.current || DEFAULT_CURRENT_PAGE; 25 | // 显示页码个数 26 | this.showNum = options.showNum || DEFAULT_SHOW_NUM; 27 | // 每页显示数量 28 | this.itemsLimit = options.itemsLimit || DEFAULT_ITEMS_LIMIT; 29 | // 总页数 30 | this.totalNum = Math.ceil(this.total / this.itemsLimit); 31 | 32 | // 缓存节点 33 | this.container = _.html2node('
    '); // 容器节点 34 | 35 | this.render(); 36 | this.addEvent(); 37 | } 38 | 39 | // 渲染分页器,选项卡 40 | Pagination.prototype.render = function(){ 41 | // 起始页 42 | this.startNum = Math.floor((this.current - 1) / this.showNum) * this.showNum + 1; 43 | // 截止页 44 | this.endNum = Math.min(this.startNum + this.showNum - 1, this.totalNum); 45 | 46 | // 分页器结构 47 | var html = `
  • 第一页
  • `; 48 | html += `
  • 上一页
  • `; 49 | for(var i=this.startNum;i<=this.endNum;i++){ 50 | html += `
  • ${i}
  • `; 51 | } 52 | html += `
  • 下一页
  • `; 53 | html += `
  • 尾页
  • `; 54 | this.container.innerHTML = html; 55 | this.parent.appendChild(this.container); 56 | }; 57 | // 切换页码 58 | Pagination.prototype.setStatus = function(page){ 59 | // 更新当前页 60 | this.current = page; 61 | // 刷新分页器 62 | this.render(); 63 | // 重新渲染分页器组件 64 | this.togglePageNum({ 65 | total: 1, 66 | offset: (this.current - 1) * this.itemsLimit, 67 | limit: this.itemsLimit 68 | }); 69 | }; 70 | // 绑定事件 71 | Pagination.prototype.addEvent = function(){ 72 | // 容器节点,事件代理 73 | this.container.addEventListener('click', function(evt){ 74 | // 有页码,且未被禁用时 75 | if(evt.target.dataset.page && ! _.hasClassName(evt.target, 'disable')){ 76 | // 切换页码 77 | this.setStatus(evt.target.dataset.page); 78 | } 79 | }.bind(this)); 80 | }; 81 | 82 | App.Pagination = Pagination; 83 | 84 | })(window.App); -------------------------------------------------------------------------------- /public/component/Profile/Profile.css: -------------------------------------------------------------------------------- 1 | /* 用户简介 组件样式 */ 2 | .m-profile{padding-top:27px;box-sizing:border-box;text-align:center;} 3 | /* 头像 */ 4 | .m-profile .u-avatar{height:111px;background:url(../../res/images/avatar.png) 50% 50%/contain no-repeat;} 5 | /* 用户信息 */ 6 | .m-profile .u-info{margin:12px 0;line-height:1em;font-size:11px;color:#727171;} 7 | /* 名称与性别 */ 8 | .m-profile em,.m-profile span{display:inline-block;} 9 | .m-profile .name{display:inline-block;max-width:75px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:15px;line-height:1em;color:#000;font-weight:900;} 10 | .m-profile .u-icon{font-size:18px;font-weight:900;} 11 | /* 年龄、星座、地址 */ 12 | .m-profile .constellation{margin-left:3px;} 13 | .m-profile .u-icon-address{margin-left:13px;font-size:13px;} -------------------------------------------------------------------------------- /public/component/Profile/Profile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 简介组件 3 | * 依赖 工具函数、事件管理器 4 | */ 5 | 6 | (function(App){ 7 | 8 | var template = Handlebars.compile(`
    9 |
    10 | {{nickname}} 11 | 12 | 13 | 14 |
    15 |
    16 | {{age}}岁 17 | {{zodiac}}座 18 | 19 | 20 | {{city}} 21 | 22 |
    `); 23 | 24 | /** 25 | * options = { 26 | * parent: dom节点 27 | * } 28 | */ 29 | function Profile(options){ 30 | 31 | // 继承配置 32 | _.extend(this, options); 33 | 34 | // 初始化(挂载组件) 35 | this.init(); 36 | 37 | } 38 | 39 | // 混入 事件管理器 40 | _.extend(Profile.prototype, App.emitter); 41 | 42 | var iconConfig = [ 43 | 'u-icon-male', 44 | 'u-icon-female' 45 | ]; 46 | 47 | // 初始化 48 | Profile.prototype.init = function(){ 49 | // 订阅登录事件 50 | this.on('login', function(data){ 51 | // 模板所用数据 52 | var user_info = { 53 | nickname: data.nickname, // 昵称 54 | sex: iconConfig[data.sex], // 性别icon 55 | age: _.calculateAge(data.birthday), // 年龄 56 | city: _.searchCity(ADDRESS_CODES, {province: data.province,city: data.city}), // 城市 57 | zodiac: _.calculateZodiac(data.birthday) // 星座 58 | }; 59 | // 通过数据 生成模板 60 | this.parent.innerHTML = template(user_info); 61 | }.bind(this)); 62 | }; 63 | 64 | App.Profile = Profile; 65 | 66 | })(window.App); -------------------------------------------------------------------------------- /public/component/Radio/Radio.css: -------------------------------------------------------------------------------- 1 | /* 单选框组件 */ 2 | .m-radio li label{margin-top:0;margin-bottom:21px;font-size:11px;line-height:16px;white-space:nowrap;color:#848383;cursor:pointer;} 3 | /* 隐藏单选按钮 */ 4 | .m-radio li input[type=radio]{display:none;} 5 | /* 自定义单选按钮样式 */ 6 | .m-radio li .u-icon{float:left;margin-right:8px;color:#5ed0ba;} 7 | .m-radio li input[type=radio] ~ .u-icon-radio{display:none;} 8 | .m-radio li input[type=radio]:checked ~ .u-icon-radio{display:block;} 9 | .m-radio li input[type=radio]:checked ~ .u-icon-radiocircle{display:none;} -------------------------------------------------------------------------------- /public/component/Radio/Radio.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 单选组件 3 | */ 4 | (function(App){ 5 | 6 | var template = `
      `; 7 | 8 | /** 9 | * options = { 10 | * parent: dom节点 (必填) 父容器节点 11 | * data: {name: str, list:[{text: str, value: num}...]} (必填)单选数据(名称、文本、值) 12 | * default_checkindex: num (默认选中)序号 13 | * } 14 | */ 15 | function Radio(options){ 16 | // 继承配置 17 | _.extend(this, options); 18 | 19 | // 缓存节点 20 | this.container = _.html2node(template); 21 | 22 | // 初始化 23 | this.init(); 24 | } 25 | 26 | // 初始化 27 | Radio.prototype.init = function(){ 28 | // 渲染单选框 29 | this.render(this.data); 30 | 31 | // 绑定事件 32 | this.container.addEventListener('change', this.setChecked.bind(this)); 33 | 34 | // 挂载组件 35 | this.parent.appendChild(this.container); 36 | }; 37 | // 渲染单选框 38 | Radio.prototype.render = function(data){ 39 | var html = ''; 40 | if(Object.prototype.toString.call(data.list).slice(8, -1) === 'Array'){ 41 | data.list.forEach(function(item){ 42 | html += `
    • 43 | 47 |
    • `; 48 | }); 49 | }; 50 | this.container.innerHTML = html; 51 | // 设置默认 选中值 52 | this.list = this.container.children; 53 | if(this.list.length > 0){ 54 | this.default_checkindex = this.default_checkindex || 0; 55 | this.list[this.default_checkindex].getElementsByTagName('input')[0].checked = true; 56 | this.value = this.list[this.default_checkindex].getElementsByTagName('input')[0].value; 57 | } 58 | }; 59 | Radio.prototype.setChecked = function(evt){ 60 | this.value = evt.target.value; 61 | }; 62 | // 获取数据 63 | Radio.prototype.getValue = function(){ 64 | return this.value; 65 | }; 66 | 67 | App.Radio = Radio; 68 | 69 | })(window.App); -------------------------------------------------------------------------------- /public/component/RegisterModal/RegisterModal.css: -------------------------------------------------------------------------------- 1 | /* 注册登录弹窗 组件样式 */ 2 | .m-registermodal{position:relative;padding:39px 136px 42px 106px;} 3 | /* 关闭弹窗按钮 */ 4 | .m-registermodal .close_btn{position:absolute;right:9px;top:9px;font-size:15px;color:#c0c0c1;cursor:pointer;} 5 | /* logo */ 6 | .m-registermodal .logo{width:214px;} 7 | /* 表单 */ 8 | .m-registermodal .m-form{width:345px;} 9 | .m-registermodal .u-formitem .formitem_tt{margin-left:19px;} 10 | .m-registermodal .u-formitem .formitem_ct{width:266px;height:40px;} 11 | /* 单选框 */ 12 | .m-registermodal .sex_box{display:-webkit-flex;display:flex;height:100%;} 13 | .m-registermodal .sex_box label{display:-webkit-flex;display:flex;-webkit-flex:1;flex:1;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;align-items:center;} 14 | .m-registermodal input[type=radio]{display:none;}/* 隐藏单选框 */ 15 | .m-registermodal input[type=radio] ~ .u-icon{margin-right:16px;color:#5ed0ba;} 16 | .m-registermodal input[type=radio] ~ .u-icon-radio{display:none;}/* 构造伪单选框 */ 17 | .m-registermodal input[type=radio]:checked ~ .u-icon-radio{display:inline-block;}/* 构造伪单选框 */ 18 | .m-registermodal input[type=radio]:checked ~ .u-icon-radiocircle{display:none;}/* 构造伪单选框 */ 19 | /* 级联选择器 */ 20 | .m-registermodal .birthday_select .select_opt{z-index:99;}/* 上方的生日选择器,层级要高于下方的日期选择器 */ 21 | .m-registermodal .location_select .select_opt{z-index:9;} 22 | /* 二维码 */ 23 | .m-registermodal .formitem_ct-validate .u-ipt{float:left;height:100%;width:calc(100% - 94px);border-radius:4px 0 0 4px;box-sizing:border-box;} 24 | .m-registermodal .formitem_ct-validate img{float:right;height:100%;width:94px;border:1px solid #5ed0ba;border-left:none;box-sizing:border-box;border-radius:0 4px 4px 0;cursor:pointer;} 25 | /* 同意条款 */ 26 | .m-registermodal .terms label{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;font-size:12px;line-height:2em;cursor:pointer;} 27 | .m-registermodal .terms label span{color:#2ea7e0;} 28 | /* 复选框 */ 29 | .m-registermodal input[type=checkbox]{display:none;}/* 隐藏原生复选框 */ 30 | .m-registermodal input[type=checkbox] ~ .u-icon{margin-right:8px;color:#5ed0ba;} 31 | .m-registermodal input[type=checkbox] ~ .u-icon-checkboxchecked{display:none;}/* 构造伪复选框 */ 32 | .m-registermodal input[type=checkbox]:checked ~ .u-icon-checkboxchecked{display:inline-block;}/* 构造伪复选框 */ 33 | .m-registermodal input[type=checkbox]:checked ~ .u-icon-checkbox{display:none;}/* 构造伪复选框 */ 34 | /* 注册按钮 */ 35 | .m-registermodal button[type=submit]{width:100%;height:40px;line-height:40px;margin-top:15px;} -------------------------------------------------------------------------------- /public/component/RegisterModal/RegisterModal.js: -------------------------------------------------------------------------------- 1 | 2 | /* 注册弹窗组件 3 | * 使用注册弹窗组件前,要先引入 Modal组件、CascadeSelect组件 4 | * 以及 事件管理器、工具函数模块、md5加密库 5 | */ 6 | (function(App){ 7 | 8 | // 模板 9 | var template = `
      10 | 11 |
      12 |
      13 |
      14 | 15 | 16 |
      17 |
      18 | 19 | 20 |
      21 |
      22 | 23 | 24 |
      25 |
      26 | 27 | 28 |
      29 |
      30 | 31 |
      32 |
      33 | 37 | 41 |
      42 |
      43 |
      44 |
      45 | 46 |
      47 |
      48 |
      49 |
      50 |
      51 | 52 |
      53 |
      54 |
      55 |
      56 |
      57 | 58 |
      59 | 60 | 61 |
      62 |
      63 |
      64 | 70 |
      71 |
      72 | 73 |
      74 |
      `; 75 | 76 | /* options 参数说明 77 | * { 78 | * parent: dom节点, 父容器(必填) 79 | * } 80 | */ 81 | function RegisterModal(options){ 82 | // 弹窗内容模块 83 | this.content = template; 84 | // 继承父类Modal (挂载工作在Modal中完成) 85 | App.Modal.call(this, options); 86 | 87 | // 缓存节点 88 | this.closeBtn = _.getElementsByClassName(this.container, 'close_btn')[0]; // 关闭按钮 89 | this.captchaImg = document.getElementById('captchaimg'); // 二维码图片 90 | this.phone = document.getElementById('phone'); // 电话input 91 | this.nick = document.getElementById('nickname'); // 昵称input 92 | this.pwd = document.getElementById('password'); // 密码input 93 | this.confirmpwd = document.getElementById('comform_password'); // 确认密码input 94 | this.captcha = document.getElementById('captcha'); // 二维码input 95 | this.agree2terms = document.getElementById('agree2terms'); // 同意条款input 96 | this.errorBox = _.getElementsByClassName(this.container, 'u-error')[0]; // 错误盒子 97 | this.nError = document.getElementById('errormsg'); // 错误消息提示 98 | this.submitBtn = document.getElementById('submit'); // 提交按钮 99 | 100 | // 初始化 101 | this.initSelect(); 102 | this.initRegisterEvent(); 103 | } 104 | 105 | // 继承父类Modal的原型 106 | RegisterModal.prototype = Object.create(App.Modal.prototype); 107 | RegisterModal.prototype.constructor = RegisterModal; 108 | 109 | // 混入事件管理器 110 | _.extend(RegisterModal.prototype, App.emitter); 111 | 112 | // 初始化选择器 113 | RegisterModal.prototype.initSelect = function(){ 114 | // 生日 级联选择器 115 | this.birthdaySelect = new App.CascadeSelect({ 116 | parent: _.getElementsByClassName(this.container, 'birthday_select')[0], 117 | // 生日数据(为了让 生日和地址 可以共用一个级联选择器组件,则构造相同的数据结构) 118 | data: _.createDateData() 119 | }); 120 | // 地址 级联选择器 121 | this.locationSelect = new App.CascadeSelect({ 122 | parent: _.getElementsByClassName(this.container, 'location_select')[0], 123 | // 地址数据 124 | data: _.toSelectData(ADDRESS_CODES) 125 | }); 126 | }; 127 | 128 | // 重置验证码 129 | RegisterModal.prototype.resetCaptcha = function(){ 130 | this.captchaImg.src = `/captcha?t=${new Date().getTime()}` 131 | }; 132 | // 表单验证 133 | RegisterModal.prototype.check = function(){ 134 | var isValid = true, 135 | errorMsg = ""; 136 | 137 | // 隐藏错误信息框 138 | _.addClassName(this.errorBox, 'f-dn'); 139 | 140 | // 验证数据填写 是否符合规范 141 | var checkList = [ 142 | [this.phone, ['require', 'phone']], 143 | [this.nick, ['require', 'nickname']], 144 | [this.pwd, ['require', 'length']], 145 | [this.confirmpwd, ['require', 'length']], 146 | [this.captcha, ['require']] 147 | ]; 148 | isValid = this.checkRules(checkList); 149 | if(!isValid){ 150 | errorMsg = '输入有误'; 151 | } 152 | // 验证两次密码 153 | if(isValid && this.pwd.value !== this.confirmpwd.value){ 154 | isValid = false; 155 | errorMsg = '2次密码不一致'; 156 | } 157 | 158 | // 验证条款是否为空 159 | if(isValid && !this.agree2terms.checked){ 160 | isValid = false; 161 | errorMsg = '未同意条款'; 162 | } 163 | 164 | // 显示错误 165 | if(!isValid){ 166 | this.nError.innerText = errorMsg; 167 | _.delClassName(this.errorBox, 'f-dn'); 168 | } 169 | // 返回结果 170 | return isValid; 171 | }; 172 | // 按规则验证表单 173 | RegisterModal.prototype.checkRules = function(checkRules){ 174 | // 载入 数据验证器 175 | var validator = App.validator; 176 | // 验证结果 177 | var check_result = true; 178 | 179 | for(var i=0;i 10 | 14 | `; 15 | 16 | // options 参数说明 17 | // { 18 | // parent: dom节点, 父容器 (必填) 19 | // }) 20 | function Search(options){ 21 | // 继承配置 22 | _.extend(this, options); 23 | 24 | // 缓存节点 25 | this.nForm = this._template.cloneNode(true); 26 | this.nKeyword = this.nForm.getElementsByTagName('input')[0]; 27 | 28 | // 初始化 29 | this.init(); 30 | } 31 | 32 | // 用于复用的dom节点 33 | Search.prototype._template = _.html2node(template); 34 | 35 | // 初始化(绑定事件, 将组件载入页面) 36 | Search.prototype.init = function(){ 37 | // 绑定事件 38 | this.nForm.addEventListener('submit', this.search.bind(this)); 39 | // 挂载组件 40 | this.parent.appendChild(this.nForm); 41 | } 42 | Search.prototype.search = function(event){ 43 | // 清除空格 44 | this.nKeyword.value = this.nKeyword.value.trim(); 45 | // 验证表单是否为空 46 | if(!this.nKeyword.value){ 47 | // 不提交表单 48 | event.preventDefault(); 49 | } 50 | } 51 | 52 | App.Search = Search; 53 | 54 | })(window.App); -------------------------------------------------------------------------------- /public/component/Section/Section.css: -------------------------------------------------------------------------------- 1 | /* Section 组件样式 */ 2 | .m-section{margin-bottom:29px;} 3 | .m-section .u-icon{font-size:32px;color:#5ecbb5;} 4 | .m-section .section_head{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;-webkit-align-items:center;padding-left:30px;padding-right:28px;height:72px;border:1px solid #e4e5e5;box-sizing:border-box;background:#fff;} 5 | .m-section .section_head span{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;font-size:13px;} 6 | .m-section .section_head span i{margin-right:14px;} 7 | .m-section .section_head span.section_more{color:#727171;cursor:pointer;} 8 | .m-section .section_head span.section_more i{margin-right:0;margin-left:10px;} 9 | 10 | /* m-list水平布局列表 */ 11 | .m-list{border:1px solid #e4e5e5;background:#fff;} 12 | .m-list ul{margin-left:-12px;padding:0 12px 12px;} 13 | .m-list li{float:left;padding-left:12px;padding-top:12px;width:25%;box-sizing:border-box;} 14 | .m-list li img{display:block;width:100%;} 15 | .m-list li div{margin-top:6px;margin-bottom:7px;font-size:10px;line-height:1em;} 16 | 17 | /* m-list 样式拓展 */ 18 | .m-list-1 li{width:100%;} 19 | .m-list-2 li{width:50%;} 20 | .m-list-4 li{width:25%;} 21 | .m-list-5 li{width:20%;} 22 | -------------------------------------------------------------------------------- /public/component/Section/Section.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | (function(App){ 7 | 8 | /* options 参数说明 9 | *{ 10 | * parent: dom节点, 父容器(必填) 11 | * icon: str, 12 | * title: str, 模块标题 (必填) 13 | * contentElem: dom元素, 内容区dom (选填) 14 | *} 15 | */ 16 | function Section(options){ 17 | // 继承配置 18 | _.extend(this, options); 19 | 20 | var _template = App.template.m_section(this); 21 | 22 | // 缓存节点 23 | this.section = _.html2node(_template); 24 | 25 | // 初始化 26 | this.init(); 27 | } 28 | 29 | // 初始化 30 | Section.prototype.init = function(){ 31 | // 挂载组件 32 | this.parent.appendChild(this.section); 33 | // 替换section 内容结构 34 | if(this.contentElem){ 35 | var section_cnt = _.getElementsByClassName(this.section, 'section_cnt')[0]; 36 | section_cnt.parentNode.replaceChild(this.contentElem, section_cnt); 37 | } 38 | } 39 | 40 | App.Section = Section; 41 | 42 | })(window.App); -------------------------------------------------------------------------------- /public/component/Select/Select.css: -------------------------------------------------------------------------------- 1 | /* 选择器 组件样式 */ 2 | .m-select{position:relative;font-size:12px;text-indent:10px;cursor:pointer;} 3 | .m-select .select_hd{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;align-items:center;height:100%;border:1px solid #5ed0ba;-webkit-border-radius:4px;border-radius:4px;box-sizing:border-box;} 4 | .m-select .select_hd span{display:block;} 5 | .m-select .select_hd .select_val{overflow:hidden;} 6 | .m-select .select_hd .u-icon{margin-right:6px;text-indent:0;color:#5ed0ba;} 7 | .m-select .select_opt{position:absolute;top:100%;left:0;width:100%;height:120px;border:1px solid #f4f4f4;background:#fff;overflow-y:auto;} 8 | .m-select .select_opt::-webkit-scrollbar{display:none;} 9 | .m-select .select_opt li{line-height:30px;overflow-x:hidden;white-space:nowrap;text-overflow:ellipsis;} 10 | .m-select .select_opt li:hover{background-color:#f4f4f4;} 11 | .m-select .select_opt li.z-select{background-color:#f4f4f4;} -------------------------------------------------------------------------------- /public/component/Select/Select.js: -------------------------------------------------------------------------------- 1 | // 选择器组件 2 | (function(App){ 3 | 4 | // 模板 5 | var template = `
      6 |
      7 | 8 | 9 |
      10 |
        11 |
        `; 12 | 13 | /* options 参数说明 14 | *{ 15 | * parent: dom节点, 父容器(必填) 16 | *} 17 | */ 18 | function Select(options){ 19 | // 继承配置 20 | _.extend(this, options); 21 | 22 | // 缓存节点 23 | // 容器 24 | this.body = _.html2node(template); 25 | // 下拉列表节点 26 | this.nOption = _.getElementsByClassName(this.body, 'select_opt')[0]; 27 | // 显示选中文本节点 28 | this.nValue = _.getElementsByClassName(this.body, 'select_val')[0]; 29 | 30 | // 初始化 31 | this.init(); 32 | } 33 | 34 | // 混入事件管理器 35 | _.extend(Select.prototype, App.emitter); 36 | 37 | // 0. 渲染下拉列表 38 | Select.prototype.render = function(data, defaultIndex){ // data: [{name:,value:,list:}] 39 | // 更新下拉列表 40 | var optionsHTML = ''; 41 | data = data || []; // 若data为null,则默认空数组 42 | for(var i = 0;i < data.length;i++){ 43 | // 格式化数据{name:,value:} 44 | optionsHTML += `
      • ${data[i].name}
      • ` 45 | } 46 | this.nOption.innerHTML = optionsHTML; 47 | // 缓存下拉选项节点 48 | this.nOptions = this.nOption.children; 49 | // 缓存下拉选项数据 50 | this.options = data; 51 | // 置空选中项 52 | this.selectedIndex = undefined; 53 | // 默认选中第一项 54 | this.setSelect(defaultIndex || 0); 55 | }; 56 | // 1. 设置选中选项 57 | Select.prototype.setSelect = function(index){ 58 | // 取消上次选中效果 59 | if(this.selectedIndex !== undefined){ 60 | _.delClassName(this.nOptions[this.selectedIndex], 'z-select'); 61 | } 62 | // 为本次选中设置效果 63 | this.selectedIndex = index; 64 | // 防止被选列表为空数组报错 65 | if(this.nOptions.length > 0){ 66 | _.addClassName(this.nOptions[this.selectedIndex], 'z-select'); 67 | // 更新对外显示的选中内容 68 | this.nValue.innerText = this.options[this.selectedIndex].name; 69 | } 70 | else{ 71 | this.nValue.innerText = ''; 72 | } 73 | // 触发select事件 74 | this.emit('select', { 75 | value: this.getValue(), // 数据值(如:地址编码) 76 | target: this, // 触发事件的选择器 本身,用于辅助判定级联选择器中,哪些选择器响应 77 | index: this.selectedIndex // 选中选项的序号,用于找出下级选择器 下拉菜单的数据 78 | }); 79 | }; 80 | // 获取选项值 81 | Select.prototype.getValue = function(){ 82 | return typeof this.options[this.selectedIndex] !== 'undefined' ? this.options[this.selectedIndex].value : ''; 83 | }; 84 | 85 | // 2. 切换展开、关闭下拉列表 86 | Select.prototype.toggle = function(){ 87 | // 若关闭,则展开;若展开,则关闭 88 | _.hasClassName(this.nOption, 'f-dn') ? this.open() : this.close(); 89 | }; 90 | // 展开下拉列表 91 | Select.prototype.open = function(){ 92 | _.delClassName(this.nOption, 'f-dn'); 93 | }; 94 | // 关闭下拉列表 95 | Select.prototype.close = function(){ 96 | _.addClassName(this.nOption, 'f-dn'); 97 | }; 98 | 99 | // 3. 与选择器的交互 100 | Select.prototype.clickHandler = function(evt){ 101 | // 若选中的是li,则触发setSelect 102 | evt.target.dataset.index !== undefined ? this.setSelect(evt.target.dataset.index) : null; 103 | // 展开/关闭 104 | this.toggle(); 105 | }; 106 | 107 | // 4. 绑定事件 108 | Select.prototype.initEvent = function(){ 109 | // 点击选择器,触发交互事件 110 | this.body.addEventListener('click', this.clickHandler.bind(this)); 111 | // 点击选择器外部,触发关闭选择器事件 112 | document.addEventListener('click', function(evt){ 113 | // 若是从选择器组件 冒泡出来的,则直接退出 114 | try{ 115 | // 若浏览器 支持 event.path属性,用此写法 116 | for(var i=0;i`; 10 | 11 | /* options 参数说明 12 | *{ 13 | * parent: dom节点, 父容器(必填) 14 | * imgArray: [], 图片src数组 (必填) 15 | * interval: num, 轮播间隔时间 (选填, 默认5000) 16 | *} 17 | */ 18 | function Slider(options){ 19 | 20 | // 继承配置 21 | _.extend(this, options); 22 | this.imgLength = this.imgArray.length; 23 | this.interval = this.interval || 5000; // 若未设置 轮播间隔,默认5000 24 | 25 | // 缓存节点 26 | this.slider = this._template.cloneNode(true); 27 | this.sliders = this.buildSlider(); 28 | this.cursors = this.buildCursors(); 29 | 30 | // 初始化 31 | this.init(); 32 | 33 | } 34 | 35 | // 用于复制的节点 36 | Slider.prototype._template = _.html2node(template); 37 | 38 | // 构建轮播图节点 39 | Slider.prototype.buildSlider = function(){ 40 | var slider_ul = _.html2node('
          '); 41 | var html = ''; 42 | // 轮播图 43 | this.imgArray.forEach(function(item, index){ 44 | html += `
        • ` 45 | }); 46 | slider_ul.innerHTML = html; 47 | // 挂载到 Slider组件 48 | this.slider.appendChild(slider_ul); 49 | 50 | return slider_ul.children; 51 | } 52 | 53 | // 构建指示器节点 54 | Slider.prototype.buildCursors = function(){ 55 | var cursor = _.html2node(`
            `); 56 | var html = ''; 57 | // 添加指示器 按钮 58 | for(var i = 0;i < this.imgLength;i++){ 59 | html += `
          • `; 60 | } 61 | cursor.innerHTML = html; 62 | // 将指示器 挂载到 Slider组件 63 | this.slider.appendChild(cursor); 64 | 65 | cursor.addEventListener('click', function(evt){ 66 | // 若点击的是li,而不是ul 67 | if(typeof evt.target.dataset.index !== 'undefined'){ 68 | // 点击指示器节点按钮,跳到对应轮播 69 | this.nav(evt.target.dataset.index); 70 | } 71 | }.bind(this)); 72 | 73 | return cursor.children; 74 | } 75 | 76 | // 初始化 77 | Slider.prototype.init = function(){ 78 | // 绑定事件 79 | this.slider.addEventListener('mouseenter', this.stop.bind(this)); 80 | this.slider.addEventListener('mouseleave', this.autoPlay.bind(this)); 81 | // 挂载组件 82 | this.parent.appendChild(this.slider); 83 | // 初始化动作 84 | this.nav(this.initIndex || 0); 85 | this.autoPlay(); 86 | } 87 | 88 | // 下一页 89 | Slider.prototype.next = function(){ 90 | var index = (this.index + 1) % this.imgLength; 91 | this.nav(index); 92 | } 93 | 94 | // 跳到指定页 95 | Slider.prototype.nav = function(index){ 96 | // 若未改变index, 则不做任何操作 97 | if(this.index === index) return; 98 | // 保存上一页 99 | this.last = this.index; 100 | this.index = index; 101 | // 过渡动画 102 | this.fade(); 103 | this.setCurrent(); 104 | } 105 | 106 | // 轮播效果 107 | Slider.prototype.fade = function(){ 108 | // 若存在上一页 109 | if(typeof this.last !== 'undefined'){ 110 | // 上一页隐藏 111 | this.sliders[this.last].style.opacity = 0; 112 | } 113 | // 当前页显示 114 | this.sliders[this.index].style.opacity = 1; 115 | } 116 | 117 | // 设置当前选中状态 118 | Slider.prototype.setCurrent = function(){ 119 | // 若存在上一页 120 | if(typeof this.last !== 'undefined'){ 121 | // 除去上一节点的选中状态 122 | _.delClassName(this.sliders[this.last], 'z-active'); 123 | _.delClassName(this.cursors[this.last], 'z-active'); 124 | } 125 | // 添加当前选中节点的选中状态 126 | _.addClassName(this.sliders[this.index], 'z-active'); 127 | _.addClassName(this.cursors[this.index], 'z-active'); 128 | } 129 | 130 | // 自动播放 131 | Slider.prototype.autoPlay = function(){ 132 | this.timer = setInterval(this.next.bind(this), this.interval); 133 | } 134 | // 停止自动播放 135 | Slider.prototype.stop = function(){ 136 | clearInterval(this.timer); 137 | } 138 | 139 | App.Slider = Slider; 140 | 141 | })(window.App); -------------------------------------------------------------------------------- /public/component/StarList/StarList.css: -------------------------------------------------------------------------------- 1 | /* StarList 组件样式 */ 2 | .m-list .m-cardul{margin:0;padding:12px 0;} 3 | /* li单元 */ 4 | .m-cardul .m-card{display:-webkit-flex;display:flex;margin-left:12px;padding:14px 27px 11px 22px;width:calc(50% - 18px);border:1px solid #e9e9e9;-webkit-align-items:center;align-items:center;box-sizing:border-box;} 5 | /* 头像 */ 6 | .m-card .card_avatar{width:68px;height:68px;-webkit-border-radius:50%;border-radius:50%;} 7 | /* 信息 */ 8 | .m-card .card_info{-webkit-flex:1;flex:1;margin-left:24px;} 9 | .m-card .card_info div{max-width:155px;font-size:13px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} 10 | .m-card .card_info div:nth-of-type(1){margin-bottom:16px;} 11 | .m-card .card_info div span{font-size:9px;font-weight:200;} 12 | .m-card .card_info div span + span{margin-left:15px;} 13 | /* 关注按钮 */ 14 | .m-card .u-btn-sm{display:-webkit-flex;display:flex;width:62px;height:26px;-webkit-justify-content:center;justify-content:center;font-size:11px;line-height:1em;} 15 | .m-card .u-btn{position:relative;background:#5ed0ba;color:#fff;} 16 | .m-card .u-btn.z-follow{background:#fff;color:#5ed0ba;} 17 | .m-card .u-btn.z-follow:hover:after{content:'取消关注';position:absolute;left:0;right:0;top:0;bottom:0;background:#5ed0ba;color:#fff;line-height:24px;} 18 | .m-card .u-btn .u-icon{font-size:12px;color:#fff;} 19 | .m-card .u-btn.z-follow .u-icon{color:#5ed0ba;} 20 | .m-card .u-icon-add{margin-right:5px;} -------------------------------------------------------------------------------- /public/component/StarList/StarList.js: -------------------------------------------------------------------------------- 1 | 2 | /* 登录弹窗组件 3 | * 使用登录弹窗组件前,要先引入 Modal组件 4 | * 以及 事件管理器、工具函数模块、md5加密库 5 | */ 6 | (function(App){ 7 | 8 | // 模板 9 | var template = `
            10 |
              11 |
            12 |
            `; 13 | 14 | /** 15 | * options = { 16 | * loaction: dom节点,此组件载入的位置(必填) 17 | * } 18 | */ 19 | function StarList(options){ 20 | 21 | // 继承配置 22 | _.extend(this, options); 23 | 24 | // 缓存节点 25 | this.container = _.html2node(template); 26 | this.ul = this.container.getElementsByTagName('ul')[0]; 27 | 28 | // 初始化 29 | this.init(); 30 | } 31 | 32 | // 混入事件管理器 33 | _.extend(StarList.prototype, App.emitter); 34 | 35 | // 获取明日之星数据 36 | StarList.prototype.getstarlist = function(){ 37 | _.ajax({ 38 | url: '/api/users?getstarlist', 39 | method: 'GET', 40 | success: function(data){ 41 | data = JSON.parse(data); 42 | if(data.code === 200){ 43 | this.render(data.result); 44 | } 45 | }.bind(this), 46 | fail: function(){} 47 | }); 48 | }; 49 | // 渲染列表 50 | StarList.prototype.render = function(data){ 51 | var html = ''; 52 | data.forEach(function(item, index){ 53 | html += this.renderItem(item, index); 54 | }.bind(this)); 55 | this.ul.innerHTML = html; 56 | }; 57 | var followConfig = [ 58 | { 59 | class: '', 60 | icon: 'u-icon-add', 61 | text: '关注' 62 | }, 63 | { 64 | class: 'z-follow', 65 | icon: 'u-icon-right', 66 | text: '已关注' 67 | } 68 | ]; 69 | // 渲染m-card 70 | StarList.prototype.renderItem = function(data, index){ 71 | var config = followConfig[Number(!!data.isFollow)]; 72 | var html = ` 73 |
          • 74 | 75 |
            76 |
            ${data.nickname}
            77 |
            作品 ${data.workCount}粉丝 ${data.followCount}
            78 |
            79 | 82 |
          • `; 83 | return html; 84 | }; 85 | // 关注按钮事件管理 86 | StarList.prototype.followHandler = function(evt){ 87 | var target = evt.target; 88 | // 若点中 加号 小图标 89 | if(target.tagName !== 'BUTTON' && target.parentNode.tagName === 'BUTTON' ){ 90 | target = target.parentNode; 91 | } 92 | console.dir(target); 93 | if(target.tagName === 'BUTTON'){ 94 | // 未登录的情况 95 | if(target.dataset.loginstatus === 'false'){ 96 | // 弹出登录弹窗 97 | this.emit('showLoginModal'); 98 | return; 99 | } 100 | // 已登录的情况 101 | var data; 102 | // data 点击用户的信息 103 | data = { 104 | id: target.dataset.userid, 105 | index: target.dataset.index, 106 | nickname: target.dataset.nickname, 107 | workCount: target.dataset.workcount, 108 | followCount: target.dataset.followcount 109 | }; 110 | // 已关注 111 | if(_.hasClassName(target, 'z-follow')){ 112 | this.unFollow(data, target.parentNode); 113 | } 114 | // 未关注 115 | else{ 116 | this.follow(data, target.parentNode); 117 | } 118 | } 119 | }; 120 | // 关注 121 | StarList.prototype.follow = function(followInfo, replaceNode){ 122 | _.ajax({ 123 | url: '/api/users?follow', 124 | method: 'POST', 125 | data: {id: followInfo.id}, 126 | header: {'content-type': 'application/json'}, 127 | success: function(data){ 128 | data = JSON.parse(data); 129 | console.log(data); 130 | if(data.code === 200){ 131 | followInfo.isFollow = true; // 状态变成 已关注 132 | followInfo.followCount ++; // 关注人数 +1 133 | var newNode = _.html2node(this.renderItem(followInfo, followInfo.index)); 134 | replaceNode.parentNode.replaceChild(newNode, replaceNode); 135 | } 136 | }.bind(this), 137 | fail: function(err){ 138 | console.log(err); 139 | } 140 | }); 141 | }; 142 | // 取消关注 143 | StarList.prototype.unFollow = function(followInfo, replaceNode){ 144 | _.ajax({ 145 | url: '/api/users?unfollow', 146 | method: 'POST', 147 | data: {id: followInfo.id}, 148 | header: {'content-type': 'application/json'}, 149 | success: function(data){ 150 | data = JSON.parse(data); 151 | console.log(data); 152 | if(data.code === 200){ 153 | followInfo.isFollow = false; 154 | followInfo.followCount --; 155 | var newNode = _.html2node(this.renderItem(followInfo, followInfo.index)); 156 | replaceNode.parentNode.replaceChild(newNode, replaceNode); 157 | } 158 | }.bind(this), 159 | fail: function(err){ 160 | console.log(err); 161 | } 162 | }); 163 | }; 164 | 165 | // 初始化 166 | StarList.prototype.init = function(){ 167 | // 初始化 明日之星列表 168 | this.getstarlist(); 169 | // 绑定事件 170 | this.ul.addEventListener('click', this.followHandler.bind(this)); // 绑定关注事件 171 | // 订阅事件 172 | this.on('login', this.getstarlist.bind(this)); // 登录时,刷新明日之星列表 173 | // 挂载组件 174 | this.location.parentNode.replaceChild(this.container, this.location); 175 | }; 176 | 177 | App.StarList = StarList; 178 | })(window.App); -------------------------------------------------------------------------------- /public/component/Tabs/Tabs.css: -------------------------------------------------------------------------------- 1 | /* Tabs 组件样式 */ 2 | /* 选项卡 */ 3 | .m-tabs{float:left;} 4 | .m-tabs ul{position:relative;/* 设置relative 让li的offsetParent为ul */font-size:11px;} 5 | .m-tabs ul li{display:inline-block;} 6 | .m-tabs ul li a{display:block;height: 33px;padding: 5px 27px 12px;box-sizing:border-box;font-size:11px;color:#dcdddd;} 7 | .m-tabs ul li.z-active a{color:#5ed0ba;} 8 | /* 滑动条 */ 9 | .m-tabs .tabs_track{position:relative;height:4px;border-radius:2px;background:#dcdddd;} 10 | .m-tabs .tabs_thumb{position:absolute;height:4px;border-radius:2px;background:#5ed0ba;transition:ease 0.1s;} 11 | 12 | /* 扩展 */ 13 | .m-tabs-aside ul li a{padding:26px 13px 20px;height:auto;} -------------------------------------------------------------------------------- /public/component/Tabs/Tabs.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | // 封装Tabs组件 7 | (function(App){ 8 | 9 | // 模板 10 | var _template = Handlebars.compile(`
            11 |
              12 | {{#each nTabData}} 13 |
            • {{name}}
            • 14 | {{/each}} 15 |
            16 |
            17 |
            18 |
            19 |
            `); 20 | 21 | /** options 参数说明 22 | * { 23 | * parent: dom节点, 父容器 (必填) 24 | * index: num类型,Tab选中项的序号 25 | * nTabData: [ 选项卡数据 (必填) 26 | * { 27 | * name: str, tab名称 28 | * url: str, url地址 29 | * } 30 | * ], 31 | * list_type: str, Tab的扩展样式 32 | * } 33 | */ 34 | function Tabs(options){ 35 | // 继承配置 36 | _.extend(this, options); 37 | 38 | this.index = this.index || 0; 39 | // 缓存节点 40 | // 容器 41 | this.container = _.html2node(_template(this)); 42 | // Tab 43 | this.nTab = this.container.getElementsByTagName('ul')[0]; 44 | this.nTabs = this.nTab.children; 45 | // 滑动条 46 | this.nThumb = _.getElementsByClassName(this.container, 'tabs_thumb')[0]; 47 | 48 | // 初始化 49 | this.init(); 50 | } 51 | 52 | // 初始化(绑定事件, 将组件载入页面) 53 | Tabs.prototype.init = function(){ 54 | // 绑定事件 55 | for(var i = 0; i < this.nTabs.length; i++){ 56 | // 1, 鼠标hover时,设定highlight样式的选项 57 | this.nTabs[i].addEventListener('mouseenter', (function(index){ 58 | this.highlight(index); 59 | }).bind(this, i)); 60 | // 2, 点击时,设定tabs的选中项 61 | this.nTabs[i].addEventListener('click', (function(index){ 62 | this.setCurrent(index); 63 | }).bind(this, i)); 64 | } 65 | // 3, 鼠标离开tabs时,保持highlight 66 | this.nTab.addEventListener('mouseleave', (function(){ 67 | this.highlight(this.index); 68 | }).bind(this)); 69 | 70 | // 将组件载入页面(先挂载组件,在获取offset值,不然为0) 71 | this.parent.appendChild(this.container); 72 | // 初始化 73 | this.setCurrent(this.index); 74 | }; 75 | // 设置当前选中tab 76 | Tabs.prototype.setCurrent = function(index){ 77 | // 移除原选中tab的css类 78 | _.delClassName(this.nTabs[this.index], 'z-active'); 79 | // 更新选中的index 80 | this.index = index; 81 | // 为新选中的tab添加css类 82 | _.addClassName(this.nTabs[this.index], 'z-active'); 83 | // 实现选中tab的highlight样式 84 | this.highlight(this.index); 85 | }; 86 | // 设置选中样式 87 | Tabs.prototype.highlight = function(index){ 88 | // 获取选中的tab元素 89 | var tab = this.nTabs[index]; 90 | this.nThumb.style.width = tab.offsetWidth + 'px'; 91 | this.nThumb.style.left = tab.offsetLeft + 'px'; 92 | }; 93 | App.Tabs = Tabs; 94 | 95 | })(window.App); -------------------------------------------------------------------------------- /public/component/Tag/Tag.css: -------------------------------------------------------------------------------- 1 | /* 标签组件 样式 */ 2 | .m-tag{margin-top:23px;padding-left:161px;} 3 | .m-tag li{display:inline-block;border:1px solid #5ed0ba;border-radius:3px;text-align:center;cursor:pointer;} 4 | /* 选中标签组 */ 5 | .m-tag .tag_title{float:left;width:66px;height:26px;line-height:26px;font-size:12px;font-weight:500;} 6 | .m-tag .tag_list{overflow:hidden;} 7 | .m-tag .tag_list li{position:relative;margin-left:10px;margin-bottom:8px;padding:0 10px;height:26px;line-height:26px;font-size:10px;color:#5ed0ba;background-color:#fff;} 8 | .m-tag .tag_list li:hover::before{content:'x';position:absolute;top:0;right:2px;line-height:1em;font-size:8px;font-weight:500;color:#dcdddd;} 9 | /* 自定义标签 */ 10 | .m-tag .tag_list li.add_tag{padding:0;width:92px;white-space:nowrap;} 11 | .m-tag .tag_list li.add_tag:hover::before{content:'';} 12 | /* 自定义标签输入框 */ 13 | .m-tag .tag_list li.add_tag input{height:100%;width:100%;padding:0;border:none;box-sizing:border-box;text-indent:.5em;} 14 | /* 自定义标签按钮 */ 15 | .m-tag .tag_list li.add_tag span{display:inline-block;height:100%;width:100%;text-align:center;} 16 | .m-tag .tag_list li.add_tag span::before{content:'+ ';} 17 | /* 推荐标签组 */ 18 | .m-tag .tag_recommend{display:block;margin-top:30px;margin-bottom:10px;font-size:14px;color:#5ed0ba;line-height:1em;} 19 | .m-tag .tag_recommendlist{margin-bottom:55px;margin-left:-8px;} 20 | .m-tag .tag_recommendlist li{margin-left:8px;margin-bottom:8px;padding:0 12px;height:28px;line-height:28px;font-size:12px;color:#fff;background-color:#5ed0ba;} 21 | .m-tag .tag_recommendlist li::before{content:'+ ';font-size:12px;line-height:28px;} -------------------------------------------------------------------------------- /public/component/Tag/Tag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 标签组件 3 | */ 4 | (function(App){ 5 | 6 | var template = `
            7 |
            8 | 9 |
              10 |
              11 |
              12 | 13 |
                14 |
                15 |
                `; 16 | 17 | /** 18 | * option = { 19 | * parent: dom节点 (父容器节点) 20 | * tags: [] (默认选中标签) 21 | * tags_recommend: [] (推荐标签) 22 | * } 23 | */ 24 | function Tag(options){ 25 | 26 | // 继承配置 27 | _.extend(this, options); 28 | 29 | // 缓存节点 30 | this.container = _.html2node(template); 31 | this.tag_ul = _.getElementsByClassName(this.container, 'tag_list')[0]; 32 | this.tags_recommend_ul = _.getElementsByClassName(this.container, 'tag_recommendlist')[0]; 33 | // tag数组 34 | this.list = []; 35 | // 推荐tag数组 36 | this.list_recommend = []; 37 | 38 | // 初始化 39 | this.init(); 40 | } 41 | 42 | Tag.prototype.init = function(){ 43 | 44 | // 1.检验必传参数 45 | if(!this.parent){ 46 | console.log('请传入标签父容器节点'); 47 | return; 48 | } 49 | 50 | // 2.初始化默认标签(选中标签、推荐标签) 51 | this.initTagList(); 52 | 53 | // 3.绑定事件 54 | this.addEvent(); 55 | 56 | // 4.挂载组件 57 | this.parent.appendChild(this.container); 58 | 59 | }; 60 | // 初始化选中标签组 & 推荐标签组 61 | Tag.prototype.initTagList = function(){ 62 | // 添加最后的自定义标签 63 | this.add_tag = _.html2node(`
              • 自定义标签
              • `); 64 | this.tag_ul.appendChild(this.add_tag); 65 | // 添加默认选中标签 66 | this.tags && this.addTag(this.tags, this.tag_ul, this.add_tag); 67 | // 添加推荐标签 68 | this.tags_recommend && this.addTag(this.tags_recommend, this.tags_recommend_ul); 69 | 70 | // 缓存节点 71 | // 输入框 72 | this.add_tag_input = this.add_tag.getElementsByTagName('input')[0]; 73 | // 自定义标签按钮 74 | this.add_tag_btn = this.add_tag.getElementsByTagName('span')[0]; 75 | }; 76 | /** 添加标签 77 | * tags: [] || str 存放标签的[] 或者 单个标签 78 | * target: dom节点 插入标签的父容器节点 79 | * before: dom节点 在此节点前,插入标签 (选填) 80 | */ 81 | Tag.prototype.addTag = function(tags, target, before){ 82 | // 添加单个tag函数 83 | var add = function(tag){ 84 | // 若未添加过此标签 85 | if(this.list.indexOf(tag) === -1){ 86 | // 若对位置没有要求,则直接放在尾部 87 | if(typeof before === 'undefined'){ 88 | this.target.appendChild(_.html2node(`
              • ${tag}
              • `)); 89 | } 90 | // 否则 放在特定位置的前面 91 | else{ 92 | this.target.insertBefore(_.html2node(`
              • ${tag}
              • `), this.before); 93 | } 94 | this.list.push(tag); 95 | } 96 | }; 97 | 98 | // tags支持数组,也支持单个字符串 99 | if(tags && !Array.isArray(tags)){ 100 | tags = [tags]; 101 | } 102 | // 遍历tags, 添加时,用到的上下文 103 | var context = {}; // 指定forEach的上下文 104 | target && (context.target = target); 105 | before && (context.before = before); 106 | // 若是添加到选中tag节点 107 | if(target === this.tag_ul){ 108 | context.list = this.list; 109 | } 110 | // 否则,添加到推荐tag节点 111 | else{ 112 | context.list = this.list_recommend; 113 | } 114 | // 遍历tags, 添加tag 115 | (tags || []).forEach(add, context); 116 | }; 117 | // 移除标签 118 | Tag.prototype.removeTag = function(evt){ 119 | // 若已添加过此标签 120 | if(this.list.indexOf(evt.target.innerText) !== -1){ 121 | // 从选中list中移除 122 | this.list.splice(this.list.indexOf(evt.target.innerText),1); 123 | // 从视图中移除 124 | evt.target.parentNode.removeChild(evt.target); 125 | } 126 | } 127 | // 添加事件 128 | Tag.prototype.addEvent = function(){ 129 | 130 | // 3.1 点击推荐标签 事件 131 | this.tags_recommend_ul.addEventListener('click', function(evt){ 132 | // 若不是点中li,则直接return 133 | if(evt.target.nodeName.toUpperCase() !== 'LI'){return;} 134 | // 添加tag 到 选中list 135 | this.addTag(evt.target.innerText, this.tag_ul, this.add_tag); 136 | }.bind(this)); 137 | 138 | // 3.2 点击选中标签 事件 139 | this.tag_ul.addEventListener('click', function(evt){ 140 | // 若是点中li,则删除标签 141 | if(evt.target.nodeName.toUpperCase() === 'LI'){ 142 | this.removeTag(evt); 143 | } 144 | // 若是自定义标签按钮 145 | if(evt.target === this.add_tag_btn){ 146 | // 隐藏自定义标签按钮 147 | _.addClassName(this.add_tag_btn, 'f-dn'); 148 | // 显示自定义标签输入框 149 | _.delClassName(this.add_tag_input, 'f-dn'); 150 | // 清空输入框内内容 151 | this.add_tag_input.value = ''; 152 | // 聚焦输入框 153 | this.add_tag_input.focus(); 154 | } 155 | }.bind(this)); 156 | 157 | // 添加自定义标签 函数 158 | var addCustomTag = function(evt){ 159 | // 若自定义input内 有值 160 | if(evt.target.value.trim()){ 161 | // 向选中列表中, 添加标签 162 | this.addTag(evt.target.value.trim(), this.tag_ul, this.add_tag); 163 | } 164 | // 隐藏自定义标签输入框 165 | _.addClassName(this.add_tag_input, 'f-dn'); 166 | // 显示自定义标签按钮 167 | _.delClassName(this.add_tag_btn, 'f-dn'); 168 | }.bind(this); 169 | 170 | // 3.3 自定义标签输入框 失去焦点事件 171 | this.add_tag_input.addEventListener('blur', addCustomTag); 172 | 173 | // 3.4 自定义标签输入框 按下回车键 174 | this.add_tag_input.addEventListener('keydown',function(evt){ 175 | if(evt.keyCode == 13){ 176 | addCustomTag(evt); 177 | } 178 | }); 179 | } 180 | // 获取选中tag列表 181 | Tag.prototype.getValue = function(){ 182 | return this.list.join(','); 183 | }; 184 | 185 | App.Tag = Tag; 186 | 187 | })(window.App); -------------------------------------------------------------------------------- /public/component/UploadPictures/UploadPictures.css: -------------------------------------------------------------------------------- 1 | /* 上传图片组件 样式 */ 2 | .m-uploadpictures{padding-top:15px;} 3 | .m-uploadpictures .upload_controller{padding-bottom:4px;} 4 | /* 标题 */ 5 | .m-uploadpictures .upload_title{float:left;margin-left:31px;width:88px;line-height:40px;font-size:11px;font-weight:700;} 6 | /* */ 7 | /* 上传按钮 */ 8 | .m-uploadpictures .upload_btn{float:left;width:auto;padding:0 24px;font-size:11px;cursor:pointer;} 9 | .m-uploadpictures .upload_btn.f-pen{background:#cbcbcb;border-color:#cbcbcb;} 10 | .m-uploadpictures .upload_input{display:none;} 11 | .m-uploadpictures .prompt_box{font-size:10px;} 12 | /* 进度条 */ 13 | .m-uploadpictures progress{float:left;height:40px;margin-left:10px;} 14 | .m-uploadpictures .progress_msg{float:left;margin-left:10px;height:40px;line-height:40px;font-size:12px;} 15 | /* 图片控制器 */ 16 | .m-uploadpictures .pictures_controller{min-height:84px;padding-left:78px;padding-top:35px;border-top:2px solid #e9eaea;border-bottom:2px solid #e9eaea;background:#f7f8f8;box-sizing:border-box;} 17 | .m-uploadpictures .pictures_controller li{float:left;position:relative;margin-bottom:10px;margin-right:27px;padding-bottom:40px;} 18 | /* 图片 */ 19 | .m-uploadpictures .pictures_controller img{width:101px;height:140px;} 20 | /* 设为封面、已设为封面的按钮 */ 21 | .m-uploadpictures .pictures_controller button,.m-uploadpictures .pictures_controller button + button{display:none;position:absolute;bottom:8px;left:5%;width:90%;height:30px;line-height:30px} 22 | .m-uploadpictures .pictures_controller li:hover button{display:block;} 23 | .m-uploadpictures .pictures_controller li:hover button + button{display:none;} 24 | .m-uploadpictures .pictures_controller li.z-active{pointer-events:none;} 25 | .m-uploadpictures .pictures_controller li.z-active button + button{display:block;} 26 | /* 移除图片的按钮 */ 27 | .m-uploadpictures .pictures_controller li .hover_bg{display:none;position:absolute;top:0;left:0;width:101px;height:140px;background:rgba(0,0,0,0.2);} 28 | .m-uploadpictures .pictures_controller li:hover .hover_bg{display:block;} 29 | .m-uploadpictures .pictures_controller .u-icon{position:absolute;top:2px;right:2px;font-weight:900;color:#fff;cursor:pointer;} -------------------------------------------------------------------------------- /public/component/UploadPictures/UploadPictures.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 上传图片 组件 3 | */ 4 | 5 | (function(App){ 6 | 7 | var template = `
                8 |
                9 | 10 |
                11 |
                12 | 16 | 17 | 18 |
                19 |

                提示:作品可以包含多张图片,一次选择多张图片,最多不超过10张(单张图片大小小于1M)

                20 |
                21 |
                22 |
                  23 |
                24 |
                `; 25 | 26 | /** 27 | * options = { 28 | * parent : dom节点 (父容器节点) 29 | * } 30 | */ 31 | function UploadPictures(options){ 32 | 33 | // 继承配置 34 | _.extend(this, options); 35 | 36 | // 缓存节点 37 | this.container = _.html2node(template); 38 | this.upload_btn = _.getElementsByClassName(this.container, 'upload_btn')[0]; // 上传图片的按钮 39 | this.upload_input = _.getElementsByClassName(this.container, 'upload_input')[0]; // 存图片文件的input 40 | this.progress = this.container.getElementsByTagName('progress')[0]; // 上传进度条 41 | this.progress_msg = _.getElementsByClassName(this.container, 'progress_msg')[0]; // 进度条文字提示 42 | this.pictures_controller = _.getElementsByClassName(this.container, 'pictures_controller')[0]; // 图片列表 43 | 44 | // 图片列表 45 | this.picture_list = []; 46 | 47 | // 初始化 48 | this.init(); 49 | } 50 | 51 | // 混入事件管理器 52 | _.extend(UploadPictures.prototype, App.emitter); 53 | 54 | // 初始化 55 | UploadPictures.prototype.init = function(){ 56 | 57 | // 绑定 选择上传图片后 触发事件 58 | this.upload_input.addEventListener('change', this.changeHandler.bind(this)); 59 | // 绑定 拖拽上传 60 | this.pictures_controller.addEventListener('dragover', function(evt){ 61 | evt.preventDefault(); 62 | }); 63 | this.pictures_controller.addEventListener('drop', function(evt){ 64 | evt.preventDefault(); 65 | this.dropFiles(evt.dataTransfer.files); 66 | }.bind(this)); 67 | // 绑定 设置封面事件 68 | this.pictures_controller.addEventListener('click', this.setCoverImg.bind(this)); 69 | // 绑定 移除图片事件 70 | this.pictures_controller.addEventListener('click', this.removeImg.bind(this)); 71 | 72 | // 挂载组件 73 | this.parent.appendChild(this.container); 74 | }; 75 | 76 | // 1.通过按钮 上传文件 77 | UploadPictures.prototype.changeHandler = function(){ 78 | // 从input中获取 files 79 | var files = this.upload_input.files; 80 | // 检验规格 (不合格,则不上传) 81 | this._checkFiles(files); 82 | // 清空input file中列表中缓存内容 83 | this.upload_input.value = null; 84 | }; 85 | // 2.通过拖拽 上传文件 86 | UploadPictures.prototype.dropFiles = function(files){ 87 | // 从drop事件中,获取files 88 | // 检验规格 (不合格,则不上传) 89 | this._checkFiles(files); 90 | }; 91 | // 检验文件规格 92 | UploadPictures.prototype._checkFiles = function(files){ 93 | var maxSize = 1024 * 1024; // 1M 94 | var sizeOkFiles = []; 95 | var sizeExceedFiles = []; 96 | var typeExceedFiles = []; 97 | 98 | // 每次最多选择10张图片 99 | if(files.length > 10){ 100 | this.emit('alert', '每次最多选择10张图片'); 101 | return; 102 | } 103 | 104 | // 过滤出,大小 < 1M 的图片 105 | Array.prototype.forEach.call(files, function(item){ 106 | // 文件类型 非图片格式 107 | if(!/^image\//.test(item.type)){ 108 | typeExceedFiles.push(item); 109 | return; 110 | } 111 | // 图片尺寸 < 1M 112 | if(item.size < maxSize){ 113 | sizeOkFiles.push(item); 114 | } 115 | // 图片尺寸 > 1M 116 | else{ 117 | sizeExceedFiles.push(item); 118 | } 119 | }); 120 | 121 | // 若有文件类型 不是图片类型 122 | if(typeExceedFiles.length > 0){ 123 | this.emit('alert', '只能上传图片类型'); 124 | return; 125 | } 126 | 127 | // 若有图片超过 1M,则 所有图片不做上传动作 128 | if(sizeExceedFiles.length > 0){ 129 | this.emit('alert', '图片大小不能超过1M'); 130 | return; 131 | } 132 | 133 | // 禁止点击上传按钮 134 | _.addClassName(this.upload_btn, 'f-pen'); 135 | 136 | // 上传图片 137 | this._uploadFiles(sizeOkFiles); 138 | } 139 | // 上传文件 140 | UploadPictures.prototype._uploadFiles = function(files){ 141 | 142 | // 进度条文案参数 143 | this.uploadfiles_total = files.length; // 上传文件总数 144 | this.uploadfiles_loaded = 0 // 已上传完成文件数 145 | this.uploadfiles_progress = []; // 上传文件 进度数组 146 | for(var i=0;i= 200 && xhr.status < 300) || xhr.status == 304){ 172 | resolve(JSON.parse(xhr.responseText).result); 173 | } 174 | else{ 175 | reject(xhr.responseText); 176 | } 177 | } 178 | } 179 | 180 | xhr.upload.addEventListener('progress', this.progressHandler.bind(this, index), false); 181 | xhr.open('POST', '/api/works?upload'); 182 | // 直接send FormData实例 183 | xhr.send(fd); 184 | 185 | }.bind(this)) 186 | .then(function(res){ 187 | // 添加图片 188 | this._addImg(res); 189 | return res; 190 | }.bind(this)) 191 | .catch(function(e){ 192 | return e; 193 | }) 194 | ); 195 | }.bind(this)); 196 | 197 | // 全部请求返回后 198 | Promise.all(uploadRequests) 199 | .then(function(data){ 200 | // 隐藏进度条 201 | this._hideProgress(); 202 | // 清空进度条文案 203 | this._updateProgressMsg(); 204 | // 上传完毕,恢复按钮状态 205 | _.delClassName(this.upload_btn, 'f-pen'); 206 | }.bind(this)) 207 | .catch(function(e){ 208 | console.log(e); 209 | }); 210 | }; 211 | // 向图片列表中,添加图片 212 | UploadPictures.prototype._addImg = function(res){ 213 | 214 | switch(Object.prototype.toString.call(res).slice(8,-1)){ 215 | case 'String': 216 | // 若是string类型,则为错误信息,直接跳过 217 | return; 218 | case 'Array': 219 | res.forEach(function(item){ 220 | _addOneImg.call(this, item); 221 | }.bind(this)); 222 | break; 223 | case 'Object': 224 | _addOneImg.call(this, res); 225 | break; 226 | } 227 | // 添加 单张照片 228 | function _addOneImg(res){ 229 | // 向图片list 数据中添加 230 | this.picture_list.push(res); 231 | // 向视图list 中添加 232 | this.pictures_controller.appendChild(_.html2node(` 233 |
              • 234 | 235 |
                236 | 237 |
                238 | 239 | 240 |
              • `)); 241 | } 242 | 243 | } 244 | 245 | // 3.上传进度 回调函数 246 | UploadPictures.prototype.progressHandler = function(index, evt){ 247 | console.log(evt); 248 | // 若可计算 249 | if(evt && evt.lengthComputable){ 250 | // 更新 进度数组 251 | this.uploadfiles_progress[index] = evt.loaded / evt.total; 252 | // 当一个文件上传完毕,上传文件数 + 1 253 | if(this.uploadfiles_progress[index] === 1){this.uploadfiles_loaded ++;} 254 | } 255 | // 并归 进度数组值,传给进度条 256 | this._showProgress(this.uploadfiles_progress.reduce(function(prev, cur){return prev + cur;})); 257 | }; 258 | // 显示进度条 259 | UploadPictures.prototype._showProgress = function(value, max){ 260 | this.progress.max = max || this.progress.max || 0; 261 | this.progress.value = value; 262 | _.delClassName(this.progress, 'f-dn'); 263 | // 更新进度条文案 264 | this._updateProgressMsg(this.progress.value, this.progress.max); 265 | }; 266 | // 隐藏进度条 267 | UploadPictures.prototype._hideProgress = function(){ 268 | _.addClassName(this.progress, 'f-dn'); 269 | }; 270 | // 更新进度条 文案 271 | UploadPictures.prototype._updateProgressMsg = function(value, max){ 272 | // 默认 文案为空 273 | var progress_msg = ''; 274 | // 若value 与 max 存在 275 | if(typeof value !== 'undefined' && typeof max !== 'undefined'){ 276 | progress_msg = `共有${this.uploadfiles_total}个文件,已完成${this.uploadfiles_loaded}个文件,上传进度${parseInt(value / max * 100, 10)}%`; 277 | } 278 | // 更新 进度条 文案 279 | this.progress_msg.innerHTML = progress_msg; 280 | }; 281 | 282 | // 4.设置封面 283 | UploadPictures.prototype.setCoverImg = function(evt){ 284 | // 若不是设置封面按钮, 直接忽略 285 | if(!_.hasClassName(evt.target, 'u-btn')){return;} 286 | // 清楚其它li上的选中状态 287 | Array.prototype.forEach.call(this.pictures_controller.children, function(li){ 288 | _.delClassName(li, 'z-active'); 289 | }); 290 | // 为当前选中li,增加选中状态 291 | _.addClassName(evt.target.parentNode, 'z-active'); 292 | this.coverImg = { 293 | id: evt.target.parentNode.dataset.id, 294 | url: evt.target.parentNode.dataset.url 295 | }; 296 | }; 297 | 298 | // 5.删除图片 299 | UploadPictures.prototype.removeImg = function(evt){ 300 | // 若不是删除图片按钮,直接忽略 301 | if(!_.hasClassName(evt.target, 'u-icon')){return;} 302 | // 删除图片的id 303 | console.log('删除图片'); 304 | 305 | // 找到li元素 306 | var li = evt.target.parentNode.parentNode; 307 | if(li.nodeName.toUpperCase() === 'LI'){ 308 | // 找到id 309 | var id = li.dataset.id; 310 | // 删除视图中的li 311 | this.pictures_controller.removeChild(li); 312 | 313 | // 从列表中清除 图片 314 | for(var i=this.picture_list.length - 1;i>=0;i--){ 315 | // 若在图片列表中,找到该图片 316 | if(this.picture_list[i].id == id){ 317 | // 则从列表中删除该图片 318 | this.picture_list.splice(i,1); 319 | } 320 | } 321 | } 322 | 323 | }; 324 | 325 | // 6.获取图片列表信息 326 | UploadPictures.prototype.getValue = function(){ 327 | return { 328 | coverId: this.coverImg ? this.coverImg.id : undefined, // 封面id 329 | coverUrl: this.coverImg ? this.coverImg.url : undefined, // 封面url 330 | pictures: this.picture_list.map(function(item, index){ // 图片列表 331 | var picture = {}; 332 | for(key in item){ 333 | picture[key] = item[key]; 334 | } 335 | picture['position'] = index; 336 | return picture; 337 | }) 338 | }; 339 | }; 340 | 341 | App.UploadPictures = UploadPictures; 342 | 343 | })(window.App); -------------------------------------------------------------------------------- /public/component/User/User.css: -------------------------------------------------------------------------------- 1 | /* User 组件样式 */ 2 | .m-user{float:left;position:relative;margin-left:45px;} 3 | 4 | /* 用户信息 */ 5 | .m-user .user_info{display:-webkit-flex;display:flex;-webkit-align-items:center;} 6 | .m-user:hover .user_info{border-color:#ddd;} 7 | /* 用户头像 */ 8 | .m-user .user_avatar img{width:50px;margin-right:9px;} 9 | /* 用户头像、性别、下拉框提示图标 */ 10 | .m-user .user_basicinfo{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;margin-right:6px;} 11 | .m-user .user_name{display:inline-block;max-width:64px;margin-right:3px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:17px;font-weight:900;} 12 | .m-user .u-icon-female,.m-user .u-icon-down{font-size:17px;font-weight:900;} 13 | 14 | /* 用户管理列表 */ 15 | .m-user .user_list{display:none;position:absolute;z-index:999;top:0;left:-10px;width:calc(100% + 20px);padding:59px 0 0;border:1px solid #ddd;box-sizing:border-box;background:#fff;background-clip:content-box;} 16 | .m-user:hover .user_list{display:block;} 17 | .m-user .user_list li{text-indent:55px;padding:18px 0;line-height:1em;cursor:pointer;} 18 | .m-user .user_list li:hover{background:#eee;} -------------------------------------------------------------------------------- /public/component/User/User.js: -------------------------------------------------------------------------------- 1 | // 防止window.App 不存在 2 | if(!window.App || typeof window.App != 'object'){ 3 | window.App = {}; 4 | } 5 | 6 | (function(App){ 7 | // 模板 8 | var template = `
                9 | 17 |
                  18 |
                • 个人中心
                • 19 |
                • 信息
                • 20 |
                • 设置
                • 21 |
                • 退出账号
                • 22 |
                23 |
                `; 24 | 25 | // options 参数说明 26 | // { 27 | // parent: dom节点, 父容器 (必填) 28 | // }) 29 | function User(options){ 30 | // 继承配置 31 | _.extend(this, options); 32 | 33 | // 缓存节点 34 | this.container = this._template.cloneNode(true); 35 | this.userList = _.getElementsByClassName(this.container, 'user_list')[0]; 36 | this.nLogout = _.getElementsByClassName(this.userList, 'logout')[0]; 37 | this.nName = _.getElementsByClassName(this.container, 'user_name')[0]; 38 | this.nSexIcon = _.getElementsByClassName(this.container, 'sex_icon')[0]; 39 | 40 | // 初始化 41 | this.init(); 42 | } 43 | 44 | // 混入事件管理器 45 | _.extend(User.prototype, App.emitter); 46 | 47 | // 用于复制的Dom节点 48 | User.prototype._template = _.html2node(template); 49 | 50 | // 初始化(绑定事件,将组件载入页面) 51 | User.prototype.init = function(){ 52 | // 挂载组件 53 | this.parent.appendChild(this.container); 54 | // 订阅事件 55 | this.on('login', this.show.bind(this)); // 登录事件 56 | this.on('notLogin', this.hide.bind(this)); // 未登录事件 57 | // 绑定事件 58 | this.nLogout.addEventListener('click', this.logout.bind(this)); 59 | }; 60 | // 退出登录 61 | User.prototype.logout = function(){ 62 | _.ajax({ 63 | url: '/api/logout', 64 | method: 'POST', 65 | data: {}, 66 | success: function(data){ 67 | data = JSON.parse(data); 68 | console.log(data); 69 | if(data.code === 200){ 70 | window.location.href = "/index"; 71 | } 72 | }, 73 | fail: function(){} 74 | }) 75 | }; 76 | // 显示此组件 77 | User.prototype.show = function(data){ 78 | this.initUserInfo(data); 79 | _.delClassName(this.container, 'f-dn'); 80 | }; 81 | var iconConfig = { 82 | 0: 'u-icon-male', 83 | 1: 'u-icon-female' 84 | }; 85 | // 初始化用户信息 86 | User.prototype.initUserInfo = function(data){ 87 | // 设置用户姓名 88 | this.nName.innerText = data.nickname; 89 | // 清空之前的用户性别Icon 90 | for(var key in iconConfig){ 91 | _.delClassName(this.nSexIcon, iconConfig[key]); 92 | } 93 | // 设置用户性别Icon 94 | _.addClassName(this.nSexIcon, iconConfig[data.sex]); 95 | }; 96 | // 隐藏此组件 97 | User.prototype.hide = function(){ 98 | _.addClassName(this.container, 'f-dn'); 99 | }; 100 | 101 | App.User = User; 102 | 103 | })(window.App); -------------------------------------------------------------------------------- /public/component/WorkDescription/WorkDescription.css: -------------------------------------------------------------------------------- 1 | /* 作品名称、作品描述组件 */ 2 | .m-workdescription{padding-left:161px;padding-top:27px;} 3 | .m-workdescription .f-cb + .f-cb{margin-top:25px;} 4 | /* 标题 */ 5 | .m-workdescription label{float:left;width:77px;font-size:12px;font-weight:500;line-height:38px;} 6 | /* 输入框 */ 7 | .m-workdescription input,.m-workdescription textarea{-webkit-border-radius:0;border-radius:0;} 8 | .m-workdescription input{height:38px;width:294px;} 9 | .m-workdescription textarea{width:404px;height:104px;padding:15px;box-sizing:border-box;text-indent:0;} 10 | /* 提示框 */ 11 | .m-workdescription .prompt_msg{margin-left:28px;font-size:11px;color:red;} -------------------------------------------------------------------------------- /public/component/WorkDescription/WorkDescription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作品名称、作品说明组件 3 | */ 4 | (function(App){ 5 | 6 | var template =`
                7 |
                8 | 你的作品还没有取名字哦 9 |
                10 |
                11 | 12 |
                13 |
                `; 14 | 15 | var WorkDescription = { 16 | // 初始化 17 | /** 18 | * options = { 19 | * parent: dom节点 (父容器节点) 20 | * } 21 | */ 22 | init: function(options){ 23 | // 继承配置 24 | _.extend(this, options); 25 | 26 | // 缓存节点 27 | this.container = _.html2node(template); 28 | this.msg = _.getElementsByClassName(this.container, 'prompt_msg')[0]; 29 | 30 | // 挂载组件 31 | this.parent.appendChild(this.container); 32 | 33 | return this; 34 | }, 35 | getValue: function(){ 36 | // 若名字为空 37 | if(!this.checkName()){ 38 | // 显示警告消息 39 | _.delClassName(this.msg, 'f-dn'); 40 | } 41 | else{ 42 | // 隐藏警告消息 43 | _.addClassName(this.msg, 'f-dn'); 44 | } 45 | return { 46 | name: this.container.getElementsByTagName('input')[0].value.trim(), 47 | description: this.container.getElementsByTagName('textarea')[0].value.trim() 48 | }; 49 | }, 50 | checkName: function(){ 51 | return !!this.container.getElementsByTagName('input')[0].value.trim(); 52 | } 53 | }; 54 | 55 | App.WorkDescription = WorkDescription; 56 | 57 | })(window.App); -------------------------------------------------------------------------------- /public/component/WorkPermission/WorkPermission.css: -------------------------------------------------------------------------------- 1 | /* 权限设置 组件样式 */ 2 | .m-privilege label{display:inline-block;margin-bottom:28px;margin-top:27px;font-size:13px;} 3 | /* 作品授权 组件样式 */ 4 | .m-authorization label{display:inline-block;margin-bottom:15px;font-size:13px;} 5 | .m-authorization .m-select{margin-bottom:42px;height:39px;} 6 | .m-authorization .m-select .select_hd{background:#fff;} 7 | .m-authorization .m-select .select_opt{height:60px;} -------------------------------------------------------------------------------- /public/component/WorkPermission/WorkPermission.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作品权限设置组件 3 | * 依赖选择器组件 4 | * 单选组件 5 | */ 6 | (function(App){ 7 | 8 | var privilege_template = `
                `; 9 | 10 | var authorization_template = `
                `; 11 | 12 | /** 13 | * options = { 14 | * parent: dom 节点 (父容器节点) 15 | * } 16 | */ 17 | function WorkPermission(options){ 18 | // 继承配置 19 | _.extend(this, options); 20 | 21 | // 缓存节点 22 | this.privilege = _.html2node(privilege_template) 23 | this.authorization = _.html2node(authorization_template); 24 | 25 | // 初始化 26 | this.init(); 27 | } 28 | 29 | WorkPermission.prototype.init = function(){ 30 | // 初始化单选组件(权限设置) 31 | this.radio = new App.Radio({ 32 | parent: this.privilege, 33 | data: {name:'privilege', list:[ 34 | {text: '所有人可见', value:0}, 35 | {text: '关注者/粉丝可见', value:1}, 36 | {text: '仅自己可见', value: 2} 37 | ]} 38 | }) 39 | // 初始化选择器 (作品授权) 40 | this.select = new App.Select({parent: this.authorization}); 41 | this.select.render([{name:'不限制作品用途',value:0},{name:'禁止匿名转载;禁止商业使用',value:1}]); 42 | 43 | // 挂载 权限设置 组件 44 | this.parent.appendChild(this.privilege); 45 | // 挂载 授权类型 组件 46 | this.parent.appendChild(this.authorization); 47 | }; 48 | // 获取值 49 | WorkPermission.prototype.getValue = function(){ 50 | return { 51 | privilege: parseInt(this.radio.getValue(),10), 52 | authorization: this.select.getValue() 53 | }; 54 | } 55 | 56 | App.WorkPermission = WorkPermission; 57 | 58 | })(window.App); -------------------------------------------------------------------------------- /public/component/WorksList/WorksList.css: -------------------------------------------------------------------------------- 1 | /* 作品列表组件 */ 2 | /* 标题区域 */ 3 | .m-workstitle{display:-webkit-flex;display:flex;position:relative;z-index:1;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;align-items:center;margin-top:19px;margin-bottom:27px;} 4 | .m-workstitle .title{font-size:13px;} 5 | .m-workstitle .u-btn{width:90px;height:38px;background:#5eccb6;font-size:11px;color:#fff;} 6 | .m-workstitle .u-btn a{display:block;} 7 | .m-workstitle img{position:absolute;top:calc(100% + 27px);} 8 | /* 列表区域 */ 9 | .m-workslist{display:-webkit-flex;display:flex;flex-wrap:wrap;margin-right:-29px;} 10 | .m-workslist .item{position:relative;width:103px;height:160px;border:1px solid #d3d4d4;box-sizing:border-box;margin-right:29px;margin-bottom:23px;} 11 | .m-workslist .item a{display:block;width:103px;height:160px;} 12 | .m-workslist .item a::before{position:absolute;top:2px;right:2px;bottom:-4px;left:-4px;border:1px solid #d7d7d7;background-color:#c5c6c6;content:"";} 13 | .m-workslist .item img{position:absolute;top:5px;right:5px;bottom:-7px;left:-7px;width:103px;height:160px;border:1px solid #d7d7d7;border-top:none;border-right:none;box-sizing:border-box;} 14 | .m-workslist .item h3{position:absolute;bottom:-6px;left:-6px;right:6px;height:30px;line-height:30px;background:rgba(255,255,255,.8);text-align:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:12px;} 15 | /* 编辑与删除图标 */ 16 | .m-workslist .item .icons{display:none;position:absolute;top:5px;right:5px;bottom:-7px;left:-7px;background:rgba(0,0,0,0.5);font-weight:700;text-align:right;color:#fff;} 17 | .m-workslist .item .icons .u-icon{cursor:pointer;} 18 | .m-workslist .item .icons .u-icon:last-child{margin-right:4px;} 19 | .m-workslist .item:hover .icons{display:block;} 20 | /* 无作品时,显示文案样式 */ 21 | .m-workslist .cnt{font-size:13px;} -------------------------------------------------------------------------------- /public/component/WorksList/WorksList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作品列表组件 3 | * 依赖:分页器组件 4 | */ 5 | 6 | (function(App){ 7 | 8 | var template_title = ` 9 |
                10 |
                /我发表的作品/
                11 | 12 |
                `; 13 | 14 | var template_cnt = `
                `; 15 | 16 | var template = Handlebars.compile(` 17 |
                  18 | {{#if total}} 19 | {{#each data}} 20 |
                • 21 | 22 | 23 |

                  {{name}}

                  24 |
                  25 |
                  26 | 27 | 28 |
                  29 |
                • 30 | {{/each}} 31 | {{else}} 32 |
                  你还没有创建过作品
                  33 | {{/if}} 34 |
                `); 35 | 36 | /** 37 | * options = { 38 | * parent: dom节点, 父容器 39 | * } 40 | */ 41 | function WorksList(options){ 42 | 43 | // 继承配置 44 | _.extend(this, options); 45 | 46 | // 初始化 47 | this.init(); 48 | } 49 | 50 | // 混入事件管理器 51 | _.extend(WorksList.prototype, App.emitter); 52 | 53 | WorksList.prototype.init = function(){ 54 | 55 | // 渲染列表头 56 | this.title = _.html2node(template_title); 57 | this.parent.appendChild(this.title); 58 | // 添加Loading图标 59 | this.loading = _.html2node(``); 60 | this.title.appendChild(this.loading); 61 | // 渲染作品内容 62 | this.cnt = _.html2node(template_cnt); 63 | this.parent.appendChild(this.cnt); 64 | 65 | // 查询参数 (初始化) 用于删除作品时,为 刷新列表操作 保存默认查询参数 66 | this.param_total = 1; 67 | this.param_offset = 0; 68 | this.param_limit = 10; 69 | 70 | // 获取列表信息 (初始化) 71 | this.getWorksList(); 72 | 73 | // 绑定事件 74 | this.cnt.addEventListener('click', this.clickHandler.bind(this)); 75 | }; 76 | /** 77 | * 获取作品列表数据 78 | * options = { 79 | * total: 0 || 1 // 是否需要返回总数 80 | * offset: num // 偏移数 81 | * limit: num // 返回的 作品条数 82 | * } 83 | */ 84 | WorksList.prototype.getWorksList = function(options){ 85 | // 防止options undefined 86 | options = options || {}; 87 | // 隐藏已有数据列表 88 | this.workList && _.addClassName(this.workList, 'f-vh'); 89 | // 显示Loading图标 90 | _.delClassName(this.loading, 'f-dn'); 91 | 92 | // 更新查询参数 93 | typeof options.total === 'undefined' || (this.param_total = options.total); 94 | typeof options.offset === 'undefined' || (this.param_offset = options.offset); 95 | typeof options.limit === 'undefined' || (this.param_limit = options.limit); 96 | 97 | _.ajax({ 98 | url: '/api/works', 99 | method: 'GET', 100 | data: { 101 | total: this.param_total, // 是否需要返回总数 102 | offset: this.param_offset, // 偏移数 103 | limit: this.param_limit // 返回的 作品条数 104 | }, 105 | success: function(data){ 106 | data = JSON.parse(data); 107 | if(data.code === 200){ 108 | _.addClassName(this.loading, 'f-dn'); 109 | // 更新作品列表 110 | this.renderList(data.result); 111 | // 渲染分页器 112 | this.renderPagination(data.result); 113 | } 114 | }.bind(this), 115 | fail: function(){} 116 | }); 117 | }; 118 | // 渲染作品列表 119 | WorksList.prototype.renderList = function(data){ 120 | // 是否已存在作品列表 121 | if(this.workList){ 122 | // 若存在,则更新数据 123 | var oldWorkList = this.workList; 124 | this.workList = _.html2node(template(data)); 125 | oldWorkList.parentNode.replaceChild(this.workList, oldWorkList); 126 | } 127 | else{ 128 | // 若不存在,则新增 129 | this.workList = _.html2node(template(data)); 130 | this.cnt.appendChild(this.workList); 131 | } 132 | }; 133 | // 渲染分页器组件 134 | WorksList.prototype.renderPagination = function(data){ 135 | // 若没有数据,则不渲染分页器 136 | if(data.total === 0){return;} 137 | // 单例模式 138 | this.pagination = this.pagination || new App.Pagination({ // 若不存在则新建 139 | parent: this.parent, 140 | total: data.total, 141 | togglePageNum: this.getWorksList.bind(this) 142 | }); 143 | 144 | }; 145 | 146 | // 编辑、删除作品事件 147 | WorksList.prototype.clickHandler = function(evt){ 148 | var target = evt.target; 149 | // 若点的是删除按钮 150 | if(_.hasClassName(target, 'u-icon-delete')){ 151 | this.delWork(target.parentNode.parentNode.dataset); 152 | } 153 | // 若点的是编辑按钮 154 | else if(_.hasClassName(target, 'u-icon-edit')){ 155 | this.editWork(target.parentNode.parentNode.dataset, target); 156 | } 157 | }; 158 | // 删除作品 159 | WorksList.prototype.delWork = function(data){ 160 | 161 | // 发布 显示确认弹窗事件 162 | this.emit('confirm', { 163 | content: `确定要删除作品"${data.name}"吗?`, 164 | confirmCallBack: function(){ 165 | _.ajax({ 166 | url: `/api/works/${data.id}`, 167 | method: 'DELETE', 168 | success: function(res){ 169 | // 刷新列表 170 | this.getWorksList(); 171 | }.bind(this), 172 | fail: function(e){ 173 | console.log(e); 174 | } 175 | }); 176 | }.bind(this) 177 | }); 178 | }; 179 | // 编辑作品 180 | WorksList.prototype.editWork = function(data, work_target){ 181 | 182 | // 发布 显示确认弹窗事件 183 | this.emit('confirm', { 184 | title: '请输入新的作品名称', 185 | content: ``, 186 | confirmCallBack: function(evt){ 187 | // 弹窗组件节点 188 | var confirm_modal = evt.target.parentNode.parentNode; 189 | // 编辑后的 新的 作品名称 190 | var new_name = _.getElementsByClassName(confirm_modal, 'new_name')[0].value.trim(); 191 | // 若新名称 不为空 192 | if(new_name){ 193 | _.ajax({ 194 | url: `/api/works/${data.id}`, 195 | method: 'PATCH', 196 | data: {name: new_name}, 197 | success: function(res){ 198 | res = JSON.parse(res); 199 | console.log(res); 200 | // 要修改的作品的名称 dom节点 201 | var name_node = work_target.parentNode.parentNode.getElementsByTagName('h3')[0]; 202 | // 修改作品名称 203 | name_node.innerHTML = new_name; 204 | work_target.parentNode.parentNode.dataset.name = new_name; // 同步更新 data- 上属性值 205 | }.bind(this), 206 | fail: function(e){ 207 | console.log(e); 208 | }, 209 | header: {'content-type': 'application/json'} // 不设置content-type 会400报错 210 | }); 211 | } 212 | console.log(new_name); 213 | }.bind(this) 214 | }); 215 | }; 216 | 217 | App.WorksList = WorksList; 218 | 219 | })(window.App); 220 | -------------------------------------------------------------------------------- /public/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ego首页 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
                17 | 18 |
                19 |
                20 | 21 |
                22 | 23 |
                24 |
                25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /public/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 搜索结果页 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/html/works/create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 作品创建页 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
                17 |
                18 | 19 |
                20 |
                21 | 22 |
                23 |
                24 |
                25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /public/html/works/detail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 作品详情页 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/html/works/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 作品列表页 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
                17 | 18 |
                19 |
                20 | 21 |
                22 |
                23 |
                24 |
                25 |
                26 |
                27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /public/lib/md5.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 3 | * Digest Algorithm, as defined in RFC 1321. 4 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 6 | * Distributed under the BSD License 7 | * See http://pajhome.org.uk/crypt/md5 for more info. 8 | */ 9 | 10 | /* 11 | * Configurable variables. You may need to tweak these to be compatible with 12 | * the server-side, but the defaults work in most cases. 13 | */ 14 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ 15 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ 16 | 17 | /* 18 | * These are the functions you'll usually want to call 19 | * They take string arguments and return either hex or base-64 encoded strings 20 | */ 21 | function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } 22 | function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } 23 | function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } 24 | function hex_hmac_md5(k, d) 25 | { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } 26 | function b64_hmac_md5(k, d) 27 | { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } 28 | function any_hmac_md5(k, d, e) 29 | { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } 30 | 31 | /* 32 | * Perform a simple self-test to see if the VM is working 33 | */ 34 | function md5_vm_test() 35 | { 36 | return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72"; 37 | } 38 | 39 | /* 40 | * Calculate the MD5 of a raw string 41 | */ 42 | function rstr_md5(s) 43 | { 44 | return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); 45 | } 46 | 47 | /* 48 | * Calculate the HMAC-MD5, of a key and some data (raw strings) 49 | */ 50 | function rstr_hmac_md5(key, data) 51 | { 52 | var bkey = rstr2binl(key); 53 | if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); 54 | 55 | var ipad = Array(16), opad = Array(16); 56 | for(var i = 0; i < 16; i++) 57 | { 58 | ipad[i] = bkey[i] ^ 0x36363636; 59 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 60 | } 61 | 62 | var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); 63 | return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); 64 | } 65 | 66 | /* 67 | * Convert a raw string to a hex string 68 | */ 69 | function rstr2hex(input) 70 | { 71 | try { hexcase } catch(e) { hexcase=0; } 72 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 73 | var output = ""; 74 | var x; 75 | for(var i = 0; i < input.length; i++) 76 | { 77 | x = input.charCodeAt(i); 78 | output += hex_tab.charAt((x >>> 4) & 0x0F) 79 | + hex_tab.charAt( x & 0x0F); 80 | } 81 | return output; 82 | } 83 | 84 | /* 85 | * Convert a raw string to a base-64 string 86 | */ 87 | function rstr2b64(input) 88 | { 89 | try { b64pad } catch(e) { b64pad=''; } 90 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 91 | var output = ""; 92 | var len = input.length; 93 | for(var i = 0; i < len; i += 3) 94 | { 95 | var triplet = (input.charCodeAt(i) << 16) 96 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) 97 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); 98 | for(var j = 0; j < 4; j++) 99 | { 100 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; 101 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); 102 | } 103 | } 104 | return output; 105 | } 106 | 107 | /* 108 | * Convert a raw string to an arbitrary string encoding 109 | */ 110 | function rstr2any(input, encoding) 111 | { 112 | var divisor = encoding.length; 113 | var i, j, q, x, quotient; 114 | 115 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ 116 | var dividend = Array(Math.ceil(input.length / 2)); 117 | for(i = 0; i < dividend.length; i++) 118 | { 119 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); 120 | } 121 | 122 | /* 123 | * Repeatedly perform a long division. The binary array forms the dividend, 124 | * the length of the encoding is the divisor. Once computed, the quotient 125 | * forms the dividend for the next step. All remainders are stored for later 126 | * use. 127 | */ 128 | var full_length = Math.ceil(input.length * 8 / 129 | (Math.log(encoding.length) / Math.log(2))); 130 | var remainders = Array(full_length); 131 | for(j = 0; j < full_length; j++) 132 | { 133 | quotient = Array(); 134 | x = 0; 135 | for(i = 0; i < dividend.length; i++) 136 | { 137 | x = (x << 16) + dividend[i]; 138 | q = Math.floor(x / divisor); 139 | x -= q * divisor; 140 | if(quotient.length > 0 || q > 0) 141 | quotient[quotient.length] = q; 142 | } 143 | remainders[j] = x; 144 | dividend = quotient; 145 | } 146 | 147 | /* Convert the remainders to the output string */ 148 | var output = ""; 149 | for(i = remainders.length - 1; i >= 0; i--) 150 | output += encoding.charAt(remainders[i]); 151 | 152 | return output; 153 | } 154 | 155 | /* 156 | * Encode a string as utf-8. 157 | * For efficiency, this assumes the input is valid utf-16. 158 | */ 159 | function str2rstr_utf8(input) 160 | { 161 | var output = ""; 162 | var i = -1; 163 | var x, y; 164 | 165 | while(++i < input.length) 166 | { 167 | /* Decode utf-16 surrogate pairs */ 168 | x = input.charCodeAt(i); 169 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; 170 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) 171 | { 172 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); 173 | i++; 174 | } 175 | 176 | /* Encode output as utf-8 */ 177 | if(x <= 0x7F) 178 | output += String.fromCharCode(x); 179 | else if(x <= 0x7FF) 180 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 181 | 0x80 | ( x & 0x3F)); 182 | else if(x <= 0xFFFF) 183 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 184 | 0x80 | ((x >>> 6 ) & 0x3F), 185 | 0x80 | ( x & 0x3F)); 186 | else if(x <= 0x1FFFFF) 187 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 188 | 0x80 | ((x >>> 12) & 0x3F), 189 | 0x80 | ((x >>> 6 ) & 0x3F), 190 | 0x80 | ( x & 0x3F)); 191 | } 192 | return output; 193 | } 194 | 195 | /* 196 | * Encode a string as utf-16 197 | */ 198 | function str2rstr_utf16le(input) 199 | { 200 | var output = ""; 201 | for(var i = 0; i < input.length; i++) 202 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, 203 | (input.charCodeAt(i) >>> 8) & 0xFF); 204 | return output; 205 | } 206 | 207 | function str2rstr_utf16be(input) 208 | { 209 | var output = ""; 210 | for(var i = 0; i < input.length; i++) 211 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, 212 | input.charCodeAt(i) & 0xFF); 213 | return output; 214 | } 215 | 216 | /* 217 | * Convert a raw string to an array of little-endian words 218 | * Characters >255 have their high-byte silently ignored. 219 | */ 220 | function rstr2binl(input) 221 | { 222 | var output = Array(input.length >> 2); 223 | for(var i = 0; i < output.length; i++) 224 | output[i] = 0; 225 | for(var i = 0; i < input.length * 8; i += 8) 226 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); 227 | return output; 228 | } 229 | 230 | /* 231 | * Convert an array of little-endian words to a string 232 | */ 233 | function binl2rstr(input) 234 | { 235 | var output = ""; 236 | for(var i = 0; i < input.length * 32; i += 8) 237 | output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); 238 | return output; 239 | } 240 | 241 | /* 242 | * Calculate the MD5 of an array of little-endian words, and a bit length. 243 | */ 244 | function binl_md5(x, len) 245 | { 246 | /* append padding */ 247 | x[len >> 5] |= 0x80 << ((len) % 32); 248 | x[(((len + 64) >>> 9) << 4) + 14] = len; 249 | 250 | var a = 1732584193; 251 | var b = -271733879; 252 | var c = -1732584194; 253 | var d = 271733878; 254 | 255 | for(var i = 0; i < x.length; i += 16) 256 | { 257 | var olda = a; 258 | var oldb = b; 259 | var oldc = c; 260 | var oldd = d; 261 | 262 | a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); 263 | d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); 264 | c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); 265 | b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); 266 | a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); 267 | d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); 268 | c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); 269 | b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); 270 | a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); 271 | d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); 272 | c = md5_ff(c, d, a, b, x[i+10], 17, -42063); 273 | b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); 274 | a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); 275 | d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); 276 | c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); 277 | b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); 278 | 279 | a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); 280 | d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); 281 | c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); 282 | b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); 283 | a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); 284 | d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); 285 | c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); 286 | b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); 287 | a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); 288 | d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); 289 | c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); 290 | b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); 291 | a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); 292 | d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); 293 | c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); 294 | b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); 295 | 296 | a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); 297 | d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); 298 | c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); 299 | b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); 300 | a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); 301 | d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); 302 | c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); 303 | b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); 304 | a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); 305 | d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); 306 | c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); 307 | b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); 308 | a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); 309 | d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); 310 | c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); 311 | b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); 312 | 313 | a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); 314 | d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); 315 | c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); 316 | b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); 317 | a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); 318 | d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); 319 | c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); 320 | b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); 321 | a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); 322 | d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); 323 | c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); 324 | b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); 325 | a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); 326 | d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); 327 | c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); 328 | b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); 329 | 330 | a = safe_add(a, olda); 331 | b = safe_add(b, oldb); 332 | c = safe_add(c, oldc); 333 | d = safe_add(d, oldd); 334 | } 335 | return Array(a, b, c, d); 336 | } 337 | 338 | /* 339 | * These functions implement the four basic operations the algorithm uses. 340 | */ 341 | function md5_cmn(q, a, b, x, s, t) 342 | { 343 | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); 344 | } 345 | function md5_ff(a, b, c, d, x, s, t) 346 | { 347 | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); 348 | } 349 | function md5_gg(a, b, c, d, x, s, t) 350 | { 351 | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); 352 | } 353 | function md5_hh(a, b, c, d, x, s, t) 354 | { 355 | return md5_cmn(b ^ c ^ d, a, b, x, s, t); 356 | } 357 | function md5_ii(a, b, c, d, x, s, t) 358 | { 359 | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); 360 | } 361 | 362 | /* 363 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 364 | * to work around bugs in some JS interpreters. 365 | */ 366 | function safe_add(x, y) 367 | { 368 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 369 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 370 | return (msw << 16) | (lsw & 0xFFFF); 371 | } 372 | 373 | /* 374 | * Bitwise rotate a 32-bit number to the left. 375 | */ 376 | function bit_rol(num, cnt) 377 | { 378 | return (num << cnt) | (num >>> (32 - cnt)); 379 | } 380 | -------------------------------------------------------------------------------- /public/pages/index.css: -------------------------------------------------------------------------------- 1 | /* 首页 */ 2 | 3 | /* 引入模块化 组件样式 */ 4 | @import url(../component/Tabs/Tabs.css); 5 | @import url(../component/Search/Search.css); 6 | @import url(../component/Guest/Guest.css); 7 | @import url(../component/User/User.css); 8 | @import url(../component/Nav/Nav.css); 9 | @import url(../component/Slider/Slider.css); 10 | @import url(../component/Section/Section.css); 11 | @import url(../component/Modal/Modal.css); 12 | @import url(../component/Select/Select.css); 13 | @import url(../component/StarList/StarList.css); 14 | @import url(../component/CascadeSelect/CascadeSelect.css); 15 | @import url(../component/LoginModal/LoginModal.css); 16 | @import url(../component/RegisterModal/RegisterModal.css); 17 | 18 | /* 布局样式 */ 19 | .g-banner{height:365px;position:relative;} 20 | .g-body{width:1080px;margin:50px auto 0;} 21 | .g-main{width:760px;float:left;} 22 | .g-side{width:295px;float:right;} 23 | .g-footer{width:1080px;margin:0 auto;} 24 | 25 | 26 | /* 活动进行时 参与模块*/ 27 | .m-list .activity_ongonging{padding-left:22px;padding-top:0;} 28 | .m-list .involve_activity{margin:40px 38px 0 20px;} 29 | .m-list .involve_activity .u-btn{display:block;width:72px;height:auto;padding:7px 0;margin:12px 0;font-size:11px;line-height:1em;} 30 | 31 | /* 侧边栏 */ 32 | .m-aside{margin-bottom:29px;background:#fff;} 33 | .m-aside > ul{padding-left:12px;padding-right:10px;} 34 | .m-aside > ul li{border-top:1px solid #e5e6e6;} 35 | .m-aside > ul li:nth-of-type(1){border:none;} 36 | .m-aside .aside_more{display:-webkit-flex;display:flex;padding-bottom:10px;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;align-items:center;font-size:13px;color:#727171;cursor:pointer;} 37 | .m-aside .aside_more .u-icon-moreright{margin-left:6px;} 38 | .m-aside .u-icon{font-size:32px;color:#5ecbb5;} 39 | 40 | /* 侧边栏 排行 */ 41 | .m-aside .ranking_header .title{width:100px;height:67px;margin-right:10px;background-color:#5ed0ba;line-height:67px;text-align:center;font-size:11px;color:#fff;} 42 | .m-aside .ranking_cnt{position:relative;margin-top:8px;padding-top:16px;} 43 | .m-aside .ranking_cnt li{padding-top:16px;padding-left:5px;border-color:#e5e6e6;} 44 | .m-aside .ranking_cnt li img{width:136px;height:113px;margin-right:13px;margin-bottom:15px;} 45 | .m-aside .ranking_cnt li h6{margin-top:20px;margin-bottom:11px;line-height:1em;font-size:12px;} 46 | .m-aside .ranking_cnt li p{margin-top:0;margin-bottom:28px;font-size:12px;color:#727171;} 47 | .m-aside .ranking_cnt li p + p{margin-bottom:24px;font-size:9px;} 48 | .m-aside .ranking_cnt li p span{margin-right:10px;} 49 | .m-aside .ranking_cnt .u-icon-morebottom{position:absolute;bottom:-20px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:32px;color: #5ecbb5;} 50 | 51 | /* 侧边栏 达人排行榜 */ 52 | .m-aside .authorranking_header .title{width:100%;background-color:#5ed0ba;line-height:67px;text-align:center;font-size:11px;color:#fff;} 53 | .m-aside .authorranking_cnt{position:relative;padding-bottom:26px;} 54 | .m-aside .authorranking_cnt li{padding-top:29px;padding-left:30px;border-color:#e7e8e8;} 55 | .m-aside .authorranking_cnt li img{width:69px;height:69px;margin-right:23px;margin-bottom:30px;-webkit-border-radius:50%;border-radius:50%;} 56 | .m-aside .authorranking_cnt li h6{margin-top:16px;margin-bottom:15px;line-height:1em;font-size:10px;} 57 | .m-aside .authorranking_cnt li p{margin-top:0;margin-bottom:28px;font-size:9px;color:#727171;} 58 | .m-aside .authorranking_cnt li p span{margin-right:15px;} 59 | .m-aside .authorranking_cnt .u-icon-morebottom{position:absolute;bottom:-20px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:32px;color: #5ecbb5;} 60 | 61 | /* 侧边栏 热门话题 */ 62 | .m-aside .hottopic_header .title{width:100%;background-color:#5ed0ba;line-height:67px;text-align:center;font-size:11px;color:#fff;} 63 | .m-aside .hottopic_cnt li{padding:10px 29px;} 64 | .m-aside .hottopic_cnt li .title{position:relative;height:34px;overflow:hidden;font-size:11px;line-height:17px;} 65 | .m-aside .hottopic_cnt li .title::after{position:absolute;bottom:0;right:0;content:'. . . . .';padding-right:55%;background:#fff;} 66 | 67 | /* 侧边栏 圈子 */ 68 | .m-aside .circle_header .title{display:inline-block;width:50%;border-bottom:1px solid #ebebeb;line-height:67px;text-align:center;font-size:11px;cursor:pointer;} 69 | .m-aside .circle_header .title :checked ~ div{border-bottom:1px solid #5ed0ba;background-color:#5ed0ba;color:#fff;} 70 | .m-aside .circle_cnt li{border:none;} 71 | .m-aside .circle_cnt li img{width:87px;height:87px;margin-right:14px;margin-top:11px;} 72 | .m-aside .circle_cnt li .f-oh{padding-left:35px;border-top:1px solid #e5e6e6;} 73 | .m-aside .circle_cnt li:nth-of-type(1) .f-oh{border:none;} 74 | .m-aside .circle_cnt li h6{margin-top:33px;margin-bottom:14px;font-size:12px;line-height:1em;} 75 | .m-aside .circle_cnt li p{margin-bottom:30px;font-size:8px;line-height:1em;} 76 | 77 | /* 侧边栏 我的作品 */ 78 | .my_work{display:block;margin-left:-17px;margin-top:-19px;width:313px;background:rgba(0,0,0,0);} 79 | 80 | 81 | /* 底栏 */ 82 | .g-footer{margin-bottom:96px;} 83 | .g-footer .footer_main{float:left;width:760px;} 84 | .g-footer .footer_aside{float:right;width:295px;} 85 | .g-footer .u-btn{height:84px;line-height:84px;font-size:13px;} 86 | .g-footer .u-btn-link{border-color:#e7e7e7;color:#000;} 87 | -------------------------------------------------------------------------------- /public/pages/index.js: -------------------------------------------------------------------------------- 1 | // 首页 2 | (function(App){ 3 | 4 | var page = { 5 | 6 | // 初始化 7 | init: function(){ 8 | // 编译模板(主内容区) 9 | this.compileTemplateMain(); 10 | // 编译模板(侧边栏) 11 | this.compileTemplateAside(); 12 | // 底栏 13 | this.initFooter(); 14 | // 顶栏 15 | this.initNav(); 16 | // 轮播 17 | this.initSlider(); 18 | // 明日之星 19 | this.initStarList(); 20 | // 注册弹窗 21 | this.initRegisterModal(); 22 | // 登录弹窗 23 | this.initLoginModal(); 24 | // 侧边栏 排行tabs 25 | this.initRankingTabs(); 26 | }, 27 | 28 | // 编译模板(主内容区) 29 | compileTemplateMain: function(){ 30 | var html = ''; 31 | 32 | // 构建精选推荐 33 | html += App.template.m_section({ 34 | icon: 'u-icon-diamond', 35 | title: '/ 精 选 推 荐 /', 36 | cnt: App.template.list_img({ 37 | list_type: 'm-list-4', 38 | list: [ 39 | {img:'/res/images/work1.jpg',img_alt:'作品1'}, 40 | {img:'/res/images/work2.jpg',img_alt:'作品2'}, 41 | {img:'/res/images/work3.jpg',img_alt:'作品3'}, 42 | {img:'/res/images/work4.jpg',img_alt:'作品4'} 43 | ] 44 | }) 45 | }); 46 | // 明日之星 47 | html += App.template.m_section({ 48 | icon: 'u-icon-star', 49 | title: '/ 明 日 之 星 /', 50 | cnt: '
                ' 51 | }); 52 | // 构建最新作品 53 | html += App.template.m_section({ 54 | icon: 'u-icon-work', 55 | title: '/ 最 新 作 品 /', 56 | cnt: App.template.list_img({ 57 | list_type: 'm-list-5', 58 | list: [ 59 | {img:'/res/images/work5.jpg',img_alt:'作品1',img_name:'我是作品名字'}, 60 | {img:'/res/images/work5.jpg',img_alt:'作品2',img_name:'我是作品名字'}, 61 | {img:'/res/images/work6.jpg',img_alt:'作品3',img_name:'我是作品名字'}, 62 | {img:'/res/images/work5.jpg',img_alt:'作品4',img_name:'我是作品名字'}, 63 | {img:'/res/images/work5.jpg',img_alt:'作品5',img_name:'我是作品名字'}, 64 | {img:'/res/images/work5.jpg',img_alt:'作品6',img_name:'我是作品名字'}, 65 | {img:'/res/images/work5.jpg',img_alt:'作品7',img_name:'我是作品名字'}, 66 | {img:'/res/images/work6.jpg',img_alt:'作品8',img_name:'我是作品名字'}, 67 | {img:'/res/images/work5.jpg',img_alt:'作品9',img_name:'我是作品名字'}, 68 | {img:'/res/images/work5.jpg',img_alt:'作品10',img_name:'我是作品名字'} 69 | ] 70 | }) 71 | }); 72 | // 活动进行时 73 | html += App.template.m_section({ 74 | icon: 'u-icon-time', 75 | title: '/ 活 动 进 行 时 /', 76 | cnt: App.template.list_activity({ 77 | list_type: 'm-list-1', 78 | list: [ 79 | {img:'/res/images/activity1.png',times:'7.23-8.12',status:'正在进行'}, 80 | {img:'/res/images/activity2.png',times:'7.23-8.12',status:'正在进行'} 81 | ] 82 | }) 83 | }); 84 | 85 | // 我们都爱原创 86 | html += App.template.m_section({ 87 | icon: 'u-icon-heart', 88 | title: '/ 我 们 都 爱 原 创 /', 89 | cnt: App.template.list_img({ 90 | list_type: 'm-list-5', 91 | list: [ 92 | {img:'/res/images/work7.jpg',img_alt:'作品1',img_name:'我是作品名字'}, 93 | {img:'/res/images/work5.jpg',img_alt:'作品2',img_name:'我是作品名字'}, 94 | {img:'/res/images/work6.jpg',img_alt:'作品3',img_name:'我是作品名字'}, 95 | {img:'/res/images/work5.jpg',img_alt:'作品4',img_name:'我是作品名字'}, 96 | {img:'/res/images/work5.jpg',img_alt:'作品5',img_name:'我是作品名字'}, 97 | {img:'/res/images/work5.jpg',img_alt:'作品6',img_name:'我是作品名字'}, 98 | {img:'/res/images/work6.jpg',img_alt:'作品7',img_name:'我是作品名字'}, 99 | {img:'/res/images/work5.jpg',img_alt:'作品8',img_name:'我是作品名字'}, 100 | {img:'/res/images/work5.jpg',img_alt:'作品9',img_name:'我是作品名字'}, 101 | ] 102 | }) 103 | }); 104 | // 我们都是同人粉 105 | html += App.template.m_section({ 106 | icon:'u-icon-people', 107 | title:'/ 我 们 都 是 同 人 粉 /', 108 | cnt: App.template.list_img({ 109 | list_type: 'm-list-5', 110 | list: [ 111 | {img:'/res/images/work7.jpg',img_alt:'作品1',img_name:'我是作品名字'}, 112 | {img:'/res/images/work5.jpg',img_alt:'作品2',img_name:'我是作品名字'}, 113 | {img:'/res/images/work6.jpg',img_alt:'作品3',img_name:'我是作品名字'}, 114 | {img:'/res/images/work5.jpg',img_alt:'作品4',img_name:'我是作品名字'}, 115 | {img:'/res/images/work5.jpg',img_alt:'作品5',img_name:'我是作品名字'}, 116 | {img:'/res/images/work5.jpg',img_alt:'作品6',img_name:'我是作品名字'}, 117 | {img:'/res/images/work6.jpg',img_alt:'作品7',img_name:'我是作品名字'}, 118 | {img:'/res/images/work5.jpg',img_alt:'作品8',img_name:'我是作品名字'}, 119 | {img:'/res/images/work5.jpg',img_alt:'作品9',img_name:'我是作品名字'} 120 | ] 121 | }) 122 | }); 123 | // 看谁临摹的最好 124 | html += App.template.m_section({ 125 | icon:'u-icon-pencle', 126 | title:'/ 看 谁 临 摹 的 最 好 /', 127 | cnt: App.template.list_img({ 128 | list_type: 'm-list-5', 129 | list: [ 130 | {img:'/res/images/work7.jpg',img_alt:'作品1',img_name:'我是作品名字'}, 131 | {img:'/res/images/work5.jpg',img_alt:'作品2',img_name:'我是作品名字'}, 132 | {img:'/res/images/work6.jpg',img_alt:'作品3',img_name:'我是作品名字'}, 133 | {img:'/res/images/work5.jpg',img_alt:'作品4',img_name:'我是作品名字'}, 134 | {img:'/res/images/work5.jpg',img_alt:'作品5',img_name:'我是作品名字'}, 135 | {img:'/res/images/work5.jpg',img_alt:'作品6',img_name:'我是作品名字'}, 136 | {img:'/res/images/work6.jpg',img_alt:'作品7',img_name:'我是作品名字'}, 137 | {img:'/res/images/work5.jpg',img_alt:'作品8',img_name:'我是作品名字'}, 138 | {img:'/res/images/work5.jpg',img_alt:'作品9',img_name:'我是作品名字'} 139 | ] 140 | }) 141 | }); 142 | 143 | // 编译结果,载入页面主内容区 144 | _.getElementsByClassName(document, 'g-main')[0].innerHTML = html; 145 | 146 | }, 147 | 148 | // 编译模板(侧边栏) 149 | compileTemplateAside: function(){ 150 | var html = ''; 151 | 152 | html += ``; 153 | 154 | // 圈子 155 | html += App.template.aside_circle({ 156 | list: [ 157 | {img_url: '/res/images/circle1.jpg', circle_name: '门口小贩',circle_members:5221}, 158 | {img_url: '/res/images/circle2.jpg', circle_name: '原画集中营',circle_members:5221}, 159 | {img_url: '/res/images/circle3.jpg', circle_name: '—— Horizon ——',circle_members:5221} 160 | ] 161 | }); 162 | 163 | // 热门话题 164 | html += App.template.aside_hottopic({ 165 | list: [ 166 | {title: '1. [萝莉学院] 你不知道的那些事儿 这是标题标题 这是标题标题 这是标题标题 这是标题标题'}, 167 | {title: '1. [萝莉学院] 你不知道的那些事儿 这是标题标题 这是标题标题 这是标题标题 这是标题标题'}, 168 | {title: '1. [萝莉学院] 你不知道的那些事儿 这是标题标题 这是标题标题 这是标题标题 这是标题标题'}, 169 | {title: '1. [萝莉学院] 你不知道的那些事儿 这是标题标题 这是标题标题 这是标题标题 这是标题标题'}, 170 | {title: '1. [萝莉学院] 你不知道的那些事儿 这是标题标题 这是标题标题 这是标题标题 这是标题标题'} 171 | ] 172 | }); 173 | 174 | // 排行 175 | html += App.template.aside_ranking({ 176 | list: [ 177 | {img_url: '/res/images/work5.jpg',work_name: '我是作品名称',author_name: '用户名',visit_num: 2348,collection_num: 421}, 178 | {img_url: '/res/images/work6.jpg',work_name: '我是作品名称',author_name: '用户名',visit_num: 2348,collection_num: 421}, 179 | {img_url: '/res/images/work8.jpg',work_name: '我是作品名称',author_name: '用户名',visit_num: 2348,collection_num: 421}, 180 | {img_url: '/res/images/work9.jpg',work_name: '我是作品名称',author_name: '用户名',visit_num: 2348,collection_num: 421}, 181 | {img_url: '/res/images/work10.jpg',work_name: '我是作品名称',author_name: '用户名',visit_num: 2348,collection_num: 421} 182 | ] 183 | }); 184 | 185 | // 达人排行 186 | html += App.template.aside_authorranking({ 187 | list: [ 188 | {img_url: '/res/images/avatar0.jpg',author_name: 'Grinch',works_num: 2348,fans_num: 421}, 189 | {img_url: '/res/images/avatar0.jpg',author_name: 'Grinch',works_num: 2348,fans_num: 421}, 190 | {img_url: '/res/images/avatar0.jpg',author_name: 'Grinch',works_num: 2348,fans_num: 421}, 191 | {img_url: '/res/images/avatar0.jpg',author_name: 'Grinch',works_num: 2348,fans_num: 421}, 192 | {img_url: '/res/images/avatar0.jpg',author_name: 'Grinch',works_num: 2348,fans_num: 421} 193 | ] 194 | }); 195 | 196 | // 编译结果,载入页面主内容区 197 | _.getElementsByClassName(document, 'g-side')[0].innerHTML = html; 198 | }, 199 | 200 | initFooter: function(){ 201 | var html = ` 204 | `; 207 | 208 | _.getElementsByClassName(document, 'g-footer')[0].innerHTML = html; 209 | }, 210 | 211 | // 初始化顶栏 212 | initNav: function(){ 213 | // 构建顶栏 214 | new App.Nav({parent: _.getElementsByClassName(document, 'g-header')[0]}); 215 | }, 216 | // 初始化轮播图 217 | initSlider: function(){ 218 | // 构建轮播图 219 | new App.Slider({ 220 | parent: _.getElementsByClassName(document, 'g-banner')[0], 221 | imgArray: [ 222 | '../res/images/Slider/banner0.jpg', 223 | '../res/images/Slider/banner1.jpg', 224 | '../res/images/Slider/banner2.jpg', 225 | '../res/images/Slider/banner3.jpg' 226 | ], 227 | interval: 5000 228 | }); 229 | }, 230 | // 初始化明日之星 231 | initStarList: function(){ 232 | new App.StarList({location: document.getElementById('star_list')}); 233 | }, 234 | // 初始化注册弹窗 235 | initRegisterModal: function(){ 236 | // 注册弹窗 237 | new App.RegisterModal({parent: document.body}); 238 | }, 239 | // 初始化登录弹窗 240 | initLoginModal: function(){ 241 | // 登录弹窗 242 | new App.LoginModal({parent: document.body}); 243 | }, 244 | initRankingTabs: function(){ 245 | // 侧边栏 排行tabs 246 | new App.Tabs({ 247 | parent: document.getElementById('ranking_tabs'), 248 | nTabData: [ 249 | {name:'原 创',url:'javascript:;'}, 250 | {name:'同 人',url:'javascript:;'}, 251 | {name:'临 摹',url:'javascript:;'} 252 | ], 253 | list_type: 'm-tabs-aside' 254 | }); 255 | } 256 | } 257 | 258 | document.addEventListener('DOMContentLoaded', function(e){ 259 | page.init(); 260 | }); 261 | 262 | })(window.App); -------------------------------------------------------------------------------- /public/pages/search.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/pages/search.css -------------------------------------------------------------------------------- /public/pages/search.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/pages/search.js -------------------------------------------------------------------------------- /public/pages/works/create.css: -------------------------------------------------------------------------------- 1 | /* 作品创建页 */ 2 | 3 | /* 引入模块化 组件样式 */ 4 | @import url(../../component/Tabs/Tabs.css); 5 | @import url(../../component/Search/Search.css); 6 | @import url(../../component/Guest/Guest.css); 7 | @import url(../../component/User/User.css); 8 | @import url(../../component/Nav/Nav.css); 9 | @import url(../../component/ClassificWorks/ClassificWorks.css); 10 | @import url(../../component/UploadPictures/UploadPictures.css); 11 | @import url(../../component/Modal/Modal.css); 12 | @import url(../../component/AlertModal/AlertModal.css); 13 | @import url(../../component/WorkDescription/WorkDescription.css); 14 | @import url(../../component/Tag/Tag.css); 15 | @import url(../../component/Select/Select.css); 16 | @import url(../../component/Radio/Radio.css); 17 | @import url(../../component/WorkPermission/WorkPermission.css); 18 | 19 | /* 页面样式 */ 20 | .g-body{width:1098px;margin:54px auto 50px;} 21 | .g-main,.g-side{border:1px solid #e0e1e1;box-sizing:border-box;} 22 | /* 主栏 */ 23 | .g-main{float:left;position:relative;width:770px;padding-bottom:78px;background:#fff;} 24 | .g-main .subimit{position:absolute;left:161px;bottom:40px;width:101px;} 25 | .g-side{float:right;width:316px;padding-left:20px;padding-right:28px;box-sizing:border-box;background:#f7f8f8;} -------------------------------------------------------------------------------- /public/pages/works/create.js: -------------------------------------------------------------------------------- 1 | // 作品创建页 2 | (function(App){ 3 | 4 | var page = { 5 | 6 | // 初始化 7 | init: function(){ 8 | // 未登录,则跳转回首页 9 | App.emitter.on('notLogin', function(){ 10 | location.href = '/index'; 11 | }); 12 | // 顶栏 13 | this.initNav(); 14 | // 作品分类模块 15 | this.initClassificWorks(); 16 | // 上传图片模块 17 | this.initUploadPictures(); 18 | // 作品名称、描述模块 19 | this.initWorkDescription(); 20 | // 标签 21 | this.initTag(); 22 | // 权限设置 23 | this.initWorkPermission(); 24 | // 创建按钮 25 | this.initSubmitBtn(); 26 | // 警告弹窗 27 | this.initAlertModal(); 28 | // 绑定事件 29 | this.addEvent(); 30 | }, 31 | 32 | // 初始化顶栏 33 | initNav: function(){ 34 | // 构建顶栏 35 | new App.Nav({parent: _.getElementsByClassName(document, 'g-header')[0]}); 36 | }, 37 | // 初始化作品分类组件 38 | initClassificWorks: function(){ 39 | this.classific_works = App.ClassificWorks.init({parent: _.getElementsByClassName(document, 'g-main')[0]}); 40 | }, 41 | // 初始化上传图片组件 42 | initUploadPictures: function(){ 43 | // 构建上传图片 44 | this.upload_pictures = new App.UploadPictures({parent: _.getElementsByClassName(document, 'g-main')[0]}); 45 | }, 46 | // 初始化作品名称、描述组件 47 | initWorkDescription: function(){ 48 | this.work_description = App.WorkDescription.init({parent: _.getElementsByClassName(document, 'g-main')[0]}); 49 | }, 50 | // 初始化标签 51 | initTag: function(){ 52 | _.ajax({ 53 | url: '/api/tags?recommend', 54 | method: 'GET', 55 | success: function(data){ 56 | data = JSON.parse(data); 57 | if(data.code == 200){ 58 | this.tag = new App.Tag({ 59 | parent: _.getElementsByClassName(document, 'g-main')[0], 60 | // tags: ['18禁','萝莉'], 61 | tags_recommend: data.result.split(',') 62 | }); 63 | } 64 | else{ 65 | console.log(data.msg); 66 | } 67 | }.bind(this) 68 | }); 69 | }, 70 | // 初始化权限设置 71 | initWorkPermission: function(){ 72 | // 73 | this.work_permission = new App.WorkPermission({parent: _.getElementsByClassName(document ,'g-side')[0]}); 74 | }, 75 | // 初始化创建按钮 76 | initSubmitBtn: function(){ 77 | // 提交按钮 78 | this.submit_btn = _.html2node(``); 79 | // 挂载按钮 80 | _.getElementsByClassName(document, 'g-main')[0].appendChild(this.submit_btn); 81 | }, 82 | // 初始化注册弹窗 83 | initAlertModal: function(){ 84 | new App.AlertModal({parent: document.body}); 85 | }, 86 | // 绑定事件 87 | addEvent: function(){ 88 | // 绑定提交表单事件 89 | this.submit_btn.addEventListener('click', function(){ 90 | // 新创建作品数据 91 | var new_work = {}; 92 | 93 | // 向作品数据中,添加 名称、描述 94 | _.extend(new_work, this.work_description.getValue()); 95 | // 向作品数据中,添加 封面图片id、封面图片地址、图片列表 96 | _.extend(new_work, this.upload_pictures.getValue()); 97 | // 向作品数据中,添加 标签 98 | new_work.tag = this.tag.getValue(); 99 | // 向作品数据中,添加 分类 100 | new_work.category = this.classific_works.getValue(); 101 | // 向作品数据中,添加 权限设置、授权类型 102 | _.extend(new_work, this.work_permission.getValue()); 103 | 104 | console.log(new_work); 105 | // 若作品名称为空,则停止提交 106 | this.subimitForm(new_work); 107 | }.bind(this)); 108 | }, 109 | // 检查表单 110 | checkForm: function(data){ 111 | var result = true; 112 | // 若作品名字为空,则验证不通过 113 | if(!data.name){result = false;} 114 | // 若作品列表为空,则验证不通过 115 | else if(data.pictures.length === 0){ 116 | App.emitter.emit('alert', '请选择图片上传'); 117 | result = false; 118 | } 119 | // 若作品列表为空,则验证不通过 120 | else if(!data.coverId || !data.coverUrl){ 121 | App.emitter.emit('alert', '请设置封面图片'); 122 | result = false; 123 | } 124 | return result; 125 | }, 126 | // 提交表单 127 | subimitForm: function(data){ 128 | // 若表单数据不合格,则不做提交请求 129 | if(!this.checkForm(data)){return;} 130 | // 提交表单请求 131 | _.ajax({ 132 | url: '/api/works', 133 | method: 'POST', 134 | data: data, 135 | success: function(res){ 136 | res = JSON.parse(res); 137 | console.log(res); 138 | // 上传成功 139 | if(res.code === 200){ 140 | // 跳转回列表页 141 | location.assign('/works'); 142 | } 143 | }, 144 | fail: function(e){ 145 | console.log(e); 146 | }, 147 | header: {'content-type': 'application/json'} 148 | }); 149 | } 150 | } 151 | 152 | document.addEventListener('DOMContentLoaded', function(e){ 153 | page.init(); 154 | }); 155 | 156 | console.dir(page); 157 | 158 | })(window.App); -------------------------------------------------------------------------------- /public/pages/works/detail.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/pages/works/detail.css -------------------------------------------------------------------------------- /public/pages/works/detail.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/pages/works/detail.js -------------------------------------------------------------------------------- /public/pages/works/list.css: -------------------------------------------------------------------------------- 1 | /* 作品列表页 */ 2 | 3 | /* 引入模块化 组件样式 */ 4 | @import url(../../component/Tabs/Tabs.css); 5 | @import url(../../component/Search/Search.css); 6 | @import url(../../component/Guest/Guest.css); 7 | @import url(../../component/User/User.css); 8 | @import url(../../component/Nav/Nav.css); 9 | @import url(../../component/Profile/Profile.css); 10 | @import url(../../component/WorksList/WorksList.css); 11 | @import url(../../component/Pagination/Pagination.css); 12 | @import url(../../component/Aside/Aside.css); 13 | @import url(../../component/Modal/Modal.css); 14 | @import url(../../component/ConfirmModal/ConfirmModal.css); 15 | 16 | /* 页面样式 */ 17 | .g-banner{height:250px;background:url(../../res/images/banner4.jpg) 50% 50%/cover no-repeat;} 18 | .g-body{width:744px;margin:0 auto;} 19 | /* 作者简介 */ 20 | .g-profile{height:216px;margin-top:-115px;background-color:#fff;} 21 | /* 主内容区 */ 22 | .g-main{position:relative;padding-bottom:28px;margin-bottom:48px;background-color:#fff;} 23 | .m-aside{position:absolute;left:-100px;width:100px;height:276px;background-color:#fff;} 24 | .g-wrap{margin:0 14px;padding:0 36px;min-height:300px;border-top:1px solid #e2e3e3;box-sizing:border-box;} -------------------------------------------------------------------------------- /public/pages/works/list.js: -------------------------------------------------------------------------------- 1 | // 作品列表页 2 | (function(App){ 3 | 4 | var page = { 5 | 6 | // 初始化 7 | init: function(){ 8 | // 未登录,则跳转回首页 9 | App.emitter.on('notLogin', function(){ 10 | location.href = '/index'; 11 | }); 12 | // 顶栏 13 | this.initNav(); 14 | // 用户简介 15 | this.initProfile(); 16 | // 作品列表 17 | this.initWorksList(); 18 | // 侧边栏 19 | this.initAside(); 20 | // 确认弹窗 21 | this.initConfirmModal(); 22 | }, 23 | 24 | // 初始化顶栏 25 | initNav: function(){ 26 | // 构建顶栏 27 | new App.Nav({parent: _.getElementsByClassName(document, 'g-header')[0]}); 28 | }, 29 | // 初始化用户简介 30 | initProfile: function(){ 31 | // 构建用户简介 32 | new App.Profile({parent: _.getElementsByClassName(document, 'g-profile')[0]}); 33 | }, 34 | // 作品列表 35 | initWorksList: function(){ 36 | // 构建作品列表 37 | new App.WorksList({parent: _.getElementsByClassName(document, 'g-wrap')[0]}); 38 | }, 39 | // 侧边栏 40 | initAside: function(){ 41 | App.Aside.init({parent: _.getElementsByClassName(document, 'm-aside')[0]}); 42 | }, 43 | // 确认弹窗 44 | initConfirmModal: function(){ 45 | new App.ConfirmModal({parent: document.body}); 46 | } 47 | } 48 | 49 | 50 | document.addEventListener('DOMContentLoaded', function(e){ 51 | page.init(); 52 | }); 53 | 54 | })(window.App); -------------------------------------------------------------------------------- /public/res/images/Slider/banner0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/Slider/banner0.jpg -------------------------------------------------------------------------------- /public/res/images/Slider/banner1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/Slider/banner1.jpg -------------------------------------------------------------------------------- /public/res/images/Slider/banner2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/Slider/banner2.jpg -------------------------------------------------------------------------------- /public/res/images/Slider/banner3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/Slider/banner3.jpg -------------------------------------------------------------------------------- /public/res/images/activity1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/activity1.png -------------------------------------------------------------------------------- /public/res/images/activity2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/activity2.png -------------------------------------------------------------------------------- /public/res/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar.png -------------------------------------------------------------------------------- /public/res/images/avatar0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar0.jpg -------------------------------------------------------------------------------- /public/res/images/avatar1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar1.jpg -------------------------------------------------------------------------------- /public/res/images/avatar2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar2.jpg -------------------------------------------------------------------------------- /public/res/images/avatar3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar3.jpg -------------------------------------------------------------------------------- /public/res/images/avatar4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar4.jpg -------------------------------------------------------------------------------- /public/res/images/avatar5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/avatar5.jpg -------------------------------------------------------------------------------- /public/res/images/banner4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/banner4.jpg -------------------------------------------------------------------------------- /public/res/images/circle1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/circle1.jpg -------------------------------------------------------------------------------- /public/res/images/circle2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/circle2.jpg -------------------------------------------------------------------------------- /public/res/images/circle3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/circle3.jpg -------------------------------------------------------------------------------- /public/res/images/iconfont/u-icon-pencle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/iconfont/u-icon-pencle.png -------------------------------------------------------------------------------- /public/res/images/iconfont/u-icon-people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/iconfont/u-icon-people.png -------------------------------------------------------------------------------- /public/res/images/iconfont/u-icon-work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/iconfont/u-icon-work.png -------------------------------------------------------------------------------- /public/res/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/loading.gif -------------------------------------------------------------------------------- /public/res/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/logo.png -------------------------------------------------------------------------------- /public/res/images/my_work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/my_work.png -------------------------------------------------------------------------------- /public/res/images/page-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/page-active.png -------------------------------------------------------------------------------- /public/res/images/work1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work1.jpg -------------------------------------------------------------------------------- /public/res/images/work10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work10.jpg -------------------------------------------------------------------------------- /public/res/images/work2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work2.jpg -------------------------------------------------------------------------------- /public/res/images/work3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work3.jpg -------------------------------------------------------------------------------- /public/res/images/work4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work4.jpg -------------------------------------------------------------------------------- /public/res/images/work5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work5.jpg -------------------------------------------------------------------------------- /public/res/images/work6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work6.jpg -------------------------------------------------------------------------------- /public/res/images/work7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work7.jpg -------------------------------------------------------------------------------- /public/res/images/work8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work8.jpg -------------------------------------------------------------------------------- /public/res/images/work9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yibay/Web-netease-Ego/e78e6d7c4c8b92d42cc8e059ac543ea86ee6e877/public/res/images/work9.jpg --------------------------------------------------------------------------------