├── .vscode
└── settings.json
├── .eslintignore
├── .gitignore
├── src
├── view
│ ├── miao
│ │ ├── index.js
│ │ ├── miao.less
│ │ └── miao-component.js
│ ├── list
│ │ ├── list.less
│ │ ├── index.js
│ │ └── list-component.js
│ └── home
│ │ ├── home.less
│ │ ├── index.js
│ │ └── home-component.js
├── .DS_Store
├── favicon.ico
├── module
│ ├── mo-button
│ │ ├── hand.png
│ │ ├── index.js
│ │ └── button.less
│ ├── mo-steps
│ │ ├── index.js
│ │ ├── steps.js
│ │ ├── step.js
│ │ └── steps.less
│ ├── mo-spin
│ │ ├── spin.less.json
│ │ ├── index.js
│ │ └── spin.less
│ ├── co-simple-list
│ │ ├── list.less
│ │ ├── index.js
│ │ └── mo-toast
│ │ │ ├── index.js
│ │ │ ├── toast.less
│ │ │ ├── assets
│ │ │ ├── success.svg
│ │ │ ├── info.svg
│ │ │ ├── warning.svg
│ │ │ └── error.svg
│ │ │ └── toast.js
│ ├── mo-result-card
│ │ ├── resultcard.less
│ │ └── index.js
│ ├── mo-toast
│ │ ├── assets
│ │ │ ├── success.svg
│ │ │ ├── info.svg
│ │ │ ├── warning.svg
│ │ │ └── error.svg
│ │ ├── toast.css
│ │ ├── index.js
│ │ ├── toast.less
│ │ └── toast.js
│ ├── mo-list-loading
│ │ ├── index.js
│ │ └── listloading.less
│ ├── mo-carousel
│ │ ├── index.js
│ │ ├── carousel.css
│ │ ├── carousel.less
│ │ ├── method.js
│ │ ├── pagination.js
│ │ └── carousel.js
│ ├── mo-transtion
│ │ ├── index.js
│ │ └── transtion.less
│ ├── mo-table
│ │ ├── table.less
│ │ └── index.js
│ ├── mo-segmented
│ │ ├── segmented.less
│ │ ├── tabs-modules.js
│ │ └── index.js
│ ├── mo-icon
│ │ ├── icon.less
│ │ ├── index.js
│ │ └── svg-config.js
│ ├── mo-dialog
│ │ ├── modal.less
│ │ ├── index.js
│ │ ├── modal.js
│ │ └── modal-modules.js
│ ├── mo-infinite-scroll
│ │ └── index.js
│ ├── mo-tabs
│ │ ├── tabs-modules.js
│ │ ├── tabs.less
│ │ └── index.js
│ └── mo-time-count
│ │ └── index.js
├── reducers
│ ├── index.js
│ ├── user-info-reducer.js
│ └── data-list-reducer.js
├── router
│ ├── router.less
│ └── index.js
├── store
│ └── index.js
├── libs
│ └── my-util
│ │ └── index.js
├── app.js
├── layout
│ ├── user-card
│ │ ├── index.js
│ │ ├── user-card.less
│ │ └── component.js
│ └── list-tabs
│ │ ├── list-tabs.less
│ │ ├── index.js
│ │ └── component.js
├── styles
│ └── reset.less
└── couter.js
├── img
├── demo.gif
├── iterm1.png
├── iterm2.png
├── 文件结构.xmind
└── structure.png
├── old-verson
├── old-verson-1.0.zip
└── old-verson-2.0.zip
├── .babelrc
├── postcss.config.js
├── index.html
├── dist
├── index.html
└── bundle.css
├── server.js
├── .eslintrc.js
├── mock
├── db.js
└── api.js
├── LICENSE
├── webpack.dev.config.js
├── package.json
├── webpack.prod.config.js
└── README.md
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | dependent
4 | coverage
5 | webpack.*.js
6 | *Server.js
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build/
3 | npm-debug.log
4 | yarn-debug.log
5 | .DS_Store
6 | .vscode
--------------------------------------------------------------------------------
/src/view/miao/index.js:
--------------------------------------------------------------------------------
1 | import Miao from './miao-component.js'
2 |
3 |
4 | export default Miao
--------------------------------------------------------------------------------
/img/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/img/demo.gif
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/src/.DS_Store
--------------------------------------------------------------------------------
/img/iterm1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/img/iterm1.png
--------------------------------------------------------------------------------
/img/iterm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/img/iterm2.png
--------------------------------------------------------------------------------
/img/文件结构.xmind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/img/文件结构.xmind
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/img/structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/img/structure.png
--------------------------------------------------------------------------------
/old-verson/old-verson-1.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/old-verson/old-verson-1.0.zip
--------------------------------------------------------------------------------
/old-verson/old-verson-2.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/old-verson/old-verson-2.0.zip
--------------------------------------------------------------------------------
/src/module/mo-button/hand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tumars/boilerplate-webpack-react-es6-cssModule/HEAD/src/module/mo-button/hand.png
--------------------------------------------------------------------------------
/src/module/mo-steps/index.js:
--------------------------------------------------------------------------------
1 | import Steps from './steps'
2 | import Step from './step'
3 |
4 | Steps.Step = Step
5 |
6 |
7 | export default Steps
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/module/mo-spin/spin.less.json:
--------------------------------------------------------------------------------
1 | {"dyy":"spin-dyy-oX8Ta","laballspin":"spin-laballspin-2LrMx","la-dark":"spin-la-dark-1KlJY","ball-spin":"spin-ball-spin-3cuzS","la-sm":"spin-la-sm-2MZyy","la-2x":"spin-la-2x-1om4p","la-3x":"spin-la-3x-320-W"}
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-0",
7 | "react"
8 | ],
9 | "plugins": [
10 | "react-hot-loader/babel",
11 | "transform-decorators-legacy"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { UserReducer } from './user-info-reducer'
3 | import { DataListReducer } from './data-list-reducer'
4 |
5 | export default combineReducers({
6 | UserReducer,
7 | DataListReducer
8 | })
--------------------------------------------------------------------------------
/src/view/list/list.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | box-sizing: border-box;
3 | padding: 20px;
4 | }
5 |
6 | .btn {
7 | width: 300px !important;
8 | margin: 40px auto !important;
9 | color: #fff !important;
10 | background: #ff5252 !important;
11 | }
12 |
--------------------------------------------------------------------------------
/src/module/co-simple-list/list.less:
--------------------------------------------------------------------------------
1 | .list {
2 | padding-top: 20px;
3 | }
4 | .single {
5 | text-align: left;
6 | line-height: 2.5;
7 | &:nth-child(2n+1) {
8 | background-color: #eee
9 | }
10 | div {
11 | display: inline-block;
12 | padding: 0 40px;
13 | text-align: center;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/router/router.less:
--------------------------------------------------------------------------------
1 | .fill {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | right: 0;
6 | bottom: 0;
7 | width: 100%;
8 | max-width: 750px;
9 | margin: 0 auto;
10 | font-size: 32px;
11 | }
12 |
13 | .notfund {
14 | padding-top: 50px;
15 | text-align: center;
16 | font-size: 24px;
17 | }
18 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware} from 'redux'
2 | import thunk from 'redux-thunk'
3 | import reducers from '../reducers'
4 |
5 |
6 | let store = createStore(
7 | reducers,
8 | window.devToolsExtension && window.devToolsExtension(),
9 | applyMiddleware(thunk)
10 | )
11 |
12 | export default store
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer'),
4 | require('postcss-px-to-viewport')({
5 | viewportWidth: 750,
6 | viewportHeight: 568,
7 | unitPrecision: 5,
8 | viewportUnit: 'vw',
9 | selectorBlackList: [],
10 | minPixelValue: 1,
11 | mediaQuery: false
12 | })
13 | ]
14 | }
--------------------------------------------------------------------------------
/src/view/list/index.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import ListComponent from './list-component.js'
3 |
4 |
5 | const mapDispatchToProps = (dispatch,props) => {
6 | return {
7 | goBack() {
8 | props.history.replace('/')
9 | }
10 | }
11 | }
12 |
13 |
14 | const List = connect(
15 | null,
16 | mapDispatchToProps
17 | )(ListComponent)
18 |
19 | export default List
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | react-starter-kit
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 | react-starter-kit
--------------------------------------------------------------------------------
/src/module/mo-result-card/resultcard.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | position: relative;
3 | box-sizing: border-box;
4 | padding: 60px;
5 | line-height: 1;
6 | text-align: center;
7 | background: #fff;
8 | h2 {
9 | margin: 30px auto;
10 | font-size: 40px;
11 | font-weight: normal;
12 | }
13 | h3 {
14 | font-size: 28px;
15 | font-weight: normal;
16 | color: #666;
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/src/libs/my-util/index.js:
--------------------------------------------------------------------------------
1 | const _ut = (function() {
2 | const fixNum = function(num) {
3 | return num.toFixed(2)
4 | }
5 |
6 | const myfetch = async function(url, option) {
7 | const res = await fetch(url,option)
8 | if (!res.ok) {
9 | return Promise.reject(res)
10 | }
11 | return res.json()
12 | }
13 |
14 | return {
15 | fixNum,
16 | fetch: myfetch
17 | }
18 | })()
19 |
20 | export default _ut
--------------------------------------------------------------------------------
/src/view/home/home.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | box-sizing: border-box;
3 | padding: 20px;
4 | }
5 |
6 | .tip {
7 | font-size: 24px;
8 | color: #666;
9 | text-align: center;
10 | }
11 |
12 | .hello {
13 | margin: 60px auto 80px;
14 | padding: 6px 0;
15 | text-align: center;
16 | border-bottom: solid 1px #0e90d2;
17 | }
18 |
19 | .button {
20 | width: 400px;
21 | margin: 40px auto;
22 | line-height: 2.3;
23 | color: #fff;
24 | text-align: center;
25 | border-radius: 5px;
26 | background: #0e90d2;
27 | }
--------------------------------------------------------------------------------
/src/module/mo-toast/assets/success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './router';
4 | import { AppContainer } from 'react-hot-loader';
5 |
6 | if(process.env.NODE_ENV === 'development') {
7 | const render = (Component) => {
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root'));
13 | };
14 |
15 | render(App);
16 |
17 | if (module.hot) {
18 | module.hot.accept('./router', () => {
19 | render(App)
20 | });
21 | }
22 | } else {
23 | ReactDOM.render(, document.getElementById('root'));
24 | }
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.dev.config');
4 |
5 | var compiler = webpack(config);
6 | var server = new WebpackDevServer(compiler, {
7 | publicPath: config.output.publicPath,
8 | hot: true,
9 | historyApiFallback: true,
10 | inline: true,
11 | stats: {
12 | colors: true,
13 | hash: false,
14 | timings: true,
15 | chunks: false,
16 | chunkModules: false,
17 | modules: false
18 | }
19 | });
20 |
21 | server.listen(3000, 'localhost', function(err, result) {
22 | if (err) {
23 | return console.log(err);
24 | }
25 | console.log('Listening at http://localhost:3000/');
26 | });
--------------------------------------------------------------------------------
/src/module/mo-list-loading/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types'
3 | import './listloading.less';
4 |
5 | const ListLoading = ({className}) => (
6 |
7 |
8 |
加载中...
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | )
19 |
20 | ListLoading.propTypes = {
21 | className: PropTypes.bool
22 | };
23 |
24 | export default ListLoading
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "parserOptions": {
3 | "ecmaVersion": 8,
4 | "ecmaFeatures": {
5 | "jsx": true
6 | }
7 | },
8 | "parser": "babel-eslint",
9 | "env": {
10 | "browser": true,
11 | "es6": true,
12 | "node": true,
13 | "mocha": true
14 | },
15 | "rules": {
16 | "no-console": ["error", {
17 | "allow": ["warn", "error", "log", "info"]
18 | }],
19 | "react/jsx-uses-react": "error",
20 | "react/jsx-uses-vars": "error",
21 | "react/prop-types": [0]
22 | },
23 | "plugins": [
24 | "react"
25 | ],
26 | "extends": ["eslint:recommended", "plugin:react/recommended"]
27 | };
--------------------------------------------------------------------------------
/mock/db.js:
--------------------------------------------------------------------------------
1 |
2 | const list = {
3 | "movie": {
4 | "data": [
5 | {"name": "肖申克的救赎"},
6 | {"name": "这个杀手不太冷"},
7 | {"name": "霸王别姬"},
8 | {"name": "阿甘正传"},
9 | {"name": "美丽人生"},
10 | {"name": "千与千寻"},
11 | {"name": "辛德勒的名单"},
12 | {"name": "海上钢琴师"},
13 | {"name": "机器人总动员"},
14 | {"name": "盗梦空间"}
15 | ]
16 | },
17 | "book": {
18 | "data": [
19 | {"name": "窗边的小豆豆"},
20 | {"name": "摆渡人"},
21 | {"name": "追风筝的人"},
22 | {"name": "浮生六记"},
23 | {"name": "现代汉语词典"},
24 | {"name": "万历十五年"},
25 | {"name": "天才在左 疯子在右"},
26 | {"name": "解忧杂货店"},
27 | {"name": "半小时漫画中国史"},
28 | {"name": "新版中日交流标准日本语"}
29 | ]
30 | }
31 | }
32 |
33 |
34 |
35 | module.exports = {
36 | list
37 | }
38 |
--------------------------------------------------------------------------------
/src/module/mo-carousel/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import Carousel from "./carousel"
4 | import PaginationDecorator from "./pagination"
5 |
6 | const PaginationCarousel = PaginationDecorator(Carousel);
7 |
8 | class Wraper extends Component {
9 | constructor(props) {
10 | super(props)
11 | }
12 |
13 | render() {
14 | const { showDot } = this.props
15 | if(showDot) {
16 | return
17 | }
18 |
19 | return (
20 |
21 | )
22 | }
23 | }
24 |
25 | Carousel.propTypes = {
26 | showDot: PropTypes.bool
27 | }
28 |
29 |
30 | export default Wraper
--------------------------------------------------------------------------------
/src/module/mo-spin/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types'
3 | import './spin.less';
4 |
5 | var classNames = require('classnames');
6 |
7 |
8 | const Spin = ({visible=true, className, style, color="#333"}) => (
9 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 |
28 | Spin.propTypes = {
29 | visible: PropTypes.bool,
30 | className: PropTypes.bool,
31 | style: PropTypes.object
32 | };
33 |
34 | export default Spin
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/view/miao/miao.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | box-sizing: border-box;
3 | }
4 | .top {
5 | padding: 40px 0;
6 | text-align: center;
7 | background: #fff;
8 | }
9 | .steps {
10 | padding: 50px 50px 0;
11 | border-top: solid 1px #eee;
12 | background: #fff;
13 | }
14 |
15 | .detail {
16 | margin-top: 30px;
17 | padding: 20px 0;
18 | font-size: 28px;
19 | background: #fff;
20 | li {
21 | display: flex;
22 | justify-content: space-between;
23 | padding: 0 30px;
24 | line-height: 70px;
25 |
26 | div:first-child {
27 | color: #666666;
28 | }
29 |
30 | b {
31 | color: #f40;
32 | }
33 | }
34 | }
35 |
36 | .btn {
37 | position: fixed;
38 | bottom: 0;
39 | }
40 |
--------------------------------------------------------------------------------
/src/view/list/list-component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import CSSModules from 'react-css-modules'
4 | import Button from 'mo-button'
5 | import ListTabs from 'layout/list-tabs'
6 | import UserCard from 'layout/user-card'
7 | import style from './list.less'
8 |
9 |
10 | @CSSModules(style)
11 | class ListComponent extends Component {
12 | constructor(props) {
13 | super(props)
14 | }
15 | render() {
16 | const { goBack } = this.props
17 | return (
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 | }
26 |
27 | ListComponent.propTypes = {
28 | goBack: PropTypes.func
29 | }
30 |
31 | export default ListComponent
--------------------------------------------------------------------------------
/src/view/home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import Dialog from 'mo-dialog'
4 | import HomeComponent from './home-component.js'
5 |
6 | const mapDispatchToProps = (dispatch, props) => {
7 | return {
8 | initData() {
9 | console.log('init index')
10 | },
11 | goListPage() {
12 | props.history.push('/list')
13 | },
14 | goMiaoPage() {
15 | props.history.push('/miao')
16 | },
17 | openDialog() {
18 | Dialog.alert(Hello from the Moon~
)
19 | }
20 | }
21 | }
22 |
23 | const mapStateToProps = (state) => {
24 | return {
25 | ...state.HomeReducer
26 | }
27 | }
28 |
29 |
30 | const Home = connect(
31 | mapStateToProps,
32 | mapDispatchToProps
33 | )(HomeComponent)
34 |
35 | export default Home;
--------------------------------------------------------------------------------
/src/module/mo-transtion/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CSSTransition } from 'react-transition-group'
3 | import './transtion.less'
4 |
5 | export const SlideTransition = (props) => (
6 |
11 | )
12 |
13 | export const FadeTransition = (props) => (
14 |
19 | )
20 |
21 | export const SpreadTransition = (props) => (
22 |
27 | )
28 |
29 | export const PopTransition = (props) => (
30 |
35 | )
36 |
--------------------------------------------------------------------------------
/src/layout/user-card/index.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import _ut from 'my-util'
3 | import { changeUserInfo, increaseCount } from 'reducers/user-info-reducer'
4 | import Toast from 'mo-toast'
5 | import Component from './component.js'
6 |
7 | const mapDispatchToProps = (dispatch) => {
8 | return {
9 | initData () {
10 | const wid = _ut.fixNum(window.innerWidth)
11 | const hei = _ut.fixNum(window.innerHeight)
12 |
13 | dispatch(changeUserInfo(`${wid} * ${hei}`))
14 | },
15 | increaseCount() {
16 | dispatch(increaseCount())
17 | Toast('+1')
18 | }
19 | }
20 | }
21 |
22 | const mapStateToProps = (state) => {
23 | const { screenSize, countValue, tomorrow } = state.UserReducer
24 | return {
25 | screenSize, countValue, tomorrow
26 | }
27 | }
28 |
29 |
30 | export default connect(
31 | mapStateToProps,
32 | mapDispatchToProps
33 | )(Component)
--------------------------------------------------------------------------------
/src/module/mo-button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import './button.less'
4 | var classNames = require('classnames');
5 |
6 | const Button = ({children, onClick, type="primary", size, bg, className, style}) => {
7 | if(bg) {
8 | style.background = bg
9 | }
10 | const cln = classNames("tj-btn", {
11 | ["tj-btn__large"]: size === 'large',
12 | ["tj-btn__small"]: size === 'small',
13 | ["tj-btn__primary"]: type === 'primary',
14 | }, className)
15 | return (
16 |
17 | {children}
18 |
19 | )
20 | }
21 |
22 | Button.propTypes = {
23 | onClick: PropTypes.func,
24 | type: PropTypes.string,
25 | size: PropTypes.string,
26 | bg: PropTypes.string,
27 | className: PropTypes.bool,
28 | style: PropTypes.object
29 | };
30 |
31 |
32 | export default Button
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/module/mo-table/table.less:
--------------------------------------------------------------------------------
1 | :global{
2 | @c1: #fba112;
3 | @c7: #666666;
4 |
5 | .tj-table__content {
6 | display: table;
7 | width: 100%;
8 | line-height: 2.8;
9 | font-size: 26px;
10 | overflow: hidden;
11 | }
12 |
13 | .tj-table__row {
14 | display: table-row;
15 | // justify-content: space-between;
16 | width: 100%;
17 | overflow: hidden;
18 |
19 | &:nth-child(2n+1) {
20 | background: #f6f4f4;
21 | }
22 | }
23 |
24 | th.tj-table__row {
25 | background: #f6f4f4;
26 | }
27 |
28 | .tj-table__col {
29 | // flex-grow: 1;
30 | display: table-cell;
31 | text-align: center;
32 | word-break: break-all;
33 | }
34 |
35 | .tj-table__col--top {
36 | .tj-table__col;
37 | font-weight: normal
38 | }
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/src/module/co-simple-list/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { TransitionGroup } from 'react-transition-group'
4 | import { SpreadTransition } from '../mo-transtion'
5 | import style from './list.less'
6 |
7 | const Row = ({ value }) => (
8 |
9 |
{ value[0] }
10 |
{ value[1] }
11 |
12 | )
13 |
14 | const MyList = ({ data }) => {
15 | const items = data.map(item => (
16 |
17 |
18 |
19 | ))
20 | return (
21 |
22 |
23 | {items}
24 |
25 |
26 | )
27 | }
28 |
29 | MyList.propTypes = {
30 | data: PropTypes.array
31 | }
32 |
33 | export default MyList;
--------------------------------------------------------------------------------
/src/module/mo-segmented/segmented.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-segmented__content {
3 | position: relative;
4 | display: table;
5 | width: 100%;
6 | height: 60px;
7 | line-height: 60px;
8 | font-size: 32px;
9 | text-align: center;
10 | color: #fff;
11 | border: solid 1px #fff;
12 | border-radius: 12px;
13 | overflow: hidden;
14 | }
15 |
16 | .tj-segmented__item {
17 | display: inline-block;
18 | height: inherit;
19 | box-sizing: border-box;
20 | list-style: none;
21 | color: #fff;;
22 | border-right: solid 1px #fff;
23 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
24 | cursor: pointer;
25 |
26 | &:last-child {
27 | border-right: none;
28 | }
29 | }
30 |
31 | .tj-segmented__item--active {
32 | // background: #fff;
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/module/mo-toast/toast.css:
--------------------------------------------------------------------------------
1 | :global .tj-toast {
2 | position: fixed;
3 | left: 50%;
4 | top: 50%;
5 | transform: translateX(-50%) translateY(-100%);
6 | box-sizing: border-box;
7 | padding: 0 30px;
8 | height: 60px;
9 | line-height: 60px;
10 | font-size: 28px;
11 | color: #fff;
12 | text-align: center;
13 | border-radius: 10px;
14 | background: rgba(0, 0, 0, 0.7);
15 | }
16 | :global .tj-toast--hasicon {
17 | padding: 40px 30px;
18 | width: 100px;
19 | height: 100px;
20 | }
21 | :global .tj-toast-img {
22 | display: block;
23 | margin: 0 auto;
24 | width: 25px;
25 | height: 25px;
26 | margin-bottom: 30px;
27 | overflow: hidden;
28 | }
29 | :global .tj-toast-close {
30 | display: inline-block;
31 | box-sizing: border-box;
32 | padding-left: 20px;
33 | margin-left: 20px;
34 | margin-right: -20px;
35 | border-left: solid 1px #fff;
36 | height: 28px;
37 | line-height: 28px;
38 | font-size: 28px;
39 | cursor: pointer;
40 | }
41 |
--------------------------------------------------------------------------------
/src/module/mo-button/button.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-btn {
3 | display: block;
4 | width: 600px;
5 | margin: 20px auto 0;
6 | line-height: 2.5;
7 | font-size: 32px;
8 | text-align: center;
9 | color: #000;
10 | background: #fff;
11 | border-radius: 4px;
12 | box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.12), 0px 2px 2px 0px rgba(0,0,0,0.24);
13 | }
14 |
15 | .tj-btn__large {
16 | width: 100%;
17 | border: none;
18 | border-top: solid 1px #eee;
19 | border-radius: 0;
20 | box-shadow: 0px 0px -2px 0px rgba(0,0,0,0.12), 0px -2px -2px 0px rgba(0,0,0,0.24);
21 | }
22 |
23 | .tj-btn__small {
24 | width: 150px;
25 | margin: 20px auto 0;
26 | line-height: 2.5;
27 | font-size: 24px;
28 | box-shadow: none;
29 | }
30 |
31 | .tj-btn__primary {
32 | color: #fff;
33 | background: #2196f3;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/layout/user-card/user-card.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | position: relative;
3 | box-sizing: border-box;
4 | padding: 20px 30px 50px;
5 | width: 100%;
6 | background: #fff;
7 | box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.12), 0px 2px 2px 0px rgba(0,0,0,0.24);
8 | }
9 |
10 | .title {
11 | width: 100%;
12 | padding: 9px 0 0;
13 | margin-bottom: 12px;
14 | font-size: 32px;
15 | }
16 |
17 | .tip {
18 | line-height: 2;
19 | font-size: 26px;
20 | color: #212121;
21 | }
22 |
23 | .hit {
24 | display: inline-block;
25 | min-width: 80px;
26 | line-height: 1.2;
27 | text-align: center;
28 | color: #2196f3;
29 | border-bottom: solid 1px #2196f3
30 | }
31 |
32 | .logo {
33 | position: absolute;
34 | display: inline-block;
35 | bottom: 10px;
36 | right: 10px;
37 | width: 180px;
38 | height: 105px;
39 | background: #ffc400;
40 | background: linear-gradient(to bottom right, #ffc400 0%,#ffc400 65%, #ffeb3b 65%, #ffeb3b 100%)
41 | }
--------------------------------------------------------------------------------
/src/module/mo-result-card/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Icon from 'mo-icon'
4 | import styles from './resultcard.less'
5 |
6 | var classNames = require('classnames');
7 |
8 | const ResultCard = ({type, title, desc, className, style}) => {
9 | const isSuccess = type === 'success';
10 | const stroke = isSuccess ? '#7ed321' : '#ff7748'
11 | const iconType = isSuccess ? 'check-circle' : 'fail-circle'
12 | return (
13 |
14 |
15 |
{title}
16 | {desc}
17 |
18 | )
19 | }
20 |
21 | ResultCard.propTypes = {
22 | type: PropTypes.string,
23 | title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
24 | desc: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
25 | className: PropTypes.bool,
26 | style: PropTypes.object
27 | };
28 |
29 | export default ResultCard
30 |
31 |
32 |
--------------------------------------------------------------------------------
/mock/api.js:
--------------------------------------------------------------------------------
1 | // import { SetActiveRecordForAPP, GetActiveCollection } from './config'
2 |
3 | var db = require('./db')
4 | var http = require('http'),
5 | url = require('url');
6 |
7 | var { list } = db;
8 |
9 | function mytimeout(t) {
10 | return new Promise(resolve =>{
11 | setTimeout(resolve, t)
12 | })
13 | }
14 |
15 | // 创建http server,并传入回调函数:
16 | var server = http.createServer(async function (request, response) {
17 | await mytimeout(2000)
18 |
19 | // 回调函数接收request和response对象,
20 | // 获得HTTP请求的method和url:
21 | console.log(request.method + ': ' + request.url);
22 | // 将HTTP响应200写入response, 同时设置Content-Type: text/html:
23 | response.writeHead(200, {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'});
24 | var pathname = url.parse(request.url).pathname;
25 | response.end(JSON.stringify(list[pathname.slice(1)]));
26 | });
27 |
28 | // 让服务器监听3003端口:
29 | server.listen(3003);
30 |
31 | console.log('Server is running at http://127.0.0.1:3003/');
--------------------------------------------------------------------------------
/src/module/mo-steps/steps.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import './steps.less'
4 |
5 | var classNames = require('classnames');
6 |
7 |
8 | class Steps extends Component {
9 | constructor(props) {
10 | super(props);
11 | }
12 | render() {
13 | const {
14 | current,
15 | children,
16 | className,
17 | style,
18 | } = this.props;
19 | const len = children.length;
20 |
21 | return (
22 |
23 | {
24 | React.Children.map(children, (child, index)=> {
25 | return React.cloneElement(child, {
26 | status: current >= index ? 'success' : 'wait',
27 | lineStyle: {display: len-1 === index ? 'none' : 'auto'}
28 | })
29 | })
30 | }
31 |
32 | )
33 | }
34 | }
35 |
36 |
37 | Steps.propTypes = {
38 | current: PropTypes.number,
39 | className: PropTypes.bool,
40 | style: PropTypes.object
41 | };
42 |
43 | export default Steps
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/module/mo-toast/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom';
3 |
4 | import Toast from './Toast';
5 |
6 | export default function Message(props = {}, option) {
7 | const div = document.createElement('div')
8 | document.body.appendChild(div)
9 |
10 | if (typeof props === 'string') {
11 | props = {
12 | content: props
13 | };
14 | }
15 | if (option) {
16 | props = {...props, ...option}
17 | }
18 | const component = React.createElement(Toast, Object.assign({}, props, {
19 | onClose: () => {
20 | ReactDOM.unmountComponentAtNode(div)
21 | document.body.removeChild(div)
22 |
23 | if (props.onClose instanceof Function) {
24 | props.onClose();
25 | }
26 | }
27 | }));
28 |
29 | ReactDOM.render(component, div);
30 | }
31 |
32 | /* eslint-disable */
33 | ['text', 'success', 'warning', 'info', 'error', 'loading'].forEach(type => {
34 | Message[type] = (options = {}) => {
35 | return Message(options, type);
36 | };
37 | });
38 | /* eslint-enable */
39 |
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom';
3 |
4 | import Toast from './Toast';
5 |
6 | export default function Message(props = {}, option) {
7 | const div = document.createElement('div');
8 |
9 | document.body.appendChild(div);
10 |
11 | if (typeof props === 'string') {
12 | props = {
13 | content: props
14 | };
15 | }
16 | if (option) {
17 | props = {...props, ...option}
18 | }
19 | const component = React.createElement(Toast, Object.assign(props, {
20 | willUnmount: () => {
21 | ReactDOM.unmountComponentAtNode(div);
22 | document.body.removeChild(div);
23 | if (props.onClose instanceof Function) {
24 | props.onClose();
25 | }
26 | }
27 | }));
28 |
29 | ReactDOM.render(component, div);
30 | }
31 |
32 | /* eslint-disable */
33 | ['text', 'success', 'warning', 'info', 'error', 'loading'].forEach(type => {
34 | Message[type] = (options = {}) => {
35 | return Message(options, type);
36 | };
37 | });
38 | /* eslint-enable */
39 |
--------------------------------------------------------------------------------
/src/styles/reset.less:
--------------------------------------------------------------------------------
1 | :global{
2 | html,
3 | body,
4 | h1,
5 | h2,
6 | h3,
7 | h4,
8 | h5,
9 | h6,
10 | p,
11 | blockquote,
12 | ul,
13 | ol,
14 | li,
15 | dl,
16 | dt,
17 | dd,
18 | form,
19 | fieldset,
20 | legend,
21 | input,
22 | textarea,
23 | button,
24 | th,
25 | td {
26 | margin: 0;
27 | padding: 0;
28 | border: 0;
29 | vertical-align: baseline;
30 | }
31 | html {
32 | line-height: 1;
33 | }
34 | table {
35 | border-collapse: collapse;
36 | border-spacing: 0;
37 | }
38 | ul,
39 | ol {
40 | list-style: none;
41 | }
42 | img {
43 | border: 0;
44 | }
45 | b,
46 | strong {
47 | font-weight: 700;
48 | }
49 | article,
50 | aside,
51 | details,
52 | figcaption,
53 | figure,
54 | footer,
55 | header,
56 | hgroup,
57 | main,
58 | nav,
59 | section,
60 | summary {
61 | display: block;
62 | }
63 | audio,
64 | canvas,
65 | progress,
66 | video {
67 | display: inline-block;
68 | vertical-align: baseline;
69 | }
70 |
71 | body{
72 | line-height: 1.5;
73 | font-family: Helvetica, Tahoma, Arial, "Microsoft YaHei", "微软雅黑", Heiti, "黑体", SimSun, "宋体", sans-serif;
74 | background: #eee;
75 | }
76 | }
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/toast.less:
--------------------------------------------------------------------------------
1 | :global {
2 | .tj-msg {
3 | position: fixed;
4 | left: 50%;
5 | top: 50%;
6 | transform: translateX(-50%) translateY(-50%);
7 | box-sizing: border-box;
8 | padding: 0 30px;
9 | height: 60px;
10 | line-height: 60px;
11 | font-size: 28px;;
12 | color: #fff;
13 | text-align: center;
14 | border-radius: 10px;
15 | background: rgba(0, 0, 0, .7);
16 | }
17 |
18 | .tj-msg--hasicon {
19 | padding: 40px 30px;
20 | width: 100px;
21 | height: 100px;
22 | }
23 |
24 | .tj-msg-img {
25 | display: block;
26 | margin: 0 auto;
27 | width: 25px;
28 | height: 25px;
29 | margin-bottom: 30px;
30 | overflow: hidden;
31 | }
32 |
33 | .tj-msg-close {
34 | display: inline-block;
35 | box-sizing: border-box;
36 | padding-left: 20px;
37 | margin-left: 20px;
38 | margin-right: -20px;
39 | border-left: solid 1px #fff;
40 | height: 28px;
41 | line-height: 28px;
42 | font-size: 28px;
43 | cursor: pointer
44 | }
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/module/mo-icon/icon.less:
--------------------------------------------------------------------------------
1 |
2 | :global{
3 | .tj-icon {
4 | display: inline-block;
5 | width: 46px;
6 | height: 46px;
7 | box-sizing: border-box;
8 | padding: 2px;
9 | stroke: #f3f3f3;
10 | }
11 |
12 | .tj-icon_large {
13 | width: 130px;
14 | height: 130px;
15 | }
16 |
17 | .tj-icon_medium {
18 | width: 80px;
19 | height: 80px;
20 | stroke-dasharray: 0 1000px;
21 | }
22 |
23 | .tj-icon_ani {
24 | stroke-width: 50px;
25 | fill-opacity: 0;
26 | stroke-dashoffset: 7% 7%;
27 | stroke-dasharray: 0 35%;
28 | :local {
29 | animation: tj-icon__ani_fillIn 1s 1 linear;
30 | animation-fill-mode: forwards;
31 | }
32 | }
33 | }
34 |
35 | @keyframes tj-icon__ani_fillIn {
36 | 0%{
37 | fill-opacity: 0;
38 | stroke-dashoffset: 7%, 7%;
39 | }
40 | 70%{
41 | fill-opacity: 0;
42 | stroke-dashoffset: 100%, 7%;
43 | }
44 | 100% {
45 | fill-opacity: 1;
46 | stroke-dasharray: 100%, 0;
47 | }
48 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 tumars
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/view/home/home-component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import CSSModules from 'react-css-modules'
4 | import Button from 'mo-button'
5 | import UserCard from 'layout/user-card'
6 | import style from './home.less'
7 |
8 | @CSSModules(style, {handleNotFoundStyleName: 'ignore'})
9 | class HomeComponent extends Component {
10 | constructor(props) {
11 | super(props)
12 | }
13 | componentDidMount() {
14 | this.props.initData()
15 | }
16 | render() {
17 | const {
18 | openDialog,
19 | goListPage,
20 | goMiaoPage,
21 | countValue
22 | } = this.props
23 |
24 | return (
25 |
26 |
27 |
你好,我是个简单的示例 domo
28 |
{countValue}
29 |
30 |
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 | HomeComponent.propTypes = {
38 | initData: PropTypes.func,
39 | goListPage: PropTypes.func
40 | }
41 |
42 | export default HomeComponent
--------------------------------------------------------------------------------
/src/module/mo-toast/toast.less:
--------------------------------------------------------------------------------
1 | :global {
2 | .tj-toast {
3 | position: fixed;
4 | left: 50%;
5 | top: 50%;
6 | transform: translateX(-50%) translateY(-100%);
7 | box-sizing: border-box;
8 | padding: 0 30px;
9 | height: 60px;
10 | line-height: 60px;
11 | font-size: 28px;;
12 | color: #fff;
13 | text-align: center;
14 | border-radius: 10px;
15 | background: rgba(0, 0, 0, .7);
16 | }
17 |
18 | .tj-toast--hasicon {
19 | padding: 40px 30px;
20 | width: 100px;
21 | height: 100px;
22 | }
23 |
24 | .tj-toast-img {
25 | display: block;
26 | margin: 0 auto;
27 | width: 25px;
28 | height: 25px;
29 | margin-bottom: 30px;
30 | overflow: hidden;
31 | }
32 |
33 | .tj-toast-close {
34 | display: inline-block;
35 | box-sizing: border-box;
36 | padding-left: 20px;
37 | margin-left: 20px;
38 | margin-right: -20px;
39 | border-left: solid 1px #fff;
40 | height: 28px;
41 | line-height: 28px;
42 | font-size: 28px;
43 | cursor: pointer
44 | }
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/layout/user-card/component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import CSSModules from 'react-css-modules'
4 | import TimeCount from 'mo-time-count'
5 | import style from './user-card.less'
6 |
7 | @CSSModules(style)
8 | class UserCard extends Component {
9 | constructor(props) {
10 | super(props)
11 | this.state = {
12 | timeCount: 0
13 | }
14 | }
15 | componentDidMount() {
16 | this.props.initData()
17 | }
18 | render() {
19 | const {
20 | screenSize,
21 | countValue,
22 | tomorrow,
23 | increaseCount
24 | } = this.props
25 |
26 | return (
27 |
28 |
用户信息
29 |
你的浏览器可视面积为:{screenSize}
30 |
你无聊的点击了:+{countValue} 次
31 |
距离明天还有:
32 |
33 |
34 | )
35 | }
36 | }
37 |
38 | UserCard.propTypes = {
39 | countValue: PropTypes.number,
40 | screenSize: PropTypes.string,
41 | tomorrow: PropTypes.number,
42 | initData: PropTypes.func,
43 | increaseCount: PropTypes.func
44 | }
45 |
46 | export default UserCard
--------------------------------------------------------------------------------
/src/module/mo-icon/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import SvgConfig from './svg-config'
4 | import './icon.less'
5 | var classNames = require('classnames');
6 |
7 |
8 | const Icon = ({
9 | type,
10 | size,
11 | color='none',
12 | stroke='none',
13 | style={},
14 | className,
15 | ani=false
16 | }) => {
17 | const cln = classNames('tj-icon', {
18 | ['tj-icon_large']: size === 'large',
19 | ['tj-icon_medium']: size === 'medium',
20 | ['tj-icon_small']: size === 'small',
21 | }, className)
22 | style.fill = color;
23 | style.stroke = stroke;
24 |
25 | return (
26 |
36 | )
37 | }
38 |
39 |
40 | Icon.propTypes = {
41 | type: PropTypes.string,
42 | size: PropTypes.string,
43 | color: PropTypes.string,
44 | stroke: PropTypes.string,
45 | className: PropTypes.string,
46 | ani: PropTypes.bool,
47 | };
48 |
49 | export default Icon;
--------------------------------------------------------------------------------
/src/module/mo-carousel/carousel.css:
--------------------------------------------------------------------------------
1 | :global .tj-carousel__wrap {
2 | position: relative;
3 | width: 100%;
4 | }
5 | :global .tj-carousel__slide-list {
6 | position: relative;
7 | display: flex;
8 | flex-wrap: nowrap;
9 | touch-action: pan-y pan-x;
10 | will-change: transfrom;
11 | transition-property: transform;
12 | box-sizing: content-box;
13 | }
14 | :global .tj-carousel__slide {
15 | display: inline-block;
16 | flex: 0 1 auto;
17 | position: relative;
18 | }
19 | :global .tj-carousel__pagination-wrap {
20 | position: relative;
21 | overflow: hidden;
22 | }
23 | :global .tj-carousel__pagination-content {
24 | position: absolute;
25 | bottom: 10px;
26 | width: 100%;
27 | height: 25px;
28 | box-sizing: border-box;
29 | padding: 0;
30 | line-height: 1;
31 | text-align: center;
32 | overflow: hidden;
33 | }
34 | :global .tj-carousel__bullet {
35 | display: inline-block;
36 | width: 20px;
37 | height: 20px;
38 | margin: 0 10px;
39 | padding: 0;
40 | background: #d4a143;
41 | border-radius: 50%;
42 | }
43 | :global .tj-carousel__bullet_active {
44 | display: inline-block;
45 | width: 20px;
46 | height: 20px;
47 | margin: 0 10px;
48 | padding: 0;
49 | background: #d4a143;
50 | border-radius: 50%;
51 | background: #a32229;
52 | }
53 |
--------------------------------------------------------------------------------
/src/layout/list-tabs/list-tabs.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | position: relative;
3 | box-sizing: border-box;
4 | padding: 20px;
5 | width: 100%;
6 | background: #fff;
7 | box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.12), 0px 2px 2px 0px rgba(0,0,0,0.24);
8 | }
9 |
10 | .book-tab {
11 | text-align: center;
12 |
13 | h2 {
14 | margin-top: 30px;
15 | font-size: 48px;
16 | font-weight: normal;
17 | }
18 |
19 | h3 {
20 | box-sizing: border-box;
21 | margin: 50px 0 20px;
22 | padding: 30px 0 0 5%;
23 | font-size: 30px;
24 | font-weight: normal;
25 | text-align: left;
26 | border-top: dashed 1px #0e90d2;
27 |
28 | b {
29 | display: inline-block;
30 | box-sizing: border-box;
31 | margin: 0 10px;
32 | padding: 5px 10px;
33 | line-height: 1;
34 | font-size: .95em;
35 | color: #0e90d2;
36 | }
37 | }
38 |
39 | .segmented {
40 | width: 90%;
41 | margin: 0 auto;
42 | }
43 | }
44 |
45 | .button {
46 | width: 300px;
47 | margin: 40px auto;
48 | line-height: 2;
49 | color: #fff;
50 | text-align: center;
51 | border-radius: 10px;
52 | background: #0e90d2;
53 | }
54 |
55 | .panel {
56 | position: relative;
57 | }
58 |
--------------------------------------------------------------------------------
/src/module/mo-carousel/carousel.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-carousel__wrap {
3 | position: relative;
4 | width: 100%;
5 | }
6 | .tj-carousel__slide-list {
7 | position: relative;
8 | display: flex;
9 | flex-wrap: nowrap;
10 | touch-action: pan-y pan-x;
11 | will-change: transfrom;
12 | transition-property: transform;
13 | box-sizing: content-box;
14 | }
15 |
16 | .tj-carousel__slide {
17 | display: inline-block;
18 | flex: 0 1 auto;
19 | position: relative;
20 | }
21 |
22 | .tj-carousel__pagination-wrap {
23 | position: relative;
24 | overflow: hidden;
25 | }
26 | .tj-carousel__pagination-content {
27 | position: absolute;
28 | bottom: 10px;
29 | width: 100%;
30 | height: 25px;
31 | box-sizing: border-box;
32 | padding: 0;
33 | line-height: 1;
34 | text-align: center;
35 | overflow: hidden;
36 | // pointer-events: none
37 | }
38 |
39 | .tj-carousel__bullet {
40 | display: inline-block;
41 | width: 20px;
42 | height: 20px;
43 | margin: 0 10px;
44 | padding: 0;
45 | background: #d4a143;
46 | border-radius: 50%;
47 | }
48 |
49 | .tj-carousel__bullet_active {
50 | .tj-carousel__bullet;
51 | background: #a32229;
52 | }
53 | }
--------------------------------------------------------------------------------
/src/reducers/user-info-reducer.js:
--------------------------------------------------------------------------------
1 | /*-----------------------------------------------------------------*/
2 | /*User Reducer*/
3 | /*-----------------------------------------------------------------*/
4 | const getTomorrow = () => {
5 | const now = new Date(),
6 | date = now.getDate() + 1;
7 |
8 | return Date.parse(new Date(now.getFullYear(), now.getMonth(), date))
9 | }
10 |
11 |
12 | const initUserInfo = {
13 | screenSize: null,
14 | countValue:0,
15 | tomorrow: getTomorrow()
16 | }
17 |
18 | const UserReducer = (state = initUserInfo, action) => {
19 | switch (action.type) {
20 | case 'USER_INFO':
21 | return Object.assign({}, state, {screenSize: action.info})
22 | case 'USER_INCREMENT':
23 | return Object.assign({}, state, {countValue: state.countValue + action.num})
24 | case 'USER_CLEAR_COUNT':
25 | return Object.assign({}, state, {countValue: 0})
26 | default:
27 | return state
28 | }
29 | }
30 |
31 |
32 | /*-----------------------------------------------------------------*/
33 | /*User Action*/
34 | /*-----------------------------------------------------------------*/
35 | const changeUserInfo = (info) => ({type: 'USER_INFO', info})
36 | const increaseCount = (num=1) => ({type: 'USER_INCREMENT', num})
37 | const clearCount = () => ({type: 'USER_CLEAR_COUNT'})
38 |
39 |
40 | export {
41 | UserReducer,
42 | changeUserInfo,
43 | increaseCount ,
44 | clearCount
45 | }
--------------------------------------------------------------------------------
/src/module/mo-steps/step.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import Icon from 'mo-icon'
4 | import './steps.less'
5 |
6 | var classNames = require('classnames');
7 |
8 | class Step extends Component {
9 | static defaultProps = {
10 | status: 'wait'
11 | };
12 | constructor(props) {
13 | super(props);
14 | }
15 | render() {
16 | const {
17 | title,
18 | description,
19 | status,
20 | style,
21 | lineStyle
22 | } = this.props;
23 | const statusClass = `is-${status}`;
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 | {
33 | status === 'success'
34 | ?
35 | :
36 | }
37 |
38 |
39 |
40 |
{title}
41 |
{description}
42 |
43 |
44 | )
45 |
46 |
47 | }
48 | }
49 |
50 |
51 | Step.propTypes = {
52 | title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
53 | description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
54 | status: PropTypes.string,
55 | style: PropTypes.object,
56 | lineStyle: PropTypes.object,
57 | };
58 |
59 | export default Step
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/view/miao/miao-component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import CSSModules from 'react-css-modules'
4 | import ResultCard from 'mo-result-card'
5 | import Steps from 'mo-steps'
6 | import Button from 'mo-button'
7 | import style from './miao.less'
8 |
9 |
10 | @CSSModules(style, {handleNotFoundStyleName: 'ignore'})
11 | class MiaoComponent extends Component {
12 | constructor(props) {
13 | super(props)
14 | }
15 | render() {
16 | return (
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | -
31 |
粮
32 | 湿粮干粮猫罐头
33 |
34 | -
35 |
玩具
36 | 瓦楞纸激光枪逗猫棒
37 |
38 | -
39 |
厕所
40 | 1 猫 2 厕每天铲
41 |
42 | -
43 |
猫窝
44 | 沙发跟床都是窝
45 |
46 |
47 |
48 |
49 | )
50 | }
51 | }
52 |
53 | MiaoComponent.propTypes = {
54 | goBack: PropTypes.func
55 | }
56 |
57 | export default MiaoComponent
58 |
--------------------------------------------------------------------------------
/src/layout/list-tabs/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from 'react'
3 | import { connect } from 'react-redux'
4 | import { createSelector } from 'reselect'
5 | import _ut from 'my-util'
6 | import { fetchAddList, clearList } from 'reducers/data-list-reducer'
7 | import ListComponent from './component.js'
8 | import Dialog from 'mo-dialog'
9 |
10 |
11 | const ErrorTip = () => 网络出错, 请执行 yarn mock 启动接口
12 | const handleFetchError = (fetchPromise) => {
13 | Promise.resolve(fetchPromise)
14 | .then(res=> res)
15 | .catch(e=>{
16 | Dialog.alert()
17 | })
18 | }
19 |
20 |
21 | const mapDispatchToProps = (dispatch) => {
22 | const getMovie = () => {
23 | handleFetchError(dispatch(fetchAddList()))
24 | }
25 | return {
26 | getMovie,
27 | initData() {
28 | dispatch(clearList())
29 | getMovie()
30 | },
31 | tabChange(index) {
32 | console.log(index)
33 | }
34 | }
35 | }
36 |
37 |
38 |
39 | /* 使用 formatList 将列表转换成其他格式,并使用 reselect 避免重复计算与渲染 */
40 | const formatList = (data)=> {
41 | if(!data) {return null}
42 | const { list } = data
43 | if(!Array.isArray(list)){return data}
44 |
45 | const newList = list.map((item, i)=>
46 | ([i + 1, item.name])
47 | )
48 |
49 | return Object.assign({}, data, {list: newList})
50 | }
51 |
52 | const selectorMovieList = createSelector(
53 | state => state.DataListReducer,
54 | formatList
55 | )
56 |
57 | const mapStateToProps = (state) => {
58 | return {
59 | movieListInfo : selectorMovieList(state)
60 | }
61 | }
62 |
63 | export default connect(
64 | mapStateToProps,
65 | mapDispatchToProps
66 | )(ListComponent)
--------------------------------------------------------------------------------
/src/module/mo-dialog/modal.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-dialog__dyy {
3 | position: fixed;
4 | z-index: 10;
5 | top: 0;
6 | left: 0;
7 | box-sizing: border-box;
8 | width: 100%;
9 | height: 100%;
10 | background: rgba(0, 0, 0, 0.4);
11 | -webkit-tap-highlight-color: transparent;
12 | }
13 |
14 | .tj-dialog__content {
15 | z-index: 20;
16 | position: fixed;
17 | top: 100px;
18 | left: 50%;
19 | right: 0;
20 | width: 700px;
21 | max-height: 800px;
22 | margin-left: -350px;
23 | box-sizing: border-box;
24 | color: #000;
25 | font-size: 36px;
26 | line-height: 1.5;
27 | text-align: center;
28 | box-shadow: rgba(0, 0, 0, 0.25) 0px 28px 90px, rgba(0, 0, 0, 0.22) 0px 20px 36px;
29 | border-radius: 4px;
30 | background: #fff;
31 | overflow-x: hidden;
32 | overflow-y: scroll;
33 | -webkit-overflow-scrolling: touch;
34 | &::-webkit-scrollbar {display:none}
35 | }
36 |
37 | .tj-dialog__close {
38 | position: absolute;
39 | z-index: 30;
40 | right: 0;
41 | top: 0;
42 | width: 0.88*75px;
43 | height: 0.88*75px;
44 | line-height: 1.19*75px;
45 | font-size: 0.5*75px;
46 | font-weight: bold;
47 | text-align: center;
48 | -webkit-tap-highlight-color: transparent;
49 |
50 | svg {
51 | width: 0.63*75px;
52 | height: 0.63*75px;
53 | fill: #999;
54 | }
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/module/mo-carousel/method.js:
--------------------------------------------------------------------------------
1 | function GetSlideAngle(dx,dy) {
2 | return Math.atan2(dy,dx) * 180 / Math.PI;
3 | }
4 |
5 | // 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
6 | function GetSlideDirection(startX,startY, endX, endY) {
7 | var dy = startY - endY;
8 | var dx = endX - startX;
9 | var result = 0; // 没滑动
10 |
11 | //如果滑动距离太短
12 | if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
13 | return result;
14 | }
15 | var angle = GetSlideAngle(dx, dy);
16 | if (angle >= -45 && angle < 45) {
17 | result = 1;// 向右
18 | }else if (angle >= 45 && angle < 135) {
19 | result = 0; // 向上
20 | }else if (angle >= -135 && angle < -45) {
21 | result = 0; // 向下
22 | }else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
23 | result = -1; // 向左
24 | }
25 | return result;
26 | }
27 |
28 |
29 |
30 | // 设置 css3 属性
31 | function setCss3Style(el, prop, val) {
32 | var vendors = [
33 | '-webkit-',
34 | '-o-',
35 | '-moz-',
36 | '-ms-',
37 | ''
38 | ];
39 | function toCamelCase(str) {
40 | return str.toLowerCase().replace(/(\/-[a-z])/g, function($1) {
41 | return $1.toUpperCase().replace('-', '');
42 | });
43 | }
44 |
45 | function setCss3Style(el, prop, val) {
46 | vendors.forEach(function(vendor) {
47 | var property = toCamelCase(vendor + prop);
48 |
49 | if(property in el.style) {
50 | el.style[property] = val;
51 | }
52 | });
53 | }
54 |
55 | return setCss3Style(el, prop, val)
56 | }
57 |
58 | export {
59 | GetSlideDirection,
60 | setCss3Style
61 | }
--------------------------------------------------------------------------------
/src/module/mo-infinite-scroll/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | var classNames = require('classnames')
3 |
4 | class InfiniteScroll extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.state = {
8 | isLoading: false
9 | }
10 | }
11 | componentDidMount() {
12 | const { onLoadMore } = this.props;
13 | const handleLoadError = () =>{ this.setState({isLoading: false}) }
14 | const handleLoadStart = () => { this.setState({isLoading: true}) }
15 | const handleLoadFinish = () => { this.setState({isLoading: false})}
16 |
17 | this.myscroll.addEventListener("scroll", () => {
18 | const { scrollTop , clientHeight, scrollHeight } = this.myscroll;
19 | const isCatchBottom = scrollTop + clientHeight >= scrollHeight - 10;
20 | if (isCatchBottom && !this.state.isLoading) {
21 | handleLoadStart();
22 | Promise.resolve(onLoadMore())
23 | .catch(handleLoadError)
24 | .finally(handleLoadFinish);
25 | }
26 | });
27 | }
28 | render() {
29 | const { isLoading } = this.state;
30 | const { height = '420px', loader, className, style } = this.props;
31 |
32 | return (
33 | this.myscroll = n}
35 | className={classNames(className)}
36 | style={{ height, overflow: "auto", ...style }}
37 | >
38 | {this.props.children}
39 | {loader || ( isLoading && loading...
)}
40 |
41 | )
42 | }
43 | }
44 |
45 |
46 | export default InfiniteScroll
--------------------------------------------------------------------------------
/src/module/mo-toast/assets/info.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/module/mo-segmented/tabs-modules.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import './tabs.less';
4 |
5 | var classNames = require('classnames');
6 |
7 | // 标签页内容
8 | const Panel = ({children, isActive }) => (
9 |
15 | {children}
16 |
17 | )
18 |
19 | Panel.propTypes = {
20 | key: PropTypes.oneOfType([
21 | PropTypes.number,
22 | PropTypes.string
23 | ]),
24 | isActive: PropTypes.bool
25 | }
26 |
27 | // 标签
28 | const Nav = ({titles, activeIndex, onChange}) => {
29 | const len = titles.length
30 | return (
31 |
32 | {
33 | titles.map((title, i) =>
34 |
44 | { title }
45 |
46 | )
47 | }
48 |
49 |
50 | )
51 | }
52 |
53 | Nav.propTypes = {
54 | titles: PropTypes.array,
55 | activeIndex: PropTypes.number,
56 | onChange: PropTypes.func,
57 | }
58 |
59 | export {
60 | Panel,
61 | Nav
62 | }
--------------------------------------------------------------------------------
/src/module/mo-table/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import './table.less';
4 |
5 |
6 | const TjTable = ({columns, dataSource, cLassName}) => (
7 |
8 |
9 |
10 |
11 | {
12 | columns.map(v=>
13 | | {v.title} |
14 | )
15 | }
16 |
17 | {
18 | dataSource.map(rowItem=>
19 |
20 | {
21 | columns.map(colItem=>
22 | | {rowItem[colItem.dataIndex]} |
23 | )
24 | }
25 |
26 | )
27 | }
28 |
29 |
30 |
31 | )
32 |
33 |
34 | TjTable.propTypes = {
35 | dataSource : PropTypes.arrayOf(PropTypes.object),
36 | columns : PropTypes.arrayOf(PropTypes.object),
37 | cLassName: PropTypes.string
38 | }
39 |
40 | TjTable.defaultProps = {
41 | dataSource: [
42 | {key: 1, n1: '某某某', n2: '某某某', n3: '某某某'},
43 | {key: 2, n1: '某某某', n2: '某某某', n3: '某某某'},
44 | {key: 3, n1: '某某某', n2: '某某某', n3: '某某某'},
45 | {key: 4, n1: '某某某', n2: '某某某', n3: '某某某'},
46 | ],
47 | columns:[
48 | {title:'属性1', dataIndex:'n1'},
49 | {title:'属性2', dataIndex:'n2'},
50 | {title:'属性3', dataIndex:'n3'},
51 | ]
52 | }
53 |
54 | export default TjTable
--------------------------------------------------------------------------------
/src/module/mo-toast/assets/warning.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/module/mo-toast/assets/error.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/module/mo-steps/steps.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-step {
3 | position: relative;
4 | height: 150px;
5 | line-height: 1.5;
6 | vertical-align: top;
7 | }
8 | .tj-step__head {
9 | display: inline-block;
10 | width: 50px;
11 | height: 50px;
12 | border-radius: 50%;
13 | color: #fff;
14 | vertical-align: top;
15 | }
16 | .tj-step__line {
17 | position: absolute;
18 | top:30px;
19 | bottom: 0;
20 | left: 23px;
21 | width: 1px;
22 | height: 133px;
23 | box-sizing: border-box;
24 | border-color: inherit;
25 | background-color: #ccc;
26 | }
27 | .is-success .tj-step__line{
28 | top:46px;
29 | }
30 | .tj-step__line-inner {
31 | display: block;
32 | border-width: 1px;
33 | border-style: solid;
34 | border-color: inherit;
35 | transition: all 150ms;
36 | box-sizing: border-box;
37 | width: 0;
38 | height: 0;
39 | }
40 | .is-success .tj-step__line-inner {
41 | border-left: solid 1px #7ED321;
42 | height: 100%;
43 | }
44 | .tj-step__icon-none {
45 | position: relative;
46 | left: 14px;
47 | display: inline-block;
48 | width: 16px;
49 | height: 16px;
50 | border: solid 1px #ccc;
51 | border-radius: 50%;
52 | background: #fff;
53 | }
54 |
55 | .tj-step__main {
56 | display: inline-block;
57 | padding-left: 20px;
58 | padding-right: 10px;
59 | white-space: normal;
60 | text-align: left;
61 | }
62 |
63 | .tj-step__title {
64 | font-size: 34px;
65 | color: #000;
66 | }
67 |
68 | .tj-step__description {
69 | font-size: 28px;
70 | color: #666666;
71 | }
72 | }
--------------------------------------------------------------------------------
/src/module/mo-tabs/tabs-modules.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import './tabs.less';
4 |
5 | var classNames = require('classnames');
6 |
7 | // 标签页内容
8 | const Panel = ({children, isActive, className, style }) => (
9 |
17 | {children}
18 |
19 | )
20 |
21 | Panel.propTypes = {
22 | key: PropTypes.oneOfType([
23 | PropTypes.number,
24 | PropTypes.string
25 | ]),
26 | isActive: PropTypes.bool
27 | }
28 |
29 | // 标签
30 | const Nav = ({titles, activeIndex, onChange}) => {
31 | const len = titles.length
32 | return (
33 |
34 | {
35 | titles.map((title, i) =>
36 |
46 | { title }
47 |
48 | )
49 | }
50 |
51 |
52 | )
53 | }
54 |
55 | Nav.propTypes = {
56 | titles: PropTypes.array,
57 | activeIndex: PropTypes.number,
58 | onChange: PropTypes.func,
59 | }
60 |
61 | export {
62 | Panel,
63 | Nav
64 | }
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/assets/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route } from 'react-router'
3 | import { HashRouter, Switch } from 'react-router-dom'
4 | import { Provider } from 'react-redux'
5 | import { TransitionGroup } from 'react-transition-group'
6 | import { SlideTransition } from 'mo-transtion'
7 | import store from '../store'
8 |
9 | /*引入页面组件*/
10 | import Home from '../view/home'
11 | import List from '../view/list'
12 | import Miao from '../view/miao'
13 |
14 | /*引入路由切换样式*/
15 | import style from './router.less'
16 |
17 | /*引入全局样式*/
18 | import '../styles/reset.less'
19 |
20 | /* 做 vw vh 的降级处理 */
21 | // require('viewport-units-buggyfill').init();
22 |
23 |
24 | /*定义路由配置数组*/
25 | const routes_config = [
26 | {
27 | path: '/',
28 | component: Home,
29 | isExact: true
30 | }, {
31 | path: '/list',
32 | component: List
33 | }, {
34 | path: '/miao',
35 | component: Miao
36 | }
37 | ]
38 |
39 |
40 | const App = () => (
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {
49 | routes_config.map(config=>
50 |
51 | )
52 | }
53 |
54 |
55 |
56 |
57 | }/>
58 |
59 |
60 | )
61 |
62 |
63 | /*如果不需要页面过渡效果,参考下面更简单易懂的写法*/
64 | /*const App = () => (
65 |
66 |
67 | // import { Switch } from 'react-router'
68 |
69 |
70 |
71 |
72 |
73 | ) */
74 |
75 |
76 | export default App
--------------------------------------------------------------------------------
/src/module/mo-tabs/tabs.less:
--------------------------------------------------------------------------------
1 | :global{
2 | @c1: #fba112;
3 | @c7: #000;
4 |
5 | .tj-tabs__content {
6 | position: relative;
7 | width: 100%;
8 | overflow: hidden;
9 | }
10 |
11 | .tj-tabs__nav-wrap {
12 | display: block;
13 | position: relative;
14 | box-sizing: border-box;
15 | padding-left: 0;
16 | margin: 0;
17 | list-style: none;
18 | transition: transform .5s cubic-bezier(.645,.045,.355,1);
19 | overflow: hidden;
20 | }
21 |
22 | .tj-tabs__nav {
23 | display: inline-block;
24 | height: 60px;
25 | box-sizing: border-box;
26 | line-height: 60px;
27 | list-style: none;
28 | font-size: 32px;
29 | color: @c7;
30 | text-align: center;
31 | position: relative;
32 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
33 | cursor: pointer;
34 | }
35 |
36 | .tj-tabs__nav--active {
37 | color: @c1
38 | }
39 |
40 | .tj-tabs__activebar {
41 | position: absolute;
42 | bottom: 0;
43 | left: 0;
44 | height: 4px;
45 | background-color: @c1;
46 | z-index: 1;
47 | will-change: margin-left;
48 | transition: margin-left .2s cubic-bezier(.645,.045,.355,1);
49 | list-style: none;
50 | }
51 |
52 | .tj-tabs__pane-wrap {
53 | display: flex;
54 | flex-direction: row;
55 | flex-wrap: nowrap;
56 | width: 100%;
57 | margin-top: 30px;
58 | will-change: transform;
59 | }
60 | .tj-tabs__pane-wrap--ani {
61 | transition: transform .2s cubic-bezier(.645,.045,.355,1);
62 | }
63 |
64 |
65 | .tj-tabs__pane {
66 | width: 100%;
67 | flex-shrink: 0;
68 | transition: opacity .45s;
69 | opacity: 1;
70 | }
71 |
72 | .tj-tabs__pane--inactive {
73 | // opacity: 0;
74 | // height: 0;
75 | // padding: 0;
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/src/layout/list-tabs/component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import CSSModules from 'react-css-modules'
4 | import Tabs from 'mo-tabs'
5 | import SimpleList from 'co-simple-list'
6 | import InfiniteScroll from 'mo-infinite-scroll'
7 | import ListLoading from 'mo-list-loading'
8 | import Spin from 'mo-spin'
9 | import Segmented from 'mo-segmented'
10 | import style from './list-tabs.less'
11 |
12 | const Loader = () => (
13 |
14 | 加载中
15 |
16 | )
17 |
18 | @CSSModules(style)
19 | class ListTabs extends Component {
20 | constructor(props) {
21 | super(props)
22 | }
23 |
24 | componentDidMount() {
25 | this.props.initData()
26 | }
27 |
28 | render() {
29 | const {
30 | movieListInfo:{list},
31 | tabChange, getMovie
32 | } = this.props
33 |
34 | return (
35 |
36 | tabChange(next)}
39 | >
40 |
41 | }
46 | >
47 | {
48 | Array.isArray(list) && list.length > 0
49 | ?
50 | :
51 | }
52 |
53 |
54 |
55 |
56 | 并没有图书列表
57 | 来看看无处安放的segmented组件:
58 |
64 |
65 |
66 |
67 | )
68 | }
69 | }
70 |
71 | ListTabs.propTypes = {
72 | movieListInfo: PropTypes.object,
73 | getMovie: PropTypes.func,
74 | }
75 |
76 | export default ListTabs
--------------------------------------------------------------------------------
/src/module/mo-carousel/pagination.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | const PaginationDecorator = (Carousel) =>(
4 | class Pagination extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.state = {
8 | activeIndex: this.props.activeIndex || 0
9 | }
10 | }
11 |
12 | handleTabClick(i) {
13 | const { activeIndex } = this.state
14 |
15 | if(i !== activeIndex) {
16 | this.setState({
17 | activeIndex: i
18 | })
19 | }
20 | }
21 |
22 | render() {
23 | const { activeIndex } = this.state
24 | return (
25 |
26 |
{
30 | this.setState({activeIndex:next})
31 | this.props.onChange && this.props.onChange(perv, next)
32 | }}
33 | >
34 | {this.props.children}
35 |
36 |
37 |
38 | {
39 | this.props.children.map((v,i)=>
40 |
this.handleTabClick(i)}
44 | >
45 |
46 | )
47 | }
48 |
49 |
50 | )
51 | }
52 | }
53 | )
54 |
55 |
56 | export default PaginationDecorator
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/assets/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/assets/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/assets/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/src/module/mo-list-loading/listloading.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-list-loading__content {
3 | width: 100%;
4 | margin: 0 auto;
5 | position: relative;
6 | }
7 | .tj-list-loading__errtip {
8 | position: relative;
9 | margin-top: 20px;
10 | // filter: blur(2px);
11 | // filter: ~"progid\:DXImageTransform\.Microsoft\.Blur(PixelRadius\=1, MakeShadow\=false)"; /* IE6~IE9 */
12 | div {
13 | position: relative;
14 | display: block;
15 | height: 20px;
16 | margin: 0 0 18px;
17 | background: #ddd;
18 | border-radius: 8px;
19 | overflow: hidden;
20 |
21 | span{
22 | content: "";
23 | opacity: .8;
24 | display: block;
25 | width: 100%;
26 | height: inherit;
27 | background: #eee;
28 | :local {
29 | animation: tj-list-loading__progressactive 1s infinite linear;
30 | }
31 |
32 | }
33 |
34 | &:nth-child(1) {width: 30%;}
35 | &:nth-child(2) {width: 20%;margin-bottom: 30px;}
36 | &:nth-child(3) {width: 60%;}
37 | &:nth-child(4) {width: 80%;}
38 | &:nth-child(5) {width: 50%;}
39 | }
40 | }
41 | .tj-list-loading__word {
42 | position: absolute;
43 | top: 0;
44 | right: 0;
45 | color: #333;
46 | font-size: 13px;
47 | font-weight: bold;
48 | :local {
49 | animation: tj-list-loading__fadein .8s infinite;
50 | }
51 | }
52 |
53 |
54 | }
55 |
56 |
57 | @keyframes tj-list-loading__fadein {
58 | 0%{
59 | opacity: 1;
60 | }
61 | 50%{
62 | opacity: .8;
63 | }
64 | 100% {
65 | opacity: 1;
66 | }
67 | }
68 |
69 |
70 | @keyframes tj-list-loading__progressactive {
71 | 0% {
72 | opacity: 0;
73 | transform: translateX(0);
74 | }
75 |
76 | 50% {
77 | opacity: .8;
78 | transform: translateX(50%);
79 | }
80 |
81 | 100% {
82 | opacity: 0;
83 | transform: translateX(100%);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/module/mo-dialog/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import ReactDOM from 'react-dom'
3 | import Modal from './modal'
4 |
5 | function alert(msg, props) {
6 | props = {
7 | msg: {msg}
,
8 | visible: true,
9 | ...props
10 | }
11 | return next(props)
12 | }
13 |
14 |
15 | function next(props) {
16 | const div = document.createElement('div')
17 | document.body.appendChild(div)
18 |
19 | if(props.lockScroll != false) {
20 | document.body.style.setProperty('overflow', 'hidden')
21 | }
22 |
23 | const component = React.createElement(Modal, Object.assign({}, props, {
24 | onClose: () => {
25 | setTimeout(()=>{
26 | ReactDOM.unmountComponentAtNode(div)
27 | document.body.removeChild(div)
28 | document.body.style.removeProperty('overflow')
29 | }, 200)
30 |
31 |
32 | if (props.onClose instanceof Function) {
33 | props.onClose()
34 | }
35 | }
36 | }))
37 |
38 | ReactDOM.render(component, div)
39 | }
40 |
41 |
42 |
43 | const modalRoot = document.body;
44 |
45 | class Dialog extends Component {
46 | constructor(props) {
47 | super(props);
48 | this.el = document.createElement('div');
49 | }
50 |
51 | static alert = alert
52 |
53 | renderPoral() {
54 | const props = this.props
55 | return React.createElement(Modal, Object.assign({}, props, {
56 | onClose: () => {
57 | setTimeout(()=>{
58 | document.body.style.removeProperty('overflow')
59 | }, 200)
60 |
61 | if (props.onClose instanceof Function) {
62 | props.onClose()
63 | }
64 | }
65 | }))
66 | }
67 |
68 | componentDidMount() {
69 | modalRoot.appendChild(this.el);
70 | }
71 |
72 | componentWillUnmount() {
73 | modalRoot.removeChild(this.el);
74 | }
75 |
76 | render() {
77 | return ReactDOM.createPortal(
78 | this.renderPoral(),
79 | this.el,
80 | );
81 | }
82 | }
83 |
84 | export default Dialog
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const {
2 | resolve
3 | } = require('path');
4 | var webpack = require('webpack');
5 | var HtmlWebpackPlugin = require('html-webpack-plugin');
6 |
7 | // process.noDeprecation = true
8 |
9 | module.exports = {
10 | devtool: 'cheap-module-eval-source-map',
11 | entry: [
12 | 'react-hot-loader/patch',
13 | 'webpack-dev-server/client?http://localhost:3000',
14 | 'webpack/hot/only-dev-server',
15 | 'babel-polyfill',
16 | './src/app'
17 | ],
18 | output: {
19 | path: resolve(__dirname, '/dist/'),
20 | filename: 'bundle.js',
21 | publicPath: '/'
22 | },
23 | plugins: [
24 | new webpack.HotModuleReplacementPlugin(),
25 | new webpack.NamedModulesPlugin(),
26 | new HtmlWebpackPlugin({
27 | template: 'index.html',
28 | filename: './index.html',
29 | inject: true
30 | }),
31 | new webpack.ProvidePlugin({
32 | 'Promise':'es6-promise',
33 | 'fetch': 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch'
34 | }),
35 | new webpack.DefinePlugin({
36 | 'process.env': {
37 | 'NODE_ENV': JSON.stringify('development')
38 | }
39 | })
40 | ],
41 | module: {
42 | rules: [{
43 | test: /\.js$/,
44 | include: /src/,
45 | use: [
46 | "babel-loader",
47 | "eslint-loader"
48 | ]
49 | }, {
50 | test: /\.less$/,
51 | exclude: /node_modules/,
52 | use: [
53 | 'style-loader', {
54 | loader: 'css-loader',
55 | options: {
56 | minimize: true,
57 | modules: true,
58 | localIdentName: '[name]-[local]-[hash:base64:5]',
59 | importLoaders: 2
60 | }
61 | },
62 | 'postcss-loader',
63 | 'less-loader'
64 | ],
65 | }, {
66 | test: /\.(jpe?g|png|gif|svg)$/i,
67 | include: /src/,
68 | use: [
69 | 'url-loader?limit=8192&name=img/[hash:8].[name].[ext]' // 图片小于8k就转化为 base64, 或者单独作为文件
70 | ]
71 | }]
72 | },
73 | resolve: {
74 | extensions: ['.js', '.jsx', '.json'],
75 | modules: ['node_modules', './src/module', './src/action', './src/util/'],
76 | alias: {
77 | 'my-util': resolve(__dirname, './src/libs/my-util'),
78 | 'action': resolve(__dirname, './src/action/index.js'),
79 | 'stroe': resolve(__dirname, './src/store/index.js'),
80 | 'layout': resolve(__dirname, './src/layout'),
81 | 'reducers': resolve(__dirname, './src/reducers')
82 | }
83 | }
84 | };
--------------------------------------------------------------------------------
/src/module/mo-spin/spin.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-laballspin {
3 | position: relative;
4 | display: block;
5 | width: 32px;
6 | height: 32px;
7 | font-size: 0;
8 |
9 | div {
10 | position: absolute;
11 | display: inline-block;
12 | width: 8px;
13 | height: 8px;
14 | border-radius: 100%;
15 | background-color: currentColor;
16 | border: 0 solid currentColor;
17 | :local {
18 | animation: tj-laballspin-spin 1s infinite ease-in-out;
19 | }
20 |
21 |
22 | &:nth-child(1) {
23 | top: 5%;
24 | left: 50%;
25 | animation-delay: -1.125s;
26 | }
27 | &:nth-child(2) {
28 | top: 18.1801948466%;
29 | left: 81.8198051534%;
30 | animation-delay: -1.25s;
31 | }
32 | &:nth-child(3) {
33 | top: 50%;
34 | left: 95%;
35 | animation-delay: -1.375s;
36 | }
37 | &:nth-child(4) {
38 | top: 81.8198051534%;
39 | left: 81.8198051534%;
40 | animation-delay: -1.5s;
41 | }
42 | &:nth-child(5) {
43 | top: 94.9999999966%;
44 | left: 50.0000000005%;
45 | animation-delay: -1.625s;
46 | }
47 | &:nth-child(6) {
48 | top: 81.8198046966%;
49 | left: 18.1801949248%;
50 | animation-delay: -1.75s;
51 | }
52 | &:nth-child(7) {
53 | top: 49.9999750815%;
54 | left: 5.0000051215%;
55 | animation-delay: -1.875s;
56 | }
57 | &:nth-child(8) {
58 | top: 18.179464974%;
59 | left: 18.1803700518%;
60 | animation-delay: -2s;
61 | }
62 |
63 | }
64 | }
65 |
66 | }
67 |
68 | @keyframes tj-laballspin-spin {
69 | 0%,
70 | 100% {
71 | opacity: 1;
72 | transform: scale(1);
73 | }
74 | 20% {
75 | opacity: 1;
76 | }
77 | 80% {
78 | opacity: 0;
79 | transform: scale(0);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/module/mo-transtion/transtion.less:
--------------------------------------------------------------------------------
1 | :global{
2 | .tj-trans__slide-appear,
3 | .tj-trans__slide-enter {
4 | opacity: 0;
5 | transform: translate(100%);
6 | }
7 |
8 | .tj-trans__slide-appear-active,
9 | .tj-trans__slide-enter-active {
10 | opacity: 1;
11 | transform: translate(0);
12 | transition: transform 500ms ease, opacity 500ms ease-in;
13 | }
14 |
15 | .tj-trans__slide-exit {
16 | opacity: 1;
17 | transform: translate(0);
18 | }
19 |
20 | .tj-trans__slide-exit-active {
21 | opacity: 0.5;
22 | transform: translate(-100%);
23 | transition: transform 500ms ease, opacity 500ms ease-out;
24 | }
25 |
26 |
27 | .tj-trans__fade-appear,
28 | .tj-trans__fade-enter {
29 | opacity: 0;
30 | }
31 |
32 | .tj-trans__fade-appear-active,
33 | .tj-trans__fade-enter-active {
34 | transition: opacity .3s linear;
35 | opacity: 1;
36 | }
37 |
38 | .tj-trans__fade-exit {
39 | transition: opacity .2s linear;
40 | opacity: 1;
41 | }
42 |
43 | .tj-trans__fade-exit-active {
44 | opacity: 0;
45 | }
46 |
47 |
48 |
49 |
50 | .tj-trans__spread-enter {
51 | opacity: 0.01;
52 | transform: scale(.8);
53 | }
54 | .tj-trans__spread-enter-active {
55 | opacity: 1;
56 | transform: scale(1);
57 | transition: transform 100ms linear, opacity 100ms linear;
58 | }
59 |
60 | .tj-trans__spread-exit {
61 | opacity: 1;
62 | transform: translate(0, 0);
63 | }
64 | .tj-trans__spread-exit-active {
65 | opacity: 0.01;
66 | transform: translate(0, 2rem) scale(.8);
67 | transition: transform 200ms ease-in, opacity 200ms linear;
68 | }
69 |
70 |
71 |
72 |
73 | .tj-trans__pop-enter {
74 | opacity: 0.01;
75 | transform: scale(.8);
76 | }
77 | .tj-trans__pop-enter-active {
78 | opacity: 1;
79 | transform: scale(1);
80 | transition: transform 100ms linear, opacity 100ms linear;
81 | }
82 |
83 | .tj-trans__pop-exit {
84 | opacity: 1;
85 | transform: translate(0, 0);
86 | }
87 | .tj-trans__pop-exit-active {
88 | opacity: 0.01;
89 | transform: translate(0, 150px) scale(.8);
90 | transition: transform 200ms ease-in, opacity 200ms linear;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-boilerplate",
3 | "version": "3.0",
4 | "description": "",
5 | "private": false,
6 | "license": "MIT",
7 | "scripts": {
8 | "mock": "node mock/api.js",
9 | "start": "node server.js",
10 | "lint": "eslint src",
11 | "build-mac": "rm -rf dist && webpack --config ./webpack.prod.config.js --progress --profile --colors",
12 | "build-win": "del dist && webpack --config ./webpack.prod.config.js --progress --profile --colors"
13 | },
14 | "author": "tumars",
15 | "devDependencies": {
16 | "autoprefixer": "^7.2.3",
17 | "babel-core": "^6.24.1",
18 | "babel-eslint": "^8.0.3",
19 | "babel-loader": "^7.0.0",
20 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
21 | "babel-preset-env": "^1.6.1",
22 | "babel-preset-react": "^6.24.1",
23 | "babel-preset-stage-0": "^6.24.1",
24 | "css-loader": "^0.28.7",
25 | "eslint": "^4.13.1",
26 | "eslint-loader": "^1.7.1",
27 | "eslint-plugin-react": "^7.5.1",
28 | "eventsource-polyfill": "^0.9.6",
29 | "exports-loader": "^0.6.3",
30 | "express": "^4.16.2",
31 | "extract-text-webpack-plugin": "^3.0.2",
32 | "fetch-jsonp": "^1.0.6",
33 | "file-loader": "^1.1.6",
34 | "html-webpack-plugin": "^2.17.0",
35 | "imports-loader": "^0.7.0",
36 | "less": "^2.7.3",
37 | "less-loader": "^4.0.3",
38 | "postcss-loader": "^2.0.9",
39 | "postcss-px-to-viewport": "^0.0.3",
40 | "react-hot-loader": "3.1.3",
41 | "redux-saga": "^0.16.0",
42 | "resolve-url-loader": "^2.2.1",
43 | "style-loader": "^0.19.1",
44 | "url-loader": "^0.6.2",
45 | "webpack": "^3.10.0",
46 | "webpack-dev-middleware": "^2.0.2",
47 | "webpack-dev-server": "^2.9.7"
48 | },
49 | "dependencies": {
50 | "babel-polyfill": "^6.23.0",
51 | "es6-promise": "^4.1.1",
52 | "history": "^4.6.1",
53 | "prop-types": "^15.5.10",
54 | "react": "^16.2.0",
55 | "react-css-modules": "^4.7.1",
56 | "react-dom": "^16.2.0",
57 | "react-redux": "^5.0.4",
58 | "react-router": "^4.2.0",
59 | "react-router-dom": "^4.1.1",
60 | "react-transition-group": "^2.2.1",
61 | "redux": "^3.6.0",
62 | "redux-thunk": "^2.1.0",
63 | "reselect": "^3.0.1",
64 | "viewport-units-buggyfill": "^0.6.2",
65 | "whatwg-fetch": "^2.0.1"
66 | },
67 | "homepage": "https://github.com/tumars/boilerplate-webpack-react-es6-cssModule"
68 | }
69 |
--------------------------------------------------------------------------------
/src/module/mo-dialog/modal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Mask, Content } from './modal-modules'
4 |
5 | class Modal extends Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.state = {
10 | isShow: false
11 | };
12 |
13 | this.enter = this.enter.bind(this)
14 | this.leave = this.leave.bind(this)
15 | this.handleMaskClick = this.handleMaskClick.bind(this)
16 | }
17 |
18 | componentDidMount() {
19 | this.props.visible && this.enter();
20 | }
21 |
22 | componentWillReceiveProps(nextProps) {
23 | console.log(123, nextProps)
24 | if (nextProps.visible && !this.state.isShow) {
25 | this.enter()
26 | } else if(!nextProps.visible && this.state.isShow) {
27 | this.leave()
28 | }
29 | }
30 |
31 | shouldComponentUpdate(nextProps) {
32 | if(!nextProps.visible && !this.isShow) {
33 | return false
34 | } else {
35 | return true
36 | }
37 | }
38 |
39 | enter() {
40 | this.setState({isShow: true})
41 | }
42 |
43 | leave() {
44 | this.setState({isShow: false});
45 | this.props.onClose && this.props.onClose()
46 | }
47 |
48 | handleMaskClick() {
49 | this.props.closeOnClickModal && this.leave()
50 | }
51 |
52 | render() {
53 | const {msg, children, showCloseIcon} = this.props
54 | const { isShow } = this.state
55 |
56 | return (
57 |
58 |
59 |
65 | {children}
66 |
67 |
68 | );
69 | }
70 | }
71 |
72 | Modal.propTypes = {
73 | onClose: PropTypes.func,
74 | visible: PropTypes.bool,
75 | children:PropTypes.node,
76 | msg: PropTypes.node,
77 | showCloseIcon: PropTypes.bool,
78 | closeOnClickModal: PropTypes.bool
79 | };
80 |
81 | Modal.defaultProps = {
82 | visible: false,
83 | showCloseIcon: true,
84 | closeOnClickModal: false
85 | };
86 |
87 | export default Modal;
--------------------------------------------------------------------------------
/src/module/mo-dialog/modal-modules.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { TransitionGroup } from 'react-transition-group'
3 | import { FadeTransition, PopTransition } from '../mo-transtion'
4 | import style from './modal.less';
5 |
6 | var classNames = require('classnames');
7 |
8 | // 控制显示
9 | const View = ({ isshow, className, style, children, onClick}) => {
10 | return (
11 |
16 | {children}
17 |
18 | )
19 | }
20 |
21 |
22 | //关闭按钮
23 | const CloseIcon = ({visible, onClick}) => (
24 |
25 |
28 |
29 | )
30 |
31 |
32 | // 背景透明层
33 | const Mask = ({visible, onClick}) => (
34 |
35 |
36 |
41 |
42 |
43 | )
44 |
45 |
46 | // 内容主体
47 | const Content = ({className, visible, msg, children, showCloseIcon, onCloseIcon}) => (
48 |
49 |
50 |
51 | {children}
52 | {msg}
53 |
54 |
55 |
56 |
57 | )
58 |
59 |
60 |
61 | export {
62 | Mask,
63 | Content
64 | }
65 |
--------------------------------------------------------------------------------
/src/module/mo-icon/svg-config.js:
--------------------------------------------------------------------------------
1 | const SvgConfig = {
2 | "check-circle": 'M51.2 512C51.2 257.501288 257.501288 51.2 512 51.2s460.8 206.301288 460.8 460.8c0 254.498712-206.301288 460.8-460.8 460.8S51.2 766.498712 51.2 512zM0 512c0 282.771525 229.228475 512 512 512 282.771525 0 512-229.228475 512-512 0-282.771525-229.228475-512-512-512C229.228475 0 0 229.228475 0 512zM704.104136 369.23878c-13.876068-14.014915-36.334644-14.014915-50.453695 0L452.833627 573.144949l-83.230373-83.863864c-13.624407-14.014915-36.334644-14.014915-49.950373 0-13.624407 14.023593-13.624407 36.707797 0.503322 50.471051l105.697627 107.320407 1.770305 2.030644c14.119051 13.763254 36.325966 13.763254 50.445017 0l226.034983-228.890034C718.231864 405.937898 718.231864 383.505356 704.104136 369.23878z',
3 | "fail-circle": 'M512.697 75.445c-247.539 0-448.208 200.669-448.208 448.208 0 247.54 200.669 448.208 448.208 448.208 247.54 0 448.208-200.668 448.208-448.208 0-247.539-200.668-448.208-448.208-448.208zM690.686 657.208c12.265 12.274 12.265 32.171 0 44.427-12.283 12.274-32.171 12.274-44.453 0l-133.316-133.316-133.327 133.316c-12.274 12.274-32.161 12.274-44.445 0-12.265-12.256-12.265-32.153 0-44.427l133.335-133.353-133.335-133.317c-12.265-12.256-12.265-32.171 0-44.444 12.283-12.256 32.171-12.256 44.445 0l133.327 133.335 133.316-133.335c12.283-12.256 32.171-12.256 44.453 0 12.265 12.274 12.265 32.189 0 44.444l-133.335 133.317 133.335 133.353z',
4 | "check": 'M416.832 798.08C400.64 798.08 384.512 791.872 372.16 779.52L119.424 525.76C94.784 500.992 94.784 460.8 119.424 436.032 144.128 411.264 184.128 411.264 208.768 436.032L416.832 644.928 814.4 245.76C839.04 220.928 879.04 220.928 903.744 245.76 928.384 270.528 928.384 310.656 903.744 335.424L461.504 779.52C449.152 791.872 432.96 798.08 416.832 798.08Z',
5 | "fail": 'M813.378513 293.592688 596.64533 510.324848 813.378513 727.058031c23.917736 23.917736 23.939225 62.757323 0.002047 86.695524-23.938202 23.938202-62.777789 23.916712-86.696548-0.001023L509.950829 597.019349 293.217646 813.752532c-23.938202 23.938202-62.774719 23.917736-86.692455 0-23.958668-23.957645-23.938202-62.755276 0-86.693478L423.257352 510.325871 206.525192 293.593711c-23.960715-23.960715-23.937179-62.757323 0-86.694501 23.937179-23.937179 62.732763-23.960715 86.693478 0l216.73216 216.73216L726.684012 206.89921c23.938202-23.938202 62.73788-23.959691 86.695524-0.001023C837.297272 230.815923 837.315692 269.654486 813.378513 293.592688z',
6 | }
7 |
8 | export default SvgConfig;
--------------------------------------------------------------------------------
/src/module/mo-tabs/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import Carousel from '../mo-carousel'
4 | import { Panel, Nav} from './tabs-modules'
5 | import './tabs.less';
6 |
7 |
8 | class Tabs extends Component {
9 | static Panel = Panel
10 | constructor(props) {
11 | super(props)
12 | this.state = {
13 | activeIndex: this.props.activeIndex
14 | }
15 | this.handleChange = this.handleChange.bind(this)
16 | }
17 |
18 | shouldComponentUpdate(nextProps, nextState) {
19 | if (this.state.activeIndex !== nextState.activeIndex) {
20 | return true
21 | }
22 | if(this.props.children !== nextProps.children) {
23 | return true
24 | }
25 | return true
26 | }
27 |
28 | handleChange(nextIndex) {
29 | const { activeIndex } = this.state
30 | if(nextIndex !== activeIndex) {
31 | this.setState({
32 | activeIndex: nextIndex
33 | })
34 | this.props.onTabChange && this.props.onTabChange(activeIndex, nextIndex)
35 | }
36 | }
37 |
38 | render() {
39 | const { activeIndex } = this.state
40 | if(this.props.children.length==0) {
41 | return 'missing Tabs Panel'
42 | }
43 | const panels = this.props.children
44 |
45 | return (
46 | this.tabContent = n} className="tj-tabs__content">
47 |
70 | )
71 | }
72 | }
73 |
74 | Tabs.propTypes = {
75 | activeIndex: PropTypes.number,
76 | type: PropTypes.string,
77 | onTabChange: PropTypes.func
78 | }
79 |
80 | Tabs.defaultProps = {
81 | activeIndex: 0
82 | }
83 |
84 | export default Tabs
--------------------------------------------------------------------------------
/src/reducers/data-list-reducer.js:
--------------------------------------------------------------------------------
1 | import _ut from 'my-util'
2 |
3 | /*-----------------------------------------------------------------*/
4 | /*List Reducer*/
5 | /*-----------------------------------------------------------------*/
6 |
7 | const initListInfo = {
8 | isFetching: false,
9 | list: []
10 | }
11 |
12 |
13 | const DataListReducer = (state = initListInfo, action) => {
14 | switch (action.type) {
15 | case 'LIST_ADD':
16 | {
17 | switch (action.status) {
18 | case 'error':
19 | {
20 | return Object.assign({}, state, {
21 | isFetching: false
22 | })
23 | }
24 | case 'success':
25 | {
26 | const { list } = action;
27 | const oldList = state['list'];
28 | return Object.assign({}, {
29 | list: oldList.concat(list),
30 | isFetching: false}
31 | )
32 | }
33 | default:
34 | {
35 | return Object.assign({}, state, {
36 | isFetching: true
37 | })
38 | }
39 | }
40 | }
41 | case 'LIST_CLEAR':
42 | {
43 | return Object.assign({}, state, initListInfo)
44 | }
45 | default:
46 | return state
47 | }
48 | }
49 |
50 |
51 |
52 | /*-----------------------------------------------------------------*/
53 | /*List Action*/
54 | /*-----------------------------------------------------------------*/
55 | const addList = (list) => ({
56 | type: 'LIST_ADD',
57 | status: 'success',
58 | list
59 | })
60 |
61 | const fetchStart = () => ({
62 | type: 'LIST_ADD',
63 | })
64 |
65 | const fetchError = () => ({
66 | type: 'LIST_ADD',
67 | status: 'error',
68 | })
69 |
70 | const clearList = () => ({
71 | type: 'LIST_CLEAR'
72 | })
73 |
74 |
75 |
76 |
77 | /*-----------------------------------------------------------------*/
78 | /*Async List Action Depend on Redux-Thunk*/
79 | /*-----------------------------------------------------------------*/
80 | const fetchAddList = () => async dispatch => {
81 | dispatch(fetchStart())
82 | try {
83 | const list = await _ut.fetch(`http://localhost:3003/movie`)
84 | dispatch(addList(list.data))
85 | } catch(e) {
86 | dispatch(fetchError())
87 | console.log(e)
88 | return Promise.reject(e)
89 | }
90 | }
91 |
92 |
93 |
94 | export {
95 | DataListReducer,
96 | addList, clearList,
97 | fetchAddList
98 | }
--------------------------------------------------------------------------------
/src/module/mo-segmented/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import './segmented.less';
4 |
5 | var classNames = require('classnames');
6 |
7 |
8 | class Segmented extends Component {
9 | constructor(props) {
10 | super(props)
11 | this.state = {
12 | activeIndex: this.props.activeIndex
13 | };
14 | this.handleChange = this.handleChange.bind(this);
15 |
16 | const { tintColor, tintBg } = props
17 |
18 | this.defaultStyle = {
19 | color: tintColor,
20 | background: tintBg
21 | }
22 | this.activeStyle = {
23 | color: tintBg,
24 | background:tintColor
25 | }
26 | }
27 |
28 | shouldComponentUpdate(nextProps, nextState) {
29 | if (this.state.activeIndex !== nextState.activeIndex) {
30 | return true
31 | }
32 | if(this.props.children !== nextProps.children) {
33 | return true
34 | }
35 | return true
36 | }
37 |
38 | handleChange(nextIndex) {
39 | const { activeIndex } = this.state
40 | if(nextIndex !== activeIndex) {
41 | this.setState({
42 | activeIndex: nextIndex
43 | })
44 | this.props.onChange && this.props.onChange(activeIndex, nextIndex)
45 | }
46 | }
47 |
48 | render() {
49 | const { activeIndex } = this.state;
50 | const { values, tintBg, lineColor=tintBg, style, className } = this.props;
51 | const len = values.length;
52 |
53 | return (
54 | this.tabContent = n}
56 | className={classNames("tj-segmented__content", className)}
57 | style={{...style, borderColor: tintBg}}
58 | >
59 | {
60 | values.map((title, i) =>
61 |
75 | { title }
76 |
77 | )
78 | }
79 |
80 | )
81 | }
82 | }
83 |
84 | Segmented.propTypes = {
85 | activeIndex: PropTypes.number,
86 | values: PropTypes.array,
87 | tintColor: PropTypes.string,
88 | tintBg: PropTypes.string,
89 | onChange: PropTypes.func
90 | }
91 |
92 | Segmented.defaultProps = {
93 | activeIndex: 0,
94 | tintColor: '#0C037B',
95 | tintBg: '#fff'
96 | }
97 |
98 | export default Segmented
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | const { resolve } = require('path');
3 | var webpack = require('webpack');
4 | var autoprefixer = require('autoprefixer');
5 | var HtmlWebpackPlugin = require('html-webpack-plugin');
6 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
7 |
8 | module.exports = {
9 | devtool: false,
10 | entry: ['babel-polyfill', './src/app.js'],
11 | output: {
12 | path: path.join(__dirname, '/dist/'),
13 | filename: '[name]-[hash:5].min.js',
14 | chunkFilename: '[name]-[hash:5].chunk.js',
15 | publicPath: './'
16 | },
17 | plugins: [
18 | new webpack.LoaderOptionsPlugin({
19 | minimize: true,
20 | debug: false
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | beautify: false,
24 | mangle: {
25 | screw_ie8: true,
26 | keep_fnames: true
27 | },
28 | compress: {
29 | screw_ie8: true
30 | },
31 | comments: false
32 | }),
33 | new HtmlWebpackPlugin({
34 | template: './index.html',
35 | minify: {
36 | removeComments: true,
37 | collapseWhitespace: true,
38 | removeRedundantAttributes: true,
39 | useShortDoctype: true,
40 | removeEmptyAttributes: true,
41 | removeStyleLinkTypeAttributes: true,
42 | keepClosingSlash: true,
43 | minifyJS: true,
44 | minifyCSS: true,
45 | minifyURLs: true
46 | },
47 | inject: 'body',
48 | filename: 'index.html'
49 | }),
50 | new ExtractTextPlugin({
51 | filename: 'bundle.css',
52 | disable: false,
53 | allChunks: true
54 | }),
55 | new webpack.ProvidePlugin({
56 | 'Promise':'es6-promise',
57 | 'fetch': 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch'
58 | }),
59 | new webpack.DefinePlugin({
60 | 'process.env': {
61 | 'NODE_ENV': JSON.stringify('production')
62 | }
63 | })
64 | ],
65 | module: {
66 | rules: [{
67 | test: /\.js$/,
68 | include: resolve(__dirname, 'src'),
69 | use: ['babel-loader']
70 | }, {
71 | test: /\.less$/,
72 | exclude: [/node_modules/],
73 | use: ExtractTextPlugin.extract({
74 | fallback: 'style-loader',
75 | use: 'css-loader?modules,localIdentName="[name]-[local]-[hash:base64:5]"!postcss-loader!less-loader'
76 | }),
77 | }, {
78 | test:/\.(png|jpg|gif|svg)$/,
79 | exclude: [/node_modules/],
80 | use: 'url-loader?limit=8192&name=build/[name].[ext]'
81 | }]
82 | },
83 | resolve: {
84 | extensions: ['.js', '.jsx', '.json'],
85 | modules: ['node_modules', './src/module', './src/action', './src/util/'],
86 | alias: {
87 | 'my-util': resolve(__dirname, './src/libs/my-util'),
88 | 'action': resolve(__dirname, './src/action/index.js'),
89 | 'stroe': resolve(__dirname, './src/store/index.js'),
90 | 'layout': resolve(__dirname, './src/layout'),
91 | 'reducers': resolve(__dirname, './src/reducers')
92 | }
93 | }
94 | };
--------------------------------------------------------------------------------
/src/module/co-simple-list/mo-toast/toast.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { TransitionGroup } from 'react-transition-group'
4 | import { FadeTransition} from '../mo-transtion'
5 |
6 | import './toast.less'
7 |
8 | var classNames = require('classnames');
9 |
10 |
11 | const icons = {
12 | error: require('./assets/error.svg'),
13 | info: require('./assets/info.svg'),
14 | success: require('./assets/success.svg'),
15 | warning: require('./assets/warning.svg')
16 | }
17 |
18 | class Toast extends Component {
19 | constructor(props) {
20 | super(props);
21 |
22 | this.state = {
23 | isVisible: this.props.visible
24 | };
25 | }
26 |
27 | componentDidMount() {
28 | this.setState({
29 | isVisible: true
30 | })
31 | this.startTimer();
32 | }
33 |
34 | componentWillUnmount() {
35 | this.stopTimer();
36 | }
37 |
38 | onClose() {
39 | this.stopTimer();
40 |
41 | this.setState({
42 | isVisible: false
43 | });
44 |
45 | const { onClose } = this.props;
46 | onClose && onClose();
47 | }
48 |
49 | startTimer() {
50 | if (this.props.duration > 0) {
51 | this.timeout = setTimeout(() => {
52 | !this.props.showClose && this.onClose()
53 | }, this.props.duration)
54 | }
55 | }
56 |
57 | stopTimer() {
58 | clearTimeout(this.timeout);
59 | }
60 |
61 | renderContent() {
62 | const { iconClass, className, type } = this.props;
63 | const { isVisible } = this.state
64 | const boxClassNames = classNames(
65 | 'tj-msg',
66 | {'tj-msg--hasicon': type !== 'text'},
67 | className
68 | )
69 | return (
70 |
76 | { iconClass &&
}
77 | { !iconClass && type !== 'text' &&

}
78 | { this.props.content }
79 | { this.props.showClose &&
}
80 |
81 | )
82 | }
83 |
84 | render() {
85 | const { isVisible } = this.state
86 |
87 | return (
88 |
89 |
90 | { this.renderContent() }
91 |
92 |
93 | );
94 | }
95 | }
96 |
97 | Toast.propTypes = {
98 | type: PropTypes.oneOf(['text', 'success', 'warning', 'info', 'error', 'loading']),
99 | content: PropTypes.string.isRequired,
100 | duration: PropTypes.number,
101 | showClose: PropTypes.bool,
102 | className: PropTypes.string,
103 | iconClass: PropTypes.string
104 | };
105 |
106 | Toast.defaultProps = {
107 | type: 'text',
108 | duration: 3000,
109 | showClose: false
110 | };
111 |
112 | export default Toast;
--------------------------------------------------------------------------------
/src/module/mo-toast/toast.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { TransitionGroup } from 'react-transition-group'
4 | import { FadeTransition} from '../mo-transtion'
5 |
6 | import './toast.less'
7 |
8 | var classNames = require('classnames');
9 |
10 |
11 | const icons = {
12 | error: require('./assets/error.svg'),
13 | info: require('./assets/info.svg'),
14 | success: require('./assets/success.svg'),
15 | warning: require('./assets/warning.svg')
16 | }
17 |
18 | class Toast extends Component {
19 | constructor(props) {
20 | super(props);
21 |
22 | this.state = {
23 | isVisible: this.props.visible
24 | };
25 | }
26 |
27 | componentDidMount() {
28 | this.setState({
29 | isVisible: true
30 | })
31 | this.startTimer();
32 | }
33 |
34 | componentWillUnmount() {
35 | this.stopTimer();
36 | }
37 |
38 | onClose() {
39 | this.stopTimer();
40 | this.setState({
41 | isVisible: false
42 | });
43 |
44 | const { onClose } = this.props;
45 | if(onClose instanceof Function) {
46 | onClose();
47 | }
48 | }
49 |
50 | startTimer() {
51 | if (this.props.duration > 0) {
52 | this.timeout = setTimeout(() => {
53 | !this.props.showClose && this.onClose()
54 | }, this.props.duration)
55 | }
56 | }
57 |
58 | stopTimer() {
59 | clearTimeout(this.timeout);
60 | }
61 |
62 | renderContent() {
63 | const { iconClass, className, type } = this.props;
64 | const { isVisible } = this.state
65 | const boxClassNames = classNames(
66 | 'tj-toast',
67 | {'tj-toast--hasicon': type !== 'text'},
68 | className
69 | )
70 | return (
71 |
77 | { iconClass &&
}
78 | { !iconClass && type !== 'text' &&

}
79 | { this.props.content }
80 | { this.props.showClose &&
}
81 |
82 | )
83 | }
84 |
85 | render() {
86 | const { isVisible } = this.state
87 |
88 | return (
89 |
90 |
91 | { this.renderContent() }
92 |
93 |
94 | );
95 | }
96 | }
97 |
98 | Toast.propTypes = {
99 | type: PropTypes.oneOf(['text', 'success', 'warning', 'info', 'error', 'loading']),
100 | content: PropTypes.string.isRequired,
101 | duration: PropTypes.number,
102 | showClose: PropTypes.bool,
103 | className: PropTypes.string,
104 | iconClass: PropTypes.string
105 | };
106 |
107 | Toast.defaultProps = {
108 | type: 'text',
109 | duration: 3000,
110 | showClose: false
111 | };
112 |
113 | export default Toast;
--------------------------------------------------------------------------------
/src/module/mo-time-count/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | class TimeCount extends Component {
5 | constructor(props) {
6 | super(props);
7 | this.defaultState = {
8 | day: 0,
9 | hour: 0,
10 | minute: 0,
11 | second: 0,
12 | isfailure: true
13 | }
14 | this.state = this.defaultState
15 | }
16 | componentWillUnmount() {
17 | this.reset()
18 | }
19 |
20 | componentWillReceiveProps(nextProps) {
21 | if(nextProps.gapTime !== this.props.gapTime || nextProps.endDate !== this.props.endDate) {
22 | this.reset()
23 | this.runCount(nextProps);
24 | }
25 | }
26 |
27 | componentDidMount() {
28 | this.runCount(this.props);
29 | }
30 |
31 | reset() {
32 | clearInterval(this.timer)
33 | this.setState({
34 | ...this.defaultState
35 | })
36 | }
37 |
38 | runCount(props) {
39 | let { onOver } = props;
40 | let gapTime = this.getGapTime(props);
41 |
42 | if(!gapTime) {
43 | return
44 | }
45 |
46 | this.timer = setInterval(() => {
47 | gapTime = gapTime - 1000;
48 | if(gapTime <= 0) {
49 | clearInterval(this.timer)
50 | this.setState({
51 | isfailure: true
52 | })
53 | onOver && onOver()
54 | }
55 | this.renderCount(gapTime)
56 | }, 1000)
57 | }
58 |
59 | getGapTime(props) {
60 | let { endDate, startDate, gapTime } = props;
61 | if(gapTime && gapTime > 0) {
62 | return gapTime;
63 | } else {
64 | if (!endDate || !startDate) {
65 | this.setState({
66 | isfailure: true
67 | })
68 | console.warn('missing endDate or startDate props')
69 | return false
70 | }
71 |
72 | if(endDate instanceof Date) {
73 | endDate = Date.parse(endDate);
74 | }
75 | if(startDate instanceof Date) {
76 | startDate = Date.parse(startDate);
77 | }
78 |
79 | return (endDate - startDate)
80 | }
81 | }
82 |
83 | renderCount(temp) {
84 | // 天
85 | var int_day = Math.floor(temp / 86400000)
86 | temp -= int_day * 86400000;
87 | // 时
88 | var int_hour = Math.floor(temp / 3600000)
89 | temp -= int_hour * 3600000;
90 | // 分
91 | var int_minute = Math.floor(temp / 60000)
92 | temp -= int_minute * 60000;
93 | // 秒
94 | var int_second = Math.floor(temp / 1000)
95 | // 时分秒为单数时、前面加零
96 | if (int_day < 10) {
97 | int_day = "0" + int_day;
98 | }
99 | if (int_hour < 10) {
100 | int_hour = "0" + int_hour;
101 | }
102 | if (int_minute < 10) {
103 | int_minute = "0" + int_minute;
104 | }
105 | if (int_second < 10) {
106 | int_second = "0" + int_second;
107 | }
108 |
109 | this.setState({
110 | day: int_day,
111 | hour: int_hour,
112 | minute: int_minute,
113 | second: int_second,
114 | isfailure: false
115 | })
116 | }
117 |
118 | renderDefault() {
119 | const { day, hour, minute, second} = this.state;
120 | return `${day} 天 ${hour} 时 ${minute} 分 ${second}秒`
121 | }
122 |
123 | renderWithoutDay() {
124 | let { day, hour, minute, second} = this.state;
125 | hour = +(day * 24 + hour);
126 | hour = hour < 10 ? '0' + hour : hour;
127 |
128 | return `${hour} 时 ${minute} 分 ${second} 秒`
129 | }
130 |
131 | renderWithoutHour() {
132 | let { day, hour, minute, second} = this.state;
133 | minute = +(day * 24 * 60 + hour * 60 + minute);
134 | minute = minute < 10 ? '0' + minute : minute;
135 |
136 | return `${minute} 分 ${second} 秒`
137 | }
138 |
139 | render() {
140 | const { className, showDay, showHour } = this.props;
141 | return (
142 |
143 | {
144 | this.state.isfailure
145 | ? ' - - '
146 | : showHour
147 | ? showDay
148 | ? this.renderDefault()
149 | : this.renderWithoutDay()
150 | : this.renderWithoutHour()
151 | }
152 |
153 | );
154 | }
155 | }
156 |
157 | TimeCount.propTypes = {
158 | className: PropTypes.string,
159 | endDate: PropTypes.any,
160 | startDate: PropTypes.any,
161 | gapTime: PropTypes.number,
162 | showDay: PropTypes.bool,
163 | showHour: PropTypes.bool,
164 | onOver: PropTypes.func
165 | }
166 |
167 | TimeCount.defaultProps = {
168 | endDate: null,
169 | startDate: null,
170 | showHour: true,
171 | showDay: true,
172 | onOver: () => console.warn('coundown timer was end')
173 | }
174 |
175 | export default TimeCount
--------------------------------------------------------------------------------
/src/couter.js:
--------------------------------------------------------------------------------
1 | !function () {
2 | var base = {
3 | na: function () {
4 | var ua = window.navigator.userAgent.toLowerCase();
5 | var isIE = (ua.indexOf("msie") > -1) ? true : false;
6 | var isFirefox = (ua.indexOf("firefox") > -1) ? true : false;
7 | var isChrome = (ua.indexOf("chrome") > -1) ? true : false;
8 | var isSafari = (ua.indexOf("safari") > -1) ? true : false;
9 | var isOpera = (ua.indexOf("opera") > -1) ? true : false;
10 | var isAndroid = (ua.indexOf("android") > -1) ? true : false;
11 | if (isIE) {
12 | return "Microsoft Internet Explorer " + ua.match(/msie.([\d.]+)/)[1]
13 | } else if (isFirefox) {
14 | return "Firefox " + ua.match(/firefox.([\d.]+)/)[1]
15 | } else if (isChrome) {
16 | return "Chrome " + ua.match(/chrome.([\d.]+)/)[1]
17 | } else if (isOpera) {
18 | return "Opera " + ua.match(/opera.([\d.]+)/)[1]
19 | } else if (isSafari && isAndroid) {
20 | return "Android's browser " + ua.match(/version.([\d.]+)/)[1]
21 | } else if (isSafari) {
22 | return "Safari " + ua.match(/version.([\d.]+)/)[1]
23 | } else {
24 | return "unknown"
25 | }
26 | },
27 | os: function () {
28 | var ua = window.navigator.userAgent.toLowerCase();
29 | var isWNT = (ua.indexOf("windows nt") > -1) ? true : false;
30 | var isWindows = (ua.indexOf("windows") > -1) ? true : false;
31 | var isWP = (ua.indexOf("windows phone") > -1) ? true : false;
32 | var isMac = (ua.indexOf("mac") > -1) ? true : false;
33 | var isUnix = (ua.indexOf("x11") > -1) ? true : false;
34 | var isIphone = (ua.indexOf("iphone") > -1) ? true : false;
35 | var isIpad = (ua.indexOf("ipad") > -1) ? true : false;
36 | var isIpod = (ua.indexOf("ipod") > -1) ? true : false;
37 | var isAndroid = (ua.indexOf("android") > -1) ? true : false;
38 | if (isWindows) {
39 | var tmp = "";
40 | if (isWNT) {
41 | switch (ua.match(/windows nt ([\d\.]+)/)[1]) {
42 | case "6.2":
43 | tmp = "Win8";
44 | break;
45 | case "6.1":
46 | tmp = "Win7";
47 | break;
48 | case "6.0":
49 | tmp = "WinVista";
50 | break;
51 | case "5.2":
52 | tmp = "Windows Server 2003";
53 | break;
54 | case "5.1":
55 | tmp = "WinXp";
56 | break;
57 | case "5.0":
58 | tmp = "Win2000";
59 | break;
60 | default:
61 | tmp = "other Windows";
62 | break
63 | }
64 | return tmp
65 | } else {
66 | if (isWP) {
67 | return "mobile windows"
68 | } else {
69 | return "Lower windows"
70 | }
71 | }
72 | } else if (isIphone || isIpad || isIpod) {
73 | return "Iphone or Ipad or Ipod"
74 | } else if (isMac) {
75 | return "Mac OS X"
76 | } else if (isAndroid) {
77 | return "Android " + ua.match(/android.([\d\.]+)/)[1]
78 | } else if (isUnix) {
79 | return "like Unix"
80 | } else {
81 | return "unknown"
82 | }
83 | },
84 | size: function () {
85 | return window.screen.width + "*" + window.screen.height
86 | },
87 | src: document.referrer,
88 | url: document.location.href.replace("#","%23")
89 | };
90 |
91 | function jsLoad(sUrl, sBianMa, fCallback) {
92 | var _script = document.createElement('script');
93 | _script.setAttribute('charset', sBianMa);
94 | _script.setAttribute('type', 'text/javascript');
95 | _script.setAttribute('src', sUrl);
96 | document.getElementsByTagName('head')[0].appendChild(_script);
97 | if (/msie/.test(window.navigator.userAgent.toLowerCase())) {
98 | _script.onreadystatechange = function () {
99 | if (this.readyState == 'loaded' || this.readyState == 'complete') {
100 | _script.parentNode.removeChild(_script);
101 | if (fCallback) fCallback()
102 | }
103 | }
104 | } else if (/gecko/.test(window.navigator.userAgent.toLowerCase()) || /opera/.test(window.navigator.userAgent.toLowerCase())) {
105 | _script.onload = function () {
106 | _script.parentNode.removeChild(_script);
107 | if (fCallback) fCallback()
108 | }
109 | } else {
110 | _script.parentNode.removeChild(_script);
111 | if (fCallback) fCallback()
112 | }
113 | }
114 | window.jsonp = function () { };
115 | var tmp = "na=" + base.na() + "&os=" + base.os() + "&size=" + base.size() + "&src=" + base.src + "&url=" + base.url + "&type=Mongo&v=" + Math.random();
116 | jsLoad("https://counter1.1234567.com.cn/?" + tmp + "&callback=jsonp", "utf-8", function () { })
117 | }.call(this)
--------------------------------------------------------------------------------
/src/module/mo-carousel/carousel.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { GetSlideDirection, setCss3Style } from './method'
4 | // import styles from './carousel.less'
5 |
6 | var classNames = require('classnames');
7 |
8 |
9 | class Carousel extends Component {
10 | constructor(props) {
11 | super(props)
12 |
13 | // 正显示的元素位置
14 | this.activeIndex = this.props.activeIndex
15 | // 单个元素宽度
16 | this.scaleW = 0
17 | // 总偏移量
18 | this.totalX= 0
19 |
20 | // 手指按下时的偏移量
21 | this.startX= 0
22 | this.startY= 0
23 | // 手指移动的偏移量
24 | this.offsetX = 0
25 | this.offsetY = 0
26 |
27 | this.startTouch = this.startTouch.bind(this)
28 | this.moveTouch = this.moveTouch.bind(this)
29 | this.endTouch = this.endTouch.bind(this)
30 |
31 | }
32 |
33 | componentDidMount() {
34 | const { activeIndex } = this
35 | this.reset()
36 | this.scrollTo(activeIndex)
37 | }
38 |
39 | componentWillReceiveProps(nextProps) {
40 | const { activeIndex } = this
41 | const { activeIndex: newActiveIndex } = nextProps
42 | if(newActiveIndex !== activeIndex) {
43 | this.scrollTo(newActiveIndex)
44 | }
45 | }
46 |
47 | shouldComponentUpdate(nextProps) {
48 | const { activeIndex } = this
49 | const { activeIndex: newActiveIndex } = nextProps
50 | if(activeIndex !== newActiveIndex) {return true}
51 | if(this.props.children != nextProps.children ) {return true}
52 |
53 | return true
54 | }
55 |
56 | reset() {
57 | requestAnimationFrame(() => {
58 | const {carouselWarp: { clientWidth} , slideList} = this
59 | const { children } = slideList
60 | this.scaleW = clientWidth
61 | slideList.style.width = `${clientWidth * slideList.children.length}px`
62 | slideList.style.display = 'none'
63 | Array.prototype.forEach.call(children, (item)=>{
64 | item.style.width = clientWidth + 'px'
65 | })
66 | slideList.style.display = 'flex'
67 | })
68 | }
69 |
70 | scrollTo(index) {
71 | const { scaleW } = this;
72 | this.totalX = -index * scaleW
73 | this.toggleTransition()
74 | this.moveCarousel(-index * scaleW)
75 | this.activeIndex = index
76 | }
77 |
78 | moveCarousel(distance) {
79 | requestAnimationFrame(()=>{
80 | setCss3Style(this.slideList, 'transform', 'translateX(' + distance + 'px)')
81 | })
82 | }
83 |
84 | toggleTransition() {
85 | setCss3Style(this.slideList, 'transition', 'transform .2s cubic-bezier(.645,.045,.355,1)')
86 | setTimeout(()=> {
87 | setCss3Style(this.slideList, 'transition', 'none')
88 | }, 200)
89 | }
90 |
91 | startTouch(event) {
92 | this.startX = event.targetTouches[0].pageX
93 | this.startY = event.targetTouches[0].pageY
94 | this.offsetX = 0
95 | }
96 |
97 | moveTouch(event) {
98 | const { onMove } = this.props
99 | const { pageX, pageY } = event.targetTouches[0]
100 |
101 | if(GetSlideDirection(this.startX, this.startY, pageX, pageY) !== 0){
102 | event.preventDefault();
103 | }
104 |
105 |
106 | const { scaleW, startX, totalX } = this
107 | const offsetX = pageX - startX
108 |
109 | // 最多移动 80% 距离
110 | if (Math.abs(offsetX) > scaleW * 0.8) {return}
111 |
112 | this.offsetX = offsetX
113 | const scrollWidth = offsetX + totalX
114 | this.moveCarousel(scrollWidth)
115 |
116 | onMove && onMove(scrollWidth)
117 | }
118 |
119 | endTouch() {
120 | const { activeIndex, scaleW, offsetX } = this
121 | const { children } = this.props
122 |
123 | // 记录滑动边界,用于判定滑动的类型,超过总长 20% 即判定为移动
124 | const boundary = scaleW / 5;
125 |
126 | if (offsetX > boundary) {
127 | // 右滑
128 | if(activeIndex === 0) {
129 | this.setIndex(0)
130 | return
131 | }
132 | this.setIndex(-1);
133 | } else if (offsetX < 0 && offsetX < - boundary) {
134 | // 左滑
135 | if(activeIndex + 1 >= children.length) {
136 | this.setIndex(0)
137 | return
138 | }
139 | this.setIndex(+1);
140 | } else {
141 | this.setIndex(0);
142 | }
143 | }
144 |
145 | setIndex(n) {
146 | const { scaleW } = this
147 | const { onChange } = this.props
148 | const oldActiveIndex = this.activeIndex
149 | const newActiveIndex = oldActiveIndex + n
150 |
151 | this.activeIndex = newActiveIndex
152 | this.totalX = -newActiveIndex * scaleW
153 |
154 | this.toggleTransition()
155 | this.moveCarousel(this.totalX)
156 |
157 | onChange && onChange(oldActiveIndex, newActiveIndex)
158 | }
159 |
160 | render() {
161 | const { className } = this.props;
162 | return (
163 | this.carouselWarp = n}
165 | className={classNames('tj-carousel__wrap', className)}
166 | style={{...this.props.style}}
167 | >
168 |
this.slideList = n}
170 | className="tj-carousel__slide-list"
171 | onTouchStart={this.startTouch}
172 | onTouchMove={this.moveTouch}
173 | onTouchEnd={this.endTouch}
174 | >
175 | {
176 | React.Children.map(this.props.children, (slide, i) =>
177 | -
178 | {React.cloneElement(slide)}
179 |
180 | )
181 | }
182 |
183 |
184 |
185 | )
186 | }
187 | }
188 |
189 | Carousel.propTypes = {
190 | activeIndex: PropTypes.number,
191 | onMove: PropTypes.func,
192 | onChange: PropTypes.func,
193 | className: PropTypes.string,
194 | }
195 |
196 | Carousel.defaultProps = {
197 | activeIndex: 0,
198 | }
199 |
200 | export default Carousel
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 本项目主要作为 react 项目开发的启动模板。使用 webpack v3、react v16、react-router v4,相较旧模板有较大的变化。
2 |
3 | > webpack 的具体使用及优化可参考我的博文 [webpack 使用总结](http://www.ferecord.com/webpack-summary.html) 。
4 |
5 |
8 |
9 |
10 | ## 主要依赖及工具
11 |
12 | 
13 | 
14 | 
15 |
16 | 
17 | 
18 | 
19 | 
20 |
21 |
22 | ## 功能
23 | #### 主要功能
24 | - [x] Hot Module Replacement 热加载
25 | - [x] ESLint 检测
26 | - [x] less、autoprefixer
27 | - [x] 业务组件使用 cssModule,通用组件使用 BEM 命名法
28 | - [x] 小于 8k 图片转为 base64
29 | - [x] svg 图标
30 | - [x] 文件压缩、添加 MD5
31 | - [x] ES6+, Fetch
32 | - [x] 使用 Redux DevTools ([安装浏览器插件](https://github.com/zalmoxisus/redux-devtools-extension))
33 |
34 | #### 示例动图:
35 |
36 |
37 |
38 |
39 |
40 |
41 | ### 组件
42 | 本项目用到的纯组件主要如下:
43 | - 按钮 Button
44 | - 走马灯 Carousel
45 | - 弹框 Dialog
46 | - 图标 Icon
47 | - 无限滚动 Infinite-Scroll
48 | - 列表载入 List-Loading
49 | - 结果页 Result-Card
50 | - 分段器 Segmented
51 | - 菊花图 Spin
52 | - 步骤条 Steps
53 | - 标签页 Tabs
54 | - 倒计时 Time-Count
55 | - 轻提示 Toast
56 |
57 | 这些组件主要展示思路与方法,功能较基础,仅供参考。实际开发生产时请根据业务需求二次开发。
58 |
59 |
74 |
75 |
76 |
77 |
78 | ## 开始使用
79 | 本项目使用`yarn`作为包管理,也可替换为`npm`。两者的差异请参阅[从 npm 客户端迁移](https://yarnpkg.com/zh-Hans/docs/migrating-from-npm)。无论使用哪个都建议将安装源替换为[淘宝镜像](https://npm.taobao.org/)。
80 |
81 | ### 安装
82 | ```
83 | git clone https://github.com/tumars/boilerplate-webpack-react-es6-cssModule
84 | cd boilerplate-webpack-react-es6-cssModule
85 | yarn install
86 | ```
87 |
88 | ### 开发
89 | ```
90 | yarn start
91 | ```
92 |
93 | 访问 `http://localhost:3000/` 查看页面。
94 |
95 | 
96 |
97 | ### mock 接口数据
98 |
99 | 打开新命令行窗口,执行:
100 | ```
101 | yarn run mock
102 | ```
103 |
104 | 接口将会在本地 3003 端口启动。本项目的接口数据通过 node http 服务建立,配置文件在 /mock 文件夹内。
105 |
106 | 
107 |
108 | ### 打包
109 | Windows 用户使用:
110 | ```
111 | yarn run build-win
112 | ```
113 |
114 | Mac 用户使用:
115 | ```
116 | yarn run build-mac
117 | ```
118 |
119 | 文件将会在`./dist`文件夹内生成。
120 |
121 |
122 | 可以使用`anywhere`工具建立本地服务查看页面:
123 | ```
124 | yarn global add anywhere 或 npm i -g anywhere
125 | cd ./dist
126 | anywhere
127 | ```
128 | 页面会自动打开。
129 |
130 |
131 | ## 目录文件结构
132 | 
133 |
134 |
135 | ## 其他技术选择
136 | ### REM 与 VW、VH
137 | 之前使用 rem 布局,后来看了[再聊移动端页面的适配](https://www.w3cplus.com/css/vw-for-layout.html),决定使用 vw、vh 布局,配合 [postcss-px-to-viewport](https://github.com/evrone/postcss-px-to-viewport)和 [viewport-units-buggyfill](https://github.com/rodneyrehm/viewport-units-buggyfill) 能通过大部分机型的测试。
138 |
139 | > 经测试发现 viewport-units-buggyfill 在处理 base64 背景图片的 vw vh 时会导致图片出错,请注意。
140 |
141 | 当然使用 rem 布局还是最安全的,提供三个方案:
142 |
143 | 1. 参考本项目 v2 版本使用 js 控制 html 的 font-size 。
144 | 2. 使用 [postcss-pxtorem](https://github.com/cuth/postcss-pxtorem)。
145 | 3. 使用淘宝的 [lib-flexible](https://github.com/amfe/lib-flexible),这个是最推荐的。
146 |
147 |
148 | ### async 函数
149 | 本项目中的获取接口数据处的异步处理使用的是 async 函数,相比 Promise 直观、方便了许多。
150 |
151 | ### Decorator
152 | 本项目的一些组件使用了 Decorator(修饰器)的写法,例如 module/mo-carousel。以及 css module 也是以 Decorator 的方式使用。
153 |
154 |
155 | ## 性能优化
156 | ### Reselect
157 | Reselect 库可以创建可记忆的(Memoized)、可组合的 selector 函数。Reselect selectors 可以用来高效地计算 Redux store 里的衍生数据。
158 |
159 | 使用 Reselect 相当一个缓存,使容器组件传递的 props 输入值不变时输出值不变,以减少显示组件的重复渲染。
160 |
161 | 本项目的 layout/data-list-tabs 文件内使用了 Reselect。
162 |
163 | 更多 Reselect 的访问跟介绍请访问:[https://github.com/reactjs/reselect](https://github.com/reactjs/reselect)
164 |
165 | ### Immutable
166 | Immutable 是指数据不可变。使用 Immutable 在每次操作修改对象时都会生成一个新对象,而不修改原对象。这对 react 有这么两点意义:
167 |
168 | - 1. 保证了 state 的不可被直接更改。
169 | - 2. 便于 shouldComponentUpdate 对比前后对象是否相同。
170 |
171 | 从而一定的程度上的保证安全性和提提高性能。
172 |
173 | 这是 Immutable.js 的文档页面:[https://facebook.github.io/immutable-js/](https://facebook.github.io/immutable-js/)
174 |
175 | ### 其他优化
176 |
177 | #### 列表不要使用 index 作为 key
178 | React Diff 算法中 React 会借助元素的 key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。
179 | 如果使用 index 来作为 key,当数据更新仅仅是数组重新排序或在其中间位置插入新元素,那么视图元素都将重新渲染。
180 |
181 | #### 不要在 componentWillMount 中调用 setState
182 |
183 | 在 componentWillMount 没有意义,所有的 state 初始化应当在 this.state 中定义,而第一次 render 后改变 state 应当在 componentDidMount 中执行。
184 |
185 | 而且该时间周期钩子在 react v17 版本中将会被移除,查看:https://reactjs.org/docs/react-component.html#mounting
186 |
187 | #### 注意动画渲染优化,使用 chrome 调试性能
188 | 页面的图像渲染经过如下五个步骤
189 |
190 | - script (js 计算)
191 | - style (样式计算)
192 | - layout (布局)
193 | - paint (绘制)
194 | - composite (合成)
195 |
196 | 具体的讲解与优化请查看 chrome 开发者文档的说明: https://developers.google.com/web/fundamentals/performance/rendering/?hl=zh-cn
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | ## 主要版本变化
206 |
207 | #### v3
208 | - 主要使用 webpack v3 + react v16 + react-router v4
209 | - 更改文件结构,组件更新
210 | - Promise 改为 async await
211 | - rem 布局改为 vw、vh,使用 postcss-px-to-viewport 配置
212 | - 动画组件依赖更新至 react-transition-group
213 |
214 |
215 | #### [v2](https://github.com/tumars/boilerplate-webpack-react-es6-cssModule/tree/master/webpack1.x)
216 | 主要使用 webpack v2 + react v15 + react-router v4
217 |
218 | #### [v1](https://github.com/tumars/boilerplate-webpack-react-es6-cssModule/tree/master/webpack1.x)
219 | 主要使用 webpack v1 + react v13 + react-router v2
220 |
221 |
222 | ## 联系我
223 | 如有问题请提 issue,或通过以下方式联系到我:
224 | - 邮箱 menghui9898@gmail.com
225 | - 博客 [ferecord.com](http://www.ferecord.com/ "前端记录 ")
226 | - Twitter [@Tumars](https://twitter.com/Tumars)
227 |
228 | 欢迎指教交流 🙆
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/dist/bundle.css:
--------------------------------------------------------------------------------
1 | .-transtion-slide-appear-3mbD1-,.-transtion-slide-enter-3Wn_c-{opacity:0;-webkit-transform:translate(100%);transform:translate(100%)}.-transtion-slide-appear-active-3aPhc-,.-transtion-slide-enter-active-1M9MQ-{opacity:1;-webkit-transform:translate(0);transform:translate(0);-webkit-transition:opacity .5s ease-in,-webkit-transform .5s ease;transition:opacity .5s ease-in,-webkit-transform .5s ease;transition:transform .5s ease,opacity .5s ease-in;transition:transform .5s ease,opacity .5s ease-in,-webkit-transform .5s ease}.-transtion-slide-exit-1x8zm-{opacity:1;-webkit-transform:translate(0);transform:translate(0)}.-transtion-slide-exit-active-2B8R9-{opacity:.5;-webkit-transform:translate(-100%);transform:translate(-100%);-webkit-transition:opacity .5s ease-out,-webkit-transform .5s ease;transition:opacity .5s ease-out,-webkit-transform .5s ease;transition:transform .5s ease,opacity .5s ease-out;transition:transform .5s ease,opacity .5s ease-out,-webkit-transform .5s ease}.-transtion-fade-appear-2uiCU-,.-transtion-fade-enter-3zFq9-{opacity:0}.-transtion-fade-appear-active-2HWoK-,.-transtion-fade-enter-active-102Wm-{-webkit-transition:opacity .3s linear;transition:opacity .3s linear;opacity:1}.-transtion-fade-exit-3qVa8-{-webkit-transition:opacity .2s linear;transition:opacity .2s linear;opacity:1}.-transtion-fade-exit-active-10lmh-{opacity:0}.-transtion-spread-enter-11ARV-{opacity:.01;-webkit-transform:scale(.8);transform:scale(.8)}.-transtion-spread-enter-active-2lGcz-{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition:opacity .1s linear,-webkit-transform .1s linear;transition:opacity .1s linear,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear;transition:transform .1s linear,opacity .1s linear,-webkit-transform .1s linear}.-transtion-spread-exit-1g8v8-{opacity:1;-webkit-transform:translate(0);transform:translate(0)}.-transtion-spread-exit-active-3fUCF-{opacity:.01;-webkit-transform:translateY(2rem) scale(.8);transform:translateY(2rem) scale(.8);-webkit-transition:opacity .2s linear,-webkit-transform .2s ease-in;transition:opacity .2s linear,-webkit-transform .2s ease-in;transition:transform .2s ease-in,opacity .2s linear;transition:transform .2s ease-in,opacity .2s linear,-webkit-transform .2s ease-in}.-transtion-pop-enter-2ZhJM-{opacity:.01;-webkit-transform:scale(.8);transform:scale(.8)}.-transtion-pop-enter-active-1tUbq-{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition:opacity .1s linear,-webkit-transform .1s linear;transition:opacity .1s linear,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear;transition:transform .1s linear,opacity .1s linear,-webkit-transform .1s linear}.-transtion-pop-exit-3D4r2-{opacity:1;-webkit-transform:translate(0);transform:translate(0)}.-transtion-pop-exit-active-3Jk1C-{opacity:.01;-webkit-transform:translateY(20vw) scale(.8);transform:translateY(20vw) scale(.8);-webkit-transition:opacity .2s linear,-webkit-transform .2s ease-in;transition:opacity .2s linear,-webkit-transform .2s ease-in;transition:transform .2s ease-in,opacity .2s linear;transition:transform .2s ease-in,opacity .2s linear,-webkit-transform .2s ease-in}#root{-webkit-transition:all .15s ease-in;transition:all .15s ease-in}.blur-set{height:100%;-webkit-filter:blur(.4vw) contrast(.8);filter:blur(.4vw) contrast(.8)}.blur-fix{-webkit-filter:none;filter:none;-webkit-transform:none;transform:none}.-modal-dyy-3IuGM-{z-index:10;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.4);-webkit-tap-highlight-color:transparent}.-modal-dyy-3IuGM-,.-modal-wrapper-2oL47-{position:fixed;-webkit-box-sizing:border-box;box-sizing:border-box}.-modal-wrapper-2oL47-{z-index:20;top:13.33333vw;left:50%;right:0;width:93.33333vw;margin-left:-46.66667vw}.-modal-default-1gamp-{width:100%;font-size:4.93333vw;line-height:1.5;text-align:center;line-height:3;-webkit-box-shadow:rgba(0,0,0,.25) 0 3.73333vw 12vw,rgba(0,0,0,.22) 0 2.66667vw 4.8vw;box-shadow:0 3.73333vw 12vw rgba(0,0,0,.25),0 2.66667vw 4.8vw rgba(0,0,0,.22);max-height:106.66667vw;overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch;border-radius:1.06667vw;color:#000;background:#fff}.-modal-default-1gamp-::-webkit-scrollbar{display:none}.-modal-default-big-yVwxe-{z-index:20;position:fixed;top:0;left:50%;width:100vw;margin-left:-50vw}.-modal-default-big-yVwxe- .-modal-close-1wPj7-{top:3vw;right:3vw}.-modal-close-1wPj7-{position:absolute;z-index:30;right:0;top:0;width:8.8vw;height:8.8vw;line-height:11.9vw;font-size:5vw;font-weight:700;text-align:center}.-modal-close-1wPj7- svg{width:6.3vw;height:6.3vw;fill:#999}.-button-btn-1gv-W-{width:77.33333vw;margin:5.33333vw auto;line-height:2.8;font-size:4vw;text-align:center;color:#fff;background:#2196f3;border-radius:.53333vw;-webkit-box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24);box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24)}.-button-icon-B8XbU-{display:inline-block;vertical-align:bottom;margin-left:1.86667vw;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAA7CAMAAADRoFNUAAAAn1BMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Kd3m4AAAANHRSTlMA2fHs+xIM6Muk3oJV7saXfmNcR9KvOzgqBfS7p593cmpOS0NALxwCwLc9JiIWB6KSjG40/cttGgAAAdFJREFUSMe9lNd6ozAQRodmmsEYcMGOe4vjkmSj93+21egjHhksoZvdcyX9HIQ06BuQ+IjPlm376SK/gY48YA/c8EfpTVP2zKBSLEjLsRrr+kocMkGQ7a/HS//bYshs2hYLhrzv6E0HA6fXFD3xxdVTNMdo3jTPmG4a4fxF+IPZGpr4PI3giRGPUmixZZwdSPRwlwdogxUIQWLDg5KOMvGgZskf+CcgYh7Ej/LYLPnd8oRx5Jou+Lz/e0cYUkjFKxoHGlNpOAEIKl+sQbzx+b4eR8J0KkDuTsP84vMhHZeT9AA5uuJrxJrPF2TS/8aCuh4QB1zl9MIc1FsmZnhv2+YVh0uQycMwHJxa5m7Ecw8UkEn8b9MG2GRbEzM64V14MzDfvxiSARSrQmsmTFDCJy6tNIk0Zsiy27Td+g5us77GJKIzbr3qMImRscnGxubK2Iz/kXlY5pfaLNVmBrAXRdgDEulO5CX1v8uxiasZwvoxtgKmoQ/fzIwJ7MxE54YNoAPqaoGJKS70vTQwM0Aqq0Oj7uhFneYBDNXkDjU3X2+m8OCoVwcgqY7O/AMSl5nG/ACZqUKldk+qqzJLaDBRmZ/QUt2OAxHjrrZAFL7dxsd2/hcmLfOXmijBPAAAAABJRU5ErkJggg==);background-size:100% 100%;width:5.46667vw;height:7.86667vw}.-user-info-wrap-dAi6Y-{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2.66667vw 4vw 13.33333vw;width:100%;background:#fff;-webkit-box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24);box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24)}.-user-info-title-1WPnP-{width:100%;padding:1.2vw 0 0;margin-bottom:1.6vw;font-size:4.26667vw}.-user-info-tip-3445r-{line-height:2;font-size:3.46667vw;color:#212121}.-user-info-logo-edcg--{position:absolute;display:inline-block;bottom:1.33333vw;right:1.33333vw;width:24vw;height:14vw;background:#ffc400;background:-webkit-gradient(linear,left top,right bottom,color-stop(0,#ffc400),color-stop(65%,#ffc400),color-stop(65%,#ffeb3b),to(#ffeb3b));background:linear-gradient(to bottom right,#ffc400 0,#ffc400 65%,#ffeb3b 0,#ffeb3b)}.-home-wrap-3UMVm-{-webkit-box-sizing:border-box;box-sizing:border-box;padding:2.66667vw}.-home-tip-1cSD5-{font-size:3.2vw;color:#666;text-align:center}.-home-hello-n7ycw-{margin:8vw auto 10.66667vw;padding:.8vw 0;text-align:center;border-bottom:1px solid #0e90d2}.-home-button-2nCTt-{width:53.33333vw;margin:5.33333vw auto;line-height:2.3;color:#fff;text-align:center;border-radius:.66667vw;background:#0e90d2}.-list-list-2LkSt-{padding-top:2.66667vw}.-list-single-3XcdY-{text-align:left;line-height:2.5}.-list-single-3XcdY-:nth-child(odd){background-color:#eee}.-list-single-3XcdY- div{display:inline-block;padding:0 5.33333vw;text-align:center}.-listerr-content-pxNQ6-{width:90%;margin:0 auto;position:relative}.-listerr-errtip-2nQiN-{position:relative;margin-top:2.66667vw}.-listerr-errtip-2nQiN- div{position:relative;display:block;height:2.66667vw;margin:0 0 2.4vw;background:#ddd}.-listerr-errtip-2nQiN- div:before{content:"";opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;background:#eee;-webkit-animation:-listerr-progressactive-1BClP- 1s infinite ease-in-out;animation:-listerr-progressactive-1BClP- 1s infinite ease-in-out}.-listerr-errtip-2nQiN- div:first-child{width:30%;-webkit-animation:-listerr-ball-spin-2pxoQ- .5s .1s infinite ease-in-out;animation:-listerr-ball-spin-2pxoQ- .5s .1s infinite ease-in-out}.-listerr-errtip-2nQiN- div:nth-child(2){width:20%;margin-bottom:2.66667vw}.-listerr-errtip-2nQiN- div:nth-child(2),.-listerr-errtip-2nQiN- div:nth-child(3){-webkit-animation:-listerr-ball-spin-2pxoQ- .5s .3s infinite ease-in-out;animation:-listerr-ball-spin-2pxoQ- .5s .3s infinite ease-in-out}.-listerr-errtip-2nQiN- div:nth-child(3){width:60%}.-listerr-errtip-2nQiN- div:nth-child(4){width:80%}.-listerr-errtip-2nQiN- div:nth-child(4),.-listerr-errtip-2nQiN- div:nth-child(5){-webkit-animation:-listerr-ball-spin-2pxoQ- .5s .1s infinite ease-in-out;animation:-listerr-ball-spin-2pxoQ- .5s .1s infinite ease-in-out}.-listerr-errtip-2nQiN- div:nth-child(5){width:50%}.-listerr-word-3-vMT-{position:absolute;top:0;right:0;color:#333;font-size:1.73333vw;font-weight:700;-webkit-animation:-listerr-fadein-1CDpe- .8s infinite;animation:-listerr-fadein-1CDpe- .8s infinite}@-webkit-keyframes -listerr-fadein-1CDpe-{0%{opacity:1}50%{opacity:.7}to{opacity:1}}@keyframes -listerr-fadein-1CDpe-{0%{opacity:1}50%{opacity:.7}to{opacity:1}}@-webkit-keyframes -listerr-progressactive-1BClP-{0%{opacity:.8;width:0}to{opacity:0;width:100%}}@keyframes -listerr-progressactive-1BClP-{0%{opacity:.8;width:0}to{opacity:0;width:100%}}.-carousel-wrap-2FV_D-{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-sizing:content-box;box-sizing:content-box}.-carousel-slide-2rTec-,.-carousel-wrap-2FV_D-{position:relative;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform}.-carousel-slide-2rTec-{-ms-flex-negative:0;flex-shrink:0;width:100%;height:100%}.-carousel-pagination-wrap-3lF9P-{position:relative;overflow:hidden}.-carousel-pagination-1wS3q-{position:absolute;bottom:1.33333vw;width:100%;height:3.33333vw;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0;line-height:1;text-align:center;overflow:hidden}.-carousel-bullet-1zSis-{background:#d4a143}.-carousel-bullet-1zSis-,.-carousel-bullet-active-3_OnR-{display:inline-block;width:2.66667vw;height:2.66667vw;margin:0 1.33333vw;padding:0;border-radius:50%}.-carousel-bullet-active-3_OnR-{background:#d4a143;background:#a32229}.tj-tabs-content{position:relative;width:100%;overflow:hidden}.tj-tabs-nav-wrap{display:block;padding-left:0;margin:0;-webkit-transition:-webkit-transform .5s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .5s cubic-bezier(.645,.045,.355,1);transition:transform .5s cubic-bezier(.645,.045,.355,1);transition:transform .5s cubic-bezier(.645,.045,.355,1),-webkit-transform .5s cubic-bezier(.645,.045,.355,1);overflow:hidden}.tj-tabs-nav,.tj-tabs-nav-wrap{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;list-style:none}.tj-tabs-nav{display:inline-block;height:8vw;line-height:8vw;font-size:4.26667vw;color:#000;text-align:center;-webkit-tap-highlight-color:rgba(0,0,0,0);cursor:pointer}.tj-tabs-nav-active{color:#fba112}.tj-tabs-activebar{position:absolute;bottom:0;left:0;height:.53333vw;background-color:#fba112;z-index:1;will-change:margin-left;-webkit-transition:margin-left .2s cubic-bezier(.645,.045,.355,1);transition:margin-left .2s cubic-bezier(.645,.045,.355,1);list-style:none}.tj-tabs-pane-wrap{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:100%;margin-top:4vw;will-change:transform}.tj-tabs-pane-wrap__ani{-webkit-transition:-webkit-transform .2s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .2s cubic-bezier(.645,.045,.355,1);transition:transform .2s cubic-bezier(.645,.045,.355,1);transition:transform .2s cubic-bezier(.645,.045,.355,1),-webkit-transform .2s cubic-bezier(.645,.045,.355,1)}.tj-tabs-pane{width:100%;-ms-flex-negative:0;flex-shrink:0;-webkit-transition:opacity .45s;transition:opacity .45s;opacity:1}.tj-tabs-pane-inactive{opacity:0;height:0;padding:0}.-list-tabs-wrap-2YdRi-{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2.66667vw 2.66667vw 13.33333vw;width:100%;background:#fff;-webkit-box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24);box-shadow:0 0 .26667vw 0 rgba(0,0,0,.12),0 .26667vw .26667vw 0 rgba(0,0,0,.24)}.-list-tabs-button-NeYrU-{width:40vw;margin:5.33333vw auto;line-height:2;color:#fff;text-align:center;border-radius:1.33333vw;background:#0e90d2}.-list-tabs-panel-xK7tm-{position:relative}.-list-tabs-pagina-2Avbv-{display:inline-block;opacity:.8;font-size:.8em}.-list-tabs-more-3a0KU-{margin-top:1.33333vw;color:#0e90d2}.-list-wrap-3ma5A-{-webkit-box-sizing:border-box;box-sizing:border-box;padding:2.66667vw}.-list-button-PuCpH-{width:40vw;margin:5.33333vw auto;line-height:2;color:#fff;text-align:center;border-radius:1.33333vw;background:#0e90d2}.-list-pagina-2jZhg-{margin-top:2.66667vw;color:#666}.-list-more-3e7vs-{margin-top:1.33333vw;color:#0e90d2}.-router-fill-1w_jQ-{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;max-width:100vw;margin:0 auto;font-size:4.26667vw}.-router-notfund-2NkuC-{padding-top:6.66667vw;text-align:center;font-size:3.2vw}blockquote,body,button,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,html,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;border:0;vertical-align:baseline}html{line-height:1}table{border-collapse:collapse;border-spacing:0}ol,ul{list-style:none}img{border:0}b,strong{font-weight:700}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}body{line-height:1.5;font-family:Helvetica,Tahoma,Arial,Microsoft YaHei,\\5FAE\8F6F\96C5\9ED1,Heiti,\\9ED1\4F53,SimSun,\\5B8B\4F53,sans-serif;background:#eee}
--------------------------------------------------------------------------------