Merge branch 'feature/juguohong/20250816'

This commit is contained in:
李瑞
2025-09-30 22:30:18 +08:00
9 changed files with 745 additions and 10 deletions

View File

@@ -47,7 +47,7 @@
}
.location-position {
max-width: 66%;
max-width: 58%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -0,0 +1,653 @@
import React, { useEffect, useState } from 'react'
import { View, Canvas } from '@tarojs/components'
import Taro from '@tarojs/taro'
// 分享卡片数据接口
export interface ShareCardData {
// 用户信息
userAvatar: string // 用户头像URL
userNickname: string // 用户昵称
// 球局信息
gameType: string // 球局类型,如"单打"
skillLevel: string // 技能等级,如"NTRP 2.5 - 3.0"
gameDate: string // 日期,如"6月20日(周五)"
gameTime: string // 时间,如"下午5点 2小时"
venueName: string // 场地名称,如"因乐驰网球俱乐部(嘉定江桥万达店)"
venueImage: string // 场地图片URL
// 可选信息
playerImage?: string // 球员图片URL
}
// 组件Props接口
export interface ShareCardCanvasProps {
data: ShareCardData
width?: number // 卡片宽度默认375
height?: number // 卡片高度默认500
onGenerated?: (imagePath: string) => void // 生成完成回调
className?: string
}
const ShareCardCanvas: React.FC<ShareCardCanvasProps> = ({
data,
className = '',
onGenerated = () => { }
}) => {
const [tempImagePath, setTempImagePath] = useState('') // 存储Canvas生成的图片路径
const [isDrawing, setIsDrawing] = useState(false) // 防止重复绘制
// 设计稿尺寸
const designWidth = 500
const designHeight = 400
// 获取屏幕宽度如果没有传入width则使用屏幕宽度
const windowWidth = Taro.getSystemInfoSync().windowWidth
// 2. 计算缩放比例(设备宽度 / 设计稿宽度)
const scale = windowWidth / designWidth
// 3. 计算实际显示尺寸(按比例缩放)
const canvasWidth = designWidth * scale
const canvasHeight = designHeight * scale
// 绘制加粗文字(单行)
const drawBoldText = (ctx: any, text: string, x: number, y: number, fontSize: number, color: string, fontFamily?: string) => {
// 设置字体样式
if (fontFamily) {
try {
// 尝试使用setFont方法如果支持
ctx.setFont(`${fontSize}px ${fontFamily}`)
} catch (error) {
// 如果不支持setFont回退到setFontSize
ctx.setFontSize(fontSize)
}
} else {
ctx.setFontSize(fontSize)
}
ctx.setFillStyle(color)
ctx.setTextAlign('left')
ctx.setTextBaseline('top')
// 绘制加粗效果:多次绘制并偏移
ctx.fillText(text, x, y)
ctx.fillText(text, x + 1, y)
ctx.fillText(text, x, y + 1)
ctx.fillText(text, x + 1, y + 1)
}
// 绘制文字(支持自动换行)- 微信小程序版本
const drawText = (ctx: any, text: string, x: number, y: number, maxWidth: number, fontSize: number, color: string, bold: boolean = false, fontFamily?: string) => {
// 设置字体样式
if (fontFamily) {
try {
// 尝试使用setFont方法如果支持
ctx.setFont(`${fontSize}px ${fontFamily}`)
} catch (error) {
// 如果不支持setFont回退到setFontSize
ctx.setFontSize(fontSize)
}
} else {
ctx.setFontSize(fontSize)
}
ctx.setFillStyle(color)
ctx.setTextAlign('left')
ctx.setTextBaseline('top')
const words = text.split('')
let line = ''
let lineY = y
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i]
// 微信小程序中measureText返回的是对象需要访问width属性
const metrics = ctx.measureText(testLine)
const testWidth = metrics.width
if (testWidth > maxWidth && i > 0) {
if (bold) {
// 绘制加粗效果:多次绘制并偏移
ctx.fillText(line, x, lineY)
ctx.fillText(line, x + 1, lineY)
ctx.fillText(line, x, lineY + 1)
ctx.fillText(line, x + 1, lineY + 1)
} else {
ctx.fillText(line, x, lineY)
}
line = words[i]
lineY += fontSize * 1.2
} else {
line = testLine
}
}
if (bold) {
// 绘制加粗效果:多次绘制并偏移
ctx.fillText(line, x, lineY)
ctx.fillText(line, x + 1, lineY)
ctx.fillText(line, x, lineY + 1)
ctx.fillText(line, x + 1, lineY + 1)
} else {
ctx.fillText(line, x, lineY)
}
}
// 绘制圆角矩形函数
const drawRoundedRect = (ctx: any, x: number, y: number, width: number, height: number, radius: number) => {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.arcTo(x + width, y, x + width, y + radius, radius);
ctx.lineTo(x + width, y + height - radius);
ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
ctx.lineTo(x + radius, y + height);
ctx.arcTo(x, y + height, x, y + height - radius, radius);
ctx.lineTo(x, y + radius);
ctx.arcTo(x, y, x + radius, y, radius);
ctx.closePath();
ctx.fill();
}
// 绘制标签函数(通用)
const drawLabel = (ctx: any, x: number, y: number, width: number, height: number, radius: number, text: string, fontSize: number, textColor: string = '#000000', bgColor: string = '#FFFFFF', borderColor: string = '#E0E0E0') => {
// 绘制背景
ctx.setFillStyle(bgColor)
drawRoundedRect(ctx, x, y, width, height, radius)
// 绘制边框
ctx.setStrokeStyle(borderColor)
ctx.setLineWidth(1 * dpr)
ctx.stroke()
// 绘制文字
const textCenterX = x + width / 2
const textCenterY = y + height / 2
ctx.setFillStyle(textColor)
ctx.setTextAlign('center')
ctx.setTextBaseline('middle')
ctx.setFontSize(fontSize)
ctx.save()
ctx.translate(textCenterX, textCenterY)
ctx.fillText(text, 0, 0)
ctx.restore()
}
// 加载图片 - 微信小程序版本
const loadImage = (src: string): Promise<string> => {
return new Promise((resolve, reject) => {
Taro.getImageInfo({
src: src,
success: (res) => resolve(res.path),
fail: reject
})
})
}
// 绘制SVG路径到Canvas
const drawSVGPathToCanvas = (ctx: any) => {
// 设置绘制样式
ctx.setStrokeStyle('#48D800');
ctx.setLineWidth(scale * 3 * dpr);
ctx.setLineCap('round');
ctx.setLineJoin('round');
ctx.save();
// 移动到指定位置并缩放
ctx.translate(scale * 210 * dpr, scale * 90 * dpr);
const scaleValue = 0.8
ctx.scale(scaleValue, scaleValue);
// 手动绘制SVG路径微信小程序不支持Path2D
ctx.beginPath();
ctx.moveTo(109.808, 6.10642);
ctx.bezierCurveTo(89.5111, -2.79359, 33.4629, 3.77737, 7.49927, 30.2355);
ctx.bezierCurveTo(4.78286, 33.0044, 1.93351, 36.3827, 1.66282, 41.2355);
ctx.bezierCurveTo(1.21704, 49.1959, 7.56651, 55.1359, 13.1231, 57.7309);
ctx.bezierCurveTo(20.6373, 61.24, 28.4568, 62.6334, 36.2033, 63.2984);
ctx.bezierCurveTo(58.8813, 65.247, 81.6582, 60.9714, 101.719, 48.424);
ctx.bezierCurveTo(105.278, 46.1993, 108.823, 43.6365, 111.378, 39.5712);
ctx.bezierCurveTo(113.934, 35.5058, 115.361, 29.6397, 114.14, 24.1205);
ctx.bezierCurveTo(112.685, 17.5296, 107.961, 13.1328, 103.328, 10.8187);
ctx.bezierCurveTo(94.2761, 6.29607, 84.6677, 7.3453, 75.41, 8.45251);
// 绘制路径
ctx.stroke();
// 恢复上下文状态
ctx.restore();
}
// 获取 DPR - 使用系统像素比确保高清显示
// const systemDpr = Taro.getSystemInfoSync().pixelRatio
const dpr = 1
// Math.min(systemDpr, 3) // 限制最大dpr为3避免过度放大
// 绘制分享卡片
const drawShareCard = async () => {
// 防止重复绘制
if (isDrawing) {
console.log('正在绘制中,跳过重复绘制')
return
}
return new Promise(async (resolve, reject) => {
console.log('开始绘制分享卡片...')
setIsDrawing(true)
try {
// 在微信小程序中需要使用Taro.createCanvasContext
const ctx = Taro.createCanvasContext('shareCardCanvas')
console.log('Canvas上下文创建成功:', ctx)
// 设置Canvas的实际尺寸使用dpr确保高清显示
const canvasWidthPx = canvasWidth * dpr
const canvasHeightPx = canvasHeight * dpr
// 清空画布
ctx.clearRect(0, 0, canvasWidthPx, canvasHeightPx)
console.log('画布已清空')
// 如果dpr大于2进行缩放处理以避免内容过大
if (dpr > 2) {
const scale = 2 / dpr
ctx.scale(scale, scale)
console.log('应用缩放:', scale)
}
// 绘制背景 - 渐变色 已完成
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeightPx)
gradient.addColorStop(0, '#D8FFE5')
gradient.addColorStop(1, '#F9FFFB')
ctx.setFillStyle(gradient)
ctx.fillRect(0, 0, canvasWidthPx, canvasHeightPx)
console.log('背景绘制完成')
// 绘制背景条纹 已完成
ctx.setStrokeStyle('rgba(0, 0, 0, 0.03)')
ctx.setLineWidth(2)
for (let i = 0; i < canvasWidthPx; i += 4) {
ctx.beginPath()
ctx.moveTo(i, 0)
ctx.lineTo(i, canvasHeightPx)
ctx.stroke()
}
// 绘制用户头像(左上角) 已完成
const avatarSize = scale * 32 * dpr // 32px * dpr
const avatarX = scale * 35 * dpr // 距离左侧35px
const avatarY = scale * 35 * dpr // 距离顶部35px
try {
const avatarPath = await loadImage(data.userAvatar)
// 微信小程序中绘制圆形头像需要特殊处理
ctx.save()
ctx.beginPath()
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI)
ctx.clip()
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize)
ctx.restore()
} catch (error) {
// 如果头像加载失败,绘制默认头像
ctx.setFillStyle('#CCCCCC')
ctx.beginPath()
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI)
ctx.fill()
}
// 绘制用户昵称 已完成
const nicknameX = avatarX + avatarSize + 8 * dpr // 距离头像8px
const nicknameY = avatarY + (avatarSize - 24 * dpr) / 2 // 与头像水平居中对齐
const nicknameFontSize = scale * 24 * dpr
drawText(ctx, data.userNickname, nicknameX, nicknameY, 200 * dpr, nicknameFontSize, '#000000', true, '"Noto Sans SC"')
// 绘制"邀你加入球局"文案
const inviteX = scale * 35 * dpr // 距离画布左侧35px
const inviteY = scale * 100 * dpr // 距离画布顶部79px
const inviteFontSize = scale * 44 * dpr
// 绘制"邀你加入"
drawBoldText(ctx, '邀你加入', inviteX, inviteY, inviteFontSize, '#000000', "Noto Sans SC")
// 绘制"球局"特殊样式
const qiuJuX = inviteX + ctx.measureText('邀你加入').width + 5 * dpr
const qiuJuFontSize = scale * 44 * dpr
drawBoldText(ctx, '球局', qiuJuX, inviteY, qiuJuFontSize, '#48D800', '"Noto Sans SC"')
// 测试绘制网络图片
drawSVGPathToCanvas(ctx)
// 绘制球员图片(右上角)已完成
const playerImgX = scale * 340 * dpr
const playerImgY = scale * 35 * dpr
const playerImgSize = scale * 124 * dpr
const borderRadius = scale * 24 * dpr
const padding = scale * 4 * dpr
const rotation = scale * -8 // 旋转-8度
try {
const playerImgPath = await loadImage(data.playerImage || data.venueImage)
ctx.save()
// 移动到旋转中心点
const centerX = playerImgX + playerImgSize / 2
const centerY = playerImgY + playerImgSize / 2
ctx.translate(centerX, centerY)
// 旋转-8度
ctx.rotate((rotation * Math.PI) / 180)
// 1. 先绘制白色圆角矩形背景
ctx.setFillStyle('#FFFFFF')
ctx.beginPath()
// 使用更精确的圆角矩形绘制
const rectX = -playerImgSize / 2
const rectY = -playerImgSize / 2
const rectWidth = playerImgSize
const rectHeight = playerImgSize
// 从左上角开始,顺时针绘制
// 左上角圆角
ctx.moveTo(rectX + borderRadius, rectY)
ctx.quadraticCurveTo(rectX, rectY, rectX, rectY + borderRadius)
// 左边
ctx.lineTo(rectX, rectY + rectHeight - borderRadius)
// 左下角圆角
ctx.quadraticCurveTo(rectX, rectY + rectHeight, rectX + borderRadius, rectY + rectHeight)
// 下边
ctx.lineTo(rectX + rectWidth - borderRadius, rectY + rectHeight)
// 右下角圆角
ctx.quadraticCurveTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth, rectY + rectHeight - borderRadius)
// 右边
ctx.lineTo(rectX + rectWidth, rectY + borderRadius)
// 右上角圆角
ctx.quadraticCurveTo(rectX + rectWidth, rectY, rectX + rectWidth - borderRadius, rectY)
// 上边
ctx.lineTo(rectX + borderRadius, rectY)
ctx.closePath()
ctx.fill()
// 2. 绘制图片带4px内边距
const imgX = -playerImgSize / 2 + padding
const imgY = -playerImgSize / 2 + padding
const imgSize = playerImgSize - padding * 2
// 设置圆角裁剪区域
ctx.beginPath()
const imgRadius = borderRadius - padding
// 从左上角开始,顺时针绘制
// 左上角圆角
ctx.moveTo(imgX + imgRadius, imgY)
ctx.quadraticCurveTo(imgX, imgY, imgX, imgY + imgRadius)
// 左边
ctx.lineTo(imgX, imgY + imgSize - imgRadius)
// 左下角圆角
ctx.quadraticCurveTo(imgX, imgY + imgSize, imgX + imgRadius, imgY + imgSize)
// 下边
ctx.lineTo(imgX + imgSize - imgRadius, imgY + imgSize)
// 右下角圆角
ctx.quadraticCurveTo(imgX + imgSize, imgY + imgSize, imgX + imgSize, imgY + imgSize - imgRadius)
// 右边
ctx.lineTo(imgX + imgSize, imgY + imgRadius)
// 右上角圆角
ctx.quadraticCurveTo(imgX + imgSize, imgY, imgX + imgSize - imgRadius, imgY)
// 上边
ctx.lineTo(imgX + imgRadius, imgY)
ctx.closePath()
ctx.clip()
// 绘制图片
ctx.drawImage(playerImgPath, imgX, imgY, imgSize, imgSize)
ctx.restore()
} catch (error) {
// 如果图片加载失败,绘制占位符
ctx.save()
const centerX = playerImgX + playerImgSize / 2
const centerY = playerImgY + playerImgSize / 2
ctx.translate(centerX, centerY)
ctx.rotate((rotation * Math.PI) / 180)
// 绘制白色圆角矩形背景
ctx.setFillStyle('#FFFFFF')
ctx.beginPath()
const rectX = -playerImgSize / 2
const rectY = -playerImgSize / 2
const rectWidth = playerImgSize
const rectHeight = playerImgSize
// 从左上角开始,顺时针绘制
// 左上角圆角
ctx.moveTo(rectX + borderRadius, rectY)
ctx.quadraticCurveTo(rectX, rectY, rectX, rectY + borderRadius)
// 左边
ctx.lineTo(rectX, rectY + rectHeight - borderRadius)
// 左下角圆角
ctx.quadraticCurveTo(rectX, rectY + rectHeight, rectX + borderRadius, rectY + rectHeight)
// 下边
ctx.lineTo(rectX + rectWidth - borderRadius, rectY + rectHeight)
// 右下角圆角
ctx.quadraticCurveTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth, rectY + rectHeight - borderRadius)
// 右边
ctx.lineTo(rectX + rectWidth, rectY + borderRadius)
// 右上角圆角
ctx.quadraticCurveTo(rectX + rectWidth, rectY, rectX + rectWidth - borderRadius, rectY)
// 上边
ctx.lineTo(rectX + borderRadius, rectY)
ctx.closePath()
ctx.fill()
// 绘制灰色占位符(带内边距)
const imgX = -playerImgSize / 2 + padding
const imgY = -playerImgSize / 2 + padding
const imgSize = playerImgSize - padding * 2
ctx.setFillStyle('#E0E0E0')
ctx.beginPath()
const imgRadius = borderRadius - padding
// 从左上角开始,顺时针绘制
// 左上角圆角
ctx.moveTo(imgX + imgRadius, imgY)
ctx.quadraticCurveTo(imgX, imgY, imgX, imgY + imgRadius)
// 左边
ctx.lineTo(imgX, imgY + imgSize - imgRadius)
// 左下角圆角
ctx.quadraticCurveTo(imgX, imgY + imgSize, imgX + imgRadius, imgY + imgSize)
// 下边
ctx.lineTo(imgX + imgSize - imgRadius, imgY + imgSize)
// 右下角圆角
ctx.quadraticCurveTo(imgX + imgSize, imgY + imgSize, imgX + imgSize, imgY + imgSize - imgRadius)
// 右边
ctx.lineTo(imgX + imgSize, imgY + imgRadius)
// 右上角圆角
ctx.quadraticCurveTo(imgX + imgSize, imgY, imgX + imgSize - imgRadius, imgY)
// 上边
ctx.lineTo(imgX + imgRadius, imgY)
ctx.closePath()
ctx.fill()
ctx.restore()
console.log('球员图片占位符绘制完成')
}
// 绘制球局信息区域
const infoStartY = scale * 192
const infoSpacing = scale * 64
// 球局类型和技能等级
const gameInfoY = infoStartY
const iconSize = scale * 40
const iconX = scale * 35
const textX = iconX + iconSize + 20
// 绘制网球图标
const tennisBallPath = await loadImage('https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b3eaf45e-ef28-4e45-9195-823b832e0451.jpg')
ctx.drawImage(tennisBallPath, iconX, gameInfoY, iconSize, iconSize)
// 绘制"单打"标签
const danDaX = scale * 100
const danDaY = scale * 196
const danDaWidth = scale * 76 * dpr
const danDaHeight = scale * 40 * dpr
const danDaRadius = scale * 20 * dpr
const danDaFontSize = scale * 22 * dpr
drawLabel(ctx, danDaX, danDaY, danDaWidth, danDaHeight, danDaRadius, data.gameType, danDaFontSize)
// 绘制技能等级标签
const skillX = scale * 190
const skillY = scale * 196
const skillWidth = scale * 180 * dpr
const skillHeight = scale * 40 * dpr
const skillRadius = scale * 20 * dpr
const skillFontSize = scale * 22 * dpr
drawLabel(ctx, skillX, skillY, skillWidth, skillHeight, skillRadius, data.skillLevel, skillFontSize)
// 绘制日期时间
const dateX = danDaX
const timeInfoY = infoStartY + infoSpacing
const timeInfoFontSize = scale * 24 * dpr
const calendarPath = await loadImage("https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/ea792a5d-b105-4c95-bfc4-8af558f2b33b.jpg")
ctx.drawImage(calendarPath, iconX, timeInfoY, iconSize, iconSize)
// 绘制日期(绿色)
drawText(ctx, data.gameDate, dateX, timeInfoY + 4, 300, timeInfoFontSize, '#4CAF50')
// 绘制时间(黑色)
const timeX = textX + ctx.measureText(data.gameDate).width + 10 * dpr
drawText(ctx, data.gameTime, timeX, timeInfoY + 4, 300, timeInfoFontSize, '#000000')
// 绘制地点
const locationInfoY = infoStartY + infoSpacing * 2
const locationFontSize = scale * 22 * dpr
const locationPath = await loadImage("https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/adc9a167-2ea9-4e3b-b963-6a894a1fd91b.jpg")
ctx.drawImage(locationPath, iconX, locationInfoY, iconSize, iconSize)
drawText(ctx, data.venueName, danDaX, locationInfoY + 4, 600, locationFontSize, '#000000')
// 绘制完成调用draw方法
console.log('开始调用ctx.draw()')
ctx.draw(false, () => {
console.log('Canvas绘制完成开始生成图片...')
// 延迟一下再生成图片,确保绘制完成
setTimeout(() => {
Taro.canvasToTempFilePath({
canvasId: 'shareCardCanvas',
fileType: 'png',
quality: 1,
success: (res) => {
console.log('图片生成成功:', res.tempFilePath)
setIsDrawing(false) // 绘制完成,重置状态
resolve(res.tempFilePath)
onGenerated?.(res.tempFilePath)
setTempImagePath(res.tempFilePath)
},
fail: (error) => {
console.error('图片生成失败:', error)
setIsDrawing(false) // 绘制失败,重置状态
reject(error)
}
})
}, 500) // 延迟500ms确保Canvas完全渲染
})
console.log('Canvas绘制命令已发送')
} catch (error) {
console.error('绘制分享卡片失败:', error)
setIsDrawing(false) // 绘制失败,重置状态
Taro.showToast({
title: '生成分享卡片失败',
icon: 'none'
})
reject(error)
}
})
}
// 手动分享方法(已移除,由父组件处理分享)
// 组件挂载后绘制
useEffect(() => {
if (data && !isDrawing && !tempImagePath) {
console.log('组件挂载,开始绘制分享卡片')
// 延迟一下确保Canvas已经渲染
setTimeout(() => {
drawShareCard()
}, 500)
}
}, [data]) // 只依赖data移除canvasWidth避免无限循环
// 暴露分享方法给父组件
useEffect(() => {
if (onGenerated && tempImagePath) {
onGenerated(tempImagePath)
}
}, [tempImagePath]) // 只依赖tempImagePath移除onGenerated避免无限循环
return (
<View className={`share-card-canvas ${className}`}>
<Canvas
canvasId="shareCardCanvas"
style={{
width: `${canvasWidth}px`,
height: `${canvasHeight}px`,
position: 'absolute', // 绝对定位避免影响布局
top: '-9999px', // 移出可视区域
left: '-9999px'
}}
width={`${canvasWidth * dpr}`}
height={`${canvasHeight * dpr}`}
disableScroll={true}
onTouchStart={() => { }}
onTouchMove={() => { }}
onTouchEnd={() => { }}
onTouchCancel={() => { }}
/>
</View>
)
}
export default ShareCardCanvas

View File

@@ -16,6 +16,7 @@ import EditModal from "./EditModal/index";
import withAuth from "./Auth";
import { CustomPicker, PopupPicker } from "./Picker";
import NTRPEvaluatePopup from "./NTRPEvaluatePopup";
import ShareCardCanvas from "./ShareCardCanvas";
import RefundPopup from "./refundPopup";
import GameManagePopup from './GameManagePopup';
import FollowUserCard from './FollowUserCard/index';
@@ -43,6 +44,7 @@ export {
CustomPicker,
PopupPicker,
NTRPEvaluatePopup,
ShareCardCanvas,
RefundPopup,
GameManagePopup,
FollowUserCard,

View File

@@ -4,7 +4,7 @@
display: flex;
flex-direction: column;
gap: 5px;
padding-bottom: 34px;
padding-bottom: 80px;
// min-height: 100vh;
.recommendTextWrapper {

View File

@@ -4,6 +4,7 @@ import ListLoadError from "@/components/ListLoadError";
import ListCardSkeleton from "@/components/ListCardSkeleton";
import { useReachBottom } from "@tarojs/taro";
import "./index.scss";
import { useRef, useEffect } from "react";
const ListContainer = (props) => {
const {
@@ -14,12 +15,23 @@ const ListContainer = (props) => {
// recommendList,
loadMoreMatches,
} = props;
const timerRef = useRef<NodeJS.Timeout | null>(null);
useReachBottom(() => {
// 加载更多方法
loadMoreMatches();
timerRef.current = setTimeout(() => {
loadMoreMatches();
}, 500);
});
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, []);
if (error) {
return <ListLoadError reload={reload} />;
}

View File

@@ -142,10 +142,12 @@
.hidden {
opacity: 0;
transform: translateY(-5px);
pointer-events: none;
}
/* 可见状态 */
.visible {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}

View File

@@ -1,3 +1,9 @@
:global {
.guide-bar {
z-index: 9999;
}
}
.listPage {
background-color: #fefefe;
@@ -29,7 +35,7 @@
}
.listNavWrapper {
position: relative;
position: relative;
}
.toggleElement {
@@ -39,10 +45,10 @@
left: 0;
width: 100%;
height: 100%;
/* 过渡动画设置,实现平滑切换 */
transition: opacity 0.5s ease, transform 0.5s ease;
display: flex;
align-items: center;
justify-content: center;
@@ -75,5 +81,6 @@
.hidden {
opacity: 0;
transform: translateY(20px);
pointer-events: none; /* 隐藏时不响应鼠标事件 */
pointer-events: none;
/* 隐藏时不响应鼠标事件 */
}

View File

@@ -1,8 +1,8 @@
import SearchBar from "@/components/SearchBar";
import FilterPopup from "@/components/FilterPopup";
import styles from "./index.module.scss";
import { useEffect, useRef, useCallback } from "react";
import Taro, { usePageScroll } from "@tarojs/taro";
import { useEffect, useRef, useCallback, useState } from "react";
import Taro, { usePageScroll, useShareAppMessage, useShareTimeline } from "@tarojs/taro";
import { useListStore } from "@/store/listStore";
import { useGlobalState } from "@/store/global";
import { View } from "@tarojs/components";
@@ -12,6 +12,7 @@ import ListContainer from "@/container/listContainer";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { withAuth } from "@/components";
import { updateUserLocation } from "@/services/userService";
// import ShareCardCanvas from "@/components/ShareCardCanvas";
import { useDictionaryStore } from "@/store/dictionaryStore";
const ListPage = () => {
@@ -228,6 +229,47 @@ const ListPage = () => {
});
};
// ====== 分享 测试 ======
// const [shareImagePath, setShareImagePath] = useState('')
// const handleShare = (imagePath: string) => {
// console.log('===imagePath', imagePath)
// // 避免重复设置相同的图片路径
// if (imagePath && imagePath !== shareImagePath) {
// setShareImagePath(imagePath)
// // 图片生成完成后,显示分享菜单
// Taro.showShareMenu({
// withShareTicket: true,
// success: () => {
// console.log('分享菜单显示成功')
// },
// fail: (error) => {
// console.error('分享菜单显示失败:', error)
// }
// })
// }
// }
// // 页面级分享钩子
// useShareAppMessage(() => {
// console.log('页面分享给好友,图片路径:', shareImagePath)
// return {
// title: '列表页-邀你加入球局',
// path: '/game_pages/list/index',
// imageUrl: shareImagePath || ''
// }
// })
// useShareTimeline(() => {
// console.log('页面分享到朋友圈,图片路径:', shareImagePath)
// return {
// title: '列表页-邀你加入球局',
// query: 'from=timeline',
// imageUrl: shareImagePath || ''
// }
// })
// 初始化字典数据
const initDictionaryData = async () => {
try {
@@ -251,7 +293,6 @@ const ListPage = () => {
}}
/>
<View ref={scrollContextRef}>
{/* <ShareCardCanvas /> */}
{/* 列表内容 */}
<View className={styles.listPage} style={{ paddingTop: totalHeight }}>
{/* 综合筛选 */}
@@ -310,6 +351,22 @@ const ListPage = () => {
/>
</View>
</View>
{/* 测试分享功能 */}
{/* <ShareCardCanvas data={
{
userAvatar: "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f62c80-ac44-4f3b-bb6c-d7f6e8ebf76d.jpg",
userNickname: "华巴抡卡",
gameType: "单打",
skillLevel: "NTRP 2.5 - 3.0",
gameDate: "6月20日(周五)",
gameTime: "下午5点 2小时",
venueName: "因乐驰网球俱乐部(嘉定江桥万达店)",
venueImage: "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f62c80-ac44-4f3b-bb6c-d7f6e8ebf76d.jpg",
playerImage: "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f62c80-ac44-4f3b-bb6c-d7f6e8ebf76d.jpg"
}
}
onGenerated={handleShare}
/> */}
<GuideBar currentPage="list" />
</>
);

View File

@@ -463,12 +463,14 @@ export const useListStore = create<TennisStore>()((set, get) => ({
searchPageState: {
...searchPageStateDefaultValue
},
loading: true,
});
} else {
set({
listPageState: {
...listPageStateDefaultValue
},
loading: true,
});
}
if (!isSearchData) {