Merge branch 'feat/liujie'

This commit is contained in:
2025-09-06 18:55:32 +08:00
4 changed files with 222 additions and 101 deletions

View File

@@ -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 { Cell, Avatar, Progress, Popover } from '@nutui/nutui-react-taro'
import Taro, { useRouter, useShareAppMessage, useShareTimeline, useDidShow } from '@tarojs/taro'
import dayjs, { locale } from 'dayjs'
import 'dayjs/locale/zh-cn'
// 导入API服务
import DetailService from '../../services/detailService'
import DetailService, { MATCH_STATUS} from '../../services/detailService'
import { updateUserProfile, get_user_info } from '../../services/loginService'
import { getCurrentLocation } from '../../utils/locationUtils'
import { getCurrentLocation, calculateDistance } from '../../utils/locationUtils'
import {
useUserInfo,
useUserActions,
@@ -15,6 +17,8 @@ import { getTextColorOnImage } from '../../utils'
import './index.scss'
import { CommonPopup } from '@/components'
dayjs.locale('zh-cn')
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/cf5a82ba-90af-4138-a1b3-9119adcde9e0.png',
@@ -96,10 +100,10 @@ function StickyButton(props) {
const { handleShare, handleJoinGame, detail } = props
const userInfo = useUserInfo()
const { id } = userInfo
const { publisher_id, status } = detail || {}
const { publisher_id, match_status, price } = detail || {}
const role = Number(publisher_id) === id ? 'ownner' : 'visitor'
console.log(status, role)
console.log(match_status, role)
return (
<View className="sticky-bottom-bar">
<View className="sticky-bottom-bar-share-and-comment">
@@ -117,7 +121,110 @@ function StickyButton(props) {
<Text>🎾</Text>
<Text></Text>
<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>
@@ -133,11 +240,13 @@ function Index() {
// const [textColor, setTextColor] = useState<string []>([])
const [detail, setDetail] = useState<any>(null)
const { params } = useRouter()
const [currentLocation, setCurrentLocation] = useState([0, 0])
const [currentLocation, setCurrentLocation] = useState<[number, number]>([0, 0])
const { id, autoShare, from } = params
const { fetchUserInfo, updateUserInfo } = useUserActions()
console.log('from', from)
console.group('params')
console.log(params)
console.groupEnd()
// 本地状态管理
const [loading, setLoading] = useState(false)
@@ -168,7 +277,7 @@ function Index() {
}
const fetchDetail = async () => {
const res = await DetailService.getDetail(242/* Number(id) */)
const res = await DetailService.getDetail(243/* Number(id) */)
if (res.code === 0) {
console.log(res.data)
setDetail(res.data)
@@ -192,19 +301,9 @@ function Index() {
sharePopupRef.current.show()
}
const openMap = () => {
Taro.openLocation({
latitude: detail?.longitude, // 纬度(必填)
longitude: detail?.latitude, // 经度(必填)
name: '上海体育场', // 位置名(可选)
address: '上海市徐汇区肇嘉浜路128号', // 地址详情(可选)
scale: 15, // 地图缩放级别1-28
})
}
const handleJoinGame = () => {
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>
</View>
{/* Date and Place and weather */}
<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">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">325 </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>
<GameInfo detail={detail} currentLocation={currentLocation} />
{/* detail */}
<View className='detail-page-content-detail'>
{/* venue detail title and venue ordered status */}

View File

@@ -1,28 +1,67 @@
import React from 'react'
import React, { useState } from 'react'
import { View, Text, Button } from '@tarojs/components'
import Taro from '@tarojs/taro'
import Taro, { useDidShow, useRouter } from '@tarojs/taro'
import { delay } from '@/utils'
import orderService from '@/services/orderService'
import detailService, { GameDetail } from '@/services/detailService'
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 () => {
Taro.showLoading({
title: '支付中...',
mask: true
})
await delay(2000)
Taro.hideLoading()
Taro.showToast({
title: '支付成功',
icon: 'success'
})
await delay(1000)
Taro.navigateBack({
delta: 1
})
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.showToast({
title: '支付成功',
icon: 'success'
})
await delay(1000)
Taro.navigateBack({
delta: 1
})
},
fail: () => {
Taro.hideLoading()
Taro.showToast({
title: '支付失败',
icon: 'none'
})
}
})
}
}
}
return (
<View>
<Text>OrderCheck</Text>
<Text>{detail?.title || '-'}</Text>
<Text>¥{detail?.price || '-'}</Text>
<Button onClick={handlePay}></Button>
</View>
)

View File

@@ -20,6 +20,12 @@ export interface GameDetail {
updated_at: string,
}
export enum MATCH_STATUS {
NOT_STARTED = 0, // 未开始
IN_PROGRESS = 1, //进行中
FINISHED = 2 //已结束
}
// 响应接口
export interface Response {
code: string

View 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()