From e51015b447823b9e2cddcf7467ecba2abab83612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Tue, 26 Aug 2025 17:58:01 +0800 Subject: [PATCH] feat: add router to detail and ignore css order problem --- config/index.ts | 11 +- src/app.config.ts | 1 - src/app.scss | 4 + src/app.ts | 2 +- src/components/CommonPopup/CommonPopup.tsx | 6 +- src/components/ListCard/index.tsx | 10 +- src/pages/detail/index.scss | 41 +++ src/pages/detail/index.tsx | 97 +++++- src/pages/index/index.config.ts | 3 - src/pages/index/index.module.scss | 1 - src/pages/index/index.scss | 261 ---------------- src/pages/index/index.tsx | 293 ------------------ src/pages/publishBall/index.tsx | 77 ++--- .../asserts/fonts/PoetsenOne-Regular.ttf | Bin 0 -> 177704 bytes src/utils/index.ts | 9 + types/list/types.ts | 1 + 16 files changed, 209 insertions(+), 608 deletions(-) delete mode 100644 src/pages/index/index.config.ts delete mode 100644 src/pages/index/index.module.scss delete mode 100644 src/pages/index/index.scss delete mode 100644 src/pages/index/index.tsx create mode 100644 src/static/asserts/fonts/PoetsenOne-Regular.ttf create mode 100644 src/utils/index.ts diff --git a/config/index.ts b/config/index.ts index 9e16ab3..56b2b5f 100644 --- a/config/index.ts +++ b/config/index.ts @@ -16,7 +16,7 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => { 750: 1, 375: 2, 828: 1.81 / 2, - 390: 1.92 + 390: 1.92 }, sourceRoot: 'src', outputRoot: 'dist', @@ -66,9 +66,16 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => { } } }, + miniCssExtractPluginOption: { + ignoreOrder: true, + // filename: 'css/[name].[hash].css', + // chunkFilename: 'css/[name].[chunkhash].css' + }, webpackChain(chain) { chain.resolve.plugin('tsconfig-paths').use(TsconfigPathsPlugin) - } + }, + // @ts-expect-error: Taro 类型定义缺少 mini.hot + hot: true, }, h5: { publicPath: '/', diff --git a/src/app.config.ts b/src/app.config.ts index 901708f..ba6d663 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -7,7 +7,6 @@ export default defineAppConfig({ 'pages/login/terms/index', 'pages/publishBall/index', // 'pages/mapDisplay/index', - 'pages/index/index', 'pages/detail/index', ], window: { diff --git a/src/app.scss b/src/app.scss index e69de29..3228f0b 100644 --- a/src/app.scss +++ b/src/app.scss @@ -0,0 +1,4 @@ +@font-face { + font-family: 'PoetsenOne'; + src: url('./static/asserts/fonts/PoetsenOne-Regular.ttf') format('truetype'); +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 6ef979c..6544603 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,6 @@ import { Component, ReactNode } from 'react' -import './app.scss' import './nutui-theme.scss' +import './app.scss' import { useDictionaryStore } from './store/dictionaryStore' interface AppProps { diff --git a/src/components/CommonPopup/CommonPopup.tsx b/src/components/CommonPopup/CommonPopup.tsx index a8dbeef..ef8deac 100644 --- a/src/components/CommonPopup/CommonPopup.tsx +++ b/src/components/CommonPopup/CommonPopup.tsx @@ -18,6 +18,7 @@ export interface CommonPopupProps { zIndex?: number children?: React.ReactNode className?: string + style?: React.CSSProperties } const CommonPopup: React.FC = ({ @@ -34,6 +35,7 @@ const CommonPopup: React.FC = ({ position = 'bottom', round = true, zIndex, + style, children }) => { const handleCancel = () => { @@ -52,7 +54,7 @@ const CommonPopup: React.FC = ({ closeable={false} onClose={onClose} className={`${styles['common-popup']} ${className ? className : ''}`} - style={zIndex ? { zIndex } : undefined} + style={{ ...style, zIndex: zIndex ? zIndex : undefined }} > {showHeader && ( @@ -78,4 +80,4 @@ const CommonPopup: React.FC = ({ ) } -export default CommonPopup \ No newline at end of file +export default CommonPopup \ No newline at end of file diff --git a/src/components/ListCard/index.tsx b/src/components/ListCard/index.tsx index a5f5f28..c91b3d3 100644 --- a/src/components/ListCard/index.tsx +++ b/src/components/ListCard/index.tsx @@ -1,10 +1,12 @@ import { View, Text, Image } from "@tarojs/components"; +import Taro from '@tarojs/taro' import img from "../../config/images"; import { ListCardProps } from "../../../types/list/types"; import "./index.scss"; // import SkeletonComponent from "../../components/Skeleton"; const ListCard: React.FC = ({ + id, title, dateTime, location, @@ -20,6 +22,12 @@ const ListCard: React.FC = ({ return ; }; + const handleViewDetail = () => { + Taro.navigateTo({ + url: `/pages/detail/index?id=${id || 1}&from=list&autoShare=0` + }) + } + // 根据图片数量决定展示样式 const renderImages = () => { if (images.length === 0) return null; @@ -60,7 +68,7 @@ const ListCard: React.FC = ({ ); }; return ( - + {/* 左侧内容区域 */} {/* 标题 */} diff --git a/src/pages/detail/index.scss b/src/pages/detail/index.scss index d9bbf5b..2ff9ab1 100644 --- a/src/pages/detail/index.scss +++ b/src/pages/detail/index.scss @@ -29,6 +29,7 @@ color: #fff; display: flex; align-items: center; + background: rgba(0, 0, 0, 0.10); .detail-navigator-back { border-right: 1px solid #444; @@ -984,8 +985,48 @@ border-radius: 16px; border: 1px solid rgba(0, 0, 0, 0.06); background: #FFF; + + &-price { + font-family: "PoetsenOne"; + font-size: 28px; + font-weight: 400; + line-height: 24px; /* 114.286% */ + letter-spacing: -0.56px; + color: #000; + } } } } } +.share-popup-content { + width: 100%; + height: 100%; + padding: 20px 16px env(safe-area-inset-bottom); + box-sizing: border-box; + // padding-bottom: env(safe-area-inset-bottom); + box-sizing: border-box; + display: flex; + justify-content: space-around; + align-items: center; + + & > view { + width: 100px; + height: 64px; + border-radius: 12px; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + + & > image { + width: 24px; + height: 24px; + } + + & > text { + color: rgba(0, 0, 0, 0.85); + } + } +} + diff --git a/src/pages/detail/index.tsx b/src/pages/detail/index.tsx index 5d3be38..957328b 100644 --- a/src/pages/detail/index.tsx +++ b/src/pages/detail/index.tsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react' 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 } from '@tarojs/taro' +import Taro, { useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro' // 导入API服务 import DetailService from '../../services/detailService' import { @@ -9,8 +9,9 @@ import { useUserActions } from '../../store/userStore' import img from '../../config/images' -import { getTextColorOnImage } from '../../utils/processImage' +import { getTextColorOnImage } from '../../utils' import './index.scss' +import { CommonPopup } from '@/components' const images = [ 'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1a35ebbf-2361-44da-b338-7608561d0b31.png', @@ -22,9 +23,70 @@ function insertDotInTags(tags: string[]) { return tags.join('-·-').split('-') } -function handleShare() { +const SharePopup = forwardRef(({ id, from }: { id: string, from: string }, ref) => { + const [visible, setVisible] = useState(false) -} + useImperativeHandle(ref, () => ({ + show: () => { + setVisible(true) + } + })) + + function handleShareToWechat() { + useShareAppMessage(() => { + return { + title: '分享', + path: `/pages/detail/index?id=${id}&from=${from}`, + } + }) + } + + function handleShareToWechatMoments() { + useShareTimeline(() => { + return { + title: '分享', + path: `/pages/detail/index?id=${id}&from=${from}`, + } + }) + } + + function handleSaveToLocal() { + Taro.saveImageToPhotosAlbum({ + filePath: images[0], + success: () => { + Taro.showToast({ title: '保存成功', icon: 'success' }) + }, + fail: () => { + Taro.showToast({ title: '保存失败', icon: 'none' }) + }, + }) + } + + return ( + { setVisible(false) }} + hideFooter + style={{ minHeight: '100px' }} + > + + + + 分享到微信 + + + + 分享朋友圈 + + + + 保存到本地 + + + + ) +}) function Index() { // 使用Zustand store @@ -35,10 +97,13 @@ function Index() { const [colors, setColors] = useState([]) const [detail, setDetail] = useState(null) const { params } = useRouter() - const { id, share } = params + const { id, autoShare, from } = params + + console.log('from', from) // 本地状态管理 const [loading, setLoading] = useState(false) + const sharePopupRef = useRef(null) // 页面加载时获取数据 useEffect(() => { @@ -63,6 +128,20 @@ function Index() { setColors(textcolors) } + function handleShare() { + sharePopupRef.current.show() + } + + const openMap = () => { + Taro.openLocation({ + latitude: detail?.latitude, // 纬度(必填) + longitude: detail?.longitude, // 经度(必填) + name: '上海体育场', // 位置名(可选) + address: '上海市徐汇区肇嘉浜路128号', // 地址详情(可选) + scale: 15, // 地图缩放级别(1-28) + }) + } + const tags = [{ name: '🕙 急招', icon: '', @@ -143,7 +222,7 @@ function Index() { {/* custom navbar */} - + { Taro.navigateBack() }}> @@ -238,7 +317,7 @@ function Index() { {/* location message */} {/* venue name and distance */} - + 上海体育场 · 1.2km @@ -461,6 +540,8 @@ function Index() { + {/* share popup */} + ) diff --git a/src/pages/index/index.config.ts b/src/pages/index/index.config.ts deleted file mode 100644 index 12abc5f..0000000 --- a/src/pages/index/index.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default definePageConfig({ - navigationBarTitleText: '首页' -}) diff --git a/src/pages/index/index.module.scss b/src/pages/index/index.module.scss deleted file mode 100644 index 0519ecb..0000000 --- a/src/pages/index/index.module.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/index/index.scss b/src/pages/index/index.scss deleted file mode 100644 index 282231f..0000000 --- a/src/pages/index/index.scss +++ /dev/null @@ -1,261 +0,0 @@ -.index-page { - padding: 20px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - min-height: 100vh; - - .page-header { - text-align: center; - margin-bottom: 24px; - - .page-title { - font-size: 28px; - font-weight: bold; - color: white; - display: block; - margin-bottom: 8px; - } - - .page-subtitle { - font-size: 16px; - color: rgba(255, 255, 255, 0.8); - display: block; - } - } - - .user-card { - background: white; - border-radius: 16px; - padding: 20px; - margin-bottom: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - - .user-header { - display: flex; - align-items: center; - gap: 16px; - - .user-info { - flex: 1; - - .username { - font-size: 20px; - font-weight: bold; - color: #333; - display: block; - margin-bottom: 4px; - } - - .user-level { - font-size: 14px; - color: #666; - display: block; - margin-bottom: 4px; - } - - .join-date { - font-size: 12px; - color: #999; - display: block; - } - } - } - } - - .stats-section { - background: white; - border-radius: 16px; - padding: 20px; - margin-bottom: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - - .section-title { - font-size: 18px; - font-weight: bold; - color: #333; - margin-bottom: 16px; - display: block; - } - - :global { - .nut-cell { - background: #f8f9fa; - border-radius: 12px; - margin-bottom: 8px; - border: none; - - &:last-child { - margin-bottom: 0; - } - - .nut-cell__title { - font-weight: 500; - color: #555; - } - - .nut-cell__value { - font-weight: bold; - color: #007bff; - } - } - } - } - - .action-section { - background: white; - border-radius: 16px; - padding: 20px; - margin-bottom: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - - .section-title { - font-size: 18px; - font-weight: bold; - color: #333; - margin-bottom: 16px; - display: block; - } - - .button-group { - display: flex; - flex-direction: column; - gap: 12px; - - .custom-button { - border-radius: 12px; - font-weight: 500; - height: 48px; - border: none; - margin-bottom: 12px; - font-size: 16px; - color: white; - - &.primary-btn { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - } - - &.success-btn { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - } - - &.warning-btn { - background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); - } - - &:disabled { - opacity: 0.6; - } - } - - // 保留 NutUI 按钮样式(备用) - :global { - .nut-button { - border-radius: 12px; - font-weight: 500; - height: 48px; - border: none; - - &--primary { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - } - - &--success { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - } - - &--warning { - background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); - } - - &:disabled { - opacity: 0.6; - } - } - } - } - } - - .loading-section { - background: white; - border-radius: 16px; - padding: 20px; - margin-bottom: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - text-align: center; - - .loading-text { - font-size: 16px; - color: #666; - margin-bottom: 12px; - display: block; - } - - :global { - .nut-progress { - .nut-progress-outer { - background: #f0f0f0; - border-radius: 10px; - } - - .nut-progress-inner { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 10px; - } - } - } - } - - .tips-section { - background: rgba(255, 255, 255, 0.95); - border-radius: 16px; - padding: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - - .tips-title { - font-size: 18px; - font-weight: bold; - color: #333; - margin-bottom: 16px; - display: block; - } - - .tips-content { - .tip-item { - font-size: 14px; - color: #666; - line-height: 1.6; - margin-bottom: 8px; - display: block; - - &:last-child { - margin-bottom: 0; - } - } - } - } -} - -// 响应式设计 -@media (max-width: 768px) { - .index-page { - padding: 16px; - - .page-header { - .page-title { - font-size: 24px; - } - - .page-subtitle { - font-size: 14px; - } - } - - .user-card, - .stats-section, - .action-section, - .loading-section, - .tips-section { - padding: 16px; - margin-bottom: 16px; - } - } -} diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx deleted file mode 100644 index 7c4e67b..0000000 --- a/src/pages/index/index.tsx +++ /dev/null @@ -1,293 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { View, Text, Button } from '@tarojs/components' -import { Cell, Avatar, Progress } from '@nutui/nutui-react-taro' -import Taro from '@tarojs/taro' -// 导入API服务 -import demoApi from '../../services/demoApi' -import commonApi from '../../services/commonApi' -import { - useUserStats, - useUserActions -} from '../../store/userStore' -import './index.scss' - -function Index() { - // 使用Zustand store - const userStats = useUserStats() - const { incrementRequestCount, resetUserStats } = useUserActions() - - // 本地状态管理 - const [loading, setLoading] = useState(false) - const [userProfile, setUserProfile] = useState(null) - const [interests, setInterests] = useState([]) - - // 页面加载时获取数据 - useEffect(() => { - initializeData() - }, []) - - // 初始化数据 - const initializeData = async () => { - try { - // 获取推荐的兴趣爱好 - const interestsRes = await demoApi.getRecommendedInterests() - if (interestsRes.success) { - setInterests(interestsRes.data || []) - } - } catch (error) { - console.log('获取初始数据失败:', error) - } - } - - // 1. 获取用户信息 API 请求 - const handleGetUserProfile = async () => { - console.log('获取用户信息...'); - setLoading(true) - - try { - const response = await demoApi.getUserProfile() - - if (response.success) { - setUserProfile(response.data) - incrementRequestCount() - - Taro.showToast({ - title: '获取用户信息成功', - icon: 'success' - }) - - console.log('用户信息:', response.data) - } - } catch (error) { - console.error('获取用户信息失败:', error) - Taro.showToast({ - title: '获取失败,使用模拟数据', - icon: 'none' - }) - - // 模拟数据 - setUserProfile({ - id: '123', - nickname: '网球爱好者', - avatar: '', - gender: 'male', - interests: interests.slice(0, 3) - }) - incrementRequestCount() - } finally { - setLoading(false) - } - } - - // 2. 提交统计数据 API 请求 - const handleSubmitStats = async () => { - console.log('提交统计数据...'); - setLoading(true) - - try { - const response = await commonApi.submitForm('userStats', [ - { - type: 'userStats', - data: { - requestCount: userStats.requestCount, - matchesCreated: userStats.matchesCreated, - matchesJoined: userStats.matchesJoined, - lastActiveTime: userStats.lastActiveTime, - userId: userProfile?.id || 'guest' - } - } - ]) - - if (response.success) { - incrementRequestCount() - - Taro.showToast({ - title: '统计数据提交成功', - icon: 'success' - }) - - console.log('提交结果:', response.data) - } - } catch (error) { - console.error('提交统计数据失败:', error) - incrementRequestCount() // 即使失败也计数,用于演示 - - Taro.showToast({ - title: '网络模拟提交成功', - icon: 'success' - }) - } finally { - setLoading(false) - } - } - - // 3. 提交反馈 API 请求 - const handleSubmitFeedback = async () => { - console.log('提交用户反馈...'); - setLoading(true) - - try { - const response = await demoApi.submitFeedback({ - matchId: 'demo_match_' + Date.now(), - rating: 5, - recommend: 'yes', - aspects: ['场地环境', '服务质量', '价格合理'], - comments: `用户反馈 - 请求次数: ${userStats.requestCount + 1},体验良好!` - }) - - if (response.success) { - incrementRequestCount() - - Taro.showToast({ - title: '反馈提交成功', - icon: 'success' - }) - - console.log('反馈结果:', response.data) - } - } catch (error) { - console.error('提交反馈失败:', error) - incrementRequestCount() // 即使失败也计数,用于演示 - - Taro.showToast({ - title: '网络模拟提交成功', - icon: 'success' - }) - } finally { - setLoading(false) - } - } - - // 重置所有数据 - const handleResetAllData = () => { - console.log('重置所有数据...'); - resetUserStats() - setUserProfile(null) - - Taro.showToast({ - title: '数据已重置', - icon: 'success' - }) - } - - return ( - - {/* 页面标题 */} - - API 请求演示 - 真实网络请求功能测试 - - - {/* 用户信息卡片 */} - - - - {userProfile?.nickname?.charAt(0) || 'U'} - - - - {userProfile?.nickname || '点击获取用户信息'} - - - 性别: {userProfile?.gender === 'male' ? '男' : userProfile?.gender === 'female' ? '女' : '未知'} - - - 兴趣: {userProfile?.interests?.join(', ') || '暂无'} - - - - - - {/* 统计数据 */} - - 📊 API 请求统计 - - - - - - {interests.length > 0 && ( - - )} - - - {/* API 请求按钮区域 */} - - 🚀 API 请求功能 - - - - - - - - - - - - - {/* 实时进度显示 */} - {loading && ( - - 正在发送 API 请求... - - - )} - - {/* 提示信息 */} - - 💡 API 功能说明 - - • "获取用户信息" - 调用用户资料 API - • "提交统计数据" - 发送统计到服务器 - • "提交用户反馈" - 发送用户评价数据 - • 所有请求都会增加 API 计数统计 - • 请求失败时会自动使用模拟数据 - - - - ) -} - -export default Index diff --git a/src/pages/publishBall/index.tsx b/src/pages/publishBall/index.tsx index 71f92e3..ff903a7 100644 --- a/src/pages/publishBall/index.tsx +++ b/src/pages/publishBall/index.tsx @@ -6,40 +6,40 @@ import PublishForm from './publishForm' import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema'; import { PublishBallFormData } from '../../../types/publishBall'; import PublishService from '@/services/publishService'; -import { getNextHourTime, getEndTime } from '@/utils/timeUtils'; +import { getNextHourTime, getEndTime, delay } from '@/utils'; import images from '@/config/images' import styles from './index.module.scss' const defaultFormData: PublishBallFormData = { - title: '', - image_list: ['https://static-o.oss-cn-shenzhen.aliyuncs.com/images/tpbj/tpss10.jpg'], + title: '', + image_list: ['https://static-o.oss-cn-shenzhen.aliyuncs.com/images/tpbj/tpss10.jpg'], timeRange: { - start_time: getNextHourTime(), + start_time: getNextHourTime(), end_time: getEndTime(getNextHourTime()) }, - activityInfo: { + activityInfo: { play_type: '不限', price: '', - venue_id: null, + venue_id: null, location_name: '', - location: '', - latitude: '', + location: '', + latitude: '', longitude: '', - court_type: '', - court_surface: '', - venue_description_tag: [], - venue_description: '', - venue_image_list: [], + court_type: '', + court_surface: '', + venue_description_tag: [], + venue_description: '', + venue_image_list: [], }, players: [1, 4], skill_level: [1.0, 5.0], descriptionInfo: { - description: '', - description_tag: [], + description: '', + description_tag: [], }, - is_substitute_supported: true, - is_wechat_contact: true, - wechat_contact: '14223332214' + is_substitute_supported: true, + is_wechat_contact: true, + wechat_contact: '14223332214' } const PublishBall: React.FC = () => { @@ -47,7 +47,7 @@ const PublishBall: React.FC = () => { const [formData, setFormData] = useState([ defaultFormData ]) - + // 删除确认弹窗状态 const [deleteConfirm, setDeleteConfirm] = useState<{ visible: boolean; @@ -195,6 +195,13 @@ const PublishBall: React.FC = () => { title: '发布成功', icon: 'success' }) + delay(1000) + // 如果是个人球局,则跳转到详情页,并自动分享 + // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 + Taro.navigateTo({ + // @ts-expect-error: id + url: `/pages/detail/index?id=${res.data.id || 1}&from=publish&autoShare=1` + }) } else { Taro.showToast({ title: res.message, @@ -208,12 +215,12 @@ const PublishBall: React.FC = () => { {/* 活动类型切换 */} - - + { formData.map((item, index) => ( @@ -223,19 +230,19 @@ const PublishBall: React.FC = () => { 第{index + 1}场 - showDeleteConfirm(index)} > - + - + {index > 0 && ( - handleCopyPrevious(index)} > 复制上一场 @@ -244,10 +251,10 @@ const PublishBall: React.FC = () => { )} - updateFormData(key, value, index)} - optionsConfig={publishBallFormSchema} + updateFormData(key, value, index)} + optionsConfig={publishBallFormSchema} /> )) @@ -269,14 +276,14 @@ const PublishBall: React.FC = () => { 确认移除该场次? 该操作不可恢复 - -