├── app ├── components │ ├── Home │ │ ├── JoinBox │ │ │ ├── style.scss │ │ │ └── index.js │ │ ├── VipBox │ │ │ ├── style.scss │ │ │ └── index.js │ │ ├── AdBox │ │ │ ├── style.scss │ │ │ └── index.js │ │ ├── Modal │ │ │ └── Redpacket │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ └── index.js │ ├── TabBar │ │ ├── style.scss │ │ └── index.js │ ├── Gift │ │ ├── List │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── Home │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ ├── VipGift │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ ├── News │ │ │ │ ├── index.js │ │ │ │ └── style.scss │ │ │ ├── Card │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── Detail │ │ │ ├── style.scss │ │ │ └── index.js │ ├── Vip │ │ ├── Invite │ │ │ └── style.scss │ │ ├── Shop │ │ │ └── List │ │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── Gift │ │ │ └── List │ │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── Home │ │ │ ├── style.scss │ │ │ └── Header │ │ │ │ └── style.scss │ │ ├── Rank │ │ │ └── About │ │ │ │ └── index.js │ │ ├── Buying │ │ │ └── List │ │ │ │ └── index.js │ │ ├── Help │ │ │ └── index.js │ │ ├── Level │ │ │ └── style.scss │ │ └── Priviege │ │ │ └── Detail │ │ │ └── index.js │ ├── User │ │ ├── Address │ │ │ ├── Home │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ ├── List │ │ │ │ ├── index.js │ │ │ │ └── Item │ │ │ │ │ └── index.js │ │ │ └── Edit │ │ │ │ └── index.js │ │ ├── Coupon │ │ │ ├── Home │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── List │ │ │ │ ├── index.js │ │ │ │ └── Item │ │ │ │ └── style.scss │ │ ├── Favorite │ │ │ ├── style.scss │ │ │ └── index.js │ │ ├── Order │ │ │ ├── Home │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── List │ │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── Redpacket │ │ │ ├── Home │ │ │ │ └── style.scss │ │ │ ├── Cash │ │ │ │ └── List │ │ │ │ │ ├── Item │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ └── Spend │ │ │ │ └── List │ │ │ │ ├── index.js │ │ │ │ └── Item │ │ │ │ └── style.scss │ │ ├── Home │ │ │ └── style.scss │ │ └── Comment │ │ │ └── Add │ │ │ └── index.js │ ├── Shop │ │ ├── List │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── Detail │ │ │ ├── Coupon │ │ │ │ ├── style.scss │ │ │ │ ├── index.js │ │ │ │ └── Item │ │ │ │ │ ├── style.scss │ │ │ │ │ └── index.js │ │ │ ├── Header │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── style.scss │ │ ├── Join │ │ │ ├── style.scss │ │ │ ├── index.js │ │ │ └── Form │ │ │ │ └── index.js │ │ ├── Cashier │ │ │ └── style.scss │ │ ├── Home │ │ │ ├── Hot │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── Branch │ │ │ └── index.js │ ├── Result │ │ ├── Pay │ │ │ └── index.js │ │ ├── Warn │ │ │ └── index.js │ │ ├── Fail │ │ │ └── index.js │ │ ├── Success │ │ │ └── index.js │ │ └── index.js │ ├── Rim │ │ ├── Detail │ │ │ ├── style.scss │ │ │ └── index.js │ │ ├── List │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── Home │ │ │ └── index.js │ ├── Filter │ │ ├── style.scss │ │ └── index.js │ ├── Pay │ │ ├── index.js │ │ └── Success │ │ │ ├── style.scss │ │ │ └── index.js │ ├── Weal │ │ ├── Home │ │ │ └── style.scss │ │ ├── Rule │ │ │ └── index.js │ │ ├── Coupon │ │ │ └── List │ │ │ │ ├── index.js │ │ │ │ └── Item │ │ │ │ └── style.scss │ │ └── Redpacket │ │ │ ├── List │ │ │ ├── Item │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ │ ├── Detail │ │ │ ├── style.scss │ │ │ ├── Item │ │ │ │ └── index.js │ │ │ └── index.js │ │ │ └── Modal │ │ │ └── Open │ │ │ └── style.scss │ ├── Buying │ │ ├── Pay │ │ │ └── style.scss │ │ ├── Home │ │ │ ├── Header │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── List │ │ │ ├── index.js │ │ │ └── Item │ │ │ │ └── style.scss │ │ └── Detail │ │ │ └── Header │ │ │ ├── style.scss │ │ │ └── index.js │ ├── Lottery │ │ ├── Win │ │ │ ├── style.scss │ │ │ └── index.js │ │ └── Record │ │ │ └── List │ │ │ ├── index.js │ │ │ └── Item │ │ │ ├── style.scss │ │ │ └── index.js │ ├── Rate │ │ └── index.js │ ├── Comment │ │ └── List │ │ │ ├── index.js │ │ │ └── Item │ │ │ └── index.js │ ├── Loading │ │ └── index.js │ ├── Header │ │ ├── SearchBar │ │ │ ├── style.scss │ │ │ └── index.js │ │ └── index.js │ ├── Link │ │ └── index.js │ ├── FollowTips │ │ └── index.js │ ├── Category │ │ └── index.js │ └── Swiper │ │ └── index.js ├── reducers │ ├── cart │ │ ├── index.js │ │ └── buying.js │ ├── order.js │ ├── index.js │ ├── link.js │ ├── focus.js │ ├── notice.js │ ├── navlink.js │ ├── filter.js │ ├── lottery.js │ ├── category.js │ ├── cashier.js │ ├── config.js │ ├── gift.js │ ├── rim.js │ ├── buying.js │ ├── coupon.js │ ├── redpacket.js │ └── shop.js ├── containers │ ├── Vip │ │ ├── Rank │ │ │ ├── index.js │ │ │ └── About │ │ │ │ └── index.js │ │ ├── Privilege │ │ │ ├── index.js │ │ │ └── Detail │ │ │ │ └── index.js │ │ ├── Pay │ │ │ └── Agree │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── Help │ │ │ └── index.js │ │ ├── Level │ │ │ └── index.js │ │ ├── routes.js │ │ └── Invite │ │ │ └── index.js │ ├── Home │ │ ├── routes.js │ │ └── Citys │ │ │ └── index.js │ ├── Pay │ │ ├── Fail │ │ │ └── index.js │ │ ├── index.js │ │ └── routes.js │ ├── User │ │ ├── Comment │ │ │ └── index.js │ │ ├── Finance │ │ │ └── Result │ │ │ │ ├── style.scss │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── Home │ │ │ └── index.js │ │ ├── About │ │ │ └── index.js │ │ ├── Favorites │ │ │ └── index.js │ │ ├── Addresses │ │ │ └── Edit │ │ │ │ └── index.js │ │ └── Win │ │ │ └── detail.js │ ├── Shop │ │ ├── Coupon │ │ │ └── index.js │ │ ├── index.js │ │ ├── Branch │ │ │ └── index.js │ │ ├── Join │ │ │ ├── index.js │ │ │ └── Form │ │ │ │ └── index.js │ │ ├── Buying │ │ │ └── index.js │ │ ├── Comment │ │ │ └── index.js │ │ ├── routes.js │ │ └── Home │ │ │ └── index.js │ ├── Search │ │ ├── Result │ │ │ └── index.js │ │ ├── index.js │ │ ├── routes.js │ │ └── Home │ │ │ └── index.js │ ├── Buying │ │ ├── index.js │ │ ├── routes.js │ │ └── Home │ │ │ └── index.js │ ├── Gift │ │ ├── index.js │ │ ├── routes.js │ │ ├── Rule │ │ │ └── index.js │ │ ├── List │ │ │ └── index.js │ │ └── Home │ │ │ └── index.js │ ├── Lottery │ │ ├── routes.js │ │ └── index.js │ ├── NotFound │ │ └── index.js │ ├── Rim │ │ ├── index.js │ │ ├── routes.js │ │ ├── Home │ │ │ └── index.js │ │ └── Detail │ │ │ └── index.js │ ├── Weal │ │ ├── index.js │ │ ├── Rule │ │ │ └── index.js │ │ ├── routes.js │ │ └── Redpacket │ │ │ └── index.js │ ├── Root.js │ ├── Boot.js │ └── App.js ├── store │ ├── configureStore.js │ ├── configureStore.prod.js │ └── configureStore.dev.js ├── constants.js ├── utils │ └── cache.js ├── actions │ ├── order.js │ ├── cart │ │ └── buying.js │ ├── lottery.js │ ├── config.js │ ├── rim.js │ ├── link.js │ ├── cashier.js │ ├── notice.js │ ├── focus.js │ ├── filter.js │ ├── navlink.js │ ├── category.js │ ├── gift.js │ ├── buying.js │ ├── coupon.js │ ├── redpacket.js │ └── shop.js ├── routes.js └── middleware │ └── api.js ├── favicon.ico ├── client ├── index.js ├── entry.js ├── entry.prod.js ├── entry.dev.js └── server.js ├── server └── index.js ├── .gitignore ├── assets ├── favicon.ico ├── images │ ├── s.gif │ ├── tuijian.png │ ├── icon_finance.png │ ├── user_header_bg.png │ └── vip_header_bg.png ├── fonts │ ├── dudu.ttf │ └── dudu.woff └── scss │ ├── _color.scss │ └── antd.scss ├── webpack.build.js ├── postcss.config.js ├── README.md └── .babelrc /app/components/Home/JoinBox/style.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/TabBar/style.scss: -------------------------------------------------------------------------------- 1 | .link { 2 | color: #666; 3 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/favicon.ico -------------------------------------------------------------------------------- /app/components/Home/VipBox/style.scss: -------------------------------------------------------------------------------- 1 | .shopItem { 2 | height: 120px; 3 | } -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | require('./server'); 3 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | require('./server'); 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | .idea 4 | npm-debug.log 5 | dist 6 | .DS_Store -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/favicon.ico -------------------------------------------------------------------------------- /assets/images/s.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/images/s.gif -------------------------------------------------------------------------------- /app/components/Gift/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .img { 2 | width: 200px; 3 | height: 150px; 4 | } -------------------------------------------------------------------------------- /app/components/Home/AdBox/style.scss: -------------------------------------------------------------------------------- 1 | .thumb { 2 | width: 100%; 3 | height: 160px; 4 | } -------------------------------------------------------------------------------- /app/components/Vip/Invite/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | border-bottom: 2px solid #eecd60; 3 | } -------------------------------------------------------------------------------- /assets/fonts/dudu.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/fonts/dudu.ttf -------------------------------------------------------------------------------- /assets/fonts/dudu.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/fonts/dudu.woff -------------------------------------------------------------------------------- /app/components/User/Address/Home/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | border-bottom: 3px solid #1bbc9b; 3 | } -------------------------------------------------------------------------------- /app/components/User/Coupon/Home/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | border-bottom: 3px solid #1bbc9b; 3 | } -------------------------------------------------------------------------------- /app/components/User/Favorite/style.scss: -------------------------------------------------------------------------------- 1 | .thumb { 2 | width: 200px; 3 | height: 150px; 4 | } -------------------------------------------------------------------------------- /app/components/User/Order/Home/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | border-bottom: 3px solid #1bbc9b; 3 | } -------------------------------------------------------------------------------- /webpack.build.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | module.exports = require('./webpack.config'); -------------------------------------------------------------------------------- /app/components/Shop/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .thumb { 2 | width: 200px; 3 | height: 200px; 4 | 5 | } -------------------------------------------------------------------------------- /app/components/User/Order/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .thumb { 2 | width: 200px; 3 | height: 150px; 4 | } -------------------------------------------------------------------------------- /app/components/User/Redpacket/Home/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | border-bottom: 3px solid #1bbc9b; 3 | } -------------------------------------------------------------------------------- /app/components/Vip/Shop/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .thumb { 2 | width: 200px; 3 | height: 150px; 4 | } -------------------------------------------------------------------------------- /assets/images/tuijian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/images/tuijian.png -------------------------------------------------------------------------------- /app/components/Result/Pay/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({}) =>
-------------------------------------------------------------------------------- /app/components/Shop/Detail/Coupon/style.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | overflow-x: auto; 3 | white-space: nowrap; 4 | } -------------------------------------------------------------------------------- /assets/images/icon_finance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/images/icon_finance.png -------------------------------------------------------------------------------- /assets/images/user_header_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/images/user_header_bg.png -------------------------------------------------------------------------------- /assets/images/vip_header_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minooo/dolife/HEAD/assets/images/vip_header_bg.png -------------------------------------------------------------------------------- /app/components/Gift/Home/Item/style.scss: -------------------------------------------------------------------------------- 1 | .wrap { 2 | width: 50%; 3 | } 4 | 5 | .thumb { 6 | width: 100%; 7 | } -------------------------------------------------------------------------------- /app/components/Vip/Gift/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .wrap { 2 | width: 50%; 3 | } 4 | 5 | .thumb { 6 | width: 100%; 7 | } -------------------------------------------------------------------------------- /app/components/Vip/Home/style.scss: -------------------------------------------------------------------------------- 1 | .btnBorder { 2 | border-color: #cbaa63; 3 | } 4 | 5 | .active { 6 | border-bottom: 3px solid #cfae68; 7 | } -------------------------------------------------------------------------------- /app/components/Shop/Detail/Header/style.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: absolute; 3 | background: linear-gradient(rgba(0, 0, 0, .7), rgba(0, 0, 0, 0)); 4 | } -------------------------------------------------------------------------------- /app/reducers/cart/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import buying from './buying'; 3 | 4 | export default combineReducers({ 5 | buying 6 | }) -------------------------------------------------------------------------------- /client/entry.js: -------------------------------------------------------------------------------- 1 | if(process.env.NODE_ENV==='production'){ 2 | module.exports=require('./entry.prod') 3 | }else{ 4 | module.exports=require('./entry.dev') 5 | } -------------------------------------------------------------------------------- /app/components/Rim/Detail/style.scss: -------------------------------------------------------------------------------- 1 | .bg { 2 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)) 3 | } 4 | 5 | .pt200 { 6 | padding-top: 200px; 7 | } -------------------------------------------------------------------------------- /app/containers/Vip/Rank/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | 3 | export default class extends PureComponent { 4 | render() { 5 | return
6 | } 7 | } -------------------------------------------------------------------------------- /app/components/Filter/style.scss: -------------------------------------------------------------------------------- 1 | .filterWrap { 2 | position: relative; 3 | z-index: 2; 4 | } 5 | 6 | .filterBox { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | } -------------------------------------------------------------------------------- /app/components/Vip/Rank/About/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | 4 | export default ({}) =>
5 |
6 |
-------------------------------------------------------------------------------- /app/containers/Home/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: '/citys', 3 | getComponent(state, cb){ 4 | require.ensure([], require => cb(null, require('./Citys').default)) 5 | } 6 | } -------------------------------------------------------------------------------- /app/containers/Pay/Fail/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | 3 | export default class extends PureComponent { 4 | render() { 5 | return
6 | } 7 | } -------------------------------------------------------------------------------- /app/containers/Vip/Privilege/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | 3 | export default class extends PureComponent { 4 | render() { 5 | return
6 | } 7 | } -------------------------------------------------------------------------------- /app/containers/User/Comment/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | 3 | export default class extends PureComponent { 4 | render() { 5 | return
6 | } 7 | } -------------------------------------------------------------------------------- /app/components/Pay/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import Success from './Success'; 3 | 4 | export default class extends PureComponent { 5 | static Success = Success 6 | } -------------------------------------------------------------------------------- /app/store/configureStore.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./configureStore.prod') 3 | } else { 4 | module.exports = require('./configureStore.dev') 5 | } -------------------------------------------------------------------------------- /app/containers/User/Finance/Result/style.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | font-size: 200px; 3 | } 4 | 5 | .borderMain { 6 | border-color: #1abc9c; 7 | } 8 | 9 | .borderDefault { 10 | border-color: #989898; 11 | } -------------------------------------------------------------------------------- /app/components/Rim/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .fontw { 2 | font-weight: 700; 3 | } 4 | 5 | .pt100 { 6 | padding-top: 200px; 7 | } 8 | 9 | .bg { 10 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)) 11 | } -------------------------------------------------------------------------------- /app/containers/Shop/Coupon/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | 3 | export default class extends PureComponent { 4 | componentDidMount() { 5 | 6 | } 7 | 8 | render() { 9 | return
10 | } 11 | } -------------------------------------------------------------------------------- /app/components/Gift/Detail/style.scss: -------------------------------------------------------------------------------- 1 | .title { 2 | position: absolute; 3 | left: 0; 4 | bottom: 0; 5 | background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .7)); 6 | } 7 | 8 | .fontweight { 9 | font-weight: 700; 10 | } -------------------------------------------------------------------------------- /app/containers/Search/Result/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | export default class extends PureComponent { 3 | componentDidMount() { 4 | 5 | } 6 | 7 | render() { 8 | return
9 |
10 | } 11 | } -------------------------------------------------------------------------------- /app/components/Result/Warn/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Result from 'components/Result'; 3 | export default ({title, message}) => } 5 | title={title} 6 | message={message} 7 | /> -------------------------------------------------------------------------------- /app/components/Weal/Home/style.scss: -------------------------------------------------------------------------------- 1 | .split { 2 | border-left: 1px solid #cfae68; 3 | } 4 | 5 | .active { 6 | border-bottom: 2px solid #cfae68; 7 | } 8 | 9 | .fw { 10 | font-weight: 700; 11 | } 12 | 13 | .fws { 14 | font-weight: 500; 15 | } -------------------------------------------------------------------------------- /app/containers/Vip/Rank/About/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import VipRankAbout from 'components/Vip/Rank/About'; 3 | 4 | export default class extends PureComponent { 5 | render() { 6 | return 7 | } 8 | } -------------------------------------------------------------------------------- /app/containers/Buying/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | @connect() 4 | export default class extends PureComponent { 5 | render() { 6 | const {children} = this.props 7 | return children 8 | } 9 | } -------------------------------------------------------------------------------- /app/containers/Gift/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | @connect() 4 | export default class extends PureComponent { 5 | render() { 6 | const {children} = this.props 7 | return children 8 | } 9 | } -------------------------------------------------------------------------------- /app/containers/Search/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | @connect() 4 | export default class extends PureComponent { 5 | render() { 6 | const {children} = this.props 7 | return children 8 | } 9 | } -------------------------------------------------------------------------------- /app/components/Pay/Success/style.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | font-size: 150px; 3 | } 4 | 5 | .lottery { 6 | padding: 250px 0 30px; 7 | background: url("https://public.duduapp.net/dolife/static/images/lottery.jpg") #e12c0d no-repeat top center; 8 | background-size: 100% auto; 9 | } -------------------------------------------------------------------------------- /app/components/Buying/Pay/style.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | border: .1rem solid red; 3 | border-left: none; 4 | border-right: none; 5 | border-image-source: url(http://public.duduapp.net/duLifeImg/lo-bg.gif); 6 | border-image-slice: 9 0 9 0; 7 | border-image-repeat: repeat; 8 | } -------------------------------------------------------------------------------- /app/components/Lottery/Win/style.scss: -------------------------------------------------------------------------------- 1 | .modalbox { 2 | width: 580px; 3 | height: 760px; 4 | border-top: 4px solid #ff6162; 5 | } 6 | 7 | .modalmain { 8 | position: absolute; 9 | bottom: 50px; 10 | left: 20px; 11 | right: 20px; 12 | overflow-y: auto; 13 | } -------------------------------------------------------------------------------- /app/components/Weal/Rule/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | export default ({rule}) =>
4 |
5 |
6 |
-------------------------------------------------------------------------------- /app/containers/Shop/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | 4 | @connect() 5 | export default class extends PureComponent { 6 | render() { 7 | const {children} = this.props 8 | return children 9 | } 10 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser:'postcss-scss', 3 | plugins: { 4 | 'precss': {}, 5 | 'autoprefixer': {}, 6 | 'postcss-pxtorem': { 7 | rootValue: 100, 8 | propWhiteList: [], 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /app/components/Buying/Home/Header/style.scss: -------------------------------------------------------------------------------- 1 | .active { 2 | background-color: #1bbc9b; 3 | } 4 | 5 | .blr { 6 | border-right: 2px solid #1bbc9b; 7 | border-left: 2px solid #1bbc9b; 8 | } 9 | 10 | .border { 11 | border: 2px solid #1bbc9b; 12 | border-radius: 10px; 13 | } -------------------------------------------------------------------------------- /app/constants.js: -------------------------------------------------------------------------------- 1 | export const FAVORITE = { 2 | SHOP: 1, 3 | COUPON: 2, 4 | RIM: 3, 5 | BUYING: 4, 6 | GIFT: 5 7 | } 8 | export const MODULE = { 9 | BUYING: 1, 10 | CASHIER: 6, 11 | COUPON: 7, 12 | FIVECARD: 8, 13 | CARDPAY: 9, 14 | QRCODEPAY: 10, 15 | RIM: 11, 16 | } 17 | -------------------------------------------------------------------------------- /app/components/Gift/Home/VipGift/style.scss: -------------------------------------------------------------------------------- 1 | .split:before { 2 | display: block; 3 | content: ''; 4 | position: absolute; 5 | left: 0; 6 | top: 10%; 7 | height: 50%; 8 | border-left: 1px solid #ccc; 9 | } 10 | 11 | .box { 12 | overflow-x: auto; 13 | white-space: nowrap; 14 | } 15 | -------------------------------------------------------------------------------- /app/containers/Lottery/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/lottery', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | ] 11 | } -------------------------------------------------------------------------------- /app/containers/Pay/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | 4 | @connect() 5 | export default class extends PureComponent { 6 | componentDidMount() { 7 | } 8 | 9 | render() { 10 | const {children} = this.props 11 | return children 12 | } 13 | } -------------------------------------------------------------------------------- /app/components/Result/Fail/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Result from 'components/Result'; 3 | import {Icon} from 'antd-mobile'; 4 | export default ({title, message}) => } 6 | title={title} 7 | message={message} 8 | /> -------------------------------------------------------------------------------- /app/components/Result/Success/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Result from 'components/Result'; 3 | import {Icon} from 'antd-mobile'; 4 | export default ({title, message}) => } 6 | title={title} 7 | message={message} 8 | /> -------------------------------------------------------------------------------- /app/components/Shop/Detail/Coupon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import s from './style.scss'; 3 | import Item from './Item'; 4 | 5 | export default ({coupons, onClickCoupon}) =>
6 | {coupons.map((n, i) => )} 7 |
-------------------------------------------------------------------------------- /app/components/Rim/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Item from "./Item"; 3 | import List from 'components/List'; 4 | 5 | export default ({rim, fetchRims}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Shop/Join/style.scss: -------------------------------------------------------------------------------- 1 | .up { 2 | position: absolute; 3 | top: -20px; 4 | left: 50%; 5 | animation: mymove 1.5s infinite; 6 | 7 | } 8 | 9 | @keyframes mymove { 10 | from { 11 | top: -20px; 12 | opacity: 1; 13 | } 14 | to { 15 | top: -60px; 16 | opacity: 0; 17 | } 18 | } 19 | 20 | .link { 21 | position: relative; 22 | } -------------------------------------------------------------------------------- /app/containers/Vip/Privilege/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import VipPriviegeDetail from 'components/Vip/Priviege/Detail'; 3 | 4 | export default class extends PureComponent { 5 | render() { 6 | const {location} = this.props 7 | return 10 | } 11 | } -------------------------------------------------------------------------------- /app/components/Gift/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Shop/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Buying/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Vip/Gift/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Vip/Shop/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Rate/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const level = [1, 2, 3, 4, 5]; 3 | export default ({num, size = 36, color = 'color7', className, onChange}) =>
4 | {level.map((n, i) => = n ? color : 'color12'}`} key={i} 5 | onTouchEnd={() => onChange && onChange(n)}/>)} 6 |
-------------------------------------------------------------------------------- /app/components/Shop/Detail/Coupon/Item/style.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | display: inline-block; 3 | &:before, 4 | &:after { 5 | display: block; 6 | content: ''; 7 | position: absolute; 8 | width: 0; 9 | height: 100%; 10 | //border-right:8px dotted #fff; 11 | top: 0; 12 | } 13 | &:before { 14 | left: -4px; 15 | } 16 | &:after { 17 | right: -4px; 18 | } 19 | } -------------------------------------------------------------------------------- /app/components/Vip/Buying/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Comment/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({data, loadHandle}) => { 11 | return 12 | }} 13 | /> -------------------------------------------------------------------------------- /app/containers/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import ResultFail from 'components/Result/Fail'; 3 | import {setTitle} from 'utils'; 4 | 5 | export default class extends PureComponent { 6 | componentDidMount() { 7 | setTitle(`404`) 8 | } 9 | 10 | render() { 11 | return 15 | } 16 | } -------------------------------------------------------------------------------- /app/components/User/Coupon/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({coupon, fetchCoupons, onDelete}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/User/Redpacket/Cash/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({redpacket}) =>
4 |
5 |
{redpacket.time_create}
6 |
{redpacket.title}
7 |
8 |
{redpacket.money}
9 |
-------------------------------------------------------------------------------- /app/components/User/Order/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "components/List"; 3 | import Item from "./Item"; 4 | 5 | export default ({order, fetchOrders, onPay, onComment}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/Weal/Coupon/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({coupon, fetchCoupons, onClickCoupon}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/User/Redpacket/Cash/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({cash_redpacket, fetchCashedpackets}) => } 11 | /> -------------------------------------------------------------------------------- /app/components/User/Redpacket/Spend/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({redpacket, fetchRedpackets, onDelete}) => } 11 | /> -------------------------------------------------------------------------------- /app/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const set = (key, value, ttl = 24 * 60 * 60 * 1000) => localStorage.setItem(key, JSON.stringify({ 2 | data: value, 3 | expired_time: new Date().getTime() + ttl 4 | })) 5 | export const get = (key) => { 6 | if (localStorage.getItem(key) && JSON.parse(localStorage.getItem(key)).expired_time > new Date().getTime()) { 7 | return JSON.parse(localStorage.getItem(key)).data 8 | } else { 9 | return false 10 | } 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 嘟嘟微生活是专注于地方公众号运营的服务平台。 2 | 3 | #### 经公司同意,本着学习交流的目的,现将嘟嘟微生活前端源码全部开源。希望与各位专注于React全家桶的开发者交流心得,提升彼此,谢谢! 4 | 5 | - 由于本项目是完全基于微信端的,又所有数据只能在线上项目获取 6 | (故项目在本地无法跑起来,外行看demo,内行看code?)。 7 | 如果想看该项目实际效果,请用微信扫描下方二维码 ↓ 8 | 9 | ![dolife.png](http://upload-images.jianshu.io/upload_images/111568-8d97a4acd6f0413e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 10 | 11 | - **本项目开源的目的是和大家探讨如何基于React全家桶进行中大型项目全栈开发,在此抛砖引玉。** 12 | 13 | - 非常欢迎大牛对本项目提出建议,谢谢! 14 | -------------------------------------------------------------------------------- /app/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {ActivityIndicator} from "antd-mobile"; 3 | 4 | export default ({toast, inline, text}) => { 5 | if (toast) { 6 | return 7 | } 8 | if (inline) { 9 | return
10 | 11 |
12 | } 13 | return 14 | } -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .time { 2 | background: #cecece; 3 | } 4 | 5 | .redpacket { 6 | border-radius: 10px 10px 0 0; 7 | &:before { 8 | display: block; 9 | content: ''; 10 | position: absolute; 11 | left: -29px; 12 | top: 50%; 13 | transform: translateY(-50%); 14 | width: 0; 15 | height: 0; 16 | border: 15px solid #f99d3a; 17 | border-color: transparent #f99d3a transparent transparent; 18 | } 19 | } -------------------------------------------------------------------------------- /app/components/Lottery/Win/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({win}) =>
4 |
{win.stock}
5 |
6 |
{win.is_win ? '立即领取' : '继续抽奖'}
7 |
{win.stock > 0 ?
剩余抽奖次数:{win.stock}
:
您在抽奖机会已用完
}
8 |
9 |
-------------------------------------------------------------------------------- /app/containers/Rim/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import {setTitle} from 'utils'; 4 | @connect(state => ({ 5 | config: state.config 6 | })) 7 | export default class extends PureComponent { 8 | componentDidMount() { 9 | const {config} = this.props 10 | setTitle(config.siteConfig.sitename + '-周边活动') 11 | } 12 | 13 | render() { 14 | const {children} = this.props 15 | return children 16 | } 17 | } -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({cash_redpacket, fetchRedpackets, onClickRedpacket}) => } 11 | /> -------------------------------------------------------------------------------- /app/containers/Pay/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/pay_success', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Success').default)) 8 | } 9 | }, 10 | { 11 | path: '/pay_fail', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Fail').default)) 14 | } 15 | }, 16 | ] 17 | } -------------------------------------------------------------------------------- /app/containers/Search/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/search', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/search_:keyword', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Result').default)) 14 | } 15 | }, 16 | ] 17 | } -------------------------------------------------------------------------------- /app/containers/Weal/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import {setTitle} from 'utils'; 4 | @connect(state => ({ 5 | config: state.config 6 | })) 7 | export default class extends PureComponent { 8 | componentDidMount() { 9 | const {config} = this.props 10 | setTitle(config.siteConfig.sitename + '-超级福利') 11 | } 12 | 13 | render() { 14 | const {children} = this.props 15 | return children 16 | } 17 | } -------------------------------------------------------------------------------- /app/components/Lottery/Record/List/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from 'components/List'; 3 | import Item from "./Item"; 4 | 5 | export default ({record, fetchRecords, className, onGetAward}) => } 13 | /> -------------------------------------------------------------------------------- /app/containers/Vip/Pay/Agree/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import Header from 'components/Header'; 4 | 5 | @connect(state => ({ 6 | vip: state.vip 7 | })) 8 | export default class extends PureComponent { 9 | render() { 10 | const {vip} = this.props 11 | return
12 |
13 |
{vip.agreement}
14 |
15 | } 16 | } -------------------------------------------------------------------------------- /app/components/Buying/Detail/Header/style.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: absolute; 3 | background: linear-gradient(rgba(0, 0, 0, .7), rgba(0, 0, 0, 0)); 4 | } 5 | 6 | .rgba { 7 | background-color: rgba(255, 158, 5, .7); 8 | border-radius: 0 30px 30px 0; 9 | } 10 | 11 | .title { 12 | text-shadow: 0px 1px 0px #000; 13 | } 14 | 15 | .bg { 16 | position: absolute; 17 | width: 100%; 18 | bottom: 0px; 19 | left: 0px; 20 | background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .7)); 21 | } -------------------------------------------------------------------------------- /app/components/Vip/Help/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | 4 | export default ({helpers}) =>
5 |
6 | {helpers.map((n, i) =>
7 |
8 |
9 |
{n.title}
10 |
11 |
{n.content}
12 |
)} 13 |
-------------------------------------------------------------------------------- /app/reducers/order.js: -------------------------------------------------------------------------------- 1 | import { 2 | ORDER_REQUEST, ORDER_SUCCESS, ORDER_DONE 3 | } from "actions/order"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case ORDER_REQUEST: 8 | return Object.assign({}, state, { 9 | isFetching: true 10 | }) 11 | case ORDER_SUCCESS: 12 | return Object.assign({}, state, { 13 | isFetching: false, 14 | ...action.response 15 | }) 16 | default: 17 | return state 18 | } 19 | } -------------------------------------------------------------------------------- /app/components/Buying/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import Header from './Header'; 3 | import List from "../List"; 4 | export default class extends PureComponent { 5 | render() { 6 | const {buying, onSwitch, loadBuyings} = this.props 7 | return
8 |
9 | 14 |
15 | } 16 | } -------------------------------------------------------------------------------- /app/store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, combineReducers, createStore} from "redux"; 2 | import thunkMiddleware from "redux-thunk"; 3 | import {routerReducer} from "react-router-redux"; 4 | import api from "../middleware/api"; 5 | import * as rootReducer from "../reducers"; 6 | 7 | export default (initialState) => { 8 | return createStore( 9 | combineReducers({ 10 | ...rootReducer, 11 | routing: routerReducer 12 | }), 13 | initialState, 14 | applyMiddleware(thunkMiddleware, api) 15 | ) 16 | } -------------------------------------------------------------------------------- /app/components/Shop/Detail/style.scss: -------------------------------------------------------------------------------- 1 | .head { 2 | position: relative; 3 | } 4 | 5 | .hot { 6 | position: absolute; 7 | bottom: 20px; 8 | right: 20px; 9 | } 10 | 11 | .animat { 12 | position: absolute; 13 | white-space: nowrap; 14 | width: auto; 15 | animation-name: marquee_left; 16 | animation-timing-function: linear; 17 | animation-iteration-count: infinite; 18 | } 19 | 20 | @keyframes marquee_left { 21 | 0% { 22 | transform: translateX(470px); 23 | } 24 | 100% { 25 | transform: translateX(-100%); 26 | } 27 | } -------------------------------------------------------------------------------- /app/components/Lottery/Record/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .tips { 2 | position: absolute; 3 | top: 0px; 4 | left: 0px; 5 | border-bottom-right-radius: 10px; 6 | } 7 | 8 | .box { 9 | position: relative; 10 | border-right: 3px dotted #e1e1e1; 11 | &:before, &:after { 12 | width: 20px; 13 | height: 20px; 14 | content: ''; 15 | border-radius: 50%; 16 | background: #f2f3f5; 17 | position: absolute; 18 | right: -10px; 19 | } 20 | &:before { 21 | top: -10px; 22 | } 23 | &:after { 24 | bottom: -10px; 25 | } 26 | } -------------------------------------------------------------------------------- /app/actions/order.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from '../middleware/api' 2 | export const ORDER_REQUEST = 'ORDER_REQUEST' 3 | export const ORDER_SUCCESS = 'ORDER_SUCCESS' 4 | export const ORDER_DONE = 'ORDER_DONE' 5 | 6 | export const submitOrder = (endpoint, params) => ({ 7 | [CALL_API]: { 8 | types: [ORDER_REQUEST, ORDER_SUCCESS, ORDER_DONE], 9 | endpoint: endpoint, 10 | body: params, 11 | method: 'POST' 12 | } 13 | }) 14 | export const doneOrder = (order_id, status) => ({ 15 | type: ORDER_DONE, 16 | order_id: order_id, 17 | status: status 18 | }) -------------------------------------------------------------------------------- /app/actions/cart/buying.js: -------------------------------------------------------------------------------- 1 | export const CART_BUYING_ADD_DETAIL = 'CART_BUYING_ADD_DETAIL' 2 | export const CART_BUYING_SET_ADDRESS = 'CART_BUYING_SET_ADDRESS' 3 | export const CART_BUYING_SET_QUANTITY = 'CART_BUYING_SET_QUANTITY' 4 | 5 | export const setDetail = (buying) => ({ 6 | type: CART_BUYING_ADD_DETAIL, 7 | buying: buying 8 | }) 9 | export const setQuantity = (quantity) => ({ 10 | type: CART_BUYING_SET_QUANTITY, 11 | quantity: quantity 12 | }) 13 | export const setAddress = (address) => ({ 14 | type: CART_BUYING_SET_ADDRESS, 15 | address: address 16 | }) -------------------------------------------------------------------------------- /app/components/Shop/Cashier/style.scss: -------------------------------------------------------------------------------- 1 | .keyBtn { 2 | border: 1px solid #e5e5e5; 3 | border-width: 1px 1px 0 0; 4 | } 5 | 6 | .backspaceBtn { 7 | border-top: 1px solid #e5e5e5; 8 | } 9 | 10 | .payBtn { 11 | background-color: rgb(102, 210, 99); 12 | } 13 | 14 | .inputFocus { 15 | border-right-width: 3px; 16 | border-right-style: solid; 17 | animation: inputSplit 1s infinite; 18 | } 19 | 20 | @keyframes inputSplit { 21 | from { 22 | border-color: rgba(27, 188, 155, .0); 23 | } 24 | to { 25 | border-color: rgba(27, 188, 155, 1); 26 | } 27 | } -------------------------------------------------------------------------------- /app/components/User/Address/List/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import List from 'components/List'; 3 | import Item from './Item'; 4 | 5 | export default ({address, fetchAddresses, isManage, onEdit, onDelete, onRowClick}) => } 17 | /> -------------------------------------------------------------------------------- /client/entry.prod.js: -------------------------------------------------------------------------------- 1 | import "babel-polyfill"; 2 | import React from "react"; 3 | import {render} from "react-dom"; 4 | import {browserHistory} from "react-router"; 5 | import {syncHistoryWithStore} from "react-router-redux"; 6 | import Root from "../app/containers/Root"; 7 | import configureStore from "../app/store/configureStore"; 8 | 9 | import "react-fastclick"; 10 | 11 | const store=configureStore(window.__INITIAL_STATE__) 12 | const history=syncHistoryWithStore(browserHistory,store) 13 | render(,document.getElementById('app')) 14 | -------------------------------------------------------------------------------- /app/components/Header/SearchBar/style.scss: -------------------------------------------------------------------------------- 1 | .wrap { 2 | position: relative; 3 | min-height: 100px; 4 | } 5 | 6 | .header { 7 | position: absolute; 8 | left: 0; 9 | top: 0; 10 | width: 100%; 11 | z-index: 1; 12 | } 13 | 14 | .inputBox { 15 | border-radius: 30px; 16 | background: rgba(255, 255, 255, 0.4) 17 | } 18 | 19 | .linear { 20 | @extend .header; 21 | height: 100px; 22 | background: linear-gradient(rgba(0, 0, 0, .7), rgba(0, 0, 0, 0)); 23 | } 24 | 25 | .bg { 26 | background-color: transparent; 27 | } 28 | 29 | .pr { 30 | padding-right: 140px; 31 | } -------------------------------------------------------------------------------- /app/containers/User/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import {wx} from 'utils'; 4 | 5 | @connect(state => ({ 6 | user: state.user 7 | })) 8 | export default class extends PureComponent { 9 | componentDidMount() { 10 | const {user} = this.props 11 | wx.setShare({ 12 | title: user.nickname, 13 | imgUrl: user.avatar, 14 | link: `${location.protocol}//${location.host}/user` 15 | }) 16 | } 17 | 18 | render() { 19 | const {children} = this.props 20 | return children 21 | } 22 | } -------------------------------------------------------------------------------- /assets/scss/_color.scss: -------------------------------------------------------------------------------- 1 | $color0:#1bbc9b; 2 | $color1:#3eb9ff; 3 | $color2:#ff635c; 4 | $color3:#666; 5 | $color4:#999; 6 | $color5:#ff5b6c; 7 | $color6:#333; 8 | $color7:#ff9501; 9 | $color8:#34b234; 10 | $color9:#c184e3; 11 | $color10:#fff; 12 | $color11:#ef3a3a; 13 | $color12:#eff4f7; 14 | $color13:#ff7200; 15 | $color14:#989898; 16 | $color15:#cfae68; 17 | $color16:#282828; 18 | $color17:#cbaa63; 19 | $color18:#2c3242; 20 | $color19:#eecd60; 21 | $color20:#ffe8b4; 22 | $color21:#d5353d; 23 | $color22:#a14330; 24 | $color23:#ff8016; 25 | $color24:#87d34f; 26 | $color25:#ff5500; 27 | 28 | -------------------------------------------------------------------------------- /app/components/Shop/Home/Hot/style.scss: -------------------------------------------------------------------------------- 1 | .wrap { 2 | width: auto; 3 | white-space: nowrap; 4 | overflow-x: auto; 5 | } 6 | 7 | .item { 8 | position: relative; 9 | display: inline-block; 10 | vertical-align: top; 11 | } 12 | 13 | .label { 14 | position: absolute; 15 | top: -10px; 16 | left: 30px; 17 | } 18 | 19 | .labelArrow { 20 | position: absolute; 21 | top: 0; 22 | right: -10px; 23 | width: 0; 24 | height: 0; 25 | border-width: 5px; 26 | border-style: solid; 27 | border-color: #f0393b; 28 | border-top-color: transparent; 29 | border-right-color: transparent; 30 | } -------------------------------------------------------------------------------- /app/containers/Root.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import PropTypes from 'prop-types'; 3 | import {Provider} from "react-redux"; 4 | import routes from "../routes"; 5 | import {Router} from "react-router"; 6 | 7 | export default class extends PureComponent { 8 | static propTypes = { 9 | store: PropTypes.object.isRequired, 10 | history: PropTypes.object.isRequired 11 | } 12 | 13 | render() { 14 | const {store, history} = this.props 15 | return 16 | 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/actions/lottery.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from '../middleware/api' 2 | 3 | export const LOTTERY_ONFIG_REQUEST = 'LOTTERY_CONFIG_REQUEST' 4 | export const LOTTERY_CONFIG_SUCCESS = 'LOTTERY_CONFIG_SUCCESS' 5 | export const LOTTERY_CONFIG_FAIL = 'LOTTERY_CONFIG_FAIL' 6 | 7 | export const getConfig = () => { 8 | return { 9 | type: LOTTERY_CONFIG_SUCCESS, 10 | response: { 11 | rule: '活动规则活动规则活动规则活动规则活动规则', 12 | } 13 | } 14 | return { 15 | [CALL_API]: { 16 | types: [LOTTERY_CONFIG_REQUEST, LOTTERY_CONFIG_SUCCESS, LOTTERY_CONFIG_FAIL], 17 | endpoint: `/lottery/config`, 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /app/components/User/Home/style.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | background-position: center; 3 | background-repeat: no-repeat; 4 | background-size: auto 80%; 5 | } 6 | 7 | .badge { 8 | position: absolute; 9 | top: -10px; 10 | left: 50%; 11 | width: 28px; 12 | height: 28px; 13 | font-size: 18px; 14 | line-height: 28px; 15 | text-align: center; 16 | border: 1px solid #ff9501; 17 | } 18 | 19 | .fw { 20 | font-weight: 500; 21 | } 22 | 23 | .newmessage { 24 | background-color: #f4333c; 25 | border: 2px solid white 26 | } 27 | 28 | .border { 29 | border: 1px solid; 30 | border-color: rgba(255, 255, 255, 0.6); 31 | 32 | } -------------------------------------------------------------------------------- /app/components/Gift/Home/News/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import s from './style.scss'; 3 | export default ({news}) => { 4 | return
5 |
6 | 7 |
8 |
9 |
10 |
11 | {news.join('  ')} 12 |
13 |
14 |
15 |
16 | } -------------------------------------------------------------------------------- /app/components/Weal/Coupon/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .tips { 2 | position: absolute; 3 | top: 0px; 4 | left: 0px; 5 | border-bottom-right-radius: 10px; 6 | } 7 | 8 | .box { 9 | position: relative; 10 | border-right: 3px dotted #e1e1e1; 11 | &:before, &:after { 12 | width: 20px; 13 | height: 20px; 14 | content: ''; 15 | border-radius: 50%; 16 | background: #f2f3f5; 17 | position: absolute; 18 | right: -10px; 19 | } 20 | &:before { 21 | top: -10px; 22 | } 23 | &:after { 24 | bottom: -10px; 25 | } 26 | } 27 | 28 | .height { 29 | height: 210px; 30 | } 31 | 32 | .title { 33 | height: 85px; 34 | } -------------------------------------------------------------------------------- /app/containers/Shop/Branch/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {setTitle} from 'utils'; 3 | import ShopBranch from 'components/Shop/Branch' 4 | 5 | export default class extends PureComponent { 6 | state = { 7 | shops: [] 8 | } 9 | 10 | componentDidMount() { 11 | const {location} = this.props 12 | setTitle(`商家分店`) 13 | if (location.state && location.state.shops) { 14 | this.setState({ 15 | shops: location.state.shops 16 | }) 17 | } 18 | } 19 | 20 | render() { 21 | const {shops} = this.state 22 | return 25 | } 26 | } -------------------------------------------------------------------------------- /app/reducers/index.js: -------------------------------------------------------------------------------- 1 | export navlink from './navlink' 2 | export user from './user' 3 | export notice from './notice' 4 | export link from './link' 5 | export category from './category' 6 | export shop from './shop' 7 | export coupon from './coupon' 8 | export gift from './gift' 9 | export filter from './filter' 10 | export buying from './buying' 11 | export rim from './rim' 12 | export cashier from './cashier' 13 | export focus from './focus' 14 | export config from './config' 15 | export cart from './cart' 16 | export vip from './vip' 17 | export redpacket from './redpacket' 18 | export lottery from './lottery' 19 | export order from './order' 20 | -------------------------------------------------------------------------------- /app/containers/Rim/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/rim', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/rim_:id', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Detail').default)) 14 | } 15 | }, 16 | { 17 | path: '/order_rim', 18 | getComponent(state, cb){ 19 | require.ensure([], require => cb(null, require('./Pay').default)) 20 | } 21 | }, 22 | ] 23 | } -------------------------------------------------------------------------------- /app/containers/Buying/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/buying', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/buying_:id', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Detail').default)) 14 | } 15 | }, 16 | { 17 | path: '/order_buying', 18 | getComponent(state, cb){ 19 | require.ensure([], require => cb(null, require('./Pay').default)) 20 | } 21 | }, 22 | ] 23 | } -------------------------------------------------------------------------------- /app/reducers/link.js: -------------------------------------------------------------------------------- 1 | import { 2 | LINKS_REQUEST, LINKS_SUCCESS 3 | } from "actions/link"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case LINKS_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case LINKS_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | isFetching: false, 17 | links: action.response.links || [], 18 | isFetched: true 19 | } 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, combineReducers, compose, createStore} from "redux"; 2 | import thunkMiddleware from "redux-thunk"; 3 | import {routerReducer} from "react-router-redux"; 4 | import api from "../middleware/api"; 5 | import * as rootReducer from "../reducers"; 6 | 7 | export default (initialState) => { 8 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 9 | return createStore( 10 | combineReducers({ 11 | ...rootReducer, 12 | routing: routerReducer 13 | }), 14 | initialState, 15 | composeEnhancers( 16 | applyMiddleware(thunkMiddleware, api) 17 | ) 18 | ) 19 | } -------------------------------------------------------------------------------- /app/reducers/focus.js: -------------------------------------------------------------------------------- 1 | import { 2 | FOCUSS_REQUEST, FOCUSS_SUCCESS 3 | } from "actions/focus"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case FOCUSS_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case FOCUSS_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | isFetching: false, 17 | focuss: action.response.focuss || [], 18 | isFetched: true 19 | } 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/Detail/style.scss: -------------------------------------------------------------------------------- 1 | .bg { 2 | background: #fff; 3 | &:before { 4 | width: 2000px; 5 | height: 1500px; 6 | content: ''; 7 | background: #e0e0e0; 8 | border-radius: 50%; 9 | position: absolute; 10 | top: 0%; 11 | left: 50%; 12 | transform: translate(-50%, -80%); 13 | } 14 | &:after { 15 | width: 1600px; 16 | height: 1200px; 17 | content: ''; 18 | background: #d75940; 19 | border-radius: 50%; 20 | position: absolute; 21 | top: 0%; 22 | left: 50%; 23 | transform: translate(-50%, -75.2%); 24 | } 25 | } 26 | 27 | .close { 28 | position: absolute; 29 | top: 0; 30 | right: 0; 31 | } -------------------------------------------------------------------------------- /app/reducers/notice.js: -------------------------------------------------------------------------------- 1 | import { 2 | NOTICES_REQUEST, NOTICES_SUCCESS 3 | } from "actions/notice"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case NOTICES_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case NOTICES_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | isFetching: false, 17 | notices: action.response.notices || [], 18 | isFetched: true 19 | } 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/actions/config.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from '../middleware/api' 2 | // import {cookie} from '../utils'; 3 | 4 | export const CONFIG_REQUEST = 'CONFIG_REQUEST' 5 | export const CONFIG_SUCCESS = 'CONFIG_SUCCESS' 6 | export const CONFIG_FAIL = 'CONFIG_FAIL' 7 | 8 | export const NOT_ANY_TIPS_FOLLOW = 'NOT_ANY_TIPS_FOLLOW' 9 | 10 | export const getConfig = () => ({ 11 | [CALL_API]: { 12 | types: [CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAIL], 13 | endpoint: `/config`, 14 | } 15 | }) 16 | export const notAnyTipsFollow = () => (dispatch, getState) => { 17 | // cookie.set('not_tips_remind_follow',true,Infinity); 18 | return dispatch({ 19 | type: NOT_ANY_TIPS_FOLLOW 20 | }) 21 | } -------------------------------------------------------------------------------- /app/components/User/Redpacket/Spend/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .tips { 2 | border-bottom-right-radius: 10px; 3 | } 4 | 5 | .btn { 6 | border: 3px solid #ff9501; 7 | height: 53px; 8 | } 9 | 10 | .icon { 11 | position: absolute; 12 | right: 0; 13 | top: 0; 14 | font-size: 140px; 15 | color: #b2b2b2; 16 | } 17 | 18 | .box { 19 | min-height: 200px; 20 | &:before { 21 | border: 4px solid rgba(225, 225, 225, 0.6); 22 | background: rgba(225, 225, 225, 0.15); 23 | border-radius: 50%; 24 | width: 200%; 25 | height: 300px; 26 | content: ''; 27 | position: absolute; 28 | top: 0; 29 | left: 50%; 30 | transform: translate(-50%, -85%); 31 | } 32 | } -------------------------------------------------------------------------------- /app/reducers/navlink.js: -------------------------------------------------------------------------------- 1 | import { 2 | NAVLINKS_REQUEST, NAVLINKS_SUCCESS 3 | } from "actions/navlink"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case NAVLINKS_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case NAVLINKS_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | isFetching: false, 17 | navlinks: action.response.navlinks || [], 18 | isFetched: true 19 | } 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/reducers/filter.js: -------------------------------------------------------------------------------- 1 | import { 2 | FILTERS_FAIL, FILTERS_REQUEST, FILTERS_SUCCESS 3 | } from "actions/filter"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case FILTERS_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case FILTERS_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | filters: action.response.filters || [], 17 | isFetching: false, 18 | isFetched: true 19 | } 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/reducers/cart/buying.js: -------------------------------------------------------------------------------- 1 | import { 2 | CART_BUYING_ADD_DETAIL, CART_BUYING_SET_ADDRESS, CART_BUYING_SET_QUANTITY 3 | } from "actions/cart/buying"; 4 | 5 | export default (state = { 6 | quantity: 1 7 | }, action) => { 8 | switch (action.type) { 9 | case CART_BUYING_ADD_DETAIL: 10 | return Object.assign({}, state, { 11 | detail: action.buying 12 | }) 13 | case CART_BUYING_SET_ADDRESS: 14 | return Object.assign({}, state, { 15 | address: action.address 16 | }) 17 | case CART_BUYING_SET_QUANTITY: 18 | return Object.assign({}, state, { 19 | quantity: action.quantity 20 | }) 21 | default: 22 | return state 23 | } 24 | } -------------------------------------------------------------------------------- /app/containers/Weal/Rule/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import WealRule from 'components/Weal/Rule'; 4 | import {getApi} from 'utils'; 5 | 6 | @connect() 7 | export default class extends PureComponent { 8 | state = { 9 | rule: {} 10 | } 11 | 12 | componentDidMount() { 13 | this.fetchHelp() 14 | } 15 | 16 | fetchHelp = () => { 17 | return getApi('/help/super_offer').then(response => { 18 | this.setState(state => ({ 19 | rule: response.data 20 | })) 21 | }) 22 | } 23 | 24 | render() { 25 | const {rule} = this.state 26 | return 29 | } 30 | } -------------------------------------------------------------------------------- /app/components/User/Address/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import Link from 'components/Link'; 4 | import List from '../List'; 5 | 6 | export default ({address, fetchAddresses, isManage, toggleManage, onEdit, onDelete, onRowClick}) =>
7 |
8 | 16 | 18 |
新增地址
19 | 20 |
-------------------------------------------------------------------------------- /app/containers/Boot.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import * as utils from "utils"; 4 | import Loading from 'components/Loading'; 5 | import {getConfig} from 'actions/config' 6 | @connect(state => ({ 7 | config: state.config 8 | }), { 9 | getConfig 10 | }) 11 | export default class extends PureComponent { 12 | componentDidMount() { 13 | const {getConfig} = this.props 14 | getConfig() 15 | utils.is_IOS && utils.setWxConfig() 16 | } 17 | 18 | render() { 19 | const {config, children} = this.props 20 | if (config.isFetching || !config.isFetched) { 21 | return 22 | } 23 | return children 24 | } 25 | } -------------------------------------------------------------------------------- /app/components/Shop/Detail/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | export default ({shop, toggleFavor, onShowPhotos}) =>
6 |
9 | 10 |
}/> 11 | {shop.thumb && } 13 |
-------------------------------------------------------------------------------- /app/components/Weal/Redpacket/Modal/Open/style.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | background: #cd533e; 3 | width: 560px; 4 | height: 720px; 5 | &:before { 6 | width: 1000px; 7 | height: 1000px; 8 | content: ''; 9 | background: #d75940; 10 | border-radius: 50%; 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -90%); 15 | box-shadow: #bb3e27 2px 5px 10px; 16 | } 17 | } 18 | 19 | .rotate { 20 | animation: animt-rotate 1s infinite; 21 | } 22 | 23 | @keyframes animt-rotate { 24 | 0% { 25 | transform: rotateY(0deg); 26 | } 27 | 100% { 28 | transform: rotateY(360deg); 29 | 30 | } 31 | } 32 | 33 | .circle { 34 | border: 5px solid #dcbc83; 35 | } -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/Detail/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import p from 'assets/images/s.gif' 3 | import {setTitle, date_obj, getApi} from "utils"; 4 | export default ({user}) =>
5 |
6 | 7 |
8 |
9 |
{user.nickname}
10 |
{date_obj(user.created_at).first_time} {date_obj(user.created_at).secend_time}
12 |
13 |
{user.money}元
14 |
-------------------------------------------------------------------------------- /app/reducers/lottery.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from "redux"; 2 | import { 3 | LOTTERY_CONFIG_FAIL, LOTTERY_CONFIG_REQUEST, LOTTERY_CONFIG_SUCCESS 4 | } from "actions/lottery"; 5 | const config = (state = { 6 | isFetching: false, 7 | isFetchied: false 8 | }, action) => { 9 | switch (action.type) { 10 | case LOTTERY_CONFIG_REQUEST: 11 | return Object.assign({}, state, { 12 | isFetching: true 13 | }) 14 | case LOTTERY_CONFIG_SUCCESS: 15 | return Object.assign({}, state, { 16 | isFetching: false, 17 | isFetched: true, 18 | ...action.response 19 | }) 20 | default: 21 | return state 22 | } 23 | } 24 | 25 | export default combineReducers({ 26 | config, 27 | }) -------------------------------------------------------------------------------- /app/components/Lottery/Record/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import p from 'assets/images/s.gif'; 3 | 4 | export default ({record, onGetAward}) =>
5 | 6 |
7 |
{record.award.title}
8 |
{record.winning_time}
9 |
10 |
onGetAward(record)}> 12 | {record.is_get ? `已领取` : `立即领取`} 13 |
14 |
-------------------------------------------------------------------------------- /app/components/Gift/Home/VipGift/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | 6 | export default ({gifts, title}) =>
7 |
8 | {gifts.map((n, i) => 0 && s.split}`}> 10 | 11 |
{title ? n.title : `${n.fee}积分`}
12 | )} 13 |
14 |
15 | -------------------------------------------------------------------------------- /app/actions/rim.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | 3 | export const FETCH_RIMS_REQUEST = 'FETCH_RIMS_REQUEST' 4 | export const FETCH_RIMS_SUCCESS = 'FETCH_RIMS_SUCCESS' 5 | export const FETCH_RIMS_FAIL = 'FETCH_RIMS_FAIL' 6 | 7 | export const fetchRims = (filter) => (dispatch, getState) => { 8 | const {rim} = getState() 9 | filter = Object.assign(rim.filter, filter) 10 | if ((!rim.isMore && !filter.isRefreshing) || rim.isFetching) { 11 | return null 12 | } 13 | return dispatch({ 14 | isRefreshing: filter.isRefreshing, 15 | filter: filter, 16 | [CALL_API]: { 17 | types: [FETCH_RIMS_REQUEST, FETCH_RIMS_SUCCESS, FETCH_RIMS_FAIL], 18 | endpoint: `/rim`, 19 | body: filter 20 | } 21 | }) 22 | } -------------------------------------------------------------------------------- /app/components/Buying/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .head { 2 | position: relative; 3 | overflow: hidden; 4 | } 5 | 6 | .hot { 7 | position: absolute; 8 | bottom: 20px; 9 | right: 20px; 10 | } 11 | 12 | .time { 13 | position: absolute; 14 | bottom: 20px; 15 | left: 20px; 16 | } 17 | 18 | .stock { 19 | position: absolute; 20 | padding-left: 80px; 21 | padding-right: 80px; 22 | top: 20px; 23 | left: -85px; 24 | background-color: rgba(255, 85, 0, .7); 25 | transform: rotate(-45deg) 26 | } 27 | 28 | .bg { 29 | position: absolute; 30 | padding-top: 60px; 31 | width: 100%; 32 | bottom: 5px; 33 | left: 0px; 34 | background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .7)); 35 | } 36 | 37 | .fw { 38 | font-weight: 900; 39 | } -------------------------------------------------------------------------------- /app/containers/Lottery/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import {getConfig} from 'actions/lottery'; 4 | import Loading from 'components/Loading'; 5 | import {setTitle} from 'utils'; 6 | 7 | @connect(state => ({ 8 | config: state.config, 9 | lottery: state.lottery, 10 | }), { 11 | getConfig 12 | }) 13 | export default class extends PureComponent { 14 | componentDidMount() { 15 | const {config, getConfig} = this.props 16 | setTitle(config.siteConfig.sitename + '-抽奖') 17 | getConfig() 18 | } 19 | 20 | render() { 21 | const {lottery, children} = this.props 22 | if (lottery.config.isFetched) { 23 | return children 24 | } 25 | return 26 | } 27 | } -------------------------------------------------------------------------------- /app/reducers/category.js: -------------------------------------------------------------------------------- 1 | import { 2 | CATEGORYS_FAIL, CATEGORYS_REQUEST, CATEGORYS_SUCCESS 3 | } from "actions/category"; 4 | const initialState = {} 5 | export default (state = initialState, action) => { 6 | switch (action.type) { 7 | case CATEGORYS_REQUEST: 8 | return Object.assign({}, state, { 9 | [action.fullName]: { 10 | isFetching: true 11 | } 12 | }) 13 | case CATEGORYS_SUCCESS: 14 | return Object.assign({}, state, { 15 | [action.fullName]: { 16 | categorys: action.response.categorys || [], 17 | locals: action.response.locals || [], 18 | isFetching: false, 19 | isFetched: true 20 | } 21 | }) 22 | default: 23 | return state 24 | } 25 | } -------------------------------------------------------------------------------- /app/containers/User/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import UserHome from 'components/User/Home'; 4 | import {setTitle} from "utils"; 5 | import {sign} from 'actions/user'; 6 | 7 | @connect(state => ({ 8 | config: state.config, 9 | user: state.user 10 | }), { 11 | sign 12 | }) 13 | export default class extends PureComponent { 14 | componentDidMount() { 15 | const {config} = this.props 16 | setTitle(`${config.siteConfig.sitename}-个人中心`) 17 | } 18 | 19 | onSign = () => { 20 | const {sign} = this.props 21 | sign() 22 | } 23 | 24 | render() { 25 | const {user} = this.props 26 | return 30 | } 31 | } -------------------------------------------------------------------------------- /app/components/Gift/Home/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | 6 | export default ({gift, user}) => 7 |
8 | 9 |
10 |
{gift.title}
11 |
12 |
13 |
{gift.fee}积分
14 |
剩余{gift.stock}份
15 |
16 |
17 | -------------------------------------------------------------------------------- /app/containers/Shop/Join/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import Loading from 'components/Loading'; 4 | import ShopJoin from'components/Shop/Join'; 5 | import {setTitle} from 'utils'; 6 | import {getJoinConfig} from 'actions/shop'; 7 | 8 | @connect(state => ({ 9 | join: state.shop.join 10 | }), { 11 | getJoinConfig 12 | }) 13 | export default class index extends PureComponent { 14 | componentDidMount() { 15 | const {getJoinConfig} = this.props 16 | setTitle(`商家入驻`) 17 | getJoinConfig() 18 | } 19 | 20 | render() { 21 | const {join} = this.props 22 | if (!join.config.isFetched) { 23 | return 24 | } 25 | return 28 | } 29 | } -------------------------------------------------------------------------------- /app/components/Gift/Home/Card/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Loading from 'components/Loading'; 3 | 4 | export default ({user, config, onSign}) =>
5 | 6 |
7 |
{user.credit}积分
8 |
今日签到可领取{config.credit.sign}积分
9 |
10 |
11 |
12 | {user.signFetching && } 13 | {!user.signFetching && (user.is_sign ? '已签到' : '签到')} 14 |
15 |
16 |
-------------------------------------------------------------------------------- /app/containers/Weal/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/weal', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/weal_rule', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Rule').default)) 14 | } 15 | }, 16 | { 17 | path: '/weal_redpacket_:id', 18 | getComponent(state, cb){ 19 | require.ensure([], require => cb(null, require('./Redpacket').default)) 20 | } 21 | }, 22 | //兼容3.0路径 23 | { 24 | path: '/coupon', 25 | onEnter: ({params}, replace) => replace(`/weal?type=1`) 26 | }, 27 | ] 28 | } -------------------------------------------------------------------------------- /app/components/Vip/Home/Header/style.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: absolute; 3 | background: linear-gradient(rgba(0, 0, 0, .7), rgba(0, 0, 0, 0)); 4 | } 5 | 6 | .headerBox { 7 | background-repeat: no-repeat; 8 | background-position: bottom; 9 | background-size: 100% auto; 10 | } 11 | 12 | .border { 13 | border-color: #f4f4f4; 14 | border-width: 2px; 15 | } 16 | 17 | .creditLabel { 18 | position: absolute; 19 | bottom: 0; 20 | left: 50%; 21 | transform: translate(-50%, 50%); 22 | border-width: 3px; 23 | } 24 | 25 | .model { 26 | position: fixed; 27 | top: 0px; 28 | left: 0px; 29 | width: 100vw; 30 | height: 100vh; 31 | z-index: 20; 32 | background-color: rgba(0, 0, 0, 0.7); 33 | } 34 | 35 | .button { 36 | border: 3px solid #fff; 37 | border-radius: 10px; 38 | 39 | } -------------------------------------------------------------------------------- /app/components/Shop/Branch/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from 'components/Link'; 3 | 4 | export default({shops}) =>
5 | {shops.map((n, i) =>
6 | 7 | {n.title &&
{n.title}
} 8 | {n.address &&
{n.address}
} 9 | {n.distance &&
{n.distance.number}{n.distance.unit}
} 10 | 11 | {n.tel && 12 | 13 | } 14 |
)} 15 |
16 | -------------------------------------------------------------------------------- /app/components/Vip/Level/style.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: absolute; 3 | background: linear-gradient(rgba(0, 0, 0, .7), rgba(0, 0, 0, 0)); 4 | } 5 | 6 | .headerBox { 7 | background-repeat: no-repeat; 8 | background-position: bottom; 9 | background-size: 100% auto; 10 | } 11 | 12 | .active { 13 | span { 14 | display: block; 15 | } 16 | &:after { 17 | display: block; 18 | position: absolute; 19 | left: 50%; 20 | top: -20px; 21 | transform: translateX(-50%); 22 | content: ''; 23 | height: 0px; 24 | width: 0px; 25 | border-left: 20px solid transparent; 26 | border-right: 20px solid transparent; 27 | border-bottom: 20px solid white; 28 | } 29 | } 30 | 31 | .border { 32 | border: 2px solid #dffff9; 33 | } 34 | 35 | .icons { 36 | margin-left: 0.8rem; 37 | } -------------------------------------------------------------------------------- /app/components/Buying/Home/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header' 3 | import s from './style.scss'; 4 | export default ({onSwitch, status}) =>
} 6 | > 7 |
8 |
onSwitch(2)}>进行中 10 |
11 |
onSwitch(1)}>待抢购 13 |
14 |
onSwitch(3)}>已结束 16 |
17 |
18 |
-------------------------------------------------------------------------------- /app/components/Link/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link} from 'react-router'; 3 | export default ({href, className, onClick, style, activeClassName, onlyActiveOnIndex, children}) => { 4 | let extAttr = {} 5 | className && (extAttr['className'] = className) 6 | onClick && (extAttr['onClick'] = onClick) 7 | style && (extAttr['style'] = style) 8 | activeClassName && (extAttr['activeClassName'] = activeClassName) 9 | onlyActiveOnIndex && (extAttr['onlyActiveOnIndex'] = true) 10 | if (typeof(href) == 'string') { 11 | if (/^(http:\/\/|https:\/\/|tel:)/.test(href)) { 12 | return {children} 13 | } else { 14 | return {children} 15 | } 16 | } 17 | return {children} 18 | } -------------------------------------------------------------------------------- /app/components/Pay/Success/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import {Button} from 'antd-mobile'; 4 | import s from './style.scss'; 5 | 6 | export default ({result, onFinish, activity, onClickCoupon}) =>
7 |
8 | 9 |
支付成功
10 |
11 | {result.credit &&
12 |
获得积分
13 |
{result.credit}
14 |
} 15 |
16 |
17 | 18 |
19 |
-------------------------------------------------------------------------------- /app/containers/Gift/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/gift', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/gift_list', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./List').default)) 14 | } 15 | }, 16 | { 17 | path: '/gift_rule', 18 | getComponent(state, cb){ 19 | require.ensure([], require => cb(null, require('./Rule').default)) 20 | } 21 | }, 22 | { 23 | path: '/gift_:id', 24 | getComponent(state, cb){ 25 | require.ensure([], require => cb(null, require('./Detail').default)) 26 | } 27 | }, 28 | ] 29 | } -------------------------------------------------------------------------------- /app/reducers/cashier.js: -------------------------------------------------------------------------------- 1 | import { 2 | CASHIER_DISCOUNT_FAIL, CASHIER_DISCOUNT_REQUEST, CASHIER_DISCOUNT_SUCCESS, 3 | CASHIER_SET, 4 | } from "actions/cashier"; 5 | const initialState = { 6 | shop_id: 0, 7 | total_fee: 0.0, 8 | cash_fee: 0.0, 9 | redpacket: null, 10 | discount: { 11 | coupons: [], 12 | redpackets: [] 13 | }, 14 | real_fee: 0.0 15 | } 16 | export default (state = initialState, action) => { 17 | switch (action.type) { 18 | case CASHIER_SET: 19 | return Object.assign({}, state, action) 20 | case CASHIER_DISCOUNT_SUCCESS: 21 | return Object.assign({}, state, { 22 | discount: Object.assign({}, state.discount, action.response.discount), 23 | cash_fee: parseFloat(action.response.discount.cash_fee) 24 | }) 25 | default: 26 | return state 27 | } 28 | } -------------------------------------------------------------------------------- /client/entry.dev.js: -------------------------------------------------------------------------------- 1 | import "babel-polyfill"; 2 | import React from "react"; 3 | import {render} from "react-dom"; 4 | import {browserHistory} from "react-router"; 5 | import {AppContainer} from "react-hot-loader"; 6 | import {syncHistoryWithStore} from "react-router-redux"; 7 | import Root from "../app/containers/Root"; 8 | import configureStore from "../app/store/configureStore"; 9 | 10 | import "react-fastclick"; 11 | 12 | const store=configureStore(window.__INITIAL_STATE__) 13 | const history=syncHistoryWithStore(browserHistory,store) 14 | render(,document.getElementById('app')) 15 | if(module.hot){ 16 | module.hot.accept('../app/containers/Root',() =>{ 17 | render(,document.getElementById('app')) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /app/components/TabBar/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import "antd-mobile/lib/tab-bar/style/index.css" 4 | import s from './style.scss' 5 | 6 | export default ({navs}) =>
7 |
8 |
9 | {navs.map((n, i) => 11 |
12 |
13 | 14 |
15 |

{n.title}

16 |
17 | )} 18 |
19 |
20 |
-------------------------------------------------------------------------------- /app/components/Gift/Home/News/style.scss: -------------------------------------------------------------------------------- 1 | .wrap:before { 2 | display: block; 3 | content: ''; 4 | position: absolute; 5 | left: 0; 6 | top: 0; 7 | height: 100%; 8 | width: 60px; 9 | background: linear-gradient(to right, rgba(255, 255, 255, .8), rgba(255, 255, 255, 0)); 10 | z-index: 1; 11 | } 12 | 13 | .wrap:after { 14 | @extend .wrap:before; 15 | left: auto; 16 | right: 0; 17 | transform: rotate(180deg); 18 | } 19 | 20 | .marquee { 21 | position: absolute; 22 | top: 50%; 23 | left: 0; 24 | } 25 | 26 | .animat { 27 | composes: marquee; 28 | animation-name: marquee_left; 29 | animation-timing-function: linear; 30 | animation-iteration-count: infinite; 31 | } 32 | 33 | @keyframes marquee_left { 34 | 0% { 35 | transform: translate(0, -50%); 36 | } 37 | 100% { 38 | transform: translate(-100%, -50%); 39 | } 40 | } -------------------------------------------------------------------------------- /app/components/Result/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {Result} from 'antd-mobile'; 4 | import Success from './Success'; 5 | import Fail from './Fail'; 6 | import Warn from './Warn'; 7 | export default class extends PureComponent { 8 | static contextTypes = { 9 | router: PropTypes.object.isRequired 10 | } 11 | static Success = Success 12 | static Fail = Fail 13 | static Warn = Warn 14 | 15 | render() { 16 | const {router} = this.context 17 | const {icon, title, message} = this.props 18 | return { 24 | history.state ? history.back() : router.push({ 25 | pathname: '/' 26 | }) 27 | }} 28 | /> 29 | } 30 | } -------------------------------------------------------------------------------- /app/components/Shop/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Header from 'components/Header'; 3 | import Filter from 'components/Filter'; 4 | import Hot from "./Hot"; 5 | import List from "../List"; 6 | 7 | export default ({shop, config, filter, onSearch, loadShops, onFilter}) =>
8 |
9 | 10 |
11 | {shop.hot && shop.hot.shops && shop.hot.shops.length > 0 && } 12 |
13 | { 14 | 15 | filter && filter.shop && filter.shop.filters && filter.shop.filters.length > 0 && 16 | 17 | } 18 | 23 |
-------------------------------------------------------------------------------- /app/components/User/Coupon/List/Item/style.scss: -------------------------------------------------------------------------------- 1 | .tips { 2 | border-bottom-right-radius: 10px; 3 | } 4 | 5 | .btn { 6 | border: 3px solid #ff9501; 7 | } 8 | 9 | .icon { 10 | position: absolute; 11 | right: 0; 12 | top: 0; 13 | font-size: 140px; 14 | color: #b2b2b2; 15 | } 16 | 17 | .box { 18 | position: relative; 19 | border-right: 3px dotted #e1e1e1; 20 | &:before, &:after { 21 | width: 20px; 22 | height: 20px; 23 | content: ''; 24 | border-radius: 50%; 25 | background: #f2f3f5; 26 | position: absolute; 27 | right: -10px; 28 | } 29 | &:before { 30 | top: -10px; 31 | } 32 | &:after { 33 | bottom: -10px; 34 | } 35 | } 36 | 37 | .box2 { 38 | position: relative; 39 | &:before { 40 | border-right: 5px dotted #f5f5f5; 41 | content: ''; 42 | position: absolute; 43 | top: 0; 44 | left: 0; 45 | bottom: 0; 46 | } 47 | } -------------------------------------------------------------------------------- /app/components/User/Order/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import List from '../List'; 4 | import s from './style.scss'; 5 | 6 | const statuses = { 7 | '0': '全部', 8 | '1': '待付款', 9 | '6': '未核销', 10 | '3': '已完成', 11 | '7': '待评价', 12 | } 13 | 14 | export default ({order, fetchOrders, onSwitch, onPay, onComment}) =>
15 |
16 |
17 | {Object.keys(statuses).map((n, i) =>
onSwitch(n)}> 19 |
{statuses[n]}
20 |
)} 21 |
22 | 23 |
-------------------------------------------------------------------------------- /app/components/Home/Modal/Redpacket/style.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 6rem; 3 | position: absolute; 4 | top: 50%; 5 | left: 50%; 6 | transform: translate(-50%, -50%); 7 | } 8 | 9 | .roottop { 10 | background: url("http://public.duduapp.net/duLifeImg/redpackbg.png") no-repeat; 11 | background-size: 100% 100%; 12 | width: 100%; 13 | height: 2.8rem; 14 | margin-bottom: -1px; 15 | } 16 | 17 | .radius { 18 | border-radius: 0 0 0.2rem 0.2rem; 19 | } 20 | 21 | .rootmain { 22 | background: #fff6dc; 23 | border-radius: 0.1rem; 24 | max-height: 4.8rem; 25 | overflow-y: auto; 26 | } 27 | 28 | .rootlist { 29 | border-radius: 4px; 30 | margin-bottom: 0.2rem; 31 | &:last-child { 32 | margin-bottom: 0; 33 | } 34 | } 35 | 36 | .icon { 37 | width: 60px; 38 | height: 60px; 39 | line-height: 60px; 40 | border: 2px solid #fff; 41 | border-radius: 50%; 42 | display: block; 43 | } 44 | -------------------------------------------------------------------------------- /app/actions/link.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const LINKS_REQUEST = 'LINKS_REQUEST' 5 | export const LINKS_SUCCESS = 'LINKS_SUCCESS' 6 | export const LINKS_FAIL = 'LINKS_FAIL' 7 | 8 | export const getLinks = (fullName) => (dispatch, getState) => { 9 | const repo = getState().link[fullName] 10 | if (repo && repo.isFetched) { 11 | return null 12 | } 13 | cache.get(`${fullName}_links`) && dispatch({ 14 | type: LINKS_SUCCESS, 15 | response: cache.get(`${fullName}_links`), 16 | fullName: fullName 17 | }) 18 | return dispatch({ 19 | [CALL_API]: { 20 | types: [LINKS_REQUEST, LINKS_SUCCESS, LINKS_FAIL], 21 | endpoint: `/link` 22 | }, 23 | fullName: fullName 24 | }).then(action => { 25 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_links`, action.response) 26 | return action 27 | }) 28 | } -------------------------------------------------------------------------------- /app/components/Shop/Detail/Coupon/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import moment from 'moment'; 3 | import s from './style.scss'; 4 | import {number_format} from 'utils'; 5 | 6 | export default ({coupon, onClickCoupon}) =>
onClickCoupon(coupon)}> 9 |
10 | {coupon.type != 2 &&
} 11 |
{number_format(coupon.value)}
12 | {coupon.type == 2 &&
} 13 |
14 |
15 | {coupon.type != 2 &&
满{number_format(coupon.condition)}元使用
} 16 | {coupon.type == 2 &&
最高减免{number_format(coupon.condition)}
} 17 |
18 |
19 | {moment(coupon.use_end_time).format('YYYY.MM.DD')}前有效 20 |
21 |
-------------------------------------------------------------------------------- /app/actions/cashier.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | 3 | export const CASHIER_SET = 'CASHIER_SET' 4 | export const CASHIER_DISCOUNT_REQUEST = 'CASHIER_DISCOUNT_REQUEST' 5 | export const CASHIER_DISCOUNT_SUCCESS = 'CASHIER_DISCOUNT_SUCCESS' 6 | export const CASHIER_DISCOUNT_FAIL = 'CASHIER_DISCOUNT_FAIL' 7 | 8 | export const setCashier = (params) => ({ 9 | type: CASHIER_SET, 10 | ...params 11 | }) 12 | export const discount = (shopid, params, isShow = false) => ({ 13 | [CALL_API]: { 14 | types: [CASHIER_DISCOUNT_REQUEST, CASHIER_DISCOUNT_SUCCESS, CASHIER_DISCOUNT_FAIL], 15 | endpoint: `/shop/cashier/${shopid}/discount`, 16 | body: Object.assign({}, { 17 | fee: params.total_fee, 18 | real_fee: params.real_fee, 19 | redpacket_id: (isShow && params.redpacket) ? params.redpacket.id : 0, 20 | coupon_id: (isShow && params.coupon) ? params.coupon.id : 0, 21 | }), 22 | method: 'POST' 23 | } 24 | }) -------------------------------------------------------------------------------- /app/components/FollowTips/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | 5 | export default ({follow, onNotAnyTips}) =>
6 | 7 | {follow.follow_image && 8 | } 9 |
10 | {follow.follow_title &&
{follow.follow_title}
} 11 | {follow.follow_content &&
{follow.follow_content}
} 12 |
13 | {follow.follow_link && 关注} 15 |
16 |
-------------------------------------------------------------------------------- /app/reducers/config.js: -------------------------------------------------------------------------------- 1 | import { 2 | CONFIG_REQUEST, CONFIG_SUCCESS, 3 | NOT_ANY_TIPS_FOLLOW 4 | } from "actions/config"; 5 | import {cookie} from 'utils'; 6 | 7 | const initialState = {} 8 | export default (state = initialState, action) => { 9 | switch (action.type) { 10 | case CONFIG_REQUEST: 11 | return Object.assign({}, state, { 12 | isFetching: true 13 | }) 14 | case CONFIG_SUCCESS: 15 | if (cookie.get('not_tips_remind_follow')) { 16 | action.response.siteConfig.remind_follow = null 17 | } 18 | return Object.assign({}, state, { 19 | isFetching: false, 20 | isFetched: true, 21 | ...action.response 22 | }) 23 | case NOT_ANY_TIPS_FOLLOW: 24 | return Object.assign({}, state, { 25 | siteConfig: Object.assign({}, state.siteConfig, { 26 | remind_follow: null 27 | }) 28 | }) 29 | default: 30 | return state 31 | } 32 | } -------------------------------------------------------------------------------- /client/server.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import WebpackDevServer from 'webpack-dev-server'; 3 | import config from '../webpack.config'; 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true, 9 | // It suppress error shown in console, so it has to be set to false. 10 | quiet: false, 11 | // It suppress everything except error, so it has to be set to false as well 12 | // to see success build. 13 | noInfo: false, 14 | stats: { 15 | // Config for minimal console.log mess. 16 | assets: false, 17 | colors: true, 18 | version: false, 19 | hash: false, 20 | timings: false, 21 | chunks: false, 22 | chunkModules: false 23 | } 24 | }).listen(3333, 'localhost', function (err) { 25 | if (err) { 26 | console.log(err); 27 | } 28 | console.log('Listening at localhost:3333'); 29 | }); -------------------------------------------------------------------------------- /app/components/Home/VipBox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Carousel} from 'antd-mobile'; 3 | import Link from 'components/Link'; 4 | export default ({shops}) =>
5 |
6 | 开通特权 8 |
9 | 10 | { 11 | shops.filter(item => item.discount_info && item.discount_info.vip && item.discount_info.vip.scale < 10).map((n, i) => 12 | 13 |
{n.discount_info.vip.scale}折
14 |
{n.title}开启会员特权
15 | 16 | ) 17 | } 18 |
19 |
-------------------------------------------------------------------------------- /app/components/Shop/Home/Hot/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | export default ({shops}) =>
6 |
7 | 8 |
9 |
10 | {shops.map((n, i) => 11 | 12 |
{n.title}
13 |
15 | TOP {i + 1} 16 |
17 |
18 | )} 19 |
20 |
-------------------------------------------------------------------------------- /app/components/User/Address/Edit/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import {List, InputItem, TextareaItem, Button} from 'antd-mobile'; 4 | 5 | export default ({address, onAddressChange, onSave}) =>
6 |
7 | 8 | onAddressChange(value, 'realname')} 12 | >收货人 13 | onAddressChange(value, 'mobile')} 18 | >联系电话 19 | onAddressChange(value, 'address')} 26 | /> 27 | 28 |
29 | 30 |
31 |
-------------------------------------------------------------------------------- /app/containers/Home/Citys/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import City from 'components/City'; 3 | import {getApi, setTitle} from 'utils'; 4 | 5 | export default class extends PureComponent { 6 | state = { 7 | isFetching: false, 8 | isFetched: false, 9 | areas: [] 10 | } 11 | 12 | componentDidMount() { 13 | setTitle(`城市选择`) 14 | this.fetchCitys() 15 | } 16 | 17 | fetchCitys = () => { 18 | this.setState({ 19 | isFetching: true 20 | }, () => { 21 | return getApi('/city').then(response => { 22 | this.setState({ 23 | isFetching: false, 24 | isFetched: true, 25 | areas: response.records 26 | }) 27 | }) 28 | }) 29 | } 30 | onSearch = (keyword) => { 31 | 32 | } 33 | 34 | render() { 35 | const {isFetching, isFetched, areas} = this.state 36 | return 41 | } 42 | } -------------------------------------------------------------------------------- /app/actions/notice.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const NOTICES_REQUEST = 'NOTICES_REQUEST' 5 | export const NOTICES_SUCCESS = 'NOTICES_SUCCESS' 6 | export const NOTICES_FAIL = 'NOTICES_FAIL' 7 | 8 | export const getNotices = (fullName) => (dispatch, getState) => { 9 | const repo = getState().notice[fullName] 10 | if (repo && repo.isFetched) { 11 | return null 12 | } 13 | cache.get(`${fullName}_notices`) && dispatch({ 14 | type: NOTICES_SUCCESS, 15 | response: cache.get(`${fullName}_notices`), 16 | fullName: fullName 17 | }) 18 | const types = {index: 1} 19 | return dispatch({ 20 | [CALL_API]: { 21 | types: [NOTICES_REQUEST, NOTICES_SUCCESS, NOTICES_FAIL], 22 | endpoint: `/notice`, 23 | body: {typeid: types[fullName]} 24 | }, 25 | fullName: fullName 26 | }).then(action => { 27 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_notices`, action.response) 28 | return action 29 | }) 30 | } -------------------------------------------------------------------------------- /app/containers/Search/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import BuyingItem from 'components/Buying/List/Item'; 3 | import ShopItem from 'components/Shop/List/Item'; 4 | import {getApi} from 'utils'; 5 | 6 | export default class extends PureComponent { 7 | state = { 8 | buyings: [], 9 | shops: [] 10 | } 11 | 12 | componentDidMount() { 13 | this.fetchResult() 14 | } 15 | 16 | fetchResult = () => { 17 | const {location} = this.props 18 | getApi(`/search`, { 19 | keyword: location.query.keyword 20 | }).then(response => { 21 | this.setState(state => ({ 22 | buyings: response.buyings || [], 23 | shops: response.shops || [] 24 | })) 25 | }) 26 | } 27 | 28 | render() { 29 | const {shops, buyings} = this.state 30 | return
31 | {shops && shops.map((n, i) => )} 34 | {buyings && buyings.map((n, i) => )} 37 |
38 | } 39 | } -------------------------------------------------------------------------------- /app/actions/focus.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | export const FOCUSS_REQUEST = 'FOCUSS_REQUEST' 4 | export const FOCUSS_SUCCESS = 'FOCUSS_SUCCESS' 5 | export const FOCUSS_FAIL = 'FOCUSS_FAIL' 6 | 7 | export const getFocuss = (fullName) => (dispatch, getState) => { 8 | const repo = getState().focus[fullName] 9 | if (repo && repo.isFetched) { 10 | return null 11 | } 12 | cache.get(`${fullName}_focuss`) && dispatch({ 13 | type: FOCUSS_SUCCESS, 14 | response: cache.get(`${fullName}_focuss`), 15 | fullName: fullName 16 | }) 17 | const types = {index: 1, shop: 2, buying: 3} 18 | return dispatch({ 19 | [CALL_API]: { 20 | types: [FOCUSS_REQUEST, FOCUSS_SUCCESS, FOCUSS_FAIL], 21 | endpoint: `/focus`, 22 | body: { 23 | typeid: types[fullName] 24 | } 25 | }, 26 | fullName: fullName 27 | }).then(action => { 28 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_focuss`, action.response) 29 | return action 30 | }) 31 | } -------------------------------------------------------------------------------- /app/components/Category/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Carousel} from 'antd-mobile'; 3 | import Link from 'components/Link' 4 | import p from 'assets/images/s.gif'; 5 | const group = (categories, limit) => { 6 | return categories.map((item, index) => { 7 | return categories.slice(index * limit, index * limit + limit) 8 | }).filter(item => { 9 | return item.length > 0 10 | }) 11 | } 12 | export default ({categories, limit = 8}) => 1}> 14 | {group(categories, limit).map((item, index) =>
15 | {item.map((n, i) => 17 | 18 |
{n.title}
19 | )} 20 |
)} 21 |
-------------------------------------------------------------------------------- /app/reducers/gift.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_GIFTS_REQUEST, FETCH_GIFTS_SUCCESS, FETCH_GIFTS_FAIL, 3 | } from "actions/gift"; 4 | const initialState = { 5 | gifts: [], 6 | isFetching: false, 7 | isMore: true, 8 | isRefreshing: false, 9 | filter: { 10 | limit: 10 11 | } 12 | } 13 | export default (state = initialState, action) => { 14 | switch (action.type) { 15 | case FETCH_GIFTS_REQUEST: 16 | return Object.assign({}, state, { 17 | filter: action.filter, 18 | isFetching: true, 19 | isRefreshing: action.isRefreshing, 20 | }, action.isRefreshing ? { 21 | gifts: [] 22 | } : {}) 23 | case FETCH_GIFTS_SUCCESS: 24 | return Object.assign({}, state, { 25 | isFetching: false, 26 | isRefreshing: false, 27 | isMore: action.response.gifts && action.response.gifts.length >= action.filter.limit, 28 | gifts: action.isRefreshing ? action.response.gifts || [] : state.gifts.concat(action.response.gifts || []), 29 | }) 30 | default: 31 | return state 32 | } 33 | } -------------------------------------------------------------------------------- /app/components/Vip/Priviege/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import p from 'assets/images/s.gif'; 4 | 5 | export default ({priviege}) =>
6 |
7 |
8 |
9 | 10 |
11 |
{priviege.title}
12 |
{priviege.desc}
13 |
14 |
15 |
权益详情
16 |
{ 20 | return `:${b * 0.02}rem` 21 | }) 22 | }}/> 23 |
24 |
-------------------------------------------------------------------------------- /app/components/Rim/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import s from './style.scss'; 4 | 5 | export default ({rim}) => 6 |
7 |
8 |
9 | {rim.title} 10 |
11 |
12 | {rim.desc} 13 |
14 |
15 |
16 |
17 |
18 |
{rim.target}
19 |
{rim.time_start} 出发
20 |
21 |
{rim.adult_price} 起 23 |
24 |
25 | -------------------------------------------------------------------------------- /app/actions/filter.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const FILTERS_REQUEST = 'FILTERS_REQUEST' 5 | export const FILTERS_SUCCESS = 'FILTERS_SUCCESS' 6 | export const FILTERS_FAIL = 'FILTERS_FAIL' 7 | 8 | export const getFilters = (fullName = 'shop') => (dispatch, getState) => { 9 | const repo = getState().filter[fullName] 10 | if (repo && repo.isFetched) { 11 | return null 12 | } 13 | cache.get(`${fullName}_filters`) && dispatch({ 14 | type: FILTERS_SUCCESS, 15 | response: cache.get(`${fullName}_filters`), 16 | fullName: fullName 17 | }) 18 | const types = {shop: 1, coupon: 2, gift: 5} 19 | return dispatch({ 20 | [CALL_API]: { 21 | types: [FILTERS_REQUEST, FILTERS_SUCCESS, FILTERS_FAIL], 22 | endpoint: `/filter`, 23 | body: { 24 | typeid: types[fullName] 25 | } 26 | }, 27 | fullName: fullName 28 | }).then(action => { 29 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_filters`, action.response) 30 | return action 31 | }) 32 | } -------------------------------------------------------------------------------- /app/actions/navlink.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const NAVLINKS_REQUEST = 'NAVLINKS_REQUEST' 5 | export const NAVLINKS_SUCCESS = 'NAVLINKS_SUCCESS' 6 | export const NAVLINKS_FAIL = 'NAVLINKS_FAIL' 7 | 8 | export const getNavLinks = (fullName) => (dispatch, getState) => { 9 | const repo = getState().navlink[fullName] 10 | if (repo && repo.isFetched) { 11 | return null 12 | } 13 | cache.get(`${fullName}_navlinks`) && dispatch({ 14 | type: NAVLINKS_SUCCESS, 15 | response: cache.get(`${fullName}_navlinks`), 16 | fullName: fullName 17 | }) 18 | const types = {index: 1, rim: 4} 19 | return dispatch({ 20 | [CALL_API]: { 21 | types: [NAVLINKS_REQUEST, NAVLINKS_SUCCESS, NAVLINKS_FAIL], 22 | endpoint: `/navlink`, 23 | body: { 24 | typeid: types[fullName] 25 | } 26 | }, 27 | fullName: fullName 28 | }).then(action => { 29 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_navlinks`, action.response) 30 | return action 31 | }) 32 | } -------------------------------------------------------------------------------- /app/components/User/Address/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {SwipeAction} from 'antd-mobile'; 3 | 4 | export default ({address, isManage, onEdit, onDelete, onRowClick}) =>
onRowClick(e, address)}> 6 | onEdit(e, address), 12 | style: {backgroundColor: '#ddd', color: 'white'}, 13 | }, 14 | { 15 | text: '删除', 16 | onPress: e => onDelete(e, address), 17 | style: {backgroundColor: '#F4333C', color: 'white'}, 18 | }, 19 | ]} 20 | > 21 |
22 |
23 |
{address.realname}
24 |
{address.mobile}
25 |
26 |
{address.address}
27 |
28 |
29 |
-------------------------------------------------------------------------------- /app/actions/category.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const CATEGORYS_REQUEST = 'CATEGORYS_REQUEST' 5 | export const CATEGORYS_SUCCESS = 'CATEGORYS_SUCCESS' 6 | export const CATEGORYS_FAIL = 'CATEGORYS_FAIL' 7 | 8 | export const getCategorys = (fullName) => (dispatch, getState) => { 9 | const repo = getState().category[fullName] 10 | if (repo && repo.isFetched) { 11 | return null 12 | } 13 | cache.get(`${fullName}_categorys`) && dispatch({ 14 | type: CATEGORYS_SUCCESS, 15 | response: cache.get(`${fullName}_categorys`), 16 | fullName: fullName 17 | }) 18 | const types = {coupon: 2, rim: 3} 19 | return dispatch({ 20 | [CALL_API]: { 21 | types: [CATEGORYS_REQUEST, CATEGORYS_SUCCESS, CATEGORYS_FAIL], 22 | endpoint: `/category`, 23 | body: { 24 | typeid: types[fullName] 25 | } 26 | }, 27 | fullName: fullName 28 | }).then(action => { 29 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_categorys`, action.response) 30 | return action 31 | }) 32 | } -------------------------------------------------------------------------------- /app/actions/gift.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | 3 | export const GIFT_REQUEST = 'GIFT_REQUEST' 4 | export const GIFT_SUCCESS = 'GIFT_SUCCESS' 5 | export const GIFT_FAIL = 'GIFT_FAIL' 6 | 7 | export const FETCH_GIFTS_REQUEST = 'FETCH_GIFTS_REQUEST' 8 | export const FETCH_GIFTS_SUCCESS = 'FETCH_GIFTS_SUCCESS' 9 | export const FETCH_GIFTS_FAIL = 'FETCH_GIFTS_FAIL' 10 | 11 | export const getGifts = (filter = {}) => ({ 12 | [CALL_API]: { 13 | types: [GIFTS_REQUEST, GIFTS_SUCCESS, GIFTS_FAIL], 14 | endpoint: `/gift`, 15 | body: filter 16 | } 17 | }) 18 | 19 | export const fetchGifts = (filter) => (dispatch, getState) => { 20 | const {gift} = getState() 21 | filter = Object.assign(gift.filter, filter) 22 | if ((!gift.isMore && !filter.isRefreshing) || gift.isFetching) { 23 | return null 24 | } 25 | return dispatch({ 26 | isRefreshing: filter.isRefreshing, 27 | filter: filter, 28 | [CALL_API]: { 29 | types: [FETCH_GIFTS_REQUEST, FETCH_GIFTS_SUCCESS, FETCH_GIFTS_FAIL], 30 | endpoint: `/gift`, 31 | body: filter 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /app/containers/User/About/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import Header from "components/Header"; 4 | import Loading from "components/Loading"; 5 | import {getApi, setTitle} from "utils"; 6 | 7 | @connect(state => ({ 8 | config: state.config 9 | })) 10 | export default class extends PureComponent { 11 | state = { 12 | isLoading: true, 13 | aboutus: '' 14 | } 15 | 16 | componentDidMount() { 17 | const {config} = this.props 18 | setTitle(`${config.siteConfig.sitename}-关于我们`) 19 | getApi(`/info`).then(response => { 20 | if (response.code == 'SUCCESS') { 21 | this.setState({ 22 | aboutus: response.aboutus || '', 23 | isLoading: false 24 | }) 25 | } 26 | }) 27 | } 28 | 29 | render() { 30 | const {aboutus, isLoading} = this.state 31 | return
32 |
33 | {aboutus &&
} 34 | {isLoading && } 35 |
36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/reducers/rim.js: -------------------------------------------------------------------------------- 1 | import { 2 | RIM_REQUEST, RIM_SUCCESS, RIM_FAIL, 3 | RIMS_REQUEST, RIMS_SUCCESS, RIMS_FAIL, 4 | FETCH_RIMS_REQUEST, FETCH_RIMS_SUCCESS, FETCH_RIMS_FAIL, 5 | } from 'actions/rim'; 6 | const initialState = { 7 | rims: [], 8 | isFetching: false, 9 | isMore: true, 10 | isRefreshing: false, 11 | filter: { 12 | limit: 10 13 | } 14 | } 15 | export default (state = initialState, action) => { 16 | switch (action.type) { 17 | case FETCH_RIMS_REQUEST: 18 | return Object.assign({}, state, { 19 | filter: action.filter, 20 | isFetching: true, 21 | isRefreshing: action.isRefreshing, 22 | }, action.isRefreshing ? { 23 | rims: [] 24 | } : {}) 25 | case FETCH_RIMS_SUCCESS: 26 | return Object.assign({}, state, { 27 | isFetching: false, 28 | isRefreshing: false, 29 | isMore: action.response.rims && action.response.rims.length >= action.filter.limit, 30 | rims: action.isRefreshing ? action.response.rims || [] : state.rims.concat(action.response.rims || []), 31 | }) 32 | default: 33 | return state 34 | } 35 | } -------------------------------------------------------------------------------- /app/components/Swiper/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Carousel} from 'antd-mobile'; 3 | import Link from 'components/Link' 4 | import p from 'assets/images/s.gif'; 5 | 6 | export default ({imgs}) => 1} dragging={imgs.length > 1} swiping={imgs.length > 1} 7 | infinite dots={imgs.length > 1}> 8 | {imgs.map((n, i) =>
14 |
)} 15 |
-------------------------------------------------------------------------------- /app/actions/buying.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | 3 | export const BUYINGS_REQUEST = 'BUYINGS_REQUEST' 4 | export const BUYINGS_SUCCESS = 'BUYINGS_SUCCESS' 5 | export const BUYINGS_FAIL = 'BUYINGS_FAIL' 6 | 7 | export const FETCH_BUYINGS_REQUEST = 'FETCH_BUYINGS_REQUEST' 8 | export const FETCH_BUYINGS_SUCCESS = 'FETCH_BUYINGS_SUCCESS' 9 | export const FETCH_BUYINGS_FAIL = 'FETCH_BUYINGS_FAIL' 10 | 11 | export const getBuyings = (filter = {}) => ({ 12 | [CALL_API]: { 13 | types: [BUYINGS_REQUEST, BUYINGS_SUCCESS, BUYINGS_FAIL], 14 | endpoint: `/buying`, 15 | body: filter 16 | } 17 | }) 18 | export const fetchBuyings = (filter) => (dispatch, getState) => { 19 | const {buying} = getState() 20 | filter = Object.assign(buying.filter, filter) 21 | if ((!buying.isMore && !filter.isRefreshing) || buying.isFetching) { 22 | return null 23 | } 24 | return dispatch({ 25 | isRefreshing: filter.isRefreshing, 26 | filter: filter, 27 | [CALL_API]: { 28 | types: [FETCH_BUYINGS_REQUEST, FETCH_BUYINGS_SUCCESS, FETCH_BUYINGS_FAIL], 29 | endpoint: `/buying`, 30 | body: filter 31 | } 32 | }) 33 | } -------------------------------------------------------------------------------- /app/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import {connect} from 'react-redux' 3 | import Loading from 'components/Loading'; 4 | import TabBar from 'components/TabBar'; 5 | import {getUser} from 'actions/user' 6 | 7 | import "assets/scss/fonts.scss"; 8 | import "assets/scss/antd.scss"; 9 | import "assets/scss/app.scss"; 10 | 11 | @connect(state => ({ 12 | user: state.user, 13 | config: state.config 14 | }), { 15 | getUser 16 | }) 17 | export default class extends PureComponent { 18 | componentDidMount() { 19 | const {getUser} = this.props 20 | getUser() 21 | } 22 | 23 | render() { 24 | const {config, user, children, location} = this.props 25 | if (user.isFetching || !user.isFetched) { 26 | return 27 | } 28 | if (user.auth == -1) { 29 | window.location.href = user.authUrl + '&backurl=' + encodeURIComponent(window.location.href) 30 | } 31 | if (user.auth == 1) { 32 | return
33 | {children} 34 | {config.navs.find((n) => n.link == location.pathname) && } 35 |
36 | } 37 | return 38 | } 39 | } -------------------------------------------------------------------------------- /app/components/Vip/Gift/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | 6 | export default ({gift}) => 7 |
8 |
9 | 10 |
11 |
{gift.title}
12 |
13 |
14 |
15 | 16 |
{gift.vip_info.fee > 0 ? `${gift.vip_info.fee}积分` : '免费兑换'}
17 |
18 |
19 |
20 |
{gift.fee}积分
21 | 22 |
剩余{gift.stock}份
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /app/containers/Buying/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import BuyingHome from 'components/Buying/Home'; 4 | import {fetchBuyings} from 'actions/buying'; 5 | import {setTitle, wx} from 'utils'; 6 | 7 | @connect((state) => ({ 8 | config: state.config, 9 | buying: state.buying 10 | }), { 11 | fetchBuyings 12 | }) 13 | export default class extends PureComponent { 14 | componentDidMount() { 15 | const {config} = this.props 16 | wx.setShare(config.shareConfig.buying) 17 | setTitle(config.siteConfig.sitename + '-大牌抢购') 18 | } 19 | 20 | loadBuyings = (e, refresh = false, filter = {}) => { 21 | const {buying, fetchBuyings} = this.props 22 | fetchBuyings({ 23 | ...filter, 24 | offset: refresh ? 0 : buying.buyings.length, 25 | isRefreshing: refresh 26 | }) 27 | } 28 | onSwitch = (status) => { 29 | this.loadBuyings(null, true, { 30 | typeid: status 31 | }) 32 | } 33 | 34 | render() { 35 | const {buying} = this.props 36 | return 41 | } 42 | } -------------------------------------------------------------------------------- /app/components/Shop/Join/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import {Carousel} from 'antd-mobile'; 5 | import s from './style.scss'; 6 | 7 | export default ({config}) =>
8 | 15 | {config.slides.map((n, i) =>
16 |
18 | {i == 0 && 19 |
20 | {config.agent_name}
} 21 |
22 |
)} 23 |
24 | 25 |
^
26 |
申请入驻
27 | 28 |
-------------------------------------------------------------------------------- /app/reducers/buying.js: -------------------------------------------------------------------------------- 1 | import { 2 | BUYING_REQUEST, BUYING_SUCCESS, BUYING_FAIL, 3 | BUYINGS_REQUEST, BUYINGS_SUCCESS, BUYINGS_FAIL, 4 | FETCH_BUYINGS_REQUEST, FETCH_BUYINGS_SUCCESS, FETCH_BUYINGS_FAIL, 5 | } from 'actions/buying'; 6 | const initialState = { 7 | buyings: [], 8 | isFetching: false, 9 | isMore: true, 10 | isRefreshing: false, 11 | filter: { 12 | typeid: 2, 13 | limit: 10 14 | } 15 | } 16 | export default (state = initialState, action) => { 17 | switch (action.type) { 18 | case FETCH_BUYINGS_REQUEST: 19 | return Object.assign({}, state, { 20 | filter: action.filter, 21 | isFetching: true, 22 | isRefreshing: action.isRefreshing, 23 | }, action.isRefreshing ? { 24 | buyings: [] 25 | } : {}) 26 | case FETCH_BUYINGS_SUCCESS: 27 | return Object.assign({}, state, { 28 | isFetching: false, 29 | isRefreshing: false, 30 | isMore: action.response.buyings && action.response.buyings.length >= action.filter.limit, 31 | buyings: action.isRefreshing ? action.response.buyings || [] : state.buyings.concat(action.response.buyings || []), 32 | }) 33 | default: 34 | return state 35 | } 36 | } -------------------------------------------------------------------------------- /app/components/User/Favorite/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import {Tabs} from 'antd-mobile'; 4 | import p from 'assets/images/s.gif'; 5 | import s from './style.scss'; 6 | 7 | export default ({sections}) => 8 | {sections && sections.map((item, index) => 12 | {item.items && item.items.map((n, i) =>
13 | 14 | {item.type == "shop" && 15 |
{n.title}
16 |
{n.tag}
17 |
{n.distance.number}{n.distance.unit}
18 | } 19 | {item.type == "buying" && 20 |
{n.title}
21 |
{n.price}
22 | } 23 |
)} 24 |
)} 25 |
-------------------------------------------------------------------------------- /app/components/Home/JoinBox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Carousel} from 'antd-mobile'; 3 | import Link from 'components/Link'; 4 | export default ({shops, limit = 2}) => { 5 | const group = shops.map((item, index) => { 6 | return shops.slice(index * limit, index * limit + limit) 7 | }).filter(item => { 8 | return item.length > 0 9 | }) 10 | return
11 |
12 | 13 |
14 | 1} dragging={false} swiping={false} infinite vertical 15 | className="flex-item pl30 pt10"> 16 | {group.map((item, index) =>
17 | {item.map((n, i) => 19 | 恭喜{n.title}入驻好店 20 | )} 21 |
)} 22 |
23 | 24 | 25 |
立即入驻
26 | 27 |
28 | } -------------------------------------------------------------------------------- /app/components/Rim/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import List from '../List'; 4 | 5 | export default ({config, category, rim, fetchRims, onSearch, onFilter}) =>
6 | 7 | {category && category.categorys && category.categorys.length > 0 && 8 |
9 | {category.categorys.map((n, i) =>
onFilter('cid', n.id)} key={i} 10 | className="flex-wrp flex-item flex-cell flex-center"> 11 | 12 |
{n.title}
13 |
)} 14 |
} 15 | {category && category.locals && category.locals.length > 0 &&
16 | {category.locals.map((n, i) =>
onFilter('local_id', n.id)} key={i} 17 | className="flex-wrp flex-center ptb30 border-r size28" style={{width: '25%'}}> 18 | {n.title}
)} 19 |
} 20 | 24 |
-------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./containers/Boot').default, 3 | childRoutes: [ 4 | { 5 | path: '/', 6 | component: require('./containers/App').default, 7 | indexRoute: { 8 | getComponent(state, cb){ 9 | require.ensure([], require => cb(null, require('./containers/Home').default)) 10 | } 11 | }, 12 | childRoutes: [ 13 | require('./containers/Shop/routes').default, 14 | require('./containers/Buying/routes').default, 15 | require('./containers/Pay/routes').default, 16 | require('./containers/User/routes').default, 17 | require('./containers/Weal/routes').default, 18 | require('./containers/Rim/routes').default, 19 | require('./containers/Vip/routes').default, 20 | require('./containers/Gift/routes').default, 21 | require('./containers/Search/routes').default, 22 | require('./containers/Lottery/routes').default, 23 | require('./containers/Home/routes').default, 24 | ] 25 | }, 26 | { 27 | path: '*', 28 | getComponent(state, cb){ 29 | require.ensure([], require => cb(null, require('./containers/NotFound').default)) 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /app/components/Gift/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | 6 | export default ({gift, user}) => 8 | 9 |
10 |
{gift.title}
11 |
12 | {gift.vip_info &&
13 |
VIP专享
14 |
{gift.vip_info.fee}积分
15 |
} 16 |
{gift.fee}积分
17 |
18 |
19 | {gift.shop &&
{gift.shop.title}
} 20 |
剩余{gift.stock}份
21 |
22 |
23 | -------------------------------------------------------------------------------- /app/containers/Vip/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import {setTitle, wx} from 'utils'; 4 | import Loading from 'components/Loading'; 5 | import ResultFail from 'components/Result/Fail'; 6 | import {Toast} from 'antd-mobile'; 7 | import {getVip, setInvite} from 'actions/vip'; 8 | 9 | @connect(state => ({ 10 | config: state.config, 11 | vip: state.vip 12 | }), { 13 | getVip, 14 | setInvite 15 | }) 16 | export default class extends PureComponent { 17 | componentDidMount() { 18 | const {config, getVip, location, setInvite} = this.props 19 | location.query.invite_from && setInvite({invite_from: location.query.invite_from}) 20 | location.query.invite_from_id && setInvite({invite_from_id: location.query.invite_from_id}) 21 | setTitle(config.siteConfig.sitename + '-会员中心') 22 | wx.setShare(config.shareConfig.vip || {}) 23 | getVip() 24 | } 25 | 26 | render() { 27 | const {children, vip} = this.props 28 | if (vip.isFetching || !vip.isFetched) { 29 | return 30 | } 31 | if (!vip.isOpen) { 32 | 36 | } 37 | return children 38 | } 39 | } -------------------------------------------------------------------------------- /app/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import PropTypes from 'prop-types'; 3 | import SearchBar from "./SearchBar"; 4 | 5 | export default class extends PureComponent { 6 | static SearchBar = SearchBar 7 | static contextTypes = { 8 | router: PropTypes.object.isRequired 9 | } 10 | onLeftClick = () => { 11 | const {router} = this.context 12 | const {leftClick} = this.props 13 | if (leftClick) { 14 | return leftClick() 15 | } 16 | if (history.state) { 17 | return history.back() 18 | } 19 | router.push({ 20 | pathname: '' 21 | }) 22 | } 23 | 24 | render() { 25 | const {right, title, children, className, color} = this.props 26 | return
27 |
28 | 29 |
30 | {children} 31 | {!children &&
{title}
} 32 |
{right}
33 |
34 | } 35 | } -------------------------------------------------------------------------------- /app/components/Home/AdBox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Carousel} from 'antd-mobile'; 3 | import Link from 'components/Link'; 4 | import p from 'assets/images/s.gif'; 5 | import s from './style.scss'; 6 | export default ({items, limit = 4}) => { 7 | const group = items.map((item, index) => { 8 | return items.slice(index * limit, index * limit + limit) 9 | }).filter(item => { 10 | return item.length > 0 11 | }) 12 | return
13 | 1} swiping={false} infinite vertical> 14 | {group.map((item, index) =>
15 | {item.map((n, i) => 16 | 25 | 26 | )} 27 |
)} 28 |
29 |
30 | } -------------------------------------------------------------------------------- /app/containers/Vip/Help/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import VipHelp from 'components/Vip/Help'; 4 | 5 | @connect(state => ({ 6 | vip: state.vip 7 | })) 8 | export default class extends PureComponent { 9 | state = { 10 | helpers: [] 11 | } 12 | 13 | componentDidMount() { 14 | const {vip} = this.props 15 | this.setState({ 16 | helpers: [ 17 | { 18 | title: '什么是VIP会员', 19 | content: vip.what_is_vip, 20 | icon: 'i-help-o' 21 | }, 22 | { 23 | title: '如何开通VIP会员', 24 | content: vip.how_join_vip, 25 | icon: 'i-kttq' 26 | }, 27 | { 28 | title: 'VIP会员权益', 29 | content: vip.privilege.map((n, i) =>
{n.desc}
), 30 | icon: 'i-qy' 31 | }, 32 | { 33 | title: '会员卡如何使用', 34 | content: vip.how_to_use, 35 | icon: 'i-sysm' 36 | }, 37 | { 38 | title: '服务热线', 39 | content: vip.service_call, 40 | icon: 'i-fwrx' 41 | } 42 | ] 43 | }) 44 | } 45 | 46 | render() { 47 | const {helpers} = this.state 48 | return 51 | } 52 | } -------------------------------------------------------------------------------- /app/components/User/Coupon/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import Link from 'components/Link'; 4 | import List from '../List'; 5 | import s from './style.scss' 6 | 7 | export default ({user, onSwitch, coupon, fetchCoupons, onDelete}) =>
8 |
9 |
10 |
onSwitch(0)}>未使用({user.weal.coupon.count[0]}) 12 |
13 |
onSwitch(1)}>已使用({user.weal.coupon.count[1]}) 15 |
16 |
onSwitch(-1)}>已过期({user.weal.coupon.count[-1]}) 18 |
19 |
20 | 25 |
26 | 领取更多好券 28 |
-------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "babel-plugin-transform-decorators-legacy", 9 | [ 10 | "import", 11 | { 12 | "style":"css", 13 | "libraryName": "antd-mobile" 14 | } 15 | ], 16 | [ 17 | "module-alias", 18 | [ 19 | { 20 | "src": "./app", 21 | "expose": "app" 22 | }, 23 | { 24 | "src": "./app/actions", 25 | "expose": "actions" 26 | }, 27 | { 28 | "src": "./app/utils", 29 | "expose": "utils" 30 | }, 31 | { 32 | "src": "./app/components", 33 | "expose": "components" 34 | }, 35 | { 36 | "src": "./assets", 37 | "expose": "assets" 38 | } 39 | ] 40 | ] 41 | ], 42 | "env": { 43 | // only enable it when process.env.NODE_ENV is 'development' or undefined 44 | "development": { 45 | "plugins": [ 46 | "react-hot-loader/babel", 47 | [ 48 | "transform-runtime", 49 | { 50 | "helpers": false, 51 | "polyfill": false, 52 | "regenerator": true, 53 | "moduleName": "babel-runtime" 54 | } 55 | ] 56 | ] 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /app/containers/User/Favorites/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import {setTitle, getApi} from "utils"; 4 | import Header from 'components/Header'; 5 | import Loading from 'components/Loading'; 6 | import ResultFail from 'components/Result/Fail'; 7 | import UserFavorite from 'components/User/Favorite'; 8 | 9 | @connect(state => ({ 10 | config: state.config 11 | })) 12 | export default class extends PureComponent { 13 | state = { 14 | sections: [], 15 | isFetched: false 16 | } 17 | 18 | componentDidMount() { 19 | const {config} = this.props 20 | setTitle(config.siteConfig.sitename) 21 | this.fetchFavorites() 22 | } 23 | 24 | fetchFavorites = () => { 25 | getApi('/user/favorite').then(response => { 26 | this.setState({ 27 | sections: response.sections || [], 28 | isFetched: true 29 | }) 30 | }) 31 | } 32 | 33 | render() { 34 | const {sections, isFetched} = this.state 35 | if (!isFetched) { 36 | return 37 | } 38 | return
39 |
40 | {sections.length > 0 && } 43 | {sections.length == 0 &&
您还没有收藏商品
} 44 |
45 | } 46 | } -------------------------------------------------------------------------------- /app/containers/Gift/Rule/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import Header from 'components/Header'; 4 | import {setTitle, wx, getApi} from 'utils'; 5 | 6 | @connect((state) => ({ 7 | config: state.config 8 | })) 9 | export default class extends PureComponent { 10 | state = { 11 | rules: [ 12 | { 13 | title: ' ', 14 | content: ' ' 15 | } 16 | ], 17 | } 18 | 19 | componentDidMount() { 20 | const {config} = this.props 21 | wx.setShare(config.shareConfig.gift) 22 | setTitle(config.siteConfig.sitename + '-积分商城') 23 | this.fetchRule() 24 | } 25 | 26 | fetchRule = () => { 27 | getApi(`/gift/rule`).then(response => { 28 | this.setState({ 29 | rules: response.rules 30 | }) 31 | }) 32 | } 33 | 34 | render() { 35 | const {rules} = this.state 36 | return
37 |
38 | {rules.map((n, i) =>
39 |
40 |
 
41 |
{n.title}
42 |
43 |
44 |
)} 45 |
46 | } 47 | } -------------------------------------------------------------------------------- /app/components/Comment/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import moment from 'moment'; 3 | import Rate from 'components/Rate'; 4 | import {wx} from 'utils'; 5 | import p from 'assets/images/s.gif'; 6 | 7 | export default ({comment}) =>
8 |
9 |
11 |
12 |
{comment.user.nickname}
13 |
14 | 15 |
{moment(comment.create_at).format('MM-DD HH:mm')}
16 |
17 |
18 |
19 |
20 | {comment.content &&
{comment.content}
} 21 | {comment.pics && comment.pics.length > 0 &&
22 | {comment.pics.map((n, i) => { 23 | wx.previewImage(n, comment.pics) 24 | }} src={p} className="img-160 bg-cover mr10 mb5" style={{backgroundImage: `url(${n})`}} key={i}/>)} 25 |
} 26 |
27 | {comment.reply &&
28 | 商家回复: 29 | {comment.reply} 30 |
} 31 |
-------------------------------------------------------------------------------- /app/components/Buying/Detail/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header' 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | export default ({buying, toggleFavor, onShowPhotos}) =>
6 |
10 | 11 |
} 12 | /> 13 | {buying.thumb &&
14 | 16 |
17 | {buying.title &&
{buying.title}
} 18 |
19 |
20 | 21 | 22 | {buying.invitation ? '需预约' : '免预约'} 23 | 24 |
25 |
26 | 27 | {buying.views}人在关注 28 |
29 |
30 |
31 |
} 32 |
-------------------------------------------------------------------------------- /app/containers/Rim/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import {fetchRims, getRims} from 'actions/rim'; 4 | import {getCategorys} from 'actions/category'; 5 | import RimHome from 'components/Rim/Home'; 6 | 7 | @connect(state => ({ 8 | rim: state.rim, 9 | config: state.config, 10 | category: state.category 11 | }), { 12 | fetchRims, 13 | getCategorys 14 | }) 15 | export default class extends PureComponent { 16 | componentDidMount() { 17 | const {getCategorys} = this.props 18 | getCategorys('rim') 19 | } 20 | 21 | onSearch = (keyword) => { 22 | const {router} = this.props 23 | router.push({ 24 | pathname: `/search`, 25 | query: { 26 | keyword: keyword 27 | } 28 | }) 29 | } 30 | fetchRims = (e, refresh = false, filter = {}) => { 31 | const {rim, fetchRims} = this.props 32 | fetchRims({ 33 | ...filter, 34 | offset: refresh ? 0 : rim.rims.length, 35 | isRefreshing: refresh 36 | }) 37 | } 38 | onFilter = (key, value) => { 39 | this.fetchRims(null, true, { 40 | [key]: value 41 | }) 42 | } 43 | 44 | render() { 45 | const {rim, config, category} = this.props 46 | return 54 | } 55 | } -------------------------------------------------------------------------------- /app/containers/Shop/Buying/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import Header from 'components/Header'; 3 | import BuyingList from 'components/Buying/List'; 4 | import {getApi} from 'utils'; 5 | 6 | export default class extends PureComponent { 7 | state = { 8 | isFetching: false, 9 | isMore: true, 10 | filter: { 11 | limit: 10 12 | }, 13 | buyings: [] 14 | } 15 | 16 | componentDidMount() { 17 | 18 | } 19 | 20 | fetchBuyings = () => { 21 | const {location} = this.props 22 | if (!this.state.isMore || this.state.isFetching) { 23 | return 24 | } 25 | this.setState(state => ({ 26 | isFetching: true 27 | }), () => { 28 | getApi(`/buying`, Object.assign({}, this.state.filter, { 29 | shop_id: location.query.shop_id, 30 | offset: this.state.buyings.length 31 | })).then(response => { 32 | this.setState(state => ({ 33 | isFetching: false, 34 | isMore: (response.buyings || []).length >= state.filter.limit, 35 | buyings: state.buyings.concat(response.buyings || []) 36 | })) 37 | }) 38 | }) 39 | } 40 | 41 | render() { 42 | const {isFetching, isMore, buyings} = this.state 43 | return
44 |
45 | 53 |
54 | } 55 | } -------------------------------------------------------------------------------- /app/components/Header/SearchBar/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import Link from 'components/Link'; 3 | import s from './style.scss'; 4 | export default class extends PureComponent { 5 | onKeydown = (e) => { 6 | const {onSearch} = this.props 7 | if (e.key == 'Enter') { 8 | e.currentTarget.value && onSearch(e.currentTarget.value) 9 | } 10 | } 11 | 12 | render() { 13 | const {currentCity, children, mode, className} = this.props 14 | return
15 | {mode == 'dark' &&
} 16 |
17 | 19 |
{currentCity.title}
20 |
21 | 22 |
23 |
25 | 26 | 28 |
29 |
30 |
31 | {children} 32 |
33 | } 34 | } -------------------------------------------------------------------------------- /app/containers/User/Finance/Result/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import Header from 'components/Header'; 3 | import {Button} from 'antd-mobile' 4 | import s from './style.scss'; 5 | 6 | export default class extends PureComponent { 7 | state = { 8 | success: true, 9 | message: '' 10 | } 11 | 12 | componentDidMount() { 13 | const {location} = this.props 14 | this.setState(location.state) 15 | } 16 | 17 | render() { 18 | const {router} = this.props 19 | const {success, message} = this.state 20 | return
21 |
22 |
23 | 24 |
25 |
{message}
26 |
27 | 35 | 45 |
46 |
47 |
48 | } 49 | } -------------------------------------------------------------------------------- /app/middleware/api.js: -------------------------------------------------------------------------------- 1 | import 'isomorphic-fetch' 2 | import {callApi, is_Android, setWxConfig} from 'utils' 3 | export const CALL_API = Symbol('Call API') 4 | export default store => next => action => { 5 | if (action.type == '@@router/LOCATION_CHANGE') { 6 | is_Android && setWxConfig() 7 | return next(action) 8 | } 9 | const callAPI = action[CALL_API] 10 | if (typeof callAPI === 'undefined') { 11 | return next(action) 12 | } 13 | let {endpoint} = callAPI 14 | const {types, body, method} = callAPI 15 | if (typeof endpoint === 'function') { 16 | endpoint = endpoint(store.getState()) 17 | } 18 | if (typeof endpoint !== 'string') { 19 | throw new Error('Specify a string endpoint URL.') 20 | } 21 | if (!Array.isArray(types) || types.length !== 3) { 22 | throw new Error('Expected an array of three action types.') 23 | } 24 | if (!types.every(type => typeof type === 'string')) { 25 | throw new Error('Expected action types to be strings.') 26 | } 27 | const actionWith = (data) => { 28 | const finalAction = Object.assign({}, action, data) 29 | delete finalAction[CALL_API] 30 | return finalAction 31 | } 32 | const [requestType, successType, failureType] = types 33 | next(actionWith({ 34 | type: requestType 35 | })) 36 | return callApi(endpoint, body, method).then(response => next(actionWith({ 37 | response, 38 | type: successType 39 | })), error => next(actionWith({ 40 | type: failureType, 41 | error: error.message || 'Something bad happened' 42 | }))) 43 | } -------------------------------------------------------------------------------- /app/containers/Vip/Level/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import {connect} from 'react-redux'; 3 | import VipLevel from 'components/Vip/Level'; 4 | 5 | @connect(state => ({ 6 | user: state.user, 7 | vip: state.vip 8 | })) 9 | export default class extends PureComponent { 10 | state = { 11 | tabIndex: 0, 12 | priviegies: [ 13 | { 14 | title: '积分回馈', 15 | icon: 'i-jfhk' 16 | } 17 | ], 18 | unpriviegies: [ 19 | { 20 | title: '积分回馈', 21 | icon: 'i-jfhk' 22 | }, 23 | { 24 | title: '会员抢购', 25 | icon: 'i-buying-v' 26 | }, 27 | { 28 | title: '会员优惠', 29 | icon: 'i-coupon-v' 30 | }, { 31 | title: '会员礼品', 32 | icon: 'i-gift-v' 33 | }, { 34 | title: '会员折扣', 35 | icon: 'i-vipdis' 36 | } 37 | ] 38 | 39 | } 40 | 41 | componentDidMount() { 42 | const {user} = this.props 43 | { 44 | user.is_vip && 45 | this.setState({ 46 | tabIndex: 1 47 | }) 48 | } 49 | } 50 | 51 | onSwitchTab = (tabIndex) => { 52 | this.setState({ 53 | tabIndex 54 | }) 55 | } 56 | 57 | render() { 58 | const {user, vip} = this.props 59 | const {tabIndex, priviegies, unpriviegies} = this.state 60 | return 68 | } 69 | } -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import p from 'assets/images/s.gif'; 3 | import s from './style.scss'; 4 | import {setTitle, date_obj, getApi} from "utils"; 5 | 6 | export default ({redpacket, onClickRedpacket}) =>
7 |
8 |
{date_obj(redpacket.start_time).first_time} {date_obj(redpacket.start_time).secend_time}
10 |
11 |
12 |
13 | 14 |
15 |
16 |
{redpacket.shop.title}
17 |
onClickRedpacket(redpacket)}> 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
{redpacket.title}
26 |
27 |
领取现金红包
28 |
29 |
30 |
31 |
32 |
-------------------------------------------------------------------------------- /app/reducers/coupon.js: -------------------------------------------------------------------------------- 1 | import { 2 | COUPONS_FAIL, COUPONS_REQUEST, COUPONS_SUCCESS, 3 | FETCH_COUPONS_FAIL, FETCH_COUPONS_REQUEST, FETCH_COUPONS_SUCCESS 4 | } from "actions/coupon"; 5 | 6 | const initialState = { 7 | coupons: [], 8 | isFetching: false, 9 | isMore: true, 10 | isRefreshing: false, 11 | filter: { 12 | limit: 10 13 | } 14 | } 15 | export default (state = initialState, action) => { 16 | switch (action.type) { 17 | case COUPONS_REQUEST: 18 | return Object.assign({}, state, { 19 | [action.fullName]: { 20 | isFetching: true 21 | } 22 | }) 23 | case COUPONS_SUCCESS: 24 | return Object.assign({}, state, { 25 | [action.fullName]: { 26 | coupons: action.response.coupons || [], 27 | isFetching: false, 28 | isFetched: true 29 | } 30 | }) 31 | case FETCH_COUPONS_REQUEST: 32 | return Object.assign({}, state, { 33 | filter: action.filter, 34 | isFetching: true, 35 | isRefreshing: action.isRefreshing, 36 | }, action.isRefreshing ? { 37 | coupons: [] 38 | } : {}) 39 | case FETCH_COUPONS_SUCCESS: 40 | return Object.assign({}, state, { 41 | isFetching: false, 42 | isRefreshing: false, 43 | isMore: action.response.coupons && action.response.coupons.length >= action.filter.limit, 44 | coupons: action.isRefreshing ? action.response.coupons || [] : state.coupons.concat(action.response.coupons || []), 45 | }) 46 | default: 47 | return state 48 | } 49 | } -------------------------------------------------------------------------------- /app/containers/Shop/Comment/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import Header from 'components/Header'; 3 | import CommentList from 'components/Comment/List'; 4 | import {getApi} from 'utils'; 5 | export default class extends PureComponent { 6 | state = { 7 | filter: { 8 | limit: 10 9 | }, 10 | isFetching: false, 11 | isMore: true, 12 | comments: [] 13 | } 14 | 15 | componentDidMount() { 16 | 17 | } 18 | 19 | fetchComments = () => { 20 | const {location} = this.props 21 | if (!this.state.isMore || this.state.isFetching) { 22 | return 23 | } 24 | this.setState(state => ({ 25 | isFetching: true, 26 | filter: Object.assign({}, state.filter, { 27 | shop_id: location.query.shop_id, 28 | }) 29 | }), () => { 30 | getApi(`/comment`, Object.assign({}, this.state.filter, { 31 | offset: this.state.comments.length 32 | })).then(response => { 33 | this.setState({ 34 | comments: this.state.comments.concat(response.comments || []), 35 | isMore: (response.comments || []).length >= this.state.filter.limit, 36 | isFetching: false 37 | }) 38 | }) 39 | }) 40 | } 41 | 42 | render() { 43 | const {isFetching, isMore, comments} = this.state 44 | return
45 |
46 | 54 |
55 | } 56 | } -------------------------------------------------------------------------------- /app/components/User/Comment/Add/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {List, TextareaItem, ImagePicker} from 'antd-mobile'; 3 | import Header from 'components/Header'; 4 | import Rate from 'components/Rate'; 5 | 6 | export default ({comment, onSubmit, onCommentChange, onAddImage, onChangeImage}) =>
7 |
保存
}/> 8 | 9 | 11 | onCommentChange(value, 'score')}/> 12 |
} 13 | > 14 | 整体评价 15 | 16 | 18 | onCommentChange(value, 'score_service')}/> 19 |
} 20 | > 21 | 服务评价 22 | 23 | 25 | onCommentChange(value, 'score_environment')}/> 27 |
} 28 | > 29 | 环境评价 30 | 31 | onCommentChange(value, 'content')} 37 | placeholder="评价内容(选填)" 38 | /> 39 | 40 | onAddImage(e)} 43 | onChange={onChangeImage} 44 | /> 45 |
-------------------------------------------------------------------------------- /app/containers/Gift/List/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import Header from 'components/Header'; 3 | import GiftList from 'components/Gift/List'; 4 | import Filter from 'components/Filter'; 5 | import {connect} from 'react-redux'; 6 | import {getFilters} from 'actions/filter'; 7 | import {fetchGifts} from 'actions/gift'; 8 | import {setTitle, wx} from 'utils'; 9 | 10 | @connect((state) => ({ 11 | config: state.config, 12 | user: state.user, 13 | gift: state.gift, 14 | filter: state.filter 15 | }), { 16 | fetchGifts, 17 | getFilters 18 | }) 19 | export default class extends PureComponent { 20 | componentDidMount() { 21 | const {config, getFilters} = this.props 22 | wx.setShare(config.shareConfig.gift) 23 | setTitle(config.siteConfig.sitename + '-积分商城') 24 | getFilters('gift') 25 | } 26 | 27 | loadGifts = (e, refresh = false, filter = {}) => { 28 | const {gift, fetchGifts} = this.props 29 | fetchGifts({ 30 | ...filter, 31 | offset: refresh ? 0 : gift.gifts.length, 32 | isRefreshing: refresh 33 | }) 34 | } 35 | onFilter = (filter) => { 36 | this.loadGifts(null, true, filter) 37 | } 38 | 39 | render() { 40 | const {gift, filter} = this.props 41 | return
42 |
43 | {filter.gift && filter.gift.filters && filter.gift.filters.length > 0 && 44 | } 45 | 49 |
50 | } 51 | } -------------------------------------------------------------------------------- /app/reducers/redpacket.js: -------------------------------------------------------------------------------- 1 | import { 2 | REDPACKETS_FAIL, REDPACKETS_REQUEST, REDPACKETS_SUCCESS, 3 | FETCH_REDPACKETS_FAIL, FETCH_REDPACKETS_REQUEST, FETCH_REDPACKETS_SUCCESS 4 | } from "actions/coupon"; 5 | 6 | const initialState = { 7 | redpackets: [], 8 | isFetching: false, 9 | isMore: true, 10 | isRefreshing: false, 11 | filter: { 12 | limit: 10 13 | } 14 | } 15 | export default (state = initialState, action) => { 16 | switch (action.type) { 17 | case REDPACKETS_REQUEST: 18 | return Object.assign({}, state, { 19 | [action.fullName]: { 20 | isFetching: true 21 | } 22 | }) 23 | case REDPACKETS_SUCCESS: 24 | return Object.assign({}, state, { 25 | [action.fullName]: { 26 | redpackets: action.response.redpackets || [], 27 | isFetching: false, 28 | isFetched: true 29 | } 30 | }) 31 | case FETCH_REDPACKETS_REQUEST: 32 | return Object.assign({}, state, { 33 | filter: action.filter, 34 | isFetching: true, 35 | isRefreshing: action.isRefreshing, 36 | }, action.isRefreshing ? { 37 | redpackets: [] 38 | } : {}) 39 | case FETCH_REDPACKETS_SUCCESS: 40 | return Object.assign({}, state, { 41 | isFetching: false, 42 | isRefreshing: false, 43 | isMore: action.response.redpackets && action.response.redpackets.length >= action.filter.limit, 44 | redpackets: action.isRefreshing ? action.response.redpackets || [] : state.redpackets.concat(action.response.redpackets || []), 45 | }) 46 | default: 47 | return state 48 | } 49 | } -------------------------------------------------------------------------------- /app/containers/Rim/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {setTitle, getApi, wx, callApi} from 'utils'; 3 | import {connect} from "react-redux"; 4 | import RimDetail from 'components/Rim/Detail'; 5 | import ResultFail from 'components/Result/Fail'; 6 | 7 | @connect(state => ({ 8 | rim: state.rim, 9 | config: state.config 10 | })) 11 | export default class index extends PureComponent { 12 | state = { 13 | rim: false, 14 | isFetching: true 15 | } 16 | 17 | componentDidMount() { 18 | const {config, params} = this.props 19 | getApi(`/rim/${params.id}`).then(response => { 20 | const rim = response.rim 21 | this.setState({ 22 | rim: rim, 23 | isFetching: false 24 | }) 25 | if (rim) { 26 | setTitle(rim.title) 27 | wx.setShare({ 28 | title: `${rim.title}【${config.siteConfig.sitename}】`, 29 | imgUrl: rim.thumb, 30 | desc: `关注订阅【${config.siteConfig.sitename}】尊享全城特惠` 31 | }) 32 | } 33 | }) 34 | } 35 | 36 | onBuy = () => { 37 | const {rim} = this.state 38 | const {router} = this.props 39 | router.push({ 40 | pathname: '/order_rim', 41 | state: { 42 | rim 43 | } 44 | }) 45 | } 46 | 47 | render() { 48 | const {rim, isFetching} = this.state 49 | if (isFetching) { 50 | return 51 | } 52 | if (!rim) { 53 | return 57 | } 58 | return 62 | } 63 | } -------------------------------------------------------------------------------- /app/actions/coupon.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const COUPONS_REQUEST = 'COUPONS_REQUEST' 5 | export const COUPONS_SUCCESS = 'COUPONS_SUCCESS' 6 | export const COUPONS_FAIL = 'COUPONS_FAIL' 7 | 8 | export const FETCH_COUPONS_REQUEST = 'FETCH_COUPONS_REQUEST' 9 | export const FETCH_COUPONS_SUCCESS = 'FETCH_COUPONS_SUCCESS' 10 | export const FETCH_COUPONS_FAIL = 'FETCH_COUPONS_FAIL' 11 | 12 | export const getCoupons = (fullName, filter = {}) => (dispatch, getState) => { 13 | const repo = getState().coupon[fullName] 14 | if (repo && repo.isFetching) { 15 | return null 16 | } 17 | cache.get(`${fullName}_coupons`) && dispatch({ 18 | type: COUPONS_SUCCESS, 19 | response: cache.get(`${fullName}_coupons`), 20 | fullName: fullName 21 | }) 22 | return dispatch({ 23 | [CALL_API]: { 24 | types: [COUPONS_REQUEST, COUPONS_SUCCESS, COUPONS_FAIL], 25 | endpoint: `/coupon`, 26 | body: filter 27 | }, 28 | fullName: fullName 29 | }).then(action => { 30 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_coupons`, action.response) 31 | return action 32 | }) 33 | } 34 | 35 | export const fetchCoupons = (filter) => (dispatch, getState) => { 36 | const {coupon} = getState() 37 | filter = Object.assign(coupon.filter, filter) 38 | if ((!coupon.isMore && !filter.isRefreshing) || coupon.isFetching) { 39 | return null 40 | } 41 | return dispatch({ 42 | isRefreshing: filter.isRefreshing, 43 | filter: filter, 44 | [CALL_API]: { 45 | types: [FETCH_COUPONS_REQUEST, FETCH_COUPONS_SUCCESS, FETCH_COUPONS_FAIL], 46 | endpoint: `/coupon`, 47 | body: filter 48 | } 49 | }) 50 | } -------------------------------------------------------------------------------- /app/components/Vip/Shop/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import s from './style.scss'; 4 | import p from 'assets/images/s.gif'; 5 | export default ({shop}) => { 6 | return 7 | 8 |
9 |
10 |
{shop.title}
11 |
12 | 13 | {shop.uv} 14 |
15 |
16 |
17 | {shop.tag &&
{shop.tag}
} 18 |
19 | {shop.distance &&
20 | {shop.distance.number}{shop.distance.unit} 21 |
} 22 |
23 |
24 | {shop.discount_info &&
25 | 26 | {shop.discount_info.text} 27 |
} 28 |
29 | {shop.discount_info.vip && 30 |
31 | 32 |
专享{shop.discount_info.vip.scale}折
33 |
} 34 |
35 |
36 | 37 | } -------------------------------------------------------------------------------- /app/containers/Shop/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/shop', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/shop_join', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Join').default)) 14 | } 15 | }, { 16 | path: '/shop_join_form', 17 | getComponent(state, cb){ 18 | require.ensure([], require => cb(null, require('./Join/Form').default)) 19 | } 20 | }, 21 | { 22 | path: '/shop_comment', 23 | getComponent(state, cb){ 24 | require.ensure([], require => cb(null, require('./Comment').default)) 25 | } 26 | }, 27 | { 28 | path: '/shop_coupon', 29 | getComponent(state, cb){ 30 | require.ensure([], require => cb(null, require('./Coupon').default)) 31 | } 32 | }, 33 | { 34 | path: '/shop_buying', 35 | getComponent(state, cb){ 36 | require.ensure([], require => cb(null, require('./Buying').default)) 37 | } 38 | }, 39 | { 40 | path: '/order_cashier', 41 | getComponent(state, cb){ 42 | require.ensure([], require => cb(null, require('./Cashier').default)) 43 | } 44 | }, 45 | { 46 | path: '/shop_branch', 47 | getComponent(state, cb){ 48 | require.ensure([], require => cb(null, require('./Branch').default)) 49 | } 50 | }, 51 | { 52 | path: '/shop_:id', 53 | getComponent(state, cb){ 54 | require.ensure([], require => cb(null, require('./Detail').default)) 55 | } 56 | }, 57 | ] 58 | } -------------------------------------------------------------------------------- /app/actions/redpacket.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const REDPACKETS_REQUEST = 'REDPACKETS_REQUEST' 5 | export const REDPACKETS_SUCCESS = 'REDPACKETS_SUCCESS' 6 | export const REDPACKETS_FAIL = 'REDPACKETS_FAIL' 7 | 8 | export const FETCH_REDPACKETS_REQUEST = 'FETCH_REDPACKETS_REQUEST' 9 | export const FETCH_REDPACKETS_SUCCESS = 'FETCH_REDPACKETS_SUCCESS' 10 | export const FETCH_REDPACKETS_FAIL = 'FETCH_REDPACKETS_FAIL' 11 | 12 | export const getRedpackets = (fullName, filter = {}) => (dispatch, getState) => { 13 | const repo = getState().coupon[fullName] 14 | if (repo && repo.isFetchec) { 15 | return null 16 | } 17 | cache.get(`${fullName}_redpackets`) && dispatch({ 18 | type: REDPACKETS_SUCCESS, 19 | response: cache.get(`${fullName}_redpackets`), 20 | fullName: fullName 21 | }) 22 | return dispatch({ 23 | [CALL_API]: { 24 | types: [REDPACKETS_REQUEST, REDPACKETS_SUCCESS, REDPACKETS_FAIL], 25 | endpoint: `/redpacket`, 26 | body: filter 27 | }, 28 | fullName: fullName 29 | }).then(action => { 30 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_redpackets`, action.response) 31 | return action 32 | }) 33 | } 34 | 35 | export const fetchRedpackets = (filter) => (dispatch, getState) => { 36 | const {redpacket} = getState() 37 | filter = Object.assign(redpacket.filter, filter) 38 | if ((!redpacket.isMore && !filter.isRefreshing) || redpacket.isFetching) { 39 | return null 40 | } 41 | return dispatch({ 42 | isRefreshing: filter.isRefreshing, 43 | filter: filter, 44 | [CALL_API]: { 45 | types: [FETCH_REDPACKETS_REQUEST, FETCH_REDPACKETS_SUCCESS, FETCH_REDPACKETS_FAIL], 46 | endpoint: `/redpacket`, 47 | body: filter 48 | } 49 | }) 50 | } -------------------------------------------------------------------------------- /app/containers/Gift/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import GiftHome from 'components/Gift/Home'; 4 | import {fetchGifts} from 'actions/gift'; 5 | import {sign} from 'actions/user'; 6 | import {setTitle, wx, getApi} from 'utils'; 7 | 8 | @connect((state) => ({ 9 | config: state.config, 10 | user: state.user 11 | }), { 12 | fetchGifts, 13 | sign 14 | }) 15 | export default class extends PureComponent { 16 | state = { 17 | gifts: [], 18 | record: [], 19 | vip_gifts: [] 20 | } 21 | 22 | componentDidMount() { 23 | const {config} = this.props 24 | wx.setShare(config.shareConfig.gift) 25 | setTitle(config.siteConfig.sitename + '-积分商城') 26 | this.fetchGifts() 27 | this.fetchRecord() 28 | this.fetchVipGifts() 29 | } 30 | 31 | onSign = () => { 32 | const {sign} = this.props 33 | sign() 34 | } 35 | fetchGifts = () => { 36 | return getApi(`/gift`, {offset: 0, limit: 4}).then(response => { 37 | this.setState({ 38 | gifts: response.gifts || [] 39 | }) 40 | }) 41 | } 42 | fetchRecord = () => { 43 | return getApi(`/gift/record`, {offset: 0, limit: 4}).then(response => { 44 | this.setState({ 45 | record: response.record || [] 46 | }) 47 | }) 48 | } 49 | fetchVipGifts = () => { 50 | return getApi(`/vip/vip_gifts`).then(response => { 51 | this.setState({ 52 | vip_gifts: response.gifts || [] 53 | }) 54 | }) 55 | } 56 | 57 | render() { 58 | const {user, config} = this.props 59 | const {gifts, record, vip_gifts} = this.state 60 | return 68 | } 69 | } -------------------------------------------------------------------------------- /app/containers/Shop/Join/Form/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import ShopJoinForm from 'components/Shop/Join/Form'; 4 | import {getApi, postApi, setTitle} from 'utils'; 5 | import {Toast} from 'antd-mobile'; 6 | 7 | @connect(state => ({ 8 | join: state.shop.join 9 | })) 10 | export default class extends PureComponent { 11 | state = { 12 | joinInfo: {}, 13 | agree: false, 14 | } 15 | 16 | componentDidMount() { 17 | setTitle(`商家入驻`) 18 | } 19 | 20 | onSave = () => { 21 | const {joinInfo} = this.state 22 | if (!joinInfo.shop_name) { 23 | return Toast.info('请填写店铺名称') 24 | } 25 | if (!joinInfo.shop_address) { 26 | return Toast.info('请填写店铺地址') 27 | } 28 | if (!joinInfo.contact_name) { 29 | return Toast.info('请填写联系人姓名') 30 | } 31 | if (!joinInfo.contact_phone) { 32 | return Toast.info('请填写联系人电话') 33 | } 34 | return postApi(`/shop/join/`, joinInfo).then(response => { 35 | if (response.code == 'SUCCESS') { 36 | Toast.success('申请提交成功,运营人员会在近期与您联系', 3, () => { 37 | history.back() 38 | }) 39 | } else { 40 | response.msg && Toast.fail(response.msg) 41 | } 42 | }) 43 | } 44 | onAgreeChange = () => { 45 | this.setState(state => ({ 46 | agree: !state.agree 47 | })) 48 | } 49 | onFieldChange = (value, key) => { 50 | this.setState(state => ({ 51 | joinInfo: Object.assign({}, state.joinInfo, { 52 | [key]: value 53 | }) 54 | })) 55 | } 56 | 57 | render() { 58 | const {join} = this.props 59 | const {joinInfo, agree} = this.state 60 | return 68 | } 69 | } -------------------------------------------------------------------------------- /app/components/Home/Modal/Redpacket/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {number_format} from 'utils'; 3 | import s from "./style.scss"; 4 | 5 | export default ({redpackets, onHideRedpacket, onGetRedpacket}) =>
6 |
7 |
8 |
9 |
10 | {redpackets && redpackets.map((n, i) =>
13 |
14 | {number_format(n.fee)} 15 | 16 |
17 |
18 |
19 | 满{number_format(n.condition.lower)}元减{number_format(n.fee)}元 20 |
21 |
22 | 有效期至:{moment(n.time_end).format('YYYY.MM.DD')} 23 |
24 |
25 |
!n.is_get && onGetRedpacket(n)}> 27 | {n.is_get ? '已' : '立即'}
领取 28 |
29 |
)} 30 |
31 |
32 | 33 |
店内使用微信支付即可使用
34 |
35 |
36 |
37 | 38 |
39 |
40 |
-------------------------------------------------------------------------------- /app/containers/Shop/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import ShopHome from 'components/Shop/Home'; 4 | import {updateLocation} from 'actions/user'; 5 | import {fetchShops, getShops} from 'actions/shop'; 6 | import {getFilters} from 'actions/filter'; 7 | import {setTitle, wx} from 'utils'; 8 | 9 | @connect(state => ({ 10 | config: state.config, 11 | shop: state.shop, 12 | filter: state.filter, 13 | user: state.user 14 | }), { 15 | fetchShops, 16 | getShops, 17 | getFilters, 18 | updateLocation 19 | }) 20 | export default class extends PureComponent { 21 | componentDidMount() { 22 | const {config, user, getShops, getFilters, updateLocation} = this.props 23 | wx.setShare(config.shareConfig.shop) 24 | setTitle(config.siteConfig.sitename + '-热门好店') 25 | getShops('hot', { 26 | limit: 10, 27 | sort: 'hot' 28 | }) 29 | if (user.request_location) { 30 | updateLocation().then(() => { 31 | getFilters('shop') 32 | }) 33 | } else { 34 | getFilters('shop') 35 | } 36 | } 37 | 38 | loadShops = (e, refresh = false, filter = {}) => { 39 | const {shop, fetchShops} = this.props 40 | fetchShops({ 41 | ...filter, 42 | offset: refresh ? 0 : shop.shops.length, 43 | isRefreshing: refresh 44 | }) 45 | } 46 | onSearch = (keyword) => { 47 | const {router} = this.props 48 | router.push({ 49 | pathname: `/search`, 50 | query: { 51 | keyword: keyword 52 | } 53 | }) 54 | } 55 | onFilter = (filter) => { 56 | this.loadShops(null, true, filter) 57 | } 58 | 59 | render() { 60 | const {shop, config, filter} = this.props 61 | return 69 | } 70 | } -------------------------------------------------------------------------------- /app/components/Gift/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import p from 'assets/images/s.gif'; 4 | import s from './style.scss'; 5 | 6 | export default ({user, gift, onBuy}) =>
7 |
8 |
9 | 10 |
11 |
{gift.title}
12 |
13 |
14 |
15 |
16 | 17 |
18 |
{user.is_vip && gift.vip_info ? gift.vip_info.fee : gift.fee}积分
19 |
20 | {gift.sold > 0 &&
{gift.sold}人兑换
} 21 |
22 |
23 |
兑换须知
24 |
兑换商家:{gift.shop.name}
25 |
有效期至:{gift.time_end.substring(0, 10)}
26 | {gift.limit_num > 0 &&
限兑次数:{gift.limit_num}
} 27 | {gift.limit_time && gift.limit_time > 0 &&
购买期限:{gift.limit_time}
} 28 |
兑换说明
29 |
{ 31 | return `:${b * 0.02}rem` 32 | }) 33 | }}/> 34 |
35 |
36 |
37 |
38 |
兑换
39 |
40 |
41 |
-------------------------------------------------------------------------------- /app/components/Gift/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import Header from 'components/Header'; 4 | import Card from './Card'; 5 | import News from './News'; 6 | import Item from './Item'; 7 | import VipGifts from './VipGift'; 8 | 9 | const nav_links = [ 10 | { 11 | path: '/user_credit', 12 | icon: 'i-jf', 13 | title: '积分明细' 14 | }, 15 | { 16 | path: '/user_gifts', 17 | icon: 'i-dhjl', 18 | title: '兑换记录' 19 | }, 20 | { 21 | path: '/gift_rule', 22 | icon: 'i-jfgz', 23 | title: '积分规则' 24 | }, 25 | ] 26 | export default ({user, config, onSign, news, gifts, loadHandle, vip_gifts}) =>
27 |
28 | 29 |
30 | {nav_links.map((n, i) => 31 | 32 |
{n.title}
33 | )} 34 |
35 | 36 | {vip_gifts && vip_gifts.length > 0 &&
37 |
 
38 |
会员首开专享
39 |
} 40 | {vip_gifts && vip_gifts.length > 0 && } 41 |
42 |
 
43 |
积分兑换专区
44 | 45 | 更多 46 | 47 | 48 |
49 |
50 | {gifts && gifts.map((n, i) => )} 51 |
52 |
-------------------------------------------------------------------------------- /app/reducers/shop.js: -------------------------------------------------------------------------------- 1 | import { 2 | SHOPS_FAIL, SHOPS_REQUEST, SHOPS_SUCCESS, 3 | FETCH_SHOPS_FAIL, FETCH_SHOPS_REQUEST, FETCH_SHOPS_SUCCESS, 4 | SHOP_JOIN_CONFIG_REQUEST, SHOP_JOIN_CONFIG_SUCCESS, SHOP_JOIN_CONFIG_FAIL 5 | } from "actions/shop"; 6 | const initialState = { 7 | shops: [], 8 | isFetching: false, 9 | isMore: true, 10 | isRefreshing: false, 11 | filter: { 12 | limit: 10, 13 | sort: 'recommend' 14 | }, 15 | join: { 16 | config: { 17 | isFetched: false 18 | } 19 | } 20 | } 21 | export default (state = initialState, action) => { 22 | switch (action.type) { 23 | case SHOPS_REQUEST: 24 | return Object.assign({}, state, { 25 | [action.fullName]: { 26 | isFetching: true 27 | } 28 | }) 29 | case SHOPS_SUCCESS: 30 | return Object.assign({}, state, { 31 | [action.fullName]: { 32 | shops: action.response.shops || [], 33 | isFetching: false, 34 | isFetched: true 35 | } 36 | }) 37 | case FETCH_SHOPS_REQUEST: 38 | return Object.assign({}, state, { 39 | filter: action.filter, 40 | isFetching: true, 41 | isRefreshing: action.isRefreshing, 42 | }, action.isRefreshing ? { 43 | shops: [] 44 | } : {}) 45 | case FETCH_SHOPS_SUCCESS: 46 | return Object.assign({}, state, { 47 | isFetching: false, 48 | isRefreshing: false, 49 | isMore: action.response.shops && action.response.shops.length >= action.filter.limit, 50 | shops: action.isRefreshing ? action.response.shops || [] : state.shops.concat(action.response.shops || []), 51 | }) 52 | case SHOP_JOIN_CONFIG_SUCCESS: 53 | return Object.assign({}, state, { 54 | join: Object.assign({}, state.join, { 55 | config: { 56 | ...action.response.config, 57 | isFetched: true 58 | } 59 | }) 60 | }) 61 | default: 62 | return state 63 | } 64 | } -------------------------------------------------------------------------------- /app/components/Shop/Join/Form/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {List, InputItem, Checkbox, Popup, Button} from 'antd-mobile'; 3 | 4 | export default ({joinInfo, config, onSave, agree, onAgreeChange, onFieldChange}) =>
6 |
7 | 8 | onFieldChange(value, 'shop_name')} 10 | clear 11 | // placeholder="请填写店铺名称" 12 | >店铺名称 13 | onFieldChange(value, 'shop_address')} 15 | clear 16 | // placeholder="请填写店铺地址" 17 | >店铺地址 18 | onFieldChange(value, 'contact_name')} 20 | clear 21 | // placeholder="请填写店铺联系人" 22 | >联系人姓名 23 | onFieldChange(value, 'contact_phone')} 25 | clear 26 | // type="phone" 27 | // placeholder="请填写联系人电话" 28 | >联系人电话 29 | 30 | 31 |
32 | 33 |
34 |
同意并遵守
35 |
{ 36 | e.preventDefault() 37 | Popup.show(
38 |
40 | 41 |
) 42 | }}>《好店入驻协议》 43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
51 |
-------------------------------------------------------------------------------- /app/containers/User/Addresses/Edit/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {Toast} from 'antd-mobile'; 3 | import UserAddressEdit from 'components/User/Address/Edit'; 4 | import {callApi, postApi} from 'utils'; 5 | 6 | export default class extends PureComponent { 7 | state = { 8 | address: {} 9 | } 10 | 11 | componentDidMount() { 12 | const {location} = this.props 13 | if (location.state && location.state.address) { 14 | this.setState({ 15 | address: location.state.address 16 | }) 17 | } 18 | } 19 | 20 | onAddressChange = (value, key) => { 21 | this.setState(state => ({ 22 | address: Object.assign({}, state.address, { 23 | [key]: value 24 | }) 25 | })) 26 | } 27 | onSave = () => { 28 | const {address} = this.state 29 | const {location} = this.props 30 | if (!address.realname) { 31 | return Toast.info('请填写联系人姓名') 32 | } 33 | if (!address.mobile) { 34 | return Toast.info('请填写联系人电话') 35 | } 36 | if (!address.address) { 37 | return Toast.info('请填写联系人地址') 38 | } 39 | if (location.state && location.state.address) { 40 | return callApi(`/user/address/${address.id}`, address, "PUT").then(response => { 41 | if (response.code == 'SUCCESS') { 42 | Toast.success('保存成功', 1, () => { 43 | history.back() 44 | }) 45 | } else { 46 | response.msg && Toast.fail(response.msg) 47 | } 48 | }) 49 | } else { 50 | return postApi(`/user/address/`, address).then(response => { 51 | if (response.code == 'SUCCESS') { 52 | Toast.success('保存成功', 1, () => { 53 | history.back() 54 | }) 55 | } else { 56 | response.msg && Toast.fail(response.msg) 57 | } 58 | }) 59 | } 60 | } 61 | 62 | render() { 63 | const {address} = this.state 64 | return 69 | } 70 | } -------------------------------------------------------------------------------- /app/components/Filter/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import s from './style.scss'; 3 | export default class extends Component { 4 | state = { 5 | expend: [] 6 | } 7 | showFilter = (item, dep) => { 8 | let {expend} = this.state 9 | const {onFilter, filter} = this.props 10 | if (item.items) { 11 | if (expend.length > dep) { 12 | expend = expend.slice(0, dep + 1) 13 | if (!expend.every(cate => cate.key == item.key)) { 14 | expend[dep] = item 15 | } else { 16 | expend.pop() 17 | } 18 | } else { 19 | expend.push(item) 20 | } 21 | } else { 22 | expend = [] 23 | onFilter(Object.assign({}, filter, { 24 | [item.key]: item.value 25 | })) 26 | } 27 | this.setState({ 28 | expend: expend 29 | }) 30 | } 31 | 32 | render() { 33 | const {filters, filter} = this.props 34 | const {expend} = this.state 35 | return
36 |
37 | {filters.map((n, i) =>
this.showFilter(n, 0)}> 39 |
{n.label}
40 |
41 |
)} 42 |
43 | {expend && expend.length > 0 &&
44 |
45 | {expend.map((item, index) =>
46 | {item.items.map((n, i) =>
this.showFilter(n, index + 1)} key={i} 47 | className={`flex-wrp pd30 border-b ${(filter[n.key] && filter[n.key] == n.value) && 'color7'}`}> 48 | {n.label} 49 | {n.items && } 50 |
)} 51 |
)} 52 |
53 |
} 54 |
55 | } 56 | } -------------------------------------------------------------------------------- /app/containers/Vip/routes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | component: require('./index').default, 3 | childRoutes: [ 4 | { 5 | path: '/vip', 6 | getComponent(state, cb){ 7 | require.ensure([], require => cb(null, require('./Home').default)) 8 | } 9 | }, 10 | { 11 | path: '/vip_help', 12 | getComponent(state, cb){ 13 | require.ensure([], require => cb(null, require('./Help').default)) 14 | } 15 | }, 16 | { 17 | path: '/vip_invite', 18 | getComponent(state, cb){ 19 | require.ensure([], require => cb(null, require('./Invite').default)) 20 | } 21 | }, 22 | { 23 | path: '/order_vip', 24 | getComponent(state, cb){ 25 | require.ensure([], require => cb(null, require('./Pay').default)) 26 | } 27 | }, 28 | { 29 | path: '/vip_pay_agree', 30 | getComponent(state, cb){ 31 | require.ensure([], require => cb(null, require('./Pay/Agree').default)) 32 | } 33 | }, 34 | { 35 | path: '/vip_level', 36 | getComponent(state, cb){ 37 | require.ensure([], require => cb(null, require('./Level').default)) 38 | } 39 | }, 40 | { 41 | path: '/vip_rank', 42 | getComponent(state, cb){ 43 | require.ensure([], require => cb(null, require('./Rank').default)) 44 | } 45 | }, 46 | { 47 | path: '/vip_rank_about', 48 | getComponent(state, cb){ 49 | require.ensure([], require => cb(null, require('./Rank/About').default)) 50 | } 51 | }, 52 | { 53 | path: '/vip_priviege', 54 | getComponent(state, cb){ 55 | require.ensure([], require => cb(null, require('./Privilege').default)) 56 | } 57 | }, 58 | { 59 | path: '/vip_priviege_detail', 60 | getComponent(state, cb){ 61 | require.ensure([], require => cb(null, require('./Privilege/Detail').default)) 62 | } 63 | }, 64 | //兼容3.0路径 65 | { 66 | path: '/vip_card', 67 | onEnter: ({params}, replace) => replace(`/vip`) 68 | }, 69 | ] 70 | } -------------------------------------------------------------------------------- /app/containers/User/Win/detail.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from "react-redux"; 3 | import Header from "components/Header"; 4 | import {List} from 'antd-mobile'; 5 | import Link from 'components/Link'; 6 | import {getApi, setTitle} from "utils"; 7 | 8 | const Lists = ({order, fetchOrders}) => } 14 | /> 15 | 16 | @connect(state => ({ 17 | config: state.config 18 | })) 19 | export default class extends PureComponent { 20 | state = { 21 | data: null 22 | } 23 | 24 | componentDidMount() { 25 | const {config, location, router} = this.props; 26 | location.state && location.state.data ? this.setState({data: location.state.data}) : router.push({pathname: '/Win'}); 27 | setTitle(`${config.siteConfig.sitename}-我的中奖详情`); 28 | console.log(this.props) 29 | } 30 | 31 | render() { 32 | const {data} = this.state; 33 | if (!data) return null; 34 | return
35 |
36 | 37 | 38 | 奖品名称 39 | {data.title} 40 | 41 | 42 | 43 | 44 | 45 | 核销码 46 | {data.code} 47 | 48 | 49 | 50 | 51 | 52 | 核销方 53 | {data.applyTo} 54 | 55 | 56 | 57 | 58 | 59 | 中奖状态 60 | {data.status === '2' ? '待核销' : '已核销'} 61 | 62 | 63 | 64 | 65 | 66 | {data.status === '2' ? '领取时间' : '核销时间'} 67 | {data.datetime} 68 | 69 | 70 |
71 | } 72 | } 73 | -------------------------------------------------------------------------------- /assets/scss/antd.scss: -------------------------------------------------------------------------------- 1 | @import "./_color.scss"; 2 | .am-navbar-light{ 3 | color:$color0!important; 4 | } 5 | .am-list-body{ 6 | background: none!important; 7 | border:none!important; 8 | border-top:none!important; 9 | &::after{ 10 | border:none!important; 11 | } 12 | } 13 | .list-view-section-body:after{ 14 | display: block!important; 15 | content: ''!important; 16 | width: 100%!important; 17 | clear: both!important; 18 | } 19 | .am-tabs-bar .am-tabs-tab-active, 20 | .am-picker-popup-item{ 21 | color:$color0!important; 22 | } 23 | .am-tabs-ink-bar{ 24 | background-color:$color0!important; 25 | } 26 | .am-radio.am-radio-checked .am-radio-inner:after, 27 | .am-checkbox.am-checkbox-checked .am-checkbox-inner, 28 | .am-checkbox.am-checkbox-checked .am-checkbox-inner:after{ 29 | border-color:$color0!important; 30 | } 31 | .am-button.am-button-disabled, .am-button-primary.am-button-disabled, .am-button-ghost.am-button-disabled, .am-button-warning.am-button-disabled{ 32 | background-color: #ddd!important; 33 | color: #bbb!important; 34 | } 35 | .am-list-item .am-list-line .am-list-extra{ 36 | flex-basis: auto!important; 37 | } 38 | .am-list .am-list-body .am-radio-item .am-list-line .am-list-extra .am-radio-wrapper .am-radio .am-radio-inner:after{ 39 | border-color:$color19!important; 40 | } 41 | .vip .am-checkbox-agree .am-checkbox-agree-label .am-checkbox .am-checkbox-inner{ 42 | border-color:$color19!important; 43 | } 44 | .vip .am-checkbox-agree .am-checkbox-agree-label .am-checkbox .am-checkbox-inner:after{ 45 | border-color:$color19!important; 46 | } 47 | .shop .am-checkbox-agree .am-checkbox-agree-label .am-checkbox .am-checkbox-inner{ 48 | border-color:$color23!important; 49 | } 50 | .shop .am-checkbox-agree .am-checkbox-agree-label .am-checkbox .am-checkbox-inner:after{ 51 | border-color:$color23!important; 52 | } 53 | .am-toast.am-toast-mask .am-toast-notice{ 54 | max-width:100%!important; 55 | } 56 | .am-carousel-wrap-dot > span{ 57 | background:rgba(255,255,255,.8)!important; 58 | } 59 | .am-carousel-wrap-dot-active > span{ 60 | background: $color0!important; 61 | } 62 | .am-indexed-list-quick-search-bar{ 63 | font-size:0.26rem!important; 64 | } -------------------------------------------------------------------------------- /app/containers/Weal/Redpacket/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from "react"; 2 | import {connect} from 'react-redux'; 3 | import WealRedpacketDetail from 'components/Weal/Redpacket/Detail'; 4 | import {getApi} from 'utils'; 5 | 6 | @connect() 7 | export default class extends PureComponent { 8 | state = { 9 | redpacket: false, 10 | log: { 11 | isFetching: false, 12 | isMore: true, 13 | logs: [], 14 | filter: { 15 | limit: 10 16 | } 17 | } 18 | } 19 | 20 | componentDidMount() { 21 | const {location, params} = this.props 22 | if (location.state && location.state.redpacket) { 23 | this.setState({ 24 | redpacket: location.state.redpacket 25 | }) 26 | } else { 27 | this.fetchRedpacket(params.id) 28 | } 29 | } 30 | 31 | fetchRedpacket = (redpacket_id) => { 32 | getApi(`/cash_red_packet/${redpacket_id}`).then(response => { 33 | if (response.code == 'SUCCESS') { 34 | this.setState({ 35 | redpacket: response.redpacket 36 | }) 37 | } 38 | }) 39 | } 40 | fetchLogs = (e) => { 41 | if (this.state.log.isFetching || !this.state.log.isMore) { 42 | return 43 | } 44 | return this.setState(state => ({ 45 | log: Object.assign({}, state.log, { 46 | isFetching: true 47 | }) 48 | }), () => { 49 | const {params} = this.props 50 | return getApi(`/cash_red_packet/${params.id}/log`, Object.assign({}, this.state.log.filter, { 51 | offset: this.state.log.logs.length 52 | })).then(response => { 53 | this.setState(state => ({ 54 | log: Object.assign({}, state.log, { 55 | isFetching: false, 56 | isMore: (response.logs || []).length >= state.log.filter.limit, 57 | logs: state.log.logs.concat(response.logs || []) 58 | }) 59 | })) 60 | }) 61 | }) 62 | } 63 | 64 | render() { 65 | const {router} = this.props 66 | const {redpacket, log} = this.state 67 | return 73 | } 74 | } -------------------------------------------------------------------------------- /app/components/User/Order/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link' 3 | import {Button} from 'antd-mobile'; 4 | import p from 'assets/images/s.gif'; 5 | import s from './style.scss'; 6 | 7 | const statuses = { 8 | '-1': '支付超时', 9 | '-2': '已退款', 10 | '0': '已取消', 11 | '1': '待支付', 12 | '2': '已支付', 13 | '3': '已完成', 14 | '4': '待发货', 15 | '5': '待收货', 16 | '6': '待核销', 17 | '7': '待评价', 18 | } 19 | const modules = { 20 | '1': '大牌抢购', 21 | '6': '商家收款', 22 | '7': '优惠券支付', 23 | '8': '五折卡', 24 | '9': '刷卡支付', 25 | '10': '扫码支付', 26 | '11': '活动', 27 | '14': '会员卡' 28 | } 29 | 30 | export default ({order, onPay, onComment}) => 32 |
33 |
{order.time_create}
34 |
{statuses[order.status]}
35 |
36 |
37 |
38 |
39 |
{order.title}
40 |
来自:{modules[order.type]}
41 |
42 |
¥{order.cash_fee}
43 | {order.status == 1 && } 54 | {order.status == 7 && } 63 |
64 |
65 |
66 | -------------------------------------------------------------------------------- /app/components/Weal/Redpacket/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header'; 3 | import List from 'components/List'; 4 | import p from 'assets/images/s.gif' 5 | import Item from "./Item"; 6 | import s from './style.scss' 7 | export default ({redpacket, log, fetchLogs, backHandle, router}) =>
8 | {redpacket &&
9 |
{ 14 | if (redpacket.promote_position == '1') { 15 | return router.replace({ 16 | pathname: `/shop_${redpacket.shop.id}` 17 | }) 18 | } 19 | if (redpacket.promote_position == '2') { 20 | return router.replace({ 21 | pathname: `/shop_buying`, 22 | query: { 23 | shop_id: redpacket.shop.id 24 | } 25 | }) 26 | } 27 | router.goBack() 28 | }} 29 | /> 30 |
31 |
32 | 34 |
{redpacket.shop.title}送的现金红包
35 |
{redpacket.title}
36 | {redpacket.money && redpacket.money > 0 &&
37 |
{redpacket.money}
38 |
39 |
} 40 |
41 |
} 42 | {redpacket &&
43 | {(redpacket.total_num - redpacket.send_num) > 0 && `已抢${redpacket.send_num}个,还剩${redpacket.total_num - redpacket.send_num}个`} 44 | {(redpacket.total_num - redpacket.send_num) == 0 && `${redpacket.total_num}个红包,已被抢光`} 45 |
} 46 | } 52 | /> 53 |
-------------------------------------------------------------------------------- /app/components/Rim/Detail/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'components/Header' 3 | import s from './style.scss'; 4 | export default ({rim, toggleFavor, onShowPhotos, onBuy}) =>
5 |
6 |
7 |
8 |
9 |
10 | {rim.title} 11 |
12 |
13 | {rim.desc} 14 |
15 |
16 |
17 |
18 |
19 |
{rim.adult_price}
20 |
立省:{rim.price}
21 |
{rim.sold}人参与
22 |
23 |
剩余{rim.stock - rim.sold}份
24 |
25 |
26 | {rim.tags.map((n, i) => 27 |
28 | 29 | {n}
30 | )} 31 |
32 |
33 |
34 |
35 |
开团时间:
36 |
{rim.time_start}
37 |
38 |
39 |
出发地点:
40 |
{rim.address}
41 |
42 |
43 |
活动介绍
44 |
{ 47 | return `:${b * 0.02}rem` 48 | })] 49 | }}/> 50 |
51 | 去下单 52 |
53 |
-------------------------------------------------------------------------------- /app/components/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import VipBox from "./VipBox"; 3 | import AdBox from "./AdBox"; 4 | import JoinBox from "./JoinBox"; 5 | import ModealRedpacket from './Modal/Redpacket'; 6 | import Header from 'components/Header'; 7 | import Loading from 'components/Loading'; 8 | import ShopListItem from 'components/Shop/List/Item'; 9 | import Category from 'components/Category'; 10 | import Swiper from 'components/Swiper'; 11 | import Link from 'components/Link'; 12 | 13 | export default ({config, focus, navlink, link, shop, vip_shops, onSearch, redpacket, onHideRedpacket, onGetRedpacket}) => 14 |
15 | 16 | {focus.index && focus.index.focuss && focus.index.focuss.length > 0 && } 17 | 18 | {navlink.index && navlink.index.navlinks && navlink.index.navlinks.length > 0 && 19 | } 20 | {shop.index_new && shop.index_new.shops && shop.index_new.shops.length > 0 && 21 | } 22 | 23 | {(link.index && link.index.links && link.index.links.length > 0) &&
24 | 25 |
} 26 | 27 | {config.enableVipCard && shop.vip && shop.vip.shops && shop.vip.shops.length > 0 && 28 | } 29 | 30 | {shop.index && shop.index.shops && shop.index.shops.length > 0 &&
31 |
32 | 33 |
精品好店
34 | 更多 > 35 |
36 | {shop.index.shops.map((n, i) => )} 37 |
} 38 | {shop.index && shop.index.isFetching && } 39 | {redpacket.show && } 41 |
-------------------------------------------------------------------------------- /app/containers/Vip/Invite/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import VipInvite from 'components/Vip/Invite'; 4 | import {getApi} from 'utils'; 5 | 6 | @connect(state => ({ 7 | user: state.user 8 | })) 9 | export default class extends PureComponent { 10 | state = { 11 | tabIndex: 0, 12 | inviteConfig: {}, 13 | inviteLog: { 14 | isFetching: false, 15 | isMore: true, 16 | logs: [], 17 | filter: { 18 | limit: 10 19 | } 20 | } 21 | } 22 | 23 | componentDidMount() { 24 | this.fetchInviteConfig() 25 | } 26 | 27 | fetchInviteConfig = () => { 28 | return getApi(`/user/invite`).then(response => { 29 | this.setState({ 30 | inviteConfig: response 31 | }) 32 | }) 33 | } 34 | fetchInviteLogs = () => { 35 | if (this.state.inviteLog.isFetching || !this.state.inviteLog.isMore) { 36 | return 37 | } 38 | return this.setState(state => ({ 39 | inviteLog: Object.assign({}, state.inviteLog, { 40 | isFetching: true 41 | }) 42 | }), () => { 43 | return getApi('/user/invite/log', Object.assign({}, this.state.inviteLog.filter, { 44 | offset: this.state.inviteLog.logs.length 45 | })).then(response => { 46 | this.setState(state => ({ 47 | inviteLog: Object.assign({}, state.inviteLog, { 48 | isFetching: false, 49 | isMore: (response.logs || []).length > state.inviteLog.filter.limit, 50 | logs: state.inviteLog.logs.concat(response.logs || []), 51 | total_bonus: response.total_bonus || state.inviteLog.total_bonus, 52 | total_user_num: response.total_user_num || state.inviteLog.total_user_num 53 | }) 54 | })) 55 | }) 56 | }) 57 | } 58 | onSwitchTab = (tabIndex) => { 59 | this.setState({ 60 | tabIndex 61 | }) 62 | } 63 | 64 | render() { 65 | const {tabIndex, inviteLog, inviteConfig} = this.state 66 | return 73 | } 74 | } -------------------------------------------------------------------------------- /app/actions/shop.js: -------------------------------------------------------------------------------- 1 | import {CALL_API} from "../middleware/api"; 2 | import * as cache from "../utils/cache"; 3 | 4 | export const SHOPS_REQUEST = 'SHOPS_REQUEST' 5 | export const SHOPS_SUCCESS = 'SHOPS_SUCCESS' 6 | export const SHOPS_FAIL = 'SHOPS_FAIL' 7 | 8 | export const FETCH_SHOPS_REQUEST = 'FETCH_SHOPS_REQUEST' 9 | export const FETCH_SHOPS_SUCCESS = 'FETCH_SHOPS_SUCCESS' 10 | export const FETCH_SHOPS_FAIL = 'FETCH_SHOPS_FAIL' 11 | 12 | export const SHOP_JOIN_CONFIG_REQUEST = 'SHOP_JOIN_CONFIG_REQUEST' 13 | export const SHOP_JOIN_CONFIG_SUCCESS = 'SHOP_JOIN_CONFIG_SUCCESS' 14 | export const SHOP_JOIN_CONFIG_FAIL = 'SHOP_JOIN_CONFIG_FAIL' 15 | 16 | 17 | export const getShops = (fullName, filter = {}, endpoint = '/shop') => (dispatch, getState) => { 18 | const repo = getState().shop[fullName] 19 | if (repo && repo.isFetched) { 20 | return null 21 | } 22 | cache.get(`${fullName}_shops`) && dispatch({ 23 | type: SHOPS_SUCCESS, 24 | response: cache.get(`${fullName}_shops`), 25 | fullName: fullName 26 | }) 27 | return dispatch({ 28 | [CALL_API]: { 29 | types: [SHOPS_REQUEST, SHOPS_SUCCESS, SHOPS_FAIL], 30 | endpoint: endpoint, 31 | body: filter 32 | }, 33 | fullName: fullName 34 | }).then(action => { 35 | action.response.code == 'SUCCESS' && cache.set(`${fullName}_shops`, action.response) 36 | return action 37 | }) 38 | } 39 | export const fetchShops = (filter) => (dispatch, getState) => { 40 | const {shop} = getState() 41 | filter = Object.assign(shop.filter, filter) 42 | if ((!shop.isMore && !filter.isRefreshing) || shop.isFetching) { 43 | return null 44 | } 45 | return dispatch({ 46 | isRefreshing: filter.isRefreshing, 47 | filter: filter, 48 | [CALL_API]: { 49 | types: [FETCH_SHOPS_REQUEST, FETCH_SHOPS_SUCCESS, FETCH_SHOPS_FAIL], 50 | endpoint: `/shop`, 51 | body: filter 52 | } 53 | }) 54 | } 55 | export const getJoinConfig = () => (dispatch, getState) => { 56 | const repo = getState().shop.join.config 57 | if (repo && repo.isFetched) { 58 | return null 59 | } 60 | return dispatch({ 61 | [CALL_API]: { 62 | types: [SHOP_JOIN_CONFIG_REQUEST, SHOP_JOIN_CONFIG_SUCCESS, SHOP_JOIN_CONFIG_FAIL], 63 | endpoint: `/shop/join/config` 64 | } 65 | }) 66 | } -------------------------------------------------------------------------------- /app/components/Shop/List/Item/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'components/Link'; 3 | import s from './style.scss'; 4 | import p from 'assets/images/s.gif'; 5 | 6 | export default ({shop}) => { 7 | return 8 | 9 |
10 |
11 |
{shop.title}
12 |
13 | 14 | {shop.uv} 15 |
16 |
17 | {(shop.tag || shop.distance) &&
18 |
{shop.tag}
19 |
20 |
{shop.distance.number}{shop.distance.unit}
21 |
} 22 | {shop.discount_info &&
23 | { 24 | shop.discount_info.scale < 10 ?
25 | 26 | {shop.discount_info.scale}折/酒水除外 27 |
:
28 | } 29 | {shop.discount_info.vip && shop.discount_info.vip.scale < 10 && 30 |
31 |
VIP
32 |
{shop.discount_info.vip.scale}
33 |
34 |
} 35 |
} 36 | {shop.redpackets &&
37 | 38 | 现金红包领不停 39 |
} 40 | {shop.discount_info &&
41 | 42 | {shop.discount_info.text} 43 |
} 44 |
45 | 46 | } --------------------------------------------------------------------------------