├── src
├── components
│ ├── App
│ │ ├── app.scss
│ │ └── index.js
│ ├── City
│ │ ├── CityActivityDisplayMain
│ │ │ ├── cityActivityDisplayMain.scss
│ │ │ └── index.js
│ │ ├── CityActivityDisplay
│ │ │ ├── cityActivityDisplay.scss
│ │ │ └── index.js
│ │ ├── CityTag
│ │ │ ├── cityTag.scss
│ │ │ └── index.js
│ │ ├── CityTagItem
│ │ │ ├── cityTagItem.scss
│ │ │ └── index.js
│ │ ├── CityActivityDisplayHeader
│ │ │ ├── cityActivityDisplayHeader.scss
│ │ │ └── index.js
│ │ ├── CityActivityDisplayHeaderItem
│ │ │ ├── cityActivityDisplayHeaderItem.scss
│ │ │ └── index.js
│ │ ├── CityActivityDisplayMainItem
│ │ │ ├── cityActivityDisplayMainItem.scss
│ │ │ └── index.js
│ │ ├── CityActivity
│ │ │ └── index.js
│ │ └── CityActivityDisplayItem
│ │ │ └── index.js
│ ├── Main
│ │ ├── main.scss
│ │ └── index.js
│ ├── Common
│ │ ├── CommonSlider
│ │ │ ├── CommonSliderDots
│ │ │ │ ├── commonSliderDots.scss
│ │ │ │ ├── CommonSliderDot
│ │ │ │ │ ├── commonSliderDot.scss
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ ├── CommonSliderArrows
│ │ │ │ ├── commonSliderArrows.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── CommonTagList
│ │ │ ├── CommonTagItem
│ │ │ │ ├── commonTagItem.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── CommonModal
│ │ │ ├── commonModal.scss
│ │ │ └── index.js
│ ├── Music
│ │ └── MusicTagContent
│ │ │ ├── MusicTag
│ │ │ ├── musicTag.scss
│ │ │ └── index.js
│ │ │ ├── MusicTagItem
│ │ │ ├── musicTagItem.scss
│ │ │ └── index.js
│ │ │ ├── MusicDisplay
│ │ │ ├── index.js
│ │ │ └── musicDisplay.scss
│ │ │ └── index.js
│ ├── Book
│ │ ├── BookTag
│ │ │ ├── BookTagsList
│ │ │ │ ├── bookTagsList.scss
│ │ │ │ └── index.js
│ │ │ ├── bookTag.scss
│ │ │ ├── BookTagsItem
│ │ │ │ ├── bookTagsItem.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── BookTagContent
│ │ │ ├── bookTagContent.scss
│ │ │ ├── BookTagDisplayList
│ │ │ │ ├── bookTagDisplayList.scss
│ │ │ │ └── index.js
│ │ │ ├── BookTagDisplay
│ │ │ │ ├── bookTagDisplay.scss
│ │ │ │ └── index.js
│ │ │ ├── BookTagDisplayItem
│ │ │ │ ├── bookTagDisplayItem.scss
│ │ │ │ └── index.js
│ │ │ ├── BookTagPrompt
│ │ │ │ ├── bookTagPrompt.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── BookTagMoreContent
│ │ │ ├── BookTagMoreDisplay
│ │ │ │ ├── bookTagMoreDisplay.scss
│ │ │ │ └── index.js
│ │ │ ├── bookTagMoreContent.scss
│ │ │ ├── LoadMoreBooks
│ │ │ │ ├── loadMoreBooks.scss
│ │ │ │ └── index.js
│ │ │ ├── BookTagMoreDisplayList
│ │ │ │ └── index.js
│ │ │ ├── BookTagMoreDisplayItem
│ │ │ │ ├── bookTagMoreDisplayItem.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── BookTypeContent
│ │ │ ├── bookTypeContent.scss
│ │ │ ├── BookTypeDisplayList
│ │ │ └── index.js
│ │ │ ├── BookTypeDisplayItem
│ │ │ ├── bookTypeDisplayItem.scss
│ │ │ └── index.js
│ │ │ └── index.js
│ ├── Movie
│ │ ├── MovieTagContent
│ │ │ ├── movieTagContent.scss
│ │ │ ├── MovieTagDisplayList
│ │ │ │ ├── movieTagDisplayList.scss
│ │ │ │ └── index.js
│ │ │ ├── MovieTag
│ │ │ │ ├── movieTag.scss
│ │ │ │ └── index.js
│ │ │ ├── MovieTagDisplay
│ │ │ │ ├── movieTagDisplay.scss
│ │ │ │ └── index.js
│ │ │ ├── MovieTagDisplayItem
│ │ │ │ ├── movieTagDisplayItem.scss
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── MovieTypeContent
│ │ │ ├── MovieTypeDisplayList
│ │ │ ├── movieTypeDisplayList.scss
│ │ │ └── index.js
│ │ │ ├── MovieModal
│ │ │ ├── movieModal.scss
│ │ │ └── index.js
│ │ │ ├── movieTypeContent.scss
│ │ │ ├── MovieTypeDisplayItem
│ │ │ ├── movieTypeDisplayItem.scss
│ │ │ └── index.js
│ │ │ └── index.js
│ └── Header
│ │ ├── header.scss
│ │ ├── HeaderSuggest
│ │ ├── headerSuggest.scss
│ │ └── index.js
│ │ ├── HeaderInput
│ │ ├── headerInput.scss
│ │ └── index.js
│ │ ├── HeaderModules
│ │ ├── index.js
│ │ └── headerModules.scss
│ │ └── index.js
├── assets
│ ├── images
│ │ ├── book.png
│ │ ├── city.png
│ │ ├── favicon.ico
│ │ ├── movie.png
│ │ ├── music.png
│ │ ├── scoring.png
│ │ ├── book_search_btn.png
│ │ ├── city_search_btn.png
│ │ ├── movie_search_btn.png
│ │ └── music_search_btn.png
│ └── styles
│ │ ├── vars.scss
│ │ ├── mixin.scss
│ │ └── index.scss
├── stores
│ ├── index.js
│ └── modules
│ │ ├── musicStore.js
│ │ ├── cityStore.js
│ │ ├── headerStore.js
│ │ ├── movieStore.js
│ │ └── bookStore.js
├── index.js
├── apis
│ └── index.js
└── utils
│ ├── utils.js
│ └── registerServiceWorker.js
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── screenshot
├── search.png
├── bookTagContent.png
├── cityActivity.png
├── bookTypeContent.png
├── movieTagContent.png
├── movieTypeContent.png
├── musicTagContent.png
└── bookTagMoreContent.png
├── .eslintignore
├── config
├── jest
│ ├── fileTransform.js
│ └── cssTransform.js
├── polyfills.js
├── paths.js
├── env.js
├── webpackDevServer.config.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── .gitignore
├── .stylelintrc
├── .eslintrc
├── scripts
├── test.js
├── start.js
└── build.js
├── README.md
└── package.json
/src/components/App/app.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayMain/cityActivityDisplayMain.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/screenshot/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/search.png
--------------------------------------------------------------------------------
/src/components/Main/main.scss:
--------------------------------------------------------------------------------
1 | .main-content {
2 | margin: 0 auto;
3 | width: 936px;
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/images/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/book.png
--------------------------------------------------------------------------------
/src/assets/images/city.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/city.png
--------------------------------------------------------------------------------
/screenshot/bookTagContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/bookTagContent.png
--------------------------------------------------------------------------------
/screenshot/cityActivity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/cityActivity.png
--------------------------------------------------------------------------------
/src/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/favicon.ico
--------------------------------------------------------------------------------
/src/assets/images/movie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/movie.png
--------------------------------------------------------------------------------
/src/assets/images/music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/music.png
--------------------------------------------------------------------------------
/src/assets/images/scoring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/scoring.png
--------------------------------------------------------------------------------
/screenshot/bookTypeContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/bookTypeContent.png
--------------------------------------------------------------------------------
/screenshot/movieTagContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/movieTagContent.png
--------------------------------------------------------------------------------
/screenshot/movieTypeContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/movieTypeContent.png
--------------------------------------------------------------------------------
/screenshot/musicTagContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/musicTagContent.png
--------------------------------------------------------------------------------
/screenshot/bookTagMoreContent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/screenshot/bookTagMoreContent.png
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /config/
2 | /scripts/
3 | /public/
4 | /build/
5 | /coverage/
6 |
7 | /src/utils/registerServiceWorker.js
8 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderDots/commonSliderDots.scss:
--------------------------------------------------------------------------------
1 | .slider-dots {
2 | display: inline-block;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/MusicTag/musicTag.scss:
--------------------------------------------------------------------------------
1 | .music-tag-list {
2 | border-bottom: 2px solid #02a682;
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/images/book_search_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/book_search_btn.png
--------------------------------------------------------------------------------
/src/assets/images/city_search_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/city_search_btn.png
--------------------------------------------------------------------------------
/src/assets/images/movie_search_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/movie_search_btn.png
--------------------------------------------------------------------------------
/src/assets/images/music_search_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nh0007/react-douban/HEAD/src/assets/images/music_search_btn.png
--------------------------------------------------------------------------------
/src/components/Book/BookTag/BookTagsList/bookTagsList.scss:
--------------------------------------------------------------------------------
1 | .tags-list {
2 | border-bottom: 1px solid #eee;
3 | padding: 10px 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/styles/vars.scss:
--------------------------------------------------------------------------------
1 | $book-color: #f6f6f1;
2 | $movie-color: #f0f3f5;
3 | $music-color: #f0f3ef;
4 | $city-color: #f6f5f2;
5 |
6 | $common-font-color: #37a;
7 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/movieTagContent.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-content {
4 | @include display-content;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Common/CommonTagList/CommonTagItem/commonTagItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../.././../assets/styles/mixin';
2 |
3 | .tag-item {
4 | @include aside-item;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplay/cityActivityDisplay.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-content {
4 | @include display-content;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/MovieTypeDisplayList/movieTypeDisplayList.scss:
--------------------------------------------------------------------------------
1 | .movie-list {
2 | height: 560px;
3 | overflow: hidden;
4 | position: relative;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/bookTagContent.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-content {
4 | @include display-content;
5 | height: 535px;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/BookTagMoreDisplay/bookTagMoreDisplay.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/bookTagMoreContent.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-content {
4 | @include display-content;
5 | font-size: 1.3rem;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Book/BookTag/bookTag.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .aside-content {
4 | @include aside-content;
5 | }
6 |
7 | .aside-header {
8 | @include aside-header;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagDisplayList/bookTagDisplayList.scss:
--------------------------------------------------------------------------------
1 | .book-list {
2 | height: 489px;
3 | margin-top: 16px;
4 | overflow: hidden;
5 | position: relative;
6 | width: 100%;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/City/CityTag/cityTag.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .aside-content {
4 | @include aside-content;
5 | }
6 |
7 | .aside-header {
8 | @include aside-header;
9 | margin-bottom: 3px;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTagDisplayList/movieTagDisplayList.scss:
--------------------------------------------------------------------------------
1 | .movie-list {
2 | font-size: 1.3rem;
3 | height: 560px;
4 | margin-top: 16px;
5 | overflow: hidden;
6 | position: relative;
7 | width: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTag/movieTag.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .aside-content {
4 | @include aside-content;
5 | }
6 |
7 | .aside-header {
8 | @include aside-header;
9 | margin-bottom: 3px;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/App/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Header from '../Header';
3 | import Main from '../Main';
4 |
5 | export default function App() {
6 | return (
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderDots/CommonSliderDot/commonSliderDot.scss:
--------------------------------------------------------------------------------
1 | .slider-dot {
2 | border-radius: 50%;
3 | cursor: pointer;
4 | display: inline-block;
5 | height: 5px;
6 | margin-left: 8px;
7 | outline: none;
8 | width: 5px;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/MovieModal/movieModal.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .modal-header {
4 | color: #42b983;
5 | }
6 |
7 | .modal-title {
8 | margin: 0 3px;
9 | }
10 |
11 | .movie-link {
12 | @include common-link;
13 | margin: 0 6px;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTagDisplay/movieTagDisplay.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | }
6 |
7 | .display-title {
8 | display: inline-block;
9 | }
10 |
11 | .slider-component {
12 | float: right;
13 | margin-top: 5px;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/LoadMoreBooks/loadMoreBooks.scss:
--------------------------------------------------------------------------------
1 | .load-more {
2 | background: #f6f6f1;
3 | color: #37a;
4 | cursor: pointer;
5 | height: 34px;
6 | line-height: 34px;
7 | margin-top: 15px;
8 | outline: none;
9 | text-align: center;
10 |
11 | &:hover {
12 | background: #e8e8e8;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/City/CityTagItem/cityTagItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 | @import '../../../assets/styles/vars';
3 |
4 | .tag-item {
5 | @include aside-item;
6 | }
7 |
8 | .in-active {
9 | background: $city-color;
10 | }
11 |
12 | .not-active {
13 | &:hover {
14 | background: $city-color;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/src/stores/index.js:
--------------------------------------------------------------------------------
1 | import headerStore from './modules/headerStore';
2 | import bookStore from './modules/bookStore';
3 | import movieStore from './modules/movieStore';
4 | import musicStore from './modules/musicStore';
5 | import cityStore from './modules/cityStore';
6 |
7 | export default {
8 | headerStore,
9 | bookStore,
10 | movieStore,
11 | musicStore,
12 | cityStore
13 | };
14 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagDisplay/bookTagDisplay.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | }
6 |
7 | .display-title {
8 | display: inline-block;
9 | }
10 |
11 | .nav-link {
12 | @include common-link;
13 | margin-left: 15px;
14 | }
15 |
16 | .slider-component {
17 | float: right;
18 | margin-top: 5px;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayHeader/cityActivityDisplayHeader.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | margin-bottom: 20px;
6 | }
7 |
8 | .display-title {
9 | display: inline-block;
10 | font-size: 1.4rem;
11 | font-weight: normal;
12 | margin-right: 40px;
13 | }
14 |
15 | .day-list {
16 | display: inline-block;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderArrows/commonSliderArrows.scss:
--------------------------------------------------------------------------------
1 | .slider-arrows {
2 | display: inline-block;
3 | margin-left: 10px;
4 | }
5 |
6 | .slider-arrow {
7 | border-radius: 50%;
8 | color: #fff;
9 | cursor: pointer;
10 | display: inline-block;
11 | font-weight: bold;
12 | height: 18px;
13 | line-height: 18px;
14 | margin-left: 6px;
15 | outline: none;
16 | text-align: center;
17 | width: 18px;
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagDisplayItem/bookTagDisplayItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .book-page {
4 | @include common-slider-page;
5 |
6 | li {
7 | width: 20%;
8 | }
9 | }
10 |
11 | .book-image {
12 | height: 70%;
13 | width: 85%;
14 | }
15 |
16 | .book-title {
17 | @include common-title;
18 | }
19 |
20 | .book-author {
21 | @include text-hidden;
22 | font-size: 1.3rem;
23 | margin: 6px 0;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Book/BookTypeContent/bookTypeContent.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | margin-bottom: 16px;
6 | }
7 |
8 | .display-title {
9 | display: inline-block;
10 | }
11 |
12 | .type-link {
13 | @include common-link;
14 | cursor: default;
15 | margin-left: 12px;
16 | outline: none;
17 | }
18 |
19 | .in-active {
20 | @include in-active;
21 | }
22 |
23 | .not-active {
24 | @include not-active;
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | /dist/
24 |
25 | # Editor directories and files
26 | .idea
27 | .vscode
28 | *.suo
29 | *.ntvs*
30 | *.njsproj
31 | *.sln
32 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-sass-guidelines",
3 | "rules": {
4 | "selector-pseudo-class-no-unknown": [
5 | true,
6 | {
7 | "ignorePseudoClasses": [
8 | "global"
9 | ]
10 | }
11 | ],
12 | "selector-pseudo-element-no-unknown": [
13 | true,
14 | {
15 | "ignorePseudoElements": [
16 | "global"
17 | ]
18 | }
19 | ],
20 | "max-nesting-depth": 2,
21 | },
22 | "ignoreFiles": [
23 | "build/**/*.css"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTagDisplayItem/movieTagDisplayItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .movie-page {
4 | @include common-slider-page;
5 |
6 | li {
7 | width: 20%;
8 | }
9 | }
10 |
11 | .movie-image {
12 | height: 160px;
13 | width: 115px;
14 | }
15 |
16 | .movie-title {
17 | @include common-title;
18 | margin: 6px 0;
19 | }
20 |
21 | .movie-score {
22 | margin: 10px 0;
23 |
24 | span {
25 | color: #e09015;
26 | }
27 | }
28 |
29 | .movie-year {
30 | margin: 8px 0;
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/movieTypeContent.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .display-header {
4 | @include main-header;
5 | margin-bottom: 16px;
6 | }
7 |
8 | .display-title {
9 | display: inline-block;
10 | }
11 |
12 | .type-link {
13 | @include common-link;
14 | cursor: default;
15 | margin-left: 12px;
16 | outline: none;
17 | }
18 |
19 | .in-active {
20 | @include in-active;
21 | }
22 |
23 | .not-active {
24 | @include not-active;
25 | }
26 |
27 | .slider-component {
28 | float: right;
29 | margin-top: 5px;
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Book/BookTag/BookTagsItem/bookTagsItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/vars';
2 |
3 | .tags-item {
4 | display: inline-block;
5 | margin: 5px 30px 3px 0;
6 | padding: 3px 5px;
7 |
8 | button {
9 | background: none;
10 | color: $common-font-color;
11 | cursor: pointer;
12 | font-size: 1.2rem;
13 |
14 | &:hover {
15 | color: #09f;
16 | }
17 | }
18 | }
19 |
20 | .selected-item {
21 | background: #111;
22 |
23 | button {
24 | color: #fff;
25 |
26 | &:hover {
27 | color: #fff;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'mobx-react';
4 | import { BrowserRouter } from 'react-router-dom';
5 | import stores from './stores';
6 | import App from './components/App';
7 | import registerServiceWorker from './utils/registerServiceWorker';
8 |
9 | import './assets/styles/index.scss';
10 |
11 | ReactDOM.render(
12 |
13 |
14 |
15 |
16 | ,
17 | document.getElementById('root')
18 | );
19 | registerServiceWorker();
20 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayHeaderItem/cityActivityDisplayHeaderItem.scss:
--------------------------------------------------------------------------------
1 | .day-tag {
2 | display: inline-block;
3 | margin-right: 10px;
4 |
5 | span {
6 | border-radius: 2px;
7 | display: inline-block;
8 | height: 16px;
9 | line-height: 1.4;
10 | outline: none;
11 | padding: 1px 15px;
12 | }
13 |
14 | .in-active {
15 | background: #e6e6e6;
16 | color: #333;
17 | }
18 |
19 | .not-active {
20 | color: #0192b5;
21 | cursor: pointer;
22 |
23 | &:hover {
24 | background: #6cc1d6;
25 | color: #fff;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "react-app",
4 | "airbnb",
5 | "prettier"
6 | ],
7 | "plugins": [
8 | "prettier"
9 | ],
10 | "env": {
11 | "browser": true,
12 | "node": true,
13 | "jest": true
14 | },
15 | "rules": {
16 | "linebreak-style": 0,
17 | "prettier/prettier": "error",
18 | "func-names": 0,
19 | "no-console": 0,
20 | "jsx-a11y/click-events-have-key-events": 0,
21 | "react/jsx-filename-extension": [
22 | "error",
23 | {
24 | "extensions": [
25 | ".js",
26 | ".jsx"
27 | ]
28 | }
29 | ]
30 | }
31 | }
--------------------------------------------------------------------------------
/src/components/Book/BookTypeContent/BookTypeDisplayList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import BookTypeDisplayItem from '../BookTypeDisplayItem';
4 |
5 | export default class BookTypeDisplayList extends PureComponent {
6 | static propTypes = {
7 | bookList: PropTypes.arrayOf(PropTypes.object).isRequired
8 | };
9 |
10 | render() {
11 | const { bookList } = this.props;
12 | return (
13 |
14 | {bookList.map(book => (
15 |
16 | ))}
17 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/BookTagMoreDisplayList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import BookTagMoreDisplayItem from '../BookTagMoreDisplayItem';
4 |
5 | export default class BookTagMoreDisplayList extends PureComponent {
6 | static propTypes = {
7 | bookList: PropTypes.arrayOf(PropTypes.object).isRequired
8 | };
9 |
10 | render() {
11 | const { bookList } = this.props;
12 | return (
13 |
14 | {bookList.map(book => (
15 |
16 | ))}
17 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/MusicTagItem/musicTagItem.scss:
--------------------------------------------------------------------------------
1 | .music-tag-item {
2 | display: inline-block;
3 | font-size: 1.3rem;
4 | margin-bottom: 5px;
5 | vertical-align: middle;
6 |
7 | span {
8 | outline: none;
9 | }
10 |
11 | &::before {
12 | color: #222;
13 | content: ' | ';
14 | padding: 0 3px;
15 | }
16 |
17 | &:first-child::before {
18 | content: ' ';
19 | }
20 | }
21 |
22 | .in-active {
23 | color: #138a64;
24 | font-weight: bolder;
25 | }
26 |
27 | .not-active {
28 | color: #999;
29 | cursor: pointer;
30 |
31 | &:hover {
32 | color: #138a64;
33 | text-decoration: underline;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayMain/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CityActivityDisplayMainItem from '../CityActivityDisplayMainItem';
4 |
5 | export default class CityActivityDisplayMain extends PureComponent {
6 | static propTypes = {
7 | activityList: PropTypes.arrayOf(PropTypes.object).isRequired
8 | };
9 |
10 | render() {
11 | const { activityList } = this.props;
12 | return (
13 |
14 | {activityList.map(activity => (
15 |
16 | ))}
17 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/Common/CommonModal/commonModal.scss:
--------------------------------------------------------------------------------
1 | .modal-mask {
2 | background-color: rgba(128, 128, 128, 0.5);
3 | display: table;
4 | height: 100%;
5 | left: 0;
6 | position: fixed;
7 | text-align: left;
8 | top: 0;
9 | transition: opacity 0.3s ease;
10 | width: 100%;
11 | z-index: 9998;
12 | }
13 |
14 | .modal-wrapper {
15 | display: table-cell;
16 | vertical-align: middle;
17 | }
18 |
19 | .modal-body {
20 | margin: 20px 0;
21 | }
22 |
23 | .modal-button {
24 | background-color: #409eff;
25 | border: 0;
26 | border-radius: 3px;
27 | color: #fff;
28 | cursor: pointer;
29 | float: right;
30 | outline: none;
31 | padding: 9px 15px;
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/LoadMoreBooks/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './loadMoreBooks.scss';
4 |
5 | export default class LoadMoreBooks extends PureComponent {
6 | static propTypes = {
7 | setPageCount: PropTypes.func.isRequired
8 | };
9 |
10 | handleClick = () => {
11 | const { setPageCount } = this.props;
12 | setPageCount(null, 10);
13 | };
14 |
15 | render() {
16 | return (
17 |
23 | 加载更多
24 |
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayMainItem/cityActivityDisplayMainItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/mixin';
2 |
3 | .activity-item {
4 | display: inline-block;
5 | height: 132px;
6 | overflow: hidden;
7 | width: 50%;
8 | }
9 |
10 | .image-link {
11 | float: left;
12 | }
13 |
14 | .activity-image {
15 | height: 97px;
16 | width: 75px;
17 | }
18 |
19 | .activity-info {
20 | color: #666;
21 | font-size: 1.2rem;
22 | margin-left: 90px;
23 | padding-right: 30px;
24 | }
25 |
26 | .activity-title {
27 | @include common-link;
28 | font-size: 1.3rem;
29 | }
30 |
31 | .date-info {
32 | margin-top: 6px;
33 | }
34 |
35 | .address-info {
36 | @include text-hidden;
37 | margin: 3px 0;
38 | width: 100%;
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/Header/header.scss:
--------------------------------------------------------------------------------
1 | .outer-header {
2 | margin-bottom: 40px;
3 | min-width: 936px;
4 | transition: background 0.5s ease-in-out;
5 | }
6 |
7 | .inner-header {
8 | margin: 0 auto;
9 | padding: 10px 0 5px;
10 | width: 936px;
11 | }
12 |
13 | .module-logo {
14 | background-position: 0 12px;
15 | background-repeat: no-repeat;
16 | color: transparent;
17 | display: inline-block;
18 | height: 56px;
19 | margin: 0 13px 0 0;
20 | width: 145px;
21 | }
22 |
23 | .book-logo {
24 | background-image: url('../../assets/images/book.png');
25 | }
26 |
27 | .movie-logo {
28 | background-image: url('../../assets/images/movie.png');
29 | }
30 |
31 | .music-logo {
32 | background-image: url('../../assets/images/music.png');
33 | }
34 |
35 | .city-logo {
36 | background-image: url('../../assets/images/city.png');
37 | }
38 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 | const jest = require('jest');
19 | let argv = process.argv.slice(2);
20 |
21 | // Watch unless on CI or in coverage mode
22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) {
23 | argv.push('--watch');
24 | }
25 |
26 |
27 | jest.run(argv);
28 |
--------------------------------------------------------------------------------
/src/components/Book/BookTypeContent/BookTypeDisplayItem/bookTypeDisplayItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .book-item {
4 | display: inline-block;
5 | font-size: 1.3rem;
6 | margin-bottom: 30px;
7 | overflow: hidden;
8 | vertical-align: middle;
9 | width: 310px;
10 | }
11 |
12 | .book-image {
13 | float: left;
14 | height: 137px;
15 | margin-right: 18px;
16 | width: 95px;
17 | }
18 |
19 | .book-title {
20 | @include common-title;
21 | margin-bottom: 6px;
22 | }
23 |
24 | .score-image {
25 | @include score-image('../../../../assets/images/scoring.png');
26 | }
27 |
28 | .average-score {
29 | color: #e09015;
30 | margin-left: 6px;
31 | }
32 |
33 | .book-more-info {
34 | margin-right: 20px;
35 |
36 | p {
37 | margin: 4px 0;
38 | }
39 | }
40 |
41 | .book-details {
42 | clear: both;
43 | padding: 15px 0 3px;
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/MusicTagItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Proptypes from 'prop-types';
3 | import styles from './musicTagItem.scss';
4 |
5 | export default class MusicTagItem extends PureComponent {
6 | static propTypes = {
7 | tag: Proptypes.string.isRequired,
8 | isSelected: Proptypes.bool.isRequired,
9 | handleClick: Proptypes.func.isRequired
10 | };
11 |
12 | render() {
13 | const { tag, isSelected, handleClick } = this.props;
14 | return (
15 |
16 | handleClick(tag)}
21 | >
22 | {tag}
23 |
24 |
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/Book/BookTag/BookTagsItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './bookTagsItem.scss';
4 |
5 | export default class BookTagsItem extends PureComponent {
6 | static propTypes = {
7 | tagObject: PropTypes.shape({
8 | tagName: PropTypes.string.isRequired
9 | }).isRequired,
10 | isSelected: PropTypes.bool.isRequired,
11 | handleClick: PropTypes.func.isRequired
12 | };
13 |
14 | render() {
15 | const { tagObject, isSelected, handleClick } = this.props;
16 | const selectClass = isSelected ? styles['selected-item'] : '';
17 | return (
18 |
19 |
22 |
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/City/CityTagItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './cityTagItem.scss';
4 |
5 | export default class CityTagItem extends PureComponent {
6 | static propTypes = {
7 | isSelected: PropTypes.bool.isRequired,
8 | city: PropTypes.shape({
9 | name: PropTypes.string.isRequired
10 | }).isRequired,
11 | setCurrentCity: PropTypes.func.isRequired
12 | };
13 |
14 | render() {
15 | const { isSelected, city, setCurrentCity } = this.props;
16 | return (
17 |
18 |
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof Promise === 'undefined') {
4 | // Rejection tracking prevents a common issue where React gets into an
5 | // inconsistent state due to an error, but it gets swallowed by a Promise,
6 | // and the user has no idea what causes React's erratic future behavior.
7 | require('promise/lib/rejection-tracking').enable();
8 | window.Promise = require('promise/lib/es6-extensions.js');
9 | }
10 |
11 | // fetch() polyfill for making API calls.
12 | require('whatwg-fetch');
13 |
14 | // Object.assign() is commonly used with React.
15 | // It will use the native implementation if it's present and isn't buggy.
16 | Object.assign = require('object-assign');
17 |
18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
19 | // We don't polyfill it in the browser--this is user's responsibility.
20 | if (process.env.NODE_ENV === 'test') {
21 | require('raf').polyfill(global);
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Header/HeaderSuggest/headerSuggest.scss:
--------------------------------------------------------------------------------
1 | @import '../../../assets/styles/vars';
2 |
3 | .search-suggest {
4 | background: #fff;
5 | border: 1px solid #ddd;
6 | margin-top: 8px;
7 | position: absolute;
8 | width: 356px;
9 | z-index: 99;
10 |
11 | li {
12 | border-bottom: 1px solid #eee;
13 | overflow: hidden;
14 |
15 | &:hover {
16 | background: #f9f9f9;
17 | }
18 | }
19 | }
20 |
21 | .search-link {
22 | color: #999;
23 | display: block;
24 | overflow: hidden;
25 | padding: 6px;
26 | zoom: 1;
27 | }
28 |
29 | .search-image {
30 | float: left;
31 | height: 56px;
32 | margin-right: 8px;
33 | margin-top: 3px;
34 | width: 40px;
35 | }
36 |
37 | .search-title {
38 | color: $common-font-color;
39 | display: inline-block;
40 | font-size: 1.2rem;
41 | font-weight: normal;
42 | margin-right: 10px;
43 | }
44 |
45 | .search-author,
46 | .search-genres {
47 | margin-top: 10px;
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/MovieTypeDisplayItem/movieTypeDisplayItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 |
3 | .movie-page {
4 | @include common-slider-page;
5 |
6 | li {
7 | width: 16.66%;
8 | }
9 | }
10 |
11 | .movie-image {
12 | height: 160px;
13 | width: 115px;
14 | }
15 |
16 | .movie-title {
17 | @include text-hidden;
18 | font-size: 1.3rem;
19 | font-weight: normal;
20 | margin: 12px 0 16px;
21 |
22 | a {
23 | color: #333;
24 | }
25 | }
26 |
27 | .score-image {
28 | @include score-image('../../../../assets/images/scoring.png');
29 | }
30 |
31 | .average-score {
32 | color: #e09015;
33 | margin-left: 4px;
34 | }
35 |
36 | .buy-ticket {
37 | background: #268dcd;
38 | border-radius: 2px;
39 | color: #fff;
40 | cursor: pointer;
41 | height: 24px;
42 | line-height: 24px;
43 | margin: 12px auto 0;
44 | text-align: center;
45 | width: 90px;
46 | }
47 |
48 | .movie-genres {
49 | margin: 12px 0;
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagPrompt/bookTagPrompt.scss:
--------------------------------------------------------------------------------
1 | .book-prompt {
2 | background: #f9f9f7;
3 | border: 1px solid #acacac;
4 | border-radius: 5px;
5 | box-shadow: 0 1px 1px #fdfdfd inset, 0 1px 1px #dcdbd4;
6 | box-sizing: border-box;
7 | max-height: 220px;
8 | padding: 10px;
9 | position: absolute;
10 | width: 330px;
11 | }
12 |
13 | .outside-triangle,
14 | .inside-triangle {
15 | border-bottom: 8px solid transparent;
16 | border-top: 8px solid transparent;
17 | height: 0;
18 | position: absolute;
19 | top: 45%;
20 | width: 0;
21 | }
22 |
23 | .outside-triangle {
24 | border-right: 8px solid #acacac;
25 | left: -8px;
26 | }
27 |
28 | .inside-triangle {
29 | border-right: 8px solid #f9f9f9;
30 | left: -7px;
31 | }
32 |
33 | .prompt-title {
34 | color: #666;
35 | margin-bottom: 6px;
36 | }
37 |
38 | .prompt-introduce {
39 | margin-bottom: 6px;
40 | }
41 |
42 | .prompt-summary {
43 | line-height: 1.6;
44 | max-height: 130px;
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderDots/CommonSliderDot/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './commonSliderDot.scss';
4 |
5 | export default class CommonSliderDot extends PureComponent {
6 | static propTypes = {
7 | index: PropTypes.number.isRequired,
8 | isSelected: PropTypes.bool.isRequired,
9 | backgroundColor: PropTypes.string.isRequired,
10 | handleDotClick: PropTypes.func.isRequired
11 | };
12 |
13 | handleClick = () => {
14 | const { index, handleDotClick } = this.props;
15 | handleDotClick(index);
16 | };
17 |
18 | render() {
19 | const { isSelected, backgroundColor } = this.props;
20 | return (
21 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayHeaderItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './cityActivityDisplayHeaderItem.scss';
4 |
5 | export default class CityActivityDisplayHeaderItem extends PureComponent {
6 | static propTypes = {
7 | dayType: PropTypes.shape({
8 | text: PropTypes.string.isRequired,
9 | value: PropTypes.string.isRequired
10 | }).isRequired,
11 | isSelected: PropTypes.bool.isRequired,
12 | handleClick: PropTypes.func.isRequired
13 | };
14 |
15 | render() {
16 | const { dayType, isSelected, handleClick } = this.props;
17 | return (
18 |
19 | handleClick(dayType)}
24 | >
25 | {dayType.text}
26 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTagDisplayList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import MovieTagDisplayItem from '../MovieTagDisplayItem';
4 | import styles from './movieTagDisplayList.scss';
5 |
6 | export default class MovieTagDisplayList extends PureComponent {
7 | static propTypes = {
8 | currentPage: PropTypes.number.isRequired,
9 | currentDirection: PropTypes.string.isRequired,
10 | movieList: PropTypes.arrayOf(PropTypes.array).isRequired
11 | };
12 |
13 | render() {
14 | const { currentPage, currentDirection, movieList } = this.props;
15 | return (
16 |
17 | {movieList.map((movieArray, index) => (
18 |
24 | ))}
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Book/BookTag/BookTagsList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import BookTagsItem from '../BookTagsItem';
4 | import styles from './bookTagsList.scss';
5 |
6 | export default class BookTagsList extends PureComponent {
7 | static propTypes = {
8 | bookTags: PropTypes.arrayOf(PropTypes.object).isRequired,
9 | currentBookTags: PropTypes.shape({
10 | tagName: PropTypes.string.isRequired
11 | }).isRequired,
12 | handleClick: PropTypes.func.isRequired
13 | };
14 |
15 | render() {
16 | const { bookTags, currentBookTags, handleClick } = this.props;
17 | return (
18 |
19 | {bookTags.map(tagObject => (
20 |
26 | ))}
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Common/CommonTagList/CommonTagItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './commonTagItem.scss';
4 |
5 | export default class CommonTagItem extends PureComponent {
6 | static propTypes = {
7 | tag: PropTypes.string.isRequired,
8 | isSelected: PropTypes.bool.isRequired,
9 | handleClick: PropTypes.func.isRequired,
10 | activeTagClass: PropTypes.string.isRequired,
11 | hoverTagClass: PropTypes.string.isRequired
12 | };
13 |
14 | render() {
15 | const {
16 | tag,
17 | isSelected,
18 | handleClick,
19 | activeTagClass,
20 | hoverTagClass
21 | } = this.props;
22 | const selectClass = isSelected ? activeTagClass : '';
23 |
24 | return (
25 |
26 |
32 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/City/CityTag/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CityTagItem from '../CityTagItem';
4 | import styles from './cityTag.scss';
5 |
6 | export default class CityTag extends PureComponent {
7 | static propTypes = {
8 | cityList: PropTypes.arrayOf(PropTypes.object).isRequired,
9 | currentCity: PropTypes.shape({
10 | id: PropTypes.string.isRequired,
11 | name: PropTypes.string.isRequired
12 | }).isRequired,
13 | setCurrentCity: PropTypes.func.isRequired
14 | };
15 |
16 | render() {
17 | const { cityList, currentCity, setCurrentCity } = this.props;
18 | return (
19 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/Header/HeaderInput/headerInput.scss:
--------------------------------------------------------------------------------
1 | .search-content {
2 | display: inline-block;
3 | }
4 |
5 | .search-form {
6 | position: relative;
7 | top: 9px;
8 | }
9 |
10 | .search-input {
11 | background: #fff;
12 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.16);
13 | height: 32px;
14 | padding-left: 10px;
15 | width: 350px;
16 | }
17 |
18 | .search-submit {
19 | background-position: 0 -40px;
20 | background-repeat: no-repeat;
21 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.16);
22 | color: transparent;
23 | cursor: pointer;
24 | height: 32px;
25 | margin: 0 0 0 -4px;
26 | vertical-align: middle;
27 | width: 37px;
28 | }
29 |
30 | .book-search-icon {
31 | background-image: url('../../../assets/images/book_search_btn.png');
32 | }
33 |
34 | .movie-search-icon {
35 | background-image: url('../../../assets/images/movie_search_btn.png');
36 | }
37 |
38 | .music-search-icon {
39 | background-image: url('../../../assets/images/music_search_btn.png');
40 | }
41 |
42 | .city-search-icon {
43 | background-image: url('../../../assets/images/city_search_btn.png');
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/Common/CommonTagList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CommonTagItem from './CommonTagItem';
4 |
5 | export default class CommonTagList extends PureComponent {
6 | static propTypes = {
7 | tagList: PropTypes.arrayOf(PropTypes.string).isRequired,
8 | currentTag: PropTypes.string.isRequired,
9 | handleClick: PropTypes.func.isRequired,
10 | activeTagClass: PropTypes.string.isRequired,
11 | hoverTagClass: PropTypes.string.isRequired
12 | };
13 |
14 | render() {
15 | const {
16 | tagList,
17 | currentTag,
18 | handleClick,
19 | activeTagClass,
20 | hoverTagClass
21 | } = this.props;
22 |
23 | return (
24 |
25 | {tagList.map(tag => (
26 |
34 | ))}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplay/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CityActivityDisplayItem from '../CityActivityDisplayItem';
4 | import styles from './cityActivityDisplay.scss';
5 |
6 | export default class CityActivityDisplay extends PureComponent {
7 | static propTypes = {
8 | currentCity: PropTypes.shape({
9 | id: PropTypes.string.isRequired,
10 | name: PropTypes.string.isRequired
11 | }).isRequired,
12 | activityTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
13 | dayTypes: PropTypes.arrayOf(PropTypes.object).isRequired
14 | };
15 |
16 | render() {
17 | const { currentCity, activityTypes, dayTypes } = this.props;
18 | return (
19 |
20 | {activityTypes.map(activityType => (
21 |
27 | ))}
28 |
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/MusicTag/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import MusicTagItem from '../MusicTagItem';
4 | import styles from './musicTag.scss';
5 |
6 | export default class MusicTag extends PureComponent {
7 | static propTypes = {
8 | musicTags: PropTypes.arrayOf(PropTypes.string).isRequired,
9 | currentMusicTag: PropTypes.string.isRequired,
10 | setCurrentMusicTag: PropTypes.func.isRequired,
11 | setTagMusics: PropTypes.func.isRequired
12 | };
13 |
14 | handleClick = tag => {
15 | const { setCurrentMusicTag, setTagMusics } = this.props;
16 | setCurrentMusicTag(tag);
17 | setTagMusics(tag);
18 | };
19 |
20 | render() {
21 | const { musicTags, currentMusicTag } = this.props;
22 | return (
23 |
24 | {musicTags.map(tag => (
25 |
31 | ))}
32 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagDisplayList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import BookTagDisplayItem from '../BookTagDisplayItem';
4 | import styles from './bookTagDisplayList.scss';
5 |
6 | export default class BookTagDisplayList extends PureComponent {
7 | static propTypes = {
8 | currentPage: PropTypes.number.isRequired,
9 | currentDirection: PropTypes.string.isRequired,
10 | bookList: PropTypes.arrayOf(PropTypes.array).isRequired,
11 | setBookPrompt: PropTypes.func.isRequired
12 | };
13 |
14 | render() {
15 | const {
16 | currentPage,
17 | currentDirection,
18 | bookList,
19 | setBookPrompt
20 | } = this.props;
21 | return (
22 |
23 | {bookList.map((bookArray, index) => (
24 |
31 | ))}
32 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/BookTagMoreDisplayItem/bookTagMoreDisplayItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../assets/styles/mixin';
2 | @import '../../../../assets/styles/vars';
3 |
4 | .book-item {
5 | border-bottom: 1px solid #ddd;
6 | overflow: hidden;
7 | padding: 15px 0;
8 | zoom: 1;
9 | }
10 |
11 | .book-image {
12 | float: left;
13 | height: 128px;
14 | margin-right: 20px;
15 | width: 90px;
16 | }
17 |
18 | .book-title {
19 | @include common-title;
20 | }
21 |
22 | .book-info {
23 | overflow: hidden;
24 | zoom: 1;
25 | }
26 |
27 | .book-other-info {
28 | margin: 6px 0 8px;
29 | }
30 |
31 | .score-image {
32 | @include score-image('../../../../assets/images/scoring.png');
33 | }
34 |
35 | .average-score {
36 | @include average-score;
37 | margin: 0 9px;
38 | }
39 |
40 | .book-summary {
41 | line-height: 1.6;
42 | margin: 12px 0 10px;
43 | }
44 |
45 | .buy-books {
46 | color: $common-font-color;
47 |
48 | &:hover {
49 | background: $common-font-color;
50 | color: #fff;
51 | }
52 | }
53 |
54 | .e-book {
55 | color: #973f3a;
56 | float: right;
57 |
58 | &:hover {
59 | background: #973f31;
60 | color: #fff;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/Header/HeaderModules/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { NavLink } from 'react-router-dom';
3 | import PropTypes from 'prop-types';
4 | import styles from './headerModules.scss';
5 |
6 | export default class headerModules extends PureComponent {
7 | static propTypes = {
8 | modules: PropTypes.arrayOf(PropTypes.object).isRequired
9 | };
10 |
11 | render() {
12 | const { modules } = this.props;
13 |
14 | return (
15 |
16 | {modules.map(module => (
17 | -
18 | {module.text}
19 |
20 |
21 | {module.subTypes.map(subType => (
22 |
27 | {subType.text}
28 |
29 | ))}
30 |
31 |
32 |
33 | ))}
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTag/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CommonTagList from '../../../Common/CommonTagList';
4 | import styles from './movieTag.scss';
5 |
6 | export default class MovieTag extends PureComponent {
7 | static propTypes = {
8 | movieTags: PropTypes.arrayOf(PropTypes.string).isRequired,
9 | currentMovieTag: PropTypes.string.isRequired,
10 | setCurrentMovieTag: PropTypes.func.isRequired,
11 | setTagMovies: PropTypes.func.isRequired
12 | };
13 |
14 | handleClick = tag => {
15 | const { setCurrentMovieTag, setTagMovies } = this.props;
16 | setCurrentMovieTag(tag);
17 | setTagMovies(tag);
18 | };
19 |
20 | render() {
21 | const { movieTags, currentMovieTag } = this.props;
22 | return (
23 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderDots/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Proptypes from 'prop-types';
3 | import CommonSliderDot from './CommonSliderDot';
4 | import styles from './commonSliderDots.scss';
5 |
6 | export default class CommonSliderDots extends PureComponent {
7 | static propTypes = {
8 | pageCount: Proptypes.number.isRequired,
9 | currentPage: Proptypes.number.isRequired,
10 | handleDotClick: Proptypes.func.isRequired,
11 | backgroundColor: Proptypes.string.isRequired
12 | };
13 |
14 | getDotList() {
15 | const {
16 | pageCount,
17 | currentPage,
18 | handleDotClick,
19 | backgroundColor
20 | } = this.props;
21 | const dotList = [];
22 | for (let i = 0; i < pageCount; i += 1) {
23 | dotList.push(
24 |
31 | );
32 | }
33 | return dotList;
34 | }
35 |
36 | render() {
37 | const dotList = this.getDotList();
38 | return {dotList}
;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Common/CommonModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './commonModal.scss';
4 |
5 | export default class CommonModal extends PureComponent {
6 | static propTypes = {
7 | hideModal: PropTypes.func.isRequired,
8 | header: PropTypes.node,
9 | body: PropTypes.node,
10 | footer: PropTypes.node
11 | };
12 |
13 | static defaultProps = {
14 | header: 'default header',
15 | body: 'default body',
16 | footer: ''
17 | };
18 |
19 | render() {
20 | const { header, body, footer, hideModal } = this.props;
21 | return (
22 |
23 |
24 |
25 |
{header}
26 |
27 |
{body}
28 |
29 |
30 | {footer}
31 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CommonSliderDots from './CommonSliderDots';
4 | import CommonSliderArrows from './CommonSliderArrows';
5 |
6 | export default class CommonSlider extends PureComponent {
7 | static propTypes = {
8 | pageCount: PropTypes.number.isRequired,
9 | currentPage: PropTypes.number.isRequired,
10 | handleDotClick: PropTypes.func.isRequired,
11 | handleArrowClick: PropTypes.func.isRequired,
12 | backgroundColor: PropTypes.string
13 | };
14 |
15 | static defaultProps = {
16 | backgroundColor: '#9b9a8e'
17 | };
18 |
19 | render() {
20 | const {
21 | pageCount,
22 | currentPage,
23 | handleDotClick,
24 | handleArrowClick,
25 | backgroundColor
26 | } = this.props;
27 | return (
28 |
29 |
35 |
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/Header/HeaderModules/headerModules.scss:
--------------------------------------------------------------------------------
1 | .module-list {
2 | display: inline-block;
3 | font-size: 1.3rem;
4 | left: 40px;
5 | position: relative;
6 | top: 10px;
7 |
8 | li {
9 | cursor: pointer;
10 | display: inline-block;
11 | margin-left: 25px;
12 | padding: 2px 5px;
13 |
14 | &:hover {
15 | background: #37a;
16 | color: #fff;
17 | }
18 |
19 | &:hover .outer-module-target {
20 | display: block;
21 | }
22 | }
23 | }
24 |
25 | .outer-module-target {
26 | display: none;
27 | margin-left: -40px;
28 | position: absolute;
29 | }
30 |
31 | .inner-module-target {
32 | background: #fff;
33 | border: 1px solid #e7eaf1;
34 | box-shadow: 0 5px 20px rgba(0, 34, 77, 0.1);
35 | margin-top: 10px;
36 | width: 100px;
37 |
38 | &::before {
39 | border-bottom: 10px solid #fff;
40 | border-left: 10px solid transparent;
41 | border-right: 10px solid transparent;
42 | content: '';
43 | height: 0;
44 | margin-left: calc(50% - 10px);
45 | margin-top: -10px;
46 | position: absolute;
47 | width: 0;
48 | }
49 | }
50 |
51 | .module-link {
52 | color: #000;
53 | display: block;
54 | padding: 10px;
55 | text-align: center;
56 |
57 | &:hover {
58 | background: #f4f8fb;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/stores/modules/musicStore.js:
--------------------------------------------------------------------------------
1 | import { observable, action, computed, flow } from 'mobx';
2 | import { getCurrentTagMusics } from '../../apis';
3 |
4 | const musicTags = [
5 | '华语',
6 | '欧美',
7 | '日韩',
8 | '流行',
9 | '摇滚',
10 | '民谣',
11 | '原声',
12 | '轻音乐',
13 | '古典',
14 | '粤语',
15 | 'R&B'
16 | ];
17 |
18 | class MusicStore {
19 | musicTags = musicTags;
20 | @observable currentMusicTag = musicTags[0];
21 | @observable tagMusics = new Map();
22 |
23 | @action
24 | setCurrentMusicTag = tag => {
25 | if (tag !== this.currentMusicTag) {
26 | this.currentMusicTag = tag;
27 | }
28 | };
29 |
30 | @computed
31 | get currentTagMusicList() {
32 | const currentTagMusics = this.tagMusics.get(this.currentMusicTag);
33 | return currentTagMusics || [];
34 | }
35 |
36 | setTagMusics = flow(
37 | function*(tag, start, count, isAfresh = false) {
38 | const oldData = this.tagMusics.get(tag);
39 | if (oldData && !isAfresh) return;
40 | try {
41 | const response = yield getCurrentTagMusics(tag, start, count);
42 | const data = response.data.musics;
43 | this.tagMusics.set(tag, data);
44 | } catch (error) {
45 | console.log(error);
46 | }
47 | }.bind(this)
48 | );
49 | }
50 |
51 | const musicStore = new MusicStore();
52 | export default musicStore;
53 |
--------------------------------------------------------------------------------
/src/components/Main/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route } from 'react-router-dom';
3 | import BookTagContent from '../Book/BookTagContent';
4 | import BookTagMoreContent from '../Book/BookTagMoreContent';
5 | import BookTypeContent from '../Book/BookTypeContent';
6 | import MovieTypeContent from '../Movie/MovieTypeContent';
7 | import MovieTagContent from '../Movie/MovieTagContent';
8 | import MusicTagContent from '../Music/MusicTagContent';
9 | import CityActivity from '../City/CityActivity';
10 | import styles from './main.scss';
11 |
12 | export default function Main() {
13 | return (
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/Common/CommonSlider/CommonSliderArrows/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './commonSliderArrows.scss';
4 |
5 | export default class CommonSliderArrows extends PureComponent {
6 | static propTypes = {
7 | handleArrowClick: PropTypes.func.isRequired,
8 | backgroundColor: PropTypes.string.isRequired
9 | };
10 |
11 | handleLeftBtnClick = () => {
12 | const { handleArrowClick } = this.props;
13 | handleArrowClick('left');
14 | };
15 |
16 | handleRightBtnClick = () => {
17 | const { handleArrowClick } = this.props;
18 | handleArrowClick('right');
19 | };
20 |
21 | render() {
22 | return (
23 |
24 |
31 | ‹
32 |
33 |
34 |
41 | ›
42 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/MovieTypeDisplayList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import MovieTypeDisplayItem from '../MovieTypeDisplayItem';
4 | import styles from './movieTypeDisplayList.scss';
5 |
6 | export default class MovieTypeDisplayList extends PureComponent {
7 | static propTypes = {
8 | currentPage: PropTypes.number.isRequired,
9 | currentDirection: PropTypes.string.isRequired,
10 | movieList: PropTypes.arrayOf(PropTypes.array).isRequired,
11 | currentMovieType: PropTypes.shape({
12 | value: PropTypes.string.isRequired,
13 | text: PropTypes.string.isRequired
14 | }).isRequired,
15 | setSelectedMovie: PropTypes.func.isRequired
16 | };
17 |
18 | render() {
19 | const {
20 | currentPage,
21 | currentDirection,
22 | movieList,
23 | currentMovieType,
24 | setSelectedMovie
25 | } = this.props;
26 | return (
27 |
28 | {movieList.map((movieArray, index) => (
29 |
37 | ))}
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayHeader/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CityActivityDisplayHeaderItem from '../CityActivityDisplayHeaderItem';
4 | import styles from './cityActivityDisplayHeader.scss';
5 |
6 | export default class CityActivityDisplayHeader extends PureComponent {
7 | static propTypes = {
8 | currentDayType: PropTypes.shape({
9 | value: PropTypes.string.isRequired,
10 | text: PropTypes.string.isRequired
11 | }).isRequired,
12 | currentActivityType: PropTypes.shape({
13 | value: PropTypes.string.isRequired,
14 | text: PropTypes.string.isRequired
15 | }).isRequired,
16 | dayTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
17 | handleClick: PropTypes.func.isRequired
18 | };
19 |
20 | render() {
21 | const {
22 | currentDayType,
23 | currentActivityType,
24 | dayTypes,
25 | handleClick
26 | } = this.props;
27 | return (
28 |
29 |
{currentActivityType.text}
30 |
31 |
32 | {dayTypes.map(dayType => (
33 |
39 | ))}
40 |
41 |
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/stores/modules/cityStore.js:
--------------------------------------------------------------------------------
1 | import { observable, action, flow } from 'mobx';
2 | import { getCities } from '../../apis';
3 |
4 | const activityTypes = [
5 | { value: 'music', text: '音乐' },
6 | { value: 'drama', text: '戏剧' },
7 | { value: 'exhibition', text: '展览' },
8 | { value: 'salon', text: '讲座' },
9 | { value: 'party', text: '聚会' },
10 | { value: 'sports', text: '运动' },
11 | { value: 'travel', text: '旅行' },
12 | { value: 'commonweal', text: '公益' },
13 | { value: 'film', text: '电影' }
14 | ];
15 |
16 | const dayTypes = [
17 | { value: 'today', text: '今天' },
18 | { value: 'tomorrow', text: '明天' },
19 | { value: 'weekend', text: '周末' },
20 | { value: 'week', text: '最近一周' }
21 | ];
22 |
23 | class CityStore {
24 | activityTypes = activityTypes;
25 | dayTypes = dayTypes;
26 | @observable cities = [];
27 | @observable currentCity = {};
28 |
29 | @action
30 | setCurrentCity = city => {
31 | if (city.id !== this.currentCity.id) {
32 | this.currentCity = city;
33 | }
34 | };
35 |
36 | setCities = flow(
37 | function*(start, count, isAfresh = false) {
38 | if (this.cities.length !== 0 && !isAfresh) {
39 | return;
40 | }
41 | try {
42 | const response = yield getCities(start, count);
43 | const data = response.data.locs;
44 | this.cities = data;
45 | this.setCurrentCity(data[0]);
46 | } catch (error) {
47 | console.log(error);
48 | }
49 | }.bind(this)
50 | );
51 | }
52 |
53 | const cityStore = new CityStore();
54 | export default cityStore;
55 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTypeContent/MovieModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { CSSTransition } from 'react-transition-group';
4 | import CommonModal from '../../../Common/CommonModal';
5 | import styles from './movieModal.scss';
6 |
7 | export default class MovieModal extends PureComponent {
8 | static propTypes = {
9 | selectedMovie: PropTypes.shape({
10 | title: PropTypes.string.isRequired
11 | }).isRequired,
12 | isShowModal: PropTypes.bool.isRequired,
13 | hideModal: PropTypes.func.isRequired
14 | };
15 |
16 | header = null;
17 | body = null;
18 |
19 | render() {
20 | const { selectedMovie, isShowModal, hideModal } = this.props;
21 | if (isShowModal) {
22 | this.header = 您好
;
23 | this.body = (
24 |
25 | 本程序并无实际购票功能,若您喜欢
26 |
27 | {selectedMovie.title}
28 |
29 | ,可往
30 |
31 | 豆瓣
32 |
33 | 查看
34 |
35 | );
36 | }
37 | return (
38 |
44 |
49 |
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/MovieTagDisplayItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { CSSTransition } from 'react-transition-group';
3 | import PropTypes from 'prop-types';
4 | import styles from './movieTagDisplayItem.scss';
5 |
6 | export default class MovieTagDisplayItem extends PureComponent {
7 | static propTypes = {
8 | isShow: PropTypes.bool.isRequired,
9 | currentDirection: PropTypes.string.isRequired,
10 | movieArray: PropTypes.arrayOf(PropTypes.object).isRequired
11 | };
12 |
13 | render() {
14 | const { isShow, currentDirection, movieArray } = this.props;
15 | return (
16 |
22 |
48 |
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/City/CityActivityDisplayMainItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { getDateString } from '../../../utils/utils';
4 | import styles from './cityActivityDisplayMainItem.scss';
5 |
6 | export default class CityActivityDisplayMainItem extends PureComponent {
7 | static propTypes = {
8 | activity: PropTypes.shape({
9 | alt: PropTypes.string.isRequired,
10 | image: PropTypes.string.isRequired,
11 | title: PropTypes.string.isRequired,
12 | begin_time: PropTypes.string.isRequired,
13 | end_time: PropTypes.string.isRequired,
14 | address: PropTypes.string.isRequired,
15 | wisher_count: PropTypes.number.isRequired,
16 | participant_count: PropTypes.number.isRequired
17 | }).isRequired
18 | };
19 |
20 | render() {
21 | const { activity } = this.props;
22 | return (
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 | {activity.title}
35 |
36 |
37 |
38 | {getDateString(activity.begin_time, activity.end_time)}
39 |
40 |
41 |
42 | {activity.address}
43 |
44 |
45 |
{activity.wisher_count + activity.participant_count}人关注
46 |
47 |
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/MusicDisplay/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { getSongArray } from '../../../../utils/utils';
4 | import styles from './musicDisplay.scss';
5 |
6 | export default class MusicDisplay extends PureComponent {
7 | static propTypes = {
8 | musicList: PropTypes.arrayOf(PropTypes.object).isRequired
9 | };
10 |
11 | render() {
12 | const { musicList } = this.props;
13 | return (
14 |
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/City/CityActivity/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer, inject } from 'mobx-react';
3 | import PropTypes from 'prop-types';
4 | import { Facebook as Loading } from 'react-content-loader';
5 | import CityActivityDisplay from '../CityActivityDisplay';
6 | import CityTag from '../CityTag';
7 |
8 | @inject('cityStore')
9 | @observer
10 | export default class CityActivity extends Component {
11 | static propTypes = {
12 | cityStore: PropTypes.shape({
13 | cities: PropTypes.arrayOf(PropTypes.object).isRequired,
14 | currentCity: PropTypes.object.isRequired,
15 | activityTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
16 | dayTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
17 | setCurrentCity: PropTypes.func.isRequired,
18 | setCities: PropTypes.func.isRequired
19 | }).isRequired
20 | };
21 |
22 | componentDidMount() {
23 | const { setCities } = this.props.cityStore;
24 | setCities();
25 | }
26 |
27 | render() {
28 | const {
29 | cities,
30 | currentCity,
31 | activityTypes,
32 | dayTypes,
33 | setCurrentCity
34 | } = this.props.cityStore;
35 | return (
36 |
37 | {cities.length !== 0 ? (
38 |
39 |
44 |
45 |
50 |
51 | ) : (
52 |
53 | )}
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/Music/MusicTagContent/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer, inject } from 'mobx-react';
3 | import PropTypes from 'prop-types';
4 | import { Facebook as Loading } from 'react-content-loader';
5 | import MusicTag from './MusicTag';
6 | import MusicDisplay from './MusicDisplay';
7 |
8 | @inject('musicStore')
9 | @observer
10 | export default class MusicTagContent extends Component {
11 | static propTypes = {
12 | musicStore: PropTypes.shape({
13 | musicTags: PropTypes.arrayOf(PropTypes.string).isRequired,
14 | currentMusicTag: PropTypes.string.isRequired,
15 | currentTagMusicList: PropTypes.arrayOf(PropTypes.object).isRequired,
16 | setCurrentMusicTag: PropTypes.func.isRequired,
17 | setTagMusics: PropTypes.func.isRequired
18 | }).isRequired
19 | };
20 |
21 | componentDidMount() {
22 | const { currentMusicTag, setTagMusics } = this.props.musicStore;
23 | setTagMusics(currentMusicTag);
24 | }
25 |
26 | render() {
27 | const {
28 | musicTags,
29 | currentMusicTag,
30 | currentTagMusicList,
31 | setCurrentMusicTag,
32 | setTagMusics
33 | } = this.props.musicStore;
34 | return (
35 |
36 |
42 | {currentTagMusicList.length !== 0 ? (
43 |
44 | ) : (
45 |
52 | )}
53 |
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagMoreContent/BookTagMoreDisplay/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observable, action } from 'mobx';
3 | import { observer } from 'mobx-react';
4 | import PropTypes from 'prop-types';
5 | import BookTagMoreDisplayList from '../BookTagMoreDisplayList';
6 | import LoadMoreBooks from '../LoadMoreBooks';
7 | import styles from './bookTagMoreDisplay.scss';
8 |
9 | @observer
10 | export default class BookTagMoreDisplay extends Component {
11 | static propTypes = {
12 | currentBookTag: PropTypes.string.isRequired,
13 | bookList: PropTypes.arrayOf(PropTypes.object).isRequired,
14 | setTagBooks: PropTypes.func.isRequired
15 | };
16 |
17 | componentWillReceiveProps(nextProps) {
18 | if (nextProps.currentBookTag !== this.props.currentBookTag) {
19 | this.setPageCount(10);
20 | }
21 | }
22 |
23 | @action
24 | setPageCount = (count, growth) => {
25 | if (count) {
26 | this.pageCount = count;
27 | } else {
28 | this.pageCount += growth;
29 | }
30 | const { currentBookTag, bookList, setTagBooks } = this.props;
31 | const { length } = bookList;
32 | if (this.pageCount > length) {
33 | setTagBooks(
34 | currentBookTag,
35 | this.pageCount,
36 | this.pageCount - length,
37 | true,
38 | true
39 | );
40 | }
41 | };
42 |
43 | @observable pageCount = 10;
44 |
45 | render() {
46 | const { currentBookTag, bookList } = this.props;
47 | const newBookList = bookList.slice(0, this.pageCount);
48 | return (
49 |
50 |
51 |
豆瓣读书标签: {currentBookTag}
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/Book/BookTag/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import BookTagsList from './BookTagsList';
4 | import CommonTagList from '../../Common/CommonTagList';
5 | import styles from './bookTag.scss';
6 |
7 | export default class BookTag extends PureComponent {
8 | static propTypes = {
9 | bookTags: PropTypes.arrayOf(PropTypes.object).isRequired,
10 | currentBookTags: PropTypes.shape({
11 | tagName: PropTypes.string.isRequired
12 | }).isRequired,
13 | currentBookTag: PropTypes.string.isRequired,
14 | setCurrentBookTags: PropTypes.func.isRequired,
15 | setCurrentBookTag: PropTypes.func.isRequired,
16 | setTagBooks: PropTypes.func.isRequired
17 | };
18 |
19 | handleTagsClick = tags => {
20 | const { setCurrentBookTags, setCurrentBookTag, setTagBooks } = this.props;
21 | setCurrentBookTags(tags);
22 | setCurrentBookTag(tags.subTags[0]);
23 | setTagBooks(tags.subTags[0]);
24 | };
25 |
26 | handleSubTagClick = tag => {
27 | const { setCurrentBookTag, setTagBooks } = this.props;
28 | setCurrentBookTag(tag);
29 | setTagBooks(tag);
30 | };
31 |
32 | render() {
33 | const { bookTags, currentBookTags, currentBookTag } = this.props;
34 |
35 | return (
36 |
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/Header/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer, inject } from 'mobx-react';
3 | import PropTypes from 'prop-types';
4 | import HeaderInput from './HeaderInput';
5 | import HeaderModules from './HeaderModules';
6 | import styles from './header.scss';
7 |
8 | @inject('headerStore')
9 | @observer
10 | export default class Header extends Component {
11 | static propTypes = {
12 | headerStore: PropTypes.shape({
13 | currentModule: PropTypes.object.isRequired,
14 | modules: PropTypes.arrayOf(PropTypes.object).isRequired,
15 | setCurrentModule: PropTypes.func.isRequired
16 | }).isRequired
17 | };
18 |
19 | componentWillMount() {
20 | this.onHashChange();
21 | }
22 |
23 | componentWillReceiveProps() {
24 | this.onHashChange();
25 | }
26 |
27 | // 路由变化时,切换到不同路由组件
28 | onHashChange() {
29 | const { modules, currentModule, setCurrentModule } = this.props.headerStore;
30 | const hashname = window.location.pathname;
31 | if (hashname.indexOf(currentModule.value) !== -1) return;
32 | for (let i = 0, ii = modules.length; i < ii; i += 1) {
33 | if (hashname.indexOf(modules[i].value) !== -1) {
34 | setCurrentModule(modules[i]);
35 | return;
36 | }
37 | }
38 | }
39 |
40 | render() {
41 | const { currentModule, modules } = this.props.headerStore;
42 |
43 | return (
44 |
48 |
49 |
52 | {currentModule.text}
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/stores/modules/headerStore.js:
--------------------------------------------------------------------------------
1 | import { observable, action } from 'mobx';
2 |
3 | const modules = [
4 | {
5 | value: 'book',
6 | text: '读书',
7 | field: 'books',
8 | logo: 'book-logo',
9 | searchIcon: 'book-search-icon',
10 | placeholder: '书名、作者、ISBN',
11 | backgroundColor: '#f6f6f1',
12 | subTypes: [
13 | {
14 | text: '按标签分类',
15 | path: 'book-tag'
16 | },
17 | {
18 | text: '按类型分类',
19 | path: 'book-type'
20 | }
21 | ]
22 | },
23 | {
24 | value: 'movie',
25 | text: '电影',
26 | field: 'subjects',
27 | logo: 'movie-logo',
28 | searchIcon: 'movie-search-icon',
29 | placeholder: '电影、影人、影院、电视剧',
30 | backgroundColor: '#f0f3f5',
31 | subTypes: [
32 | {
33 | text: '按时间分类',
34 | path: 'movie-show-time'
35 | },
36 | {
37 | text: '按标签分类',
38 | path: 'movie-tag'
39 | }
40 | ]
41 | },
42 | {
43 | value: 'music',
44 | text: '音乐',
45 | field: 'musics',
46 | logo: 'music-logo',
47 | searchIcon: 'music-search-icon',
48 | placeholder: '唱片名、表演者、条码、ISRC',
49 | backgroundColor: '#f0f3ef',
50 | subTypes: [
51 | {
52 | text: '按标签分类',
53 | path: 'music-tag'
54 | }
55 | ]
56 | },
57 | {
58 | value: 'city',
59 | text: '同城',
60 | logo: 'city-logo',
61 | searchIcon: 'city-search-icon',
62 | placeholder: 'sorry, 同城活动暂无搜索功能',
63 | backgroundColor: '#f6f5f2',
64 | subTypes: [
65 | {
66 | text: '按城市分类',
67 | path: 'city'
68 | }
69 | ]
70 | }
71 | ];
72 |
73 | class HeaderStore {
74 | modules = modules;
75 | @observable currentModule = modules[0];
76 |
77 | @action
78 | setCurrentModule = module => {
79 | this.currentModule = module;
80 | };
81 | }
82 |
83 | const headerStore = new HeaderStore();
84 | export default headerStore;
85 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagPrompt/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styles from './bookTagPrompt.scss';
4 |
5 | export default class BookTagPrompt extends PureComponent {
6 | static propTypes = {
7 | bookPrompt: PropTypes.shape({
8 | book: PropTypes.object,
9 | index: PropTypes.number
10 | }).isRequired,
11 | position: PropTypes.shape({
12 | width: PropTypes.number,
13 | height: PropTypes.number,
14 | top: PropTypes.number,
15 | left: PropTypes.number
16 | }).isRequired
17 | };
18 |
19 | // 计算提示框显示位置
20 | getPromptPosition() {
21 | const { bookPrompt, position } = this.props;
22 | const { width, height, top } = position;
23 | const { index } = bookPrompt;
24 | const promptTop = index < 5 ? top + 35 : top + height * 0.5 + 10;
25 | const columnIndex = (index % 5) + 1;
26 | const promptLeft = columnIndex * (width * 0.2) - columnIndex * 8;
27 | return {
28 | top: `${promptTop}px`,
29 | left: `${promptLeft}px`
30 | };
31 | }
32 |
33 | render() {
34 | const { book } = this.props.bookPrompt;
35 | const promptPosition = this.getPromptPosition();
36 | return (
37 |
41 |
42 |
43 |
{book.title}
44 |
45 | {book.author.join()} / {book.pubdate} / {book.publisher}
46 |
47 |
48 | {book.summary.length >= 160
49 | ? `${book.summary.substring(0, 160)}...`
50 | : book.summary}
51 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/Book/BookTagContent/BookTagDisplayItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { CSSTransition } from 'react-transition-group';
3 | import PropTypes from 'prop-types';
4 | import styles from './bookTagDisplayItem.scss';
5 |
6 | export default class BookTagDisplayItem extends PureComponent {
7 | static propTypes = {
8 | isShow: PropTypes.bool.isRequired,
9 | currentDirection: PropTypes.string.isRequired,
10 | bookArray: PropTypes.arrayOf(PropTypes.object).isRequired,
11 | setBookPrompt: PropTypes.func.isRequired
12 | };
13 |
14 | onMouseChange = (bookObject, index) => {
15 | const { setBookPrompt } = this.props;
16 | setBookPrompt(bookObject, index);
17 | };
18 |
19 | render() {
20 | const { isShow, currentDirection, bookArray } = this.props;
21 | return (
22 |
28 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/Movie/MovieTagContent/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer, inject } from 'mobx-react';
3 | import PropTypes from 'prop-types';
4 | import { Facebook as Loading } from 'react-content-loader';
5 | import MovieTagDisplay from './MovieTagDisplay';
6 | import MovieTag from './MovieTag';
7 | import styles from './movieTagContent.scss';
8 |
9 | @inject('movieStore')
10 | @observer
11 | export default class MovieTagContent extends Component {
12 | static propTypes = {
13 | movieStore: PropTypes.shape({
14 | movieTags: PropTypes.arrayOf(PropTypes.string).isRequired,
15 | currentMovieTag: PropTypes.string.isRequired,
16 | currentTagMovieList: PropTypes.arrayOf(PropTypes.array).isRequired,
17 | setCurrentMovieTag: PropTypes.func.isRequired,
18 | setTagMovies: PropTypes.func.isRequired
19 | }).isRequired
20 | };
21 |
22 | componentDidMount() {
23 | const { currentMovieTag, setTagMovies } = this.props.movieStore;
24 | setTagMovies(currentMovieTag);
25 | }
26 |
27 | pageCount = 0;
28 |
29 | render() {
30 | const {
31 | movieTags,
32 | currentMovieTag,
33 | currentTagMovieList,
34 | setCurrentMovieTag,
35 | setTagMovies
36 | } = this.props.movieStore;
37 | this.pageCount = currentTagMovieList.length;
38 | return (
39 |
40 |
41 | {this.pageCount !== 0 ? (
42 |
46 | ) : (
47 |
48 | )}
49 |
50 |
51 |
57 |
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/apis/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export function getSearchData(keyword, moduleType, start = 0, count = 6) {
4 | return axios.get(`/${moduleType}/search`, {
5 | params: {
6 | q: keyword,
7 | start,
8 | count
9 | }
10 | });
11 | }
12 |
13 | export function getCurrentTagBooks(tag, start = 0, count = 40) {
14 | return axios.get('/book/search', {
15 | params: {
16 | tag,
17 | start,
18 | count
19 | }
20 | });
21 | }
22 |
23 | export function getCurrentTypeBooks(type, start = 0, count = 12) {
24 | return axios.get('/book/search', {
25 | params: {
26 | q: type,
27 | start,
28 | count
29 | }
30 | });
31 | }
32 |
33 | export function getCurrentTypeMovies(type, start = 0, count = 48) {
34 | return axios.get(`/movie/${type}`, {
35 | params: {
36 | start,
37 | count
38 | }
39 | });
40 | }
41 |
42 | export function getCurrentTagMovies(tag, start = 0, count = 20) {
43 | return axios.get('/movie/search', {
44 | params: {
45 | tag,
46 | start,
47 | count
48 | }
49 | });
50 | }
51 |
52 | export function getCurrentTagMusics(tag, start = 0, count = 24) {
53 | return axios.get('/music/search', {
54 | params: {
55 | tag,
56 | start,
57 | count
58 | }
59 | });
60 | }
61 |
62 | export function getCities(start = 0, count = 20) {
63 | return axios.get('/loc/list', {
64 | params: {
65 | start,
66 | count
67 | }
68 | });
69 | }
70 |
71 | export function getActivities(loc, dayType, type, start = 0, count = 6) {
72 | return axios.get('/event/list', {
73 | params: {
74 | loc,
75 | day_type: dayType,
76 | type,
77 | start,
78 | count
79 | }
80 | });
81 | }
82 |
83 | export default {
84 | getSearchData,
85 | getCurrentTagBooks,
86 | getCurrentTypeBooks,
87 | getCurrentTypeMovies,
88 | getCurrentTagMovies,
89 | getCurrentTagMusics,
90 | getCities,
91 | getActivities
92 | };
93 |
--------------------------------------------------------------------------------
/src/components/Header/HeaderSuggest/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { observable, action } from 'mobx';
3 | import PropTypes from 'prop-types';
4 | import { processedAuthor } from '../../../utils/utils';
5 | import styles from './headerSuggest.scss';
6 |
7 | export default class HeaderSuggest extends PureComponent {
8 | static propTypes = {
9 | searchData: PropTypes.arrayOf(PropTypes.object).isRequired,
10 | isFocusOnInput: PropTypes.bool.isRequired
11 | };
12 |
13 | @action
14 | setIsFocusOnList = isFocus => {
15 | this.isFocusOnList = isFocus;
16 | };
17 |
18 | @observable isFocusOnList = false;
19 |
20 | render() {
21 | const { isFocusOnInput, searchData } = this.props;
22 |
23 | return (
24 |
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right