订单列表/详情

This commit is contained in:
bentz
2019-02-26 21:15:46 +08:00
parent 6d26096eaa
commit fafb608c06
18 changed files with 551 additions and 80 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ dist/
.rn_temp/
node_modules/
.DS_Store
.idea

15
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{
"name": "mshop-taro",
"name": "espier-taro-fenxiao",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
@@ -2777,6 +2777,14 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
"copy-to-clipboard": {
"version": "3.0.8",
"resolved": "http://registry.npm.taobao.org/copy-to-clipboard/download/copy-to-clipboard-3.0.8.tgz",
"integrity": "sha1-9OgvSogw3ORma3643tDJvMMTq6k=",
"requires": {
"toggle-selection": "^1.0.3"
}
},
"core-js": {
"version": "2.6.5",
"resolved": "https://r.cnpmjs.org/core-js/download/core-js-2.6.5.tgz",
@@ -9574,6 +9582,11 @@
"repeat-string": "^1.6.1"
}
},
"toggle-selection": {
"version": "1.0.6",
"resolved": "http://registry.npm.taobao.org/toggle-selection/download/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"toposort": {
"version": "1.0.7",
"resolved": "https://r.cnpmjs.org/toposort/download/toposort-1.0.7.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "mshop-taro",
"app_name": "我的商铺",
"name": "espier-taro-fenxiao",
"app_name": "源源客h5",
"version": "1.0.0",
"private": true,
"description": "taro app for bbc",
@@ -34,6 +34,7 @@
"@tarojs/taro-weapp": "1.2.13",
"axios": "^0.18.0",
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.0.8",
"lodash-es": "^4.17.11",
"memoize-one": "^5.0.0",
"moment": "^2.24.0",

View File

@@ -25,7 +25,8 @@ class App extends Component {
'pages/member/index',
'pages/member/favorite',
'pages/trade/list'
'pages/trade/list',
'pages/trade/detail'
],
window: {
backgroundTextStyle: 'light',

View File

@@ -77,6 +77,7 @@ page, body {
.toolbar {
background: #fff;
font-size: $font-size;
display: flex;
height: $toolbar-height;
box-shadow: $toolbar-shadow;
@@ -90,6 +91,9 @@ page, body {
align-content: center;
justify-content: space-between;
z-index: $z-index-toolbar;
.at-button {
font-size: $font-size;
}
&-hd {}
&-ft {
max-width: 120px;

View File

@@ -2,7 +2,7 @@ export default function withPager (Component) {
return class WithPagerComponent extends Component {
constructor (props) {
super(props)
const { pageSize = 10, pageNo = 1, pageTotal = 0 } = props
const { pageSize = 10, pageNo = 0, pageTotal = 0 } = props || {}
const page = {
hasNext: true,

View File

@@ -10,7 +10,7 @@ export default class Index extends Component {
componentWillMount () {
Taro.redirectTo({
url: `/pages/trade/list`
url: `/pages/trade/detail?id=123`
})
}

View File

@@ -1,5 +1,8 @@
import Taro, { Component } from '@tarojs/taro'
import { View, Text, Image } from '@tarojs/components'
import { AtButton } from 'taro-ui'
import { Price } from '@/components'
import { OrderItem } from './order-item'
import './item.scss'
@@ -9,48 +12,85 @@ export default class TradeItem extends Component {
}
static defaultProps = {
customHeader: false
customHeader: false,
customFooter: false,
customRender: false,
onClickBtn: () => {},
onClick: () => {}
}
handleClickBtn (type) {
const { info } = this.props
this.props.onClickBtn && this.props.onClickBtn(type, info)
}
render () {
const { customHeader, info } = this.props
const { customHeader, customFooter, onClick, info } = this.props
return (
<View className='trade-item'>
{
customHeader
? <View className='trade-item__hd'>
<Text className='trade-item__shop'>丰收蟹旗舰店</Text><Text className='more'></Text><Text calssName='more'>{info.status_desc}</Text>
? <View className='trade-item__hd'>{this.props.renderHeader}</View>
: <View className='trade-item__hd'>
<Text className='trade-item__shop'>{info.shopname}</Text><Text className='more'>{info.status_desc}</Text>
</View>
: this.props.renderHeader
}
<View className='trade-item__bd'>
<View
className='trade-item__bd'
onClick={onClick}
>
{
info.order.map((item, idx) => {
return (
<View
key={idx}
className='goods-item'
>
<View className='goods-item__hd'>
<Image
mode='aspectFill'
className='goods-item__img'
src={item.pic_path}
/>
</View>
<View className='goods-item__bd'>
<Text className='goods-item__title'>{item.title}</Text>
<Text className='goods-item__desc'>{item.goods_props}</Text>
</View>
<View className='goods-item__ft'>
<price className='goods-item__price' value={item.image_default_id}></price>
</View>
info.order.map((item, idx) =>
<OrderItem
key={idx}
info={item}
/>
)
}
{
this.props.customRender
? this.props.customRender
: <View className="trade-item__total">
{info.totalItem}件商品 合计:<Price value={info.payment} />
</View>
)
})
}
</View>
{customFooter && <View className='trade-item__ft'>{this.props.renderFooter}</View>}
{!customFooter && info.status === 'WAIT_BUYER_PAY' && <View className='trade-item__ft'>
<AtButton
circle
size='small'
onClick={this.handleClickBtn.bind(this, 'cancel')}
>取消订单</AtButton>
<AtButton
circle
type='secondary'
size='small'
onClick={this.handleClickBtn.bind(this, 'pay')}
>立即支付</AtButton>
</View>}
{!customFooter && info.status === 'WAIT_BUYER_CONFIRM_GOODS' && <View className='trade-item__ft'>
<AtButton
circle
size='small'
onClick={this.handleClickBtn.bind(this, 'express')}
>查看物流</AtButton>
<AtButton
circle
type='secondary'
size='small'
onClick={this.handleClickBtn.bind(this, 'confirm')}
>确认收货</AtButton>
</View>}
{!customFooter && info.status === 'WAIT_RATE' && <View className='trade-item__ft'>
<AtButton
circle
type='secondary'
size='small'
onClick={this.handleClickBtn.bind(this, 'rate')}
>评价</AtButton>
</View>}
</View>
)
}

View File

@@ -3,6 +3,7 @@
.trade-item {
background: #fff;
border-radius: $item-border-radius;
margin: 0 0 $edge-margin;
&__hd {
padding: 28px $edge-size;
line-height: 1;
@@ -15,4 +16,15 @@
text-align: right;
}
}
&__total {
padding: 0 $edge-size floor($edge-size/2);
text-align: right;
}
&__ft {
display: flex;
justify-content: flex-end;
padding: floor($edge-size/2) $edge-size;
}
}

View File

@@ -0,0 +1,38 @@
import Taro, { Component } from '@tarojs/taro'
import { View, Text, Image } from '@tarojs/components'
import { Price } from '@/components'
import './order-item.scss'
export default class OrderItem extends Component {
static defaultProps = {
onClick: () => {}
}
render () {
const { info, onClick } = this.props
return (
<View
className='order-item'
onClick={onClick}
>
<View className='order-item__hd'>
<Image
mode='aspectFill'
className='order-item__img'
src={info.pic_path}
/>
</View>
<View className='order-item__bd'>
<Text className='order-item__title'>{info.title}</Text>
<Text className='order-item__desc'>{info.goods_props}</Text>
</View>
<View className='order-item__ft'>
<Price className='order-item__price' value={info.price}></Price>
<Text className="order-item__num">x {info.num}</Text>
</View>
</View>
)
}
}

View File

@@ -0,0 +1,39 @@
@import "../../../style/imports";
.order-item {
display: flex;
align-items: flex-start;
padding: $edge-size;
&__img {
width: 160px;
height: 160px;
margin-right: 18px
}
&__hd {
}
&__bd {
width: 50%;
}
&__ft {
flex: 1;
text-align: right;
font-size: $font-size-small;
}
&__title {
font-size: floor(0.95 * $font-size);
@include mult-ellipsis(2);
line-height: 1.4;
margin: 0 0 4px;
}
&__desc {
color: $color-gray;
font-size: $font-size-small;
}
&__price {
color: $color-primary-text;
}
&__num {
display: block;
color: $color-gray;
}
}

178
src/pages/trade/detail.js Normal file
View File

@@ -0,0 +1,178 @@
import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { AtButton } from 'taro-ui'
import { Loading, SpCell, SpToast, GoodsItem, Price } from '@/components'
import { classNames, formatTime, copyText } from '@/utils'
import api from '@/api'
import S from '@/spx'
import OrderItem from './comps/order-item'
import './detail.scss'
function resolveTradeOrders (info) {
return info.orders.map(order => {
const { item_id, title, pic_path: img, total_fee: price, num } = order
return {
item_id,
title,
img,
price,
num
}
})
}
export default class TradeDetail extends Component {
constructor (props) {
super(props)
this.state = {
info: null
}
}
componentDidMount () {
this.fetch()
}
async fetch () {
const { id } = this.$router.params
const info = await api.trade.detail(id)
info.created_time_str = formatTime(info.created_time, 'YYYY-MM-DD hh:mm:ss')
this.setState({
info
})
}
handleCopy = async () => {
const { info } = this.state
const msg = `收货人:${info.receiver_name} ${info.receiver_mobile}
收货地址:${info.receiver_state}${info.receiver_city}${info.receiver_district}${info.receiver_address}
订单号:${info.tid}
创建时间:${info.created_time_str}
`
await copyText(msg)
S.toast('已复制信息')
}
async handleClickBtn (type) {
console.log(type)
}
render () {
const { info } = this.state
if (!info) {
return <Loading></Loading>
}
// TODO: orders 多商铺
const tradeOrders = resolveTradeOrders(info)
return (
<View className={classNames('trade-detail', `trade-detail__status-${info.status}`)}>
<View className='trade-detail__status'>
<Text className='trade-detail__status-text'>等待付款</Text>
</View>
<View className='trade-detail__addr'>
<SpCell
isLink
icon='map-pin'
>
{
<View className='address-info__bd'>
<Text className='address-info__receiver'>
收货人{info.receiver_name} {info.receiver_mobile}
</Text>
<Text className='address-info__addr'>
收货地址{info.receiver_state}{info.receiver_city}{info.receiver_district}{info.receiver_address}
</Text>
</View>
}
</SpCell>
<View className='trade-detail__express'>配送方式{info.shipping_type_name}</View>
</View>
<View className='sec sec-orders'>
<View className='sec-orders__hd'>
<Text>丰收蟹旗舰店</Text>
</View>
<View className='sec-orders__bd'>
{
info.orders.map((item, idx) => {
return (
<OrderItem
key={idx}
info={item}
/>
)
})
}
<View className='trade-detail__total'>
<SpCell
className='trade-detail__total-item'
title='运费'
border={false}
>
<Price value={info.post_fee} />
</SpCell>
<SpCell
className='trade-detail__total-item'
title='积分'
border={false}
>
<Price value={`-${info.points_fee}`} />
</SpCell>
<SpCell
className='trade-detail__total-item'
title='实付款(含运费)'
border={false}
>
<Price primary value={info.payment} />
</SpCell>
</View>
</View>
</View>
<View className='sec trade-extra'>
<View className='trade-extra__bd'>
<Text>订单编号{info.tid}</Text>
<Text>创建时间{info.created_time_str}</Text>
</View>
<View className='trade-extra__ft'>
<AtButton size='small' onClick={this.handleCopy}>复制</AtButton>
</View>
</View>
{info.status === 'WAIT_BUYER_PAY' && (<View className='toolbar toolbar-actions'>
<AtButton
circle
onClick={this.handleClickBtn.bind(this, 'cancel')
}>取消订单</AtButton>
<AtButton
circle
type='secondary'
onClick={this.handleClickBtn.bind(this, 'pay')
}>立即支付</AtButton>
</View>)}
{info.status === 'WAIT_BUYER_CONFIRM_GOODS' && (<View className='toolbar toolbar-actions'>
<AtButton
circle
type='secondary'
onClick={this.handleClickBtn.bind(this, 'confirm')
}>确认收货</AtButton>
</View>)}
{info.status === 'WAIT_RATE' && (<View className='toolbar toolbar-actions'>
<AtButton
circle
type='secondary'
onClick={this.handleClickBtn.bind(this, 'rate')
}>评价</AtButton>
</View>)}
<SpToast></SpToast>
</View>
)
}
}

View File

@@ -0,0 +1,85 @@
@import "../../style/imports";
.trade-detail {
padding-bottom: $toolbar-height + $edge-margin;
&__status {
background: linear-gradient(227deg,rgba(250,167,97,1) 0%,rgba(250,53,21,1) 100%);
position: relative;
height: 160px;
display: flex;
align-items: center;
padding: 0 80px;
&-text {
color: #fff;
}
}
&__addr {
background: #fff;
margin: 0 0 $edge-margin;
}
&__express {
padding: $edge-margin $edge-size $edge-margin ($edge-size + 40px);
}
&__total {
padding: 0 0 $edge-size;
.sp-cell {
padding: 8px $edge-size;
display: flex;
align-items: space-between;
&__bd {
text-align: right;
}
}
}
.address-info {
margin: 0 0 $edge-margin;
&__receiver {
color: #111;
display: block;
font-size: $font-size-large;
margin: 0 0 16px;
line-height: 1;
}
&__addr {
color: #111;
line-height: 1;
display: block;
}
}
.sec-orders {
&__hd {
padding: $edge-margin $edge-size;
}
}
.trade-extra {
display: flex;
align-items: space-between;
padding: $edge-margin $edge-size;
&__bd {
flex: 1;
display: flex;
flex-direction: column;
font-size: $font-size-small;
}
&__ft {
.at-button {
transform: scale(0.8);
transform-origin: center right;
}
}
}
.toolbar-actions {
display: flex;
justify-content: flex-end;
.at-button {
margin: 0 0 0 20px;
}
}
}

View File

@@ -1,14 +1,18 @@
import Taro, { Component } from '@tarojs/taro'
import { View, Text, ScrollView } from '@tarojs/components'
import { AtTabs, AtTabsPane } from 'taro-ui'
import { Loading } from '@/components'
import api from '@/api'
import { withPager } from '@/hocs'
import { TradeItem } from './comps/item'
import './list.scss'
@withPager
export default class TradeList extends Component {
constructor (props) {
super(props)
this.state = {
...this.state,
curTabIdx: 0,
@@ -19,41 +23,67 @@ export default class TradeList extends Component {
{title: '待收货'},
{title: '待评价'}
],
initedTabs: {},
lists: []
list: [],
isLoading: false
}
}
componentDidMount () {
this.fetch({ tab: 0 })
this.nextPage()
}
async fetch (params) {
const { tab } = params
const { list } = await api.trade.list({ status: tab })
this.setState({ list })
this.setState({ isLoading: true })
const { curTabIdx } = this.state
const { list, total_count: total } = await api.trade.list({ status: curTabIdx, ...params })
const nList = this.state.list.concat(list)
this.setState({
list: nList,
isLoading: false
})
return { total }
}
onClickTab = (idx) => {
handleClickTab = (idx) => {
if (this.state.isLoading || this.state.page.isLoading) return
if (idx !== this.state.curTabIdx) {
this.resetPage()
this.setState({
list: []
})
}
this.setState({
curTabIdx: idx
}, () => {
this.nextPage()
})
console.log(idx)
if (this.state.lists[idx] === undefined) {
this.fetch({ tab: idx })
}
}
handleClickItem (trade) {
const { tid } = trade
Taro.navigateTo({
url: `/pages/trade/detail?id=${tid}`
})
}
handleClickItemBtn = (type, trade) => {
console.log(type, trade)
}
render () {
const { curTabIdx, tabList, lists } = this.state
const list = lists[curTabIdx]
const { curTabIdx, tabList, list, page } = this.state
return (
<View className='trade-list'>
<AtTabs
className='trade-list__tabs'
current={curTabIdx}
tabList={tabList}
onClick={this.onClickTab}
onClick={this.handleClickTab}
>
{
tabList.map((panes, pIdx) =>
@@ -62,22 +92,32 @@ export default class TradeList extends Component {
key={pIdx}
index={pIdx}
>
<View
className='trade-list__scroll'
>
{list.map((item, idx) => {
return (
<TradeItem
key={idx}
info={item}
></TradeItem>
)
})}
</View>
</AtTabsPane>)
)
}
</AtTabs>
<ScrollView
scrollY
className='trade-list__scroll'
onScrollToLower={this.nextPage}
>
{
list.map((item, idx) => {
return (
<TradeItem
key={idx}
info={item}
onClick={this.handleClickItem}
onClickBtn={this.handleClickItemBtn}
/>
)
})
}
{
page.isLoading && <Loading>正在加载...</Loading>
}
</ScrollView>
</View>
)
}

View File

@@ -1,7 +1,30 @@
@import "../../style/imports";
.trade-list {
.at-tabs__header {
position: absolute;
top: 0;
width: 100%;
z-index: $z-index-toolbar;
}
&__scroll {
padding-top: $tabbar-height $edge-size $edge-margin;
height: 100vh;
padding: $tabbar-height 0 $edge-margin;
box-sizing: border-box;
}
&__tabs {
position: fixed;
left: 0;
top: 0;
width: 100%;
}
.trade-item {
margin: 0 $edge-size $edge-margin;
.at-button {
font-size: $font-size-small;
padding-left: 26px;
padding-right: 26px;
margin-left: 20px;
}
}
}

View File

@@ -10,7 +10,7 @@ $edge-margin : 5px * 4 !default;
$toolbar-height : 120px !default;
$toolbar-shadow : 0 -2px 3px rgba(#000, 0.05);
$tabbar-height : 120px !default;
$tabbar-height : 100px !default;
$color-weixin : #07C15F !default;
$item-border-radius : 20px;

View File

@@ -1,18 +0,0 @@
@import "../../style/imports";
.trade-item {
background: #fff;
border-radius: $item-border-radius;
&__hd {
padding: 28px $edge-size;
line-height: 1;
display: flex;
align-items: center;
justify-content: space-between;
.more {
color: $color-link;
font-size: small;
text-align: right;
}
}
}

View File

@@ -3,6 +3,7 @@ import styleNames from 'stylenames'
import Taro from '@tarojs/taro'
import qs from 'qs'
import moment from 'moment'
import copy from 'copy-to-clipboard'
export { default as log } from './log'
@@ -67,6 +68,19 @@ export function formatTime (time, formatter = 'YYYY-MM-DD') {
return moment(time).format(formatter)
}
export function copyText (text) {
return new Promise((resolve, reject) => {
if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) {
Taro.setClipboardData({
data: text,
success: resolve
})
} else if (copy(Text)) {
resolve(text)
}
})
}
export {
classNames,
styleNames