diff --git a/src/dealer/qrcode/index.tsx b/src/dealer/qrcode/index.tsx index 882931b..90edb58 100644 --- a/src/dealer/qrcode/index.tsx +++ b/src/dealer/qrcode/index.tsx @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react' import {View, Text, Image} from '@tarojs/components' import {Button, Loading} from '@nutui/nutui-react-taro' -import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro' +import {Download, QrCode} from '@nutui/icons-react-taro' import Taro from '@tarojs/taro' import {useDealerUser} from '@/hooks/useDealerUser' import {generateInviteCode} from '@/api/invite' @@ -115,52 +115,52 @@ const DealerQrcode: React.FC = () => { } // 复制邀请信息 - const copyInviteInfo = () => { - if (!dealerUser?.userId) { - Taro.showToast({ - title: '用户信息未加载', - icon: 'error' - }) - return - } - - const inviteText = `🎉 邀请您加入我的团队! - -扫描小程序码或搜索"时里院子市集"小程序,即可享受优质商品和服务! - -💰 成为我的团队成员,一起赚取丰厚佣金 -🎁 新用户专享优惠等你来拿 - -邀请码:${dealerUser.userId} -快来加入我们吧!` - - Taro.setClipboardData({ - data: inviteText, - success: () => { - Taro.showToast({ - title: '邀请信息已复制', - icon: 'success' - }) - } - }) - } +// const copyInviteInfo = () => { +// if (!dealerUser?.userId) { +// Taro.showToast({ +// title: '用户信息未加载', +// icon: 'error' +// }) +// return +// } +// +// const inviteText = `🎉 邀请您加入我的团队! +// +// 扫描小程序码或搜索"时里院子市集"小程序,即可享受优质商品和服务! +// +// 💰 成为我的团队成员,一起赚取丰厚佣金 +// 🎁 新用户专享优惠等你来拿 +// +// 邀请码:${dealerUser.userId} +// 快来加入我们吧!` +// +// Taro.setClipboardData({ +// data: inviteText, +// success: () => { +// Taro.showToast({ +// title: '邀请信息已复制', +// icon: 'success' +// }) +// } +// }) +// } // 分享小程序码 - const shareMiniProgramCode = () => { - if (!dealerUser?.userId) { - Taro.showToast({ - title: '用户信息未加载', - icon: 'error' - }) - return - } - - // 小程序分享 - Taro.showShareMenu({ - withShareTicket: true, - showShareItems: ['shareAppMessage'] - }) - } + // const shareMiniProgramCode = () => { + // if (!dealerUser?.userId) { + // Taro.showToast({ + // title: '用户信息未加载', + // icon: 'error' + // }) + // return + // } + // + // // 小程序分享 + // Taro.showShareMenu({ + // withShareTicket: true, + // showShareItems: ['shareAppMessage'] + // }) + // } if (!dealerUser) { return ( @@ -263,29 +263,29 @@ const DealerQrcode: React.FC = () => { 保存小程序码到相册 - - } - onClick={copyInviteInfo} - disabled={!dealerUser?.userId || loading} - > - 复制邀请信息 - - - - } - onClick={shareMiniProgramCode} - disabled={!dealerUser?.userId || loading} - > - 分享给好友 - - + {/**/} + {/* }*/} + {/* onClick={copyInviteInfo}*/} + {/* disabled={!dealerUser?.userId || loading}*/} + {/* >*/} + {/* 复制邀请信息*/} + {/* */} + {/**/} + {/**/} + {/* }*/} + {/* onClick={shareMiniProgramCode}*/} + {/* disabled={!dealerUser?.userId || loading}*/} + {/* >*/} + {/* 分享给好友*/} + {/* */} + {/**/} {/* 推广说明 */} diff --git a/src/pages/cart/cart.tsx b/src/pages/cart/cart.tsx index 8711e5b..e415a03 100644 --- a/src/pages/cart/cart.tsx +++ b/src/pages/cart/cart.tsx @@ -41,7 +41,7 @@ function Cart() { useShareAppMessage(() => { return { - title: '购物车 - 网宿小店', + title: '购物车 - 时里院子市集', success: function () { console.log('分享成功'); }, diff --git a/src/pages/index/BestSellers.tsx b/src/pages/index/BestSellers.tsx index a9c9322..b10a93f 100644 --- a/src/pages/index/BestSellers.tsx +++ b/src/pages/index/BestSellers.tsx @@ -51,30 +51,8 @@ const BestSellers = () => { reload() }, []) - // 分享给好友 - useShareAppMessage(() => { - return { - title: goods?.name || '精选商品', - path: `/shop/goodsDetail/index?id=${goods?.goodsId}`, - imageUrl: goods?.image, // 分享图片 - success: function (res: any) { - console.log('分享成功', res); - Taro.showToast({ - title: '分享成功', - icon: 'success', - duration: 2000 - }); - }, - fail: function (res: any) { - console.log('分享失败', res); - Taro.showToast({ - title: '分享失败', - icon: 'none', - duration: 2000 - }); - } - }; - }); + // 注意:不在这里配置分享,避免与首页分享冲突 + // 商品分享应该在商品详情页处理,首页分享应该分享首页本身 return ( <> diff --git a/src/pages/index/Header.tsx b/src/pages/index/Header.tsx index cd5fc25..0427d86 100644 --- a/src/pages/index/Header.tsx +++ b/src/pages/index/Header.tsx @@ -173,9 +173,10 @@ const Header = (props: any) => { return ( <> - + {/*{!props.stickyStatus && }*/} ('') const onKeywords = (keywords: string) => { @@ -39,7 +39,7 @@ function MySearch() { background: '#ffffff', padding: '0 5px', borderRadius: '20px', - marginTop: '100px', + marginTop: `${props.statusBarHeight + 50}px`, }} > diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index 1f9d78f..0fc9fd4 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -47,14 +47,27 @@ function Home() { } useShareAppMessage(() => { + // 获取当前用户ID,用于生成邀请链接 + const userId = Taro.getStorageSync('UserId'); + return { - title: '网宿小店 - 网宿软件', - path: `/pages/index/index`, + title: '时里院子市集', + path: userId ? `/pages/index/index?inviter=${userId}&source=share&t=${Date.now()}` : `/pages/index/index`, success: function () { - console.log('分享成功'); + console.log('首页分享成功'); + Taro.showToast({ + title: '分享成功', + icon: 'success', + duration: 2000 + }); }, fail: function () { - console.log('分享失败'); + console.log('首页分享失败'); + Taro.showToast({ + title: '分享失败', + icon: 'none', + duration: 2000 + }); } }; }); @@ -185,18 +198,32 @@ function Home() { }); }); - // 检查是否有待处理的邀请关系 + // 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载 if (hasPendingInvite()) { console.log('检测到待处理的邀请关系') - // 延迟处理,确保用户信息已加载 + // 延迟处理,确保用户信息已加载,并设置超时保护 setTimeout(async () => { try { - const success = await checkAndHandleInviteRelation() + // 设置超时保护,避免长时间等待 + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('邀请关系处理超时')), 8000) + ); + + const invitePromise = checkAndHandleInviteRelation(); + + const success = await Promise.race([invitePromise, timeoutPromise]); if (success) { console.log('首页邀请关系处理成功') } } catch (error) { console.error('首页邀请关系处理失败:', error) + // 邀请关系处理失败不应该影响页面正常显示 + // 可以选择清除邀请参数,避免重复尝试 + const errorMessage = error instanceof Error ? error.message : String(error) + if (errorMessage?.includes('超时')) { + console.log('邀请关系处理超时,清除邀请参数') + // 可以选择清除邀请参数或稍后重试 + } } }, 2000) } diff --git a/src/pages/user/components/IsDealer.tsx b/src/pages/user/components/IsDealer.tsx index 15936cc..f8315aa 100644 --- a/src/pages/user/components/IsDealer.tsx +++ b/src/pages/user/components/IsDealer.tsx @@ -56,7 +56,7 @@ const UserCell = () => { 管理中心 + className={'pl-3 text-orange-100 font-medium'}>分销中心 {/*门店核销*/} } diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx index 18f1c1e..8b3456b 100644 --- a/src/pages/user/components/UserCard.tsx +++ b/src/pages/user/components/UserCard.tsx @@ -1,6 +1,7 @@ import {Button} from '@nutui/nutui-react-taro' import {Avatar, Tag} from '@nutui/nutui-react-taro' import {View, Text} from '@tarojs/components' +import {Scan} from '@nutui/icons-react-taro'; import {getUserInfo, getWxOpenId} from '@/api/layout'; import Taro from '@tarojs/taro'; import {useEffect, useState, forwardRef, useImperativeHandle} from "react"; @@ -10,9 +11,6 @@ import {TenantId} from "@/config/app"; import {useUser} from "@/hooks/useUser"; import {useUserData} from "@/hooks/useUserData"; import {getStoredInviteParams} from "@/utils/invite"; -import {useQRLogin} from "@/hooks/useQRLogin"; -import {useAdminMode} from "@/hooks/useAdminMode"; -import AdminPanel from "@/components/AdminPanel"; const UserCard = forwardRef((_, ref) => { const { @@ -23,16 +21,6 @@ const UserCard = forwardRef((_, ref) => { const [IsLogin, setIsLogin] = useState(false) const [userInfo, setUserInfo] = useState() - // 扫码登录Hook - const { startScan, isLoading: isScanLoading } = useQRLogin(); - console.log(isScanLoading,'>isScanLoading>>>>') - - // 管理员模式Hook - const { isAdminMode, toggleAdminMode } = useAdminMode(); - - // 管理员面板显示状态 - const [showAdminPanel, setShowAdminPanel] = useState(false); - // 下拉刷新 const handleRefresh = async () => { await refresh() @@ -180,27 +168,6 @@ const UserCard = forwardRef((_, ref) => { }) } - // 处理扫码登录 - const handleQRLogin = async () => { - try { - await startScan(); - } catch (error) { - console.error('扫码登录失败:', error); - } - }; - - console.log(handleQRLogin,'handleQRLogin()') - - // 打开管理员面板 - const handleOpenAdminPanel = () => { - setShowAdminPanel(true); - }; - - // 关闭管理员面板 - const handleCloseAdminPanel = () => { - setShowAdminPanel(false); - }; - return ( @@ -238,31 +205,7 @@ const UserCard = forwardRef((_, ref) => { ) : ''} - {isAdmin() && ( - - {/* 管理员模式切换按钮 */} - - {isAdminMode ? '管理员' : '普通用户'} - - - {/* 管理员功能入口 */} - {isAdminMode && ( - - 管理面板 - - )} - - )} + {isAdmin() && navTo('/user/store/verification', true)} />} navTo('/user/profile/profile', true)}> {'个人资料'} @@ -291,14 +234,6 @@ const UserCard = forwardRef((_, ref) => { - - {/* 管理员面板 */} - {isAdmin() && ( - - )} ) diff --git a/src/pages/user/components/UserGrid.tsx b/src/pages/user/components/UserGrid.tsx new file mode 100644 index 0000000..7fc7765 --- /dev/null +++ b/src/pages/user/components/UserGrid.tsx @@ -0,0 +1,111 @@ +import {Grid, ConfigProvider} from '@nutui/nutui-react-taro' +import navTo from "@/utils/common"; +import Taro from '@tarojs/taro' +import {View} from '@tarojs/components' +import {ShieldCheck, Location, Tips, Ask, Dongdong, People, AfterSaleService, Logout} from '@nutui/icons-react-taro' +import {useUser} from "@/hooks/useUser"; + +const UserCell = () => { + const {logoutUser} = useUser(); + + const onLogout = () => { + Taro.showModal({ + title: '提示', + content: '确定要退出登录吗?', + success: function (res) { + if (res.confirm) { + // 使用 useUser hook 的 logoutUser 方法 + logoutUser(); + Taro.reLaunch({ + url: '/pages/index/index' + }) + } + } + }) + } + + return ( + <> + + 我的服务 + + + navTo('/user/address/index', true)}> + + + + + + + + navTo('/user/userVerify/index', true)}> + + + + + + + + navTo('/dealer/team/index', true)}> + + + + + + + + navTo('/dealer/qrcode/index', true)}> + + + + + + + + navTo('/admin/index', true)}> + + + + + + + + navTo('/user/help/index')}> + + + + + + + + navTo('/user/about/index')}> + + + + + + + + + + + + + + + + + + + > + ) +} +export default UserCell diff --git a/src/pages/user/user.tsx b/src/pages/user/user.tsx index c4f4930..2ad1f98 100644 --- a/src/pages/user/user.tsx +++ b/src/pages/user/user.tsx @@ -2,17 +2,13 @@ import {useEffect, useRef} from 'react' import {PullToRefresh} from '@nutui/nutui-react-taro' import UserCard from "./components/UserCard"; import UserOrder from "./components/UserOrder"; -import UserCell from "./components/UserCell"; import UserFooter from "./components/UserFooter"; -import {useUser} from "@/hooks/useUser"; import {useUserData} from "@/hooks/useUserData"; import './user.scss' import IsDealer from "./components/IsDealer"; +import UserGrid from "@/pages/user/components/UserGrid"; function User() { - const { - isAdmin - } = useUser(); const { refresh } = useUserData() const userCardRef = useRef() @@ -29,28 +25,6 @@ function User() { useEffect(() => { }, []); - /** - * 门店核销管理 - */ - if (isAdmin()) { - return ( - - - - - - - - - - ) - } - return ( - + diff --git a/src/shop/goodsDetail/index.tsx b/src/shop/goodsDetail/index.tsx index 7b39342..4d16e73 100644 --- a/src/shop/goodsDetail/index.tsx +++ b/src/shop/goodsDetail/index.tsx @@ -24,6 +24,7 @@ const GoodsDetail = () => { const [specAction, setSpecAction] = useState<'cart' | 'buy'>('cart'); // const [selectedSku, setSelectedSku] = useState(null); const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); const router = Taro.getCurrentInstance().router; const goodsId = router?.params?.id; @@ -117,48 +118,57 @@ const GoodsDetail = () => { } }; - useEffect(() => { - if (goodsId) { - setLoading(true); + // 重新加载数据的函数 + const reloadData = async () => { + if (!goodsId) return; + + setLoading(true); + setError(null); + + try { + // 设置超时时间 + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('请求超时')), 10000) + ); // 加载商品详情 - getShopGoods(Number(goodsId)) - .then((res) => { - // 处理富文本内容,去掉图片间距 - if (res.content) { - res.content = wxParse(res.content); - } - setGoods(res); - if (res.files) { - const arr = JSON.parse(res.files); - arr.length > 0 && setFiles(arr); - } - }) - .catch((error) => { - console.error("Failed to fetch goods detail:", error); - }) - .finally(() => { - setLoading(false); - }); + const goodsPromise = getShopGoods(Number(goodsId)).then((res) => { + // 处理富文本内容,去掉图片间距 + if (res.content) { + res.content = wxParse(res.content); + } + setGoods(res); + if (res.files) { + const arr = JSON.parse(res.files); + arr.length > 0 && setFiles(arr); + } + return res; + }); - // 加载商品规格 - listShopGoodsSpec({goodsId: Number(goodsId)} as any) - .then((data) => { - setSpecs(data || []); - }) - .catch((error) => { - console.error("Failed to fetch goods specs:", error); - }); + // 等待商品详情加载完成(带超时) + await Promise.race([goodsPromise, timeout]); - // 加载商品SKU - listShopGoodsSku({goodsId: Number(goodsId)} as any) - .then((data) => { - setSkus(data || []); - }) - .catch((error) => { - console.error("Failed to fetch goods skus:", error); - }); + // 并行加载规格和SKU(不阻塞主要内容显示) + Promise.all([ + listShopGoodsSpec({goodsId: Number(goodsId)} as any) + .then((data) => setSpecs(data || [])) + .catch((error) => console.error("Failed to fetch goods specs:", error)), + + listShopGoodsSku({goodsId: Number(goodsId)} as any) + .then((data) => setSkus(data || [])) + .catch((error) => console.error("Failed to fetch goods skus:", error)) + ]); + + } catch (error: any) { + console.error("Failed to fetch goods detail:", error); + setError(error.message || '加载失败,请重试'); + } finally { + setLoading(false); } + }; + + useEffect(() => { + reloadData(); }, [goodsId]); // 分享给好友 @@ -186,8 +196,42 @@ const GoodsDetail = () => { }; }); - if (!goods || loading) { - return 加载中...; + // 错误状态 + if (error) { + return ( + + + 😵 + 页面加载失败 + {error} + + + 重新加载 + + Taro.navigateBack()} + > + 返回上页 + + + + + ); + } + + // 加载状态 - 使用更好的加载UI + if (loading || !goods) { + return ( + + + 正在加载商品信息... + 请稍候 + + ); } return ( diff --git a/src/utils/invite.ts b/src/utils/invite.ts index 655d73f..34975d6 100644 --- a/src/utils/invite.ts +++ b/src/utils/invite.ts @@ -65,11 +65,23 @@ export function parseInviteParams(options: any): InviteParams | null { } } - // 从 query 参数中解析邀请信息(兼容旧版本) - if (options.referrer) { - return { - inviter: options.referrer, - source: 'link' + // 从 query 参数中解析邀请信息(处理首页分享链接) + if (options.query) { + const query = options.query + if (query.inviter) { + return { + inviter: query.inviter, + source: query.source || 'share', + t: query.t + } + } + + // 兼容旧版本 + if (query.referrer) { + return { + inviter: query.referrer, + source: 'link' + } } } @@ -163,13 +175,21 @@ export async function handleInviteRelation(userId: number): Promise { return true // 返回true表示关系已存在 } + // 设置API调用超时 + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('API调用超时')), 5000) + ); + // 使用新的绑定推荐关系接口 - await bindRefereeRelation({ + const apiPromise = bindRefereeRelation({ dealerId: inviterId, userId: userId, source: inviteParams.source || 'qrcode', scene: inviteParams.source === 'qrcode' ? `uid_${inviterId}` : `inviter=${inviterId}&source=${inviteParams.source}&t=${inviteParams.t}` - }) + }); + + // 等待API调用完成或超时 + await Promise.race([apiPromise, timeoutPromise]); // 标记邀请关系已处理(设置过期时间为7天) Taro.setStorageSync(relationKey, { @@ -185,6 +205,16 @@ export async function handleInviteRelation(userId: number): Promise { return true } catch (error) { console.error('建立邀请关系失败:', error) + + // 如果是网络错误或超时,不清除邀请参数,允许稍后重试 + const errorMessage = error instanceof Error ? error.message : String(error) + if (errorMessage.includes('超时') || errorMessage.includes('网络')) { + console.log('网络问题,保留邀请参数供稍后重试') + return false + } + + // 其他错误(如业务逻辑错误),清除邀请参数 + clearInviteParams() return false } } @@ -329,9 +359,30 @@ export async function checkAndHandleInviteRelation(): Promise { } console.log('使用用户ID处理邀请关系:', finalUserId) - return await handleInviteRelation(parseInt(finalUserId)) + + // 设置整体超时保护 + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('邀请关系处理整体超时')), 6000) + ); + + const handlePromise = handleInviteRelation(parseInt(finalUserId)); + + return await Promise.race([handlePromise, timeoutPromise]); } catch (error) { console.error('检查邀请关系失败:', error) + + // 记录失败次数,避免无限重试 + const failKey = 'invite_handle_fail_count' + const failCount = Taro.getStorageSync(failKey) || 0 + + if (failCount >= 3) { + console.log('邀请关系处理失败次数过多,清除邀请参数') + clearInviteParams() + Taro.removeStorageSync(failKey) + } else { + Taro.setStorageSync(failKey, failCount + 1) + } + return false } } diff --git a/快速验证修复.md b/快速验证修复.md new file mode 100644 index 0000000..ccca212 --- /dev/null +++ b/快速验证修复.md @@ -0,0 +1,104 @@ +# 快速验证分享链接白屏问题修复 + +## 🚀 立即验证步骤 + +### 1. 重新编译项目 +```bash +# 清理并重新编译 +npm run build:weapp +``` + +### 2. 测试首页分享链接 +1. **打开微信开发者工具** +2. **在首页点击分享按钮** +3. **复制分享链接** +4. **从分享链接重新进入** + +### 3. 观察控制台日志 +应该看到以下日志序列: + +``` +=== 小程序启动参数处理开始 === +完整启动参数: {...} +检测到待处理的邀请关系 +使用用户ID处理邀请关系: [用户ID] +首页邀请关系处理成功 (或失败,但不影响页面) +=== 小程序启动参数处理结束 === +``` + +## 🔧 修复内容总结 + +### 修复的文件: +1. **`src/pages/index/index.tsx`** - 首页邀请处理超时保护 +2. **`src/utils/invite.ts`** - 邀请API调用超时和错误处理 +3. **`src/shop/goodsDetail/index.tsx`** - 商品详情页加载优化 + +### 关键改进: +- ✅ 邀请关系处理添加超时保护(5-8秒) +- ✅ API调用失败不阻塞页面显示 +- ✅ 改进错误处理和类型安全 +- ✅ 失败重试计数机制 +- ✅ 更好的加载UI和错误提示 + +## 🧪 测试场景 + +### 场景1:正常分享链接 +- **操作**:从首页分享链接进入 +- **预期**:页面正常加载,不出现白屏 + +### 场景2:带邀请参数的链接 +- **操作**:从经销商分享的链接进入 +- **预期**:页面正常显示,邀请关系在后台处理 + +### 场景3:网络异常 +- **操作**:断网状态下从分享链接进入 +- **预期**:页面能显示基本内容,邀请处理超时后不影响页面 + +## 🔍 问题排查 + +如果仍有问题,请检查: + +### 1. 编译是否成功 +```bash +# 查看编译输出 +npm run build:weapp +``` + +### 2. 清理缓存 +在微信开发者工具中: +- 点击"清缓存" -> "清除全部缓存" +- 重新编译项目 + +### 3. 查看错误日志 +在控制台中查看是否有: +- TypeScript编译错误 +- 运行时错误 +- API调用错误 + +### 4. 手动测试邀请功能 +在控制台执行: +```javascript +// 查看邀请参数 +console.log('邀请参数:', Taro.getStorageSync('invite_params')) + +// 清除邀请参数(如果需要重置) +Taro.removeStorageSync('invite_params') +``` + +## ✅ 成功标准 + +修复成功的标志: +1. 首页分享链接不再出现长时间白屏 +2. 控制台日志显示正常的处理流程 +3. 邀请关系处理不影响页面显示 +4. 网络异常时页面仍能访问 + +## 🆘 如果仍有问题 + +请提供: +1. 控制台的完整错误日志 +2. 网络面板的API请求状态 +3. 具体的复现步骤 +4. 是否有特定的邀请参数 + +这样我可以进一步诊断和修复问题。 diff --git a/最终验证指南.md b/最终验证指南.md new file mode 100644 index 0000000..a754543 --- /dev/null +++ b/最终验证指南.md @@ -0,0 +1,149 @@ +# 🎉 分享链接白屏问题修复完成 + +## ✅ 修复内容总结 + +### 问题根因 +1. **分享配置冲突**:BestSellers组件的分享配置覆盖了首页分享配置 +2. **邀请参数处理阻塞**:邀请关系API调用可能超时,导致页面卡住 +3. **缺少错误处理**:没有合适的超时和错误处理机制 + +### 修复方案 +1. **移除冲突的分享配置**:删除BestSellers中的useShareAppMessage +2. **改进首页分享**:添加邀请参数到分享链接 +3. **添加超时保护**:邀请处理8秒超时,API调用5秒超时 +4. **完善错误处理**:区分网络错误和业务错误 +5. **修复TypeScript错误**:正确处理unknown类型 + +## 🧪 立即验证步骤 + +### 1. 启动开发环境 +```bash +npm run dev:weapp +``` + +### 2. 测试首页分享 +1. **在微信开发者工具中打开小程序** +2. **进入首页** +3. **点击右上角分享按钮** +4. **选择"分享给朋友"** +5. **从分享链接重新进入** + +### 3. 预期结果 +- ✅ 页面正常加载,显示首页内容 +- ✅ 不再出现"页面加载失败"错误 +- ✅ 不再出现"无效的ID参数"错误 +- ✅ 邀请关系在后台处理(如果有的话) + +### 4. 控制台日志验证 +应该看到类似以下日志: + +``` +=== 小程序启动参数处理开始 === +完整启动参数: { + "path": "pages/index/index", + "query": { + "inviter": "123", + "source": "share", + "t": "1234567890" + } +} +✅ 成功检测到邀请参数: {inviter: "123", source: "share", t: "1234567890"} +检测到待处理的邀请关系 +使用用户ID处理邀请关系: [用户ID] +首页邀请关系处理成功 +=== 小程序启动参数处理结束 === +``` + +## 🔍 测试场景 + +### 场景1:普通用户分享首页 +- **操作**:未登录用户分享首页 +- **预期**:分享链接为 `/pages/index/index`(无邀请参数) + +### 场景2:已登录用户分享首页 +- **操作**:已登录用户分享首页 +- **预期**:分享链接包含邀请参数 `/pages/index/index?inviter=用户ID&source=share&t=时间戳` + +### 场景3:从邀请链接进入 +- **操作**:点击包含邀请参数的分享链接 +- **预期**:页面正常显示,邀请关系在后台建立 + +### 场景4:网络异常测试 +- **操作**:断网状态下从分享链接进入 +- **预期**:页面能显示基本内容,邀请处理超时后不影响页面 + +## 🛠️ 问题排查 + +### 如果仍有问题,请检查: + +#### 1. 清理缓存 +```bash +# 在微信开发者工具中点击"清缓存" -> "清除全部缓存" +# 然后重新编译 +npm run build:weapp +``` + +#### 2. 查看分享配置 +在控制台执行: +```javascript +// 查看当前页面的分享配置 +console.log('当前页面:', Taro.getCurrentPages()) + +// 查看用户登录状态 +console.log('用户ID:', Taro.getStorageSync('UserId')) +console.log('访问令牌:', Taro.getStorageSync('access_token')) +``` + +#### 3. 手动测试邀请参数 +```javascript +// 查看邀请参数 +console.log('邀请参数:', Taro.getStorageSync('invite_params')) + +// 清除邀请参数(如果需要重置测试) +Taro.removeStorageSync('invite_params') +``` + +## 📋 成功验证清单 + +- [ ] **首页分享功能** + - [ ] 分享按钮正常工作 + - [ ] 分享链接格式正确 + - [ ] 从分享链接能正常进入首页 + +- [ ] **邀请功能** + - [ ] 已登录用户分享包含邀请参数 + - [ ] 邀请参数正确解析 + - [ ] 邀请关系正常建立 + +- [ ] **错误处理** + - [ ] 网络异常时页面仍可访问 + - [ ] 超时保护正常工作 + - [ ] 错误日志清晰明确 + +- [ ] **性能表现** + - [ ] 页面加载时间合理(3秒内) + - [ ] 邀请处理不阻塞页面显示 + - [ ] 用户体验流畅 + +## 🎯 修复对比 + +### 修复前 ❌ +- 首页分享链接跳转到商品详情页 +- 显示"无效的ID参数"错误 +- 页面无法正常显示 + +### 修复后 ✅ +- 首页分享链接正确跳转到首页 +- 页面正常显示内容 +- 邀请功能正常工作 +- 错误处理完善 + +## 🚀 下一步 + +如果验证通过,建议: +1. **真机测试**:在实际手机上测试分享功能 +2. **用户体验测试**:让其他用户测试分享链接 +3. **性能监控**:观察页面加载时间和错误率 +4. **功能完善**:考虑添加分享统计和分析 + +现在您可以测试首页分享功能了!应该不再出现"页面加载失败"的错误页面。 diff --git a/测试首页分享链接问题.md b/测试首页分享链接问题.md new file mode 100644 index 0000000..bee5584 --- /dev/null +++ b/测试首页分享链接问题.md @@ -0,0 +1,202 @@ +# 首页分享链接白屏问题测试指南 + +## 问题描述 +用户从分享的首页链接点击进入时,页面显示"加载中..."后出现白屏,但商品详情页分享链接正常。 + +## 问题根因分析 +1. **邀请参数处理阻塞**:首页加载时会检查邀请关系,如果API调用失败或超时,可能导致页面卡住 +2. **缺少超时保护**:邀请关系处理没有合适的超时机制 +3. **错误处理不完善**:网络错误时没有降级处理 + +## 修复内容 +已对以下文件进行修复: + +### 1. `src/pages/index/index.tsx` +- 添加邀请关系处理的超时保护(8秒) +- 邀请处理失败不阻塞页面正常显示 +- 改进错误日志记录 + +### 2. `src/utils/invite.ts` +- `handleInviteRelation`: 添加API调用超时(5秒) +- `checkAndHandleInviteRelation`: 添加整体超时保护(6秒) +- 添加失败重试计数,避免无限重试 +- 区分网络错误和业务错误的处理策略 + +## 测试步骤 + +### 准备工作 +```bash +# 重新编译项目 +npm run build:weapp + +# 或启动开发模式 +npm run dev:weapp +``` + +### 测试用例1:正常首页分享链接 +1. **操作步骤**: + - 在小程序首页点击分享 + - 分享给微信好友或群聊 + - 从分享链接点击进入 + +2. **预期结果**: + - 页面应该正常加载,不出现长时间白屏 + - 如果有邀请参数,应该在后台处理,不影响页面显示 + - 控制台应该有相关日志输出 + +### 测试用例2:网络异常情况 +1. **操作步骤**: + - 在微信开发者工具中设置网络为"离线"或"慢速" + - 从首页分享链接进入 + +2. **预期结果**: + - 页面应该能正常显示基本内容 + - 邀请关系处理超时后不影响页面 + - 控制台显示超时错误日志 + +### 测试用例3:带邀请参数的分享链接 +1. **操作步骤**: + - 使用带邀请参数的链接(如从经销商分享的链接) + - 点击进入首页 + +2. **预期结果**: + - 页面正常加载 + - 邀请关系在后台处理 + - 处理成功或失败都不影响页面显示 + +## 调试方法 + +### 1. 查看控制台日志 +在微信开发者工具的控制台中查看以下日志: + +```javascript +// 正常情况下应该看到: +"检测到待处理的邀请关系" +"使用用户ID处理邀请关系: [用户ID]" +"首页邀请关系处理成功" 或 "首页邀请关系处理失败" + +// 超时情况下应该看到: +"邀请关系处理超时,清除邀请参数" +"邀请关系处理整体超时" +``` + +### 2. 手动测试邀请参数 +在控制台中执行: + +```javascript +// 查看当前邀请参数 +console.log('邀请参数:', Taro.getStorageSync('invite_params')) + +// 查看用户信息 +console.log('用户ID:', Taro.getStorageSync('UserId')) + +// 手动触发邀请关系处理 +import { checkAndHandleInviteRelation } from '@/utils/invite' +checkAndHandleInviteRelation().then(result => { + console.log('手动处理结果:', result) +}) +``` + +### 3. 清除测试数据 +如果需要重置测试环境: + +```javascript +// 清除邀请相关数据 +Taro.removeStorageSync('invite_params') +Taro.removeStorageSync('invite_handle_fail_count') + +// 清除邀请关系记录 +const keys = Taro.getStorageInfoSync().keys +keys.forEach(key => { + if (key.startsWith('invite_relation_')) { + Taro.removeStorageSync(key) + } +}) +``` + +## 性能验证 + +### 页面加载时间测试 +```javascript +// 在首页组件中添加性能监控 +const startTime = Date.now() + +useEffect(() => { + // 页面加载完成后 + const endTime = Date.now() + console.log('首页加载耗时:', endTime - startTime, 'ms') +}, []) +``` + +### 预期性能指标 +- **正常情况**:首页应在2秒内完成基本加载 +- **网络异常**:即使邀请处理失败,页面也应在10秒内显示 +- **超时保护**:邀请处理最多8秒后自动放弃 + +## 验证清单 + +- [ ] **基础功能** + - [ ] 首页分享链接可以正常打开 + - [ ] 页面内容正常显示 + - [ ] 不出现长时间白屏 + +- [ ] **邀请功能** + - [ ] 带邀请参数的链接正常处理 + - [ ] 邀请处理失败不影响页面显示 + - [ ] 超时保护机制正常工作 + +- [ ] **错误处理** + - [ ] 网络异常时页面仍可访问 + - [ ] 错误日志正常记录 + - [ ] 失败重试机制正常 + +- [ ] **性能表现** + - [ ] 页面加载时间在可接受范围 + - [ ] 邀请处理不阻塞主要功能 + - [ ] 内存和存储使用合理 + +## 常见问题排查 + +### 问题1:仍然出现白屏 +**可能原因**: +- 代码未正确编译 +- 缓存未清理 +- 其他组件加载问题 + +**解决方法**: +```bash +# 清理缓存并重新编译 +rm -rf dist/ +npm run build:weapp +``` + +### 问题2:邀请关系处理失败 +**可能原因**: +- API接口问题 +- 用户信息获取失败 +- 网络连接问题 + +**解决方法**: +- 检查API接口状态 +- 确认用户登录状态 +- 查看网络请求日志 + +### 问题3:页面加载缓慢 +**可能原因**: +- 邀请处理超时时间过长 +- 其他API调用阻塞 + +**解决方法**: +- 调整超时时间设置 +- 优化API调用顺序 + +## 成功标准 + +✅ **修复成功的标志**: +1. 从首页分享链接进入不再出现长时间白屏 +2. 邀请参数处理在后台进行,不阻塞页面显示 +3. 网络异常时页面仍能正常访问 +4. 控制台日志显示正常的处理流程 +5. 页面加载时间在合理范围内 + +如果以上标准都满足,说明首页分享链接白屏问题已经解决。 diff --git a/首页分享链接测试步骤.md b/首页分享链接测试步骤.md new file mode 100644 index 0000000..919e7a6 --- /dev/null +++ b/首页分享链接测试步骤.md @@ -0,0 +1,142 @@ +# 首页分享链接白屏问题测试 + +## ✅ 编译成功 +项目已成功编译,没有TypeScript错误。现在可以开始测试。 + +## 🧪 立即测试步骤 + +### 1. 启动开发环境 +```bash +npm run dev:weapp +``` + +### 2. 测试首页分享功能 + +#### 步骤A:创建分享链接 +1. 在微信开发者工具中打开小程序 +2. 进入首页(pages/index/index) +3. 点击右上角的分享按钮 +4. 选择"分享给朋友" +5. 复制生成的分享链接 + +#### 步骤B:从分享链接进入 +1. 关闭当前小程序 +2. 从分享链接重新进入 +3. **观察加载过程** + +### 3. 预期结果对比 + +#### ❌ 修复前(问题状态) +- 页面显示"加载中..." +- 长时间白屏 +- 无法正常显示内容 +- 用户无法操作 + +#### ✅ 修复后(正常状态) +- 页面正常加载 +- 邀请关系在后台处理(不阻塞页面) +- 即使邀请处理失败,页面仍正常显示 +- 控制台有相关日志输出 + +### 4. 关键日志监控 + +在微信开发者工具的控制台中,应该看到: + +``` +=== 小程序启动参数处理开始 === +完整启动参数: {...} +检测到待处理的邀请关系 +使用用户ID处理邀请关系: [用户ID] +首页邀请关系处理成功 (或失败但不影响页面) +=== 小程序启动参数处理结束 === +``` + +### 5. 特殊场景测试 + +#### 场景1:网络异常测试 +1. 在开发者工具中设置网络为"离线" +2. 从首页分享链接进入 +3. **预期**:页面应该能显示基本内容,邀请处理超时后不影响页面 + +#### 场景2:邀请API失败测试 +1. 暂时修改邀请API地址为无效地址 +2. 从分享链接进入 +3. **预期**:邀请处理失败,但页面正常显示 + +### 6. 性能验证 + +#### 加载时间测试 +- **目标**:首页应在3秒内完成基本加载 +- **方法**:在控制台查看加载时间日志 +- **标准**:即使邀请处理失败,页面也应快速显示 + +### 7. 问题排查 + +如果仍有问题,请检查: + +#### 检查1:清理缓存 +```bash +# 在微信开发者工具中 +# 点击"清缓存" -> "清除全部缓存" +# 然后重新编译 +npm run build:weapp +``` + +#### 检查2:查看具体错误 +在控制台中执行: +```javascript +// 查看邀请参数 +console.log('邀请参数:', Taro.getStorageSync('invite_params')) + +// 查看用户登录状态 +console.log('用户ID:', Taro.getStorageSync('UserId')) +console.log('访问令牌:', Taro.getStorageSync('access_token')) + +// 手动测试邀请处理 +import { checkAndHandleInviteRelation } from '@/utils/invite' +checkAndHandleInviteRelation().then(result => { + console.log('手动处理结果:', result) +}).catch(error => { + console.log('手动处理错误:', error) +}) +``` + +#### 检查3:API状态 +在网络面板中查看: +- `/api/wx-login/loginByOpenId` 请求状态 +- `/shop/shop-dealer-referee` 请求状态 +- 是否有请求超时或失败 + +### 8. 成功验证清单 + +- [ ] **基础功能** + - [ ] 首页分享链接可以正常打开 + - [ ] 页面内容正常显示 + - [ ] 不出现长时间白屏 + +- [ ] **邀请功能** + - [ ] 邀请参数正常解析 + - [ ] 邀请关系处理不阻塞页面 + - [ ] 处理失败时有合适的日志 + +- [ ] **错误处理** + - [ ] 网络异常时页面仍可访问 + - [ ] 超时保护机制正常工作 + - [ ] 错误日志清晰明确 + +- [ ] **性能表现** + - [ ] 页面加载时间合理 + - [ ] 用户体验流畅 + - [ ] 内存使用正常 + +## 🎯 测试结论 + +如果以上测试都通过,说明首页分享链接白屏问题已经解决。 + +如果仍有问题,请提供: +1. 具体的错误信息 +2. 控制台完整日志 +3. 网络请求状态 +4. 复现的具体步骤 + +这样我可以进一步诊断和修复。