mirror of
https://gitee.com/ShopeX/ECShopX_mobile-frontend
synced 2026-08-14 17:41:45 +08:00
本地购物车
This commit is contained in:
1517
package-lock.json
generated
1517
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@
|
||||
"axios": "^0.18.0",
|
||||
"classnames": "^2.2.6",
|
||||
"copy-to-clipboard": "^3.0.8",
|
||||
"dot-prop-immutable": "^1.5.0",
|
||||
"lodash": "^4.17.11",
|
||||
"memoize-one": "^5.0.0",
|
||||
"moment": "^2.24.0",
|
||||
|
||||
@@ -10,7 +10,7 @@ export function detail (tid) {
|
||||
|
||||
export function create (data) {
|
||||
// return req.post('/trade.create', data)
|
||||
return req.post('http://pjj.aixue7.com/index.php/api/h5app/wxapp/order', data)
|
||||
return req.post('http://pjj.aixue7.com/index.php/api/h5app/wxapp/order_new', data)
|
||||
}
|
||||
|
||||
export function confirm (tid) {
|
||||
|
||||
@@ -8,6 +8,10 @@ export function logout () {
|
||||
return req.post('/user.logout')
|
||||
}
|
||||
|
||||
export function refreshToken () {
|
||||
return req.get('http://pjj.aixue7.com/index.php/api/h5app/wxapp/token/refresh')
|
||||
}
|
||||
|
||||
export function reg (data) {
|
||||
// return req.post('http://pjj.aixue7.com/index.php/api/weapp/deposit/rechargeruleshttp://api.espier.local/index.php/api/h5app/wxapp/member', data)
|
||||
return req.post('http://pjj.aixue7.com/index.php/api/h5app/wxapp/member?company_id=1', data)
|
||||
|
||||
@@ -25,7 +25,7 @@ class App extends Component {
|
||||
'pages/item/point-list',
|
||||
'pages/item/point-detail',
|
||||
|
||||
'pages/cart/index',
|
||||
'pages/cart/espier-index',
|
||||
'pages/cart/espier-checkout',
|
||||
'pages/cart/coupon-picker',
|
||||
'pages/article/index',
|
||||
|
||||
@@ -25,7 +25,7 @@ export default class TabBar extends Component {
|
||||
tabList: [
|
||||
{ title: '首页', iconType: 'shop', iconPrefixClass: 'sp-icon', url: '/pages/home/index' },
|
||||
{ title: '分类', iconType: 'menu', iconPrefixClass: 'sp-icon', url: '/pages/category/index' },
|
||||
{ title: '购物车', iconType: 'cart', text: this.props.cartTotalCount || '', max: '99', iconPrefixClass: 'sp-icon', url: '/pages/cart/index' },
|
||||
{ title: '购物车', iconType: 'cart', text: this.props.cartTotalCount || '', max: '99', iconPrefixClass: 'sp-icon', url: '/pages/cart/espier-index' },
|
||||
{ title: '会员', iconType: 'user', iconPrefixClass: 'sp-icon', url: '/pages/member/index' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,12 +34,10 @@ function wxParse(bindName = 'wxParseData', type='html', data='<div class="color:
|
||||
var transData = {};//存放转化后的数据
|
||||
if (type == 'html') {
|
||||
transData = HtmlToJson.html2json(data, bindName);
|
||||
console.log(JSON.stringify(transData, ' ', ' '));
|
||||
} else if (type == 'md' || type == 'markdown') {
|
||||
var converter = new showdown.Converter();
|
||||
var html = converter.makeHtml(data);
|
||||
transData = HtmlToJson.html2json(html, bindName);
|
||||
console.log(JSON.stringify(transData, ' ', ' '));
|
||||
}
|
||||
transData.view = {};
|
||||
transData.view.imagePadding = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Taro, { Component } from '@tarojs/taro'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { AtForm, AtInput, AtButton } from 'taro-ui'
|
||||
import { SpToast, SpIconMenu } from '@/components'
|
||||
import { SpToast } from '@/components'
|
||||
|
||||
import S from '@/spx'
|
||||
import api from '@/api'
|
||||
@@ -38,6 +38,7 @@ export default class Login extends Component {
|
||||
...this.state.info,
|
||||
...value
|
||||
}
|
||||
|
||||
if (!data.username || !/1\d{10}/.test(data.username)) {
|
||||
return S.toast('请输入正确的手机号')
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ import { View, Text, Image, ScrollView, Switch } from '@tarojs/components'
|
||||
import { connect } from '@tarojs/redux'
|
||||
import { AtButton, AtInput, AtActionSheet, AtActionSheetItem } from 'taro-ui'
|
||||
import { Loading, Price, SpCell, AddressPicker, SpToast } from '@/components'
|
||||
import qs from 'qs'
|
||||
import api from '@/api'
|
||||
import S from '@/spx'
|
||||
import { pickBy, navigateTo } from '@/utils'
|
||||
import { pickBy } from '@/utils'
|
||||
import { lockScreen } from '@/utils/dom'
|
||||
import CheckoutItems from './checkout-items'
|
||||
|
||||
@@ -16,7 +15,8 @@ import './espier-checkout.scss'
|
||||
coupon: cart.coupon,
|
||||
fastbuy: cart.fastbuy
|
||||
}), (dispatch) => ({
|
||||
onClearFastbuy: () => dispatch({ type: 'cart/clearFastbuy' })
|
||||
onClearFastbuy: () => dispatch({ type: 'cart/clearFastbuy' }),
|
||||
onClearCart: () => dispatch({ type: 'cart/clear' })
|
||||
}))
|
||||
export default class CartCheckout extends Component {
|
||||
constructor (props) {
|
||||
@@ -79,7 +79,8 @@ export default class CartCheckout extends Component {
|
||||
receipt_type: 'logistics',
|
||||
order_type: 'normal',
|
||||
promotion: 'normal',
|
||||
member_discount: false
|
||||
member_discount: false,
|
||||
pay_type: 'deposit'
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -223,9 +224,9 @@ export default class CartCheckout extends Component {
|
||||
return S.notify('请选择地址')
|
||||
}
|
||||
|
||||
const data = await api.trade.create(this.params)
|
||||
console.log(data)
|
||||
const url = `/pages/cashier/index`
|
||||
const { order_id } = await api.trade.create(this.params)
|
||||
const url = `/pages/cashier/index?order_id=${order_id}`
|
||||
this.props.onClearCart()
|
||||
Taro.navigateTo({ url })
|
||||
}
|
||||
|
||||
@@ -420,14 +421,6 @@ export default class CartCheckout extends Component {
|
||||
onClickBack={this.toggleCheckoutItems.bind(this, false)}
|
||||
/>
|
||||
|
||||
<AtActionSheet
|
||||
isOpened={showShippingPicker}
|
||||
onClose={this.toggleState.bind(this, 'showShippingPicker', false)}
|
||||
>
|
||||
<AtActionSheetItem onClick={this.handleShippingChange}>顺丰</AtActionSheetItem>
|
||||
<AtActionSheetItem onClick={this.handleShippingChange}>自提</AtActionSheetItem>
|
||||
</AtActionSheet>
|
||||
|
||||
<View className='toolbar checkout-toolbar'>
|
||||
<View className='checkout__total'>
|
||||
共<Text className='total-items'>{total.items_count}</Text>件,合计: <Price primary unit='cent' value={total.total_fee} />
|
||||
|
||||
240
src/pages/cart/espier-index.js
Normal file
240
src/pages/cart/espier-index.js
Normal file
@@ -0,0 +1,240 @@
|
||||
import Taro, { Component } from '@tarojs/taro'
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { connect } from '@tarojs/redux'
|
||||
import { AtInputNumber, AtButton } from 'taro-ui'
|
||||
import { GoodsItem, SpCheckbox, SpNote, Loading, Price } from '@/components'
|
||||
import { log, navigateTo, pickBy } from '@/utils'
|
||||
// import api from '@/api'
|
||||
import { withLogin } from '@/hocs'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
@connect(({ cart }) => ({
|
||||
list: cart.list
|
||||
}), (dispatch) => ({
|
||||
onCartUpdate: (item, num) => dispatch({ type: 'cart/update', payload: { item, num } }),
|
||||
onCartDel: (item) => dispatch({ type: 'cart/delete', payload: item })
|
||||
}))
|
||||
@withLogin()
|
||||
export default class CartIndex extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
selection: new Set(),
|
||||
totalPrice: '0.00',
|
||||
items: null,
|
||||
cartMode: 'default'
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
}
|
||||
|
||||
componentDidShow () {
|
||||
// this.fetch()
|
||||
}
|
||||
|
||||
async fetch () {
|
||||
// const { cartlist } = await api.cart.get()
|
||||
// // const list = resolveCartItems(cartlist)
|
||||
// // const items = normalizeItems(list)
|
||||
|
||||
// const list = []
|
||||
// const items = []
|
||||
|
||||
// this.setState({
|
||||
// list,
|
||||
// items
|
||||
// })
|
||||
}
|
||||
|
||||
get isTotalChecked () {
|
||||
return this.props.list.length === this.state.selection.size
|
||||
}
|
||||
|
||||
toggleCartMode = () => {
|
||||
const cartMode = this.state.cartMode !== 'edit' ? 'edit' : 'default'
|
||||
this.setState({
|
||||
cartMode
|
||||
})
|
||||
}
|
||||
|
||||
handleSelectionChange (item_id, checked) {
|
||||
this.state.selection[checked ? 'add' : 'delete'](item_id)
|
||||
const selection = new Set(this.state.selection)
|
||||
|
||||
this.setState({
|
||||
selection
|
||||
})
|
||||
|
||||
log.debug(`[cart change] item: ${item_id}, selection: ${selection}`)
|
||||
}
|
||||
|
||||
handleQuantityChange (idx, quantity) {
|
||||
const item = this.props.list[idx]
|
||||
this.props.onCartUpdate(item, quantity)
|
||||
}
|
||||
|
||||
handleAllSelect = (checked) => {
|
||||
const { items, selection } = this.state
|
||||
if (checked) {
|
||||
for (let item of items.values()) {
|
||||
selection.add(item.item_id)
|
||||
}
|
||||
} else {
|
||||
selection.clear()
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selection
|
||||
})
|
||||
}
|
||||
|
||||
handleDelect = () => {
|
||||
const selection = [...this.state.selection]
|
||||
selection.forEach(item_id => {
|
||||
this.props.onCartDel({ item_id })
|
||||
})
|
||||
}
|
||||
|
||||
handleCheckout = () => {
|
||||
Taro.navigateTo({
|
||||
url: '/pages/cart/checkout'
|
||||
})
|
||||
|
||||
this.setState({
|
||||
selection: new Set()
|
||||
})
|
||||
}
|
||||
|
||||
transformCartList (list) {
|
||||
return pickBy(list, {
|
||||
item_id: 'item_id',
|
||||
title: 'item_name',
|
||||
desc: 'brief',
|
||||
curSymbol: 'cur.symbol',
|
||||
img: ({ pics }) => pics[0],
|
||||
price: ({ price }) => (+price / 100).toFixed(2),
|
||||
market_price: ({ market_price }) => (+market_price / 100).toFixed(2),
|
||||
quantity: 'num'
|
||||
})
|
||||
}
|
||||
|
||||
navigateTo = navigateTo
|
||||
|
||||
render () {
|
||||
const { list } = this.props
|
||||
const { selection, totalPrice, cartMode } = this.state
|
||||
|
||||
if (!list) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
const totalSelection = selection.size
|
||||
const totalItems = totalSelection
|
||||
const isEmpty = !list.length
|
||||
|
||||
const cartList = this.transformCartList(list)
|
||||
|
||||
return (
|
||||
<View className='page-cart-index'>
|
||||
<ScrollView
|
||||
className='cart-list__wrap'
|
||||
scrollY
|
||||
>
|
||||
{
|
||||
!isEmpty && (
|
||||
<View className='cart-list__actions'>
|
||||
<Text
|
||||
clasName='btn-cart-mode'
|
||||
onClick={this.toggleCartMode}
|
||||
>{cartMode === 'edit' ? '完成' : '编辑'}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
<View className='cart-list'>
|
||||
<View className='cart-group__shop'>
|
||||
{
|
||||
cartList.map((item, idx) => {
|
||||
return (
|
||||
<GoodsItem
|
||||
key={item.item_id}
|
||||
info={item}
|
||||
renderFooter={
|
||||
<block>
|
||||
<AtInputNumber
|
||||
min={1}
|
||||
max={999999}
|
||||
value={item.quantity}
|
||||
onChange={this.handleQuantityChange.bind(this, idx)}
|
||||
/>
|
||||
</block>
|
||||
}
|
||||
>
|
||||
<SpCheckbox
|
||||
key={item.item_id}
|
||||
checked={selection.has(item.item_id)}
|
||||
onChange={this.handleSelectionChange.bind(this, item.item_id)}
|
||||
/>
|
||||
</GoodsItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
</View>
|
||||
{
|
||||
!list.length && (
|
||||
<View>
|
||||
<SpNote customStyle='margin-bottom: 20px' img='cart_empty.png'>快去给我挑点宝贝吧~</SpNote>
|
||||
<AtButton
|
||||
circle
|
||||
type='primary'
|
||||
onClick={this.navigateTo.bind(this, '/pages/home/index', true)}
|
||||
>随便逛逛</AtButton>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<View
|
||||
className={`toolbar cart-toolbar ${isEmpty && 'hidden'}`}
|
||||
>
|
||||
<View className='cart-toolbar__hd'>
|
||||
<SpCheckbox
|
||||
checked={this.isTotalChecked}
|
||||
onChange={this.handleAllSelect}
|
||||
>全选</SpCheckbox>
|
||||
</View>
|
||||
{
|
||||
cartMode !== 'edit'
|
||||
? <View className='cart-toolbar__bd'>
|
||||
<Text className='cart-total__hint'>不含运费</Text>
|
||||
<View className='cart-total'>
|
||||
<Price
|
||||
primary
|
||||
value={totalPrice}
|
||||
/>
|
||||
</View>
|
||||
<AtButton
|
||||
circle
|
||||
type='primary'
|
||||
className='btn-checkout'
|
||||
disabled={totalItems <= 0}
|
||||
onClick={this.handleCheckout}
|
||||
>结算({totalItems})</AtButton>
|
||||
</View>
|
||||
: <View className='cart-toolbar__bd'>
|
||||
<AtButton
|
||||
circle
|
||||
type='primary'
|
||||
className='btn-checkout'
|
||||
onClick={this.handleDelect}
|
||||
>删除</AtButton>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@
|
||||
.goods-item {
|
||||
position: relative;
|
||||
}
|
||||
.sp-checkbox {
|
||||
margin-right: 24px;
|
||||
margin-left: -8px;
|
||||
.sp-checkbox__wrap {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.at-input-number {
|
||||
transform: scale(0.8);
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class Index extends Component {
|
||||
componentWillMount () {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
Taro.redirectTo({
|
||||
url: '/pages/item/espier-detail?id=270'
|
||||
url: '/pages/item/espier-detail?id=268'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class GoodsBuyToolbar extends Component {
|
||||
</View>
|
||||
<View
|
||||
className='goods-buy-toolbar__menu-item'
|
||||
onClick={this.navigateTo.bind(this, '/pages/cart/index')}
|
||||
onClick={this.navigateTo.bind(this, '/pages/cart/espier-index')}
|
||||
>
|
||||
<AtBadge
|
||||
value={2}
|
||||
|
||||
@@ -14,7 +14,8 @@ import './espier-detail.scss'
|
||||
@connect(({ cart }) => ({
|
||||
cart
|
||||
}), (dispatch) => ({
|
||||
onFastbuy: (item) => dispatch({ type: 'cart/fastbuy', payload: { item } })
|
||||
onFastbuy: (item) => dispatch({ type: 'cart/fastbuy', payload: { item } }),
|
||||
onAddCart: (item) => dispatch({ type: 'cart/add', payload: { item } })
|
||||
}))
|
||||
@withBackToTop
|
||||
export default class Detail extends Component {
|
||||
@@ -123,15 +124,12 @@ export default class Detail extends Component {
|
||||
}
|
||||
|
||||
if (type === 'cart') {
|
||||
const data = await api.cart.add(info)
|
||||
url = `/pages/cart/espier-index`
|
||||
|
||||
if (data.cart_id) {
|
||||
return Taro.navigateTo({
|
||||
url
|
||||
})
|
||||
} else {
|
||||
S.toast('加入购物车失败,请稍后再试')
|
||||
}
|
||||
this.props.onAddCart(info)
|
||||
return Taro.navigateTo({
|
||||
url
|
||||
})
|
||||
}
|
||||
|
||||
if (type === 'fastbuy') {
|
||||
|
||||
@@ -3,7 +3,8 @@ import api from '@/api'
|
||||
import { getCurrentRoute, log } from '@/utils'
|
||||
|
||||
const globalData = {}
|
||||
const TOKEN_IDENTIFIER = 'authToken'
|
||||
const TOKEN_IDENTIFIER = 'auth_token'
|
||||
const TOKEN_TIMESTAMP = 'refresh_token_time'
|
||||
|
||||
function remove (arr, item) {
|
||||
const idx = arr.indexOf(item)
|
||||
@@ -23,8 +24,16 @@ function isAsync (func) {
|
||||
}
|
||||
|
||||
class Spx {
|
||||
constructor () {
|
||||
constructor (options = {}) {
|
||||
this.hooks = []
|
||||
this.options = {
|
||||
autoRefreshToken: true,
|
||||
...options
|
||||
}
|
||||
|
||||
if (this.options.autoRefreshToken) {
|
||||
this.startRefreshToken()
|
||||
}
|
||||
}
|
||||
|
||||
getAuthToken () {
|
||||
@@ -38,6 +47,25 @@ class Spx {
|
||||
setAuthToken (token) {
|
||||
this.set(TOKEN_IDENTIFIER, token)
|
||||
Taro.setStorageSync(TOKEN_IDENTIFIER, token)
|
||||
Taro.setStorageSync(TOKEN_TIMESTAMP, Date.now() + 55 * 60 * 1000)
|
||||
}
|
||||
|
||||
startRefreshToken () {
|
||||
if (this._refreshTokenTimer) {
|
||||
clearTimeout(this._refreshTokenTimer)
|
||||
}
|
||||
const checkAndRefresh = async () => {
|
||||
const expired = Taro.getStorageSync(TOKEN_TIMESTAMP)
|
||||
if (!expired) return
|
||||
const delta = expired - Date.now()
|
||||
if (delta > 0 && delta <= 5 * 60 * 1000) {
|
||||
const { token } = await api.user.refreshToken()
|
||||
clearTimeout(this._refreshTokenTimer)
|
||||
this.setAuthToken(token)
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(checkAndRefresh, 5 * 60 * 1000)
|
||||
}
|
||||
|
||||
async getUserInfo () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createReducer } from 'redux-create-reducer'
|
||||
import dotProp from 'dot-prop-immutable'
|
||||
|
||||
const initState = {
|
||||
list: [],
|
||||
@@ -7,15 +8,6 @@ const initState = {
|
||||
}
|
||||
|
||||
const cart = createReducer(initState, {
|
||||
['cart/update'](state, action) {
|
||||
const { list } = action.payload
|
||||
|
||||
return {
|
||||
...state,
|
||||
list
|
||||
}
|
||||
},
|
||||
|
||||
['cart/checkout'](state, action) {
|
||||
const checkoutItem = action.payload
|
||||
|
||||
@@ -24,7 +16,6 @@ const cart = createReducer(initState, {
|
||||
checkoutItem
|
||||
}
|
||||
},
|
||||
|
||||
['cart/fastbuy'](state, action) {
|
||||
const { item , num = 1 } = action.payload
|
||||
|
||||
@@ -36,14 +27,56 @@ const cart = createReducer(initState, {
|
||||
}
|
||||
}
|
||||
},
|
||||
['cart/add'](state, action) {
|
||||
const { item, num = 1 } = action.payload
|
||||
const idx = state.list.findIndex(t => item.item_id === t.item_id)
|
||||
let list
|
||||
|
||||
if (idx >= 0) {
|
||||
list = dotProp.set(state.list, `${idx}`, { ...item, num: state.list[idx].num + num })
|
||||
} else {
|
||||
list = [...state.list, { ...item, num }]
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
list
|
||||
}
|
||||
},
|
||||
['cart/update'](state, action) {
|
||||
const { item, num } = action.payload
|
||||
const idx = state.list.findIndex(t => item.item_id === t.item_id)
|
||||
let list
|
||||
|
||||
if (idx >= 0) {
|
||||
list = dotProp.set(state.list, `${idx}`, { ...item, num })
|
||||
} else {
|
||||
list = [...state.list, { ...item, num }]
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
list
|
||||
}
|
||||
},
|
||||
['cart/delete'](state, action) {
|
||||
const { item_id } = action.payload
|
||||
const idx = state.list.findIndex(t => t.item_id === item_id)
|
||||
|
||||
return dotProp.delete(state, `list.${idx}`)
|
||||
},
|
||||
['cart/clearFastbuy'](state) {
|
||||
return {
|
||||
...state,
|
||||
fastbuy: null
|
||||
}
|
||||
},
|
||||
|
||||
['cart/clear'](state) {
|
||||
return {
|
||||
...state,
|
||||
...initState
|
||||
}
|
||||
},
|
||||
['cart/changeCoupon'](state, action) {
|
||||
const coupon = action.payload
|
||||
|
||||
|
||||
Reference in New Issue
Block a user