Merge branch 'feat/liujie'
This commit is contained in:
@@ -2,10 +2,12 @@ import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } f
|
|||||||
import { View, Text, Button, Swiper, SwiperItem, Image, Map, ScrollView } from '@tarojs/components'
|
import { View, Text, Button, Swiper, SwiperItem, Image, Map, ScrollView } from '@tarojs/components'
|
||||||
import { Cell, Avatar, Progress, Popover } from '@nutui/nutui-react-taro'
|
import { Cell, Avatar, Progress, Popover } from '@nutui/nutui-react-taro'
|
||||||
import Taro, { useRouter, useShareAppMessage, useShareTimeline, useDidShow } from '@tarojs/taro'
|
import Taro, { useRouter, useShareAppMessage, useShareTimeline, useDidShow } from '@tarojs/taro'
|
||||||
|
import dayjs, { locale } from 'dayjs'
|
||||||
|
import 'dayjs/locale/zh-cn'
|
||||||
// 导入API服务
|
// 导入API服务
|
||||||
import DetailService from '../../services/detailService'
|
import DetailService, { MATCH_STATUS} from '../../services/detailService'
|
||||||
import { updateUserProfile, get_user_info } from '../../services/loginService'
|
import { updateUserProfile, get_user_info } from '../../services/loginService'
|
||||||
import { getCurrentLocation } from '../../utils/locationUtils'
|
import { getCurrentLocation, calculateDistance } from '../../utils/locationUtils'
|
||||||
import {
|
import {
|
||||||
useUserInfo,
|
useUserInfo,
|
||||||
useUserActions,
|
useUserActions,
|
||||||
@@ -15,6 +17,8 @@ import { getTextColorOnImage } from '../../utils'
|
|||||||
import './index.scss'
|
import './index.scss'
|
||||||
import { CommonPopup } from '@/components'
|
import { CommonPopup } from '@/components'
|
||||||
|
|
||||||
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
const images = [
|
const images = [
|
||||||
'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1a35ebbf-2361-44da-b338-7608561d0b31.png',
|
'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1a35ebbf-2361-44da-b338-7608561d0b31.png',
|
||||||
'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/cf5a82ba-90af-4138-a1b3-9119adcde9e0.png',
|
'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/cf5a82ba-90af-4138-a1b3-9119adcde9e0.png',
|
||||||
@@ -96,10 +100,10 @@ function StickyButton(props) {
|
|||||||
const { handleShare, handleJoinGame, detail } = props
|
const { handleShare, handleJoinGame, detail } = props
|
||||||
const userInfo = useUserInfo()
|
const userInfo = useUserInfo()
|
||||||
const { id } = userInfo
|
const { id } = userInfo
|
||||||
const { publisher_id, status } = detail || {}
|
const { publisher_id, match_status, price } = detail || {}
|
||||||
|
|
||||||
const role = Number(publisher_id) === id ? 'ownner' : 'visitor'
|
const role = Number(publisher_id) === id ? 'ownner' : 'visitor'
|
||||||
console.log(status, role)
|
console.log(match_status, role)
|
||||||
return (
|
return (
|
||||||
<View className="sticky-bottom-bar">
|
<View className="sticky-bottom-bar">
|
||||||
<View className="sticky-bottom-bar-share-and-comment">
|
<View className="sticky-bottom-bar-share-and-comment">
|
||||||
@@ -117,7 +121,110 @@ function StickyButton(props) {
|
|||||||
<Text>🎾</Text>
|
<Text>🎾</Text>
|
||||||
<Text>立即加入</Text>
|
<Text>立即加入</Text>
|
||||||
<View className='game-price'>
|
<View className='game-price'>
|
||||||
<Text>¥ 65</Text>
|
<Text>¥ {price}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 球局信息
|
||||||
|
function GameInfo(props) {
|
||||||
|
const { detail, currentLocation } = props
|
||||||
|
const { latitude, longitude, location, location_name, start_time, end_time } = detail || {}
|
||||||
|
|
||||||
|
const openMap = () => {
|
||||||
|
Taro.openLocation({
|
||||||
|
latitude, // 纬度(必填)
|
||||||
|
longitude, // 经度(必填)
|
||||||
|
name: location_name, // 位置名(可选)
|
||||||
|
address: location, // 地址详情(可选)
|
||||||
|
scale: 15, // 地图缩放级别(1-28)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [c_latitude, c_longitude] = currentLocation
|
||||||
|
const distance = calculateDistance(c_latitude, c_longitude, latitude, longitude) / 1000
|
||||||
|
|
||||||
|
const startTime = dayjs(start_time)
|
||||||
|
const endTime = dayjs(end_time)
|
||||||
|
const game_length = endTime.diff(startTime, 'minutes') / 60
|
||||||
|
|
||||||
|
const startMonth = startTime.format('M')
|
||||||
|
const startDay = startTime.format('D')
|
||||||
|
const theDayOfWeek = startTime.format('dddd')
|
||||||
|
const startDate = `${startMonth}月${startDay}日 ${theDayOfWeek}`
|
||||||
|
const gameRange = `${startTime.format('HH:mm')} - ${endTime.format('HH:mm')}`
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className='detail-page-content-game-info'>
|
||||||
|
{/* Date and Weather */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather'>
|
||||||
|
{/* Calendar and Date time */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-calendar-date'>
|
||||||
|
{/* Calendar */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-calendar-date-calendar'>
|
||||||
|
<View className="month">{startMonth}月</View>
|
||||||
|
<View className="day">{startDay}</View>
|
||||||
|
</View>
|
||||||
|
{/* Date time */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-calendar-date-date'>
|
||||||
|
<View className="date">{startDate}</View>
|
||||||
|
<View className="venue-time">{gameRange} ({game_length}小时)</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* Weather */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-weather'>
|
||||||
|
{/* Weather icon */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-weather-icon'>
|
||||||
|
<Image className="weather-icon" src={img.ICON_WEATHER_SUN} />
|
||||||
|
</View>
|
||||||
|
{/* Weather text and temperature */}
|
||||||
|
<View className='detail-page-content-game-info-date-weather-weather-text-temperature'>
|
||||||
|
<Text>28℃ - 32℃</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* Place */}
|
||||||
|
<View className='detail-page-content-game-info-place'>
|
||||||
|
{/* venue location message */}
|
||||||
|
<View className='location-message'>
|
||||||
|
{/* location icon */}
|
||||||
|
<View className='location-message-icon'>
|
||||||
|
<Image className='location-message-icon-image' src={img.ICON_DETAIL_MAP} />
|
||||||
|
</View>
|
||||||
|
{/* location message */}
|
||||||
|
<View className='location-message-text'>
|
||||||
|
{/* venue name and distance */}
|
||||||
|
<View className='location-message-text-name-distance' onClick={openMap}>
|
||||||
|
<Text>{location_name || '-'}</Text>
|
||||||
|
<Text>·</Text>
|
||||||
|
<Text>{distance.toFixed(1)}km</Text>
|
||||||
|
<Image className='location-message-text-name-distance-arrow' src={img.ICON_DETAIL_ARROW_RIGHT} />
|
||||||
|
</View>
|
||||||
|
{/* venue address */}
|
||||||
|
<View className='location-message-text-address'>
|
||||||
|
<Text>{location || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* venue map */}
|
||||||
|
<View className='location-map'>
|
||||||
|
{longitude && latitude && (
|
||||||
|
<Map
|
||||||
|
className='location-map-map'
|
||||||
|
longitude={latitude}
|
||||||
|
latitude={longitude}
|
||||||
|
markers={[{ id: 1, latitude: longitude, longitude: latitude, iconPath: require('@/static/detail/icon-stark.svg'), width: 16, height: 16 }]}
|
||||||
|
includePoints={[{ latitude: longitude, longitude: latitude }, { latitude: currentLocation[0], longitude: currentLocation[1] }]}
|
||||||
|
includePadding={{ left: 50, right: 50, top: 50, bottom: 50 }}
|
||||||
|
onError={() => {}}
|
||||||
|
// hide business msg
|
||||||
|
showLocation
|
||||||
|
theme='dark'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -133,11 +240,13 @@ function Index() {
|
|||||||
// const [textColor, setTextColor] = useState<string []>([])
|
// const [textColor, setTextColor] = useState<string []>([])
|
||||||
const [detail, setDetail] = useState<any>(null)
|
const [detail, setDetail] = useState<any>(null)
|
||||||
const { params } = useRouter()
|
const { params } = useRouter()
|
||||||
const [currentLocation, setCurrentLocation] = useState([0, 0])
|
const [currentLocation, setCurrentLocation] = useState<[number, number]>([0, 0])
|
||||||
const { id, autoShare, from } = params
|
const { id, autoShare, from } = params
|
||||||
const { fetchUserInfo, updateUserInfo } = useUserActions()
|
const { fetchUserInfo, updateUserInfo } = useUserActions()
|
||||||
|
|
||||||
console.log('from', from)
|
console.group('params')
|
||||||
|
console.log(params)
|
||||||
|
console.groupEnd()
|
||||||
|
|
||||||
// 本地状态管理
|
// 本地状态管理
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@@ -168,7 +277,7 @@ function Index() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchDetail = async () => {
|
const fetchDetail = async () => {
|
||||||
const res = await DetailService.getDetail(242/* Number(id) */)
|
const res = await DetailService.getDetail(243/* Number(id) */)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
console.log(res.data)
|
console.log(res.data)
|
||||||
setDetail(res.data)
|
setDetail(res.data)
|
||||||
@@ -192,19 +301,9 @@ function Index() {
|
|||||||
sharePopupRef.current.show()
|
sharePopupRef.current.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
const openMap = () => {
|
|
||||||
Taro.openLocation({
|
|
||||||
latitude: detail?.longitude, // 纬度(必填)
|
|
||||||
longitude: detail?.latitude, // 经度(必填)
|
|
||||||
name: '上海体育场', // 位置名(可选)
|
|
||||||
address: '上海市徐汇区肇嘉浜路128号', // 地址详情(可选)
|
|
||||||
scale: 15, // 地图缩放级别(1-28)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleJoinGame = () => {
|
const handleJoinGame = () => {
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: `/pages/orderCheck/index?id=${id}`,
|
url: `/pages/orderCheck/index?gameId=${243/* id */}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,76 +463,7 @@ function Index() {
|
|||||||
<Text className='detail-page-content-title-text'>{title}</Text>
|
<Text className='detail-page-content-title-text'>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
{/* Date and Place and weather */}
|
{/* Date and Place and weather */}
|
||||||
<View className='detail-page-content-game-info'>
|
<GameInfo detail={detail} currentLocation={currentLocation} />
|
||||||
{/* Date and Weather */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather'>
|
|
||||||
{/* Calendar and Date time */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-calendar-date'>
|
|
||||||
{/* Calendar */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-calendar-date-calendar'>
|
|
||||||
<View className="month">3月</View>
|
|
||||||
<View className="day">25</View>
|
|
||||||
</View>
|
|
||||||
{/* Date time */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-calendar-date-date'>
|
|
||||||
<View className="date">3月25日 周一</View>
|
|
||||||
<View className="venue-time">19:00-21:00 (2小时)</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
{/* Weather */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-weather'>
|
|
||||||
{/* Weather icon */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-weather-icon'>
|
|
||||||
<Image className="weather-icon" src={img.ICON_WEATHER_SUN} />
|
|
||||||
</View>
|
|
||||||
{/* Weather text and temperature */}
|
|
||||||
<View className='detail-page-content-game-info-date-weather-weather-text-temperature'>
|
|
||||||
<Text>28℃ - 32℃</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
{/* Place */}
|
|
||||||
<View className='detail-page-content-game-info-place'>
|
|
||||||
{/* venue location message */}
|
|
||||||
<View className='location-message'>
|
|
||||||
{/* location icon */}
|
|
||||||
<View className='location-message-icon'>
|
|
||||||
<Image className='location-message-icon-image' src={img.ICON_DETAIL_MAP} />
|
|
||||||
</View>
|
|
||||||
{/* location message */}
|
|
||||||
<View className='location-message-text'>
|
|
||||||
{/* venue name and distance */}
|
|
||||||
<View className='location-message-text-name-distance' onClick={openMap}>
|
|
||||||
<Text>上海体育场</Text>
|
|
||||||
<Text>·</Text>
|
|
||||||
<Text>1.2km</Text>
|
|
||||||
<Image className='location-message-text-name-distance-arrow' src={img.ICON_DETAIL_ARROW_RIGHT} />
|
|
||||||
</View>
|
|
||||||
{/* venue address */}
|
|
||||||
<View className='location-message-text-address'>
|
|
||||||
<Text>上海市徐汇区肇嘉浜路128号</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
{/* venue map */}
|
|
||||||
<View className='location-map'>
|
|
||||||
{longitude && latitude && (
|
|
||||||
<Map
|
|
||||||
className='location-map-map'
|
|
||||||
longitude={latitude}
|
|
||||||
latitude={longitude}
|
|
||||||
markers={[{ id: 1, latitude: longitude, longitude: latitude, iconPath: require('@/static/detail/icon-stark.svg'), width: 16, height: 16 }]}
|
|
||||||
includePoints={[{ latitude: longitude, longitude: latitude }, { latitude: currentLocation[0], longitude: currentLocation[1] }]}
|
|
||||||
includePadding={{ left: 50, right: 50, top: 50, bottom: 50 }}
|
|
||||||
onError={() => {}}
|
|
||||||
// hide business msg
|
|
||||||
showLocation
|
|
||||||
theme='dark'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
{/* detail */}
|
{/* detail */}
|
||||||
<View className='detail-page-content-detail'>
|
<View className='detail-page-content-detail'>
|
||||||
{/* venue detail title and venue ordered status */}
|
{/* venue detail title and venue ordered status */}
|
||||||
|
|||||||
@@ -1,15 +1,41 @@
|
|||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { View, Text, Button } from '@tarojs/components'
|
import { View, Text, Button } from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro, { useDidShow, useRouter } from '@tarojs/taro'
|
||||||
import { delay } from '@/utils'
|
import { delay } from '@/utils'
|
||||||
|
import orderService from '@/services/orderService'
|
||||||
|
import detailService, { GameDetail } from '@/services/detailService'
|
||||||
|
|
||||||
const OrderCheck = () => {
|
const OrderCheck = () => {
|
||||||
|
const { params } = useRouter()
|
||||||
|
const { id, gameId } = params
|
||||||
|
const [detail ,setDetail] = useState<GameDetail | {}>({})
|
||||||
|
|
||||||
|
useDidShow(async () => {
|
||||||
|
const res = await detailService.getDetail(Number(gameId))
|
||||||
|
console.log(res)
|
||||||
|
if (res.code === 0) {
|
||||||
|
setDetail(res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//TODO: get order msg from id
|
||||||
const handlePay = async () => {
|
const handlePay = async () => {
|
||||||
Taro.showLoading({
|
Taro.showLoading({
|
||||||
title: '支付中...',
|
title: '支付中...',
|
||||||
mask: true
|
mask: true
|
||||||
})
|
})
|
||||||
await delay(2000)
|
const res = await orderService.createOrder(Number(gameId))
|
||||||
|
if (res.code === 0) {
|
||||||
|
const { payment_required, payment_params } = res.data
|
||||||
|
if (payment_required) {
|
||||||
|
const { timeStamp, nonceStr, package: package_, signType, paySign } = payment_params
|
||||||
|
await Taro.requestPayment({
|
||||||
|
timeStamp,
|
||||||
|
nonceStr,
|
||||||
|
package: package_,
|
||||||
|
signType,
|
||||||
|
paySign,
|
||||||
|
success: async () => {
|
||||||
Taro.hideLoading()
|
Taro.hideLoading()
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '支付成功',
|
title: '支付成功',
|
||||||
@@ -19,10 +45,23 @@ const OrderCheck = () => {
|
|||||||
Taro.navigateBack({
|
Taro.navigateBack({
|
||||||
delta: 1
|
delta: 1
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
Taro.hideLoading()
|
||||||
|
Taro.showToast({
|
||||||
|
title: '支付失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Text>OrderCheck</Text>
|
<Text>OrderCheck</Text>
|
||||||
|
<Text>球局名称:{detail?.title || '-'}</Text>
|
||||||
|
<Text>价格:¥{detail?.price || '-'}</Text>
|
||||||
<Button onClick={handlePay}>支付</Button>
|
<Button onClick={handlePay}>支付</Button>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ export interface GameDetail {
|
|||||||
updated_at: string,
|
updated_at: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MATCH_STATUS {
|
||||||
|
NOT_STARTED = 0, // 未开始
|
||||||
|
IN_PROGRESS = 1, //进行中
|
||||||
|
FINISHED = 2 //已结束
|
||||||
|
}
|
||||||
|
|
||||||
// 响应接口
|
// 响应接口
|
||||||
export interface Response {
|
export interface Response {
|
||||||
code: string
|
code: string
|
||||||
|
|||||||
46
src/services/orderService.ts
Normal file
46
src/services/orderService.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import httpService from './httpService'
|
||||||
|
import type { ApiResponse } from './httpService'
|
||||||
|
import { requestPayment } from '@tarojs/taro'
|
||||||
|
|
||||||
|
export interface SignType {
|
||||||
|
/** 仅在微信支付 v2 版本接口适用 */
|
||||||
|
MD5
|
||||||
|
/** 仅在微信支付 v2 版本接口适用 */
|
||||||
|
'HMAC-SHA256'
|
||||||
|
/** 仅在微信支付 v3 版本接口适用 */
|
||||||
|
RSA
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayMentParams {
|
||||||
|
order_id: number,
|
||||||
|
order_no: string,
|
||||||
|
status: number,
|
||||||
|
appId: string,
|
||||||
|
timeStamp: string,
|
||||||
|
nonceStr: string,
|
||||||
|
package: string,
|
||||||
|
signType: keyof SignType,
|
||||||
|
paySign: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户接口
|
||||||
|
export interface OrderResponse {
|
||||||
|
participant_id: number,
|
||||||
|
payment_required: boolean,
|
||||||
|
payment_params: PayMentParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布球局类
|
||||||
|
class OrderService {
|
||||||
|
// 用户登录
|
||||||
|
async createOrder(game_id: number): Promise<ApiResponse<OrderResponse>> {
|
||||||
|
return httpService.post('/payment/create_order', { game_id }, {
|
||||||
|
showLoading: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// async getOrderInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出认证服务实例
|
||||||
|
export default new OrderService()
|
||||||
Reference in New Issue
Block a user