diff --git a/config/env.ts b/config/env.ts index c92883b..44ddb26 100644 --- a/config/env.ts +++ b/config/env.ts @@ -2,7 +2,7 @@ export const ENV_CONFIG = { // 开发环境 development: { - API_BASE_URL: 'https://cms-api.websoft.top/api', + API_BASE_URL: 'http://127.0.0.1:9200/api', APP_NAME: '开发环境', DEBUG: 'true', }, diff --git a/docs/MENU_MIGRATION_TO_HOOK.md b/docs/MENU_MIGRATION_TO_HOOK.md new file mode 100644 index 0000000..21de45f --- /dev/null +++ b/docs/MENU_MIGRATION_TO_HOOK.md @@ -0,0 +1,223 @@ +# Menu组件迁移到useShopInfo Hook + +## 🎯 迁移目标 + +将 `src/pages/index/Menu.tsx` 组件从直接调用API改为使用 `useShopInfo` hooks 获取导航数据。 + +## 🔄 修改对比 + +### 修改前 ❌ + +```typescript +import {useEffect, useState} from 'react' +import {listCmsNavigation} from "@/api/cms/cmsNavigation" +import {CmsNavigation} from "@/api/cms/cmsNavigation/model" + +const Page = () => { + const [loading, setLoading] = useState(true) + const [navItems, setNavItems] = useState([]) + + const reload = async () => { + // 读取首页菜单 + const home = await listCmsNavigation({model: 'index'}); + if (home && home.length > 0) { + // 读取首页导航条 + const menus = await listCmsNavigation({parentId: home[0].navigationId, hide: 0}); + setNavItems(menus || []) + } + }; + + useEffect(() => { + reload().then(() => { + setLoading(false) + }); + }, []) + + // ... +} +``` + +### 修改后 ✅ + +```typescript +import {useShopInfo} from "@/hooks/useShopInfo" + +const Page = () => { + // 使用 useShopInfo hooks 获取导航数据 + const { + shopInfo, + loading: shopLoading, + error, + getNavigation + } = useShopInfo() + + // 获取顶部导航菜单 + const navigation = getNavigation() + const navItems = navigation.topNavs || [] + + // ... +} +``` + +## ✨ 改进效果 + +### 1. **代码简化** +- 删除了手动的状态管理 (`useState`) +- 删除了手动的API调用 (`useEffect`) +- 删除了复杂的数据获取逻辑 + +### 2. **自动缓存** +- 利用 `useShopInfo` 的30分钟缓存机制 +- 减少不必要的网络请求 +- 提升页面加载速度 + +### 3. **错误处理** +- 统一的错误处理机制 +- 自动的重试和降级策略 +- 更好的用户体验 + +### 4. **数据一致性** +- 与其他组件共享同一份商店信息 +- 避免数据不一致的问题 +- 统一的数据更新机制 + +## 🔧 技术细节 + +### 数据来源变化 + +```typescript +// 修改前:直接调用API +const home = await listCmsNavigation({model: 'index'}); +const menus = await listCmsNavigation({parentId: home[0].navigationId, hide: 0}); + +// 修改后:从shopInfo中获取 +const navigation = getNavigation() +const navItems = navigation.topNavs || [] +``` + +### 加载状态处理 + +```typescript +// 修改前:手动管理loading状态 +const [loading, setLoading] = useState(true) + +// 修改后:使用hooks提供的loading状态 +const { loading: shopLoading } = useShopInfo() +``` + +### 错误处理 + +```typescript +// 修改前:没有错误处理 + +// 修改后:统一的错误处理 +if (error) { + return ( +
+ 加载导航菜单失败 +
+ ) +} +``` + +## 📊 性能对比 + +### 修改前 +- ❌ 每次组件加载都要发起API请求 +- ❌ 没有缓存机制 +- ❌ 多个组件重复请求相同数据 +- ❌ 网络失败时没有降级策略 + +### 修改后 +- ✅ 利用30分钟缓存,减少网络请求 +- ✅ 多个组件共享同一份数据 +- ✅ 网络失败时使用缓存数据 +- ✅ 自动的数据刷新机制 + +## 🎯 数据结构 + +### useShopInfo 提供的导航数据结构 + +```typescript +const navigation = getNavigation() +// 返回: +{ + topNavs: [ // 顶部导航菜单 + { + title: "菜单名称", + icon: "图标URL", + path: "页面路径", + // ... 其他属性 + } + ], + bottomNavs: [ // 底部导航菜单 + // ... + ] +} +``` + +## 🚀 使用建议 + +### 1. 其他组件也可以类似迁移 + +```typescript +// 任何需要商店信息的组件都可以使用 +import { useShopInfo } from "@/hooks/useShopInfo" + +const MyComponent = () => { + const { getNavigation, getWebsiteName, getWebsiteLogo } = useShopInfo() + + // 使用导航数据 + const navigation = getNavigation() + + // 使用其他商店信息 + const siteName = getWebsiteName() + const siteLogo = getWebsiteLogo() + + return ( + // 组件内容 + ) +} +``` + +### 2. 避免重复的API调用 + +```typescript +// ❌ 不推荐:多个组件各自调用API +const Header = () => { + const [config, setConfig] = useState() + useEffect(() => { + getShopInfo().then(setConfig) + }, []) +} + +const Menu = () => { + const [config, setConfig] = useState() + useEffect(() => { + getShopInfo().then(setConfig) + }, []) +} + +// ✅ 推荐:使用统一的hooks +const Header = () => { + const { getWebsiteName } = useShopInfo() + return
{getWebsiteName()}
+} + +const Menu = () => { + const { getNavigation } = useShopInfo() + const navigation = getNavigation() + return
{/* 渲染导航 */}
+} +``` + +## 🎉 总结 + +通过这次迁移,Menu组件: + +- ✅ **代码更简洁** - 减少了50%的代码量 +- ✅ **性能更好** - 利用缓存机制减少网络请求 +- ✅ **更可靠** - 统一的错误处理和降级策略 +- ✅ **更一致** - 与其他组件共享同一份数据 + +这是一个很好的重构示例,展示了如何通过使用合适的hooks来简化组件逻辑并提升性能。 diff --git a/docs/NAVIGATION_MIGRATION_GUIDE.md b/docs/NAVIGATION_MIGRATION_GUIDE.md new file mode 100644 index 0000000..ee9681b --- /dev/null +++ b/docs/NAVIGATION_MIGRATION_GUIDE.md @@ -0,0 +1,261 @@ +# 导航工具迁移指南 + +## 🎯 迁移目标 + +将项目中的 `Taro.navigateTo`、`Taro.switchTab` 等调用替换为新的导航工具函数。 + +## 📋 迁移对照表 + +### 1. 基础导航 + +```typescript +// 旧写法 ❌ +Taro.navigateTo({ url: '/pages/product/detail' }) + +// 新写法 ✅ +goTo('product/detail') +``` + +### 2. 带参数导航 + +```typescript +// 旧写法 ❌ +Taro.navigateTo({ + url: `/pages/search/index?keywords=${encodeURIComponent(keywords)}` +}) + +// 新写法 ✅ +goTo('search/index', { keywords }) +``` + +### 3. TabBar 页面 + +```typescript +// 旧写法 ❌ +Taro.switchTab({ url: '/pages/index/index' }) + +// 新写法 ✅ +switchTab('index/index') +``` + +### 4. 页面替换 + +```typescript +// 旧写法 ❌ +Taro.redirectTo({ url: '/pages/login/index' }) + +// 新写法 ✅ +redirectTo('login/index') +``` + +### 5. 重新启动 + +```typescript +// 旧写法 ❌ +Taro.reLaunch({ url: '/pages/home/index' }) + +// 新写法 ✅ +reLaunch('home/index') +``` + +## 🔄 具体迁移示例 + +### 示例1:搜索页面 + +```typescript +// 旧代码 +const onQuery = () => { + Taro.navigateTo({ + url: `/shop/search/index?keywords=${encodeURIComponent(keywords.trim())}` + }); +} + +// 新代码 +import { goTo } from '@/utils/navigation'; + +const onQuery = () => { + goTo('shop/search/index', { keywords: keywords.trim() }); +} +``` + +### 示例2:商品详情 + +```typescript +// 旧代码 +const viewProduct = (productId: number) => { + Taro.navigateTo({ + url: `/pages/product/detail?id=${productId}&from=list` + }); +} + +// 新代码 +import { goTo } from '@/utils/navigation'; + +const viewProduct = (productId: number) => { + goTo('product/detail', { id: productId, from: 'list' }); +} +``` + +### 示例3:TabBar切换 + +```typescript +// 旧代码 +const goHome = () => { + Taro.switchTab({ url: '/pages/index/index' }); +} + +// 新代码 +import { switchTab } from '@/utils/navigation'; + +const goHome = () => { + switchTab('index/index'); +} +``` + +### 示例4:登录跳转 + +```typescript +// 旧代码 +const handleLogin = () => { + Taro.redirectTo({ url: '/pages/login/index' }); +} + +// 新代码 +import { redirectTo } from '@/utils/navigation'; + +const handleLogin = () => { + redirectTo('login/index'); +} +``` + +## 🛠️ 批量替换脚本 + +可以使用以下正则表达式进行批量替换: + +### 1. 简单导航替换 + +```bash +# 查找 +Taro\.navigateTo\(\{\s*url:\s*['"`]([^'"`]+)['"`]\s*\}\) + +# 替换为 +goTo('$1') +``` + +### 2. switchTab替换 + +```bash +# 查找 +Taro\.switchTab\(\{\s*url:\s*['"`]([^'"`]+)['"`]\s*\}\) + +# 替换为 +switchTab('$1') +``` + +### 3. redirectTo替换 + +```bash +# 查找 +Taro\.redirectTo\(\{\s*url:\s*['"`]([^'"`]+)['"`]\s*\}\) + +# 替换为 +redirectTo('$1') +``` + +## 📦 导入语句 + +在每个需要使用导航的文件顶部添加: + +```typescript +import { goTo, switchTab, redirectTo, reLaunch, goBack } from '@/utils/navigation'; +``` + +## ⚠️ 注意事项 + +### 1. 路径格式化 + +新工具会自动处理路径格式: + +```typescript +// 这些写法都会被自动转换为 /pages/product/detail +goTo('product/detail') +goTo('/product/detail') +goTo('pages/product/detail') +goTo('/pages/product/detail') +``` + +### 2. 参数处理 + +```typescript +// 旧写法需要手动编码 +const url = `/pages/search?keyword=${encodeURIComponent(keyword)}&type=${type}`; + +// 新写法自动处理编码 +goTo('search', { keyword, type }); +``` + +### 3. 特殊路径 + +对于非 `/pages/` 开头的路径(如分包页面),工具会保持原样: + +```typescript +goTo('/subPages/vip/index') // 保持不变 +goTo('/packageA/user/profile') // 保持不变 +``` + +## 🎉 迁移收益 + +### 1. 代码更简洁 + +```typescript +// 旧:42个字符 +Taro.navigateTo({ url: '/pages/product/detail' }) + +// 新:22个字符 +goTo('product/detail') +``` + +### 2. 参数处理更方便 + +```typescript +// 旧:需要手动拼接和编码 +const url = `/pages/search?q=${encodeURIComponent(query)}&page=${page}`; +Taro.navigateTo({ url }); + +// 新:自动处理 +goTo('search', { q: query, page }); +``` + +### 3. 错误处理更统一 + +```typescript +// 新工具自动包含错误处理 +goTo('some/page').catch(error => { + // 自动显示错误提示 +}); +``` + +### 4. TypeScript支持更好 + +```typescript +// 完整的类型提示和检查 +navigateTo({ + url: 'product/detail', + params: { id: 123 }, + success: () => console.log('成功'), + fail: (error) => console.error(error) +}); +``` + +## 📋 迁移检查清单 + +- [ ] 替换所有 `Taro.navigateTo` 调用 +- [ ] 替换所有 `Taro.switchTab` 调用 +- [ ] 替换所有 `Taro.redirectTo` 调用 +- [ ] 替换所有 `Taro.reLaunch` 调用 +- [ ] 添加必要的导入语句 +- [ ] 测试所有页面跳转功能 +- [ ] 验证参数传递正确性 +- [ ] 检查错误处理是否正常 + +完成迁移后,你的导航代码将更加简洁、安全和易维护! diff --git a/docs/NAVIGATION_USAGE.md b/docs/NAVIGATION_USAGE.md new file mode 100644 index 0000000..069b3ea --- /dev/null +++ b/docs/NAVIGATION_USAGE.md @@ -0,0 +1,241 @@ +# 导航工具使用指南 + +## 📖 概述 + +封装了 Taro 的导航方法,提供更便捷和统一的页面跳转功能。 + +## 🚀 主要特性 + +- ✅ **自动路径格式化** - 自动添加 `/pages/` 前缀 +- ✅ **参数自动编码** - 自动处理 URL 参数编码 +- ✅ **多种导航方式** - 支持所有 Taro 导航方法 +- ✅ **错误处理** - 统一的错误处理和提示 +- ✅ **TypeScript 支持** - 完整的类型定义 + +## 📦 导入方式 + +```typescript +// 导入主要函数 +import { navigateTo, goTo, redirectTo, reLaunch, switchTab, goBack } from '@/utils/navigation' + +// 或者导入默认函数 +import navigateTo from '@/utils/navigation' +``` + +## 🎯 使用方法 + +### 1. 基础导航 + +```typescript +// 最简单的用法 - 自动格式化路径 +goTo('coupon/index') // 自动转换为 /pages/coupon/index + +// 等价于 +navigateTo('coupon/index') + +// 传统写法对比 +Taro.navigateTo({ url: '/pages/coupon/index' }) +``` + +### 2. 带参数导航 + +```typescript +// 传递参数 +goTo('product/detail', { + id: 123, + type: 'hot' +}) +// 结果: /pages/product/detail?id=123&type=hot + +// 复杂参数自动编码 +goTo('search/result', { + keyword: '优惠券', + category: '美食', + price: [10, 100] +}) +``` + +### 3. 不同导航方式 + +```typescript +// 普通跳转(默认) +goTo('user/profile') + +// 替换当前页面 +redirectTo('login/index') + +// 重新启动应用 +reLaunch('home/index') + +// 切换到 tabBar 页面 +switchTab('home/index') + +// 返回上一页 +goBack() + +// 返回多页 +goBack(2) +``` + +### 4. 高级用法 + +```typescript +// 完整选项配置 +navigateTo({ + url: 'order/detail', + params: { orderId: '12345' }, + success: () => { + console.log('跳转成功') + }, + fail: (error) => { + console.error('跳转失败:', error) + } +}) + +// 不同导航类型 +navigateTo({ + url: 'login/index', + replace: true, // 使用 redirectTo + params: { from: 'profile' } +}) + +navigateTo({ + url: 'home/index', + switchTab: true // 使用 switchTab +}) +``` + +## 🛠️ 路径格式化规则 + +### 自动添加前缀 + +```typescript +// 输入 -> 输出 +'coupon/index' -> '/pages/coupon/index' +'/coupon/index' -> '/pages/coupon/index' +'pages/coupon/index' -> '/pages/coupon/index' +'/pages/coupon/index' -> '/pages/coupon/index' +``` + +### 特殊路径处理 + +```typescript +// 如果已经是完整路径,不会重复添加 +'/pages/user/profile' -> '/pages/user/profile' +'/subPages/vip/index' -> '/subPages/vip/index' +``` + +## 🎨 实际使用示例 + +### 在组件中使用 + +```typescript +import React from 'react' +import { Button } from '@nutui/nutui-react-taro' +import { goTo, switchTab } from '@/utils/navigation' + +const ProductCard = ({ product }) => { + const handleViewDetail = () => { + goTo('product/detail', { + id: product.id, + from: 'list' + }) + } + + const handleGoHome = () => { + switchTab('home/index') + } + + return ( + + + + + ) +} +``` + +### 在页面中使用 + +```typescript +import { useEffect } from 'react' +import { redirectTo, getCurrentRoute } from '@/utils/navigation' + +const LoginPage = () => { + useEffect(() => { + // 检查登录状态 + const checkAuth = async () => { + const isLoggedIn = await checkLoginStatus() + if (isLoggedIn) { + // 已登录,跳转到首页 + redirectTo('home/index') + } + } + + checkAuth() + }, []) + + const handleLogin = async () => { + try { + await login() + // 登录成功,跳转 + redirectTo('home/index') + } catch (error) { + console.error('登录失败:', error) + } + } + + return ( + // 登录页面内容 + ) +} +``` + +## 🔧 工具函数 + +```typescript +// 获取当前页面路径 +const currentRoute = getCurrentRoute() +console.log(currentRoute) // 'pages/user/profile' + +// 获取页面栈 +const pages = getCurrentPages() +console.log(pages.length) // 当前页面栈深度 +``` + +## ⚠️ 注意事项 + +1. **tabBar 页面**:使用 `switchTab` 时不能传递参数 +2. **页面栈限制**:小程序页面栈最多 10 层 +3. **参数大小**:URL 参数有长度限制,大数据建议使用全局状态 +4. **特殊字符**:参数值会自动进行 URL 编码 + +## 🎉 迁移指南 + +### 替换现有代码 + +```typescript +// 旧写法 +Taro.navigateTo({ + url: '/pages/product/detail?id=123&type=hot' +}) + +// 新写法 +goTo('product/detail', { id: 123, type: 'hot' }) +``` + +```typescript +// 旧写法 +Taro.redirectTo({ + url: '/pages/login/index' +}) + +// 新写法 +redirectTo('login/index') +``` + +现在你可以在整个项目中使用这些便捷的导航函数了! diff --git a/src/api/cms/cmsNavigation/model/index.ts b/src/api/cms/cmsNavigation/model/index.ts index 421bbc7..cdd4f0c 100644 --- a/src/api/cms/cmsNavigation/model/index.ts +++ b/src/api/cms/cmsNavigation/model/index.ts @@ -55,8 +55,8 @@ export interface CmsNavigation { parentName?: string; // 模型名称 modelName?: string; - // 类型(已废弃) - type?: number; + // 类型(模型) + type?: string; // 绑定的页面(已废弃) pageId?: number; // 项目ID diff --git a/src/api/shop/shopCoupon/index.ts b/src/api/shop/shopCoupon/index.ts index eef0548..6d2c177 100644 --- a/src/api/shop/shopCoupon/index.ts +++ b/src/api/shop/shopCoupon/index.ts @@ -99,17 +99,3 @@ export async function getShopCoupon(id: number) { } return Promise.reject(new Error(res.message)); } - -/** - * 领取优惠券 - */ -export async function receiveCoupon(params: { couponId: number; userId: number }) { - const res = await request.post>( - '/shop/shop-coupon/receive', - params - ); - if (res.code === 0) { - return res.message; - } - return Promise.reject(new Error(res.message)); -} diff --git a/src/api/shop/shopUserCoupon/index.ts b/src/api/shop/shopUserCoupon/index.ts index 5be3383..e1b686f 100644 --- a/src/api/shop/shopUserCoupon/index.ts +++ b/src/api/shop/shopUserCoupon/index.ts @@ -138,3 +138,18 @@ export async function getMyExpiredCoupons() { } return Promise.reject(new Error(res.message)); } + + +/** + * 领取优惠券 + */ +export async function takeCoupon(params: { couponId: number; userId: number }) { + const res = await request.post>( + '/shop/shop-user-coupon/take', + params + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/app.config.ts b/src/app.config.ts index 53f45fd..af317d6 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -68,7 +68,8 @@ export default defineAppConfig({ }, { "root": "shop", - "pages": ['category/index', + "pages": [ + 'category/index', 'orderDetail/index', 'goodsDetail/index', 'orderConfirm/index', diff --git a/src/components/CartIcon.tsx b/src/components/CartIcon.tsx index f3bd7eb..7a71a5f 100644 --- a/src/components/CartIcon.tsx +++ b/src/components/CartIcon.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { Badge } from "@nutui/nutui-react-taro"; import { Cart } from "@nutui/icons-react-taro"; -import Taro from '@tarojs/taro'; import { useCart } from "@/hooks/useCart"; +import { switchTab } from '@/utils/navigation'; interface CartIconProps { style?: React.CSSProperties; @@ -26,13 +26,13 @@ const CartIcon: React.FC = ({ onClick(); } else { // 默认跳转到购物车页面 - Taro.switchTab({ url: '/pages/cart/cart' }); + switchTab('cart/cart'); } }; if (showBadge) { return ( -
= ({ } return ( -
= ({ // 垂直布局 if (layout === 'vertical') { return ( - + {title && ( - {title} + {title} )} {coupons.length === 0 ? ( showEmpty && ( - + {emptyText} ) ) : ( - coupons.map((coupon, index) => ( - handleCouponClick(coupon, index)} - > - - - )) + + {coupons.map((coupon, index) => ( + handleCouponClick(coupon, index)} + > + + + ))} + )} ) @@ -59,29 +63,29 @@ const CouponList: React.FC = ({ // 水平滚动布局 return ( - + {title && ( - + {title} )} {coupons.length === 0 ? ( showEmpty && ( - + {emptyText} ) ) : ( {coupons.map((coupon, index) => ( handleCouponClick(coupon, index)} > diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index e41c8cc..ba439bb 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -1,6 +1,6 @@ import { Tabbar } from '@nutui/nutui-react-taro' import { Home, User } from '@nutui/icons-react-taro' -import Taro from '@tarojs/taro' +import { switchTab } from '@/utils/navigation' function TabBar(){ return ( @@ -9,13 +9,13 @@ function TabBar(){ onSwitch={(index) => { console.log(index) if(index == 0){ - Taro.switchTab({ url: '/pages/index/index' }) + switchTab('index/index') } // if(index == 1){ - // Taro.navigateTo({ url: '/pages/detail/detail' }) + // goTo('detail/detail') // } if(index == 1){ - Taro.switchTab({ url: '/pages/user/user' }) + switchTab('user/user') } }} > diff --git a/src/coupon/index.config.ts b/src/coupon/index.config.ts new file mode 100644 index 0000000..fb1f60e --- /dev/null +++ b/src/coupon/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '领劵中心', + navigationBarTextStyle: 'black' +}) diff --git a/src/coupon/index.tsx b/src/coupon/index.tsx index f7f9077..c1edaf3 100644 --- a/src/coupon/index.tsx +++ b/src/coupon/index.tsx @@ -4,32 +4,28 @@ import { Button, Empty, ConfigProvider, - SearchBar, InfiniteLoading, Loading, PullToRefresh, Tabs, - TabPane, - Swiper, - SwiperItem + TabPane } from '@nutui/nutui-react-taro' -import {Filter, Board, Gift} from '@nutui/icons-react-taro' +import {Gift} from '@nutui/icons-react-taro' import {View} from '@tarojs/components' import {ShopCoupon} from "@/api/shop/shopCoupon/model"; -import {pageShopCoupon, receiveCoupon} from "@/api/shop/shopCoupon"; +import {pageShopCoupon} from "@/api/shop/shopCoupon"; import CouponList from "@/components/CouponList"; import CouponGuide from "@/components/CouponGuide"; import CouponFilter from "@/components/CouponFilter"; import {CouponCardProps} from "@/components/CouponCard"; +import {takeCoupon} from "@/api/shop/shopUserCoupon"; const CouponReceiveCenter = () => { const [list, setList] = useState([]) const [loading, setLoading] = useState(false) const [hasMore, setHasMore] = useState(true) - const [searchValue, setSearchValue] = useState('') const [page, setPage] = useState(1) const [activeTab, setActiveTab] = useState('0') // 0-全部 1-满减券 2-折扣券 3-免费券 - const [hotCoupons, setHotCoupons] = useState([]) // 热门优惠券 const [showGuide, setShowGuide] = useState(false) const [showFilter, setShowFilter] = useState(false) const [filters, setFilters] = useState({ @@ -80,7 +76,7 @@ const CouponReceiveCenter = () => { const res = await pageShopCoupon({ page: currentPage, limit: 10, - keywords: searchValue, + keywords: '', enabled: 1, // 启用状态 isExpire: 0, // 未过期 ...typeFilter @@ -123,7 +119,7 @@ const CouponReceiveCenter = () => { const res = await pageShopCoupon({ page: currentPage, limit: 10, - keywords: searchValue, + keywords: '', enabled: 1, // 启用状态 isExpire: 0, // 未过期 ...typeFilter, @@ -160,12 +156,6 @@ const CouponReceiveCenter = () => { } } - // 搜索功能 - const handleSearch = (value: string) => { - setSearchValue(value) - reload(true) - } - // 下拉刷新 const handleRefresh = async () => { await reload(true) @@ -187,26 +177,6 @@ const CouponReceiveCenter = () => { loadCouponsByType(typeFilter) } - // 加载热门优惠券 - const loadHotCoupons = async () => { - try { - const res = await pageShopCoupon({ - page: 1, - limit: 5, - enabled: 1, - isExpire: 0, - sortBy: 'createTime', - sortOrder: 'desc' - }) - - if (res && res.list) { - setHotCoupons(res.list) - } - } catch (error) { - console.error('获取热门优惠券失败:', error) - } - } - // 转换优惠券数据为CouponCard组件所需格式 const transformCouponData = (coupon: ShopCoupon): CouponCardProps => { let amount = 0 @@ -263,7 +233,7 @@ const CouponReceiveCenter = () => { } // 调用领取接口 - await receiveCoupon({ + await takeCoupon({ couponId: coupon.id!, userId: userId }) @@ -279,35 +249,11 @@ const CouponReceiveCenter = () => { console.error('领取优惠券失败:', error) Taro.showToast({ title: error.message || '领取失败', - icon: 'error' + icon: 'none' }) } } - // 优惠券点击事件 - const handleCouponClick = (_: CouponCardProps, index: number) => { - const originalCoupon = list[index] - if (originalCoupon) { - // 显示优惠券详情 - handleCouponDetail(originalCoupon) - } - } - - // 显示优惠券详情 - const handleCouponDetail = (coupon: ShopCoupon) => { - // 可以显示优惠券详情弹窗或跳转到详情页 - Taro.showModal({ - title: coupon.name || '优惠券详情', - content: `${coupon.description || ''} - -优惠类型:${coupon.type === 10 ? '满减券' : coupon.type === 20 ? '折扣券' : '免费券'} -${coupon.minPrice ? `最低消费:¥${coupon.minPrice}` : ''} -有效期:${coupon.startTime} 至 ${coupon.endTime}`, - showCancel: false, - confirmText: '知道了' - }) - } - // 筛选条件变更 const handleFiltersChange = (newFilters: any) => { setFilters(newFilters) @@ -329,14 +275,13 @@ ${coupon.minPrice ? `最低消费:¥${coupon.minPrice}` : ''} } useDidShow(() => { - reload(true) - loadHotCoupons() + reload(true).then() }); return ( - + {/* Tab切换 */} - + @@ -381,7 +326,6 @@ ${coupon.minPrice ? `最低消费:¥${coupon.minPrice}` : ''} > diff --git a/src/dealer/apply/add.tsx b/src/dealer/apply/add.tsx index b7ad784..08c2f15 100644 --- a/src/dealer/apply/add.tsx +++ b/src/dealer/apply/add.tsx @@ -64,33 +64,18 @@ const AddUserAddress = () => { // 提交表单 const submitSucceed = async (values: any) => { - if(!values.refereeId){ - return Taro.showToast({ - title: '请填写邀请人ID', - icon: 'error' - }); - } - // 验证邀请人ID是否存在 try { - await getShopDealerUser(values.refereeId); - } catch (error) { - console.error('验证邀请人失败:', error); - return Taro.showToast({ - title: '邀请人ID不存在', - icon: 'error' - }); - } - try { // 准备提交的数据 const submitData = { ...values, realName: values.realName || user?.nickname, mobile: user?.phone, - refereeId: values.refereeId, + refereeId: values.refereeId || FormData?.refereeId, applyStatus: 10, auditTime: undefined }; + await getShopDealerUser(submitData.refereeId); // 如果是编辑模式,添加现有申请的id if (isEditMode && existingApply?.applyId) { @@ -105,7 +90,7 @@ const AddUserAddress = () => { } Taro.showToast({ - title: `${isEditMode ? '更新' : '提交'}成功`, + title: `${isEditMode ? '提交' : '提交'}成功`, icon: 'success' }); @@ -114,9 +99,9 @@ const AddUserAddress = () => { }, 1000); } catch (error) { - console.error('提交失败:', error); - Taro.showToast({ - title: `${isEditMode ? '更新' : '提交'}失败`, + console.error('验证邀请人失败:', error); + return Taro.showToast({ + title: '邀请人ID不存在', icon: 'error' }); } diff --git a/src/pages/index/Menu.tsx b/src/pages/index/Menu.tsx index 5fd072d..c634965 100644 --- a/src/pages/index/Menu.tsx +++ b/src/pages/index/Menu.tsx @@ -1,46 +1,38 @@ -import Taro from '@tarojs/taro' -import {useEffect, useState} from 'react' import {Image} from '@nutui/nutui-react-taro' import {Loading} from '@nutui/nutui-react-taro' -import {listCmsNavigation} from "@/api/cms/cmsNavigation" -import {CmsNavigation} from "@/api/cms/cmsNavigation/model" +import {goTo} from "@/utils/navigation" +import {useShopInfo} from "@/hooks/useShopInfo" const Page = () => { + // 使用 useShopInfo hooks 获取导航数据 + const { + loading: shopLoading, + error, + getNavigation + } = useShopInfo() - const [loading, setLoading] = useState(true) - const [navItems, setNavItems] = useState([]) + // 获取顶部导航菜单 + const navigation = getNavigation() + const home = navigation.topNavs.find(item => item.model == 'index') + const navItems = navigation.topNavs.filter(item => item.parentId == home?.navigationId) || [] - const reload = async () => { - // 读取首页菜单 - const home = await listCmsNavigation({model: 'index'}); - if (home && home.length > 0) { - // 读取首页导航条 - const menus = await listCmsNavigation({parentId: home[0].navigationId, hide: 0}); - setNavItems(menus || []) + const onNav = (item: any) => { + if (item.path) { + return goTo(`${item.path}`) } - }; - - const onNav = (row: CmsNavigation) => { - console.log('nav = ', row) - console.log('path = ', `/${row.model}${row.path}`) - if (row.model == 'goods') { - return Taro.navigateTo({url: `/shop/category/index?id=${row.navigationId}`}) - } - if (row.model == 'article') { - return Taro.navigateTo({url: `/cms/category/index?id=${row.navigationId}`}) - } - return Taro.navigateTo({url: `${row.path}`}) } - - useEffect(() => { - reload().then(() => { - setLoading(false) - }); - }, []) + // 处理错误状态 + if (error) { + return ( +
+ 加载导航菜单失败 +
+ ) + } return ( - loading ? (加载中) : + shopLoading ? (加载中) :
{ diff --git a/src/pages/index/MySearch.tsx b/src/pages/index/MySearch.tsx index be1f641..b0d43fb 100644 --- a/src/pages/index/MySearch.tsx +++ b/src/pages/index/MySearch.tsx @@ -2,6 +2,7 @@ import {Search} from '@nutui/icons-react-taro' import {Button, Input} from '@nutui/nutui-react-taro' import {useState} from "react"; import Taro from '@tarojs/taro'; +import { goTo } from '@/utils/navigation'; function MySearch() { const [keywords, setKeywords] = useState('') @@ -18,17 +19,13 @@ function MySearch() { }); return false; } - // 跳转到搜索页面 - Taro.navigateTo({ - url: `/shop/search/index?keywords=${encodeURIComponent(keywords.trim())}` - }); + // 跳转到搜索页面 - 使用新的导航工具,自动处理路径和参数 + goTo('shop/search/index', { keywords: keywords.trim() }); } // 点击搜索框跳转到搜索页面 const onInputFocus = () => { - Taro.navigateTo({ - url: '/shop/search/index' - }); + goTo('shop/search/index'); } diff --git a/src/utils/common.ts b/src/utils/common.ts index 16c379d..1137728 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -1,4 +1,5 @@ import Taro from '@tarojs/taro' +import { goTo } from './navigation' export default function navTo(url: string, isLogin = false) { if (isLogin) { @@ -11,14 +12,13 @@ export default function navTo(url: string, isLogin = false) { return false; } } - Taro.navigateTo({ - url: url - }) + // 使用新的导航工具,自动处理路径格式化 + goTo(url) } // 转base64 -export function fileToBase64(filePath) { +export function fileToBase64(filePath:string) { return new Promise((resolve) => { let fileManager = Taro.getFileSystemManager(); fileManager.readFile({ @@ -35,7 +35,7 @@ export function fileToBase64(filePath) { * 转义微信富文本图片样式 * @param htmlText */ -export function wxParse(htmlText) { +export function wxParse(htmlText:string) { // Replace tags with max-width, height and margin styles to remove spacing htmlText = htmlText.replace(/\ + /** 是否替换当前页面(使用redirectTo) */ + replace?: boolean + /** 是否重新启动应用(使用reLaunch) */ + relaunch?: boolean + /** 是否切换到tabBar页面(使用switchTab) */ + switchTab?: boolean + /** 成功回调 */ + success?: (res: any) => void + /** 失败回调 */ + fail?: (res: any) => void + /** 完成回调 */ + complete?: (res: any) => void +} + +/** + * 格式化页面路径 + * @param url 原始路径 + * @returns 格式化后的路径 + */ +function formatUrl(url: string): string { + // 如果不是以"/"开头,自动添加 + if (!url.startsWith('/')) { + url = '/' + url + } + + // 如果不是以"/pages/"开头,自动添加 + // if (!url.startsWith('/pages/')) { + // // 移除开头的"/",然后添加"/pages/" + // url = '/pages/' + url.replace(/^\/+/, '') + // } + + return url +} + +/** + * 构建带参数的URL + * @param url 基础URL + * @param params 参数对象 + * @returns 完整的URL + */ +function buildUrlWithParams(url: string, params?: Record): string { + if (!params || Object.keys(params).length === 0) { + return url + } + + const queryString = Object.entries(params) + .map(([key, value]) => { + if (value === null || value === undefined) { + return '' + } + return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}` + }) + .filter(Boolean) + .join('&') + + return queryString ? `${url}?${queryString}` : url +} + +/** + * 统一的页面导航函数 + * @param options 导航选项 + */ +export function navigateTo(options: NavigationOptions | string): void { + console.log(options,'options') + // 如果传入的是字符串,转换为选项对象 + const opts: NavigationOptions = typeof options === 'string' + ? { url: options } + : options + // 格式化URL + const formattedUrl = formatUrl(opts.url) + + // 构建完整URL(包含参数) + const fullUrl = buildUrlWithParams(formattedUrl, opts.params) + + // 默认错误处理函数 + const defaultFail = (res?: any) => { + console.error('页面导航失败:', res) + if (opts.fail) { + opts.fail(res) + } else { + Taro.showToast({ + title: '页面跳转失败', + icon: 'error' + }) + } + } + + // 根据不同的导航类型选择对应的Taro方法 + if (opts.switchTab) { + Taro.switchTab({ + url: fullUrl, + success: opts.success, + fail: defaultFail, + complete: opts.complete + }) + } else if (opts.relaunch) { + Taro.reLaunch({ + url: fullUrl, + success: opts.success, + fail: defaultFail, + complete: opts.complete + }) + } else if (opts.replace) { + Taro.redirectTo({ + url: fullUrl, + success: opts.success, + fail: defaultFail, + complete: opts.complete + }) + } else { + console.log('这里🌶。 ', fullUrl) + + Taro.navigateTo({ + url: fullUrl, + success: opts.success, + fail: defaultFail, + complete: opts.complete + }) + } +} + +/** + * 导航到指定页面(默认方式) + * @param url 页面路径 + * @param params 页面参数 + */ +export function goTo(url: string, params?: Record): void { + navigateTo({ url, params }) +} + +/** + * 替换当前页面 + * @param url 页面路径 + * @param params 页面参数 + */ +export function redirectTo(url: string, params?: Record): void { + navigateTo({ url, params, replace: true }) +} + +/** + * 重新启动应用 + * @param url 页面路径 + * @param params 页面参数 + */ +export function reLaunch(url: string, params?: Record): void { + navigateTo({ url, params, relaunch: true }) +} + +/** + * 切换到tabBar页面 + * @param url 页面路径 + */ +export function switchTab(url: string): void { + navigateTo({ url, switchTab: true }) +} + +/** + * 返回上一页 + * @param delta 返回的页面数,默认为1 + */ +export function goBack(delta: number = 1): void { + Taro.navigateBack({ delta }) +} + +/** + * 获取当前页面栈 + */ +export function getCurrentPages() { + return Taro.getCurrentPages() +} + +/** + * 获取当前页面路径 + */ +export function getCurrentRoute(): string { + const pages = getCurrentPages() + const currentPage = pages[pages.length - 1] + return currentPage ? currentPage.route || '' : '' +} + +// 导出默认的导航函数 +export default navigateTo