├── src ├── layouts │ ├── ProductLayout.less │ ├── BaseLayout.less │ ├── ProductLayout.js │ ├── MainLayout.js │ └── BaseLayout.js ├── Untitled.gif ├── assets │ ├── p1.jpg │ ├── p2.jpg │ ├── p3.png │ ├── p4.jpg │ ├── p5.jpg │ ├── ad1.jpg │ ├── car1.png │ ├── car2.png │ ├── me1.png │ ├── me2.png │ ├── pd1.jpg │ ├── pd2.jpg │ ├── pd3.jpg │ ├── pd4.jpg │ ├── sc1.png │ ├── sc2.png │ ├── sc3.png │ ├── sc4.png │ ├── banner1.gif │ ├── banner2.gif │ ├── banner3.gif │ ├── close.png │ ├── footer.png │ ├── home1.png │ ├── home2.png │ ├── sort1.png │ ├── sort2.png │ ├── koubei@2x.png │ ├── shopcar1.png │ ├── shopcar2.png │ ├── koubei_sel@2x.png │ ├── search_icon_2.png │ ├── list.svg │ ├── home.svg │ ├── scar.svg │ └── logo.svg ├── index.css ├── components │ ├── TabBar │ │ ├── index.less │ │ └── index.js │ ├── ImageAd │ │ ├── index.less │ │ └── index.js │ ├── ShortCut │ │ ├── index.less │ │ └── index.js │ ├── Footer │ │ ├── index.less │ │ └── index.js │ ├── Popup │ │ ├── index.less │ │ └── index.js │ ├── SearchProduct │ │ ├── index.js │ │ └── index.less │ ├── ProductDetail │ │ ├── index.less │ │ └── index.js │ ├── Stepper │ │ ├── index.less │ │ └── index.js │ ├── ProductList │ │ ├── index.js │ │ └── index.less │ └── HomeShopCar │ │ ├── index.less │ │ └── index.js ├── saga │ ├── globalSaga.js │ ├── productSaga.js │ ├── bannerSaga.js │ ├── homeShopcarSaga.js │ ├── shortcutSaga.js │ ├── productDetailSaga.js │ └── index.js ├── routes │ ├── classify.js │ ├── me.js │ ├── shopcar.js │ ├── homeShopcar.js │ ├── productList.js │ ├── shortcut.js │ ├── productdetail.js │ ├── home.js │ └── banner.js ├── utils │ ├── authority.js │ ├── Authorized.js │ ├── utils.less │ ├── request.js │ └── utils.js ├── redux │ ├── bannerRedux.js │ ├── productsRedux.js │ ├── shortCutRedux.js │ ├── homeShopcarRedux.js │ ├── productDetailRedux.js │ ├── globalRedux.js │ ├── index.js │ └── login.js ├── index.js ├── store │ └── index.js ├── common │ ├── router.js │ └── menu.js ├── services │ └── api.js ├── logo.svg ├── mock │ └── data.js └── registerServiceWorker.js ├── screenshot.gif ├── public ├── favicon.ico ├── manifest.json └── index.html ├── .gitignore ├── package.json ├── LICENSE ├── README.md └── config-overrides.js /src/layouts/ProductLayout.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.less: -------------------------------------------------------------------------------- 1 | .container{ 2 | 3 | width: 100%; 4 | 5 | } -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/screenshot.gif -------------------------------------------------------------------------------- /src/Untitled.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/Untitled.gif -------------------------------------------------------------------------------- /src/assets/p1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/p1.jpg -------------------------------------------------------------------------------- /src/assets/p2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/p2.jpg -------------------------------------------------------------------------------- /src/assets/p3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/p3.png -------------------------------------------------------------------------------- /src/assets/p4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/p4.jpg -------------------------------------------------------------------------------- /src/assets/p5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/p5.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/ad1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/ad1.jpg -------------------------------------------------------------------------------- /src/assets/car1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/car1.png -------------------------------------------------------------------------------- /src/assets/car2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/car2.png -------------------------------------------------------------------------------- /src/assets/me1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/me1.png -------------------------------------------------------------------------------- /src/assets/me2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/me2.png -------------------------------------------------------------------------------- /src/assets/pd1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/pd1.jpg -------------------------------------------------------------------------------- /src/assets/pd2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/pd2.jpg -------------------------------------------------------------------------------- /src/assets/pd3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/pd3.jpg -------------------------------------------------------------------------------- /src/assets/pd4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/pd4.jpg -------------------------------------------------------------------------------- /src/assets/sc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sc1.png -------------------------------------------------------------------------------- /src/assets/sc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sc2.png -------------------------------------------------------------------------------- /src/assets/sc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sc3.png -------------------------------------------------------------------------------- /src/assets/sc4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sc4.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .overflow_hidden{ 5 | 6 | overflow: hidden !important; 7 | 8 | } -------------------------------------------------------------------------------- /src/assets/banner1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/banner1.gif -------------------------------------------------------------------------------- /src/assets/banner2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/banner2.gif -------------------------------------------------------------------------------- /src/assets/banner3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/banner3.gif -------------------------------------------------------------------------------- /src/assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/close.png -------------------------------------------------------------------------------- /src/assets/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/footer.png -------------------------------------------------------------------------------- /src/assets/home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/home1.png -------------------------------------------------------------------------------- /src/assets/home2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/home2.png -------------------------------------------------------------------------------- /src/assets/sort1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sort1.png -------------------------------------------------------------------------------- /src/assets/sort2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/sort2.png -------------------------------------------------------------------------------- /src/assets/koubei@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/koubei@2x.png -------------------------------------------------------------------------------- /src/assets/shopcar1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/shopcar1.png -------------------------------------------------------------------------------- /src/assets/shopcar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/shopcar2.png -------------------------------------------------------------------------------- /src/assets/koubei_sel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/koubei_sel@2x.png -------------------------------------------------------------------------------- /src/assets/search_icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonsir/wx-mobile-app/HEAD/src/assets/search_icon_2.png -------------------------------------------------------------------------------- /src/components/TabBar/index.less: -------------------------------------------------------------------------------- 1 | .container{ 2 | 3 | position: fixed; 4 | left: 0px; 5 | bottom: 0px; 6 | width: 100%; 7 | background-color: #fff; 8 | box-sizing: border-box; 9 | 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/components/ImageAd/index.less: -------------------------------------------------------------------------------- 1 | .image_ad_container{ 2 | 3 | a{ 4 | display: block; 5 | width: 100%; 6 | img{ 7 | height: 104px; 8 | width: inherit; 9 | } 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/saga/globalSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | 3 | 4 | 5 | 6 | function* getSubTabPosition(){ 7 | 8 | 9 | 10 | 11 | } 12 | 13 | 14 | 15 | 16 | function* globalSaga() { 17 | 18 | 19 | 20 | } 21 | 22 | export default globalSaga; -------------------------------------------------------------------------------- /src/routes/classify.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | 3 | 4 | class Classify extends PureComponent{ 5 | 6 | render(){ 7 | return ( 8 |
9 | 商品分类 10 |
11 | ) 12 | } 13 | 14 | } 15 | 16 | export default Classify; -------------------------------------------------------------------------------- /src/routes/me.js: -------------------------------------------------------------------------------- 1 | import React ,{PureComponent} from 'react' 2 | 3 | class Me extends PureComponent{ 4 | 5 | 6 | render(){ 7 | 8 | return( 9 |
10 | 11 | me 12 |
13 | ) 14 | 15 | } 16 | 17 | 18 | } 19 | 20 | export default Me; -------------------------------------------------------------------------------- /src/utils/authority.js: -------------------------------------------------------------------------------- 1 | // use localStorage to store the authority info, which might be sent from server in actual project. 2 | export function getAuthority() { 3 | return localStorage.getItem('antd-pro-authority'); 4 | } 5 | 6 | export function setAuthority(authority) { 7 | return localStorage.setItem('antd-pro-authority', authority); 8 | } 9 | -------------------------------------------------------------------------------- /src/layouts/ProductLayout.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import ProductDetail from '../routes/productdetail' 3 | 4 | export default class ProductLayout extends PureComponent{ 5 | 6 | 7 | render(){ 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | ) 15 | 16 | 17 | } 18 | 19 | 20 | 21 | } -------------------------------------------------------------------------------- /src/routes/shopcar.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | 3 | class ShopCar extends PureComponent{ 4 | 5 | render(){ 6 | 7 | return( 8 | 9 |
10 | shopcar 11 |
12 | 13 | 14 | ) 15 | 16 | 17 | 18 | 19 | } 20 | 21 | 22 | } 23 | 24 | export default ShopCar; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/Authorized.js: -------------------------------------------------------------------------------- 1 | import RenderAuthorized from 'ant-design-pro/lib/Authorized'; 2 | import { getAuthority } from './authority'; 3 | 4 | let Authorized = RenderAuthorized(getAuthority()); // eslint-disable-line 5 | 6 | // Reload the rights component 7 | const reloadAuthorized = () => { 8 | Authorized = RenderAuthorized(getAuthority()); 9 | }; 10 | 11 | export { reloadAuthorized }; 12 | export default Authorized; 13 | -------------------------------------------------------------------------------- /src/redux/bannerRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | data:[] 4 | 5 | } 6 | 7 | function bannerRedux(state=initstate,{type,payload}){ 8 | 9 | switch(type){ 10 | 11 | case 'getBannerListRedux': 12 | 13 | return { 14 | ...state, 15 | data:payload 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | 22 | 23 | } 24 | 25 | export default bannerRedux; -------------------------------------------------------------------------------- /src/redux/productsRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | data:[] 4 | 5 | } 6 | 7 | function productsRedux(state=initstate,{type,payload}){ 8 | 9 | switch(type){ 10 | 11 | case 'getProduct_1ReduxList': 12 | 13 | return { 14 | ...state, 15 | data:payload 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | 22 | 23 | } 24 | 25 | export default productsRedux; -------------------------------------------------------------------------------- /src/redux/shortCutRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | data:[] 4 | 5 | } 6 | 7 | function shortCutRedux(state=initstate,{type,payload}){ 8 | 9 | switch(type){ 10 | 11 | case 'getShortcutReduxList': 12 | 13 | return { 14 | ...state, 15 | data:payload 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | 22 | 23 | } 24 | 25 | export default shortCutRedux; -------------------------------------------------------------------------------- /src/redux/homeShopcarRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | data:{}, 4 | 5 | } 6 | 7 | function homeShopcarRedux(state=initstate,{type,payload}){ 8 | 9 | switch(type){ 10 | 11 | case 'addHomeShopCar': 12 | 13 | return { 14 | ...state, 15 | data:payload, 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | 22 | 23 | } 24 | 25 | export default homeShopcarRedux; -------------------------------------------------------------------------------- /src/redux/productDetailRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | data:{} 4 | 5 | } 6 | 7 | function productDetailRedux(state=initstate,{type,payload}){ 8 | 9 | switch(type){ 10 | 11 | case 'pd/prodcutDetailByid': 12 | 13 | return { 14 | ...state, 15 | data:payload, 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | 22 | 23 | } 24 | 25 | export default productDetailRedux; -------------------------------------------------------------------------------- /src/saga/productSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | import {getproduct_1} from '../services/api' 3 | 4 | 5 | 6 | function* productList_1(){ 7 | 8 | const res = yield call(getproduct_1) 9 | 10 | yield put({ 11 | type:'getProduct_1ReduxList', 12 | payload:res.data, 13 | }) 14 | 15 | 16 | } 17 | 18 | 19 | function* productSaga(){ 20 | 21 | yield takeLatest('getproduct_1',productList_1) 22 | 23 | } 24 | 25 | export default productSaga; -------------------------------------------------------------------------------- /src/saga/bannerSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | import {getBanners} from '../services/api' 3 | 4 | function* getBannerList(){ 5 | 6 | const res = yield call(getBanners); 7 | 8 | yield put({ 9 | type:'getBannerListRedux', 10 | payload:res.data, 11 | } 12 | ) 13 | 14 | 15 | 16 | } 17 | 18 | 19 | 20 | function* bannerSaga(){ 21 | 22 | yield takeLatest('getBannerListSaga',getBannerList) 23 | 24 | } 25 | 26 | export default bannerSaga; -------------------------------------------------------------------------------- /src/saga/homeShopcarSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | import {getHShopCar} from '../services/api' 3 | 4 | 5 | 6 | function* getHomeShopcarSaga(){ 7 | 8 | const res = yield call(getHShopCar) 9 | 10 | yield put({ 11 | type:'addHomeShopCar', 12 | payload:res.data, 13 | }) 14 | 15 | 16 | } 17 | 18 | 19 | function* homeShopcarSaga(){ 20 | 21 | yield takeLatest('getHomeShopcarSaga',getHomeShopcarSaga) 22 | 23 | } 24 | 25 | export default homeShopcarSaga; -------------------------------------------------------------------------------- /src/saga/shortcutSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | import {getShortcutList} from '../services/api' 3 | 4 | 5 | function* getShortcutSagaList(){ 6 | 7 | const res = yield call(getShortcutList) 8 | 9 | yield put({ 10 | type:'getShortcutReduxList', 11 | payload:res.data, 12 | }) 13 | 14 | 15 | } 16 | 17 | 18 | function* shortcutSaga(){ 19 | 20 | yield takeLatest('getShortcutSagaList',getShortcutSagaList) 21 | 22 | } 23 | 24 | export default shortcutSaga; -------------------------------------------------------------------------------- /src/components/ShortCut/index.less: -------------------------------------------------------------------------------- 1 | .containter{ 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: flex-start; 5 | flex-wrap: wrap; 6 | width: inherit; 7 | overflow: hidden; 8 | 9 | div{ 10 | 11 | width: 25%; 12 | 13 | a{ 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | 18 | img{ 19 | 20 | width: 100%; 21 | 22 | } 23 | 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/redux/globalRedux.js: -------------------------------------------------------------------------------- 1 | const initstate = { 2 | 3 | homePopup:true, 4 | } 5 | 6 | function globalRedux(state=initstate,{type,payload}){ 7 | 8 | switch(type){ 9 | 10 | case 'openPopup': 11 | 12 | return { 13 | ...state, 14 | homePopup:false, 15 | } 16 | case 'closePopup': 17 | 18 | return { 19 | ...state, 20 | homePopup:true, 21 | } 22 | 23 | default: 24 | return state; 25 | } 26 | 27 | 28 | } 29 | 30 | export default globalRedux; -------------------------------------------------------------------------------- /src/saga/productDetailSaga.js: -------------------------------------------------------------------------------- 1 | import { put,takeLatest,call,select } from 'redux-saga/effects' 2 | import {getProductdetailById} from '../services/api' 3 | 4 | 5 | 6 | function* getProductdetailSaga({payload}){ 7 | 8 | const res = yield call(getProductdetailById,payload); 9 | 10 | 11 | yield put({ 12 | type:'pd/prodcutDetailByid', 13 | payload:res, 14 | } 15 | ) 16 | 17 | 18 | 19 | } 20 | 21 | 22 | 23 | function* productDetailSaga(){ 24 | 25 | yield takeLatest('getProductdetailSaga',getProductdetailSaga) 26 | 27 | } 28 | 29 | export default productDetailSaga; -------------------------------------------------------------------------------- /src/components/Footer/index.less: -------------------------------------------------------------------------------- 1 | .footer_container{ 2 | 3 | color: #666; 4 | 5 | .content{ 6 | width: 200px; 7 | margin: auto; 8 | display: flex; 9 | flex-direction: row; 10 | align-items: center; 11 | a{ 12 | display: flex; 13 | flex-direction: row; 14 | justify-content: center; 15 | align-items: center; 16 | color: #666; 17 | font-size: 14px; 18 | img{ 19 | margin-right: 5px; 20 | } 21 | margin-right: 5px; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/routes/homeShopcar.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import {connect} from 'react-redux' 3 | import {default as HomeShopcarComponent} from '../components/HomeShopCar' 4 | 5 | 6 | class HomeShopcar extends PureComponent{ 7 | 8 | render(){ 9 | 10 | const { ajaxdata } = this.props; 11 | 12 | return ( 13 | 14 | ) 15 | } 16 | 17 | } 18 | 19 | export default connect(({ homeShopcarRedux })=>{ 20 | 21 | return { 22 | ajaxdata : homeShopcarRedux.data 23 | } 24 | 25 | })(HomeShopcar) -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import Styles from './index.less' 3 | import footer from '../../assets/footer.png' 4 | 5 | /** 6 | * 页尾组件 7 | */ 8 | export default class Footer extends PureComponent{ 9 | 10 | render(){ 11 | return ( 12 |
13 |
14 | 码农之家 提供技术支持 15 |
16 |
17 | ) 18 | } 19 | 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/saga/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { put,takeEvery,fork,all } from 'redux-saga/effects' 3 | import globalSaga from './globalSaga' 4 | import bannerSaga from './bannerSaga' 5 | import shortcutSaga from './shortcutSaga' 6 | import productSaga from './productSaga' 7 | import homeShopcarSaga from './homeShopcarSaga' 8 | import productDetailSaga from './productDetailSaga' 9 | 10 | // 所有saga的入口配置文件 11 | const config = [ 12 | 13 | fork(globalSaga), 14 | fork(bannerSaga), 15 | fork(shortcutSaga), 16 | fork(productSaga), 17 | fork(homeShopcarSaga), 18 | fork(productDetailSaga), 19 | 20 | ] 21 | 22 | 23 | export default function* rootSaga(){ 24 | 25 | yield all(config) 26 | 27 | }; -------------------------------------------------------------------------------- /src/components/ImageAd/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import {Link} from 'react-router-dom' 3 | import Styles from './index.less' 4 | import classNames from 'classnames' 5 | 6 | class ImageAd extends PureComponent{ 7 | 8 | 9 | render(){ 10 | 11 | const { imgsrc,imgurl } = this.props; 12 | 13 | return ( 14 | // 图片广告 15 |
16 | 17 | 18 | 19 | 20 | 21 |
22 | ) 23 | 24 | } 25 | 26 | 27 | } 28 | 29 | export default ImageAd; -------------------------------------------------------------------------------- /src/routes/productList.js: -------------------------------------------------------------------------------- 1 | import React ,{PureComponent} from 'react' 2 | 3 | import {default as Products} from '../components/ProductList' 4 | 5 | import {connect} from 'react-redux' 6 | 7 | 8 | class ProductList extends PureComponent{ 9 | 10 | componentDidMount(){ 11 | 12 | this.props.dispatch({ 13 | type:'getproduct_1' 14 | }) 15 | 16 | 17 | } 18 | 19 | 20 | render(){ 21 | 22 | 23 | return ( 24 | 25 | 26 | ) 27 | 28 | } 29 | 30 | 31 | } 32 | 33 | export default connect(({productsRedux})=>{ 34 | 35 | return { 36 | data : productsRedux.data, 37 | } 38 | 39 | })(ProductList); -------------------------------------------------------------------------------- /src/redux/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { combineReducers } from 'redux'; 3 | import { 4 | routerReducer, 5 | } from 'react-router-redux' 6 | import bannerRedux from './bannerRedux' 7 | import shortCutRedux from './shortCutRedux' 8 | import productsRedux from './productsRedux' 9 | import globalRedux from './globalRedux' 10 | import homeShopcarRedux from './homeShopcarRedux' 11 | import productDetailRedux from './productDetailRedux' 12 | 13 | 14 | const config = { 15 | routerReducer, 16 | bannerRedux, 17 | shortCutRedux, 18 | productsRedux, 19 | globalRedux, 20 | homeShopcarRedux, 21 | productDetailRedux, 22 | } 23 | 24 | 25 | 26 | 27 | 28 | export default combineReducers(config); 29 | 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import React from 'react' 3 | import { render } from 'react-dom' 4 | import { connect, Provider } from 'react-redux' 5 | import store ,{history} from './store' 6 | import { ConnectedRouter } from 'react-router-redux' 7 | import { Route, Switch,Redirect } from 'react-router-dom' 8 | 9 | import BaseLayout from './layouts/BaseLayout' 10 | 11 | import 'weui' 12 | import 'react-weui/build/packages/react-weui.css'; 13 | 14 | import './index.css'; 15 | 16 | 17 | render( 18 | 19 | 20 | 21 | 22 | 23 | 24 | , 25 | document.getElementById('root') 26 | ) -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import createSagaMiddleware from 'redux-saga' 2 | import createHistory from 'history/createBrowserHistory' 3 | import { createStore, applyMiddleware, compose } from 'redux' 4 | import { routerMiddleware } from 'react-router-redux' 5 | import reducer from '../redux/index' 6 | import rootSaga from '../saga/index' 7 | 8 | const history = createHistory() 9 | 10 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 11 | 12 | const sagaMiddleware = createSagaMiddleware() 13 | 14 | const middlewares = [ routerMiddleware(history),sagaMiddleware]; 15 | 16 | const store = createStore( 17 | reducer, 18 | composeEnhancers(applyMiddleware(...middlewares)) 19 | ) 20 | 21 | sagaMiddleware.run(rootSaga) 22 | 23 | export {history} 24 | 25 | export default store; -------------------------------------------------------------------------------- /src/routes/shortcut.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import {connect} from 'react-redux' 3 | 4 | import { default as ShortCutComponent} from '../components/ShortCut' 5 | 6 | /** 7 | * @author pythonsir| baidu211@vip.qq.com | 微信: cxyzxh1388 8 | * 9 | * 首页中部快捷按钮组件 10 | */ 11 | class ShortCut extends PureComponent{ 12 | 13 | 14 | 15 | 16 | componentDidMount(){ 17 | 18 | this.props.dispatch({ 19 | type: 'getShortcutSagaList', 20 | }); 21 | 22 | } 23 | 24 | 25 | 26 | render(){ 27 | 28 | return ( 29 | 30 | ) 31 | 32 | } 33 | 34 | 35 | } 36 | 37 | export default connect( 38 | ({ shortCutRedux,})=>{ 39 | 40 | return { 41 | data : shortCutRedux.data, 42 | } 43 | 44 | }, 45 | )(ShortCut); -------------------------------------------------------------------------------- /src/common/router.js: -------------------------------------------------------------------------------- 1 | import Home from '../routes/home' 2 | import Classify from '../routes/classify' 3 | import Me from '../routes/me' 4 | import ShopCar from '../routes/shopcar' 5 | import BasicLayout from '../layouts/BaseLayout' 6 | 7 | 8 | export const getRouterData = () => { 9 | 10 | const routerConfig = [ 11 | 12 | 13 | { 14 | path:'/home', 15 | component:Home, 16 | }, 17 | { 18 | path:'/home/classify', 19 | component:Classify, 20 | }, 21 | { 22 | path:'/home/shopcar', 23 | component:ShopCar, 24 | }, 25 | { 26 | path:'/home/me', 27 | component:Me, 28 | } 29 | 30 | 31 | ] 32 | 33 | return routerConfig; 34 | 35 | } -------------------------------------------------------------------------------- /src/components/ShortCut/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | 3 | import {Link} from 'react-router-dom' 4 | 5 | import Styles from './index.less' 6 | 7 | class ShortCut extends PureComponent { 8 | 9 | 10 | render(){ 11 | 12 | const {data} = this.props; 13 | 14 | return ( 15 | 16 |
17 | 18 | { 19 | 20 | 21 | data.map((item,index)=>( 22 | 23 |
24 | 25 | 26 | 27 | 28 |
29 | 30 | )) 31 | 32 | 33 | } 34 | 35 | 36 | 37 |
38 | 39 | ) 40 | 41 | 42 | 43 | 44 | } 45 | 46 | 47 | 48 | } 49 | 50 | export default ShortCut; -------------------------------------------------------------------------------- /src/components/Popup/index.less: -------------------------------------------------------------------------------- 1 | .wx_modal{ 2 | position: fixed; 3 | width: 100%; 4 | height: 100%; 5 | top: 0; 6 | left: 0; 7 | background-color: rgba(0, 0, 0, 0.7); 8 | z-index: 2008; 9 | } 10 | 11 | .wx_popup{ 12 | 13 | position: fixed; 14 | background-color: #fff; 15 | top: 50%; 16 | left: 50%; 17 | -webkit-transform: translate3d(-50%, -50%, 0); 18 | -moz-transform: translate3d(-50%, -50%, 0); 19 | transform: translate3d(-50%, -50%, 0); 20 | -webkit-transition: .2s ease-out; 21 | -moz-transition: .2s ease-out; 22 | transition: .2s ease-out; 23 | 24 | } 25 | .wx_content_bottom{ 26 | 27 | width: 100%; 28 | top: auto; 29 | bottom: 0; 30 | right: auto; 31 | left: 50%; 32 | -webkit-transform: translate3d(-50%, 0, 0); 33 | -moz-transform: translate3d(-50%, 0, 0); 34 | transform: translate3d(-50%, 0, 0); 35 | z-index: 2009; 36 | 37 | } -------------------------------------------------------------------------------- /src/components/SearchProduct/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import Styles from './index.less' 3 | import buttonimg from '../../assets/search_icon_2.png' 4 | 5 | class SearchProduct extends PureComponent{ 6 | 7 | 8 | 9 | 10 | 11 | render(){ 12 | 13 | const {text} = this.props; 14 | 15 | const bg ={ 16 | 17 | backgroundImage: `url(${buttonimg})`, 18 | 19 | 20 | } 21 | 22 | return ( 23 |
24 |
25 | 26 | 27 |
28 | 29 | 30 |
31 | ) 32 | 33 | } 34 | } 35 | 36 | export default SearchProduct; -------------------------------------------------------------------------------- /src/components/Popup/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react' 2 | import classNames from 'classnames' 3 | import Styles from './index.less' 4 | import HomeShopcar from '../../routes/homeShopcar' 5 | 6 | 7 | /** 8 | * 弹出层 9 | */ 10 | class Popup extends PureComponent{ 11 | 12 | 13 | 14 | render(){ 15 | 16 | 17 | const {flag} = this.props; 18 | 19 | return( 20 | 21 |
22 | 23 | 24 |
26 | 27 | 28 | 29 |
30 | 31 | 32 |
33 | 34 | 35 |
36 | 37 |
38 | 39 | ) 40 | 41 | 42 | } 43 | 44 | } 45 | 46 | export default Popup; -------------------------------------------------------------------------------- /src/layouts/MainLayout.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | 3 | import {Switch,Route,Redirect} from 'react-router-dom' 4 | 5 | 6 | import {getRouterData} from '../common/router' 7 | 8 | import WxTabBar from '../components/TabBar' 9 | 10 | export default class MainLayout extends PureComponent{ 11 | 12 | 13 | 14 | render(){ 15 | return( 16 |
17 | 18 | 19 | 20 | { 21 | getRouterData().map((item,index)=>( 22 | 23 | 24 | 25 | )) 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | ) 38 | } 39 | 40 | 41 | } -------------------------------------------------------------------------------- /src/redux/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { setAuthority } from '../utils/authority'; 3 | 4 | const initstate={ 5 | 6 | status:undefined, 7 | submitting:false, 8 | 9 | } 10 | 11 | function commonlogin(state,type,payload){ 12 | 13 | setAuthority(payload.currentAuthority); 14 | 15 | return { 16 | ...state, 17 | status:payload.status, 18 | type:payload.type, 19 | submitting:payload.status == 'error'?false:true, 20 | } 21 | 22 | } 23 | 24 | 25 | function login(state = initstate, {type,payload}) { 26 | 27 | switch (type) { 28 | case 'commonlogin': 29 | return commonlogin(state,type,payload) 30 | case 'tologin': 31 | return { 32 | ...state, 33 | status:payload.status, 34 | type:payload.type, 35 | submitting:false, 36 | 37 | } 38 | case 'mobilelogin': 39 | 40 | return state 41 | case 'loading': 42 | return { 43 | ...initstate, 44 | submitting:true 45 | } 46 | default: 47 | return state 48 | } 49 | } 50 | 51 | export default login; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wx-mobile-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "amfe-flexible": "^2.2.1", 7 | "antd-mobile": "^2.1.8", 8 | "axios": "^0.18.0", 9 | "babel-polyfill": "^6.26.0", 10 | "classnames": "^2.2.5", 11 | "history": "^4.7.2", 12 | "mockjs": "^1.0.1-beta3", 13 | "react": "^16.2.0", 14 | "react-dom": "^16.2.0", 15 | "react-redux": "^5.0.7", 16 | "react-router-dom": "^4.2.2", 17 | "react-router-redux": "^5.0.0-alpha.9", 18 | "react-scripts": "1.1.1", 19 | "react-weui": "^1.1.3", 20 | "redux": "^3.7.2", 21 | "redux-saga": "^0.16.0", 22 | "weui": "1.1.1" 23 | }, 24 | "scripts": { 25 | "start": "react-app-rewired start", 26 | "build": "react-app-rewired build", 27 | "test": "react-app-rewired test --env=jsdom" 28 | }, 29 | "devDependencies": { 30 | "babel-plugin-import": "^1.6.7", 31 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 32 | "less": "^2.7.3", 33 | "less-loader": "^4.0.5", 34 | "react-app-rewired": "^1.5.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ProductDetail/index.less: -------------------------------------------------------------------------------- 1 | .banner{ 2 | 3 | } 4 | .pro_header{ 5 | 6 | display: block; 7 | height: auto; 8 | padding: 10px 0 0; 9 | -webkit-tap-highlight-color: transparent; 10 | border-top: 1px solid #f2f2f2; 11 | background-color: #fff; 12 | margin-bottom: 10px; 13 | border-bottom-width: 0; 14 | 15 | .title{ 16 | font-weight: normal; 17 | margin: 0; 18 | padding: 0 10px; 19 | font-size: 16px; 20 | line-height: 20px; 21 | text-align: left; 22 | overflow: hidden; 23 | text-overflow: ellipsis; 24 | display: -webkit-box; 25 | /* autoprefixer: off*/ 26 | -webkit-line-clamp: 2; 27 | -webkit-box-orient: vertical; 28 | /* autoprefixer: on*/ 29 | 30 | } 31 | 32 | } 33 | .pro_price{ 34 | 35 | padding: 10px 10px 2px; 36 | text-align: left; 37 | } 38 | .pro_curr_price{ 39 | color: #f44!important; 40 | span{ 41 | font-size: 16px; 42 | } 43 | .price{ 44 | display: inline-block; 45 | vertical-align: middle; 46 | font-size: 18px; 47 | } 48 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2016-present Alipay.com, https://www.manzj.net/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/utils/utils.less: -------------------------------------------------------------------------------- 1 | .textOverflow() { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | word-break: break-all; 5 | white-space: nowrap; 6 | } 7 | 8 | .textOverflowMulti(@line: 3, @bg: #fff) { 9 | overflow: hidden; 10 | position: relative; 11 | line-height: 1.5em; 12 | max-height: @line * 1.5em; 13 | text-align: justify; 14 | margin-right: -1em; 15 | padding-right: 1em; 16 | &:before { 17 | background: @bg; 18 | content: '...'; 19 | padding: 0 1px; 20 | position: absolute; 21 | right: 14px; 22 | bottom: 0; 23 | } 24 | &:after { 25 | background: white; 26 | content: ''; 27 | margin-top: 0.2em; 28 | position: absolute; 29 | right: 14px; 30 | width: 1em; 31 | height: 1em; 32 | } 33 | } 34 | 35 | // mixins for clearfix 36 | // ------------------------ 37 | .clearfix() { 38 | zoom: 1; 39 | &:before, 40 | &:after { 41 | content: " "; 42 | display: table; 43 | } 44 | &:after { 45 | clear: both; 46 | visibility: hidden; 47 | font-size: 0; 48 | height: 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/routes/productdetail.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import {connect} from 'react-redux' 3 | import {history} from '../store' 4 | import {withRouter} from 'react-router-dom' 5 | import ProductDetailCom from '../components/ProductDetail' 6 | 7 | 8 | 9 | @connect(({productDetailRedux})=>( 10 | productDetailRedux 11 | )) 12 | class ProductDetail extends PureComponent{ 13 | 14 | 15 | componentDidMount(){ 16 | 17 | const {location} = this.props; 18 | 19 | if( undefined == location.search || "" == location.search){ 20 | 21 | history.push("/home") 22 | 23 | }else{ 24 | 25 | this.props.dispatch({ 26 | type:'getProductdetailSaga', 27 | payload:{"id":this.props.location.search} 28 | }) 29 | 30 | } 31 | 32 | console.log(this.props.location.search) 33 | 34 | 35 | 36 | 37 | } 38 | 39 | render(){ 40 | 41 | return ( 42 | 43 | 44 | ) 45 | 46 | 47 | } 48 | 49 | 50 | } 51 | 52 | export default withRouter(ProductDetail) 53 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.js: -------------------------------------------------------------------------------- 1 | import React ,{PureComponent} from 'react' 2 | 3 | import {Switch,Route,Redirect} from 'react-router-dom' 4 | 5 | import MainLayout from './MainLayout' 6 | 7 | import Styles from './BaseLayout.less' 8 | 9 | import ProductLayout from './ProductLayout' 10 | 11 | 12 | 13 | 14 | 15 | 16 | class BaseLayout extends PureComponent{ 17 | 18 | 19 | constructor(props) { 20 | super(props); 21 | this.state = { 22 | selectedTab: 'blueTab', 23 | hidden: false, 24 | fullScreen: true, 25 | }; 26 | } 27 | 28 | 29 | 30 | render(){ 31 | 32 | 33 | const { match } = this.props; 34 | 35 | 36 | return ( 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | ) 51 | 52 | 53 | 54 | } 55 | 56 | 57 | 58 | 59 | } 60 | 61 | export default BaseLayout; -------------------------------------------------------------------------------- /src/assets/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/menu.js: -------------------------------------------------------------------------------- 1 | 2 | import home1 from '../assets/home1.png' 3 | import home2 from '../assets/home2.png' 4 | 5 | import me1 from '../assets/me1.png' 6 | import me2 from '../assets/me2.png' 7 | 8 | import shopcar1 from '../assets/shopcar1.png' 9 | import shopcar2 from '../assets/shopcar2.png' 10 | 11 | import sort1 from '../assets/sort1.png' 12 | import sort2 from '../assets/sort2.png' 13 | 14 | /** 15 | * tabbar菜单 16 | */ 17 | 18 | const menuData = [ 19 | 20 | { 21 | name:'主页', 22 | key:'home', 23 | path:'/home', 24 | icon: home1, 25 | selectedIcon:home2, 26 | unselectedTintColor:"#949494", 27 | tintColor:"#33A3F4", 28 | }, 29 | { 30 | name:'分类', 31 | key:'classify', 32 | path:'/home/classify', 33 | icon: sort1, 34 | selectedIcon:sort2, 35 | unselectedTintColor:"#949494", 36 | tintColor:"#33A3F4", 37 | }, 38 | { 39 | name:'购物车', 40 | key:'shopcar', 41 | path:'/home/shopcar', 42 | icon: shopcar1, 43 | selectedIcon:shopcar2, 44 | unselectedTintColor:"#949494", 45 | tintColor:"#33A3F4", 46 | }, 47 | { 48 | name:'我的', 49 | key:'me', 50 | path:'/home/me', 51 | icon: me1, 52 | selectedIcon:me2, 53 | unselectedTintColor:"#949494", 54 | tintColor:"#33A3F4", 55 | }, 56 | 57 | 58 | 59 | ] 60 | 61 | export { 62 | menuData 63 | } -------------------------------------------------------------------------------- /src/routes/home.js: -------------------------------------------------------------------------------- 1 | import React ,{PureComponent} from 'react' 2 | 3 | import classNames from 'classnames' 4 | 5 | import {connect} from 'react-redux' 6 | 7 | import Banner from '../routes/banner' 8 | 9 | import ShortCut from '../routes/shortcut' 10 | 11 | import SearchProduct from '../components/SearchProduct' 12 | 13 | import ImageAd from '../components/ImageAd' 14 | 15 | import Popup from '../components/Popup' 16 | 17 | import ad1 from '../assets/ad1.jpg' 18 | 19 | import {menuData} from '../common/menu' 20 | 21 | import ProductList from '../routes/productList' 22 | import { width } from 'window-size'; 23 | import { inherits } from 'util'; 24 | 25 | import Styles from '../index.css' 26 | 27 | import Footer from '../components/Footer' 28 | 29 | 30 | class Home extends PureComponent{ 31 | 32 | 33 | 34 | render(){ 35 | 36 | const {homePopup,dispatch} = this.props; 37 | 38 | return ( 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
49 | 50 | ) 51 | 52 | 53 | } 54 | 55 | 56 | } 57 | 58 | export default connect(({globalRedux})=>{ 59 | return { 60 | homePopup:globalRedux.homePopup, 61 | } 62 | })(Home) 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # wx-mobile-app 3 | 4 | [![node](https://img.shields.io/badge/node-%3E%3D7.9.0-blue.svg)]() 5 | [![npm](https://img.shields.io/badge/npm-%3E%3D4.2.0-green.svg)]() 6 | 7 | 使用 [Create-React-App](https://github.com/facebook/create-react-app) 脚手架,集合了react-redux、react-router-redux、redux-saga、axios 等构建的开源微商城 8 | 9 | 10 | ## 效果gif 11 | 12 | ![](https://github.com/pythonsir/wx-mobile-app/blob/master/screenshot.gif) 13 | 14 | ## 项目结构 15 | 16 | ``` 17 | - build 打包后的项目目录 18 | - public 19 | - favicon.ico 20 | - index.html 主页面 21 | - mainifest.json 22 | - src 源码文件夹 23 | - assets 资源文件夹 24 | - common 25 | - menu.js 底部Tabbar配置 26 | - router.js Tabbar切换对应的组件 27 | - components 组件文件夹 28 | ... 29 | - layouts 布局文件夹 30 | - BaseLayout.js 后台布局 31 | - BaseLayout.less 32 | - mock 模拟数据文件夹 33 | ...... 34 | - redux 35 | - index.js 所有同步redux的入口 36 | ...... 37 | - routes 容器组件文件夹 38 | ...... 39 | - saga 40 | - index.js 异步redux的入口 41 | ...... 42 | - services 43 | - api.js 请求入口 44 | - store 45 | - index.js 46 | - untils 辅助类 47 | ..... 48 | index.js 49 | index.css 50 | - config-overrides.js webpack配置文件 51 | ...... 52 | ``` 53 | 54 | 55 | ## 安装 56 | 下载完成后,在项目根目录使用 `yarn install` , 推荐使用 `yarn` 安装。 57 | ## 启动 58 | 在根目录运行如下命令: 59 | `yarn start` 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/services/api.js: -------------------------------------------------------------------------------- 1 | 2 | import axios from 'axios' 3 | require('../mock/data') 4 | 5 | 6 | export async function getBanners(){ 7 | 8 | 9 | return axios.get('/api/banner'); 10 | 11 | } 12 | 13 | export async function getShortcutList(){ 14 | 15 | 16 | return axios.get('/api/shortcut') 17 | 18 | } 19 | 20 | export async function getproduct_1(){ 21 | 22 | return axios.get('/api/products_1') 23 | 24 | 25 | } 26 | 27 | export async function getHShopCar(){ 28 | return axios.get('/api/getshopcar') 29 | } 30 | 31 | 32 | export async function getProductdetailById(param){ 33 | 34 | 35 | return axios.get('/api/productdetail/').then(function(response){ 36 | 37 | 38 | return response.data; 39 | 40 | }) 41 | 42 | } 43 | 44 | export async function login(param) { 45 | 46 | const {username,password,type} = param; 47 | 48 | 49 | return axios.get('/api/login').then(function(response){ 50 | 51 | 52 | if(username === 'admin' && password === '888888'){ 53 | 54 | return { 55 | status:'ok', 56 | type:type, 57 | currentAuthority:'admin' 58 | }; 59 | 60 | }else if(password === '123456' && username === 'user'){ 61 | 62 | return { 63 | status: 'ok', 64 | type, 65 | currentAuthority: 'user' 66 | } 67 | 68 | }else{ 69 | 70 | return { 71 | status:'error', 72 | type:type, 73 | } 74 | 75 | } 76 | 77 | }) 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/components/Stepper/index.less: -------------------------------------------------------------------------------- 1 | .wx_stepper{ 2 | 3 | top: 7px; 4 | left: 4px; 5 | float: right; 6 | font-size: 0; 7 | } 8 | .wx_stepper__stepper{ 9 | 10 | width: 40px; 11 | height: 30px; 12 | box-sizing: border-box; 13 | background-color: #fff; 14 | border: 1px solid #ccc; 15 | position: relative; 16 | padding: 5px; 17 | vertical-align: middle; 18 | 19 | } 20 | 21 | 22 | 23 | .wx_stepper__minus{ 24 | 25 | border-radius: 2px 0 0 2px; 26 | 27 | } 28 | .wx_stepper__input{ 29 | 30 | width: 33px; 31 | height: 26px; 32 | padding: 1px; 33 | border: 1px solid #ccc; 34 | border-width: 1px 0; 35 | border-radius: 0; 36 | -webkit-box-sizing: content-box; 37 | -moz-box-sizing: content-box; 38 | box-sizing: content-box; 39 | color: #666; 40 | font-size: 14px; 41 | vertical-align: middle; 42 | text-align: center; 43 | -webkit-appearance: none; 44 | 45 | 46 | } 47 | .wx_stepper__stepper::after,.wx_stepper__stepper::before{ 48 | 49 | content: ''; 50 | position: absolute; 51 | margin: auto; 52 | top: 0; 53 | left: 0; 54 | right: 0; 55 | bottom: 0; 56 | background-color: #6c6c6c; 57 | width: 9px; 58 | height: 1px; 59 | } 60 | 61 | 62 | .wx_stepper__stepper_1::before { 63 | width: 9px; 64 | height: 1px; 65 | } 66 | .wx_stepper__stepper_2::after{ 67 | 68 | width: 1px; 69 | height: 9px; 70 | } 71 | .wx_stepper__stepper__minus_disabled{ 72 | 73 | background-color: #f8f8f8; 74 | border-color: #e8e8e8 #ccc #e8e8e8 #e8e8e8; 75 | } 76 | 77 | .wx_stepper__plus{ 78 | 79 | border-radius: 0 2px 2px 0; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 微信商城 11 | 37 | 38 | 39 | 40 | 41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/SearchProduct/index.less: -------------------------------------------------------------------------------- 1 | 2 | 3 | .custom_search{ 4 | 5 | background-color: #f8f8f8; 6 | padding: 10px; 7 | 8 | .form{ 9 | 10 | margin: 0; 11 | position: relative; 12 | background: #fff; 13 | border-radius: 4px; 14 | border: 0 none; 15 | overflow: hidden; 16 | 17 | .custom_search_input{ 18 | 19 | margin: 0; 20 | padding: 8px 8px 8px 32px; 21 | -webkit-appearance: none; 22 | -moz-appearance: none; 23 | line-height: 20px; 24 | height: 35px; 25 | width: 100%; 26 | -webkit-box-sizing: border-box; 27 | -moz-box-sizing: border-box; 28 | box-sizing: border-box; 29 | border: 1px solid #e5e5e5; 30 | border-radius: 4px; 31 | outline: none; 32 | font-size: 14px; 33 | -webkit-box-shadow: 0 0 0 0; 34 | box-shadow: 0 0 0 0; 35 | 36 | } 37 | .custom_search_button{ 38 | 39 | position: absolute; 40 | top: 1px; 41 | left: 1px; 42 | border: 0px none; 43 | border-radius: 4px; 44 | height: 33px; 45 | line-height: 35px; 46 | width: 22px; 47 | padding: 0; 48 | color: #fff; 49 | font-size: 14px; 50 | text-indent: -9999px; 51 | background-size: 14px 14px; 52 | border-radius: 5px 0 0 5px; 53 | background-color: #fff; 54 | background-position: right center; 55 | background-repeat: no-repeat; 56 | 57 | } 58 | 59 | } 60 | 61 | 62 | } -------------------------------------------------------------------------------- /src/assets/scar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Stepper/index.js: -------------------------------------------------------------------------------- 1 | import React , {PureComponent} from 'react' 2 | import Styles from './index.less' 3 | import classNames from 'classnames' 4 | 5 | 6 | /** 7 | * 步进器 8 | */ 9 | class Stepper extends PureComponent{ 10 | 11 | state={ 12 | currValue:1, 13 | } 14 | 15 | subtract=()=>{ 16 | 17 | if(this.state.currValue != 1){ 18 | 19 | this.props.onnums(this.state.currValue -1) 20 | 21 | this.setState({ 22 | currValue:this.state.currValue - 1, 23 | }) 24 | 25 | } 26 | 27 | 28 | } 29 | 30 | 31 | 32 | addNums=()=>{ 33 | 34 | this.props.onnums(this.state.currValue +1) 35 | 36 | this.setState({ 37 | currValue:this.state.currValue +1, 38 | }) 39 | 40 | 41 | } 42 | 43 | 44 | render(){ 45 | 46 | 47 | return ( 48 | 49 |
50 | 54 | 55 | 56 |
57 | ) 58 | } 59 | 60 | } 61 | 62 | export default Stepper; -------------------------------------------------------------------------------- /src/routes/banner.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react' 2 | import { Carousel } from 'antd-mobile'; 3 | import { connect } from 'react-redux' 4 | import { Swiper, Flex, FlexItem } from 'react-weui' 5 | 6 | 7 | /** 8 | * banner 9 | */ 10 | class Banner extends PureComponent { 11 | 12 | 13 | 14 | state = { 15 | data:[1,2,3,5,6] 16 | } 17 | 18 | 19 | componentDidMount() { 20 | // simulate img loading 21 | 22 | this.props.dispatch({ 23 | type: 'getBannerListSaga', 24 | }); 25 | 26 | 27 | 28 | } 29 | 30 | componentWillReceiveProps(nextprops){ 31 | 32 | this.setState({ 33 | data:nextprops.data, 34 | }) 35 | 36 | } 37 | 38 | 39 | render() { 40 | 41 | console.log(this.state.data); 42 | 43 | return ( 44 | 45 | 50 | {this.state.data.map(val => ( 51 | 56 | { 61 | window.dispatchEvent(new Event('resize')); 62 | }} 63 | /> 64 | 65 | ))} 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | 73 | 74 | 75 | } 76 | 77 | export default connect(({ bannerRedux }) => { 78 | 79 | 80 | return bannerRedux 81 | 82 | 83 | 84 | })(Banner); -------------------------------------------------------------------------------- /src/components/ProductDetail/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import { Carousel } from 'antd-mobile' 3 | import Styles from './index.less' 4 | 5 | 6 | /** 7 | * 商品详情 8 | */ 9 | export default class ProductDetailCom extends PureComponent{ 10 | 11 | state={ 12 | imgurls:[1,2,3,4,5,6,7,9,10] 13 | } 14 | 15 | 16 | componentWillReceiveProps(nextProps){ 17 | 18 | 19 | this.setState({ 20 | imgurls:nextProps.data.banner 21 | }) 22 | 23 | } 24 | 25 | render(){ 26 | 27 | const {data} = this.props; 28 | 29 | console.log(data) 30 | 31 | return ( 32 |
33 |
34 | 35 | 41 | 42 | { 43 | this.state.imgurls.map((item,index)=>( 44 | 45 | { 48 | window.dispatchEvent(new Event('resize')); 49 | }} 50 | /> 51 | )) 52 | 53 | 54 | } 55 | 56 | 57 | 58 | 59 |
60 |
61 | 62 |

63 | 64 | {data.title} 65 | 66 |

67 |
68 | 69 |
70 | ¥ 71 | {data.price} 72 |
73 | 74 |
75 | 76 | 77 |
78 | 79 |
80 | ) 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/components/TabBar/index.js: -------------------------------------------------------------------------------- 1 | import React , { PureComponent } from 'react' 2 | 3 | import classNames from 'classnames' 4 | 5 | import {Link} from 'react-router-dom' 6 | 7 | import {menuData} from '../../common/menu' 8 | 9 | import 'antd-mobile/lib/tab-bar/style/index.css'; 10 | import 'antd-mobile/lib/badge/style/index.css' 11 | 12 | import Styles from './index.less' 13 | 14 | import home1 from '../../assets/home1.png' 15 | 16 | 17 | 18 | class WxTabBar extends PureComponent{ 19 | 20 | 21 | state={ 22 | 23 | selectedTab:'home' 24 | 25 | } 26 | 27 | changeTab =(tab)=>{ 28 | 29 | this.setState({ 30 | selectedTab:tab, 31 | }) 32 | 33 | 34 | } 35 | 36 | 37 | render(){ 38 | 39 | 40 | return ( 41 | 42 | 43 |
46 | 47 |
48 | 49 | 50 | { 51 | 52 | menuData.map(item =>( 53 | 54 | 55 |
56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | { 65 | 66 | this.state.selectedTab == item.key? 67 | 68 |
69 | : 70 |
71 | 72 | } 73 | 74 | 75 | 76 | {/* 1 */} 77 | 78 |
79 | 80 | 81 |
82 | 83 |

{item.name}

84 | 85 | 86 | 87 |
88 | 89 | 90 | 91 | )) 92 | 93 | } 94 | 95 |
96 | 97 |
98 | ) 99 | 100 | } 101 | 102 | 103 | 104 | } 105 | 106 | export default WxTabBar; -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import { notification } from 'antd'; 2 | import axios from 'axios' 3 | import store from '../store' 4 | import {push} from 'react-router-redux' 5 | 6 | 7 | const codeMessage = { 8 | 200: '服务器成功返回请求的数据', 9 | 201: '新建或修改数据成功。', 10 | 202: '一个请求已经进入后台排队(异步任务)', 11 | 204: '删除数据成功。', 12 | 400: '发出的请求有错误,服务器没有进行新建或修改数据,的操作。', 13 | 401: '用户没有权限(令牌、用户名、密码错误)。', 14 | 403: '用户得到授权,但是访问是被禁止的。', 15 | 404: '发出的请求针对的是不存在的记录,服务器没有进行操作', 16 | 406: '请求的格式不可得。', 17 | 410: '请求的资源被永久删除,且不会再得到的。', 18 | 422: '当创建一个对象时,发生一个验证错误。', 19 | 500: '服务器发生错误,请检查服务器', 20 | 502: '网关错误', 21 | 503: '服务不可用,服务器暂时过载或维护', 22 | 504: '网关超时', 23 | }; 24 | 25 | function checkStatus(response) { 26 | if (response.status >= 200 && response.status < 300) { 27 | return response; 28 | } 29 | const errortext = codeMessage[response.status] || response.statusText; 30 | notification.error({ 31 | message: `请求错误 ${response.status}: ${response.url}`, 32 | description: errortext, 33 | }); 34 | const error = new Error(errortext); 35 | error.name = response.status; 36 | error.response = response; 37 | throw error; 38 | } 39 | 40 | 41 | 42 | /** 43 | * Requests a URL, returning a promise. 44 | * 45 | * @param {string} url The URL we want to request 46 | * @param {object} [options] The options we want to pass to "fetch" 47 | * @return {object} An object containing either "data" or "err" 48 | */ 49 | export default function request(url, options) { 50 | const defaultOptions = { 51 | credentials: 'include', 52 | }; 53 | const newOptions = { ...defaultOptions, ...options }; 54 | if (newOptions.method === 'POST' || newOptions.method === 'PUT') { 55 | newOptions.headers = { 56 | Accept: 'application/json', 57 | 'Content-Type': 'application/json; charset=utf-8', 58 | ...newOptions.headers, 59 | }; 60 | newOptions.body = JSON.stringify(newOptions.body); 61 | } 62 | 63 | return axios(url, newOptions) 64 | .then(checkStatus) 65 | .then((response) => { 66 | if (newOptions.method === 'DELETE' || response.status === 204) { 67 | return response.text(); 68 | } 69 | return response.json(); 70 | }) 71 | .catch((e) => { 72 | const { dispatch } = store; 73 | const status = e.name; 74 | if (status === 401) { 75 | dispatch({ 76 | type: 'login/logout', 77 | }); 78 | return; 79 | } 80 | if (status === 403) { 81 | dispatch(push('/exception/403')); 82 | return; 83 | } 84 | if (status <= 504 && status >= 500) { 85 | dispatch(push('/exception/500')); 86 | return; 87 | } 88 | if (status >= 404 && status < 422) { 89 | dispatch(push('/exception/404')); 90 | } 91 | }); 92 | } -------------------------------------------------------------------------------- /src/components/ProductList/index.js: -------------------------------------------------------------------------------- 1 | import React,{ PureComponent } from 'react' 2 | 3 | import {history} from '../../store' 4 | 5 | import {Link} from 'react-router-dom' 6 | 7 | import Styles from './index.less' 8 | 9 | import Popup from '../../components/Popup' 10 | 11 | import scar from '../../assets/scar.svg' 12 | 13 | class ProductList extends PureComponent{ 14 | 15 | 16 | 17 | state={ 18 | hidden:false, 19 | } 20 | 21 | 22 | clickShopCar = (index,event)=>{ 23 | 24 | 25 | event.stopPropagation(); 26 | 27 | this.props.dispatch({ 28 | type:'openPopup' 29 | }) 30 | 31 | this.props.dispatch({ 32 | type:'getHomeShopcarSaga' 33 | }) 34 | 35 | var _body = document.getElementsByTagName("body") 36 | 37 | _body[0].style.overflow="hidden"; 38 | 39 | 40 | } 41 | 42 | // 跳转商品详情 43 | gotoProductDetail = (id)=>{ 44 | 45 | history.push({ 46 | pathname:"/product", 47 | search:"?id="+id, 48 | }) 49 | 50 | 51 | 52 | 53 | 54 | } 55 | 56 | 57 | 58 | render(){ 59 | 60 | const {data} = this.props; 61 | 62 | 63 | return ( 64 | 65 | // 商品列表 66 |
67 |
68 |
    69 | 70 | { 71 | data.map((item)=>( 72 | 73 |
  • 74 |
    75 | 76 |
    77 | 78 |
    79 |
    80 |

    {item.title}

    81 |
    82 |
    83 | 84 |

    ¥ {item.price}

    85 |
    86 |
    87 | 88 |
    89 |
    90 |
    91 | 92 |
    93 |
  • 94 | )) 95 | 96 | 97 | } 98 | 99 | 100 |
101 |
102 | 103 |
104 | 105 | ) 106 | 107 | 108 | } 109 | 110 | 111 | 112 | 113 | } 114 | 115 | export default ProductList; -------------------------------------------------------------------------------- /src/mock/data.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import banner1 from '../assets/banner1.gif' 3 | import banner2 from '../assets/banner2.gif' 4 | import banner3 from '../assets/banner3.gif' 5 | 6 | import sc1 from '../assets/sc1.png' 7 | import sc2 from '../assets/sc2.png' 8 | import sc3 from '../assets/sc3.png' 9 | import sc4 from '../assets/sc4.png' 10 | 11 | 12 | import p1 from '../assets/p1.jpg' 13 | import p2 from '../assets/p2.jpg' 14 | import p3 from '../assets/p3.png' 15 | import p4 from '../assets/p4.jpg' 16 | import p5 from '../assets/p5.jpg' 17 | 18 | import pd1 from '../assets/pd1.jpg' 19 | import pd2 from '../assets/pd2.jpg' 20 | import pd3 from '../assets/pd3.jpg' 21 | import pd4 from '../assets/pd4.jpg' 22 | 23 | 24 | const Random = Mock.Random 25 | 26 | 27 | const banner= Mock.mock('/api/banner',[ 28 | 29 | banner1, 30 | banner2, 31 | banner3, 32 | 33 | ]) 34 | 35 | const shortcut = Mock.mock('/api/shortcut',[ 36 | 37 | { 38 | url:"", 39 | img:sc1, 40 | }, 41 | { 42 | url:"", 43 | img:sc2, 44 | }, 45 | { 46 | url:"", 47 | img:sc3, 48 | }, 49 | { 50 | url:"", 51 | img:sc4, 52 | }, 53 | 54 | 55 | ]) 56 | 57 | const products_1 = Mock.mock('/api/products_1',[ 58 | 59 | { 60 | "id|1000-2000":1, 61 | url:'', 62 | imgsrc:p1, 63 | title:'【买2份减10元】海南贵妃芒果 香甜多汁 皮薄核小 原产地芒果新鲜采摘 中大果净重5斤装京东包邮', 64 | "price|1-100.1-2":1, 65 | }, 66 | { 67 | "id|1000-2000":1, 68 | url:'', 69 | imgsrc:p2, 70 | title:'熊猫精选 黑芝麻蜜丸 手工现做 柴火慢蒸 九蒸九晒黑芝麻蜜丸12丸/罐/108克 买三罐送一罐 四罐1疗程', 71 | "price|1-100.1-2":1, 72 | }, 73 | { 74 | "id|1000-2000":1, 75 | url:'', 76 | imgsrc:p3, 77 | title:'熊猫精选 陕北米脂小米 农家月子米 养胃养颜 现磨现发 5斤装包邮', 78 | "price|1-100.1-2":1, 79 | }, 80 | { 81 | "id|1000-2000":1, 82 | url:'', 83 | imgsrc:p4, 84 | title:'熊猫有礼 阿九提冰川牧场纯牛奶 原生牧场 军工品质 滴滴鲜醇(200ml*16袋)', 85 | "price|1-100.1-2":1, 86 | }, 87 | { 88 | "id|1000-2000":1, 89 | url:'', 90 | imgsrc:p5, 91 | title:'熊猫有礼 燕麦代餐酸奶 醇香口感,奇妙风味,能量酸奶(180g*12杯)', 92 | "price|1-100.1-2":1, 93 | }, 94 | { 95 | "id|1000-2000":1, 96 | url:'', 97 | imgsrc:p4, 98 | title:'熊猫有礼 阿九提冰川牧场纯牛奶 原生牧场 军工品质 滴滴鲜醇(200ml*16袋)', 99 | "price|1-100.1-2":1, 100 | }, 101 | 102 | 103 | ]) 104 | 105 | 106 | const addShopcar = Mock.mock('/api/getshopcar',{ 107 | 108 | "id|1000-2000":1, 109 | "title":'熊猫有礼 健康无添加 小孩可以放心吃的猪肉丝 香酥猪肉松&黄金猪肉丝', 110 | "priceRange":"20.00-30.00", 111 | "imgsrc":p1, 112 | "data|3":[ 113 | { 114 | "id|3000-4000":1, 115 | "sepc|1":[ 116 | "黄金猪肉丝98g+香酥猪肉松108g", 117 | "黄金猪肉丝98g", 118 | "香酥猪肉松108g" 119 | ], 120 | "price|20-30.2":1, 121 | "count|1000-2000":1, 122 | } 123 | 124 | ], 125 | 126 | 127 | 128 | 129 | }) 130 | 131 | const productdetail = Mock.mock('/api/productdetail/',{ 132 | 133 | "id":10004, 134 | "banner":[pd1,pd2,pd3,pd4], 135 | "title":"熊猫精选 黑芝麻蜜丸 手工现做 柴火慢蒸 九蒸九晒黑芝麻蜜丸12丸/罐/108克 买三罐送一罐 四罐1疗程", 136 | "price":"49.90", 137 | "carriage":{ 138 | "carriage_id":1, 139 | "carriage_text":'免运费', 140 | "carriage_price":'0.00' 141 | }, 142 | }); 143 | 144 | export { 145 | banner, 146 | shortcut, 147 | products_1, 148 | addShopcar, 149 | productdetail, 150 | } -------------------------------------------------------------------------------- /src/components/ProductList/index.less: -------------------------------------------------------------------------------- 1 | 2 | .list_warper{ 3 | 4 | overflow: hidden; 5 | padding: 0 7px; 6 | 7 | ul{ 8 | list-style: none; 9 | padding-left: 0px; 10 | margin-top: 0px; 11 | display: flex; 12 | flex-direction: row; 13 | justify-content: flex-start; 14 | flex-wrap: wrap; 15 | li{ 16 | width: 50%; 17 | .pwarper{ 18 | position: relative; 19 | display: block; 20 | margin: 5px 3px; 21 | background-color: #fff; 22 | color: #333; 23 | box-sizing: border-box; 24 | 25 | .price_warper{ 26 | display: flex; 27 | flex-direction: row; 28 | justify-content: flex-start; 29 | align-items: center; 30 | padding-bottom: 10px; 31 | p{ 32 | flex: 1; 33 | margin: 0px; 34 | font-size: 16px; 35 | color: #f44; 36 | font-weight: 400; 37 | font-stretch: 100%; 38 | padding-left: 10px; 39 | } 40 | 41 | .shop_cart{ 42 | 43 | flex: 1; 44 | display: flex; 45 | flex-direction: row; 46 | justify-content: flex-end; 47 | padding-right: 10px; 48 | .cap_goods_list__buy_btn{ 49 | 50 | width: 24px; 51 | height: 24px; 52 | color: #f44; 53 | background-color: #fff; 54 | -webkit-box-sizing: border-box; 55 | -moz-box-sizing: border-box; 56 | box-sizing: border-box; 57 | text-align: center; 58 | font-size: 12px; 59 | cursor: pointer; 60 | border: 1px solid #f44; 61 | border-radius: 50%; 62 | display: flex; 63 | flex-direction: column; 64 | justify-content: center; 65 | align-items: center; 66 | 67 | .scimg{ 68 | 69 | width: 78%; 70 | 71 | } 72 | } 73 | 74 | } 75 | 76 | } 77 | .title_warper{ 78 | 79 | position: relative; 80 | overflow: hidden; 81 | padding: 10px; 82 | cursor: pointer; 83 | width: inherit; 84 | p{ 85 | height: 36px; 86 | overflow: hidden; 87 | text-overflow: ellipsis; 88 | display: -webkit-box; 89 | font-size: 14px; 90 | line-height: 18px; 91 | text-align: left; 92 | font-weight:none; 93 | margin: 0px; 94 | -webkit-line-clamp: 2; 95 | /* autoprefixer: off*/ 96 | -webkit-box-orient: vertical; 97 | /* autoprefixer: on*/ 98 | } 99 | 100 | } 101 | } 102 | 103 | img{ 104 | width: 100%; 105 | height: auto; 106 | } 107 | 108 | } 109 | 110 | } 111 | 112 | 113 | 114 | } -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 28 Copy 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 42 | 43 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/components/HomeShopCar/index.less: -------------------------------------------------------------------------------- 1 | .hsc_container{ 2 | 3 | background: #f8f8f8; 4 | } 5 | .hac_layout{ 6 | 7 | background: #fff; 8 | } 9 | .hac_header{ 10 | 11 | margin-left: 15px; 12 | position: relative; 13 | } 14 | .hairline::after{ 15 | content: ''; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | width: 200%; 20 | height: 200%; 21 | -webkit-transform: scale(0.5); 22 | -moz-transform: scale(0.5); 23 | -ms-transform: scale(0.5); 24 | transform: scale(0.5); 25 | -webkit-transform-origin: 0 0; 26 | -moz-transform-origin: 0 0; 27 | -ms-transform-origin: 0 0; 28 | transform-origin: 0 0; 29 | pointer-events: none; 30 | -webkit-box-sizing: border-box; 31 | -moz-box-sizing: border-box; 32 | box-sizing: border-box; 33 | border: 0 solid #e5e5e5; 34 | border-bottom-width: 1px; 35 | } 36 | 37 | .hac_header_img_warper{ 38 | 39 | position: relative; 40 | float: left; 41 | margin-top: -10px; 42 | width: 80px; 43 | height: 80px; 44 | background: #f8f8f8; 45 | border-radius: 2px; 46 | 47 | .hac_header_img{ 48 | 49 | position: absolute; 50 | margin: auto; 51 | top: 0; 52 | left: 0; 53 | right: 0; 54 | bottom: 0; 55 | max-width: 100%; 56 | max-height: 100%; 57 | } 58 | } 59 | .hac_header_info{ 60 | 61 | padding: 10px 60px 10px 10px; 62 | min-height: 82px; 63 | overflow: hidden; 64 | box-sizing: border-box; 65 | 66 | } 67 | 68 | 69 | .hac_header_info_title{ 70 | 71 | font-size: 12px; 72 | overflow: hidden; 73 | white-space: nowrap; 74 | text-overflow: ellipsis; 75 | color: #333; 76 | } 77 | .hac_header_info_price{ 78 | 79 | color: #f44; 80 | margin-top: 10px; 81 | font-size: 16px; 82 | vertical-align: middle; 83 | 84 | .price_symbol{ 85 | 86 | color: #f44; 87 | font-size: 14px; 88 | vertical-align: middle; 89 | } 90 | .price_number{ 91 | 92 | 93 | } 94 | } 95 | .close_icon{ 96 | 97 | position: absolute; 98 | top: 0; 99 | right: 0; 100 | width: 44px; 101 | height: 44px; 102 | background-size: 22px 22px; 103 | background-repeat: no-repeat; 104 | background-position: 7px 10px; 105 | cursor: pointer; 106 | 107 | } 108 | 109 | .hac_body{ 110 | 111 | max-height: 350px; 112 | overflow-y: scroll; 113 | -webkit-overflow-scrolling: touch; 114 | 115 | .group_container{ 116 | 117 | margin-left: 15px; 118 | padding: 12px 0 2px; 119 | position: relative; 120 | 121 | .row_group{ 122 | 123 | margin: 0 15px 10px 0; 124 | } 125 | .row_group:last-child { 126 | margin-bottom: 0; 127 | } 128 | .row_group_item{ 129 | 130 | .row_group_item_title{ 131 | font-size: 14px; 132 | padding-bottom: 10px; 133 | } 134 | 135 | .row_group_item_gg{ 136 | 137 | .row_group_item_gg_i{ 138 | display: inline-block; 139 | padding: 5px 9px; 140 | margin: 0 10px 10px 0; 141 | height: 28px; 142 | min-width: 52px; 143 | line-height: 16px; 144 | font-size: 12px; 145 | color: #333; 146 | text-align: center; 147 | border: 1px solid #999; 148 | border-radius: 3px; 149 | box-sizing: border-box; 150 | } 151 | .active{ 152 | background-color: #f44; 153 | border-color: #f44; 154 | color: #fff; 155 | } 156 | } 157 | 158 | } 159 | 160 | 161 | } 162 | 163 | .stepper_stock{ 164 | 165 | padding: 12px 0; 166 | margin-left: 15px; 167 | 168 | .stepper_container{ 169 | height: 30px; 170 | margin-right: 20px; 171 | } 172 | .stock_count{ 173 | display: inline-block; 174 | margin-right: 10px; 175 | color: #999; 176 | font-size: 12px; 177 | } 178 | .stepper_title{ 179 | 180 | float: left; 181 | line-height: 30px; 182 | font-size: 14px; 183 | 184 | } 185 | 186 | } 187 | 188 | .hairline_top_bottom{ 189 | 190 | position: relative; 191 | border-width: 1px 0; 192 | 193 | } 194 | } 195 | .wx_actions{ 196 | 197 | display: flex; 198 | margin-top: 10px; 199 | } 200 | .van_button{ 201 | width: 100%; 202 | height: 50px; 203 | line-height: 50px; 204 | border: 0; 205 | border-radius: 0; 206 | font-size: 16px; 207 | color: #fff; 208 | background-color: #f85; 209 | } 210 | .van_button_default{ 211 | background: #f85; 212 | color: #fff; 213 | } 214 | .van_button_primary{ 215 | background: #f44; 216 | color: #fff; 217 | 218 | } -------------------------------------------------------------------------------- /src/utils/utils.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export function fixedZero(val) { 4 | return val * 1 < 10 ? `0${val}` : val; 5 | } 6 | 7 | export function getTimeDistance(type) { 8 | const now = new Date(); 9 | const oneDay = 1000 * 60 * 60 * 24; 10 | 11 | if (type === 'today') { 12 | now.setHours(0); 13 | now.setMinutes(0); 14 | now.setSeconds(0); 15 | return [moment(now), moment(now.getTime() + (oneDay - 1000))]; 16 | } 17 | 18 | if (type === 'week') { 19 | let day = now.getDay(); 20 | now.setHours(0); 21 | now.setMinutes(0); 22 | now.setSeconds(0); 23 | 24 | if (day === 0) { 25 | day = 6; 26 | } else { 27 | day -= 1; 28 | } 29 | 30 | const beginTime = now.getTime() - (day * oneDay); 31 | 32 | return [moment(beginTime), moment(beginTime + ((7 * oneDay) - 1000))]; 33 | } 34 | 35 | if (type === 'month') { 36 | const year = now.getFullYear(); 37 | const month = now.getMonth(); 38 | const nextDate = moment(now).add(1, 'months'); 39 | const nextYear = nextDate.year(); 40 | const nextMonth = nextDate.month(); 41 | 42 | return [moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`), moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000)]; 43 | } 44 | 45 | if (type === 'year') { 46 | const year = now.getFullYear(); 47 | 48 | return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)]; 49 | } 50 | } 51 | 52 | export function getPlainNode(nodeList, parentPath = '') { 53 | const arr = []; 54 | nodeList.forEach((node) => { 55 | const item = node; 56 | item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/'); 57 | item.exact = true; 58 | if (item.children && !item.component) { 59 | arr.push(...getPlainNode(item.children, item.path)); 60 | } else { 61 | if (item.children && item.component) { 62 | item.exact = false; 63 | } 64 | arr.push(item); 65 | } 66 | }); 67 | return arr; 68 | } 69 | 70 | export function digitUppercase(n) { 71 | const fraction = ['角', '分']; 72 | const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; 73 | const unit = [ 74 | ['元', '万', '亿'], 75 | ['', '拾', '佰', '仟'], 76 | ]; 77 | let num = Math.abs(n); 78 | let s = ''; 79 | fraction.forEach((item, index) => { 80 | s += (digit[Math.floor(num * 10 * (10 ** index)) % 10] + item).replace(/零./, ''); 81 | }); 82 | s = s || '整'; 83 | num = Math.floor(num); 84 | for (let i = 0; i < unit[0].length && num > 0; i += 1) { 85 | let p = ''; 86 | for (let j = 0; j < unit[1].length && num > 0; j += 1) { 87 | p = digit[num % 10] + unit[1][j] + p; 88 | num = Math.floor(num / 10); 89 | } 90 | s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s; 91 | } 92 | 93 | return s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整'); 94 | } 95 | 96 | 97 | function getRelation(str1, str2) { 98 | if (str1 === str2) { 99 | console.warn('Two path are equal!'); // eslint-disable-line 100 | } 101 | const arr1 = str1.split('/'); 102 | const arr2 = str2.split('/'); 103 | if (arr2.every((item, index) => item === arr1[index])) { 104 | return 1; 105 | } else if (arr1.every((item, index) => item === arr2[index])) { 106 | return 2; 107 | } 108 | return 3; 109 | } 110 | 111 | function getRenderArr(routes) { 112 | let renderArr = []; 113 | renderArr.push(routes[0]); 114 | for (let i = 1; i < routes.length; i += 1) { 115 | let isAdd = false; 116 | // 是否包含 117 | isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3); 118 | // 去重 119 | renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1); 120 | if (isAdd) { 121 | renderArr.push(routes[i]); 122 | } 123 | } 124 | return renderArr; 125 | } 126 | 127 | /** 128 | * Get router routing configuration 129 | * { path:{name,...param}}=>Array<{name,path ...param}> 130 | * @param {string} path 131 | * @param {routerData} routerData 132 | */ 133 | export function getRoutes(path, routerData) { 134 | let routes = Object.keys(routerData).filter(routePath => 135 | routePath.indexOf(path) === 0 && routePath !== path); 136 | // Replace path to '' eg. path='user' /user/name => name 137 | routes = routes.map(item => item.replace(path, '')); 138 | // Get the route to be rendered to remove the deep rendering 139 | const renderArr = getRenderArr(routes); 140 | // Conversion and stitching parameters 141 | const renderRoutes = renderArr.map((item) => { 142 | const exact = !routes.some(route => route !== item && getRelation(route, item) === 1); 143 | return { 144 | ...routerData[`${path}${item}`], 145 | key: `${path}${item}`, 146 | path: `${path}${item}`, 147 | exact, 148 | }; 149 | }); 150 | return renderRoutes; 151 | } 152 | 153 | 154 | /* eslint no-useless-escape:0 */ 155 | const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/g; 156 | 157 | export function isUrl(path) { 158 | return reg.test(path); 159 | } 160 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const { injectBabelPlugin, getLoader } = require('react-app-rewired'); 2 | 3 | const fileLoaderMatcher = function (rule) { 4 | return rule.loader && rule.loader.indexOf(`file-loader`) != -1; 5 | } 6 | 7 | module.exports = { 8 | 9 | webpack: function override(config, env) { 10 | 11 | config = injectBabelPlugin('transform-decorators-legacy',config) 12 | 13 | // babel-plugin-import 14 | config = injectBabelPlugin(['import', 15 | { 16 | "libraryName": "antd-mobile", 17 | "libraryDirectory": "lib", // default: lib 18 | "style": true 19 | }, 20 | ], config); 21 | 22 | // customize theme 23 | config.module.rules[1].oneOf.unshift( 24 | { 25 | test: /\.less$/, 26 | use: [ 27 | require.resolve('style-loader'), 28 | require.resolve('css-loader'), 29 | { 30 | loader: require.resolve('postcss-loader'), 31 | options: { 32 | // Necessary for external CSS imports to work 33 | // https://github.com/facebookincubator/create-react-app/issues/2677 34 | ident: 'postcss', 35 | plugins: () => [ 36 | require('postcss-flexbugs-fixes'), 37 | autoprefixer({ 38 | browsers: [ 39 | '>1%', 40 | 'last 4 versions', 41 | 'Firefox ESR', 42 | 'not ie < 9', // React doesn't support IE8 anyway 43 | ], 44 | flexbox: 'no-2009', 45 | }), 46 | ], 47 | }, 48 | }, 49 | { 50 | loader: require.resolve('less-loader'), 51 | options: { 52 | // theme vars, also can use theme.js instead of this. 53 | modifyVars: { "@brand-primary": "#1DA57A" }, 54 | }, 55 | }, 56 | ] 57 | } 58 | ); 59 | 60 | config.module.rules[1].oneOf.unshift( 61 | { 62 | test: /\.less$/, 63 | exclude: /node_modules|antd-mobile|antd/, 64 | use: [ 65 | require.resolve('style-loader'), 66 | { 67 | loader: require.resolve('css-loader'), 68 | options: { 69 | modules: true, 70 | importLoaders: 1, 71 | localIdentName: '[local]___[hash:base64:5]' 72 | }, 73 | }, 74 | { 75 | loader: require.resolve('postcss-loader'), 76 | options: { 77 | // Necessary for external CSS imports to work 78 | // https://github.com/facebookincubator/create-react-app/issues/2677 79 | ident: 'postcss', 80 | plugins: () => [ 81 | require('postcss-flexbugs-fixes'), 82 | autoprefixer({ 83 | browsers: [ 84 | '>1%', 85 | 'last 4 versions', 86 | 'Firefox ESR', 87 | 'not ie < 9', // React doesn't support IE8 anyway 88 | ], 89 | flexbox: 'no-2009', 90 | }), 91 | ], 92 | }, 93 | }, 94 | { 95 | loader: require.resolve('less-loader'), 96 | options: { 97 | // theme vars, also can use theme.js instead of this. 98 | modifyVars: { "@brand-primary": "#1DA57A" }, 99 | }, 100 | }, 101 | ] 102 | } 103 | ); 104 | 105 | // css-modules 106 | config.module.rules[1].oneOf.unshift( 107 | { 108 | test: /\.css$/, 109 | exclude: /node_modules|antd-mobile|antd\.css/, 110 | use: [ 111 | require.resolve('style-loader'), 112 | { 113 | loader: require.resolve('css-loader'), 114 | options: { 115 | modules: true, 116 | importLoaders: 1, 117 | localIdentName: '[local]___[hash:base64:5]' 118 | }, 119 | }, 120 | { 121 | loader: require.resolve('postcss-loader'), 122 | options: { 123 | // Necessary for external CSS imports to work 124 | // https://github.com/facebookincubator/create-react-app/issues/2677 125 | ident: 'postcss', 126 | plugins: () => [ 127 | require('postcss-flexbugs-fixes'), 128 | autoprefixer({ 129 | browsers: [ 130 | '>1%', 131 | 'last 4 versions', 132 | 'Firefox ESR', 133 | 'not ie < 9', // React doesn't support IE8 anyway 134 | ], 135 | flexbox: 'no-2009', 136 | }), 137 | ], 138 | }, 139 | }, 140 | ] 141 | } 142 | ); 143 | 144 | 145 | 146 | // file-loader exclude 147 | let l = getLoader(config.module.rules, fileLoaderMatcher); 148 | l.exclude.push(/\.less$/); 149 | 150 | return config; 151 | }, 152 | devServer: function(configFunction){ 153 | 154 | return function(proxy, allowedHost) { 155 | 156 | const config = configFunction(proxy, allowedHost); 157 | 158 | // 启用局域网通过ip访问 159 | config.disableHostCheck = true; 160 | 161 | 162 | 163 | 164 | return config; 165 | } 166 | 167 | 168 | }, 169 | 170 | 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/components/HomeShopCar/index.js: -------------------------------------------------------------------------------- 1 | import React,{PureComponent} from 'react' 2 | import Styles from './index.less' 3 | import close from '../../assets/close.png' 4 | import classNames from 'classnames' 5 | import Stepper from '../Stepper' 6 | 7 | /** 8 | * 首页添加购物车 9 | */ 10 | class HomeShopCar extends PureComponent{ 11 | 12 | 13 | state={ 14 | 15 | submitFlag:false, 16 | price:undefined, 17 | currgg:undefined, 18 | val:1, 19 | 20 | } 21 | 22 | onChange = (val) => { 23 | // console.log(val); 24 | this.setState({ val:val }); 25 | } 26 | 27 | 28 | closeMe=()=>{ 29 | 30 | this.props.dispatch({ 31 | type:'closePopup' 32 | }) 33 | 34 | this.setState({ 35 | ...this.state, 36 | price:undefined, 37 | currgg:undefined, 38 | }) 39 | 40 | var _body = document.getElementsByTagName("body") 41 | 42 | _body[0].style.overflow=""; 43 | 44 | 45 | 46 | 47 | } 48 | 49 | 50 | setPrice=(item)=>{ 51 | 52 | this.setState({ 53 | ...this.state, 54 | submitFlag:true, 55 | price:item.price, 56 | currgg:item.id, 57 | }) 58 | 59 | } 60 | 61 | // 获取商品的数量 62 | getNums =(val)=>{ 63 | console.log(val) 64 | } 65 | 66 | renderGg=(data)=>{ 67 | 68 | if(data != undefined){ 69 | 70 | return data.map(item=>( 71 | 72 | 73 | {item.sepc} 74 | 75 | )) 76 | 77 | } 78 | 79 | 80 | } 81 | 82 | render(){ 83 | 84 | const {data} = this.props; 85 | 86 | const gg = this.renderGg(data.data) 87 | 88 | 89 | return ( 90 |
91 | 92 |
93 | 94 |
95 | 96 |
97 | 98 | 99 | 100 |
101 |
102 | 103 |
104 | 105 | {data.title} 106 | 107 |
108 | 109 |
110 | 111 | 112 | 113 | 114 | {this.state.price == undefined? data.priceRange:this.state.price} 115 |
116 | 117 | 118 | 119 |
120 | 121 |
122 |
123 | 124 |
125 | 126 |
127 | 128 |
129 | 130 |
131 | 净含量: 132 |
133 | 134 |
135 | {gg} 136 |
137 | 138 |
139 | 140 |
141 | 142 |
143 | 144 |
145 | 146 |
147 | 148 |
购物数量:
149 | 150 | 151 |
152 | 153 |
剩余2017件
154 |
155 |
156 |
157 | 158 | 159 |
160 |
161 | 162 | 163 |
164 | 165 | 166 |
167 |
168 | ) 169 | 170 | } 171 | 172 | } 173 | 174 | export default HomeShopCar; --------------------------------------------------------------------------------