├── .swan └── editor.json ├── src ├── components │ ├── EIcon │ │ ├── Eicon.scss │ │ └── EIcon.tsx │ ├── ShopList │ │ ├── ShopList.scss │ │ └── ShopList.tsx │ ├── EList │ │ ├── EList.scss │ │ ├── index.tsx │ │ ├── EList.tsx │ │ ├── EItem.scss │ │ └── EItem.tsx │ ├── Test │ │ └── Test.tsx │ ├── Card │ │ ├── Card.scss │ │ └── Card.tsx │ ├── EScroll │ │ ├── EScroll.scss │ │ └── EScroll.tsx │ ├── ETransition │ │ ├── ETransition.scss │ │ └── ETransition.tsx │ ├── FooterNav │ │ ├── images │ │ │ ├── order_1.svg │ │ │ ├── profile_1.svg │ │ │ ├── order_2.svg │ │ │ ├── profile_2.svg │ │ │ ├── discover_2.svg │ │ │ ├── discover_1.svg │ │ │ ├── msite_2.svg │ │ │ └── msite_1.svg │ │ ├── FooterNav.scss │ │ └── FooterNav.tsx │ ├── NoDataTip │ │ ├── NoDataTip.scss │ │ └── NoDataTip.tsx │ ├── Star │ │ ├── Star.scss │ │ └── Star.tsx │ ├── ShopButton │ │ ├── ShopButton.scss │ │ └── ShopButton.tsx │ ├── ETabs │ │ ├── ETabs.scss │ │ └── ETabs.tsx │ ├── NavBar │ │ ├── NavBar.scss │ │ └── NavBar.tsx │ ├── EButton │ │ ├── EButton.tsx │ │ └── EButton.scss │ ├── ELoading │ │ ├── ELoading.tsx │ │ └── ELoading.scss │ └── UserAddress │ │ └── UserAddress.scss ├── pages │ ├── test │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── msite │ │ ├── components │ │ │ ├── Tip │ │ │ │ ├── Tip.scss │ │ │ │ └── Tip.tsx │ │ │ ├── MsiteNavBar │ │ │ │ ├── MsiteNavBar.scss │ │ │ │ └── MsiteNavBar.tsx │ │ │ ├── NavSwipe │ │ │ │ ├── NavSwipe.scss │ │ │ │ └── NavSwipe.tsx │ │ │ ├── NavSwipeItem │ │ │ │ ├── NavSwipeItem.scss │ │ │ │ └── NavSwipeItem.tsx │ │ │ ├── Framework │ │ │ │ ├── Framework.scss │ │ │ │ └── Framework.tsx │ │ │ ├── SearchBar │ │ │ │ ├── SearchBar.scss │ │ │ │ └── SearchBar.tsx │ │ │ ├── Svip │ │ │ │ ├── Svip.tsx │ │ │ │ └── Svip.scss │ │ │ └── Advertising │ │ │ │ ├── Advertising.scss │ │ │ │ └── Advertising.tsx │ │ ├── index.config.ts │ │ └── index.scss │ ├── profile │ │ ├── address │ │ │ ├── add │ │ │ │ ├── index.scss │ │ │ │ ├── index.config.ts │ │ │ │ └── index.tsx │ │ │ ├── edit │ │ │ │ ├── index.scss │ │ │ │ ├── index.config.ts │ │ │ │ └── index.tsx │ │ │ ├── index.config.ts │ │ │ ├── search │ │ │ │ ├── index.config.ts │ │ │ │ ├── index.scss │ │ │ │ └── index.tsx │ │ │ ├── index.scss │ │ │ └── components │ │ │ │ └── AddressRow │ │ │ │ ├── AddressRow.scss │ │ │ │ └── AddressRow.tsx │ │ ├── index.config.ts │ │ ├── info │ │ │ ├── index.config.ts │ │ │ ├── password │ │ │ │ ├── index.config.ts │ │ │ │ ├── components │ │ │ │ │ └── Input │ │ │ │ │ │ ├── MyInput.scss │ │ │ │ │ │ └── MyInput.tsx │ │ │ │ └── index.scss │ │ │ ├── username │ │ │ │ ├── index.config.ts │ │ │ │ ├── index.scss │ │ │ │ └── index.tsx │ │ │ └── index.scss │ │ ├── index.scss │ │ └── components │ │ │ ├── Money │ │ │ ├── Money.scss │ │ │ └── Money.tsx │ │ │ └── UserHead │ │ │ ├── UserHead.tsx │ │ │ └── UserHead.scss │ ├── city │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── components │ │ │ ├── ABC │ │ │ ├── ABC.scss │ │ │ └── ABC.tsx │ │ │ ├── FilterList │ │ │ ├── FilterList.scss │ │ │ └── FilterList.tsx │ │ │ ├── List │ │ │ ├── List.scss │ │ │ └── List.tsx │ │ │ └── Search │ │ │ ├── Search.scss │ │ │ └── Search.tsx │ ├── discover │ │ ├── index.config.ts │ │ ├── components │ │ │ └── Prize │ │ │ │ ├── Prize.tsx │ │ │ │ └── Prize.scss │ │ └── index.scss │ ├── food │ │ ├── index.config.ts │ │ └── index.scss │ ├── login │ │ ├── index.config.ts │ │ └── index.scss │ ├── order │ │ ├── index.config.ts │ │ ├── detail │ │ │ └── index.config.ts │ │ ├── index.scss │ │ ├── components │ │ │ └── Ordercard │ │ │ │ ├── Ordercard.scss │ │ │ │ └── Ordercard.tsx │ │ └── index.tsx │ ├── register │ │ ├── index.config.ts │ │ └── index.scss │ ├── search │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── components │ │ │ ├── Back │ │ │ ├── Back.scss │ │ │ └── Back.tsx │ │ │ ├── SearchBar │ │ │ ├── SearchBar.scss │ │ │ └── SearchBar.tsx │ │ │ └── SearchItem │ │ │ ├── SearchItem.scss │ │ │ └── SearchItem.tsx │ ├── shop │ │ ├── index.config.ts │ │ ├── components │ │ │ ├── LeftBar │ │ │ │ ├── LeftBar.scss │ │ │ │ └── LeftBar.tsx │ │ │ ├── Recommend │ │ │ │ ├── Recommend.scss │ │ │ │ └── Recommend.tsx │ │ │ ├── ActivityModal │ │ │ │ ├── ActivityModal.scss │ │ │ │ └── ActivityModal.tsx │ │ │ ├── ShopItem │ │ │ │ ├── ShopItem.scss │ │ │ │ └── ShopItem.tsx │ │ │ ├── ShopInfo │ │ │ │ └── ShopInfo.scss │ │ │ ├── ShopInfoModal │ │ │ │ ├── ShopInfoModal.scss │ │ │ │ └── ShopInfoModal.tsx │ │ │ └── Head │ │ │ │ ├── Head.tsx │ │ │ │ └── Head.scss │ │ └── index.scss │ ├── address │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── components │ │ │ ├── Result │ │ │ ├── Result.scss │ │ │ └── Result.tsx │ │ │ ├── ResultItem │ │ │ ├── ResultItem.scss │ │ │ └── ResultItem.tsx │ │ │ ├── AtAddress │ │ │ ├── AtAddress.scss │ │ │ └── AtAddress.tsx │ │ │ ├── Profile │ │ │ ├── Profile.scss │ │ │ └── Profile.tsx │ │ │ └── Search │ │ │ ├── Search.scss │ │ │ └── Search.tsx │ └── clearing │ │ ├── index.config.ts │ │ ├── index.scss │ │ ├── components │ │ ├── Distribution │ │ │ ├── Distribution.scss │ │ │ └── Distribution.tsx │ │ ├── FooterDic │ │ │ ├── FooterDic.scss │ │ │ └── FooterDic.tsx │ │ ├── CartAddress │ │ │ ├── CartAddress.scss │ │ │ └── CartAddress.tsx │ │ └── CartInfo │ │ │ ├── CartInfo.scss │ │ │ └── CartInfo.tsx │ │ └── index.tsx ├── assets │ ├── styles │ │ ├── index.scss │ │ ├── _mixin.scss │ │ ├── _variables.scss │ │ └── normalize.scss │ ├── iconfont │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ └── images │ │ ├── default-head.png │ │ ├── add.svg │ │ ├── rec.svg │ │ ├── red.svg │ │ ├── gold.svg │ │ ├── service.svg │ │ ├── rule.svg │ │ ├── point.svg │ │ ├── download.svg │ │ ├── commend.svg │ │ ├── address.svg │ │ ├── xx2.svg │ │ ├── xx1.svg │ │ ├── cart2.svg │ │ ├── cart1.svg │ │ └── default-shop.svg ├── app.scss ├── redux │ ├── actions │ │ ├── cart.ts │ │ └── user.ts │ ├── reducers │ │ ├── index.ts │ │ ├── cart.ts │ │ └── user.ts │ ├── interface.ts │ ├── action-types │ │ └── index.ts │ └── store.ts ├── utils │ ├── setstylepx.ts │ ├── distance.ts │ ├── getDom.ts │ └── imgUrl.ts ├── config │ └── base.ts ├── hooks │ └── useDebounce.tsx ├── app.tsx ├── app.config.ts ├── index.html └── api │ └── serve.ts ├── .eslintrc.js ├── .gitignore ├── .editorconfig ├── babel.config.js ├── project.swan.json ├── config ├── prod.js ├── dev.js └── index.js ├── global.d.ts ├── tsconfig.json ├── project.config.json ├── package.json └── tatus /.swan/editor.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/components/EIcon/Eicon.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/test/index.config.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/msite/components/Tip/Tip.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/profile/address/add/index.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/profile/address/edit/index.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/msite/components/MsiteNavBar/MsiteNavBar.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': ['taro/react'] 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import 'mixin'; 2 | @import 'variables'; 3 | -------------------------------------------------------------------------------- /src/pages/city/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '选择城市', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/discover/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '发现', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/food/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '饿了么', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/login/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '登录', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/msite/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '饿了么', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/order/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '订单', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '我的', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/register/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '注册', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/search/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '搜索商家', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/shop/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '商家详情', 3 | } 4 | -------------------------------------------------------------------------------- /src/app.scss: -------------------------------------------------------------------------------- 1 | @import './assets/iconfont/iconfont.css'; 2 | @import './assets/styles/normalize.scss'; -------------------------------------------------------------------------------- /src/pages/address/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '选择收货地址', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/clearing/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '结算中心', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/order/detail/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '订单详情', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/info/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '账户信息', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/address/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '添加地址', 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | deploy_versions/ 3 | .temp/ 4 | .rn_temp/ 5 | node_modules/ 6 | .DS_Store 7 | .history 8 | -------------------------------------------------------------------------------- /src/components/ShopList/ShopList.scss: -------------------------------------------------------------------------------- 1 | .ele-shoplist-bottom { 2 | @include flex(); 3 | height: 80px; 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/profile/address/add/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '添加地址', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/address/edit/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '编辑地址', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/address/search/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '我的地址', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/info/password/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '修改密码', 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/profile/info/username/index.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navigationBarTitleText: '修改用户名', 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qingjuesama/taro-ele/HEAD/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qingjuesama/taro-ele/HEAD/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qingjuesama/taro-ele/HEAD/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qingjuesama/taro-ele/HEAD/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/images/default-head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qingjuesama/taro-ele/HEAD/src/assets/images/default-head.png -------------------------------------------------------------------------------- /src/pages/msite/components/NavSwipe/NavSwipe.scss: -------------------------------------------------------------------------------- 1 | .navswiper { 2 | padding-bottom: 20px; 3 | display: flex; 4 | flex-wrap: wrap; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/order/index.scss: -------------------------------------------------------------------------------- 1 | .order { 2 | min-height: 100vh; 3 | background: #f5f5f5; 4 | .order-main { 5 | padding-bottom: 150px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/redux/actions/cart.ts: -------------------------------------------------------------------------------- 1 | import { ADDCART } from '../action-types' 2 | 3 | // 增加商品 4 | export const actionAddCart = food => ({ type: ADDCART, payload: food }) 5 | -------------------------------------------------------------------------------- /src/components/EList/EList.scss: -------------------------------------------------------------------------------- 1 | .ele-elist { 2 | .ele-elist-title { 3 | margin: 25px 0 20px 25px; 4 | font-size: $font-size-24; 5 | color: $color-999; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/search/index.scss: -------------------------------------------------------------------------------- 1 | .search { 2 | padding: 0 20px; 3 | .icon { 4 | padding: 20px 0; 5 | color: $color-999; 6 | font-size: 34px; 7 | font-weight: 600; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/setstylepx.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import { designWidth } from '../config/base' 3 | 4 | export default (size?: number) => { 5 | return Taro.pxTransform(size, designWidth) 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/distance.ts: -------------------------------------------------------------------------------- 1 | export default (distance: number): string => { 2 | if (distance > 1000) { 3 | return Math.round(distance / 10) / 100 + 'km' 4 | } else { 5 | return distance + 'm' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/city/index.scss: -------------------------------------------------------------------------------- 1 | .selectcity { 2 | background: #fff; 3 | -webkit-overflow-scrolling: touch; //ios不流畅问题 4 | } 5 | .city-show { 6 | display: block; 7 | } 8 | .city-hide { 9 | display: none; 10 | } 11 | -------------------------------------------------------------------------------- /src/redux/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import user from './user' 3 | import cart from './cart' 4 | 5 | export default combineReducers({ 6 | ...user, 7 | ...cart, 8 | }) 9 | 10 | -------------------------------------------------------------------------------- /src/components/Test/Test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | const Test = (test) => { 5 | if (test) { 6 | return null 7 | } 8 | return 1 9 | } 10 | export default Test 11 | -------------------------------------------------------------------------------- /src/components/Card/Card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | margin-bottom: 20px; 3 | padding: 30px; 4 | background: #fff; 5 | .card-title { 6 | padding-bottom: 20px; 7 | color: #000; 8 | font-size: 30px; 9 | font-weight: 600; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/components/EScroll/EScroll.scss: -------------------------------------------------------------------------------- 1 | .escroll { 2 | flex: 1; 3 | overflow: hidden; 4 | .scroll-box { 5 | height: 100%; 6 | display: flex; 7 | flex-direction: column; 8 | -webkit-overflow-scrolling: touch; //ios不流畅问题 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/config/base.ts: -------------------------------------------------------------------------------- 1 | export const H5 = process.env.TARO_ENV === 'h5' 2 | export const WEAPP = process.env.TARO_ENV === 'weapp' 3 | export const designWidth = '750' 4 | export const defaultImg = 5 | 'https://cube.elemecdn.com/a/7c/a7e9e5aa15b1b8fc6f1bece8ee385jpeg.jpeg' 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | # root = true 3 | 4 | [*] 5 | # indent_style = space 6 | # indent_size = 2 7 | # charset = utf-8 8 | # trim_trailing_whitespace = true 9 | # insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel-preset-taro 更多选项和默认值: 2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md 3 | module.exports = { 4 | presets: [ 5 | ['taro', { 6 | framework: 'react', 7 | ts: true 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/clearing/index.scss: -------------------------------------------------------------------------------- 1 | .clearing { 2 | min-height: 100vh; 3 | background-image: linear-gradient( 4 | 0deg, 5 | #f5f5f5, 6 | #f5f5f5 65%, 7 | hsla(0, 0%, 96%, .3) 75%, 8 | hsla(0, 0%, 96%, 0) 9 | ), 10 | linear-gradient(270deg, #0085ff, #0af); 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/city/components/ABC/ABC.scss: -------------------------------------------------------------------------------- 1 | .selectcity-main-content-abc { 2 | position: fixed; 3 | right: 20px; 4 | top: 240px; 5 | .selectcity-main-content-abc-item { 6 | text-align: center; 7 | color: #9d9d9d; 8 | margin: 4px; 9 | font-size: 24px; 10 | } 11 | } -------------------------------------------------------------------------------- /project.swan.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "baiduboxapp", 3 | "setting": { 4 | "urlCheck": true 5 | }, 6 | "swan": { 7 | "baiduboxapp": { 8 | "swanJsVersion": "3.190.255-rc", 9 | "extensionJsVersion": "100.14.255" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/pages/test/index.scss: -------------------------------------------------------------------------------- 1 | .test { 2 | background: #eeeeee; 3 | margin-top: 20px; 4 | height: 500px; 5 | } 6 | .abc { 7 | position: fixed; 8 | top: 0; 9 | right: 0; 10 | .abc-item { 11 | background: red; 12 | color: #fff; 13 | padding: 10px; 14 | margin: 10px; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/profile/index.scss: -------------------------------------------------------------------------------- 1 | .profile { 2 | background: #f5f5f5; 3 | height: 100vh; 4 | 5 | .profile-block { 6 | margin-top: 20px; 7 | } 8 | .profile-privacy { 9 | color: #4da6f0; 10 | text-align: center; 11 | padding: 30px 0; 12 | } 13 | .profile-list { 14 | padding-top: 25px; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/ETransition/ETransition.scss: -------------------------------------------------------------------------------- 1 | @include ele-animation('top', scaleY(0), scaleY(1), center top); 2 | @include ele-animation('left', scale(0.45, 0.45), scale(1, 1), top left); 3 | @include ele-animation('right', scale(0.45, 0.45), scale(1, 1), top right); 4 | @include ele-animation('bottom', scaleY(0), scaleY(1), center bottom); 5 | -------------------------------------------------------------------------------- /src/pages/address/index.scss: -------------------------------------------------------------------------------- 1 | .selectaddress { 2 | height: 100vh; 3 | background: #f4f4f4; 4 | color: #333; 5 | -webkit-overflow-scrolling: touch; //ios不流畅问题 6 | } 7 | .start { 8 | transform: translateX(100vw); 9 | } 10 | .end { 11 | transform: translateX(0); 12 | } 13 | .add { 14 | position: absolute; 15 | right: 20px; 16 | top: 24px; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/EList/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | import EList, { EListProps } from './EList' 4 | import EItem, { EItemProps } from './EItem' 5 | 6 | type TransListProps = FC & { 7 | EItem: FC 8 | } 9 | 10 | const TransList = EList as TransListProps 11 | 12 | TransList.EItem = EItem 13 | 14 | export default TransList 15 | -------------------------------------------------------------------------------- /src/pages/food/index.scss: -------------------------------------------------------------------------------- 1 | .food { 2 | height: 100vh; 3 | .food-topbar { 4 | position: sticky; 5 | top: 0; 6 | z-index: 3; 7 | } 8 | .food-shoplist { 9 | height: calc(100vh - 160px); 10 | -webkit-overflow-scrolling: touch; // ios 11 | } 12 | .food-filterbar { 13 | top: 80px; 14 | } 15 | .no-page { 16 | top: 0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/getDom.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | 3 | export default (nodeName: string) => { 4 | return new Promise((resolve) => { 5 | const query = Taro.createSelectorQuery() 6 | query.selectAll(nodeName).boundingClientRect() 7 | query.selectViewport().scrollOffset() 8 | query.exec((res) => { 9 | resolve(res) 10 | }) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/redux/interface.ts: -------------------------------------------------------------------------------- 1 | export interface Address { 2 | id: string 3 | city: string 4 | address: string 5 | address_detail: string 6 | latitude: string 7 | longitude: string 8 | name: string 9 | phone: string 10 | sex: string 11 | } 12 | 13 | export interface Reducers { 14 | currentAddress: Address 15 | userAddress: Address 16 | userAddressList: Address[] 17 | token: string 18 | } 19 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/order_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/NoDataTip/NoDataTip.scss: -------------------------------------------------------------------------------- 1 | .ele-nodatatipo { 2 | @include flex; 3 | flex-direction: column; 4 | padding-bottom: 20px; 5 | .ele-nodatatipo-img { 6 | width: 420px; 7 | } 8 | .ele-nodatatipo-title { 9 | font-size: $font-size-34; 10 | padding-bottom: 20px; 11 | } 12 | .ele-nodatatipo-info { 13 | font-size: $font-size-24; 14 | color: $color-999; 15 | padding-bottom: 20px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Star/Star.scss: -------------------------------------------------------------------------------- 1 | .star { 2 | position: relative; 3 | width: 120px; 4 | height: 25px; 5 | .star1 { 6 | position: absolute; 7 | width: 100%; 8 | height: 100%; 9 | background: url('../../assets/images/xx2.svg') no-repeat; 10 | } 11 | .star2 { 12 | position: absolute; 13 | width: 100%; 14 | height: 100%; 15 | background: url('../../assets/images/xx1.svg') no-repeat; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"production"' 4 | }, 5 | defineConstants: { 6 | }, 7 | mini: {}, 8 | h5: { 9 | /** 10 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。 11 | * 参考代码如下: 12 | * webpackChain (chain) { 13 | * chain.plugin('analyzer') 14 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) 15 | * } 16 | */ 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | env: { 5 | NODE_ENV: '"production"', // production development 6 | }, 7 | defineConstants: {}, 8 | mini: {}, 9 | h5: { 10 | devServer: { 11 | port: 3000, 12 | proxy: { 13 | '/api': { 14 | target: 'http://localhost:4000', 15 | // pathRewrite: { '^/api': '' }, 16 | }, 17 | }, 18 | }, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/profile/info/password/components/Input/MyInput.scss: -------------------------------------------------------------------------------- 1 | .myinput { 2 | .password-input { 3 | background: #f2f2f2; 4 | display: flex; 5 | border: 1px #ececec solid; 6 | height: 80px; 7 | align-items: center; 8 | padding-left: 10px; 9 | } 10 | .password-error { 11 | border: 1px #f0937e solid; 12 | } 13 | .password-tip { 14 | color: #ea3106; 15 | font-size: 24px; 16 | padding-top: 10px; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/ShopButton/ShopButton.scss: -------------------------------------------------------------------------------- 1 | .shopbutton { 2 | display: flex; 3 | align-items: center; 4 | .shopbutton-rec { 5 | .icon { 6 | color: #2395ff; 7 | font-size: 42px; 8 | } 9 | } 10 | 11 | .shopbutton-num { 12 | padding: 0 15px; 13 | color: #3f3f3f; 14 | } 15 | 16 | .shopbutton-add { 17 | overflow: hidden; 18 | .icon { 19 | color: #2395ff; 20 | font-size: 42px; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/profile_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/clearing/components/Distribution/Distribution.scss: -------------------------------------------------------------------------------- 1 | .distribution { 2 | margin: 20px; 3 | padding: 10px 20px; 4 | background: #fff; 5 | .distribution-row { 6 | display: flex; 7 | justify-content: space-between; 8 | padding: 20px 0; 9 | &:first-child { 10 | border-bottom: 1px #f8f8f8 solid; 11 | } 12 | .row-left { 13 | color: #333; 14 | } 15 | 16 | .row-right { 17 | color: #2395ff; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/images/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/pages/address/components/Result/Result.scss: -------------------------------------------------------------------------------- 1 | // 搜索地址 2 | .search-result { 3 | background: #fff; 4 | .search-main-tip { 5 | color: #999; 6 | padding-top: 50px; 7 | padding-bottom: 150px; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | .tip-title { 12 | font-size: 30px; 13 | padding: 20px 0; 14 | } 15 | } 16 | } 17 | .search-show { 18 | display: block; 19 | } 20 | .search-hide { 21 | display: none; 22 | } 23 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | 3 | const useDebounce = (value: any, delay: number = 300) => { 4 | const [debounceValue, setDebounceValue] = useState(value) 5 | 6 | useEffect(() => { 7 | const handle = setTimeout(() => { 8 | setDebounceValue(value) 9 | }) 10 | 11 | return () => { 12 | clearTimeout(handle) 13 | } 14 | }, [value, delay]) 15 | 16 | return debounceValue 17 | } 18 | 19 | export default useDebounce 20 | -------------------------------------------------------------------------------- /src/pages/msite/components/NavSwipeItem/NavSwipeItem.scss: -------------------------------------------------------------------------------- 1 | .navswiper-item { 2 | @include flex; 3 | flex-direction: column; 4 | width: 20%; 5 | padding-top: 20px; 6 | .navswiper-item-image { 7 | display: flex; 8 | justify-content: center; 9 | width: 100%; 10 | .nav-image { 11 | width: 90px; 12 | height: 90px; 13 | } 14 | } 15 | .navswiper-item-title { 16 | color: $color-666; 17 | font-size: 24px; 18 | text-align: center; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png"; 2 | declare module "*.gif"; 3 | declare module "*.jpg"; 4 | declare module "*.jpeg"; 5 | declare module "*.svg"; 6 | declare module "*.css"; 7 | declare module "*.less"; 8 | declare module "*.scss"; 9 | declare module "*.sass"; 10 | declare module "*.styl"; 11 | 12 | // @ts-ignore 13 | declare const process: { 14 | env: { 15 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'; 16 | [key: string]: any; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/images/rec.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/pages/msite/index.scss: -------------------------------------------------------------------------------- 1 | .msite { 2 | height: 100%; 3 | flex: 1; 4 | display: flex; 5 | flex-direction: column; 6 | 7 | .msite-navbar { 8 | padding: 20px 20px 0 20px; 9 | height: 69px; 10 | .msite-navbar-title { 11 | padding: 0 10px; 12 | font-size: $font-size-34; 13 | color: $white-color; 14 | font-weight: $font-weight; 15 | max-width: 40vw; 16 | } 17 | } 18 | .nologin { 19 | padding: 50px 0; 20 | } 21 | .msite-filter { 22 | top: 110px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/order_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/city/components/FilterList/FilterList.scss: -------------------------------------------------------------------------------- 1 | // 城市搜索结果 2 | .result-city { 3 | margin-left: 20px; 4 | height: 100vh; 5 | -webkit-overflow-scrolling: touch; //ios不流畅问题 6 | .result-city-item { 7 | padding: 25px 0; 8 | border-bottom: 1px #eee solid; 9 | color: #666; 10 | font-size: 24px; 11 | &:last-of-type { 12 | border: none; 13 | } 14 | } 15 | .result-city-null { 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | padding: 100px 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/images/gold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/service.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/images/rule.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/pages/city/components/List/List.scss: -------------------------------------------------------------------------------- 1 | // 左侧列表 2 | .selectcity-main-content-city { 3 | -webkit-overflow-scrolling: touch; //ios不流畅问题 4 | height: 100vh; 5 | .selectcity-main-content-city-item { 6 | .selectcity-main-content-city-a { 7 | background: #f5f5f5; 8 | padding: 20px; 9 | color: #666; 10 | border-bottom: 1px #eee solid; 11 | } 12 | 13 | .selectcity-main-content-city-name { 14 | color: #333; 15 | margin: 0 20px; 16 | padding: 20px 0; 17 | border-bottom: 1px #f5f5f5 solid; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/msite/components/Framework/Framework.scss: -------------------------------------------------------------------------------- 1 | .framework { 2 | display: flex; 3 | flex-wrap: wrap; 4 | .framework-item { 5 | @include flex; 6 | flex-direction: column; 7 | width: 20%; 8 | padding-top: 20px; 9 | .framework-item-title { 10 | width: 90px; 11 | height: 90px; 12 | background: $background-f5f5f5; 13 | border-radius: 50%; 14 | } 15 | .framework-item-txt { 16 | width: 80px; 17 | height: 25px; 18 | margin-top: 15px; 19 | background: $background-f5f5f5; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/pages/msite/components/SearchBar/SearchBar.scss: -------------------------------------------------------------------------------- 1 | $main-height: 72px; 2 | 3 | .searchbar { 4 | @include flex; 5 | background-image: $defualt-background-image; 6 | padding: 20px 26px; 7 | position: sticky; 8 | top: -1px; 9 | z-index: 9; 10 | .searchbar-main { 11 | background: $white-color; 12 | width: 100%; 13 | height: $main-height; 14 | line-height: $main-height; 15 | text-align: center; 16 | .searchbar-icon { 17 | padding-right: 10px; 18 | } 19 | .searchbar-title { 20 | color: $color-999; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/redux/reducers/cart.ts: -------------------------------------------------------------------------------- 1 | import { ADDCART } from '../action-types' 2 | 3 | const initCart = { 4 | totalPrice: 0, // (现)总价 5 | originalPrice: 0, // (原)总价 6 | boxPrice: 0, // 餐盒费 7 | foodTotal: 0, // 总数量 8 | foods: [], 9 | } 10 | const cart = (state = initCart, action) => { 11 | const { type, payload } = action 12 | switch (type) { 13 | case ADDCART: 14 | const index = state.foods.findIndex() 15 | state.foods.push(payload) 16 | return { ...state } 17 | default: 18 | return state 19 | } 20 | } 21 | 22 | export default { cart } 23 | -------------------------------------------------------------------------------- /src/redux/action-types/index.ts: -------------------------------------------------------------------------------- 1 | export const CURRENTADDRESS = 'CURRENTADDRESS' // 定位信息 2 | export const SETTOKEN = 'SETTOKEN' // 设置token 3 | export const REMOVETOKEN = 'REMOVETOKEN' // 删除token 4 | export const USERADDRESS = 'USERADDRESS' // 用户当前编辑地址信息 5 | export const SETUSERADDRESS = 'SETUSERADDRESS' // 更新当前用户地址信息 6 | export const REMOVEUSERADDRESS = 'REMOVEUSERADDRESS' // 清空用户地址信息 7 | export const GETUSERADDRESSLIST = 'GETUSERADDRESSLIST' // 获取用户收货地址列表 8 | export const REMOVEUSERADDRESSLIST = 'REMOVEUSERADDRESSLIST' // 清空用户收货地址列表 9 | export const ADDCART = 'ADDCART' // 增加购物车商品 10 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/profile_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/imgUrl.ts: -------------------------------------------------------------------------------- 1 | // 处理图片地址 2 | export default (url: string): string => { 3 | const BASEURL = 'https://cube.elemecdn.com/' 4 | let types = ['jpeg', 'jpg', 'png', 'gif', 'JPEG'] 5 | let str = '' 6 | let type = '' 7 | 8 | for (let i = 0; i < url.length; i++) { 9 | if (i === 1) { 10 | str += '/' 11 | } else if (i === 3) { 12 | str += '/' 13 | } 14 | str += url[i] 15 | } 16 | 17 | for (let i = 0; i < types.length; i++) { 18 | if (url.includes(types[i])) { 19 | type = types[i] 20 | } 21 | } 22 | 23 | return BASEURL + str + '.' + type 24 | } 25 | -------------------------------------------------------------------------------- /src/pages/address/components/ResultItem/ResultItem.scss: -------------------------------------------------------------------------------- 1 | .search-result-item { 2 | padding: 30px 20px; 3 | border-bottom: 1px #eee solid; 4 | &:last-of-type { 5 | border: none; 6 | } 7 | .search-result-item-top { 8 | display: flex; 9 | justify-content: space-between; 10 | .search-result-top-name { 11 | font-weight: 600; 12 | color: #333; 13 | font-size: 30px; 14 | } 15 | 16 | .search-result-item-top-distance { 17 | font-size: 24px; 18 | } 19 | } 20 | 21 | .search-result-item-address { 22 | font-size: 24px; 23 | color: #333; 24 | } 25 | } -------------------------------------------------------------------------------- /src/assets/images/point.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/components/Star/Star.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './Star.scss' 5 | 6 | interface StarProps { 7 | rating: number 8 | } 9 | 10 | const Star: FC = (props) => { 11 | const { rating } = props 12 | 13 | // 计算评分⭐⭐ 14 | const countGrade = (): string => { 15 | return (rating / 5) * 100 + '%' 16 | } 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export default Star 27 | -------------------------------------------------------------------------------- /src/components/ETabs/ETabs.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | .tabs-bar { 3 | position: sticky; 4 | top: 0; 5 | z-index: 1; 6 | background: #fff; 7 | border-bottom: 1px #f5f5f5 solid; 8 | display: flex; 9 | .tabs-bar-item { 10 | width: 33.33%; 11 | text-align: center; 12 | padding: 15px 0; 13 | .tabs-bar-item-title { 14 | color: #666; 15 | font-size: 30px; 16 | } 17 | .tabs-active { 18 | font-weight: 600; 19 | border-bottom: 4px #2291f8 solid; 20 | color: #333 !important; 21 | padding-bottom: 15px; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/images/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/pages/profile/info/password/index.scss: -------------------------------------------------------------------------------- 1 | .password { 2 | height: 100vh; 3 | background: #f5f5f5; 4 | .passowrd-main { 5 | background: #ffffff; 6 | padding: 0 20px 20px 20px; 7 | .passowrd-item { 8 | margin-top: 20px; 9 | } 10 | } 11 | .password-button-main { 12 | margin: 20px; 13 | .password-button { 14 | background: #3199e8; 15 | color: #fff; 16 | font-weight: 600; 17 | &:active { 18 | background: #62bbff; 19 | } 20 | &::after { 21 | border: none; 22 | } 23 | } 24 | .password-button[disabled] { 25 | background: #cccccc; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/search/components/Back/Back.scss: -------------------------------------------------------------------------------- 1 | .back { 2 | margin: 20px 0 30px 0; 3 | .head { 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | .title { 8 | color: $color-666; 9 | font-size: 32px; 10 | font-weight: 600; 11 | padding-bottom: 20px; 12 | } 13 | 14 | .icon { 15 | color: #cecece; 16 | font-size: 24px; 17 | } 18 | } 19 | 20 | .tabs { 21 | display: flex; 22 | flex-wrap: wrap; 23 | .tab { 24 | margin: 10px 20px 10px 0; 25 | padding: 16px 20px; 26 | background: #f7f7f7; 27 | color: $color-666; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Provider } from 'react-redux' 3 | import configStore from './redux/store' 4 | import { initCurrentAddress } from './redux/actions/user' 5 | 6 | import './app.scss' 7 | 8 | const store = configStore() 9 | 10 | const App = (props) => { 11 | useEffect(() => { 12 | // 获取ip地址 经纬度 13 | const { currentAddress } = store.getState() 14 | if (!currentAddress.latitude && !currentAddress.longitude) { 15 | store.dispatch(initCurrentAddress()) 16 | } 17 | }, []) 18 | 19 | // 请勿修改此函数 20 | return {props.children} 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/discover_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/profile/address/index.scss: -------------------------------------------------------------------------------- 1 | .profileaddress { 2 | background: #f5f5f5; 3 | height: 100vh; 4 | .profileaddress-list { 5 | margin-bottom: 100px; 6 | } 7 | .profileaddress-add { 8 | position: fixed; 9 | bottom: 0; 10 | left: 0; 11 | right: 0; 12 | border-top: 1px #ddd solid; 13 | background: #fff; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | height: 100px; 18 | color: #3190e8; 19 | .profileaddress-add-text { 20 | font-size: 32px; 21 | padding-left: 10px; 22 | } 23 | .icon { 24 | font-size: 40px; 25 | margin-top: 4px; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/city/components/Search/Search.scss: -------------------------------------------------------------------------------- 1 | // 搜索 2 | .selectcity-search { 3 | background-image: $defualt-background-image; 4 | overflow: hidden; 5 | .selectcity-search-main { 6 | background: #fff; 7 | border-radius: 50px; 8 | display: flex; 9 | align-items: center; 10 | margin: 20px 50px; 11 | .selectcity-search-icon { 12 | margin: 0 10px 0 20px; 13 | .icon { 14 | font-size: 25px; 15 | } 16 | } 17 | .selectcity-search-input { 18 | width: 500px; 19 | font-size: 24px; 20 | padding: 10px 0; 21 | .selectcity-search-input-content { 22 | width: 500px; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/EList/EList.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import classnames from 'classnames' 3 | import { View } from '@tarojs/components' 4 | 5 | import './EList.scss' 6 | 7 | export interface EListProps { 8 | children: React.ReactNode 9 | renderHeader?: () => React.ReactNode 10 | className?: string 11 | } 12 | 13 | const EList: FC = (props) => { 14 | const { renderHeader, children, className } = props 15 | const classes = classnames('ele-elist', className) 16 | return ( 17 | 18 | {renderHeader && {renderHeader()}} 19 | {children} 20 | 21 | ) 22 | } 23 | export default EList 24 | -------------------------------------------------------------------------------- /src/components/Card/Card.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | 5 | import './Card.scss' 6 | 7 | interface CardProps { 8 | title?: string 9 | children?: React.ReactNode 10 | className?: string 11 | style?: React.CSSProperties 12 | } 13 | 14 | const Card: FC = (props) => { 15 | const { title, children, className, style } = props 16 | const classes = classnames('card', className) 17 | return ( 18 | 19 | {title && {title}} 20 | {children} 21 | 22 | ) 23 | } 24 | 25 | export default Card 26 | -------------------------------------------------------------------------------- /src/pages/msite/components/Framework/Framework.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './Framework.scss' 5 | 6 | const framework = Array(10).fill(1) 7 | 8 | const Framework = () => { 9 | return ( 10 | 11 | 12 | {framework.map((item, i) => { 13 | return ( 14 | 15 | 16 | 17 | 18 | ) 19 | })} 20 | 21 | 22 | ) 23 | } 24 | 25 | export default Framework 26 | -------------------------------------------------------------------------------- /src/pages/search/components/SearchBar/SearchBar.scss: -------------------------------------------------------------------------------- 1 | .mysearch { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | position: relative; 6 | padding: 30px 0; 7 | .zoom { 8 | position: absolute; 9 | left: 20px; 10 | color: $color-999; 11 | font-size: 24px; 12 | } 13 | .clear { 14 | position: absolute; 15 | right: 100px; 16 | color: $color-999; 17 | font-size: 24px; 18 | } 19 | 20 | .mysearch-input { 21 | flex: 1; 22 | padding: 8px 0; 23 | padding: 10px 70px; 24 | background: #f8f8f8; 25 | } 26 | 27 | .mysearch-text { 28 | padding-left: 20px; 29 | color: $color-333; 30 | font-size: 32px; 31 | font-weight: 700; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/msite/components/Svip/Svip.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | 4 | import './Svip.scss' 5 | 6 | const Svip: FC = () => { 7 | return ( 8 | 9 | 10 | 11 | 15 | 16 | 超级会员 17 | 每月领20元红包 18 | 19 | 限时6元开通 20 | 21 | ) 22 | } 23 | 24 | export default Svip 25 | -------------------------------------------------------------------------------- /src/pages/discover/components/Prize/Prize.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | 4 | import './Prize.scss' 5 | 6 | const Prize = ({ suggest }) => { 7 | return ( 8 | 9 | {suggest.corner_marker} 10 | 11 | 12 | {suggest.title} 13 | 14 | {suggest.points_required}金币 15 | ¥{suggest.original_price} 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default Prize 23 | -------------------------------------------------------------------------------- /src/pages/city/components/ABC/ABC.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './ABC.scss' 5 | 6 | interface ABCProps { 7 | alphabet: string[] 8 | onLinkCity: (alp: string) => void 9 | } 10 | 11 | const ABC: FC = (props) => { 12 | const { alphabet, onLinkCity } = props 13 | return ( 14 | 15 | {alphabet.map((alp) => { 16 | return ( 17 | onLinkCity(alp)} 21 | > 22 | {alp} 23 | 24 | ) 25 | })} 26 | 27 | ) 28 | } 29 | 30 | export default ABC 31 | -------------------------------------------------------------------------------- /src/assets/images/commend.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/pages/msite/components/Advertising/Advertising.scss: -------------------------------------------------------------------------------- 1 | $kf-color: #af8260; 2 | 3 | .advertising { 4 | display: flex; 5 | justify-content: space-between; 6 | margin: 20px 20px 0 20px; 7 | padding: 10px 30px; 8 | background: $background-f5f5f5; 9 | line-height: 1.2; 10 | .advertising-left { 11 | padding-top: 20px; 12 | .title { 13 | font-size: 32px; 14 | font-weight: 600; 15 | } 16 | 17 | .detail { 18 | padding: 10px 0; 19 | color: $color-777; 20 | } 21 | 22 | .href { 23 | padding: 10px 0 0 0; 24 | color: $kf-color; 25 | } 26 | } 27 | 28 | .advertising-right { 29 | width: 282px; 30 | height: 188px; 31 | .image { 32 | width: 100%; 33 | height: 100%; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/profile/components/Money/Money.scss: -------------------------------------------------------------------------------- 1 | .money { 2 | display: flex; 3 | height: 150px; 4 | background: #fff; 5 | .money-red { 6 | width: 50%; 7 | display: flex; 8 | justify-content: center; 9 | flex-direction: column; 10 | align-items: center; 11 | border-right: 1px #f0f0f0 solid; 12 | &:last-child { 13 | border: none; 14 | } 15 | .money-imgs { 16 | width: 48px; 17 | height: 48px; 18 | .money-image { 19 | width: 100%; 20 | height: 100%; 21 | } 22 | } 23 | } 24 | 25 | .money-gold { 26 | width: 50%; 27 | .money-imgs { 28 | width: 48px; 29 | height: 48px; 30 | .money-image { 31 | width: 100%; 32 | height: 100%; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/images/address.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/pages/clearing/components/FooterDic/FooterDic.scss: -------------------------------------------------------------------------------- 1 | .footerdic { 2 | display: flex; 3 | align-items: center; 4 | position: fixed; 5 | right: 0; 6 | bottom: 0; 7 | left: 0; 8 | height: 80px; 9 | background: #3c3c3c; 10 | .footerdic-price { 11 | padding-left: 10px; 12 | color: #fff; 13 | font-size: 36px; 14 | font-weight: 600; 15 | } 16 | 17 | .footerdic-discounts { 18 | color: #999; 19 | font-size: 24px; 20 | } 21 | 22 | .footerdic-submit { 23 | position: absolute; 24 | right: 0; 25 | bottom: 0; 26 | width: 210px; 27 | height: 100%; 28 | border: none; 29 | background: #00e067; 30 | color: #fff; 31 | font-size: 34px; 32 | line-height: 80px; 33 | text-align: center; 34 | border-radius: 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/shop/components/LeftBar/LeftBar.scss: -------------------------------------------------------------------------------- 1 | .leftbar { 2 | display: flex; 3 | align-items: center; 4 | position: relative; 5 | padding: 30px 15px; 6 | background: #f8f8f8; 7 | .leftbar-icon { 8 | width: 26px; 9 | height: 26px; 10 | margin-right: 10px; 11 | } 12 | .leftbar-name { 13 | color: #666; 14 | font-size: 24px; 15 | } 16 | 17 | .leftbar-num { 18 | position: absolute; 19 | top: 5px; 20 | right: 10px; 21 | height: 25px; 22 | padding: 0 7px; 23 | background-image: linear-gradient(-90deg, #ff7416, #ff3c15 98%); 24 | color: #fff; 25 | font-size: 24px; 26 | line-height: 25px; 27 | border-radius: 15px; 28 | } 29 | } 30 | .leftbar-active { 31 | background: #fff; 32 | .leftbar-name { 33 | color: #000; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/EScroll/EScroll.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, ScrollView } from '@tarojs/components' 3 | import { ScrollViewProps } from '@tarojs/components/types/ScrollView' 4 | import classnames from 'classnames' 5 | 6 | import './EScroll.scss' 7 | 8 | interface EScrollProps extends ScrollViewProps { 9 | className?: string 10 | id?: string 11 | children: React.ReactNode 12 | } 13 | 14 | const EScroll: FC = (props) => { 15 | const { className, id, children, ...reset } = props 16 | const classes = classnames('scroll-box', className) 17 | return ( 18 | 19 | 20 | {children} 21 | 22 | 23 | ) 24 | } 25 | 26 | export default EScroll 27 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/discover_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/clearing/components/FooterDic/FooterDic.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './FooterDic.scss' 5 | 6 | interface FooterDicProps { 7 | totalPrice 8 | discountsPrice 9 | onPay 10 | payLoading 11 | } 12 | 13 | const FooterDic: FC = (props) => { 14 | const { totalPrice, discountsPrice, onPay, payLoading } = props 15 | return ( 16 | 17 | ¥{totalPrice} 18 | 19 | |已优惠¥ 20 | {discountsPrice} 21 | 22 | 23 | {payLoading ? '正在支付...' : '去支付'} 24 | 25 | 26 | ) 27 | } 28 | 29 | export default FooterDic 30 | -------------------------------------------------------------------------------- /src/pages/shop/components/Recommend/Recommend.scss: -------------------------------------------------------------------------------- 1 | .recommend { 2 | display: inline-block; 3 | width: 250px; 4 | margin-right: 30px; 5 | .recommend-img { 6 | width: 250px; 7 | height: 250px; 8 | border-radius: 10px; 9 | } 10 | 11 | .recommend-main { 12 | .recommend-main-title { 13 | text-overflow: ellipsis; 14 | white-space: nowrap; 15 | overflow: hidden; 16 | } 17 | 18 | .recommend-main-pingjia { 19 | font-size: 24px; 20 | color: #999; 21 | } 22 | 23 | .recommend-main-numpice { 24 | display: flex; 25 | justify-content: space-between; 26 | .recommend-main-numpice-price { 27 | color: #ff5339; 28 | font-size: 36px; 29 | .price-fuhao { 30 | font-size: 24px; 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/pages/profile/info/index.scss: -------------------------------------------------------------------------------- 1 | .profileinfo { 2 | color: #333; 3 | background: #f5f5f5; 4 | height: 100vh; 5 | .profileinfo-headimg { 6 | border-radius: 50%; 7 | width: 120px; 8 | height: 120px; 9 | margin: 15px 0; 10 | } 11 | .profileinfo-phone { 12 | color: #0097ff; 13 | font-size: 34px; 14 | margin-right: 10px; 15 | } 16 | .out-login { 17 | margin-top: 20px; 18 | .out-button { 19 | background: #fff; 20 | border: none; 21 | outline: none; 22 | color: #ff5339; 23 | font-weight: 600; 24 | &::after { 25 | border: none; 26 | } 27 | &:active { 28 | color: #ff7c68; 29 | } 30 | } 31 | } 32 | .profileinfo-title { 33 | font-weight: bold; 34 | } 35 | .profileinfo-alter { 36 | color: $blue-color; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/msite/components/SearchBar/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Text, Navigator } from '@tarojs/components' 3 | 4 | import EIcon, { EIconProps } from '../../../../components/EIcon/EIcon' 5 | 6 | import './SearchBar.scss' 7 | 8 | interface SearchBarProps extends Omit { 9 | title: string 10 | icon: EIconProps['type'] 11 | url: string 12 | } 13 | 14 | const SearchBar: FC = (props) => { 15 | const { title, icon, url } = props 16 | return ( 17 | 18 | 19 | 20 | {title} 21 | 22 | 23 | ) 24 | } 25 | 26 | export default SearchBar 27 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.scss: -------------------------------------------------------------------------------- 1 | $height: 88px; 2 | $margin-lr: 20px; 3 | 4 | .navbar { 5 | display: flex; 6 | position: relative; 7 | align-items: center; 8 | height: $height; 9 | background: $defualt-background-image; 10 | .navbar-black { 11 | position: absolute; 12 | z-index: 1; 13 | font-size: $font-size-34; 14 | color: $white-color; 15 | left: $margin-lr; 16 | } 17 | .navbar-title { 18 | position: absolute; 19 | left: 0; 20 | right: 0; 21 | top: 0; 22 | bottom: 0; 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | color: $white-color; 27 | font-size: $font-size-34; 28 | } 29 | .navbar-href { 30 | position: absolute; 31 | z-index: 1; 32 | color: $white-color; 33 | font-size: $font-size-32; 34 | right: $margin-lr; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/clearing/components/CartAddress/CartAddress.scss: -------------------------------------------------------------------------------- 1 | $color: #fff; 2 | $lh: 1.2; 3 | 4 | .cartaddress { 5 | padding: 20px; 6 | .title { 7 | color: #ceedff; 8 | } 9 | 10 | .address-title { 11 | color: $color; 12 | } 13 | 14 | .useinfo { 15 | .useinfo-title { 16 | display: flex; 17 | align-items: center; 18 | justify-content: space-between; 19 | .useinfo-title-text { 20 | overflow: hidden; 21 | padding: 10px 0; 22 | color: $color; 23 | font-size: 44px; 24 | font-weight: 600; 25 | line-height: $lh; 26 | white-space: nowrap; 27 | text-overflow: ellipsis; 28 | } 29 | .icon { 30 | color: #fff; 31 | font-size: 28px; 32 | } 33 | } 34 | 35 | .useinfo-name { 36 | color: $color; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "removeComments": false, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "lib", 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "rootDir": ".", 18 | "jsx": "react", 19 | "jsxFactory": "React.createElement", 20 | "allowJs": true, 21 | "resolveJsonModule": true, 22 | "typeRoots": [ 23 | "node_modules/@types", 24 | "global.d.ts" 25 | ] 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | "dist" 30 | ], 31 | "compileOnSave": false 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/profile/info/username/index.scss: -------------------------------------------------------------------------------- 1 | .username { 2 | height: 100vh; 3 | background: #f5f5f5; 4 | .username-main { 5 | padding-top: 20px; 6 | margin: 0 20px; 7 | .username-input { 8 | display: flex; 9 | background: #fff; 10 | height: 80px; 11 | align-items: center; 12 | padding-left: 20px; 13 | } 14 | 15 | .username-text { 16 | color: #a9a9a9; 17 | font-size: 24px; 18 | padding: 10px 0; 19 | } 20 | 21 | .username-button { 22 | background: #00d762; 23 | color: #fff; 24 | font-weight: 600; 25 | &:active{ 26 | background: #0dec72; 27 | } 28 | } 29 | 30 | .username-button[disabled] { 31 | background: #c7c7c7; 32 | font-weight: 600; 33 | border: none; 34 | &::after { 35 | border: none; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/EList/EItem.scss: -------------------------------------------------------------------------------- 1 | .ele-eitem { 2 | background: $white-color; 3 | font-size: $font-size-32; 4 | padding-left: 20px; 5 | &:last-child { 6 | .ele-eitem-main { 7 | border-bottom: none; 8 | } 9 | } 10 | .ele-eitem-main { 11 | border-bottom: 1px solid #f5f5f5; 12 | display: flex; 13 | align-items: center; 14 | .ele-eitem-content-img { 15 | margin-right: 10px; 16 | width: 60px; 17 | height: 60px; 18 | } 19 | .ele-eitem-content { 20 | display: flex; 21 | align-items: center; 22 | flex: 1; 23 | padding-right: 20px; 24 | .ele-eitem-content-title { 25 | flex: 1; 26 | padding: 25px 0; 27 | } 28 | .ele-eitem-content-icon { 29 | margin-left: 10px; 30 | } 31 | .ele-eitem-extra { 32 | color: $color-666; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/msite/components/Advertising/Advertising.tsx: -------------------------------------------------------------------------------- 1 | // 首页广告位 2 | import React, { FC } from 'react' 3 | import { View, Image, Navigator } from '@tarojs/components' 4 | 5 | import './Advertising.scss' 6 | 7 | interface AdvertisingProps { 8 | title: string 9 | detail: string 10 | img: string 11 | url: string 12 | } 13 | 14 | const Advertising: FC = (props) => { 15 | const { title, detail, img, url } = props 16 | 17 | return ( 18 | 19 | 20 | {title} 21 | {detail} 22 | 立即抢购> 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | export default Advertising 32 | -------------------------------------------------------------------------------- /src/pages/profile/components/UserHead/UserHead.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Image, Text } from '@tarojs/components' 3 | import './UserHead.scss' 4 | 5 | const UserHead = ({ userInfo, onLink }) => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | {userInfo.userName} 14 | 15 | 16 | {userInfo.phone} 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default UserHead 26 | -------------------------------------------------------------------------------- /src/pages/city/components/FilterList/FilterList.tsx: -------------------------------------------------------------------------------- 1 | // 筛选城市 2 | import React from 'react' 3 | import classnames from 'classnames' 4 | import { ScrollView, View } from '@tarojs/components' 5 | import './FilterList.scss' 6 | 7 | const FilterList = ({ cityValue, resultCityList, onSelectCity }) => { 8 | return ( 9 | 10 | {resultCityList.map(city => { 11 | return ( 12 | onSelectCity(city)} 16 | > 17 | {city.name} 18 | 19 | ) 20 | })} 21 | 0 , 24 | })} 25 | > 26 | 无结果 27 | 28 | 29 | ) 30 | } 31 | export default FilterList 32 | -------------------------------------------------------------------------------- /src/pages/profile/address/search/index.scss: -------------------------------------------------------------------------------- 1 | .search { 2 | .search-main { 3 | height: calc(100vh - 85px); 4 | .search-top { 5 | border: 1px #eaeaea solid; 6 | background: #f5f5f5; 7 | display: flex; 8 | align-items: center; 9 | padding: 5px 0; 10 | margin: 20px 20px 0 20px; 11 | .icon { 12 | font-size: 30px; 13 | padding-left: 10px; 14 | color: #757575; 15 | } 16 | 17 | .input { 18 | flex: 1; 19 | margin-left: 10px; 20 | } 21 | } 22 | 23 | .search-list { 24 | margin: 10px 20px; 25 | .search-item { 26 | border-bottom: 1px #f0f0f0 solid; 27 | padding: 15px 0; 28 | .title { 29 | color: #333; 30 | font-size: 32px; 31 | } 32 | 33 | .detail { 34 | color: #a7a7a7; 35 | font-size: 26px; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/address/components/AtAddress/AtAddress.scss: -------------------------------------------------------------------------------- 1 | // 当前地址 2 | .ataddress { 3 | .ataddress-title { 4 | padding: 20px; 5 | color: $color-666; 6 | font-size:$font-size-26; 7 | } 8 | 9 | .ataddress-main { 10 | background: #fff; 11 | display: flex; 12 | justify-content: space-between; 13 | align-items: center; 14 | height: 85px; 15 | padding: 0 20px; 16 | > .text { 17 | color: #333; 18 | font-weight: 600; 19 | overflow: hidden; 20 | text-overflow: ellipsis; 21 | white-space: nowrap; 22 | font-size: $font-size-30; 23 | } 24 | 25 | .location { 26 | display: flex; 27 | align-items: center; 28 | .location-icon { 29 | .icon { 30 | color: #008fff; 31 | font-size: 35px; 32 | } 33 | } 34 | 35 | > .text { 36 | color: #008fff; 37 | width: 120px; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pages/address/components/AtAddress/AtAddress.tsx: -------------------------------------------------------------------------------- 1 | // 当前地址 2 | import React, { FC } from 'react' 3 | import { View, Text } from '@tarojs/components' 4 | 5 | import './AtAddress.scss' 6 | 7 | interface AtAddressProps { 8 | onLocationCity: () => void 9 | atAddress: string 10 | } 11 | const AtAddress: FC = (props) => { 12 | const { onLocationCity, atAddress } = props 13 | return ( 14 | 15 | 当前地址 16 | 17 | {atAddress} 18 | 19 | 20 | 21 | 22 | 重新定位 23 | 24 | 25 | 26 | ) 27 | } 28 | 29 | export default AtAddress 30 | -------------------------------------------------------------------------------- /src/pages/address/components/Profile/Profile.scss: -------------------------------------------------------------------------------- 1 | // 收货地址 2 | .profile { 3 | .profile-title { 4 | padding: 20px; 5 | color: $color-666; 6 | font-size: $font-size-26; 7 | } 8 | .profile-main { 9 | background: #fff; 10 | padding: 0 20px; 11 | .profile-item { 12 | border-bottom: 1px #eeeeee solid; 13 | padding: 20px 0; 14 | .item-top { 15 | display: flex; 16 | align-items: center; 17 | .ming { 18 | color: #333; 19 | font-weight: 600; 20 | font-size: $font-size-30; 21 | } 22 | .sex { 23 | padding: 0 10px; 24 | font-size: 24px; 25 | color: $color-666; 26 | } 27 | .iphone { 28 | font-size: 24px; 29 | color: $color-666; 30 | } 31 | } 32 | .item-address{ 33 | padding-top: 10px; 34 | font-size: $font-size-24; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/address/components/ResultItem/ResultItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | import { Address } from '../../../../api/interface' 4 | 5 | import './ResultItem.scss' 6 | 7 | interface ResultItemProps { 8 | onSaveAddress: (detail: Address) => void 9 | detail: any 10 | } 11 | 12 | const ResultItem: FC = (props) => { 13 | const { onSaveAddress, detail } = props 14 | 15 | return ( 16 | onSaveAddress(detail)}> 17 | 18 | {detail.name} 19 | 20 | {detail.distance} 21 | 22 | 23 | {detail.address} 24 | 25 | ) 26 | } 27 | 28 | export default ResultItem 29 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux' 2 | import thunkMiddleware from 'redux-thunk' 3 | import rootReducer from './reducers' 4 | 5 | const composeEnhancers = 6 | typeof window === 'object' && 7 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? 8 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ 9 | // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize... 10 | }) : compose 11 | 12 | const middlewares = [ 13 | thunkMiddleware 14 | ] 15 | 16 | if (process.env.NODE_ENV === 'development' && process.env.TARO_ENV !== 'quickapp') { 17 | middlewares.push(require('redux-logger').createLogger()) 18 | } 19 | 20 | const enhancer = composeEnhancers( 21 | applyMiddleware(...middlewares), 22 | // other store enhancers if any 23 | ) 24 | 25 | export default function configStore () { 26 | const store = createStore(rootReducer, enhancer) 27 | return store 28 | } 29 | -------------------------------------------------------------------------------- /src/app.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | pages: [ 3 | 'pages/msite/index', 4 | 'pages/login/index', 5 | 'pages/register/index', 6 | 'pages/profile/index', 7 | 'pages/profile/info/index', 8 | 'pages/profile/info/username/index', 9 | 'pages/profile/info/password/index', 10 | 'pages/profile/address/index', 11 | 'pages/profile/address/add/index', 12 | 'pages/profile/address/edit/index', 13 | 'pages/profile/address/search/index', 14 | 'pages/discover/index', 15 | 'pages/order/index', 16 | 'pages/order/detail/index', 17 | 'pages/address/index', 18 | 'pages/city/index', 19 | 'pages/search/index', 20 | 'pages/food/index', 21 | 'pages/shop/index', 22 | 'pages/clearing/index', 23 | 'pages/test/index', 24 | ], 25 | window: { 26 | backgroundTextStyle: 'light', 27 | navigationBarBackgroundColor: '#fff', 28 | navigationBarTitleText: 'WeChat', 29 | navigationBarTextStyle: 'black', 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/msite_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ETransition/ETransition.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | import { CSSTransition } from 'react-transition-group' 4 | import { CSSTransitionProps } from 'react-transition-group/CSSTransition' 5 | 6 | import './ETransition.scss' 7 | 8 | type AnimationName = 'ele-top' | 'ele-bottom' | 'ele-left' | 'ele-right' 9 | 10 | type ETransitionProps = CSSTransitionProps & { 11 | animation?: AnimationName 12 | wrapper?: boolean 13 | } 14 | 15 | const ETransition: FC = (props) => { 16 | const { classNames, animation, wrapper, children, ...restProps } = props 17 | 18 | return ( 19 | 23 | {wrapper ? {children} : children} 24 | 25 | ) 26 | } 27 | ETransition.defaultProps = { 28 | unmountOnExit: true, 29 | appear: true, 30 | } 31 | export default ETransition 32 | -------------------------------------------------------------------------------- /src/components/EButton/EButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, Fragment } from 'react' 2 | import { Text, Button } from '@tarojs/components' 3 | import { ButtonProps } from '@tarojs/components/types/Button' 4 | import classnames from 'classnames' 5 | 6 | import './EButton.scss' 7 | 8 | type EButtonType = 'default' | 'green' | 'blue' | 'href' 9 | 10 | interface EButtonProps extends ButtonProps { 11 | className?: string 12 | btnType: EButtonType 13 | children: React.ReactNode 14 | loading?: boolean 15 | href?: string 16 | } 17 | 18 | const EButton: FC = (props) => { 19 | const { className, btnType, loading, children, ...restProps } = props 20 | 21 | const classes = classnames('ele-ebutton', className, { 22 | [`ele-ebutton-${btnType}`]: btnType, 23 | }) 24 | 25 | return ( 26 | 30 | ) 31 | } 32 | 33 | export default EButton 34 | -------------------------------------------------------------------------------- /src/components/ShopList/ShopList.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactElement } from 'react' 2 | import { View } from '@tarojs/components' 3 | import ShopItem from '../ShopItem/ShopItem' 4 | import ELoading from '../ELoading/ELoading' 5 | 6 | import { IShopList } from '../../api/interface' 7 | import './ShopList.scss' 8 | 9 | interface ShopListProps { 10 | shopListData: IShopList[] 11 | renderLoading: ReactElement | null 12 | } 13 | 14 | const ShopList: FC = (props) => { 15 | const { shopListData, renderLoading } = props 16 | 17 | if (!shopListData.length) { 18 | return 19 | } 20 | 21 | return ( 22 | 23 | {shopListData.map((shopdata) => { 24 | return ( 25 | 29 | ) 30 | })} 31 | {renderLoading} 32 | 33 | ) 34 | } 35 | 36 | export default ShopList 37 | -------------------------------------------------------------------------------- /src/pages/profile/info/password/components/Input/MyInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Input } from '@tarojs/components' 3 | import { InputProps } from '@tarojs/components/types/Input' 4 | 5 | import classnames from 'classnames' 6 | import './MyInput.scss' 7 | 8 | interface MyInputProps extends Omit { 9 | tip: string | null 10 | onInput: (value: string) => void 11 | } 12 | 13 | const MyInput: FC = (props) => { 14 | const { placeholder, type, tip, onInput, value } = props 15 | return ( 16 | 17 | onInput(e.detail.value)} 26 | /> 27 | {tip && {tip}} 28 | 29 | ) 30 | } 31 | 32 | export default MyInput 33 | -------------------------------------------------------------------------------- /src/pages/discover/components/Prize/Prize.scss: -------------------------------------------------------------------------------- 1 | .prize { 2 | display: flex; 3 | flex-direction: column; 4 | position: relative; 5 | width: 220px; 6 | margin: 10px 15px; 7 | text-align: center; 8 | .tab { 9 | position: absolute; 10 | z-index: 1; 11 | padding: 0 8px; 12 | background: #413d3c; 13 | color: #fff; 14 | font-size: 24px; 15 | } 16 | 17 | .prize-img { 18 | width: 220px; 19 | height: 220px; 20 | border: 1px #f8f8f8 solid; 21 | } 22 | 23 | .prize-head { 24 | .title { 25 | overflow: hidden; 26 | font-size: 26px; 27 | white-space: nowrap; 28 | text-overflow: ellipsis; 29 | } 30 | 31 | .price { 32 | display: flex; 33 | align-items: center; 34 | .points { 35 | color: #ff5339; 36 | font-size: 26; 37 | } 38 | 39 | .original { 40 | padding-left: 10px; 41 | color: #aaa; 42 | font-size: 22px; 43 | text-decoration: line-through; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/pages/msite/components/NavSwipeItem/NavSwipeItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Navigator, Image } from '@tarojs/components' 3 | import imgUrl from '../../../../utils/imgUrl' 4 | import { INavSwiper } from '../../../../api/interface' 5 | import './NavSwipeItem.scss' 6 | 7 | interface ItemProps { 8 | navItem: INavSwiper 9 | } 10 | 11 | const NavSwipeItem: FC = (props) => { 12 | const { navItem } = props 13 | return ( 14 | 15 | 18 | 19 | 24 | 25 | {navItem.name} 26 | 27 | 28 | ) 29 | } 30 | 31 | export default NavSwipeItem 32 | -------------------------------------------------------------------------------- /src/pages/msite/components/MsiteNavBar/MsiteNavBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Navigator } from '@tarojs/components' 3 | import { useSelector } from 'react-redux' 4 | import NavBar from '../../../../components/NavBar/NavBar' 5 | import EIcon from '../../../../components/EIcon/EIcon' 6 | import { Reducers } from '../../../../redux/interface' 7 | 8 | import './MsiteNavBar.scss' 9 | 10 | const MsiteNavBar = () => { 11 | const { currentAddress } = useSelector((state: Reducers) => state) 12 | return ( 13 | 14 | {/* 图标 */} 15 | 16 | {/* 地址信息 */} 17 | 22 | {currentAddress.address || '手动选择收货地址'} 23 | 24 | {/* 图标 */} 25 | 26 | 27 | ) 28 | } 29 | 30 | export default MsiteNavBar 31 | -------------------------------------------------------------------------------- /src/pages/profile/address/components/AddressRow/AddressRow.scss: -------------------------------------------------------------------------------- 1 | .addressrow { 2 | display: flex; 3 | align-items: center; 4 | padding: 20px 0; 5 | border-bottom: 1px #f0f0f0 solid; 6 | background: #fff; 7 | color: #6f6f6f; 8 | .addressrow-icon { 9 | padding-left: 20px; 10 | color: #4cd964; 11 | font-size: 60px; 12 | } 13 | .addressrow-left { 14 | flex: 1; 15 | padding-left: 20px; 16 | .myinfo { 17 | display: flex; 18 | align-items: center; 19 | .myname { 20 | color: #333; 21 | font-size: 34px; 22 | font-weight: 600; 23 | } 24 | .mysex { 25 | margin-left: 10px; 26 | } 27 | .myphone { 28 | margin-left: 20px; 29 | font-size: 32px; 30 | } 31 | } 32 | .myaddress { 33 | } 34 | } 35 | 36 | .addressrow-right { 37 | display: flex; 38 | align-items: center; 39 | .addressrow-right-icon { 40 | padding: 0 20px; 41 | .icon { 42 | font-size: 34px; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/pages/clearing/components/Distribution/Distribution.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './Distribution.scss' 5 | 6 | const Distribution = () => { 7 | const getTime = useCallback(() => { 8 | const timeNow = Date.now() + 30 * 60 * 1000 9 | const time = new Date(timeNow) 10 | const hours = time.getHours() 11 | const minutes = 12 | Number(time.getMinutes()) < 10 13 | ? '0' + time.getMinutes() 14 | : time.getMinutes() 15 | 16 | return hours + ':' + minutes 17 | }, []) 18 | 19 | return ( 20 | 21 | 22 | 送达时间 23 | 尽快送达({getTime()}送达) 24 | 25 | 26 | 支付方式 27 | 在线支付 28 | 29 | 30 | ) 31 | } 32 | 33 | export default Distribution 34 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/NoDataTip/NoDataTip.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | import EButton from '../EButton/EButton' 5 | 6 | import './NoDataTip.scss' 7 | 8 | export interface NoDataTipProps { 9 | img: string 10 | title: string 11 | info?: string 12 | btnContent?: string 13 | onButtonClick?: () => void 14 | className?: string 15 | } 16 | 17 | const NoDataTip: FC = (props) => { 18 | const { img, title, info, btnContent, onButtonClick, className } = props 19 | const classes = classnames('ele-nodatatipo', className) 20 | return ( 21 | 22 | 23 | {title} 24 | {info && {info}} 25 | {btnContent && ( 26 | 27 | {btnContent} 28 | 29 | )} 30 | 31 | ) 32 | } 33 | 34 | export default NoDataTip 35 | -------------------------------------------------------------------------------- /src/assets/styles/_mixin.scss: -------------------------------------------------------------------------------- 1 | // @mixin border($direction: 'top', $color, $px: '1px', $solid: 'solid') { 2 | // border-#{$direction}: $px $color $solid; 3 | // } 4 | 5 | @mixin flex() { 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | } 10 | 11 | @mixin ele-animation( 12 | $direction: 'top', 13 | $scaleStart: scaleY(0), 14 | $scaleEnd: scaleY(1), 15 | $origin: center top 16 | ) { 17 | .ele-#{$direction}-enter { 18 | opacity: 0; 19 | transform: $scaleStart; 20 | } 21 | .ele-#{$direction}-enter-active { 22 | opacity: 1; 23 | transform: $scaleEnd; 24 | transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms, 25 | opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms; 26 | transform-origin: $origin; 27 | } 28 | .ele-#{$direction}-exit { 29 | opacity: 1; 30 | } 31 | .ele-#{$direction}-exit-active { 32 | opacity: 0; 33 | transform: $scaleStart; 34 | transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms, 35 | opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms; 36 | transform-origin: $origin; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/msite/components/Svip/Svip.scss: -------------------------------------------------------------------------------- 1 | .svip { 2 | display: flex; 3 | justify-content: space-between; 4 | padding: 20px; 5 | margin: 10px 20px 0 20px; 6 | background-image: $svip-background-image; 7 | color: #644f1b; 8 | border-radius: 6px; 9 | .svip-left { 10 | display: flex; 11 | align-items: center; 12 | .svip-icon { 13 | margin-right: 10px; 14 | width: 33px; 15 | height: 33px; 16 | .image { 17 | width: 100%; 18 | height: 100%; 19 | } 20 | } 21 | .svip-title { 22 | font-weight: 600; 23 | } 24 | .svip-tip { 25 | font-size: 22px; 26 | &::before { 27 | content: '\B7'; 28 | margin: 0 10px; 29 | } 30 | } 31 | } 32 | 33 | .svip-right { 34 | font-size: 22px; 35 | display: flex; 36 | align-items: center; 37 | &::after { 38 | display: block; 39 | content: ''; 40 | width: 5px; 41 | height: 5px; 42 | border-right: 2px solid #8c632b; 43 | border-bottom: 2px solid #8c632b; 44 | transform: rotate(-45deg); 45 | margin-left: 10px; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/FooterNav/FooterNav.scss: -------------------------------------------------------------------------------- 1 | .footerbar { 2 | height: 100px; 3 | .footerbar-mian { 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | position: fixed; 8 | z-index: 3; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | height: 100px; 13 | background: #fff; 14 | color: $color-999; 15 | box-shadow: 0 -0.3px 0.6vw rgba(0, 0, 0, 0.1); 16 | .footer-item { 17 | display: flex; 18 | align-items: center; 19 | flex: 1; 20 | flex-direction: column; 21 | justify-content: center; 22 | .item-icon { 23 | width: 36px; 24 | height: 36px; 25 | .taro-img { 26 | display: block; 27 | width: auto; 28 | height: auto; 29 | } 30 | .item-icon-img { 31 | width: 100%; 32 | height: 100%; 33 | border: none; 34 | } 35 | } 36 | 37 | .item-name { 38 | font-size: 20px; 39 | padding-top: 5px; 40 | } 41 | } 42 | .active { 43 | .item-name { 44 | color: $blue-color; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/pages/city/components/Search/Search.tsx: -------------------------------------------------------------------------------- 1 | // 搜索城市input 2 | import React, { FC } from 'react' 3 | import { View, Input, Text } from '@tarojs/components' 4 | import { InputProps } from '@tarojs/components/types/Input' 5 | import { BaseEventOrig } from '@tarojs/components/types/common' 6 | import './Search.scss' 7 | 8 | interface SearchProps { 9 | cityValue: string 10 | onInput: (e: BaseEventOrig) => void 11 | } 12 | 13 | const Search: FC = (props) => { 14 | const { cityValue, onInput } = props 15 | 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | export default Search 36 | -------------------------------------------------------------------------------- /src/components/ShopButton/ShopButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, Fragment } from 'react' 2 | import { View } from '@tarojs/components' 3 | 4 | import './ShopButton.scss' 5 | 6 | interface ShopButtonProps { 7 | onUpdateCart 8 | good 9 | count 10 | } 11 | 12 | const ShopButton: FC = (props) => { 13 | const { onUpdateCart, good, count } = props 14 | return ( 15 | 16 | {count > 0 && ( 17 | 18 | onUpdateCart(good, 'dec')} 21 | > 22 | 23 | 24 | 25 | {count} 26 | 27 | 28 | )} 29 | onUpdateCart(good, 'add')} 32 | > 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | ShopButton.defaultProps = { 40 | count: 0, 41 | } 42 | 43 | export default ShopButton 44 | -------------------------------------------------------------------------------- /src/pages/shop/components/LeftBar/LeftBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | import imgUrl from '../../../../utils/imgUrl' 5 | 6 | import './LeftBar.scss' 7 | 8 | interface LeftBarProps { 9 | good 10 | isActive 11 | onActive 12 | classCount 13 | } 14 | 15 | const LeftBar: FC = (props) => { 16 | const { good, isActive, onActive, classCount } = props 17 | const icon = useMemo(() => { 18 | if (good.icon_url) { 19 | return ( 20 | 24 | ) 25 | } 26 | }, [good, isActive]) 27 | return ( 28 | onActive(good)} 33 | > 34 | {icon} 35 | {good.name} 36 | {classCount > 0 && {classCount}} 37 | 38 | ) 39 | } 40 | 41 | export default LeftBar 42 | -------------------------------------------------------------------------------- /src/pages/profile/components/Money/Money.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | 4 | import './Money.scss' 5 | 6 | type MoneyList = { 7 | id: number 8 | img: string 9 | title: string 10 | } 11 | 12 | interface MoneyProps { 13 | moneyList: MoneyList[] 14 | onClick: () => void 15 | } 16 | 17 | const Money: FC = (props) => { 18 | const { moneyList, onClick } = props 19 | return ( 20 | 21 | {/* 22 | 23 | 24 | 25 | 红包 26 | */} 27 | {moneyList.map((item) => { 28 | return ( 29 | 30 | 31 | 32 | 33 | 金币 34 | 35 | ) 36 | })} 37 | 38 | ) 39 | } 40 | 41 | export default Money 42 | -------------------------------------------------------------------------------- /src/pages/profile/components/UserHead/UserHead.scss: -------------------------------------------------------------------------------- 1 | .user { 2 | background-image: linear-gradient(90deg, #0af, #0085ff); 3 | .user-content { 4 | display: flex; 5 | justify-content: space-between; 6 | align-items: center; 7 | padding: 50px 30px; 8 | .user-left { 9 | width: 120px; 10 | height: 120px; 11 | .user-image { 12 | width: 100%; 13 | height: 100%; 14 | border-radius: 50%; 15 | } 16 | } 17 | 18 | > .icon { 19 | font-size: 30px; 20 | color: #fff; 21 | } 22 | 23 | .user-right { 24 | flex: 1; 25 | display: flex; 26 | height: 100px; 27 | flex-direction: column; 28 | justify-content: space-between; 29 | margin-left: 30px; 30 | .user-title { 31 | font-size: 40px; 32 | font-weight: 600; 33 | color: #fff; 34 | } 35 | 36 | .right-content { 37 | display: flex; 38 | align-items: center; 39 | .icon { 40 | color: #fff; 41 | font-size: 24px; 42 | } 43 | .user-text { 44 | color: #fff; 45 | font-size: 24px; 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/pages/search/components/SearchBar/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Input } from '@tarojs/components' 3 | import { InputProps } from '@tarojs/components/types/Input' 4 | import { BaseEventOrig } from '@tarojs/components/types/common' 5 | 6 | import './SearchBar.scss' 7 | 8 | interface SearcBarProps { 9 | onSearch: (keyword: string) => void 10 | onInput: (e: BaseEventOrig) => void 11 | keyword: string 12 | onClearKeyWord: () => void 13 | } 14 | const SearchBar: FC = (props) => { 15 | const { onSearch, onInput, keyword, onClearKeyWord } = props 16 | return ( 17 | 18 | 19 | onInput(e)} 24 | /> 25 | {keyword && ( 26 | 27 | )} 28 | onSearch(keyword)}> 29 | 搜索 30 | 31 | 32 | ) 33 | } 34 | 35 | export default SearchBar 36 | -------------------------------------------------------------------------------- /src/components/ELoading/ELoading.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | import setstylepx from '../../utils/setstylepx' 5 | import './ELoading.scss' 6 | 7 | interface ELoadingProps { 8 | height?: number | string 9 | className?: string 10 | move?: boolean 11 | title?: string 12 | icon?: boolean 13 | } 14 | 15 | const ELoading: FC = (props) => { 16 | const { height, className, move, title, icon } = props 17 | const classes = classnames('ele-isloading', className) 18 | 19 | const pHeight = () => { 20 | if (typeof height === 'string') { 21 | return height 22 | } else { 23 | return setstylepx(height) 24 | } 25 | } 26 | 27 | if (move) { 28 | return ( 29 | 30 | {icon && } 31 | {title} 32 | 33 | ) 34 | } else { 35 | return 36 | } 37 | } 38 | 39 | ELoading.defaultProps = { 40 | height: 200, 41 | title: '正在加载...', 42 | icon: true, 43 | } 44 | export default ELoading 45 | -------------------------------------------------------------------------------- /src/components/ELoading/ELoading.scss: -------------------------------------------------------------------------------- 1 | .ele-isloading { 2 | @include flex; 3 | height: 200px; 4 | &::after { 5 | content: ''; 6 | display: block; 7 | width: 40px; 8 | height: 40px; 9 | animation: isloading 0.5s infinite linear; 10 | border-left: 4px solid transparent; 11 | border-right: 4px solid transparent; 12 | border-bottom: 4px solid transparent; 13 | border-top: 4px solid #2395ff; 14 | border-radius: 100%; 15 | z-index: 1; 16 | } 17 | } 18 | 19 | .ele-move { 20 | @include flex; 21 | height: 50px; 22 | .ele-move-icon { 23 | padding-right: 10px; 24 | &::after { 25 | content: ''; 26 | display: block; 27 | width: 26px; 28 | height: 26px; 29 | animation: isloading 1s infinite linear; 30 | border-left: 4px #eeeeee solid; 31 | border-right: 4px #eeeeee solid; 32 | border-top: 4px #eeeeee solid; 33 | border-bottom: 4px #2395ff solid; 34 | border-radius: 50%; 35 | } 36 | } 37 | .ele-move-title { 38 | color: $color-666; 39 | font-size: $font-size-24; 40 | } 41 | } 42 | 43 | @keyframes isloading { 44 | 0% { 45 | transform: rotate(0deg); 46 | } 47 | 100% { 48 | transform: rotate(360deg); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/pages/profile/address/add/index.tsx: -------------------------------------------------------------------------------- 1 | // 添加地址 2 | import Taro from '@tarojs/taro' 3 | import React, { FC } from 'react' 4 | import { View } from '@tarojs/components' 5 | import { useSelector } from 'react-redux' 6 | import { H5 } from '../../../../config/base' 7 | import API from '../../../../api' 8 | import { Reducers, Address } from '../../../../redux/interface' 9 | import NavBar from '../../../../components/NavBar/NavBar' 10 | import UserAddress from '../../../../components/UserAddress/UserAddress' 11 | 12 | import './index.scss' 13 | 14 | const AddAddress: FC = () => { 15 | const userAddress = useSelector((state: Reducers) => state.userAddress) 16 | const onForm = async (form: Address) => { 17 | const { err, res } = await API.reqAddUserAddress(form) 18 | 19 | if (err) { 20 | console.log(err) 21 | return 22 | } 23 | 24 | if (res.code === 0) { 25 | Taro.navigateBack({ delta: 1 }) 26 | } else { 27 | console.log(res) 28 | Taro.showToast({ title: res.message, icon: 'none' }) 29 | } 30 | } 31 | return ( 32 | 33 | {H5 && } 34 | 35 | 36 | ) 37 | } 38 | 39 | export default AddAddress 40 | -------------------------------------------------------------------------------- /src/pages/address/components/Profile/Profile.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | import { Address } from 'src/api/interface' 4 | 5 | import './Profile.scss' 6 | 7 | interface ProfileProps { 8 | userAddressList: any[] 9 | onClick: (item: Address) => void 10 | } 11 | const Profile: FC = (props) => { 12 | const { userAddressList, onClick } = props 13 | return ( 14 | 15 | 收货地址 16 | 17 | {userAddressList.map((item) => { 18 | return ( 19 | onClick(item)} 23 | > 24 | 25 | {item.name} 26 | 27 | {item.sex === '1' ? '先生' : '女士'} 28 | 29 | {item.phone} 30 | 31 | {item.address} 32 | 33 | ) 34 | })} 35 | 36 | 37 | ) 38 | } 39 | 40 | export default Profile 41 | -------------------------------------------------------------------------------- /src/pages/address/components/Search/Search.scss: -------------------------------------------------------------------------------- 1 | .selectaddress-search { 2 | background: #fff; 3 | display: flex; 4 | align-items: center; 5 | height: 108px; 6 | .search-left { 7 | width: 128px; 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | margin-left: 20px; 12 | .search-left-text { 13 | flex: 1; 14 | align-items: center; 15 | text-align: center; 16 | font-size: 24px; 17 | color: #333; 18 | width: 80px; 19 | overflow: hidden; 20 | text-overflow: ellipsis; 21 | white-space: nowrap; 22 | } 23 | .search-left-icon { 24 | >.icon { 25 | color: $color-666 !important; 26 | } 27 | } 28 | } 29 | 30 | .search-right { 31 | flex: 1; 32 | display: flex; 33 | background: #f2f2f2; 34 | margin-right: 20px; 35 | align-items: center; 36 | margin-left: 20px; 37 | .search-right-icon { 38 | margin-left: 20px; 39 | .icon { 40 | font-size: 30px; 41 | } 42 | } 43 | .search-right-input { 44 | flex: 1; 45 | padding: 18px; 46 | } 47 | .search-right-close { 48 | width: 80px; 49 | display: flex; 50 | justify-content: center; 51 | .icon { 52 | font-size: 25px; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/search/components/Back/Back.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, FC } from 'react' 2 | import { View } from '@tarojs/components' 3 | import ELoading from '../../../../components/ELoading/ELoading' 4 | 5 | import './Back.scss' 6 | 7 | interface BackProps { 8 | title: string 9 | clear?: boolean 10 | dataList: any[] 11 | onTypeaHead: (value: any) => void 12 | onClearHistory?: () => void 13 | } 14 | const Back: FC = (props) => { 15 | const { title, clear, dataList, onTypeaHead, onClearHistory } = props 16 | 17 | if (!dataList.length) { 18 | return 19 | } 20 | 21 | return ( 22 | 23 | 24 | {title} 25 | {clear && ( 26 | 27 | )} 28 | 29 | 30 | {dataList.map((item, i) => { 31 | return ( 32 | onTypeaHead(item.word || item)} 36 | > 37 | {item.word || item} 38 | 39 | ) 40 | })} 41 | 42 | 43 | ) 44 | } 45 | 46 | export default memo(Back) 47 | -------------------------------------------------------------------------------- /src/assets/images/xx2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/shop/components/Recommend/Recommend.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image, Text } from '@tarojs/components' 3 | import imgUrl from '../../../../utils/imgUrl' 4 | import ShopButton from '../../../../components/ShopButton/ShopButton' 5 | 6 | import './Recommend.scss' 7 | 8 | interface RecommendProps { 9 | onUpdateCart 10 | recData 11 | count 12 | } 13 | const Recommend: FC = (props) => { 14 | const { onUpdateCart, recData, count } = props 15 | return ( 16 | 17 | 18 | 19 | {recData.name} 20 | 21 | 月售{recData.month_sales} 好评率{recData.satisfy_rate}% 22 | 23 | 24 | 25 | ¥ 26 | {recData.price} 27 | 28 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default Recommend 40 | -------------------------------------------------------------------------------- /src/pages/address/components/Result/Result.tsx: -------------------------------------------------------------------------------- 1 | // 搜索地址 2 | import React, { FC, useMemo, memo } from 'react' 3 | import classnames from 'classnames' 4 | import { View } from '@tarojs/components' 5 | import { Address } from '../../../../api/interface' 6 | import SelectAddressResultItem from '../ResultItem/ResultItem' 7 | import './Result.scss' 8 | 9 | interface ResultProps { 10 | detailList: Address[] 11 | onSaveAddress: (detail: Address) => void 12 | } 13 | 14 | const Result: FC = (props) => { 15 | const { detailList, onSaveAddress } = props 16 | 17 | const flag = useMemo(() => { 18 | if (detailList.length > 0) { 19 | return true 20 | } else { 21 | return false 22 | } 23 | }, [detailList]) 24 | 25 | return ( 26 | 31 | {detailList.map((detail) => ( 32 | 37 | ))} 38 | 39 | 40 | 找不到地址? 41 | 请尝试只输入小区、写字楼或学校名 42 | 详细地址(如门牌号)可稍后输入 43 | 44 | 45 | ) 46 | } 47 | 48 | export default memo(Result) 49 | -------------------------------------------------------------------------------- /src/pages/profile/address/edit/index.tsx: -------------------------------------------------------------------------------- 1 | // 编辑地址 2 | import Taro from '@tarojs/taro' 3 | import React, { FC } from 'react' 4 | import { View } from '@tarojs/components' 5 | import { useSelector } from 'react-redux' 6 | import { H5 } from '../../../../config/base' 7 | import API from '../../../../api' 8 | import { Reducers, Address } from '../../../../redux/interface' 9 | import NavBar from '../../../../components/NavBar/NavBar' 10 | import UserAddress from '../../../../components/UserAddress/UserAddress' 11 | 12 | import './index.scss' 13 | 14 | const ProfileAddressEdit: FC = () => { 15 | const userAddress = useSelector((state: Reducers) => state.userAddress) 16 | 17 | // 获取修改的数据 18 | const onForm = async (myAddress: Address) => { 19 | const { err, res } = await API.reqSetUserAddress(myAddress) 20 | 21 | if (err) { 22 | console.log(err) 23 | return 24 | } 25 | 26 | if (res.code === 0) { 27 | Taro.navigateTo({ url: '/pages/profile/address/index' }) 28 | } else { 29 | console.log(res) 30 | Taro.showToast({ title: res.message, icon: 'none' }) 31 | } 32 | } 33 | 34 | return ( 35 | 36 | {H5 && } 37 | 38 | 39 | ) 40 | } 41 | 42 | export default ProfileAddressEdit 43 | -------------------------------------------------------------------------------- /src/pages/msite/components/Tip/Tip.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Taro from '@tarojs/taro' 3 | import { useSelector } from 'react-redux' 4 | import NoDataTip from '../../../../components/NoDataTip/NoDataTip' 5 | import { Reducers } from '../../../../redux/interface' 6 | 7 | const Tip = () => { 8 | const { token, currentAddress } = useSelector((state: Reducers) => state) 9 | // 跳转到登录 10 | const handleToLogin = () => { 11 | Taro.reLaunch({ url: '/pages/login/index' }) 12 | } 13 | // 跳转到选择地址 14 | const handleSelectAddress = () => { 15 | Taro.reLaunch({ url: '/pages/address/index' }) 16 | } 17 | 18 | if (!token && currentAddress.latitude) { 19 | return ( 20 | 28 | ) 29 | } 30 | 31 | if (!currentAddress.latitude) { 32 | return ( 33 | 40 | ) 41 | } 42 | return null 43 | } 44 | 45 | export default Tip 46 | -------------------------------------------------------------------------------- /src/pages/login/index.scss: -------------------------------------------------------------------------------- 1 | .login { 2 | margin-top: 30px; 3 | .logo { 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | padding: 50px 0; 8 | .logo-content { 9 | width: 300px; 10 | height: 120px; 11 | .logo-image { 12 | width: 100%; 13 | height: 100%; 14 | } 15 | } 16 | } 17 | .form { 18 | margin: 0 50px; 19 | display: block; 20 | .input-row { 21 | margin-bottom: 20px; 22 | position: relative; 23 | height: 90px; 24 | display: flex; 25 | align-items: center; 26 | .input { 27 | flex: 1; 28 | border: 1px #eee solid; 29 | outline: none; 30 | padding: 20px 0 20px 20px; 31 | } 32 | .inputactive { 33 | border: 1px #0089dc solid; 34 | } 35 | } 36 | 37 | .tiptext { 38 | padding: 40px 0; 39 | color: #333; 40 | .text { 41 | color: #23a2ff; 42 | } 43 | } 44 | 45 | .submit { 46 | border: 0; 47 | box-shadow: none; 48 | background: #4cd96f; 49 | color: #fff; 50 | outline: none; 51 | &:active { 52 | background: #3cc95f; 53 | } 54 | } 55 | 56 | .register { 57 | display: block; 58 | text-align: center; 59 | color: #ccc; 60 | padding-top: 20px; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/ETabs/ETabs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | 5 | import './ETabs.scss' 6 | 7 | const ETabs = ({ tabs, children }) => { 8 | const [tabIndex, setTabIndex] = useState(0) 9 | 10 | const activeTabs = i => { 11 | setTabIndex(i) 12 | } 13 | 14 | return ( 15 | 16 | 17 | {tabs.map((tab, i) => ( 18 | activeTabs(i)} 22 | > 23 | 28 | {tab.title} 29 | 30 | 31 | ))} 32 | 33 | 34 | {children.map((item, i) => { 35 | return ( 36 | 42 | {item} 43 | 44 | ) 45 | })} 46 | 47 | 48 | ) 49 | } 50 | 51 | export default ETabs 52 | -------------------------------------------------------------------------------- /src/pages/search/components/SearchItem/SearchItem.scss: -------------------------------------------------------------------------------- 1 | .searchitem { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | .searchitem-left { 6 | padding-right: 20px; 7 | .searchitem-img { 8 | width: 48px; 9 | height: 48px; 10 | } 11 | } 12 | .searchitem-right { 13 | display: flex; 14 | align-items: center; 15 | flex: 1; 16 | justify-content: space-between; 17 | padding: 20px 0; 18 | border-bottom: 1px #f8f8f8 solid; 19 | .searchitem-content { 20 | .searchitem-head { 21 | display: flex; 22 | padding: 4px 0; 23 | .title { 24 | font-size: 30px; 25 | } 26 | .searchitem-bold { 27 | color: $color-999; 28 | font-size: 30px; 29 | } 30 | 31 | .tag { 32 | display: table; 33 | margin-left: 20px; 34 | padding: 4px; 35 | color: $white-color; 36 | font-size: 24px; 37 | line-height: 1.2; 38 | } 39 | } 40 | 41 | .searchitem-bus { 42 | display: inline-block; 43 | padding: 0 8px; 44 | border: 1px currentColor solid; 45 | color: #00e066; 46 | font-size: 24px; 47 | border-radius: 20px; 48 | } 49 | } 50 | 51 | .searchitem-rating { 52 | color: #999; 53 | font-size: 24px; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/FooterNav/images/msite_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/test/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, ScrollView } from '@tarojs/components' 3 | 4 | import './index.scss' 5 | 6 | const Test = () => { 7 | const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 8 | 9 | const [viewId, setViewId] = useState('') 10 | 11 | const handleViewId = (id) => { 12 | setViewId(`test${id}`) 13 | } 14 | 15 | return ( 16 | 17 | handleViewId(1)} 22 | > 23 | 1 24 | 25 | handleViewId(2)} 30 | > 31 | 2 32 | 33 | handleViewId(3)} 38 | > 39 | 3 40 | 41 | handleViewId(4)} 46 | > 47 | 4 48 | 49 | handleViewId(5)} 54 | > 55 | 5 56 | 57 | 58 | ) 59 | } 60 | 61 | export default Test 62 | -------------------------------------------------------------------------------- /src/pages/shop/components/ActivityModal/ActivityModal.scss: -------------------------------------------------------------------------------- 1 | .activitymodal { 2 | position: fixed; 3 | z-index: 8; 4 | top: 0; 5 | right: 0; 6 | bottom: 0; 7 | left: 0; 8 | .activitymodal-main { 9 | position: absolute; 10 | z-index: 2; 11 | bottom: 0; 12 | left: 0; 13 | right: 0; 14 | height: 60vw; 15 | padding: 30px; 16 | background: #fff; 17 | .activitymodal-title { 18 | color: #333; 19 | font-size: 34px; 20 | font-weight: 600; 21 | text-align: center; 22 | } 23 | 24 | .icon.icon-guanbi { 25 | position: absolute; 26 | top: 10px; 27 | right: 10px; 28 | color: #666; 29 | font-size: 50px; 30 | } 31 | .activitymodal-list { 32 | height: 50vw; 33 | .activitymodal-item { 34 | display: flex; 35 | padding: 10px 0; 36 | .activitymodal-item-left { 37 | display: inline-table; 38 | margin-right: 15px; 39 | padding: 0 10px; 40 | font-size: 24px; 41 | line-height: 1.2; 42 | } 43 | 44 | .activitymodal-item-right { 45 | flex: 1; 46 | color: #666; 47 | font-size: 24px; 48 | line-height: 1.2; 49 | } 50 | } 51 | } 52 | } 53 | .activitymodal-bg { 54 | position: absolute; 55 | z-index: 1; 56 | top: 0; 57 | right: 0; 58 | bottom: 0; 59 | left: 0; 60 | background: rgba(0, 0, 0, 0.5); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/assets/images/xx1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/msite/components/NavSwipe/NavSwipe.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState, useEffect } from 'react' 2 | import { View } from '@tarojs/components' 3 | import { useSelector } from 'react-redux' 4 | 5 | import API from '../../../../api' 6 | import { INavSwiper } from '../../../../api/interface' 7 | import { Reducers } from '../../../../redux/interface' 8 | 9 | import NavSwiperFramework from '../Framework/Framework' 10 | import NavSwiperItem from '../NavSwipeItem/NavSwipeItem' 11 | 12 | import './NavSwipe.scss' 13 | 14 | const NavSwiper: FC = () => { 15 | const { currentAddress } = useSelector((state: Reducers) => state) 16 | const [navSwipeList, setNavSwipeList] = useState([]) 17 | 18 | useEffect(() => { 19 | const { latitude, longitude } = currentAddress 20 | // 导航 21 | if (latitude && longitude) { 22 | API.reqNavList({ latitude, longitude }).then((result) => { 23 | const { err, res } = result 24 | if (err) { 25 | console.log(err) 26 | return 27 | } 28 | if (res.code === 0) { 29 | setNavSwipeList(res.data) 30 | } 31 | }) 32 | } 33 | }, [currentAddress]) 34 | 35 | if (!navSwipeList.length) { 36 | // 骨架 37 | return 38 | } 39 | 40 | // 导航 41 | return ( 42 | 43 | {navSwipeList.map((navItem) => { 44 | return 45 | })} 46 | 47 | ) 48 | } 49 | 50 | export default NavSwiper 51 | -------------------------------------------------------------------------------- /src/pages/clearing/components/CartInfo/CartInfo.scss: -------------------------------------------------------------------------------- 1 | .cartinfo { 2 | margin: 0 20px; 3 | padding: 0 30px; 4 | background: #fff; 5 | .cartinfo-title { 6 | padding: 20px 0; 7 | border-bottom: 1px #f5f5f5 solid; 8 | color: #333; 9 | font-size: 34px; 10 | font-weight: 600; 11 | } 12 | 13 | .cartinfo-goods { 14 | .cartinfo-food { 15 | display: flex; 16 | align-items: center; 17 | padding: 20px 0; 18 | border-bottom: 1px #ededed solid; 19 | .cartinfo-img { 20 | width: 72px; 21 | height: 72px; 22 | padding-right: 10px; 23 | } 24 | 25 | .cartinfo-name { 26 | flex: 9; 27 | } 28 | 29 | .cartinfo-count { 30 | flex: 1; 31 | font-size: 24px; 32 | text-align: right; 33 | } 34 | 35 | .origin-price { 36 | min-width: 100px; 37 | color: #999; 38 | text-align: right; 39 | text-decoration: line-through; 40 | } 41 | 42 | .lowest_price { 43 | flex: 3; 44 | color: #ff4a07; 45 | text-align: right; 46 | } 47 | } 48 | .sundry { 49 | display: flex; 50 | align-items: center; 51 | justify-content: space-between; 52 | padding: 20px 0; 53 | border-bottom: 1px #f5f5f5 solid; 54 | .sundry-left { 55 | } 56 | .sundry-right { 57 | } 58 | } 59 | } 60 | 61 | .result { 62 | padding: 20px 0; 63 | text-align: right; 64 | .result-price { 65 | font-size: 44px; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/assets/images/cart2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/clearing/components/CartAddress/CartAddress.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | 4 | import './CartAddress.scss' 5 | 6 | interface CartAddressProps { 7 | useAddress 8 | onSelectAddress 9 | } 10 | 11 | const CartAddress: FC = (props) => { 12 | const { useAddress, onSelectAddress } = props 13 | // 收货地址 14 | const addressText = useMemo(() => { 15 | if (useAddress.id) { 16 | return useAddress.address 17 | } else { 18 | return '请选择地址' 19 | } 20 | }, [useAddress]) 21 | 22 | // 性别 23 | const getSex = useMemo(() => { 24 | const { sex } = useAddress 25 | switch (sex) { 26 | case 0: 27 | return '先生' 28 | case 1: 29 | return '女士' 30 | default: 31 | return null 32 | } 33 | }, [useAddress]) 34 | 35 | return ( 36 | 37 | 订单配送至 38 | 39 | 40 | 41 | {addressText} 42 | 43 | 44 | 45 | {useAddress.name} 46 | {getSex ? `(${getSex})` : null} 47 | {useAddress.phone} 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 | CartAddress.defaultProps = { 55 | useAddress: {}, 56 | } 57 | 58 | export default CartAddress 59 | -------------------------------------------------------------------------------- /src/assets/images/cart1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/shop/components/ActivityModal/ActivityModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, ScrollView, Text } from '@tarojs/components' 3 | 4 | import './ActivityModal.scss' 5 | 6 | interface ActivityModalProps { 7 | activities 8 | onActivityHide 9 | } 10 | 11 | const ActivityModal: FC = (props) => { 12 | const { activities, onActivityHide } = props 13 | return ( 14 | 15 | 16 | 优惠活动 17 | 18 | 19 | {activities.map((activitie) => { 20 | return ( 21 | 22 | 29 | {activitie.icon_name} 30 | 31 | 32 | {activitie.description} 33 | 34 | 35 | ) 36 | })} 37 | 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | export default ActivityModal 45 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "dist/", 3 | "projectname": "ele-ts-client", 4 | "description": "仿饿了么H5, 可编译跨端 H5 微信小程序", 5 | "appid": "wxbdfb622485c1051d", 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": false, 9 | "enhance": false, 10 | "postcss": false, 11 | "preloadBackgroundData": false, 12 | "minified": false, 13 | "newFeature": false, 14 | "coverView": true, 15 | "nodeModules": false, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": true, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": false, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "babelSetting": { 25 | "ignore": [], 26 | "disablePlugins": [], 27 | "outputPath": "" 28 | }, 29 | "useIsolateContext": true, 30 | "useCompilerModule": false, 31 | "userConfirmedUseCompilerModuleSwitch": false 32 | }, 33 | "compileType": "miniprogram", 34 | "simulatorType": "wechat", 35 | "simulatorPluginLibVersion": {}, 36 | "condition": { 37 | "search": { 38 | "current": -1, 39 | "list": [] 40 | }, 41 | "conversation": { 42 | "current": -1, 43 | "list": [] 44 | }, 45 | "plugin": { 46 | "current": -1, 47 | "list": [] 48 | }, 49 | "game": { 50 | "current": -1, 51 | "list": [] 52 | }, 53 | "gamePlugin": { 54 | "current": -1, 55 | "list": [] 56 | }, 57 | "miniprogram": { 58 | "current": -1, 59 | "list": [ 60 | { 61 | "id": -1, 62 | "name": "我的", 63 | "pathName": "pages/profile/index", 64 | "scene": null 65 | } 66 | ] 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/pages/register/index.scss: -------------------------------------------------------------------------------- 1 | .register { 2 | margin-top: 30px; 3 | .logo { 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | padding: 50px 0; 8 | .logo-content { 9 | width: 300px; 10 | height: 120px; 11 | .logo-image { 12 | width: 100%; 13 | height: 100%; 14 | } 15 | } 16 | } 17 | .form { 18 | margin: 0 50px; 19 | display: block; 20 | .input-row { 21 | margin-bottom: 20px; 22 | position: relative; 23 | display: flex; 24 | .input { 25 | flex: 1; 26 | border: 1px #eee solid; 27 | padding: 25px 0; 28 | outline: none; 29 | padding-left: 20px; 30 | &::placeholder { 31 | color: #ccc; 32 | } 33 | } 34 | .inputactive { 35 | border: 1px #ccc solid; 36 | } 37 | .pass { 38 | border: 1px #4cd86f solid; 39 | } 40 | .placeholder { 41 | color: #ccc; 42 | } 43 | .error { 44 | border: 1px #ff7979 solid; 45 | } 46 | } 47 | 48 | .tiptext { 49 | padding: 40px 0; 50 | color: #333; 51 | .text { 52 | color: #23a2ff; 53 | } 54 | } 55 | 56 | .submit { 57 | border: 0; 58 | box-shadow: none; 59 | background: #008cff; 60 | color: #fff; 61 | outline: none; 62 | 63 | &:active { 64 | background: #3ba7ff; 65 | } 66 | } 67 | 68 | .login { 69 | display: block; 70 | text-align: center; 71 | color: #ccc; 72 | padding-top: 20px; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/components/EIcon/EIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { Text } from '@tarojs/components' 3 | import { StandardProps } from '@tarojs/components/types/common' 4 | import classnames from 'classnames' 5 | import setstylepx from '../../utils/setstylepx' 6 | 7 | import './EIcon.scss' 8 | 9 | type EIconType = 10 | | 'jiazailoading' 11 | | 'daohangshouye' 12 | | 'jianshao' 13 | | 'zengjia' 14 | | 'tianjia' 15 | | 'bianji' 16 | | 'miaozhun' 17 | | 'icon-test' 18 | | 'dianpu1' 19 | | 'jiantou-copy-copy' 20 | | 'jiantou-copy-copy-copy' 21 | | 'shouji' 22 | | 'iconset0143' 23 | | 'jiantou1' 24 | | 'zan' 25 | | 'success_fill' 26 | | 'ai-cart' 27 | | 'lajitong' 28 | | 'guanbi' 29 | | 'close' 30 | | 'huangguan' 31 | | 'jiantou' 32 | | 'jiazai' 33 | | 'shangjiantou' 34 | | 'safetycertificate-f' 35 | | 'xiazai9' 36 | | 'duihao' 37 | | 'funnel' 38 | | 'daohangdizhi' 39 | | 'xiajiantou' 40 | | 'fanhui' 41 | | 'sousuo' 42 | | 'loading' 43 | 44 | 45 | export interface EIconProps extends StandardProps { 46 | black?: boolean 47 | type: EIconType 48 | size?: number 49 | color?: string 50 | } 51 | 52 | const EIcon: FC = (props) => { 53 | const { black, type, size, className, color, ...restProps } = props 54 | const classes = classnames('icon', `icon-${type}`, className) 55 | const styles = { 56 | display: black ? 'block' : '', 57 | fontSize: setstylepx(size), 58 | color, 59 | } 60 | return 61 | } 62 | 63 | EIcon.defaultProps = { 64 | black: false, 65 | size: 38, 66 | } 67 | export default EIcon 68 | -------------------------------------------------------------------------------- /src/pages/shop/components/ShopItem/ShopItem.scss: -------------------------------------------------------------------------------- 1 | .shopitem { 2 | overflow: hidden; 3 | display: flex; 4 | padding-bottom: 30px; 5 | .shopitem-left { 6 | width: 190px; 7 | height: 190px; 8 | margin-right: 20px; 9 | 10 | .shopitem-left-img { 11 | width: 190px; 12 | height: 190px; 13 | } 14 | } 15 | 16 | .shopitem-right { 17 | overflow: hidden; 18 | display: flex; 19 | flex: 1; 20 | flex-direction: column; 21 | justify-content: space-between; 22 | height: 190px; 23 | .shopitem-title { 24 | overflow: hidden; 25 | color: #333; 26 | font-size: 30px; 27 | font-weight: 600; 28 | white-space: nowrap; 29 | text-overflow: ellipsis; 30 | } 31 | 32 | .shopitem-intro { 33 | color: #999; 34 | font-size: 24px; 35 | } 36 | 37 | .shopitem-pingfen { 38 | color: #999; 39 | font-size: 24px; 40 | } 41 | .shopitem-num { 42 | display: flex; 43 | align-items: center; 44 | justify-content: space-between; 45 | .shopitem-num-price { 46 | color: #ff5339; 47 | font-size: 30px; 48 | display: flex; 49 | align-items: center; 50 | &::before { 51 | content: '\A5'; 52 | display: inline-block; 53 | font-size: 24px; 54 | } 55 | .shopitem-num-price-left { 56 | color: #ff5339; 57 | font-size: 30px; 58 | } 59 | .shopitem-num-price-right { 60 | padding-left: 14px; 61 | color: #999; 62 | font-size: 24px; 63 | text-decoration: line-through; 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/assets/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | // body 2 | $body-line-height: 1.2 !default; 3 | $body-font-size: 28px !default; 4 | $body-font-famify: 'Helvetica Neue', Tahoma, Arial, PingFangSC-Regular, 5 | 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif !default; 6 | 7 | // 字体颜色 8 | $white-color: #ffffff !default; 9 | $black-color: #000000 !default; 10 | $orange-color: #ff4b33 !default; 11 | $blue-color: #2395ff !default; 12 | $green-color: #38ca73 !default; 13 | $color-333: #333333 !default; 14 | $color-666: #666666 !default; 15 | $color-777: #777777 !default; 16 | $color-999: #999999 !default; 17 | $color-ddd:#dddddd !default; 18 | 19 | // 字体大小 20 | $font-size-41: 41px !default; 21 | $font-size-38: 38px !default; 22 | $font-size-36: 36px !default; 23 | $font-size-34: 34px !default; 24 | $font-size-32: 32px !default; 25 | $font-size-30: 30px !default; 26 | $font-size-28: 28px !default; 27 | $font-size-26: 26px !default; 28 | $font-size-24: 24px !default; 29 | $font-size-22: 22px !default; 30 | $font-size-20: 20px !default; 31 | 32 | // 加粗 33 | $font-weight: 700 !default; 34 | 35 | // 渐变背景 36 | $defualt-background-image: linear-gradient(90deg, #0af, #0085ff) !default; 37 | $tag-background-image: linear-gradient(45deg, #0085ff, #0af) !default; 38 | $svip-background-image: linear-gradient(90deg, #ffefc4, #f3dda0) !default; 39 | $brand-background-image: linear-gradient(-139deg, #fff100, #ffe339) !default; 40 | $count-background-image: linear-gradient(-90deg, #ff7416, #ff3c15 98%) !default; 41 | $bg-background-image: linear-gradient( 42 | 0deg, 43 | #f5f5f5, 44 | #f5f5f5 65%, 45 | hsla(0, 0%, 96%, 0.3) 75%, 46 | hsla(0, 0%, 96%, 0) 47 | ), 48 | linear-gradient(270deg, #0085ff, #0af) !default; 49 | 50 | $background-f5f5f5: #f5f5f5; 51 | -------------------------------------------------------------------------------- /src/pages/order/components/Ordercard/Ordercard.scss: -------------------------------------------------------------------------------- 1 | .ordercard { 2 | margin-bottom: 20px; 3 | padding: 20px; 4 | background: #fff; 5 | .ordercard-top { 6 | display: flex; 7 | .ordercard-top-left { 8 | margin-right: 20px; 9 | .ordercard-top-img { 10 | width: 64px; 11 | height: 64px; 12 | } 13 | } 14 | 15 | .ordercard-top-right { 16 | flex: 1; 17 | .ordercard-head { 18 | padding-bottom: 20px; 19 | border-bottom: 1px #f5f5f5 solid; 20 | .ordercard-head-top { 21 | display: flex; 22 | align-items: center; 23 | justify-content: space-between; 24 | .ordercard-head-top-title { 25 | font-size: 32px; 26 | } 27 | 28 | .ordercard-head-top-tip { 29 | color: #2395ff; 30 | } 31 | } 32 | 33 | .ordercard-head-time { 34 | padding-top: 10px; 35 | color: #999; 36 | font-size: 24px; 37 | line-height: 1.2; 38 | } 39 | } 40 | 41 | .ordercard-text { 42 | display: flex; 43 | justify-content: space-between; 44 | padding: 20px 0; 45 | border-bottom: 1px #f5f5f5 solid; 46 | 47 | .ordercard-text-title { 48 | color: #666; 49 | } 50 | 51 | .ordercard-text-price { 52 | color: #333; 53 | font-weight: 600; 54 | } 55 | } 56 | } 57 | } 58 | 59 | .ordercard-bottom { 60 | display: flex; 61 | justify-content: flex-end; 62 | margin-top: 20px; 63 | .ordercard-bottom-button { 64 | padding: 10px; 65 | border: 1px currentColor solid; 66 | color: #2395ff; 67 | line-height: 1.2; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/pages/search/components/SearchItem/SearchItem.tsx: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import React, { useMemo, memo, FC } from 'react' 3 | import { View, Image, Text, RichText } from '@tarojs/components' 4 | import imgUrl from '../../../../utils/imgUrl' 5 | 6 | import './SearchItem.scss' 7 | 8 | interface SearchItemProps { 9 | restaurant: any 10 | keyword: string 11 | } 12 | const SearchItem: FC = (props) => { 13 | const { restaurant, keyword } = props 14 | const shopName = useMemo(() => { 15 | return restaurant.name.replace( 16 | keyword, 17 | `${keyword}` 18 | ) 19 | }, [restaurant, keyword]) 20 | 21 | const goShop = () => { 22 | Taro.navigateTo({ url: '/pages/myshop/index' }) 23 | } 24 | 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | {restaurant.tags[0].name} 39 | 40 | 41 | 42 | 接受预定,{restaurant.next_business_time}开始配送 43 | 44 | 45 | 评价 {restaurant.rating} 46 | 47 | 48 | ) 49 | } 50 | 51 | export default memo(SearchItem) 52 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Taro from '@tarojs/taro' 3 | import { View, Navigator } from '@tarojs/components' 4 | import classnames from 'classnames' 5 | import EIcon, { EIconProps } from '../EIcon/EIcon' 6 | 7 | import './NavBar.scss' 8 | 9 | type onRightHref = () => void 10 | 11 | interface INavBarProps extends Omit { 12 | icon?: EIconProps['type'] 13 | onIconClick?: () => void 14 | title?: string 15 | rightText?: string 16 | rightHref?: string | onRightHref 17 | } 18 | 19 | const NavBar: React.FC = (props) => { 20 | const { 21 | icon, 22 | onIconClick, 23 | title, 24 | rightText, 25 | rightHref, 26 | className, 27 | children, 28 | ...restProps 29 | } = props 30 | const classes = classnames('navbar', className) 31 | 32 | const handleRightHref = () => { 33 | if (rightHref && rightText) { 34 | if (typeof rightHref === 'string') { 35 | return ( 36 | 37 | {rightText} 38 | 39 | ) 40 | } else { 41 | return ( 42 | 43 | {rightText} 44 | 45 | ) 46 | } 47 | } 48 | } 49 | 50 | return ( 51 | 52 | {icon && ( 53 | 54 | )} 55 | {title && {title}} 56 | {handleRightHref()} 57 | {children} 58 | 59 | ) 60 | } 61 | 62 | NavBar.defaultProps = { 63 | onIconClick: () => { 64 | Taro.navigateBack({ delta: -1 }) 65 | }, 66 | } 67 | export default NavBar 68 | -------------------------------------------------------------------------------- /src/pages/address/components/Search/Search.tsx: -------------------------------------------------------------------------------- 1 | // 选择城市,搜索地址 2 | import React, { FC } from 'react' 3 | import { View, Input, Text, Navigator } from '@tarojs/components' 4 | import { InputProps } from '@tarojs/components/types/Input' 5 | import { BaseEventOrig } from '@tarojs/components/types/common' 6 | import { Address } from '../../../../redux/interface' 7 | 8 | import './Search.scss' 9 | 10 | interface SearchProps { 11 | cityUrl: string 12 | currentAddress: Address 13 | searchValue: string 14 | onInput: (e: BaseEventOrig) => void 15 | onDelSearch: () => void 16 | } 17 | 18 | const Search: FC = (props) => { 19 | const { cityUrl, currentAddress, searchValue, onInput, onDelSearch } = props 20 | return ( 21 | 22 | {/* 选择城市 */} 23 | 24 | 25 | {currentAddress.city ? currentAddress.city : '选择城市'} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | onInput(e)} 40 | /> 41 | {searchValue && ( 42 | 43 | 44 | 45 | )} 46 | 47 | 48 | ) 49 | } 50 | 51 | export default Search 52 | -------------------------------------------------------------------------------- /src/pages/profile/info/username/index.tsx: -------------------------------------------------------------------------------- 1 | // 修改用户名 2 | import Taro, { FC } from '@tarojs/taro' 3 | import React, { useState } from 'react' 4 | import { View, Input, Button } from '@tarojs/components' 5 | import { InputProps } from '@tarojs/components/types/Input' 6 | import { BaseEventOrig } from '@tarojs/components/types/common' 7 | import API from '../../../../api' 8 | 9 | import './index.scss' 10 | 11 | const UserName: FC = () => { 12 | const [userName, setUserName] = useState('') 13 | const [flag, setFlag] = useState(true) 14 | 15 | // 修改用户名 16 | const handleSetUserName = (e: BaseEventOrig) => { 17 | const { value } = e.detail 18 | if (value.length >= 5 && value.length <= 24) { 19 | setUserName(value) 20 | setFlag(false) 21 | } else { 22 | setFlag(true) 23 | } 24 | } 25 | 26 | // 提交 27 | const handleSubmit = async () => { 28 | if (flag) { 29 | return 30 | } 31 | 32 | const { err, res } = await API.reqSetUserName({ userName }) 33 | 34 | if (err) { 35 | console.log(err) 36 | return 37 | } 38 | 39 | if (res.code === 0) { 40 | Taro.navigateBack({ delta: 1 }) 41 | } 42 | } 43 | 44 | return ( 45 | 46 | 47 | 53 | 修改用户名(5-24个字) 54 | 61 | 62 | 63 | ) 64 | } 65 | 66 | export default UserName 67 | -------------------------------------------------------------------------------- /src/pages/shop/components/ShopInfo/ShopInfo.scss: -------------------------------------------------------------------------------- 1 | .shopinfo { 2 | background: #f5f5f5; 3 | .peisong-img { 4 | width: 100%; 5 | } 6 | .shopinfo-bgcolor { 7 | color: #666; 8 | } 9 | .peisong-content { 10 | .peisong-title { 11 | padding: 10px; 12 | color: #333; 13 | font-weight: 600; 14 | } 15 | .peisong-info { 16 | overflow: hidden; 17 | display: -webkit-box; 18 | -webkit-box-orient: vertical; 19 | 20 | -webkit-line-clamp: 2; 21 | } 22 | } 23 | 24 | .serve { 25 | display: flex; 26 | line-height: 1.2; 27 | .icon-name { 28 | margin-right: 10px; 29 | padding: 0 6px; 30 | } 31 | .serve-descripton { 32 | } 33 | } 34 | .scene { 35 | display: flex; 36 | flex-wrap: wrap; 37 | .scene-item { 38 | position: relative; 39 | width: 150px; 40 | height: 150px; 41 | margin-right: 10px; 42 | .scene-img { 43 | width: 150px; 44 | height: 150px; 45 | } 46 | .scene-name { 47 | position: absolute; 48 | right: 0; 49 | bottom: 0; 50 | left: 0; 51 | background: rgba(0, 0, 0, .5); 52 | color: #fff; 53 | font-size: 24px; 54 | text-align: center; 55 | } 56 | } 57 | } 58 | .description { 59 | padding-bottom: 20px; 60 | border-bottom: 1px #eee solid; 61 | color: #666; 62 | } 63 | .info { 64 | display: flex; 65 | justify-content: space-between; 66 | padding: 20px 0; 67 | border-bottom: 1px #eee solid; 68 | .info-left { 69 | color: #333; 70 | font-weight: 600; 71 | } 72 | .info-right { 73 | color: #666; 74 | } 75 | .tel { 76 | color: #00a6ff; 77 | } 78 | } 79 | .qualification { 80 | color: #333; 81 | font-weight: 600; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/pages/shop/components/ShopItem/ShopItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image, Text } from '@tarojs/components' 3 | import imgUrl from '../../../../utils/imgUrl' 4 | import ShopButton from '../../../../components/ShopButton/ShopButton' 5 | 6 | import './ShopItem.scss' 7 | 8 | interface ShopItemProps { 9 | onUpdateCart 10 | food 11 | count 12 | } 13 | 14 | const ShopItem: FC = (props) => { 15 | const { onUpdateCart, food, count } = props 16 | return ( 17 | 18 | {food.image_path ? ( 19 | 20 | 25 | 26 | ) : null} 27 | 28 | {food.name} 29 | {food.materials} 30 | 31 | 月售{food.month_sales}份 好评率{food.satisfy_rate}% 32 | 33 | 34 | 35 | 36 | {food.price} 37 | 38 | 39 | {food.origin_price !== food.price && ( 40 | 41 | 42 | ¥{food.origin_price} 43 | 44 | 45 | )} 46 | 47 | 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | 55 | export default ShopItem 56 | -------------------------------------------------------------------------------- /src/components/EList/EItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | import classnames from 'classnames' 4 | import EIcon from '../EIcon/EIcon' 5 | 6 | import './EItem.scss' 7 | 8 | type Arrow = 'jiantou1' | 'none' 9 | export interface EItemProps { 10 | thumb?: string | React.ReactNode 11 | extra?: string | React.ReactNode 12 | arrow?: Arrow 13 | onClick?: () => void 14 | className?: string 15 | } 16 | 17 | const EItem: FC = (props) => { 18 | const { thumb, extra, arrow, onClick, className, children } = props 19 | const classes = classnames('ele-eitem', className) 20 | 21 | const isThumb = () => { 22 | if (typeof thumb === 'string') { 23 | return 24 | } else { 25 | return thumb 26 | } 27 | } 28 | 29 | const isExtra = () => { 30 | if (typeof extra === 'string') { 31 | return {extra} 32 | } else { 33 | return extra 34 | } 35 | } 36 | 37 | const isArrow = () => { 38 | if (arrow === 'none') { 39 | return false 40 | } else { 41 | return true 42 | } 43 | } 44 | 45 | return ( 46 | 47 | 48 | {isThumb && isThumb()} 49 | 50 | {children} 51 | {isExtra && isExtra()} 52 | {isArrow() && ( 53 | 59 | )} 60 | 61 | 62 | 63 | ) 64 | } 65 | EItem.defaultProps = { 66 | arrow: 'jiantou1', 67 | } 68 | export default EItem 69 | -------------------------------------------------------------------------------- /src/components/UserAddress/UserAddress.scss: -------------------------------------------------------------------------------- 1 | .useraddress { 2 | background: #f5f5f5; 3 | height: calc(100vh - 100px); 4 | color: #333; 5 | font-size: 30px; 6 | .useraddress-item { 7 | display: flex; 8 | align-items: center; 9 | background: #fff; 10 | padding-left: 20px; 11 | .item-left { 12 | width: 120px; 13 | font-weight: 600; 14 | } 15 | 16 | .item-right { 17 | flex: 1; 18 | margin-left: 20px; 19 | display: flex; 20 | align-items: center; 21 | justify-content: space-between; 22 | margin-right: 20px; 23 | .item-right-input { 24 | flex: 1; 25 | border-bottom: 1px #f0f0f0 solid; 26 | padding: 25px 0; 27 | } 28 | .item-right-text { 29 | flex: 1; 30 | padding: 25px 0; 31 | border-bottom: 1px #f0f0f0 solid; 32 | } 33 | .icon { 34 | font-size: 30px; 35 | color: #bbb; 36 | } 37 | .br { 38 | border: none; 39 | } 40 | } 41 | } 42 | .useraddress-sex { 43 | background: #fff; 44 | padding: 20px 0 0 160px; 45 | display: flex; 46 | 47 | .sex-item { 48 | border: 1px #ededed solid; 49 | padding: 10px 35px; 50 | border-radius: 10px; 51 | color: #787878; 52 | &:last-child { 53 | margin-left: 20px; 54 | } 55 | } 56 | .sex-active { 57 | background: #eef7ff; 58 | color: #2395ff; 59 | } 60 | } 61 | .address-submit { 62 | margin: 30px 20px 0 20px; 63 | .address-submit-button { 64 | background: #00d762; 65 | font-size: 34px; 66 | font-weight: 600; 67 | border: none; 68 | color: #fff; 69 | outline: none; 70 | border-radius: 4px; 71 | &:after { 72 | border: none; 73 | border-radius: 0; 74 | } 75 | &:active { 76 | background: #47cf84; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/components/EButton/EButton.scss: -------------------------------------------------------------------------------- 1 | $default-color: #56d176; 2 | $blue-color: #008cfe; 3 | taro-button-core + taro-button-core { 4 | margin-top: 0 !important; 5 | } 6 | .ele-ebutton { 7 | border: none; 8 | background: none; 9 | outline: none; 10 | font-size: 28px; 11 | border-radius: 6px; 12 | @include flex; 13 | &::after { 14 | border: none; 15 | } 16 | } 17 | .ele-ebutton-loading { 18 | padding-right: 10px; 19 | &::after { 20 | content: ''; 21 | display: block; 22 | width: 26px; 23 | height: 26px; 24 | animation: isloading 0.8s infinite linear; 25 | border-left: 2.5px transparent solid; 26 | border-right: 2.5px transparent solid; 27 | border-top: 2.5px transparent solid; 28 | border-bottom: 2.5px #f5f5f5 solid; 29 | border-radius: 50%; 30 | } 31 | } 32 | .ele-ebutton-default { 33 | border: 1px solid $blue-color; 34 | width: 240px; 35 | height: 80px; 36 | color: $blue-color; 37 | &:active { 38 | color: lighten($blue-color, 15%) !important; 39 | border: 1px solid lighten($blue-color, 15%) !important; 40 | background: none !important; 41 | } 42 | } 43 | 44 | .ele-ebutton-green { 45 | width: 240px; 46 | height: 80px; 47 | background: $default-color; 48 | color: $white-color; 49 | &:active { 50 | color: $white-color !important; 51 | background: lighten($default-color, 15%) !important; 52 | } 53 | } 54 | 55 | .ele-ebutton-blue { 56 | width: 240px; 57 | height: 80px; 58 | background: $blue-color; 59 | color: $white-color; 60 | &:active { 61 | color: $white-color !important; 62 | background: lighten($blue-color, 15%) !important; 63 | } 64 | } 65 | .ele-ebutton-href { 66 | color: $color-333; 67 | &:active { 68 | color: $color-666 !important; 69 | background: none !important; 70 | } 71 | } 72 | 73 | @keyframes isloading { 74 | 0% { 75 | transform: rotate(0deg); 76 | } 77 | 100% { 78 | transform: rotate(360deg); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/pages/clearing/components/CartInfo/CartInfo.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image, Text } from '@tarojs/components' 3 | import imgUrl from '../../../../utils/imgUrl' 4 | 5 | import './CartInfo.scss' 6 | 7 | interface CartInfoProps { 8 | cartInfo 9 | shopName 10 | } 11 | 12 | const CartInfo: FC = (props) => { 13 | const { cartInfo, shopName } = props 14 | return ( 15 | 16 | {shopName} 17 | 18 | {cartInfo.foods.map((food) => { 19 | return ( 20 | 21 | 22 | {food.name} 23 | ×{food.count} 24 | 25 | {Number(food.origin_price) !== food.lowest_price 26 | ? ' ¥' + food.origin_price * food.count 27 | : null} 28 | 29 | 30 | ¥{food.count * food.lowest_price} 31 | 32 | 33 | ) 34 | })} 35 | 36 | 包装费 37 | ¥{cartInfo.goodTotal} 38 | 39 | 40 | 配送费 41 | ¥{cartInfo.boxPrice} 42 | 43 | 44 | 45 | 小计¥ 46 | 47 | {cartInfo.totalPrice + cartInfo.goodTotal + cartInfo.boxPrice} 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 | export default CartInfo 55 | -------------------------------------------------------------------------------- /src/pages/city/components/List/List.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo } from 'react' 2 | import { ScrollView, View } from '@tarojs/components' 3 | import { Address } from '../../../../redux/interface' 4 | import { 5 | CityList as CityListInterface, 6 | Cities, 7 | } from '../../../../api/interface' 8 | import setstylepx from '../../../../utils/setstylepx' 9 | 10 | import './List.scss' 11 | 12 | interface ListProps { 13 | currentAddress: Address 14 | cityList: CityListInterface[] 15 | onSelectCity: (city: Cities) => void 16 | infoView: string 17 | } 18 | 19 | const List: FC = (props) => { 20 | const { currentAddress, cityList, onSelectCity, infoView } = props 21 | return ( 22 | 27 | 28 | 当前定位城市 29 | 30 | {currentAddress.city ? currentAddress.city : '定位当前城市失败'} 31 | 32 | 33 | {cityList.map((cityItem) => { 34 | return ( 35 | 40 | 41 | {cityItem.idx} 42 | 43 | 44 | {cityItem.cities.map((city) => { 45 | return ( 46 | onSelectCity(city)} 50 | > 51 | {city.name} 52 | 53 | ) 54 | })} 55 | 56 | ) 57 | })} 58 | 59 | ) 60 | } 61 | 62 | export default memo(List) 63 | -------------------------------------------------------------------------------- /src/pages/profile/address/components/AddressRow/AddressRow.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | import { Address } from '../../../../../redux/interface' 4 | import './AddressRow.scss' 5 | 6 | interface AddressRowProps { 7 | address: Address 8 | currentAddress: Address 9 | onDelAddress: (id: Address['id']) => void 10 | onEditAddress: (address: Address) => void 11 | selectFlag: boolean 12 | onSelectUserAddress: (address: Address) => void 13 | } 14 | 15 | const AddressRow: FC = (props) => { 16 | const { 17 | address, 18 | onDelAddress, 19 | onEditAddress, 20 | selectFlag, 21 | currentAddress, 22 | onSelectUserAddress, 23 | } = props 24 | return ( 25 | 26 | {selectFlag && currentAddress.id === address.id && ( 27 | 28 | )} 29 | onSelectUserAddress(address)} 32 | > 33 | 34 | {address.name} 35 | {address.sex === '1' ? '先生' : '女士'} 36 | {address.phone} 37 | 38 | 39 | {address.address + address.address_detail} 40 | 41 | 42 | 43 | onEditAddress(address)} 46 | > 47 | 48 | 49 | 50 | onDelAddress(address.id)} 53 | > 54 | 55 | 56 | 57 | ) 58 | } 59 | 60 | export default AddressRow 61 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const config = { 4 | projectName: 'ele-client', 5 | date: '2020-5-27', 6 | designWidth: 750, 7 | deviceRatio: { 8 | 640: 2.34 / 2, 9 | 750: 1, 10 | 828: 1.81 / 2, 11 | }, 12 | sourceRoot: 'src', 13 | outputRoot: 'dist', 14 | plugins: [], 15 | defineConstants: {}, 16 | copy: { 17 | patterns: [ 18 | // { 19 | // from: 'sitemap.json', 20 | // to: 'dist/sitemap.json', 21 | // }, 22 | ], 23 | options: {}, 24 | }, 25 | sass: { 26 | resource: path.join(__dirname, '../src/assets/styles/index.scss'), 27 | }, 28 | alias: { 29 | // '@/src': path.resolve(__dirname, '..', 'src'), 30 | }, 31 | framework: 'react', 32 | mini: { 33 | postcss: { 34 | pxtransform: { 35 | enable: true, 36 | config: {}, 37 | }, 38 | url: { 39 | enable: true, 40 | config: { 41 | limit: 1024, // 设定转换尺寸上限 42 | }, 43 | }, 44 | cssModules: { 45 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 46 | config: { 47 | namingPattern: 'module', // 转换模式,取值为 global/module 48 | generateScopedName: '[name]__[local]___[hash:base64:5]', 49 | }, 50 | }, 51 | }, 52 | }, 53 | h5: { 54 | publicPath: '/', 55 | staticDirectory: 'static', 56 | postcss: { 57 | autoprefixer: { 58 | enable: true, 59 | config: {}, 60 | }, 61 | cssModules: { 62 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 63 | config: { 64 | namingPattern: 'module', // 转换模式,取值为 global/module 65 | generateScopedName: '[name]__[local]___[hash:base64:5]', 66 | }, 67 | }, 68 | }, 69 | router: { 70 | mode: 'browser', // browser 或 'hash' 71 | }, 72 | }, 73 | } 74 | 75 | module.exports = function (merge) { 76 | if (process.env.NODE_ENV === 'development') { 77 | return merge({}, config, require('./dev')) 78 | } 79 | return merge({}, config, require('./prod')) 80 | } 81 | -------------------------------------------------------------------------------- /src/assets/images/default-shop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/order/components/Ordercard/Ordercard.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | import { View, Image } from '@tarojs/components' 3 | import dayjs from 'dayjs' 4 | import imgUrl from '../../../../utils/imgUrl' 5 | 6 | import EButton from '../../../../components/EButton/EButton' 7 | 8 | import './Ordercard.scss' 9 | 10 | interface OrdercardProps { 11 | orderData: any 12 | onLink: (orderData: any) => void 13 | } 14 | 15 | const Ordercard: FC = (props) => { 16 | const { orderData, onLink } = props 17 | 18 | const goodNames = useMemo(() => { 19 | const foods = orderData.foods 20 | const lc = foods.length 21 | let names = '' 22 | foods.forEach((food, i) => { 23 | if (i === 0) { 24 | names += food.name + ',' 25 | } else if (i === 1) { 26 | names += food.name 27 | } 28 | }) 29 | names += `等${lc}件商品` 30 | return names 31 | }, [orderData]) 32 | 33 | return ( 34 | onLink(orderData)}> 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | {orderData.shopName} 47 | 48 | 订单完成 49 | 50 | 51 | {dayjs(orderData.createTime).format('YYYY-MM-DD HH:mm:ss')} 52 | 53 | 54 | 55 | {goodNames} 56 | 57 | ¥{orderData.totalPrice} 58 | 59 | 60 | 61 | 62 | 63 | 再来一单 64 | 65 | 66 | ) 67 | } 68 | 69 | export default Ordercard 70 | -------------------------------------------------------------------------------- /src/pages/order/index.tsx: -------------------------------------------------------------------------------- 1 | // 订单 2 | import Taro from '@tarojs/taro' 3 | import React, { FC, useState, useEffect, useCallback } from 'react' 4 | import { View } from '@tarojs/components' 5 | import API from '../../api' 6 | 7 | import FooterNav from '../../components/FooterNav/FooterNav' 8 | import NoDataTip, { NoDataTipProps } from '../../components/NoDataTip/NoDataTip' 9 | import EIsLoading from '../../components/ELoading/ELoading' 10 | import Ordercard from './components/Ordercard/Ordercard' 11 | 12 | import './index.scss' 13 | 14 | const noLogin = { 15 | img: '//fuss10.elemecdn.com/d/60/70008646170d1f654e926a2aaa3afpng.png', 16 | title: '登录后查看外卖订单', 17 | btnContent: '立即登录', 18 | onButtonClick: () => { 19 | Taro.reLaunch({ url: '/pages/login/index' }) 20 | }, 21 | } 22 | const noOrder = { 23 | img: '//fuss10.elemecdn.com/8/c8/bbe5984003cb26fc7b445a4a15195png.png', 24 | title: '你还没有订餐哦', 25 | } 26 | 27 | const Order: FC = () => { 28 | // 订单数据 29 | const [orderDatas, setOrderDatas] = useState([]) 30 | // 未登录/无订单内容提示 31 | const [noData, setNoData] = useState({} as NoDataTipProps) 32 | // 数据加载状态 33 | const [loading, setLoading] = useState(false) 34 | 35 | useEffect(() => { 36 | setLoading(true) 37 | API.getOrder().then(({ err, res }) => { 38 | if (err) { 39 | setNoData(noLogin) 40 | setLoading(false) 41 | return 42 | } 43 | if (res.code === 0) { 44 | setOrderDatas(res.data) 45 | if (res.data.length === 0) { 46 | setNoData(noOrder) 47 | } 48 | setLoading(false) 49 | } 50 | }) 51 | }, []) 52 | 53 | // 跳转订单详情 54 | const goOrderDetail = useCallback((data) => { 55 | Taro.navigateTo({ url: `/pages/order/detail/index?id=${data.orderNum}` }) 56 | }, []) 57 | 58 | return ( 59 | 60 | 61 | {loading && } 62 | 63 | {orderDatas.map((orderData, i) => ( 64 | 69 | ))} 70 | 71 | {noData.img && } 72 | 73 | 74 | 75 | ) 76 | } 77 | export default Order 78 | -------------------------------------------------------------------------------- /src/pages/shop/index.scss: -------------------------------------------------------------------------------- 1 | .default-shop-img { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | .myshop { 6 | height: 100vh; 7 | -webkit-overflow-scrolling: touch; //ios不流畅问题 8 | 9 | .myshop-content { 10 | margin-top: 30px; 11 | .myshop-order { 12 | .myshop-order-banner { 13 | height: auto; 14 | margin: 20px 0; 15 | padding: 0 32px; 16 | 17 | .myshop-order-banner-img { 18 | width: 100%; 19 | height: 170px; 20 | border-radius: 5px; 21 | } 22 | } 23 | 24 | .myshop-order-recommend { 25 | margin-bottom: 30px; 26 | margin-left: 32px; 27 | 28 | .myshop-order-recommend-title { 29 | margin-right: 32px; 30 | color: #333; 31 | font-size: 32px; 32 | font-weight: 600; 33 | } 34 | 35 | .myshop-order-recommend-main { 36 | margin-top: 20px; 37 | white-space: nowrap; 38 | -webkit-overflow-scrolling: touch; //ios不流畅问题 39 | } 40 | } 41 | 42 | .myshop-order-main { 43 | .myshop-order-main-content { 44 | display: flex; 45 | .myshop-order-main-left { 46 | width: 150px; 47 | height: 100%; 48 | -webkit-overflow-scrolling: touch; //ios不流畅问题 49 | } 50 | .myshop-order-main-right { 51 | width: 550px; 52 | margin-left: 20px; 53 | height: 100%; 54 | -webkit-overflow-scrolling: touch; //ios不流畅问题 55 | .myshop-order-main-right-block { 56 | .myshop-order-main-right-title { 57 | display: flex; 58 | padding: 10px 0; 59 | .myshop-order-main-right-name { 60 | color: #666; 61 | font-size: 24px; 62 | font-weight: 600; 63 | } 64 | .myshop-order-main-right-des { 65 | padding-left: 10px; 66 | color: #666; 67 | font-size: 24px; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | .myshop-appraise { 76 | background: #f5f5f5; 77 | } 78 | } 79 | } 80 | 81 | .test { 82 | position: absolute; 83 | bottom: 100px; 84 | width: 100%; 85 | height: 300px; 86 | background: red; 87 | } 88 | -------------------------------------------------------------------------------- /src/redux/actions/user.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import API from '../../api' 3 | import { 4 | SETTOKEN, 5 | CURRENTADDRESS, 6 | SETUSERADDRESS, 7 | REMOVETOKEN, 8 | GETUSERADDRESSLIST, 9 | USERADDRESS, 10 | REMOVEUSERADDRESS, 11 | REMOVEUSERADDRESSLIST, 12 | } from '../action-types' 13 | 14 | // 设置token 15 | export const setToken = (token: string) => { 16 | token = `Bearer ${token}` 17 | Taro.setStorageSync('token', token) 18 | return { type: SETTOKEN, payload: token } 19 | } 20 | 21 | // 删除token 22 | export const removeToken = () => { 23 | Taro.removeStorageSync('token') 24 | return { type: REMOVETOKEN } 25 | } 26 | 27 | // 设置地址信息 28 | export const setCurrentAddress = (address) => ({ 29 | type: CURRENTADDRESS, 30 | payload: address, 31 | }) 32 | 33 | // 初始化ip定位地址 34 | export const initCurrentAddress = () => { 35 | return async (dispatch) => { 36 | const { err, res } = await API.reqIpAddress() 37 | 38 | if (err) { 39 | console.log(err) 40 | return 41 | } 42 | 43 | if (res.code === 0) { 44 | const { city, latitude, longitude, recommend } = res.data 45 | // 保存地址到redux 46 | dispatch( 47 | setCurrentAddress({ 48 | city, 49 | address: recommend, 50 | latitude, 51 | longitude, 52 | }) 53 | ) 54 | } else { 55 | // console.log(result) 56 | } 57 | } 58 | } 59 | 60 | // 清空用户收货地址列表 61 | export const removeUserAddressList = () => ({ type: REMOVEUSERADDRESSLIST }) 62 | // 获取用户收货地址列表 63 | const getUserAddressListSync = (userAddressList) => ({ 64 | type: GETUSERADDRESSLIST, 65 | payload: userAddressList, 66 | }) 67 | export const getUserAddressList = () => { 68 | return async (dispatch) => { 69 | const { err, res } = await API.reqUserAddress() 70 | 71 | if (err) { 72 | dispatch(removeUserAddressList()) 73 | dispatch(removeToken()) 74 | return 75 | } 76 | 77 | if (res.code === 0) { 78 | dispatch(getUserAddressListSync(res.data)) 79 | } 80 | } 81 | } 82 | 83 | // 编辑用户收货地址 84 | export const atUserAddress = (userAddress) => ({ 85 | type: USERADDRESS, 86 | payload: userAddress, 87 | }) 88 | 89 | // 修改用户收货地址 90 | export const setAtUserAddress = (userAddress) => ({ 91 | type: SETUSERADDRESS, 92 | payload: userAddress, 93 | }) 94 | 95 | // 清空用户收货地址 96 | export const removeUserAddress = () => ({ type: REMOVEUSERADDRESS }) 97 | -------------------------------------------------------------------------------- /src/pages/shop/components/ShopInfoModal/ShopInfoModal.scss: -------------------------------------------------------------------------------- 1 | .myshop-modal { 2 | position: fixed; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | background: rgba(0, 0, 0, 0.5); 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | flex-direction: column; 12 | z-index: 6; 13 | .myshop-modal-main { 14 | background: #fff; 15 | width: 80%; 16 | border-radius: 10px; 17 | .myshop-modal-main-title { 18 | font-size: 41px; 19 | font-weight: 600; 20 | text-align: center; 21 | padding: 20px 50px; 22 | .myshop-modal-main-title-tag { 23 | width: 70px; 24 | font-weight: 600; 25 | font-size: 24px; 26 | background-image: linear-gradient(90deg, #fff100, #ffe339); 27 | color: #6a3709; 28 | padding: 0 4px; 29 | margin-right: 15px; 30 | } 31 | } 32 | 33 | .myshop-modal-main-ul { 34 | display: flex; 35 | .myshop-modal-main-ul-li { 36 | width: 20%; 37 | text-align: center; 38 | .myshop-modal-main-ul-li-title { 39 | font-size: 24px; 40 | font-weight: 600; 41 | } 42 | 43 | .myshop-modal-main-ul-li-tag { 44 | color: #bdbdbd; 45 | font-size: 24px; 46 | } 47 | } 48 | } 49 | 50 | .myshop-modal-main-gongao { 51 | .myshop-modal-main-gongao-title { 52 | display: flex; 53 | align-items: center; 54 | justify-content: center; 55 | color: #999; 56 | padding: 20px 0; 57 | font-size: 26px; 58 | &::after, 59 | &::before { 60 | content: ''; 61 | display: block; 62 | width: 40px; 63 | height: 4px; 64 | background-image: linear-gradient( 65 | 90deg, 66 | #fff, 67 | rgb(199, 199, 199) 50%, 68 | #fff 69 | ); 70 | } 71 | &::after { 72 | margin-left: 10px; 73 | } 74 | &::before { 75 | margin-right: 10px; 76 | } 77 | } 78 | 79 | .myshop-modal-main-gongao-content { 80 | color: #333; 81 | padding: 0 40px 40px 40px; 82 | } 83 | } 84 | } 85 | 86 | .myshop-modal-clear { 87 | width: 80px; 88 | height: 80px; 89 | font-size: 60px; 90 | color: #fff; 91 | display: flex; 92 | justify-content: center; 93 | align-items: center; 94 | margin-top: 30px; 95 | opacity: 0.5; 96 | } 97 | 98 | .myshop-modal-bg { 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/assets/styles/normalize.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #container, 4 | .taro-tabbar__panel, 5 | #app, 6 | #app > div, 7 | .taro_page { 8 | height: 100%; 9 | width: 100%; 10 | } 11 | 12 | /* #ifndef h5 */ 13 | page { 14 | font-size: $body-font-size; 15 | font-family: $body-font-famify; 16 | color: $color-333; 17 | box-sizing: border-box; 18 | height: 100%; 19 | } 20 | /* #endif */ 21 | 22 | /* #ifdef h5 */ 23 | html { 24 | line-height: $body-line-height !important; 25 | font-family: $body-font-famify; 26 | color: $color-333; 27 | box-sizing: border-box; 28 | user-select: none; 29 | touch-action: manipulation; 30 | text-size-adjust: none; 31 | } 32 | body { 33 | margin: 0; 34 | padding: 0; 35 | line-height: $body-line-height !important; 36 | font-size: $body-font-size; 37 | } 38 | 39 | button, 40 | input, 41 | optgroup, 42 | select, 43 | textarea { 44 | font-family: inherit; /* 1 */ 45 | font-size: 100%; /* 1 */ 46 | line-height: $body-line-height; /* 1 */ 47 | margin: 0; /* 2 */ 48 | } 49 | 50 | button, 51 | input { 52 | /* 1 */ 53 | overflow: visible; 54 | } 55 | 56 | button, 57 | select { 58 | /* 1 */ 59 | text-transform: none; 60 | } 61 | 62 | button, 63 | [type="button"], 64 | [type="reset"], 65 | [type="submit"] { 66 | -webkit-appearance: button; 67 | } 68 | 69 | button::-moz-focus-inner, 70 | [type="button"]::-moz-focus-inner, 71 | [type="reset"]::-moz-focus-inner, 72 | [type="submit"]::-moz-focus-inner { 73 | border-style: none; 74 | padding: 0; 75 | } 76 | 77 | button:-moz-focusring, 78 | [type="button"]:-moz-focusring, 79 | [type="reset"]:-moz-focusring, 80 | [type="submit"]:-moz-focusring { 81 | outline: 1px dotted ButtonText; 82 | } 83 | 84 | [type="checkbox"], 85 | [type="radio"] { 86 | box-sizing: border-box; /* 1 */ 87 | padding: 0; /* 2 */ 88 | } 89 | 90 | [type="number"]::-webkit-inner-spin-button, 91 | [type="number"]::-webkit-outer-spin-button { 92 | height: auto; 93 | } 94 | 95 | [type="search"] { 96 | -webkit-appearance: textfield; /* 1 */ 97 | outline-offset: -2px; /* 2 */ 98 | } 99 | 100 | [type="search"]::-webkit-search-decoration { 101 | -webkit-appearance: none; 102 | } 103 | 104 | ::-webkit-file-upload-button { 105 | -webkit-appearance: button; /* 1 */ 106 | font: inherit; /* 2 */ 107 | } 108 | /* #endif */ 109 | 110 | .ellipsis { 111 | overflow: hidden; 112 | white-space: nowrap; 113 | text-overflow: ellipsis; 114 | } 115 | 116 | .ele-hide { 117 | display: none !important; 118 | } 119 | .ele-show { 120 | display: block !important; 121 | } 122 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ele-ts-client", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "仿饿了么H5, 可编译跨端 H5 微信小程序", 6 | "templateInfo": { 7 | "name": "redux", 8 | "typescript": true, 9 | "css": "sass" 10 | }, 11 | "scripts": { 12 | "build:weapp": "taro build --type weapp", 13 | "build:swan": "taro build --type swan", 14 | "build:alipay": "taro build --type alipay", 15 | "build:tt": "taro build --type tt", 16 | "build:h5": "taro build --type h5", 17 | "build:rn": "taro build --type rn", 18 | "build:qq": "taro build --type qq", 19 | "build:jd": "taro build --type jd", 20 | "build:quickapp": "taro build --type quickapp", 21 | "dev:weapp": "npm run build:weapp -- --watch", 22 | "dev:swan": "npm run build:swan -- --watch", 23 | "dev:alipay": "npm run build:alipay -- --watch", 24 | "dev:tt": "npm run build:tt -- --watch", 25 | "dev:h5": "npm run build:h5 -- --watch", 26 | "dev:rn": "npm run build:rn -- --watch", 27 | "dev:qq": "npm run build:qq -- --watch", 28 | "dev:jd": "npm run build:jd -- --watch", 29 | "dev:quickapp": "npm run build:quickapp -- --watch" 30 | }, 31 | "browserslist": [ 32 | "last 3 versions", 33 | "Android >= 4.1", 34 | "ios >= 8" 35 | ], 36 | "author": "", 37 | "license": "MIT", 38 | "dependencies": { 39 | "@babel/runtime": "^7.7.7", 40 | "@tarojs/components": "3.0.7", 41 | "@tarojs/react": "3.0.7", 42 | "@tarojs/runtime": "3.0.7", 43 | "@tarojs/taro": "3.0.7", 44 | "@types/classnames": "^2.2.10", 45 | "@types/react-redux": "^7.1.9", 46 | "@types/react-transition-group": "^4.4.0", 47 | "await-to-js": "^2.1.1", 48 | "classnames": "^2.2.6", 49 | "dayjs": "^1.8.34", 50 | "lodash": "^4.17.20", 51 | "react": "^16.10.0", 52 | "react-dom": "^16.10.0", 53 | "react-redux": "^7.2.0", 54 | "react-transition-group": "^4.4.1", 55 | "redux": "^4.0.0", 56 | "redux-logger": "^3.0.6", 57 | "redux-thunk": "^2.3.0", 58 | "tua-body-scroll-lock": "^1.1.0" 59 | }, 60 | "devDependencies": { 61 | "@babel/core": "^7.8.0", 62 | "@tarojs/mini-runner": "3.0.7", 63 | "@tarojs/webpack-runner": "3.0.7", 64 | "@types/react": "^16.9.46", 65 | "@types/webpack-env": "^1.13.6", 66 | "@typescript-eslint/eslint-plugin": "^2.x", 67 | "@typescript-eslint/parser": "^2.x", 68 | "babel-preset-taro": "3.0.7", 69 | "eslint": "^6.8.0", 70 | "eslint-config-taro": "3.0.7", 71 | "eslint-plugin-import": "^2.12.0", 72 | "eslint-plugin-react": "^7.8.2", 73 | "eslint-plugin-react-hooks": "^1.6.1", 74 | "stylelint": "9.3.0", 75 | "typescript": "^3.7.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/pages/shop/components/ShopInfoModal/ShopInfoModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import classnames from 'classnames' 3 | import { View, Text } from '@tarojs/components' 4 | import distance from '../../../../utils/distance' 5 | 6 | import './ShopInfoModal.scss' 7 | 8 | interface ShopInfoModalProps { 9 | shopInfo 10 | modalHide 11 | onOpenModal 12 | } 13 | 14 | const ShopInfoModal: FC = (props) => { 15 | const { shopInfo, modalHide, onOpenModal } = props 16 | return ( 17 | 22 | 23 | 24 | 品牌 25 | 唐宫海鲜舫(新侨店) 26 | 27 | 28 | 29 | 30 | {shopInfo.rating} 31 | 32 | 评分 33 | 34 | 35 | 36 | {shopInfo.recent_order_num} 37 | 38 | 月售 39 | 40 | 41 | 42 | {shopInfo.delivery_mode.text} 43 | 44 | 45 | 约{shopInfo.order_lead_time} 46 | 47 | 48 | 49 | 50 | {shopInfo.piecewise_agent_fee.rules[0].fee}元 51 | 52 | 配送费 53 | 54 | 55 | 56 | {distance(shopInfo.distance)} 57 | 58 | 距离 59 | 60 | 61 | 62 | 公告 63 | 64 | {shopInfo.promotion_info} 65 | 66 | 67 | 68 | 72 | 73 | 74 | ) 75 | } 76 | 77 | export default ShopInfoModal 78 | -------------------------------------------------------------------------------- /tatus: -------------------------------------------------------------------------------- 1 | * 3e006ff (HEAD -> master) 清空 2 | * ae4e2ec 初始化 3 | * ed6660c 初始化 4 | * 14c70b5 重构 5 | * df741e5 (origin/master) 说明 6 | * ff61169 修改地址,微调说明 7 | * 736a36d 删除本地历史 8 | * f3f645c 说明 9 | * 5aa692c 滚动条机制修改 10 | * 1544b0d 筛选top值问题 11 | * d93ab38 重写请求api,修复dom等bug 12 | * 6e3c31f 数据请求模块重构 13 | * b78ca58 微调,去除无用代码 14 | * 3ed101c 小程序修复头部导航问题,去除代码冗余 15 | * f4514ce 去除index.html无用script 16 | * 135ddd2 说明 17 | | * 01339dc (origin/dependabot/npm_and_yarn/elliptic-6.5.3) Bump elliptic from 6.5.2 to 6.5.3 18 | |/ 19 | * c35c955 订单详情 20 | * 6b52667 订单列表 21 | | * 0983996 (origin/dependabot/npm_and_yarn/websocket-extensions-0.1.4) Bump websocket-extensions from 0.1.3 to 0.1.4 22 | |/ 23 | * 42871ca 结算 24 | * 3cbffc8 git排除history 25 | * c10f986 发现页 26 | * 33db12d 商家展开优惠问题修复 27 | * 0cb0871 商家信息,商家搜索 28 | |\ 29 | | * 613d69f 搜索页 30 | * | 75a3c49 商家阿微调 31 | * | 6e436da 商家信息 32 | |/ 33 | * 38e27cb 商家评价 34 | * 62509c4 购物车数据 35 | * 2710e5b 右侧商品列表滚动兼容 36 | * 25cca13 商家详情微调 37 | * ae59649 商家详情 38 | * 2d67769 商家列表没有更多 39 | * 3990f8f 商家列表 40 | * bea1eb7 商家列表头部分类 41 | * 0cf6e2a 调整ip定位商家参数冲突问题 42 | * bdab1b3 Merge branch 'indexselectshop' 43 | |\ 44 | | * 60a9cb0 action处理每次加载重置商家条数 45 | | * 30e80ce 筛选联动更新商家列表 46 | | * fa655bb 暂存 47 | | * 0483dd2 解决首页滚动条位置问题 48 | * | ac881c8 配置 49 | |/ 50 | * 34e79bb 说明调整 51 | * 05ae1b1 首页获取用户地址并且设置用户地址 52 | * cff7523 调整navigateTo navigateBack 53 | * fcfe851 微调 54 | * ce77a5d 个人中心 55 | * 7448bdc onDidShow 解决 navigateTo 问题 56 | * e1a3927 个人中心 57 | * b927e6c 调整我的结构 58 | * b64663c 用户信息 59 | * 7f5c4cb 收货地址 60 | |\ 61 | | * f586947 我的收货地址 62 | | * 73c632c 我的 63 | * | bbae65d 合并 64 | * | 8627784 Merge branch 'profile' 65 | |\| 66 | | * dbfffb2 我的 67 | * | 2dd5a06 Merge branch 'register' 68 | |\ \ 69 | | * | 1d53d06 注册 70 | | * | 7d4dd42 注册 71 | * | | 74a8258 登录 72 | |/ / 73 | * / 680dce0 登录 74 | |/ 75 | * dfa4c8a 我的 76 | * 9fa2749 订单 77 | * 0644fc7 发现 78 | * 6487980 首页 79 | * 01563f3 首页 80 | * 21e3e70 初始化 81 | -------------------------------------------------------------------------------- /src/pages/shop/components/Head/Head.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { View, Image, Text, ITouchEvent } from '@tarojs/components' 3 | import imgUrl from '../../../../utils/imgUrl' 4 | import { H5 } from '../../../../config/base' 5 | 6 | import './Head.scss' 7 | 8 | interface HeadProps { 9 | shopInfo 10 | onOpenModal: () => void 11 | onActivityHide: (e: ITouchEvent) => void 12 | onBack: () => void 13 | } 14 | 15 | const Head: FC = (props) => { 16 | const { shopInfo, onOpenModal, onActivityHide, onBack } = props 17 | 18 | return ( 19 | 20 | 28 | {H5 && } 29 | 30 | 31 | 32 | 品牌 33 | 37 | 38 | 39 | {shopInfo.name} 40 | 41 | 42 | 43 | 评价{shopInfo.rating} 44 | 45 | 月售{shopInfo.recent_order_num}单 46 | 47 | 48 | {shopInfo.delivery_mode.text}约{shopInfo.order_lead_time}分钟 49 | 50 | 51 | {!!shopInfo.activity_tags.length && ( 52 | onActivityHide(e)}> 53 | 54 | {shopInfo.activity_tags.map((tag) => { 55 | return ( 56 | 57 | {tag.text} 58 | 59 | ) 60 | })} 61 | 62 | 63 | 64 | {shopInfo.activities.length}个优惠 65 | 66 | 67 | 68 | 69 | )} 70 | {shopInfo.promotion_info && ( 71 | 72 | 73 | 公告:{shopInfo.promotion_info} 74 | 75 | 76 | )} 77 | 78 | 79 | ) 80 | } 81 | 82 | export default Head 83 | -------------------------------------------------------------------------------- /src/pages/discover/index.scss: -------------------------------------------------------------------------------- 1 | .discover { 2 | height: 100%; 3 | background: #f5f5f5; 4 | 5 | .discover-main { 6 | background: #fff; 7 | .discover-item { 8 | display: flex; 9 | align-items: center; 10 | justify-content: space-between; 11 | height: 160px; 12 | margin-left: 50%; 13 | padding: 0 30px; 14 | &:first-child { 15 | align-items: normal; 16 | justify-content: space-between; 17 | position: absolute; 18 | left: 0; 19 | width: calc(50% - 56px); 20 | height: 264px; 21 | margin: 0; 22 | padding: 44px 22px 12px 32px; 23 | border-right: 1px #ededed solid; 24 | .discover-img { 25 | align-self: flex-end; 26 | width: 150px; 27 | height: 150px; 28 | } 29 | } 30 | &:nth-child(2) { 31 | border-bottom: 1px #ededed solid; 32 | } 33 | .discover-left { 34 | .discover-title { 35 | font-size: 32px; 36 | } 37 | 38 | .discover-discover-subtitle { 39 | color: #999; 40 | font-size: 24px; 41 | } 42 | } 43 | 44 | .discover-img { 45 | width: 90px; 46 | height: 90px; 47 | } 48 | } 49 | } 50 | 51 | .suggest { 52 | margin-top: 20px; 53 | background: #fff; 54 | .suggest-head { 55 | padding-top: 30px; 56 | .suggest-head-title { 57 | display: flex; 58 | align-items: center; 59 | justify-content: center; 60 | .striping { 61 | position: relative; 62 | width: 40px; 63 | height: 4px; 64 | background: #333; 65 | &::after { 66 | content: ''; 67 | position: absolute; 68 | top: -3px; 69 | width: 12px; 70 | height: 12px; 71 | background: #333; 72 | border-radius: 50%; 73 | } 74 | &:first-child { 75 | &::after { 76 | right: 0; 77 | } 78 | } 79 | } 80 | 81 | .icon { 82 | padding: 0 10px 0 5px; 83 | color: #f9534e; 84 | font-size: 36px; 85 | } 86 | 87 | .title { 88 | padding-right: 10px; 89 | font-size: 36px; 90 | font-weight: 600; 91 | } 92 | } 93 | 94 | .tip { 95 | color: #999; 96 | font-size: 24px; 97 | line-height: 1.2; 98 | text-align: center; 99 | } 100 | } 101 | 102 | .suggest-main { 103 | display: flex; 104 | align-items: center; 105 | flex-wrap: wrap; 106 | margin-top: 20px; 107 | .suggest-more { 108 | width: 100%; 109 | margin: 20px 0; 110 | color: #999; 111 | text-align: center; 112 | .icon { 113 | color: #999; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/components/FooterNav/FooterNav.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { getCurrentInstance } from '@tarojs/taro' 2 | import React, { FC, useState, useEffect } from 'react' 3 | import { View, Image } from '@tarojs/components' 4 | import classnames from 'classnames' 5 | import { H5 } from '../../config/base' 6 | 7 | import msiteIcon from './images/msite_1.svg' 8 | import msiteActiveIcon from './images/msite_2.svg' 9 | import discoverIcon from './images/discover_1.svg' 10 | import discoverActiveIcon from './images/discover_2.svg' 11 | import orderIcon from './images/order_1.svg' 12 | import orderActiveIcon from './images/order_2.svg' 13 | import profileIcon from './images/profile_1.svg' 14 | import profileActiveIcon from './images/profile_2.svg' 15 | 16 | import './FooterNav.scss' 17 | 18 | interface Bar { 19 | id: number 20 | name: string 21 | path: string 22 | icon: string 23 | active_icon: string 24 | } 25 | 26 | const FooterNav: FC = () => { 27 | // 路由地址 28 | const [path, setPath] = useState('') 29 | const bars: Bar[] = [ 30 | { 31 | id: 0, 32 | name: '首页', 33 | path: '/pages/msite/index', 34 | icon: msiteIcon, 35 | active_icon: msiteActiveIcon, 36 | }, 37 | { 38 | id: 1, 39 | name: '发现', 40 | path: '/pages/discover/index', 41 | icon: discoverIcon, 42 | active_icon: discoverActiveIcon, 43 | }, 44 | { 45 | id: 2, 46 | name: '订单', 47 | path: '/pages/order/index', 48 | icon: orderIcon, 49 | active_icon: orderActiveIcon, 50 | }, 51 | { 52 | id: 3, 53 | name: '我的', 54 | path: '/pages/profile/index', 55 | icon: profileIcon, 56 | active_icon: profileActiveIcon, 57 | }, 58 | ] 59 | 60 | useEffect(() => { 61 | // 获取路由路径 62 | const current: Taro.Current = getCurrentInstance() 63 | if (H5) { 64 | if (current.app) { 65 | // H5端 66 | setPath(current.app.config.router.pathname) 67 | } 68 | } else { 69 | // 小程序 70 | setPath(current.router.path) 71 | } 72 | }, []) 73 | 74 | // 跳转 75 | const handleGo = (bar: Bar) => { 76 | Taro.reLaunch({ 77 | url: bar.path, 78 | }) 79 | } 80 | 81 | return ( 82 | 83 | 84 | {bars.map((bar) => { 85 | return ( 86 | handleGo(bar)} 89 | className={classnames('footer-item', { 90 | active: path === bar.path, 91 | })} 92 | > 93 | 94 | 98 | 99 | {bar.name} 100 | 101 | ) 102 | })} 103 | 104 | 105 | ) 106 | } 107 | export default FooterNav 108 | -------------------------------------------------------------------------------- /src/pages/clearing/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import React, { useMemo, useState, useCallback } from 'react' 3 | import { View } from '@tarojs/components' 4 | import { useSelector } from 'react-redux' 5 | import { H5 } from '../../config/base' 6 | import NavBar from '../../components/NavBar/NavBar' 7 | import API from '../../api' 8 | import { Reducers } from '../../redux/interface' 9 | 10 | import CartAddress from './components/CartAddress/CartAddress' 11 | import Distribution from './components/Distribution/Distribution' 12 | import CartInfo from './components/CartInfo/CartInfo' 13 | import FooterDic from './components/FooterDic/FooterDic' 14 | 15 | import './index.scss' 16 | 17 | const Clearing = () => { 18 | const { currentAddress } = useSelector((state: Reducers) => state) 19 | const { cartInfo, shopInfo } = Taro.getStorageSync('clearing') 20 | const shopName = shopInfo.name 21 | const [payLoading, setPayLoading] = useState(false) 22 | 23 | // 选择地址 24 | const selectAddress = () => { 25 | Taro.navigateTo({ url: '/pages/profile/address/index?clearing=true' }) 26 | } 27 | 28 | // 总价 29 | const totalPrice = useMemo(() => { 30 | return cartInfo.totalPrice + cartInfo.goodTotal + cartInfo.boxPrice 31 | }, [cartInfo]) 32 | 33 | // 优惠价格 34 | const discountsPrice = useMemo(() => { 35 | return cartInfo.originalPrice - cartInfo.totalPrice 36 | }, [cartInfo]) 37 | 38 | // 去支付 39 | const pay = useCallback(async () => { 40 | if (currentAddress.id) { 41 | const body = { 42 | ...cartInfo, 43 | shopName: shopInfo.name, 44 | imagePath: shopInfo.image_path, 45 | delivery: shopInfo.delivery_mode.text, 46 | address: JSON.stringify(currentAddress), 47 | foods: JSON.stringify(cartInfo.foods), 48 | } 49 | 50 | setPayLoading(true) 51 | const { err, res } = await API.reqPay(body) 52 | if (err) { 53 | console.log(err) 54 | return 55 | } 56 | if (res.code === 0) { 57 | setPayLoading(false) 58 | Taro.showToast({ 59 | title: '支付成功', 60 | icon: 'loading', 61 | duration: 1500, 62 | success() { 63 | setTimeout(() => { 64 | Taro.reLaunch({ url: '/pages/order/index' }) 65 | }, 1500) 66 | }, 67 | }) 68 | } 69 | } else { 70 | Taro.showToast({ title: '请选择收货地址', icon: 'none', duration: 1500 }) 71 | } 72 | }, [cartInfo, shopInfo, currentAddress]) 73 | 74 | if (H5) { 75 | document.removeEventListener('scroll', () => {}, false) 76 | } 77 | 78 | return ( 79 | 80 | {H5 && } 81 | 85 | 86 | 87 | 93 | 94 | ) 95 | } 96 | 97 | export default Clearing 98 | -------------------------------------------------------------------------------- /src/redux/reducers/user.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import { 3 | GETUSERADDRESSLIST, 4 | SETTOKEN, 5 | REMOVETOKEN, 6 | CURRENTADDRESS, 7 | USERADDRESS, 8 | SETUSERADDRESS, 9 | REMOVEUSERADDRESS, 10 | REMOVEUSERADDRESSLIST, 11 | } from '../action-types' 12 | import { Reducers } from '../interface' 13 | 14 | interface Action { 15 | type: T 16 | payload: D 17 | } 18 | 19 | // 用户token 20 | type TOKENTYPE = typeof SETTOKEN | typeof REMOVETOKEN 21 | const initToken: string = Taro.getStorageSync('token') || '' 22 | const token = ( 23 | state = initToken, 24 | action: Action 25 | ) => { 26 | const { type, payload } = action 27 | switch (type) { 28 | case SETTOKEN: 29 | return payload 30 | case REMOVETOKEN: 31 | return '' 32 | default: 33 | return state 34 | } 35 | } 36 | 37 | // 当前收货地址 38 | type CURRENTADDRESSTYPE = typeof CURRENTADDRESS 39 | const initCurrentAddress = { 40 | id: '', 41 | city: '', // 城市名称 42 | address: '', //详细地址 43 | address_detail: '', // 门牌号 44 | latitude: '', // 纬度 45 | longitude: '', // 经度 46 | name: '', // 收货人 47 | phone: '', // 收货人手机 48 | sex: '', // 性别 49 | } 50 | const currentAddress = ( 51 | state = initCurrentAddress, 52 | action: Action 53 | ) => { 54 | const { type, payload } = action 55 | switch (type) { 56 | case CURRENTADDRESS: 57 | return { ...state, ...payload } 58 | default: 59 | return state 60 | } 61 | } 62 | 63 | // 收货地址列表 64 | type USERADDRESSLISTTYPE = 65 | | typeof GETUSERADDRESSLIST 66 | | typeof REMOVEUSERADDRESSLIST 67 | const initAddressList = [] 68 | const userAddressList = ( 69 | state = initAddressList, 70 | action: Action 71 | ) => { 72 | const { type, payload } = action 73 | switch (type) { 74 | case GETUSERADDRESSLIST: 75 | return payload 76 | case REMOVEUSERADDRESSLIST: 77 | return [] 78 | default: 79 | return state 80 | } 81 | } 82 | 83 | // 编辑收货地址 84 | type USERADDRESSTYPE = 85 | | typeof USERADDRESS 86 | | typeof SETUSERADDRESS 87 | | typeof REMOVEUSERADDRESS 88 | const initUserAddress = { 89 | id: '', 90 | city: '', // 城市名称 91 | address: '', //详细地址 92 | address_detail: '', // 门牌号 93 | latitude: '', // 纬度 94 | longitude: '', // 经度 95 | name: '', // 收货人 96 | phone: '', // 收货人手机 97 | sex: '', // 性别 98 | } 99 | const userAddress = ( 100 | state = initUserAddress, 101 | action: Action 102 | ) => { 103 | const { type, payload } = action 104 | switch (type) { 105 | case USERADDRESS: 106 | return payload 107 | case SETUSERADDRESS: 108 | return { 109 | ...state, 110 | ...payload, 111 | } 112 | case REMOVEUSERADDRESS: 113 | return initUserAddress 114 | default: 115 | return state 116 | } 117 | } 118 | 119 | export default { 120 | token, 121 | userAddressList, 122 | userAddress, 123 | currentAddress, 124 | } 125 | -------------------------------------------------------------------------------- /src/api/serve.ts: -------------------------------------------------------------------------------- 1 | import { request, getStorageSync } from '@tarojs/taro' 2 | 3 | class Server { 4 | protected ajax({ 5 | url, 6 | data, 7 | method = 'GET', 8 | ...restParams 9 | }: Taro.RequestParams) { 10 | // 用户token 11 | const Authorization: string = getStorageSync('token') || '' 12 | // 判断请求类型 13 | let contentType: string 14 | // GET请求 15 | if (method === 'GET') { 16 | contentType = 'application/json' 17 | // POST 请求 18 | } else if (method === 'POST') { 19 | contentType = 'application/x-www-form-urlencoded' 20 | } 21 | return new Promise( 22 | (resolve, reject) => { 23 | request({ 24 | url, 25 | data, 26 | method, 27 | header: { 28 | 'content-type': contentType, 29 | Authorization, 30 | }, 31 | ...restParams, 32 | // 成功回调 33 | success(res: Taro.request.SuccessCallbackResult): void { 34 | resolve(res) 35 | }, 36 | // 失败回调 37 | fail(err: Taro.General.CallbackResult): void { 38 | reject(err) 39 | }, 40 | }) 41 | } 42 | ) 43 | } 44 | } 45 | 46 | export default Server 47 | 48 | // import { request, showLoading, hideLoading, getStorageSync } from '@tarojs/taro' 49 | 50 | // class Server { 51 | // // 初始请求次数 52 | // protected reqNum: number = 0 53 | // // 用户token 54 | // protected Authorization: string = getStorageSync('token') || '' 55 | 56 | // protected ajax({ 57 | // url, 58 | // data, 59 | // method = 'GET', 60 | // ...restParams 61 | // }: Taro.RequestParams) { 62 | // // 提示 63 | // showLoading({ title: '加载中...', mask: true }) 64 | // // 请求次数递增 65 | // this.reqNum++ 66 | // // 判断请求类型 67 | // let contentType: string 68 | // // GET请求 69 | // if (method === 'GET') { 70 | // contentType = 'application/json' 71 | // // POST 请求 72 | // } else if (method === 'POST') { 73 | // contentType = 'application/x-www-form-urlencoded' 74 | // } 75 | 76 | // return new Promise( 77 | // (resolve, reject) => { 78 | // request({ 79 | // url, 80 | // data, 81 | // method, 82 | // header: { 83 | // 'content-type': contentType, 84 | // Authorization: this.Authorization, 85 | // }, 86 | // ...restParams, 87 | // // 成功回调 88 | // success(res: Taro.request.SuccessCallbackResult): void { 89 | // resolve(res) 90 | // }, 91 | // // 失败回调 92 | // fail(err: Taro.General.CallbackResult): void { 93 | // reject(err) 94 | // }, 95 | // // 成功失败都回调 96 | // complete: () => { 97 | // // 请求次数递减 98 | // this.reqNum-- 99 | // // reqNum=0 说明最后一个请求发送完毕 100 | // if (this.reqNum === 0) { 101 | // hideLoading() 102 | // } 103 | // }, 104 | // }) 105 | // } 106 | // ) 107 | // } 108 | // } 109 | 110 | // export default Server 111 | -------------------------------------------------------------------------------- /src/pages/profile/address/search/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import React, { useState, useCallback, useRef, useEffect } from 'react' 3 | import { View, Text, Input, ScrollView } from '@tarojs/components' 4 | import { useSelector, useDispatch } from 'react-redux' 5 | import API from '../../../../api' 6 | import { UseSearchAddress } from '../../../../api/interface' 7 | import { Reducers, Address } from '../../../../redux/interface' 8 | import { 9 | setAtUserAddress, 10 | initCurrentAddress, 11 | } from '../../../../redux/actions/user' 12 | import NavBar from '../../../../components/NavBar/NavBar' 13 | import './index.scss' 14 | 15 | const Search = () => { 16 | const currentAddress = useSelector((state: Reducers) => state.currentAddress) 17 | const dispatch = useDispatch() 18 | const [value, setValue] = useState('') 19 | const [addressList, setAddressList] = useState([]) 20 | 21 | // 获取搜索地址 22 | const getAddressList = useCallback(async () => { 23 | // 如果redux没有数据 重新获取ip地址信息 24 | if (!currentAddress.longitude && !currentAddress.latitude) { 25 | dispatch(initCurrentAddress()) 26 | } else { 27 | const parmas = { 28 | key: value, 29 | longitude: currentAddress.longitude, 30 | latitude: currentAddress.latitude, 31 | } 32 | const { err, res } = await API.reqUseSearchAddress(parmas) 33 | 34 | if (err) { 35 | console.log(err) 36 | return 37 | } 38 | 39 | if (res.code === 0) { 40 | setAddressList(res.data) 41 | } else { 42 | console.log(res) 43 | Taro.showToast({ title: res.message, icon: 'none' }) 44 | } 45 | } 46 | }, [value, currentAddress, dispatch]) 47 | 48 | useEffect(() => { 49 | getAddressList() 50 | }, [getAddressList]) 51 | 52 | // 搜索 53 | const onSearch = (e) => { 54 | const keyword = e.detail.value 55 | setValue(keyword) 56 | } 57 | 58 | // 选择地址 59 | const selectCity = (item: Address) => { 60 | const atAddress = { 61 | address: item.name, 62 | address_detail: item.address, 63 | city: item.city, 64 | latitude: item.latitude, 65 | longitude: item.longitude, 66 | } 67 | dispatch(setAtUserAddress({ ...atAddress })) 68 | Taro.navigateBack({ delta: 1 }) 69 | } 70 | 71 | return ( 72 | 73 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | {addressList.map((item) => { 85 | return ( 86 | selectCity(item)} 90 | > 91 | {item.name} 92 | {item.address} 93 | 94 | ) 95 | })} 96 | 97 | 98 | 99 | ) 100 | } 101 | 102 | export default Search 103 | -------------------------------------------------------------------------------- /src/pages/shop/components/Head/Head.scss: -------------------------------------------------------------------------------- 1 | .myshop-top { 2 | .myshop-top-bg { 3 | position: relative; 4 | height: 200px; 5 | background-repeat: no-repeat !important; 6 | background-size: 100% 100% !important; 7 | .icon { 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | width: 60px; 12 | height: 60px; 13 | padding: 20px 0 0 20px; 14 | color: #fff; 15 | font-size: 30px; 16 | font-weight: 600; 17 | } 18 | } 19 | 20 | .myshop-top-main { 21 | position: relative; 22 | padding-top: 50px; 23 | 24 | .myshop-logo { 25 | overflow: hidden; 26 | position: absolute; 27 | top: 0; 28 | left: 50%; 29 | margin-top: -120px; 30 | transform: translateX(-50%); 31 | border-radius: 6px; 32 | 33 | .myshop-logo-pinpai { 34 | position: absolute; 35 | z-index: 1; 36 | top: 0; 37 | left: 0; 38 | padding: 0 5px; 39 | background-image: linear-gradient(90deg, #fff100, #ffe339); 40 | color: #6f3f15; 41 | font-size: 22px; 42 | } 43 | 44 | .myshop-logo-img { 45 | width: 150px; 46 | height: 150px; 47 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2); 48 | } 49 | } 50 | 51 | .myshop-name { 52 | display: flex; 53 | align-items: center; 54 | justify-content: center; 55 | width: 72vw; 56 | margin: 0 auto; 57 | color: #333; 58 | font-weight: 600; 59 | 60 | .myshop-name-title { 61 | overflow: hidden; 62 | font-size: 41px; 63 | white-space: nowrap; 64 | text-overflow: ellipsis; 65 | } 66 | .icon { 67 | font-size: 24px; 68 | } 69 | } 70 | 71 | .myshop-pingjia { 72 | display: flex; 73 | justify-content: center; 74 | padding: 5px 0; 75 | color: #666; 76 | 77 | .myshop-pingjia-item { 78 | font-size: $font-size-22; 79 | padding-top: 14px ; 80 | padding-bottom: 14px ; 81 | } 82 | .myshop-pingjia-item-c { 83 | padding-left: 20px; 84 | padding-right: 20px; 85 | } 86 | } 87 | 88 | .myshop-tags { 89 | display: flex; 90 | justify-content: space-between; 91 | margin: 0 80px; 92 | 93 | .myshop-tags-left { 94 | overflow: hidden; 95 | white-space: nowrap; 96 | text-overflow: ellipsis; 97 | 98 | .myshop-tags-left-tag { 99 | display: inline-block; 100 | margin: 0 5px; 101 | padding: 0 10px; 102 | border: 1px #ffe4e0 solid; 103 | color: #ff4b33; 104 | font-size: 24px; 105 | } 106 | } 107 | .myshop-tags-right { 108 | display: flex; 109 | align-items: center; 110 | width: 150px; 111 | color: #999; 112 | 113 | .myshop-tags-right-title { 114 | font-size: 24px; 115 | } 116 | .icon { 117 | font-size: 18px; 118 | } 119 | } 120 | } 121 | 122 | .myshop-notice { 123 | display: flex; 124 | margin: 0 80px; 125 | padding-top: 25px; 126 | 127 | .myshop-notice-content { 128 | overflow: hidden; 129 | color: #999; 130 | font-size: 24px; 131 | white-space: nowrap; 132 | text-overflow: ellipsis; 133 | } 134 | } 135 | } 136 | } 137 | --------------------------------------------------------------------------------