Merge branch 'master' into light

This commit is contained in:
张成
2025-09-06 23:16:47 +08:00
5 changed files with 234 additions and 101 deletions

View File

@@ -121,6 +121,18 @@
align-items: center; align-items: center;
border-bottom: 1px solid rgba(0, 0, 0, 0.12); border-bottom: 1px solid rgba(0, 0, 0, 0.12);
.upload-source-popup-item-text {
width: 100%;
height: 56px;
padding: 16px 24px;
box-sizing: border-box;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 24px;
text-align: center;
}
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }

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 { 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">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>
{/* 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 */}

View File

@@ -1,28 +1,67 @@
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))
Taro.hideLoading() if (res.code === 0) {
Taro.showToast({ const { payment_required, payment_params } = res.data
title: '支付成功', if (payment_required) {
icon: 'success' const { timeStamp, nonceStr, package: package_, signType, paySign } = payment_params
}) await Taro.requestPayment({
await delay(1000) timeStamp,
Taro.navigateBack({ nonceStr,
delta: 1 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 ( 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>
) )

View File

@@ -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

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