├── pages
├── detail
│ ├── detail.json
│ ├── detail.js
│ ├── detail.wxml
│ └── detail.wxss
└── index
│ ├── index.json
│ ├── index.js
│ ├── index.wxml
│ └── index.wxss
├── images
├── home.png
├── love.png
├── medal.png
├── speech.png
├── trophy.png
└── favorite.png
├── app.json
├── app.wxss
├── README.md
├── LICENSE
└── app.js
/pages/detail/detail.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/images/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/home.png
--------------------------------------------------------------------------------
/images/love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/love.png
--------------------------------------------------------------------------------
/images/medal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/medal.png
--------------------------------------------------------------------------------
/images/speech.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/speech.png
--------------------------------------------------------------------------------
/images/trophy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/trophy.png
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index",
4 | "pages/detail/detail"
5 | ]
6 | }
--------------------------------------------------------------------------------
/images/favorite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackSwjtu/weapp-clawjtu/HEAD/images/favorite.png
--------------------------------------------------------------------------------
/pages/detail/detail.js:
--------------------------------------------------------------------------------
1 | let app = getApp();
2 | Page({
3 | data: {
4 | post: null
5 | },
6 | onLoad: function(_options) {
7 | let _type = _options.id.slice(0, _options.id.indexOf('_'));
8 | this.setData({
9 | post: app.globalData[_type + 's'].get(_options.id)
10 | });
11 | }
12 | });
--------------------------------------------------------------------------------
/pages/detail/detail.wxml:
--------------------------------------------------------------------------------
1 | {{post.title}}
2 |
3 |
4 | {{description}}
5 |
6 |
7 | {{post.detail}}
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | page {
2 | position: relative;
3 | height: 100%;
4 | width: 100%;
5 | --tablet-height: 90rpx;
6 | --banner-height: 152rpx;
7 | font-family: 等线, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
8 | background-color: #FAFAFA;
9 | color: rgb(88, 89, 91);
10 | }
11 | .icon {
12 | height: 50rpx;
13 | width: 50rpx;
14 | }
15 | .icon.active {
16 | filter: brightness(2);
17 | }
18 | .hr {
19 | display: block;
20 | height: 1px;
21 | width: 100%;
22 | background-color: rgb(198, 186, 186);
23 | }
--------------------------------------------------------------------------------
/pages/detail/detail.wxss:
--------------------------------------------------------------------------------
1 | #title {
2 | font-size: 48rpx;
3 | font-weight: 700;
4 | }
5 | .medal {
6 | display: inline-block;
7 | background-image: url(../../images/medal.png);
8 | background-position: center center;
9 | background-size: contain;
10 | }
11 | .post-description {
12 | display: block;
13 | font-size: 32rpx;
14 | font-weight: 700;
15 | }
16 | #detail {
17 | font-size: 30rpx;
18 | line-height: 125%;
19 | }
20 | .detail-block {
21 | --vmargin: 30rpx;
22 | --hmargin: 60rpx;
23 | margin: var(--vmargin) var(--hmargin);
24 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # weapp-clawjtu
2 |
3 | 
4 | 
5 | 
6 |
7 | 
8 |
9 | > Clawjtu - 查询交大最新讲座信息和学术竞赛信息的微信小程序
10 |
11 | ## Data source
12 |
13 | + [pyspider-clawswjtu](https://github.com/HackSwjtu/pyspider-clawswjtu)
14 |
15 | ## MIT License
16 |
17 | Copyright (c) 2017 Hack Swjtu
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in all
27 | copies or substantial portions of the Software.
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Hack Swjtu
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 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | let app = getApp();
2 | Page({
3 | data: {
4 | userInfo: null,
5 | posts: null,
6 | status: 'all',
7 | scroll_top: 0
8 | },
9 | backToTop() {
10 | this.setData({scroll_top: 0});
11 | },
12 | toggleFav(_event) {
13 | let _id = _event.currentTarget.id.slice(4);
14 | app.toggleFav({
15 | id: _id,
16 | success: () => this.getCurrent()
17 | });
18 | },
19 | setPosts(_posts) {
20 | this.setData({posts: _posts});
21 | },
22 | getPosts(_type) {
23 | if(this.data.status !== _type)
24 | this.backToTop();
25 | this.setData({status: _type});
26 | if(_type == 'fav')
27 | return app.getPosts({
28 | success: _posts => this.setPosts(_posts.filter(_post => _post.favorited))
29 | });
30 | if(_type == 'all')
31 | return app.getPosts({
32 | success: this.setPosts
33 | });
34 | app.getPostsByType({
35 | type: _type,
36 | success: this.setPosts
37 | });
38 | },
39 | getPostsByEvent(_event) {
40 | this.getPosts(_event.currentTarget.id);
41 | },
42 | refreshPosts() {
43 | app.refreshPosts({success: this.setPosts});
44 | },
45 | getCurrent() {
46 | this.getPosts(this.data.status);
47 | },
48 | onLoad() {
49 | this.getPosts('all');
50 | app.getUserInfo({success: _userInfo => this.setData({userInfo: _userInfo})});
51 | }
52 | });
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{post.title}}
6 |
7 | {{description}}
8 |
9 |
10 |
11 | {{post.detail}}
12 |
13 |
14 |
15 |
16 |
17 |
18 | {{userInfo.nickName}}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | #banner {
2 | position: absolute;
3 | top: 0;
4 | height: var(--banner-height);
5 | width: 100%;
6 | display: flex;
7 | align-items: center;
8 | box-shadow: 0 0 16rpx rgba(0, 0, 0, 0.5);
9 | background-color: #F6F6F6;
10 | }
11 | #avatar {
12 | display: block;
13 | margin: 20rpx 40rpx 20rpx 60rpx;
14 | height: 112rpx;
15 | width: 112rpx;
16 | border-radius: 50%;
17 | }
18 | #post-container {
19 | --margin: 30rpx;
20 | position: absolute;
21 | padding-bottom: var(--margin);
22 | width: 100%;
23 | }
24 | .post {
25 | position: relative;
26 | --padding: 30rpx;
27 | margin: var(--margin);
28 | border-radius: 8rpx;
29 | background-color: white;
30 | box-shadow: 0 4rpx 8rpx 2rpx rgba(0, 0, 0, 0.25);
31 | }
32 | .post-info {
33 | position: relative;
34 | padding: var(--padding);
35 | }
36 | .post-title {
37 | display: block;
38 | width: calc(100% - var(--padding) - 50rpx);
39 | font-size: 32rpx;
40 | color: rgb(88, 89, 91);
41 | font-weight: 700;
42 | margin-bottom: 10px;
43 | }
44 | .post-description text {
45 | display: block;
46 | font-size: 24rpx;
47 | color: rgb(187, 187, 188);
48 | }
49 | .post-preview {
50 | padding: var(--padding);
51 | font-size: 28rpx;
52 | color: rgb(88, 89, 91);
53 | text-overflow: ellipsis;
54 | white-space: nowrap;
55 | overflow: hidden;
56 | }
57 | .post-love {
58 | position: absolute;
59 | right: var(--padding);
60 | top: var(--padding);
61 | filter: grayscale();
62 | }
63 | .post-love.loved {
64 | filter: grayscale(0);
65 | }
66 | #tablet {
67 | display: flex;
68 | width: 100%;
69 | align-items: center;
70 | justify-content: space-between;
71 | position: absolute;
72 | bottom: 0;
73 | height: var(--tablet-height);
74 | box-shadow: 0 0 16rpx rgba(0, 0, 0, 0.5);
75 | background-color: white;
76 | }
77 | .tablet {
78 | flex-grow: 1;
79 | text-align: center;
80 | background-color: white;
81 | }
82 | #fav {
83 | margin-right: 60rpx;
84 | height: 50rpx;
85 | width: 50rpx;
86 | }
87 | #post-container {
88 | height: calc(100% - var(--banner-height) - var(--tablet-height));
89 | top: var(--banner-height);
90 | }
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | let description_keywords = {
2 | competition: {
3 | time: 'publishdate'
4 | },
5 | lecture: {
6 | content: 'place',
7 | time: 'time',
8 | speaker: 'speaker'
9 | }
10 | };
11 | let parseDescriptions = (_post, _type) => {
12 | let _result = {};
13 | Object.keys(description_keywords[_type]).forEach(_key => _result[_key] = _post[description_keywords[_type][_key]]);
14 | return _result;
15 | };
16 | let parsePost = (_post, _favorited, _type) => ({
17 | id: _type + '_' + _post.id,
18 | title: _post.title,
19 | descriptions: parseDescriptions(_post, _type),
20 | detail: _post.detail,
21 | favorited: _favorited
22 | });
23 | let getFavsFromStorage = _type => {
24 | let _favorites;
25 | try{
26 | _favorites = JSON.parse(wx.getStorageSync('favorited_' + _type + '_id'));
27 | } catch(e) { _favorites = []; }
28 | return new Map(_favorites);
29 | };
30 | let parsePosts = (_posts, _type) => {
31 | let _favorites = getFavsFromStorage(_type);
32 | return new Map(
33 | _posts.map(_post => [
34 | _type + '_' + _post.id,
35 | parsePost(_post, !!_favorites.get(_type + '_' + _post.id), _type),
36 | ])
37 | );
38 | };
39 | let mapToArray = _map => {
40 | let _arr = [];
41 | _map.forEach(_element => _arr.push(_element));
42 | return _arr;
43 | };
44 | let compareTime = (_p0, _p1) => _p0.descriptions.time > _p1.descriptions.time;
45 | App({
46 | globalData: {
47 | userInfo: null,
48 | lectures: null,
49 | competitions: null,
50 | spider_apis: {
51 | lecture: 'https://api.hackswjtu.com/lecture/lastweek',
52 | competition: 'https://api.hackswjtu.com/competition/recent'
53 | }
54 | },
55 | refreshUserInfo(_options) {
56 | let _fail = () => {
57 | console.log(1);
58 | this.globalData.userInfo = null;
59 | _options.fail && _options.fail();
60 | };
61 | wx.login({
62 | success: () => {
63 | wx.getUserInfo({
64 | success: _res => {
65 | this.globalData.userInfo = _res.userInfo;
66 | _options.success(this.globalData.userInfo);
67 | },
68 | fail: _fail
69 | });
70 | },
71 | fail: _fail
72 | });
73 | },
74 | getUserInfo(_options) {
75 | let _fail = () => this.refreshUserInfo(_options);
76 | if(!this.globalData.userInfo)
77 | !this.refreshUserInfo(_options);
78 | },
79 | refreshPostsByType(_options) {
80 | wx.request({
81 | url: this.globalData.spider_apis[_options.type],
82 | success: _res => {
83 | this.globalData[_options.type + 's'] = parsePosts(_res.data[_options.type], _options.type);
84 | _options.success(mapToArray(this.globalData[_options.type + 's']).sort(compareTime));
85 | },
86 | fail: () => {
87 | this.globalData[_options.type + 's'] = null;
88 | _options.fail && _options.fail();
89 | }
90 | });
91 | },
92 | getPostsByType(_options) {
93 | if(this.globalData[_options.type + 's'] !== null)
94 | _options.success(mapToArray(this.globalData[_options.type + 's']).sort(compareTime));
95 | else
96 | this.refreshPostsByType(_options);
97 | },
98 | refreshPosts(_options) {
99 | let _result;
100 | this.refreshPostsByType({
101 | type: 'lecture',
102 | success: _lectures => {
103 | this.refreshPostsByType({
104 | type: 'competition',
105 | success: _competitions => _options.success(_lectures.concat(_competitions).sort(compareTime)),
106 | fail: _options.fail
107 | });
108 | },
109 | fail: () => _options.fail && _options.fail()
110 | });
111 | },
112 | getEachTypesOfPosts(_options, _types, _results = []) {
113 | let _current_type = _types.shift();
114 | let _current_options = {fail: _options.fail};
115 | let _success = _res => {
116 | _results = _results.concat(_res);
117 | _types.length ? this.getEachTypesOfPosts(_options, _types, _results) : _options.success(_results.sort(compareTime));
118 | };
119 | this.getPostsByType({
120 | type: _current_type,
121 | success: _success,
122 | fail: _options.fail
123 | });
124 | },
125 | getPosts(_options) {
126 | this.getEachTypesOfPosts(_options, [
127 | 'lecture',
128 | 'competition'
129 | ]);
130 | },
131 | toggleFav(_options) {
132 | let _type = _options.id.slice(0, _options.id.indexOf('_'));
133 | let _map = this.globalData[_type + 's'];
134 | let _copy = _map.get(_options.id);
135 | _copy.favorited = !(_copy.favorited);
136 | _map.set(_options.id, _copy);
137 | wx.setStorage({
138 | key: 'favorited_' + _type + '_id',
139 | data: JSON.stringify(
140 | mapToArray(_map).map(_post => [_post.id, _post.favorited])
141 | ),
142 | success: _options.success
143 | });
144 | },
145 | onLaunch() {
146 | wx.setNavigationBarTitle({title: 'Clawjtu'});
147 | }
148 | });
--------------------------------------------------------------------------------