Merge branch 'feat/liujie'

This commit is contained in:
2025-09-04 20:17:26 +08:00
4 changed files with 137 additions and 89 deletions

View File

@@ -4,11 +4,11 @@ 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'
// 导入API服务 // 导入API服务
import DetailService from '../../services/detailService' import DetailService from '../../services/detailService'
import { updateUserProfile } from '../../services/loginService' import { updateUserProfile, get_user_info } from '../../services/loginService'
import { getCurrentLocation } from '../../utils/locationUtils' import { getCurrentLocation } from '../../utils/locationUtils'
import { import {
useUserStats, useUserInfo,
useUserActions useUserActions,
} from '../../store/userStore' } from '../../store/userStore'
import img from '../../config/images' import img from '../../config/images'
import { getTextColorOnImage } from '../../utils' import { getTextColorOnImage } from '../../utils'
@@ -25,6 +25,7 @@ function insertDotInTags(tags: string[]) {
return tags.join('-·-').split('-') return tags.join('-·-').split('-')
} }
// 分享弹窗
const SharePopup = forwardRef(({ id, from }: { id: string, from: string }, ref) => { const SharePopup = forwardRef(({ id, from }: { id: string, from: string }, ref) => {
const [visible, setVisible] = useState(false) const [visible, setVisible] = useState(false)
@@ -90,6 +91,39 @@ const SharePopup = forwardRef(({ id, from }: { id: string, from: string }, ref)
) )
}) })
// 底部操作栏
function StickyButton(props) {
const { handleShare, handleJoinGame, detail } = props
const userInfo = useUserInfo()
const { id } = userInfo
const { publisher_id, status } = detail || {}
const role = Number(publisher_id) === id ? 'ownner' : 'visitor'
console.log(status, role)
return (
<View className="sticky-bottom-bar">
<View className="sticky-bottom-bar-share-and-comment">
<View className='sticky-bottom-bar-share' onClick={handleShare}>
<Image className='sticky-bottom-bar-share-icon' src={img.ICON_DETAIL_SHARE} />
<Text className='sticky-bottom-bar-share-text'></Text>
</View>
<View className='sticky-bottom-bar-share-and-comment-separator' />
<View className='sticky-bottom-bar-comment' onClick={() => { Taro.showToast({ title: 'To be continued', icon: 'none' }) }}>
<Image className='sticky-bottom-bar-comment-icon' src={img.ICON_DETAIL_COMMENT_DARK} />
<Text className='sticky-bottom-bar-comment-text'>32</Text>
</View>
</View>
<View className="sticky-bottom-bar-join-game" onClick={handleJoinGame}>
<Text>🎾</Text>
<Text></Text>
<View className='game-price'>
<Text>¥ 65</Text>
</View>
</View>
</View>
)
}
function Index() { function Index() {
// 使用Zustand store // 使用Zustand store
// const userStats = useUserStats() // const userStats = useUserStats()
@@ -99,7 +133,9 @@ 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 { id, autoShare, from } = params const { id, autoShare, from } = params
const { fetchUserInfo, updateUserInfo } = useUserActions()
console.log('from', from) console.log('from', from)
@@ -115,6 +151,7 @@ function Index() {
useDidShow(async () => { useDidShow(async () => {
await updateLocation() await updateLocation()
await fetchUserInfo()
await fetchDetail() await fetchDetail()
// calcBgMainColors() // calcBgMainColors()
}) })
@@ -122,7 +159,8 @@ function Index() {
const updateLocation = async () => { const updateLocation = async () => {
try { try {
const location = await getCurrentLocation() const location = await getCurrentLocation()
await updateUserProfile({ latitude: location.latitude, longitude: location.longitude }) setCurrentLocation([location.latitude, location.longitude])
await updateUserInfo({ latitude: location.latitude, longitude: location.longitude })
console.log('用户位置更新成功') console.log('用户位置更新成功')
} catch (error) { } catch (error) {
console.error('用户位置更新失败', error) console.error('用户位置更新失败', error)
@@ -130,7 +168,7 @@ function Index() {
} }
const fetchDetail = async () => { const fetchDetail = async () => {
const res = await DetailService.getDetail(Number(id)) const res = await DetailService.getDetail(242/* Number(id) */)
if (res.code === 0) { if (res.code === 0) {
console.log(res.data) console.log(res.data)
setDetail(res.data) setDetail(res.data)
@@ -156,8 +194,8 @@ function Index() {
const openMap = () => { const openMap = () => {
Taro.openLocation({ Taro.openLocation({
latitude: detail?.latitude, // 纬度(必填) latitude: detail?.longitude, // 纬度(必填)
longitude: detail?.longitude, // 经度(必填) longitude: detail?.latitude, // 经度(必填)
name: '上海体育场', // 位置名(可选) name: '上海体育场', // 位置名(可选)
address: '上海市徐汇区肇嘉浜路128号', // 地址详情(可选) address: '上海市徐汇区肇嘉浜路128号', // 地址详情(可选)
scale: 15, // 地图缩放级别1-28 scale: 15, // 地图缩放级别1-28
@@ -188,6 +226,8 @@ function Index() {
const { title, longitude, latitude } = detail || {} const { title, longitude, latitude } = detail || {}
console.log(longitude, latitude, 2222)
const requirements = [{ const requirements = [{
title: 'NTRP水平要求', title: 'NTRP水平要求',
desc: '2.0 - 4.5 之间', desc: '2.0 - 4.5 之间',
@@ -245,6 +285,7 @@ function Index() {
}, },
] ]
console.log('detail', detail)
return ( return (
<View className='detail-page'> <View className='detail-page'>
{/* custom navbar */} {/* custom navbar */}
@@ -376,15 +417,20 @@ function Index() {
</View> </View>
{/* venue map */} {/* venue map */}
<View className='location-map'> <View className='location-map'>
<Map {longitude && latitude && (
className='location-map-map' <Map
longitude={longitude} className='location-map-map'
latitude={latitude} longitude={latitude}
onError={() => {}} latitude={longitude}
// hide business msg markers={[{ id: 1, latitude: longitude, longitude: latitude, iconPath: require('@/static/detail/icon-stark.svg'), width: 16, height: 16 }]}
showLocation includePoints={[{ latitude: longitude, longitude: latitude }, { latitude: currentLocation[0], longitude: currentLocation[1] }]}
theme='dark' includePadding={{ left: 50, right: 50, top: 50, bottom: 50 }}
/> onError={() => {}}
// hide business msg
showLocation
theme='dark'
/>
)}
</View> </View>
</View> </View>
</View> </View>
@@ -565,26 +611,7 @@ function Index() {
</View> </View>
</View> </View>
{/* sticky bottom action bar */} {/* sticky bottom action bar */}
<View className="sticky-bottom-bar"> <StickyButton handleShare={handleShare} handleJoinGame={handleJoinGame} detail={detail} />
<View className="sticky-bottom-bar-share-and-comment">
<View className='sticky-bottom-bar-share' onClick={handleShare}>
<Image className='sticky-bottom-bar-share-icon' src={img.ICON_DETAIL_SHARE} />
<Text className='sticky-bottom-bar-share-text'></Text>
</View>
<View className='sticky-bottom-bar-share-and-comment-separator' />
<View className='sticky-bottom-bar-comment' onClick={() => { Taro.showToast({ title: 'To be continued', icon: 'none' }) }}>
<Image className='sticky-bottom-bar-comment-icon' src={img.ICON_DETAIL_COMMENT_DARK} />
<Text className='sticky-bottom-bar-comment-text'>32</Text>
</View>
</View>
<View className="sticky-bottom-bar-join-game" onClick={handleJoinGame}>
<Text>🎾</Text>
<Text></Text>
<View className='game-price'>
<Text>¥ 65</Text>
</View>
</View>
</View>
{/* share popup */} {/* share popup */}
<SharePopup ref={sharePopupRef} id={id as string} from={from as string} /> <SharePopup ref={sharePopupRef} id={id as string} from={from as string} />
</View> </View>

View File

@@ -1,5 +1,5 @@
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import httpService from './httpService'; import httpService, { ApiResponse } from './httpService';
import tokenManager from '../utils/tokenManager'; import tokenManager from '../utils/tokenManager';
// 微信用户信息接口 // 微信用户信息接口
@@ -34,8 +34,26 @@ export interface VerifyCodeResponse {
token?: string; token?: string;
user_info?: WechatUserInfo; user_info?: WechatUserInfo;
} }
// 用户详细信息
export interface UserInfoType {
id: number
openid: string
unionid: string
session_key: string
nickname: string
avatar_url: string
gender: string
country: string
province: string
city: string
language: string
phone: string
is_subscribed: string
latitude: number
longitude: number
subscribe_time: string
last_login_time: string
}
// 微信授权登录 // 微信授权登录
@@ -332,8 +350,19 @@ export const refresh_login_status = async (): Promise<boolean> => {
} }
}; };
// 获取用户详细信息
export const fetchUserProfile = async (): Promise<ApiResponse<UserInfoType>> => {
try {
const response = await httpService.post('/user/detail');
return response;
} catch (error) {
console.error('获取用户信息失败:', error);
throw error;
}
};
// 更新用户信息 // 更新用户信息
export const updateUserProfile = async (payload: Partial<WechatUserInfo> & { latitude?: number; longitude?: number; }) => { export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
try { try {
const response = await httpService.post('user/update', payload); const response = await httpService.post('user/update', payload);
return response; return response;

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M2.324 11.4521C2.00189 11.2179 1.82886 10.8309 1.8696 10.4357L2.15096 7.70601L0.287363 5.64062C0.0200831 5.3444 -0.0686262 4.92874 0.054557 4.54977C0.17774 4.17079 0.494164 3.8859 0.88497 3.80209L3.40903 3.26084C3.5459 3.23149 3.6642 3.1461 3.73517 3.02544L5.03312 0.818684C5.23426 0.476709 5.60222 0.266602 6 0.266602C6.39777 0.266602 6.76574 0.476709 6.96688 0.818683L8.26483 3.02544C8.3358 3.1461 8.4541 3.23149 8.59097 3.26084L11.115 3.80209C11.5058 3.8859 11.8223 4.17079 11.9454 4.54977C12.0686 4.92874 11.9799 5.3444 11.7126 5.64062L9.84904 7.70601L10.1304 10.4357C10.1711 10.8309 9.99811 11.2179 9.67599 11.4521C9.35388 11.6862 8.9312 11.7322 8.56599 11.5728L6.19998 10.5403C6.07246 10.4847 5.92753 10.4847 5.80002 10.5403L3.43401 11.5728C3.0688 11.7322 2.64612 11.6862 2.324 11.4521Z" fill="#FFCC00"/>
</svg>

After

Width:  |  Height:  |  Size: 924 B

View File

@@ -1,58 +1,47 @@
import { create } from 'zustand' import { create } from 'zustand'
import { fetchUserProfile, updateUserProfile, UserInfoType } from '@/services/loginService'
// 用户统计信息 export interface UserState {
export interface UserStats { user: UserInfoType
requestCount: number fetchUserInfo: () => Promise<void>
matchesCreated: number updateUserInfo: (userInfo: Partial<UserInfoType>) => void
matchesJoined: number
lastActiveTime: string
} }
// Store 状态接口 export const useUser = create<UserState>()((set) => ({
interface UserState { user: {
userStats: UserStats id: 0,
incrementRequestCount: () => void "openid": "",
resetUserStats: () => void "unionid": "",
} "session_key": "",
"nickname": "张三",
// 创建适配 Zustand 4.x 的 store "avatar_url": "https://example.com/avatar.jpg",
export const useUserStore = create<UserState>()((set) => ({ "gender": "",
// 初始状态 "country": "",
userStats: { "province": "",
requestCount: 0, "city": "",
matchesCreated: 0, "language": "",
matchesJoined: 0, "phone": "13800138000",
lastActiveTime: new Date().toISOString() "is_subscribed": "0",
"latitude": 0,
"longitude": 0,
"subscribe_time": "2024-06-15 14:00:00",
"last_login_time": "2024-06-15 14:00:00"
}, },
fetchUserInfo: async () => {
// Actions const res = await fetchUserProfile()
incrementRequestCount: () => { console.log(res)
console.log('store: incrementRequestCount 被调用') set({ user: res.data })
set((state) => ({
userStats: {
...state.userStats,
requestCount: state.userStats.requestCount + 1,
lastActiveTime: new Date().toISOString()
}
}))
}, },
updateUserInfo: async(userInfo: Partial<UserInfoType>) => {
resetUserStats: () => { const res = await updateUserProfile(userInfo)
console.log('store: resetUserStats 被调用') console.log(res)
set({ set({ user: res.data })
userStats: {
requestCount: 0,
matchesCreated: 0,
matchesJoined: 0,
lastActiveTime: new Date().toISOString()
}
})
} }
})) }))
// 简单的 hooks export const useUserInfo = () => useUser((state) => state.user)
export const useUserStats = () => useUserStore((state) => state.userStats)
export const useUserActions = () => useUserStore((state) => ({ export const useUserActions = () => useUser((state) => ({
incrementRequestCount: state.incrementRequestCount, fetchUserInfo: state.fetchUserInfo,
resetUserStats: state.resetUserStats updateUserInfo: state.updateUserInfo
})) }))