├── .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 |
5 | - 项目首页:轮播图展示作品、明日之星关注、用户注册、用户登录
6 | - 作品列表页:分页器展示 登录用户个人的作品列表、可坐编辑、删除操作、根据生日计算用户星座并显示。
7 | - 作品创建页:设置作品名称、标签、分类、权限、上传作品图片(支持批量上传、拖拽上传,限制单次上传图片的数量和大小)
8 |
9 | 终端命令
10 |
11 | npm install nei -g
若未安装nei脚手架,先全局安装nei
12 | nei server
在项目根目录下,启动 本地项目服务器
13 |
14 | WebStorm Run/Debug配置
15 | 如要使用 WebStorm Run/Debug 来运行项目,配置如下:
16 |
17 | -
18 | 方案1:Add New Configuration > npm > Command 为 test
19 |
20 | -
21 | 方案2:Add New Configuration > Node.js > JavaScirpt file 为 WebStorm_run_nei.js
22 |
23 |
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 |
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 |
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 |
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 |
155 |
156 | {{#each list}}
157 | -
158 |
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 |
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 |
10 | 提示信息
11 |
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 |
11 | 提示信息
12 |
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 = ``;
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 = `
10 |

11 |
`;
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 = ``;
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 |
11 |
12 |
13 |
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 = ``;
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 |
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 = ``;
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 |
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 |
10 |

11 |
12 |
13 |
14 |
15 |
16 |
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 | `;
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 |
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 |
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
--------------------------------------------------------------------------------