From c3b29d4d76cde92d8f742d9b78781bb650888ed9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com>
Date: Wed, 18 Mar 2026 00:15:13 +0800
Subject: [PATCH] =?UTF-8?q?remove(feature):=20=E5=88=A0=E9=99=A4=E7=BB=8F?=
=?UTF-8?q?=E9=94=80=E5=95=86=E7=94=B3=E8=AF=B7=E3=80=81=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E5=9C=B0=E5=9D=80=E7=AE=A1=E7=90=86=E5=92=8C=E8=81=8A=E5=A4=A9?=
=?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除经销商申请相关页面配置和业务逻辑代码
- 删除用户地址管理功能的所有配置文件和实现组件
- 清理聊天消息发送功能的相关页面配置和业务代码
- 移除相关的API调用和数据模型引用
- 删除页面导航配置和相关的工具函数引用
---
docs/ADMIN_MODE_SOLUTION.md | 226 ---
docs/APPINFO_FIELD_MIGRATION.md | 298 ----
docs/ARGUMENTS_KEYWORD_FIX.md | 197 ---
docs/COUPON_API_INTEGRATION.md | 246 ----
docs/COUPON_CARD_ALIGNMENT_FIX.md | 178 ---
docs/COUPON_DISPLAY_DEBUG.md | 168 ---
docs/COUPON_PAYMENT_ISSUE_ANALYSIS.md | 340 -----
docs/COUPON_STATUS_DEBUG.md | 224 ---
docs/COUPON_WARNINGS_FIXED.md | 153 --
docs/DEALER_OPTIMIZATION.md | 137 --
docs/ERROR_UNKNOWN_TYPE_FIX.md | 258 ----
docs/FINAL_TYPE_ERROR_FIX.md | 166 ---
docs/GRADIENT_DESIGN_GUIDE.md | 206 ---
docs/HEADER_MIGRATION_COMPLETE.md | 224 ---
docs/HEADER_UNUSED_VARIABLES_FIX.md | 256 ----
docs/IMPLICIT_ANY_TYPE_FIX.md | 215 ---
docs/INFINITE_LOOP_FIX.md | 254 ----
docs/LOADING_ISSUE_SOLUTION.md | 204 ---
docs/MENU_MIGRATION_TO_HOOK.md | 223 ---
docs/MODULE_NOT_FOUND_FIX.md | 191 ---
docs/NAVIGATION_MIGRATION_GUIDE.md | 261 ----
docs/NAVIGATION_USAGE.md | 241 ---
docs/ORDER_CONFIRM_COUPON_INTEGRATION.md | 281 ----
docs/ORDER_FRONTEND_IMPLEMENTATION.md | 245 ----
docs/ORDER_IMPROVEMENTS.md | 103 --
docs/PAYMENT_ISSUE_FIXED.md | 275 ----
docs/PAYMENT_REFACTOR_GUIDE.md | 264 ----
docs/PHASE_ONE_IMPROVEMENTS.md | 229 ---
docs/QR_LOGIN_INTEGRATION.md | 214 ---
docs/QR_LOGIN_USAGE.md | 284 ----
docs/REQUEST_CLEANUP_SUMMARY.md | 76 -
docs/REQUEST_USAGE.md | 83 --
docs/SHOP_INFO_HOOK_SUMMARY.md | 287 ----
docs/SPEC_SELECTOR_ANALYSIS.md | 319 ----
docs/TABS_ONCHANGE_TYPE_FIX.md | 236 ---
docs/TABS_TYPE_ERROR_FIX.md | 163 ---
docs/TAB_SWITCH_DATA_FIX.md | 196 ---
docs/THEME_SYSTEM_GUIDE.md | 191 ---
docs/TYPESCRIPT_TYPE_FIX.md | 111 --
docs/TYPESCRIPT_TYPE_FIXES_COMPLETE.md | 181 ---
docs/TYPESCRIPT_WARNING_FIX.md | 157 --
docs/TYPE_WARNING_FIX.md | 171 ---
docs/UNIFIED_QR_SCAN_GUIDE.md | 212 ---
docs/USE_SHOP_INFO_HOOK.md | 357 -----
docs/backend-multi-spec-integration.md | 181 ---
docs/backend-order-service-example.java | 248 ----
docs/frontend-multi-spec-test.md | 154 --
docs/frontend-order-example.tsx | 317 ----
docs/multi-spec-integration-summary.md | 189 ---
docs/order-status-fix-summary.md | 190 ---
docs/useUser-hook-guide.md | 277 ----
docs/水票配送订单-后端提示词.md | 41 -
src/admin/article/index.config.ts | 4 -
src/admin/article/index.tsx | 271 ----
src/admin/components/UserCard.tsx | 244 ----
src/admin/components/UserCell.tsx | 186 ---
src/admin/components/UserFooter.tsx | 102 --
src/admin/components/UserOrder.tsx | 69 -
src/admin/index.config.ts | 3 -
src/admin/index.tsx | 35 -
src/app.config.ts | 92 +-
src/cms/category/index.tsx | 2 +-
src/coupon/index.config.ts | 4 -
src/coupon/index.tsx | 368 -----
src/dealer/apply/add.config.ts | 4 -
src/dealer/apply/add.tsx | 489 -------
src/dealer/capital/index.config.ts | 4 -
src/dealer/capital/index.scss | 2 -
src/dealer/capital/index.tsx | 199 ---
src/dealer/index.config.ts | 3 -
src/dealer/index.scss | 0
src/dealer/index.tsx | 295 ----
src/dealer/info.tsx | 157 --
src/dealer/invite-stats/index.config.ts | 7 -
src/dealer/invite-stats/index.tsx | 336 -----
src/dealer/orders/index.config.ts | 3 -
src/dealer/orders/index.tsx | 205 ---
src/dealer/qrcode/index.config.ts | 6 -
src/dealer/qrcode/index.tsx | 522 -------
src/dealer/team/index.config.ts | 3 -
src/dealer/team/index.tsx | 439 ------
src/dealer/withdraw/debug.tsx | 80 -
src/dealer/withdraw/index.config.ts | 3 -
src/dealer/withdraw/index.tsx | 642 --------
src/pages/cart/cart.config.ts | 4 -
src/pages/cart/cart.scss | 31 -
src/pages/cart/cart.tsx | 352 -----
src/pages/find/find.config.ts | 5 -
src/pages/find/find.scss | 144 --
src/pages/find/find.tsx | 258 ----
src/pages/index/BestSellers.scss | 0
src/pages/index/BestSellers.tsx | 141 --
src/pages/index/Chart.tsx | 69 -
src/pages/index/ExpirationTime.tsx | 83 --
src/pages/index/GoodsList.scss | 0
src/pages/index/GoodsList.tsx | 67 -
src/pages/index/Grid.tsx | 58 -
src/pages/index/Header.scss | 16 -
src/pages/index/Header.tsx | 275 ----
src/pages/index/HeaderWithHook.tsx | 221 ---
src/pages/index/Help.tsx | 68 -
src/pages/index/Login.tsx | 152 --
src/pages/index/Menu.tsx | 73 -
src/pages/index/MySearch.tsx | 70 -
src/pages/index/PopUpAd.tsx | 47 -
src/pages/index/SiteUrl.tsx | 29 -
src/pages/index/chart/DemoLine.tsx | 38 -
src/pages/index/chart/index.scss | 7 -
src/pages/index/index.scss | 12 +
src/pages/index/index.tsx | 21 +-
src/pages/index/login.scss | 10 -
src/pages/user/components/IsDealer.tsx | 96 --
src/pages/user/components/UserCard.tsx | 29 +-
src/pages/user/components/UserCell.tsx | 144 --
src/pages/user/components/UserGrid.tsx | 24 +-
src/pages/user/components/UserOrder.tsx | 122 --
src/pages/user/user.tsx | 7 +-
src/passport/register.tsx | 1 -
src/passport/sms-login.tsx | 1 -
src/rider/index.config.ts | 3 -
src/rider/index.scss | 0
src/rider/index.tsx | 304 ----
src/rider/orders/index.config.ts | 4 -
src/rider/orders/index.tsx | 610 --------
src/rider/ticket/verification/index.config.ts | 4 -
src/rider/ticket/verification/index.tsx | 280 ----
src/shop/category/components/Banner.tsx | 31 -
src/shop/category/components/GoodsList.scss | 0
src/shop/category/components/GoodsList.tsx | 54 -
src/shop/category/index.config.ts | 4 -
src/shop/category/index.scss | 0
src/shop/category/index.tsx | 71 -
src/shop/comments/index.config.ts | 4 -
src/shop/comments/index.tsx | 0
src/shop/goodsDetail/index.config.ts | 5 -
src/shop/goodsDetail/index.scss | 25 -
src/shop/goodsDetail/index.tsx | 486 -------
src/shop/orderConfirm/index.config.ts | 4 -
src/shop/orderConfirm/index.scss | 116 --
src/shop/orderConfirm/index.tsx | 1162 ---------------
src/shop/orderConfirmCart/index.config.ts | 4 -
src/shop/orderConfirmCart/index.scss | 44 -
src/shop/orderConfirmCart/index.tsx | 209 ---
src/shop/orderDetail/index.config.ts | 4 -
src/shop/orderDetail/index.scss | 26 -
src/shop/orderDetail/index.tsx | 282 ----
src/shop/search/components/GoodsItem.scss | 33 -
src/shop/search/components/GoodsItem.tsx | 58 -
src/shop/search/index.config.ts | 3 -
src/shop/search/index.scss | 103 --
src/shop/search/index.tsx | 237 ---
src/store/index.config.ts | 3 -
src/store/index.scss | 0
src/store/index.tsx | 282 ----
src/store/orders/index.config.ts | 4 -
src/store/orders/index.tsx | 83 --
src/user/address/add.config.ts | 4 -
src/user/address/add.scss | 0
src/user/address/add.tsx | 646 --------
src/user/address/index.config.ts | 4 -
src/user/address/index.scss | 3 -
src/user/address/index.tsx | 219 ---
src/user/address/wxAddress.config.ts | 4 -
src/user/address/wxAddress.tsx | 61 -
src/user/chat/conversation/index.config.ts | 3 -
src/user/chat/conversation/index.tsx | 167 ---
src/user/chat/message/add.config.ts | 4 -
src/user/chat/message/add.tsx | 135 --
src/user/chat/message/detail.config.ts | 4 -
src/user/chat/message/detail.tsx | 77 -
src/user/chat/message/index.config.ts | 3 -
src/user/chat/message/index.tsx | 179 ---
src/user/company/company.config.ts | 3 -
src/user/company/company.tsx | 54 -
src/user/coupon/coupon.ts | 4 -
src/user/coupon/coupon.tsx | 237 ---
src/user/coupon/detail.config.ts | 6 -
src/user/coupon/detail.tsx | 259 ----
src/user/coupon/index.config.ts | 5 -
src/user/coupon/index.tsx | 466 ------
src/user/coupon/receive.config.ts | 5 -
src/user/coupon/receive.tsx | 247 ----
src/user/order/components/OrderList.tsx | 903 ------------
src/user/order/evaluate/index.config.ts | 3 -
src/user/order/evaluate/index.scss | 191 ---
src/user/order/evaluate/index.tsx | 304 ----
src/user/order/logistics/index.config.ts | 3 -
src/user/order/logistics/index.scss | 186 ---
src/user/order/logistics/index.tsx | 229 ---
src/user/order/order.config.ts | 4 -
src/user/order/order.scss | 72 -
src/user/order/order.tsx | 176 ---
src/user/order/progress/index.config.ts | 3 -
src/user/order/progress/index.scss | 292 ----
src/user/order/progress/index.tsx | 388 -----
src/user/order/refund/index.config.ts | 3 -
src/user/order/refund/index.scss | 244 ----
src/user/order/refund/index.tsx | 472 ------
src/user/points/points.config.ts | 4 -
src/user/points/points.tsx | 213 ---
src/user/poster/poster.config.ts | 4 -
src/user/poster/poster.tsx | 115 --
src/user/store/orders/index.config.ts | 4 -
src/user/store/orders/index.tsx | 73 -
src/user/store/verification.config.ts | 4 -
src/user/store/verification.tsx | 376 -----
src/user/theme/index.config.ts | 4 -
src/user/theme/index.tsx | 179 ---
src/user/ticket/index.config.ts | 5 -
src/user/ticket/index.tsx | 716 ---------
src/user/ticket/orders/index.config.ts | 5 -
src/user/ticket/orders/index.tsx | 636 --------
src/user/ticket/use.config.ts | 5 -
src/user/ticket/use.scss | 116 --
src/user/ticket/use.tsx | 1296 -----------------
src/user/wallet/wallet.config.ts | 4 -
src/user/wallet/wallet.scss | 0
src/user/wallet/wallet.tsx | 105 --
218 files changed, 33 insertions(+), 33197 deletions(-)
delete mode 100644 docs/ADMIN_MODE_SOLUTION.md
delete mode 100644 docs/APPINFO_FIELD_MIGRATION.md
delete mode 100644 docs/ARGUMENTS_KEYWORD_FIX.md
delete mode 100644 docs/COUPON_API_INTEGRATION.md
delete mode 100644 docs/COUPON_CARD_ALIGNMENT_FIX.md
delete mode 100644 docs/COUPON_DISPLAY_DEBUG.md
delete mode 100644 docs/COUPON_PAYMENT_ISSUE_ANALYSIS.md
delete mode 100644 docs/COUPON_STATUS_DEBUG.md
delete mode 100644 docs/COUPON_WARNINGS_FIXED.md
delete mode 100644 docs/DEALER_OPTIMIZATION.md
delete mode 100644 docs/ERROR_UNKNOWN_TYPE_FIX.md
delete mode 100644 docs/FINAL_TYPE_ERROR_FIX.md
delete mode 100644 docs/GRADIENT_DESIGN_GUIDE.md
delete mode 100644 docs/HEADER_MIGRATION_COMPLETE.md
delete mode 100644 docs/HEADER_UNUSED_VARIABLES_FIX.md
delete mode 100644 docs/IMPLICIT_ANY_TYPE_FIX.md
delete mode 100644 docs/INFINITE_LOOP_FIX.md
delete mode 100644 docs/LOADING_ISSUE_SOLUTION.md
delete mode 100644 docs/MENU_MIGRATION_TO_HOOK.md
delete mode 100644 docs/MODULE_NOT_FOUND_FIX.md
delete mode 100644 docs/NAVIGATION_MIGRATION_GUIDE.md
delete mode 100644 docs/NAVIGATION_USAGE.md
delete mode 100644 docs/ORDER_CONFIRM_COUPON_INTEGRATION.md
delete mode 100644 docs/ORDER_FRONTEND_IMPLEMENTATION.md
delete mode 100644 docs/ORDER_IMPROVEMENTS.md
delete mode 100644 docs/PAYMENT_ISSUE_FIXED.md
delete mode 100644 docs/PAYMENT_REFACTOR_GUIDE.md
delete mode 100644 docs/PHASE_ONE_IMPROVEMENTS.md
delete mode 100644 docs/QR_LOGIN_INTEGRATION.md
delete mode 100644 docs/QR_LOGIN_USAGE.md
delete mode 100644 docs/REQUEST_CLEANUP_SUMMARY.md
delete mode 100644 docs/REQUEST_USAGE.md
delete mode 100644 docs/SHOP_INFO_HOOK_SUMMARY.md
delete mode 100644 docs/SPEC_SELECTOR_ANALYSIS.md
delete mode 100644 docs/TABS_ONCHANGE_TYPE_FIX.md
delete mode 100644 docs/TABS_TYPE_ERROR_FIX.md
delete mode 100644 docs/TAB_SWITCH_DATA_FIX.md
delete mode 100644 docs/THEME_SYSTEM_GUIDE.md
delete mode 100644 docs/TYPESCRIPT_TYPE_FIX.md
delete mode 100644 docs/TYPESCRIPT_TYPE_FIXES_COMPLETE.md
delete mode 100644 docs/TYPESCRIPT_WARNING_FIX.md
delete mode 100644 docs/TYPE_WARNING_FIX.md
delete mode 100644 docs/UNIFIED_QR_SCAN_GUIDE.md
delete mode 100644 docs/USE_SHOP_INFO_HOOK.md
delete mode 100644 docs/backend-multi-spec-integration.md
delete mode 100644 docs/backend-order-service-example.java
delete mode 100644 docs/frontend-multi-spec-test.md
delete mode 100644 docs/frontend-order-example.tsx
delete mode 100644 docs/multi-spec-integration-summary.md
delete mode 100644 docs/order-status-fix-summary.md
delete mode 100644 docs/useUser-hook-guide.md
delete mode 100644 docs/水票配送订单-后端提示词.md
delete mode 100644 src/admin/article/index.config.ts
delete mode 100644 src/admin/article/index.tsx
delete mode 100644 src/admin/components/UserCard.tsx
delete mode 100644 src/admin/components/UserCell.tsx
delete mode 100644 src/admin/components/UserFooter.tsx
delete mode 100644 src/admin/components/UserOrder.tsx
delete mode 100644 src/admin/index.config.ts
delete mode 100644 src/admin/index.tsx
delete mode 100644 src/coupon/index.config.ts
delete mode 100644 src/coupon/index.tsx
delete mode 100644 src/dealer/apply/add.config.ts
delete mode 100644 src/dealer/apply/add.tsx
delete mode 100644 src/dealer/capital/index.config.ts
delete mode 100644 src/dealer/capital/index.scss
delete mode 100644 src/dealer/capital/index.tsx
delete mode 100644 src/dealer/index.config.ts
delete mode 100644 src/dealer/index.scss
delete mode 100644 src/dealer/index.tsx
delete mode 100644 src/dealer/info.tsx
delete mode 100644 src/dealer/invite-stats/index.config.ts
delete mode 100644 src/dealer/invite-stats/index.tsx
delete mode 100644 src/dealer/orders/index.config.ts
delete mode 100644 src/dealer/orders/index.tsx
delete mode 100644 src/dealer/qrcode/index.config.ts
delete mode 100644 src/dealer/qrcode/index.tsx
delete mode 100644 src/dealer/team/index.config.ts
delete mode 100644 src/dealer/team/index.tsx
delete mode 100644 src/dealer/withdraw/debug.tsx
delete mode 100644 src/dealer/withdraw/index.config.ts
delete mode 100644 src/dealer/withdraw/index.tsx
delete mode 100644 src/pages/cart/cart.config.ts
delete mode 100644 src/pages/cart/cart.scss
delete mode 100644 src/pages/cart/cart.tsx
delete mode 100644 src/pages/find/find.config.ts
delete mode 100644 src/pages/find/find.scss
delete mode 100644 src/pages/find/find.tsx
delete mode 100644 src/pages/index/BestSellers.scss
delete mode 100644 src/pages/index/BestSellers.tsx
delete mode 100644 src/pages/index/Chart.tsx
delete mode 100644 src/pages/index/ExpirationTime.tsx
delete mode 100644 src/pages/index/GoodsList.scss
delete mode 100644 src/pages/index/GoodsList.tsx
delete mode 100644 src/pages/index/Grid.tsx
delete mode 100644 src/pages/index/Header.scss
delete mode 100644 src/pages/index/Header.tsx
delete mode 100644 src/pages/index/HeaderWithHook.tsx
delete mode 100644 src/pages/index/Help.tsx
delete mode 100644 src/pages/index/Login.tsx
delete mode 100644 src/pages/index/Menu.tsx
delete mode 100644 src/pages/index/MySearch.tsx
delete mode 100644 src/pages/index/PopUpAd.tsx
delete mode 100644 src/pages/index/SiteUrl.tsx
delete mode 100644 src/pages/index/chart/DemoLine.tsx
delete mode 100644 src/pages/index/chart/index.scss
delete mode 100644 src/pages/index/login.scss
delete mode 100644 src/pages/user/components/IsDealer.tsx
delete mode 100644 src/pages/user/components/UserCell.tsx
delete mode 100644 src/pages/user/components/UserOrder.tsx
delete mode 100644 src/rider/index.config.ts
delete mode 100644 src/rider/index.scss
delete mode 100644 src/rider/index.tsx
delete mode 100644 src/rider/orders/index.config.ts
delete mode 100644 src/rider/orders/index.tsx
delete mode 100644 src/rider/ticket/verification/index.config.ts
delete mode 100644 src/rider/ticket/verification/index.tsx
delete mode 100644 src/shop/category/components/Banner.tsx
delete mode 100644 src/shop/category/components/GoodsList.scss
delete mode 100644 src/shop/category/components/GoodsList.tsx
delete mode 100644 src/shop/category/index.config.ts
delete mode 100644 src/shop/category/index.scss
delete mode 100644 src/shop/category/index.tsx
delete mode 100644 src/shop/comments/index.config.ts
delete mode 100644 src/shop/comments/index.tsx
delete mode 100644 src/shop/goodsDetail/index.config.ts
delete mode 100644 src/shop/goodsDetail/index.scss
delete mode 100644 src/shop/goodsDetail/index.tsx
delete mode 100644 src/shop/orderConfirm/index.config.ts
delete mode 100644 src/shop/orderConfirm/index.scss
delete mode 100644 src/shop/orderConfirm/index.tsx
delete mode 100644 src/shop/orderConfirmCart/index.config.ts
delete mode 100644 src/shop/orderConfirmCart/index.scss
delete mode 100644 src/shop/orderConfirmCart/index.tsx
delete mode 100644 src/shop/orderDetail/index.config.ts
delete mode 100644 src/shop/orderDetail/index.scss
delete mode 100644 src/shop/orderDetail/index.tsx
delete mode 100644 src/shop/search/components/GoodsItem.scss
delete mode 100644 src/shop/search/components/GoodsItem.tsx
delete mode 100644 src/shop/search/index.config.ts
delete mode 100644 src/shop/search/index.scss
delete mode 100644 src/shop/search/index.tsx
delete mode 100644 src/store/index.config.ts
delete mode 100644 src/store/index.scss
delete mode 100644 src/store/index.tsx
delete mode 100644 src/store/orders/index.config.ts
delete mode 100644 src/store/orders/index.tsx
delete mode 100644 src/user/address/add.config.ts
delete mode 100644 src/user/address/add.scss
delete mode 100644 src/user/address/add.tsx
delete mode 100644 src/user/address/index.config.ts
delete mode 100644 src/user/address/index.scss
delete mode 100644 src/user/address/index.tsx
delete mode 100644 src/user/address/wxAddress.config.ts
delete mode 100644 src/user/address/wxAddress.tsx
delete mode 100644 src/user/chat/conversation/index.config.ts
delete mode 100644 src/user/chat/conversation/index.tsx
delete mode 100644 src/user/chat/message/add.config.ts
delete mode 100644 src/user/chat/message/add.tsx
delete mode 100644 src/user/chat/message/detail.config.ts
delete mode 100644 src/user/chat/message/detail.tsx
delete mode 100644 src/user/chat/message/index.config.ts
delete mode 100644 src/user/chat/message/index.tsx
delete mode 100644 src/user/company/company.config.ts
delete mode 100644 src/user/company/company.tsx
delete mode 100644 src/user/coupon/coupon.ts
delete mode 100644 src/user/coupon/coupon.tsx
delete mode 100644 src/user/coupon/detail.config.ts
delete mode 100644 src/user/coupon/detail.tsx
delete mode 100644 src/user/coupon/index.config.ts
delete mode 100644 src/user/coupon/index.tsx
delete mode 100644 src/user/coupon/receive.config.ts
delete mode 100644 src/user/coupon/receive.tsx
delete mode 100644 src/user/order/components/OrderList.tsx
delete mode 100644 src/user/order/evaluate/index.config.ts
delete mode 100644 src/user/order/evaluate/index.scss
delete mode 100644 src/user/order/evaluate/index.tsx
delete mode 100644 src/user/order/logistics/index.config.ts
delete mode 100644 src/user/order/logistics/index.scss
delete mode 100644 src/user/order/logistics/index.tsx
delete mode 100644 src/user/order/order.config.ts
delete mode 100644 src/user/order/order.scss
delete mode 100644 src/user/order/order.tsx
delete mode 100644 src/user/order/progress/index.config.ts
delete mode 100644 src/user/order/progress/index.scss
delete mode 100644 src/user/order/progress/index.tsx
delete mode 100644 src/user/order/refund/index.config.ts
delete mode 100644 src/user/order/refund/index.scss
delete mode 100644 src/user/order/refund/index.tsx
delete mode 100644 src/user/points/points.config.ts
delete mode 100644 src/user/points/points.tsx
delete mode 100644 src/user/poster/poster.config.ts
delete mode 100644 src/user/poster/poster.tsx
delete mode 100644 src/user/store/orders/index.config.ts
delete mode 100644 src/user/store/orders/index.tsx
delete mode 100644 src/user/store/verification.config.ts
delete mode 100644 src/user/store/verification.tsx
delete mode 100644 src/user/theme/index.config.ts
delete mode 100644 src/user/theme/index.tsx
delete mode 100644 src/user/ticket/index.config.ts
delete mode 100644 src/user/ticket/index.tsx
delete mode 100644 src/user/ticket/orders/index.config.ts
delete mode 100644 src/user/ticket/orders/index.tsx
delete mode 100644 src/user/ticket/use.config.ts
delete mode 100644 src/user/ticket/use.scss
delete mode 100644 src/user/ticket/use.tsx
delete mode 100644 src/user/wallet/wallet.config.ts
delete mode 100644 src/user/wallet/wallet.scss
delete mode 100644 src/user/wallet/wallet.tsx
diff --git a/docs/ADMIN_MODE_SOLUTION.md b/docs/ADMIN_MODE_SOLUTION.md
deleted file mode 100644
index ee654bb..0000000
--- a/docs/ADMIN_MODE_SOLUTION.md
+++ /dev/null
@@ -1,226 +0,0 @@
-# 🎯 管理员模式切换方案
-
-## 📋 **问题分析**
-
-### 原始问题
-- 用户卡片中有两个扫码入口(门店核销 + 扫码登录)
-- 用户体验不友好,容易混淆
-- 管理员功能分散,缺乏统一入口
-
-### 解决思路
-设计一个管理员模式切换系统,通过模式切换来统一管理所有管理员功能。
-
-## 🚀 **解决方案**
-
-### 方案概述
-创建一个**管理员模式切换**系统,包含:
-1. **模式切换按钮** - 在普通用户模式和管理员模式之间切换
-2. **统一管理面板** - 集中展示所有管理员功能
-3. **状态持久化** - 记住用户的模式选择
-
-### 核心组件
-
-#### 1. **useAdminMode Hook**
-```typescript
-// src/hooks/useAdminMode.ts
-const { isAdminMode, toggleAdminMode, setAdminMode } = useAdminMode();
-```
-
-**功能特性:**
-- ✅ 模式状态管理
-- ✅ 本地存储持久化
-- ✅ 切换提示反馈
-
-#### 2. **AdminPanel 组件**
-```typescript
-// src/components/AdminPanel.tsx
-
-```
-
-**功能特性:**
-- ✅ 底部弹出面板设计
-- ✅ 网格布局展示功能
-- ✅ 图标 + 描述的直观界面
-- ✅ 点击遮罩关闭
-
-#### 3. **UserCard 集成**
-更新用户卡片,集成模式切换功能:
-- 模式切换按钮
-- 管理面板入口
-- 状态指示器
-
-## 🎨 **用户界面设计**
-
-### 模式切换按钮
-```
-[普通用户] ←→ [管理员] [管理面板]
-```
-
-- **普通用户模式**:灰色按钮,只显示基础功能
-- **管理员模式**:蓝色按钮,显示管理面板入口
-
-### 管理员面板
-```
-┌─────────────────────────────────┐
-│ 🔧 管理员面板 [关闭] │
-├─────────────────────────────────┤
-│ [🔍 门店核销] [🔍 扫码登录] │
-│ [👤 用户管理] [🏪 门店管理] │
-│ [⚙️ 系统设置] │
-├─────────────────────────────────┤
-│ 💡 管理员功能仅对具有管理权限... │
-└─────────────────────────────────┘
-```
-
-## 📱 **用户体验流程**
-
-### 普通用户模式
-1. 用户看到简洁的用户卡片
-2. 只显示基础功能入口
-3. 管理员用户可以看到模式切换按钮
-
-### 管理员模式
-1. 点击切换到管理员模式
-2. 显示"管理面板"入口按钮
-3. 点击进入统一的管理功能面板
-4. 选择具体的管理功能
-
-### 功能访问路径
-```
-用户卡片 → 切换管理员模式 → 管理面板 → 具体功能
-```
-
-## 🔧 **技术实现**
-
-### 文件结构
-```
-src/
-├── hooks/
-│ └── useAdminMode.ts # 管理员模式Hook
-├── components/
-│ ├── AdminPanel.tsx # 管理员面板组件
-│ └── AdminPanel.scss # 面板样式
-└── pages/user/components/
- └── UserCard.tsx # 更新的用户卡片
-```
-
-### 核心功能
-
-#### 状态管理
-```typescript
-const [isAdminMode, setIsAdminMode] = useState(false);
-const [showAdminPanel, setShowAdminPanel] = useState(false);
-```
-
-#### 本地存储
-```typescript
-// 保存模式状态
-Taro.setStorageSync('admin_mode', newMode);
-
-// 加载模式状态
-const savedMode = Taro.getStorageSync('admin_mode');
-```
-
-#### 权限控制
-```typescript
-{isAdmin() && (
-
-)}
-```
-
-## 🎯 **功能特性**
-
-### ✅ **已实现功能**
-1. **模式切换** - 普通用户 ↔ 管理员模式
-2. **状态持久化** - 记住用户选择
-3. **统一面板** - 集中管理功能入口
-4. **权限控制** - 只对管理员显示
-5. **用户反馈** - 切换提示和状态指示
-6. **响应式设计** - 适配不同屏幕尺寸
-
-### 🔄 **管理员功能列表**
-1. **门店核销** - 扫码核销用户优惠券
-2. **扫码登录** - 扫码快速登录网页端
-3. **用户管理** - 管理系统用户信息(待开发)
-4. **门店管理** - 管理门店信息和设置(待开发)
-5. **系统设置** - 系统配置和参数管理(待开发)
-
-## 📊 **对比效果**
-
-### 修改前
-```
-❌ 两个扫码图标并排显示
-❌ 功能入口分散
-❌ 用户容易混淆
-❌ 界面显得杂乱
-```
-
-### 修改后
-```
-✅ 统一的模式切换入口
-✅ 清晰的功能分类
-✅ 直观的管理面板
-✅ 更好的用户体验
-```
-
-## 🚀 **使用方法**
-
-### 1. 管理员用户操作
-1. 在个人中心看到模式切换按钮
-2. 点击切换到"管理员"模式
-3. 点击"管理面板"按钮
-4. 在弹出面板中选择需要的功能
-
-### 2. 开发者扩展
-添加新的管理功能:
-```typescript
-// 在 AdminPanel.tsx 中添加新功能
-{
- id: 'new-feature',
- title: '新功能',
- description: '功能描述',
- icon: ,
- color: 'bg-blue-50 border-blue-200',
- onClick: () => {
- navTo('/new-feature-page', true);
- onClose?.();
- }
-}
-```
-
-## 🎨 **样式定制**
-
-### 主题色彩
-- **普通模式**:灰色系 (`bg-gray-100`, `text-gray-600`)
-- **管理员模式**:蓝色系 (`bg-blue-500`, `text-white`)
-- **功能卡片**:彩色分类(蓝、绿、紫、橙、灰)
-
-### 动画效果
-- 模式切换:`transition-all 0.3s ease`
-- 面板弹出:`slideUp` 动画
-- 按钮点击:`scale(0.95)` 反馈
-
-## 🔮 **未来扩展**
-
-### 计划功能
-1. **角色权限** - 不同管理员角色显示不同功能
-2. **快捷操作** - 常用功能的快捷入口
-3. **统计面板** - 管理数据的可视化展示
-4. **通知中心** - 管理员消息和提醒
-
-### 技术优化
-1. **懒加载** - 按需加载管理功能模块
-2. **缓存优化** - 管理面板状态缓存
-3. **性能监控** - 管理功能使用统计
-
-## 🎉 **总结**
-
-这个管理员模式切换方案成功解决了原有的用户体验问题:
-
-1. **统一入口** - 所有管理功能通过统一面板访问
-2. **清晰分类** - 功能按类型分组,易于理解
-3. **状态记忆** - 用户选择会被记住
-4. **扩展性强** - 易于添加新的管理功能
-5. **用户友好** - 直观的界面和流畅的交互
-
-现在管理员用户可以享受更加统一和专业的管理体验!🚀
diff --git a/docs/APPINFO_FIELD_MIGRATION.md b/docs/APPINFO_FIELD_MIGRATION.md
deleted file mode 100644
index 5a11653..0000000
--- a/docs/APPINFO_FIELD_MIGRATION.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# 🔄 useShopInfo Hook 字段迁移到AppInfo
-
-## 📋 迁移概述
-
-已成功将`useShopInfo` Hook从`CmsWebsite`字段结构迁移到`AppInfo`字段结构,以匹配后台返回的新字段格式。
-
-## 🆚 字段对比表
-
-### 核心字段映射
-
-| 功能 | 原CmsWebsite字段 | 新AppInfo字段 | 状态 |
-|------|------------------|---------------|------|
-| **应用名称** | `websiteName` | `appName` | ✅ 已映射 |
-| **Logo** | `websiteLogo` | `logo` | ✅ 已映射 |
-| **图标** | `websiteIcon` | `icon` | ✅ 已映射 |
-| **域名** | `domain` | `domain` | ✅ 保持不变 |
-| **版本** | `version` | `version` | ✅ 保持不变 |
-| **过期时间** | `expirationTime` | `expirationTime` | ✅ 保持不变 |
-| **运行状态** | `running` | `running` | ✅ 保持不变 |
-| **状态文本** | `statusText` | `statusText` | ✅ 保持不变 |
-| **配置** | `config` | `config` | ✅ 保持不变 |
-| **导航** | `topNavs/bottomNavs` | `topNavs/bottomNavs` | ✅ 保持不变 |
-
-### 新增字段
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `appId` | `number` | 应用ID |
-| `description` | `string` | 应用描述 |
-| `keywords` | `string` | 关键词 |
-| `appCode` | `string` | 应用代码 |
-| `mpQrCode` | `string` | 小程序二维码 |
-| `title` | `string` | 应用标题 |
-| `expired` | `boolean` | 是否过期 |
-| `expiredDays` | `number` | 过期天数 |
-| `soon` | `number` | 即将过期标识 |
-| `statusIcon` | `string` | 状态图标 |
-| `serverTime` | `Object` | 服务器时间 |
-| `setting` | `Object` | 应用设置 |
-
-### 移除字段
-
-| 原字段 | 处理方式 | 说明 |
-|--------|----------|------|
-| `websiteDarkLogo` | 使用`logo`替代 | AppInfo中无深色Logo |
-| `phone` | 从`config`中获取 | 移至配置中 |
-| `email` | 从`config`中获取 | 移至配置中 |
-| `address` | 从`config`中获取 | 移至配置中 |
-| `icpNo` | 从`config`中获取 | 移至配置中 |
-| `search` | 从`config`中获取 | 移至配置中 |
-| `templateId` | 移除 | AppInfo中无此字段 |
-
-## 🔧 新增工具方法
-
-### 基于AppInfo的新方法
-
-```typescript
-// 应用基本信息
-getAppName() // 获取应用名称
-getAppLogo() // 获取应用Logo
-getAppIcon() // 获取应用图标
-getDescription() // 获取应用描述
-getKeywords() // 获取关键词
-getTitle() // 获取应用标题
-getMpQrCode() // 获取小程序二维码
-
-// 应用配置
-getSetting() // 获取应用设置
-getServerTime() // 获取服务器时间
-
-// 过期状态管理
-isExpired() // 检查是否过期
-getExpiredDays() // 获取过期天数
-isSoonExpired() // 检查是否即将过期
-```
-
-### 兼容旧方法
-
-```typescript
-// 保持向后兼容
-getWebsiteName() // 映射到getAppName()
-getWebsiteLogo() // 映射到getAppLogo()
-getDarkLogo() // 使用普通Logo
-getPhone() // 从config中获取
-getEmail() // 从config中获取
-getAddress() // 从config中获取
-getIcpNo() // 从config中获取
-isSearchEnabled() // 从config中获取
-```
-
-## 📊 使用示例
-
-### 新方法使用
-
-```typescript
-const {
- // 新的AppInfo方法
- getAppName,
- getAppLogo,
- getDescription,
- getMpQrCode,
- isExpired,
- getExpiredDays,
-
- // 兼容旧方法
- getWebsiteName,
- getWebsiteLogo
-} = useShopInfo();
-
-// 使用新方法
-const appName = getAppName(); // "时里亲子市集"
-const appLogo = getAppLogo(); // Logo URL
-const description = getDescription(); // 应用描述
-const qrCode = getMpQrCode(); // 小程序二维码
-const expired = isExpired(); // false
-const expiredDays = getExpiredDays(); // 30
-
-// 兼容旧方法(推荐逐步迁移到新方法)
-const websiteName = getWebsiteName(); // 等同于getAppName()
-const websiteLogo = getWebsiteLogo(); // 等同于getAppLogo()
-```
-
-### 状态检查
-
-```typescript
-const { getStatus, isExpired, isSoonExpired } = useShopInfo();
-
-const status = getStatus();
-console.log(status);
-// {
-// running: 1,
-// statusText: "运行中",
-// statusIcon: "success",
-// expired: false,
-// expiredDays: 30,
-// soon: 0
-// }
-
-if (isExpired()) {
- console.log('应用已过期');
-} else if (isSoonExpired()) {
- console.log(`应用将在${getExpiredDays()}天后过期`);
-}
-```
-
-### 配置获取
-
-```typescript
-const { getConfig, getSetting, getServerTime } = useShopInfo();
-
-const config = getConfig(); // 应用配置
-const setting = getSetting(); // 应用设置
-const serverTime = getServerTime(); // 服务器时间
-
-// 从配置中获取联系信息
-const phone = getPhone(); // 从config.phone获取
-const email = getEmail(); // 从config.email获取
-const address = getAddress(); // 从config.address获取
-```
-
-## 🔄 迁移建议
-
-### 1. **逐步迁移**
-
-```typescript
-// 阶段1:使用兼容方法(当前可用)
-const websiteName = getWebsiteName();
-const websiteLogo = getWebsiteLogo();
-
-// 阶段2:迁移到新方法(推荐)
-const appName = getAppName();
-const appLogo = getAppLogo();
-```
-
-### 2. **新功能使用新方法**
-
-```typescript
-// 新功能直接使用AppInfo方法
-const {
- getAppName,
- getAppLogo,
- getDescription,
- isExpired,
- getMpQrCode
-} = useShopInfo();
-```
-
-### 3. **配置字段处理**
-
-```typescript
-// 对于移至config的字段,使用对应的getter方法
-const phone = getPhone(); // 自动从config中获取
-const email = getEmail(); // 自动从config中获取
-const icpNo = getIcpNo(); // 自动从config中获取
-```
-
-## ⚠️ 注意事项
-
-### 1. **字段可能为空**
-
-```typescript
-// AppInfo中某些字段可能不存在,需要提供默认值
-const description = getDescription() || '暂无描述';
-const qrCode = getMpQrCode() || '';
-```
-
-### 2. **配置字段依赖**
-
-```typescript
-// 联系信息现在依赖config字段
-const config = getConfig();
-if (config && typeof config === 'object') {
- const phone = getPhone();
- const email = getEmail();
-}
-```
-
-### 3. **过期状态处理**
-
-```typescript
-// 新增的过期状态需要特殊处理
-const { isExpired, getExpiredDays, isSoonExpired } = useShopInfo();
-
-if (isExpired()) {
- // 应用已过期的处理逻辑
- showExpiredDialog();
-} else if (isSoonExpired()) {
- // 即将过期的提醒逻辑
- showExpirationWarning(getExpiredDays());
-}
-```
-
-## 🧪 测试验证
-
-### 1. **字段映射测试**
-
-```typescript
-const TestComponent = () => {
- const {
- shopInfo,
- getAppName,
- getAppLogo,
- getWebsiteName,
- getWebsiteLogo
- } = useShopInfo();
-
- return (
-
-
原始数据
-
{JSON.stringify(shopInfo, null, 2)}
-
-
新方法
-
应用名称: {getAppName()}
-
应用Logo: {getAppLogo()}
-
-
兼容方法
-
网站名称: {getWebsiteName()}
-
网站Logo: {getWebsiteLogo()}
-
- );
-};
-```
-
-### 2. **过期状态测试**
-
-```typescript
-const ExpirationTest = () => {
- const {
- isExpired,
- getExpiredDays,
- isSoonExpired,
- getStatus
- } = useShopInfo();
-
- const status = getStatus();
-
- return (
-
-
过期状态: {isExpired() ? '已过期' : '正常'}
-
过期天数: {getExpiredDays()}
-
即将过期: {isSoonExpired() ? '是' : '否'}
-
详细状态: {JSON.stringify(status, null, 2)}
-
- );
-};
-```
-
-## 🎉 迁移完成
-
-useShopInfo Hook已成功迁移到AppInfo字段结构:
-
-- ✅ **新增AppInfo专用方法**:基于新字段结构的工具方法
-- ✅ **保持向后兼容**:旧方法名仍然可用
-- ✅ **增强功能**:新增过期状态、应用设置等功能
-- ✅ **智能映射**:自动处理字段差异和默认值
-- ✅ **类型安全**:完整的TypeScript支持
-
-**现在Hook完全支持AppInfo字段结构,同时保持向后兼容!** 🚀
diff --git a/docs/ARGUMENTS_KEYWORD_FIX.md b/docs/ARGUMENTS_KEYWORD_FIX.md
deleted file mode 100644
index af68f92..0000000
--- a/docs/ARGUMENTS_KEYWORD_FIX.md
+++ /dev/null
@@ -1,197 +0,0 @@
-# 🔧 Arguments关键字修复
-
-## 问题描述
-
-在`src/pages/index/IndexWithHook.tsx`中出现TypeScript错误:
-```
-TS2304: Cannot find name 'arguments'
-```
-
-## 🔍 问题分析
-
-### 错误代码
-```typescript
-// 问题代码 ❌
- onSticky(arguments)}>
-
-```
-
-### 问题原因
-
-1. **`arguments`对象在箭头函数中不可用**
- - `arguments`是传统函数的特性
- - 箭头函数没有自己的`arguments`对象
- - 在箭头函数中使用`arguments`会导致TypeScript错误
-
-2. **函数期望参数**
- ```typescript
- const onSticky = (args: any) => {
- setStickyStatus(args[0].isFixed);
- };
- ```
- `onSticky`函数期望接收参数,但调用方式不正确。
-
-## 🔧 修复方案
-
-### 修复前 ❌
-```typescript
- onSticky(arguments)}>
-
-```
-
-### 修复后 ✅
-```typescript
- onSticky(args)}>
-
-```
-
-## 📚 技术说明
-
-### 1. **箭头函数 vs 传统函数**
-
-#### 传统函数(有arguments)
-```javascript
-function traditionalFunction() {
- console.log(arguments); // ✅ 可用
-}
-```
-
-#### 箭头函数(无arguments)
-```javascript
-const arrowFunction = () => {
- console.log(arguments); // ❌ 不可用
-};
-```
-
-### 2. **正确的参数传递方式**
-
-#### 方式1:直接传递参数(推荐)
-```typescript
- onSticky(args)}>
-```
-
-#### 方式2:使用剩余参数
-```typescript
- onSticky(args)}>
-```
-
-#### 方式3:直接传递函数引用
-```typescript
-
-```
-
-## 🎯 Sticky组件工作原理
-
-### onChange回调
-```typescript
-// Sticky组件会调用onChange并传递参数
-onChange([{ isFixed: boolean }])
-```
-
-### onSticky处理函数
-```typescript
-const onSticky = (args: any) => {
- setStickyStatus(args[0].isFixed); // 获取isFixed状态
-};
-```
-
-### 完整流程
-```
-1. Sticky组件检测滚动位置
- ↓
-2. 当达到threshold时触发onChange
- ↓
-3. onChange调用onSticky并传递状态参数
- ↓
-4. onSticky更新stickyStatus状态
- ↓
-5. Header组件根据stickyStatus调整样式
-```
-
-## ✅ 修复验证
-
-### 1. **TypeScript编译**
-- ✅ 无TS2304错误
-- ✅ 类型检查通过
-
-### 2. **功能验证**
-- ✅ Sticky功能正常工作
-- ✅ Header状态正确切换
-- ✅ 滚动时样式变化正常
-
-### 3. **代码质量**
-- ✅ 符合ES6+标准
-- ✅ TypeScript类型安全
-- ✅ 代码简洁明了
-
-## 🛠️ 相关最佳实践
-
-### 1. **避免使用arguments**
-```typescript
-// ❌ 避免
-const func = () => {
- console.log(arguments); // 不可用
-};
-
-// ✅ 推荐
-const func = (...args) => {
- console.log(args); // 使用剩余参数
-};
-```
-
-### 2. **事件处理器参数传递**
-```typescript
-// ❌ 错误方式
- handler(arguments)} />
-
-// ✅ 正确方式
- handler(data)} />
- // 直接传递
-```
-
-### 3. **TypeScript类型定义**
-```typescript
-// 更好的类型定义
-interface StickyChangeArgs {
- isFixed: boolean;
-}
-
-const onSticky = (args: StickyChangeArgs[]) => {
- setStickyStatus(args[0].isFixed);
-};
-```
-
-## 🔄 其他可能的修复方案
-
-### 方案1:直接传递函数(最简洁)
-```typescript
-
-```
-
-### 方案2:内联处理(当前方案)
-```typescript
- onSticky(args)}>
-```
-
-### 方案3:使用useCallback优化
-```typescript
-const handleStickyChange = useCallback((args: any) => {
- setStickyStatus(args[0].isFixed);
-}, []);
-
-
-```
-
-## 🎉 总结
-
-通过将`arguments`替换为正确的参数传递方式:
-
-- ✅ **修复TypeScript错误**:消除TS2304错误
-- ✅ **保持功能完整**:Sticky功能正常工作
-- ✅ **符合ES6标准**:使用现代JavaScript语法
-- ✅ **提高代码质量**:更清晰的参数传递
-
-**现在代码符合TypeScript规范,Sticky功能正常工作!** 🚀
diff --git a/docs/COUPON_API_INTEGRATION.md b/docs/COUPON_API_INTEGRATION.md
deleted file mode 100644
index 8350823..0000000
--- a/docs/COUPON_API_INTEGRATION.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# 🎯 优惠券API集成更新
-
-## 📊 后端接口分析
-
-根据后端提供的接口,有三个专门的端点来获取不同状态的优惠券:
-
-### 🔗 API端点
-
-| 接口 | 路径 | 说明 | 返回数据 |
-|------|------|------|----------|
-| 获取可用优惠券 | `/my/available` | 获取我的可用优惠券 | `List` |
-| 获取已使用优惠券 | `/my/used` | 获取我的已使用优惠券 | `List` |
-| 获取已过期优惠券 | `/my/expired` | 获取我的已过期优惠券 | `List` |
-
-## 🔧 前端API函数实现
-
-### 新增API函数
-
-```typescript
-/**
- * 获取我的可用优惠券
- */
-export async function getMyAvailableCoupons() {
- const res = await request.get>('/my/available');
- if (res.code === 0 && res.data) {
- return res.data;
- }
- return Promise.reject(new Error(res.message));
-}
-
-/**
- * 获取我的已使用优惠券
- */
-export async function getMyUsedCoupons() {
- const res = await request.get>('/my/used');
- if (res.code === 0 && res.data) {
- return res.data;
- }
- return Promise.reject(new Error(res.message));
-}
-
-/**
- * 获取我的已过期优惠券
- */
-export async function getMyExpiredCoupons() {
- const res = await request.get>('/my/expired');
- if (res.code === 0 && res.data) {
- return res.data;
- }
- return Promise.reject(new Error(res.message));
-}
-```
-
-## 🚀 业务逻辑更新
-
-### 1. **订单确认页面** (`src/shop/orderConfirm/index.tsx`)
-
-#### 更新前
-```typescript
-// 使用通用接口 + 状态过滤
-const res = await listShopUserCoupon({
- status: 0,
- validOnly: true
-})
-```
-
-#### 更新后
-```typescript
-// 直接使用专门的可用优惠券接口
-const res = await getMyAvailableCoupons()
-```
-
-#### 优势
-- ✅ **性能提升**:后端直接返回可用优惠券,无需前端过滤
-- ✅ **数据准确**:后端计算状态,避免前后端逻辑不一致
-- ✅ **代码简化**:减少前端状态判断逻辑
-
-### 2. **用户优惠券页面** (`src/user/coupon/index.tsx`)
-
-#### 更新前
-```typescript
-// 使用分页接口 + 复杂的状态过滤
-const res = await pageShopUserCoupon({
- page: currentPage,
- limit: 10,
- status: 0,
- isExpire: 0,
- // 其他过滤条件...
-})
-```
-
-#### 更新后
-```typescript
-// 根据tab直接调用对应接口
-switch (tab) {
- case '0': // 可用优惠券
- res = await getMyAvailableCoupons()
- break
- case '1': // 已使用优惠券
- res = await getMyUsedCoupons()
- break
- case '2': // 已过期优惠券
- res = await getMyExpiredCoupons()
- break
-}
-```
-
-#### 数据处理优化
-```typescript
-// 前端处理搜索和筛选
-if (searchValue) {
- filteredList = res.filter((item: any) =>
- item.name?.includes(searchValue) ||
- item.description?.includes(searchValue)
- )
-}
-
-// 前端排序
-filteredList.sort((a: any, b: any) => {
- const aValue = getValueForSort(a, filters.sortBy)
- const bValue = getValueForSort(b, filters.sortBy)
- return filters.sortOrder === 'asc' ? aValue - bValue : bValue - aValue
-})
-```
-
-### 3. **统计数据更新**
-
-#### 更新前
-```typescript
-// 使用分页接口获取count
-const [availableRes, usedRes, expiredRes] = await Promise.all([
- pageShopUserCoupon({page: 1, limit: 1, status: 0, isExpire: 0}),
- pageShopUserCoupon({page: 1, limit: 1, status: 1}),
- pageShopUserCoupon({page: 1, limit: 1, isExpire: 1})
-])
-
-setStats({
- available: availableRes?.count || 0,
- used: usedRes?.count || 0,
- expired: expiredRes?.count || 0
-})
-```
-
-#### 更新后
-```typescript
-// 直接获取数据并计算长度
-const [availableRes, usedRes, expiredRes] = await Promise.all([
- getMyAvailableCoupons(),
- getMyUsedCoupons(),
- getMyExpiredCoupons()
-])
-
-setStats({
- available: availableRes?.length || 0,
- used: usedRes?.length || 0,
- expired: expiredRes?.length || 0
-})
-```
-
-## 📈 性能优化效果
-
-### 网络请求优化
-- ✅ **减少请求参数**:不需要复杂的状态过滤参数
-- ✅ **减少数据传输**:后端直接返回目标数据
-- ✅ **提高缓存效率**:专门的端点更容易缓存
-
-### 前端处理优化
-- ✅ **简化状态管理**:不需要复杂的状态过滤逻辑
-- ✅ **提高响应速度**:减少前端数据处理时间
-- ✅ **降低内存占用**:只加载需要的数据
-
-## 🔍 数据流对比
-
-### 更新前的数据流
-```
-前端请求 → 后端分页接口 → 返回所有数据 → 前端状态过滤 → 显示结果
-```
-
-### 更新后的数据流
-```
-前端请求 → 后端专门接口 → 返回目标数据 → 直接显示结果
-```
-
-## 🧪 测试要点
-
-### 功能测试
-1. **订单确认页面**
- - [ ] 优惠券列表正确加载
- - [ ] 只显示可用的优惠券
- - [ ] 优惠券选择功能正常
-
-2. **用户优惠券页面**
- - [ ] 三个tab分别显示对应状态的优惠券
- - [ ] 统计数据正确显示
- - [ ] 搜索和筛选功能正常
-
-3. **错误处理**
- - [ ] 网络异常时的错误提示
- - [ ] 空数据时的显示
- - [ ] 接口返回异常数据的处理
-
-### 性能测试
-1. **加载速度**
- - [ ] 页面初始化速度
- - [ ] tab切换响应速度
- - [ ] 数据刷新速度
-
-2. **内存使用**
- - [ ] 数据加载后的内存占用
- - [ ] 页面切换时的内存释放
-
-## 🚨 注意事项
-
-### 1. **接口兼容性**
-- 确保后端接口已经部署并可用
-- 检查接口返回的数据结构是否符合预期
-- 验证错误码和错误信息的处理
-
-### 2. **数据一致性**
-- 确保三个接口返回的数据状态正确
-- 验证统计数据与列表数据的一致性
-- 检查实时状态更新的准确性
-
-### 3. **用户体验**
-- 保持加载状态的显示
-- 提供合适的错误提示
-- 确保操作反馈及时
-
-## 🎯 预期收益
-
-### 开发效率
-- ✅ **代码简化**:减少复杂的状态判断逻辑
-- ✅ **维护便利**:业务逻辑更清晰
-- ✅ **扩展性强**:易于添加新的状态类型
-
-### 用户体验
-- ✅ **响应更快**:减少数据处理时间
-- ✅ **数据准确**:后端计算状态更可靠
-- ✅ **功能稳定**:减少前端状态判断错误
-
-### 系统性能
-- ✅ **网络优化**:减少不必要的数据传输
-- ✅ **服务器优化**:专门的查询更高效
-- ✅ **缓存友好**:专门接口更容易缓存
-
-**现在优惠券功能已经完全适配后端的专门接口,提供了更好的性能和用户体验!** 🎉
diff --git a/docs/COUPON_CARD_ALIGNMENT_FIX.md b/docs/COUPON_CARD_ALIGNMENT_FIX.md
deleted file mode 100644
index fb1cff3..0000000
--- a/docs/COUPON_CARD_ALIGNMENT_FIX.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# 🎨 优惠券卡片对齐问题修复
-
-## 🚨 问题描述
-
-从截图可以看出,优惠券卡片存在对齐问题:
-- 右侧的优惠券信息和按钮没有垂直居中
-- 整体布局看起来不够协调
-- 视觉效果不够美观
-
-## 🔍 问题分析
-
-### 原始布局问题
-```scss
-.coupon-right {
- flex: 1;
- display: flex;
- flex-direction: column; // ❌ 垂直布局导致对齐问题
- justify-content: space-between; // ❌ 两端对齐,中间留空
- padding: 16px;
-}
-```
-
-**问题**:
-- 使用`flex-direction: column`垂直布局
-- `justify-content: space-between`导致内容分散
-- 信息和按钮没有垂直居中对齐
-
-## ✅ 修复方案
-
-### 新的布局设计
-```scss
-.coupon-right {
- flex: 1;
- display: flex;
- flex-direction: row; // ✅ 水平布局
- align-items: center; // ✅ 垂直居中对齐
- justify-content: space-between; // ✅ 左右分布
- padding: 16px;
-}
-```
-
-### 信息区域优化
-```scss
-.coupon-info {
- flex: 1;
- display: flex;
- flex-direction: column; // ✅ 信息垂直排列
- justify-content: center; // ✅ 内容居中
-}
-```
-
-### 按钮区域优化
-```scss
-.coupon-actions {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- flex-shrink: 0; // ✅ 防止按钮被压缩
-}
-```
-
-## 🎯 修复效果
-
-### 修复前的布局
-```
-┌─────────────────────────────────────┐
-│ ¥0 │ 优惠券 │
-│ 无门槛 │ │
-│ │ │
-│ │ 立即使用 │
-└─────────────────────────────────────┘
-```
-**问题**:信息和按钮分散在上下两端
-
-### 修复后的布局
-```
-┌─────────────────────────────────────┐
-│ ¥0 │ 优惠券 立即使用 │
-│ 无门槛 │ 有效期信息 │
-└─────────────────────────────────────┘
-```
-**效果**:信息和按钮水平对齐,垂直居中
-
-## 📋 具体修改内容
-
-### 1. 主容器布局调整
-- **flex-direction**: `column` → `row`
-- **align-items**: 新增 `center`
-- **justify-content**: 保持 `space-between`
-
-### 2. 信息区域优化
-- **display**: 新增 `flex`
-- **flex-direction**: 新增 `column`
-- **justify-content**: 新增 `center`
-
-### 3. 按钮区域优化
-- **flex-shrink**: 新增 `0`(防止压缩)
-
-## 🎨 视觉效果改进
-
-### 对齐效果
-- ✅ 左侧金额区域:垂直居中
-- ✅ 中间信息区域:垂直居中
-- ✅ 右侧按钮区域:垂直居中
-- ✅ 整体布局:水平对齐
-
-### 空间利用
-- ✅ 信息区域充分利用空间
-- ✅ 按钮区域固定宽度
-- ✅ 整体比例协调
-
-### 响应式适配
-- ✅ 不同内容长度自适应
-- ✅ 按钮始终保持右对齐
-- ✅ 信息区域弹性伸缩
-
-## 🚀 验证步骤
-
-现在你可以:
-
-### 1. 重新编译项目
-```bash
-npm run build:weapp
-```
-
-### 2. 查看修复效果
-- 进入优惠券页面
-- 查看卡片布局是否对齐
-- 确认信息和按钮垂直居中
-
-### 3. 测试不同状态
-- 可用优惠券(显示"立即使用"按钮)
-- 已使用优惠券(显示状态文字)
-- 已过期优惠券(显示状态文字)
-
-## 🎯 预期效果
-
-修复后的优惠券卡片应该:
-- ✅ 左侧金额区域垂直居中
-- ✅ 中间优惠券信息垂直居中
-- ✅ 右侧按钮或状态垂直居中
-- ✅ 整体视觉效果协调美观
-- ✅ 不同内容长度都能正确对齐
-
-## 🔧 技术细节
-
-### Flexbox布局原理
-```scss
-// 主容器:水平布局,垂直居中
-.coupon-right {
- display: flex;
- flex-direction: row; // 水平排列
- align-items: center; // 垂直居中
-}
-
-// 信息区域:垂直布局,内容居中
-.coupon-info {
- display: flex;
- flex-direction: column; // 垂直排列
- justify-content: center; // 内容居中
-}
-```
-
-### 空间分配策略
-- **信息区域**: `flex: 1` 占据剩余空间
-- **按钮区域**: `flex-shrink: 0` 固定尺寸
-- **整体布局**: `justify-content: space-between` 两端对齐
-
-## 🎉 总结
-
-**优惠券卡片对齐问题已修复!**
-
-- **修复类型**: CSS布局优化
-- **影响范围**: 所有优惠券卡片
-- **视觉改进**: 垂直居中对齐
-- **兼容性**: 保持所有功能不变
-
-**现在重新编译查看效果,优惠券卡片应该完美对齐了!** 🎨
diff --git a/docs/COUPON_DISPLAY_DEBUG.md b/docs/COUPON_DISPLAY_DEBUG.md
deleted file mode 100644
index 10ec795..0000000
--- a/docs/COUPON_DISPLAY_DEBUG.md
+++ /dev/null
@@ -1,168 +0,0 @@
-# 🔍 优惠券显示问题调试
-
-## 问题描述
-
-你反馈优惠券有数据了,但是没有显示出来。这是一个常见的前端数据渲染问题。
-
-## 🚀 已添加的调试功能
-
-我已经在优惠券页面添加了详细的调试信息,帮助我们找出问题所在:
-
-### 1. 数据加载调试
-在 `reload` 函数中添加了详细的日志:
-```typescript
-console.log('优惠券数据加载成功:', {
- isRefresh,
- currentPage,
- statusFilter,
- responseData: res,
- newListLength: newList.length,
- activeTab
-})
-```
-
-### 2. 数据转换调试
-在 `transformCouponData` 函数中添加了输入输出日志:
-```typescript
-console.log('转换优惠券数据:', coupon)
-console.log('转换后的数据:', result)
-```
-
-### 3. 页面渲染调试
-在页面上添加了可视化的调试信息:
-- 显示 `list.length`、`loading` 状态、`activeTab`
-- 显示转换后的数据结构
-
-## 🔍 排查步骤
-
-现在请按以下步骤操作:
-
-### 1. 重新编译和运行
-```bash
-npm run build:weapp
-```
-
-### 2. 打开优惠券页面
-- 进入优惠券管理页面
-- 打开开发者工具的控制台
-
-### 3. 查看调试信息
-在控制台中查看以下信息:
-
-#### A. 数据加载日志
-```
-优惠券数据加载成功: {
- isRefresh: true,
- currentPage: 1,
- statusFilter: { status: 0, isExpire: 0 },
- responseData: { list: [...], count: 5 },
- newListLength: 5,
- activeTab: "0"
-}
-```
-
-#### B. 数据转换日志
-```
-转换优惠券数据: { id: 1, name: "满减券", type: 10, ... }
-转换后的数据: { amount: 10, type: 1, status: 0, ... }
-```
-
-#### C. 页面显示的调试信息
-页面上会显示黄色和蓝色的调试框,显示:
-- `list.length=5, loading=false, activeTab=0`
-- 转换后的数据结构
-
-## 🎯 可能的问题和解决方案
-
-### 问题1:数据加载失败
-**症状**:控制台显示"优惠券数据为空"
-**解决方案**:检查API接口是否正常,网络是否连通
-
-### 问题2:数据转换错误
-**症状**:原始数据有,但转换后数据异常
-**解决方案**:检查数据字段映射是否正确
-
-### 问题3:条件渲染问题
-**症状**:数据正常,但页面显示空状态
-**解决方案**:检查渲染条件逻辑
-
-### 问题4:组件渲染问题
-**症状**:数据传递正常,但CouponCard组件不显示
-**解决方案**:检查CouponCard组件的props和样式
-
-### 问题5:Tab切换问题
-**症状**:某个Tab下有数据,其他Tab下没有
-**解决方案**:检查状态过滤逻辑
-
-## 📋 常见原因分析
-
-### 1. API响应格式问题
-```typescript
-// 期望格式
-{
- code: 0,
- data: {
- list: [...],
- count: 5
- }
-}
-
-// 实际格式可能不同
-{
- code: 0,
- data: [...] // 直接是数组
-}
-```
-
-### 2. 数据字段不匹配
-```typescript
-// 期望字段
-{
- type: 10, // 满减券
- reducePrice: "10", // 减免金额
- minPrice: "100" // 最低消费
-}
-
-// 实际字段可能不同
-{
- couponType: 10,
- amount: "10",
- threshold: "100"
-}
-```
-
-### 3. 状态过滤问题
-```typescript
-// 可用优惠券过滤条件
-{ status: 0, isExpire: 0 }
-
-// 但实际数据可能是
-{ status: "0", isExpire: "0" } // 字符串类型
-```
-
-## 🚀 下一步操作
-
-1. **查看控制台日志**:告诉我你看到了什么调试信息
-2. **截图调试信息**:如果可能,截图页面上的调试框
-3. **检查网络请求**:在开发者工具的Network标签查看API请求和响应
-
-## 🔧 临时解决方案
-
-如果问题复杂,我们可以先用一个简单的测试数据来验证组件是否正常:
-
-```typescript
-// 在页面中添加测试数据
-const testCoupons = [{
- amount: 10,
- type: 1,
- status: 0,
- minAmount: 100,
- title: "测试优惠券",
- startTime: "2024-01-01",
- endTime: "2024-12-31",
- showUseBtn: true,
- theme: "red"
-}]
-```
-
-**现在请重新运行应用,查看控制台的调试信息,然后告诉我你看到了什么!** 🔍
diff --git a/docs/COUPON_PAYMENT_ISSUE_ANALYSIS.md b/docs/COUPON_PAYMENT_ISSUE_ANALYSIS.md
deleted file mode 100644
index 7f46dc0..0000000
--- a/docs/COUPON_PAYMENT_ISSUE_ANALYSIS.md
+++ /dev/null
@@ -1,340 +0,0 @@
-# 🚨 优惠券支付问题分析
-
-## 问题描述
-
-用户选择优惠券后支付失败,但系统仍然提示"支付成功",这是一个严重的用户体验问题。
-
-## 🔍 问题分析
-
-### 1. **支付流程问题**
-
-#### 当前支付流程
-```typescript
-// OrderConfirm.tsx - onPay函数
-const onPay = async (goods: ShopGoods) => {
- try {
- setPayLoading(true)
-
- // 构建订单数据
- const orderData = buildSingleGoodsOrder(
- goods.goodsId!,
- quantity,
- address.id,
- {
- comments: goods.name,
- deliveryType: 0,
- buyerRemarks: orderRemark,
- couponId: selectedCoupon ? selectedCoupon.id : undefined // ⚠️ 问题点1
- }
- );
-
- // 执行支付
- await PaymentHandler.pay(orderData, paymentType);
-
- // ❌ 问题点2:无论支付是否真正成功,都会显示成功
- Taro.showToast({
- title: '支付成功',
- icon: 'success'
- })
- } catch (error) {
- // ❌ 问题点3:错误处理不够详细
- Taro.showToast({
- title: '支付失败,请重试',
- icon: 'error'
- })
- }
-};
-```
-
-### 2. **PaymentHandler问题**
-
-#### 支付处理逻辑缺陷
-```typescript
-// payment.ts - PaymentHandler.pay
-static async pay(orderData, paymentType, callback?) {
- try {
- // 创建订单
- const result = await createOrder(orderData);
-
- // 根据支付类型处理
- switch (paymentType) {
- case PaymentType.WECHAT:
- await this.handleWechatPay(result);
- break;
- case PaymentType.BALANCE:
- await this.handleBalancePay(result); // ⚠️ 问题点4
- break;
- }
-
- // ❌ 问题点5:无论实际支付结果如何,都显示成功
- Taro.showToast({
- title: '支付成功',
- icon: 'success'
- });
-
- // ❌ 问题点6:自动跳转,用户无法确认实际状态
- setTimeout(() => {
- Taro.navigateTo({ url: '/user/order/order' });
- }, 2000);
-
- } catch (error) {
- // 错误处理
- }
-}
-```
-
-### 3. **余额支付逻辑问题**
-
-#### 余额支付处理不完善
-```typescript
-// payment.ts - handleBalancePay
-private static async handleBalancePay(result: any): Promise {
- // ❌ 问题点7:只检查orderNo,不检查实际支付状态
- if (!result || !result.orderNo) {
- throw new Error('余额支付失败');
- }
- // ❌ 问题点8:没有验证余额是否足够,支付是否真正成功
-}
-```
-
-### 4. **优惠券相关问题**
-
-#### 优惠券ID传递问题
-```typescript
-// OrderConfirm.tsx
-couponId: selectedCoupon ? selectedCoupon.id : undefined
-
-// ⚠️ 问题点9:selectedCoupon.id可能是字符串或其他类型
-// 后端可能期望数字类型的couponId
-```
-
-## 🚨 **根本原因分析**
-
-### 1. **双重成功提示**
-```
-OrderConfirm.onPay() → 显示"支付成功"
- ↓
-PaymentHandler.pay() → 再次显示"支付成功"
-```
-**结果:** 即使支付失败,用户也会看到成功提示!
-
-### 2. **支付状态验证缺失**
-- 没有验证后端返回的实际支付状态
-- 没有检查优惠券是否成功应用
-- 没有验证最终扣款金额是否正确
-
-### 3. **错误处理不完善**
-- catch块捕获异常后,PaymentHandler仍可能显示成功
-- 没有区分不同类型的支付失败原因
-- 优惠券相关错误没有特殊处理
-
-### 4. **余额支付逻辑缺陷**
-- 只检查订单创建,不检查实际扣款
-- 没有验证余额是否充足
-- 没有确认优惠券折扣是否正确应用
-
-## 🔧 **修复方案**
-
-### 1. **修复双重提示问题**
-
-#### 修改OrderConfirm.tsx
-```typescript
-const onPay = async (goods: ShopGoods) => {
- try {
- setPayLoading(true)
-
- const orderData = buildSingleGoodsOrder(/*...*/);
-
- // ✅ 不在这里显示成功提示,让PaymentHandler统一处理
- await PaymentHandler.pay(orderData, paymentType);
-
- // ❌ 删除这里的成功提示
- // Taro.showToast({
- // title: '支付成功',
- // icon: 'success'
- // })
-
- } catch (error) {
- console.error('支付失败:', error)
- // ✅ 只处理PaymentHandler未处理的错误
- if (!error.handled) {
- Taro.showToast({
- title: error.message || '支付失败,请重试',
- icon: 'error'
- })
- }
- } finally {
- setPayLoading(false)
- }
-};
-```
-
-### 2. **完善PaymentHandler**
-
-#### 修改payment.ts
-```typescript
-static async pay(orderData, paymentType, callback?) {
- Taro.showLoading({ title: '支付中...' });
-
- try {
- // 创建订单
- const result = await createOrder(orderData);
-
- if (!result) {
- throw new Error('创建订单失败');
- }
-
- // ✅ 验证订单创建结果
- if (!result.orderNo) {
- throw new Error('订单号获取失败');
- }
-
- let paymentSuccess = false;
-
- // 根据支付类型处理
- switch (paymentType) {
- case PaymentType.WECHAT:
- await this.handleWechatPay(result);
- paymentSuccess = true;
- break;
- case PaymentType.BALANCE:
- paymentSuccess = await this.handleBalancePay(result);
- break;
- }
-
- // ✅ 只有确认支付成功才显示成功提示
- if (paymentSuccess) {
- Taro.showToast({
- title: '支付成功',
- icon: 'success'
- });
-
- callback?.onSuccess?.();
-
- setTimeout(() => {
- Taro.navigateTo({ url: '/user/order/order' });
- }, 2000);
- } else {
- throw new Error('支付未完成');
- }
-
- } catch (error: any) {
- console.error('支付失败:', error);
- const errorMessage = error.message || '支付失败';
-
- Taro.showToast({
- title: errorMessage,
- icon: 'error'
- });
-
- // ✅ 标记错误已处理
- error.handled = true;
- callback?.onError?.(errorMessage);
- throw error;
- } finally {
- Taro.hideLoading();
- callback?.onComplete?.();
- }
-}
-```
-
-### 3. **完善余额支付处理**
-
-```typescript
-private static async handleBalancePay(result: any): Promise {
- if (!result || !result.orderNo) {
- throw new Error('余额支付参数错误');
- }
-
- // ✅ 检查支付状态字段
- if (result.payStatus === false || result.payStatus === 0) {
- throw new Error('余额不足或支付失败');
- }
-
- // ✅ 检查订单状态
- if (result.orderStatus !== 1) {
- throw new Error('订单状态异常');
- }
-
- // ✅ 验证实际扣款金额
- if (result.payPrice && parseFloat(result.payPrice) <= 0) {
- throw new Error('支付金额异常');
- }
-
- return true;
-}
-```
-
-### 4. **优惠券ID类型修复**
-
-```typescript
-// OrderConfirm.tsx
-const orderData = buildSingleGoodsOrder(
- goods.goodsId!,
- quantity,
- address.id,
- {
- comments: goods.name,
- deliveryType: 0,
- buyerRemarks: orderRemark,
- // ✅ 确保couponId是数字类型
- couponId: selectedCoupon ? Number(selectedCoupon.id) : undefined
- }
-);
-```
-
-### 5. **增强错误处理**
-
-```typescript
-// 在PaymentHandler中添加详细错误分类
-private static getErrorMessage(error: any): string {
- if (error.message?.includes('余额不足')) {
- return '账户余额不足,请充值后重试';
- }
- if (error.message?.includes('优惠券')) {
- return '优惠券使用失败,请重新选择';
- }
- if (error.message?.includes('库存')) {
- return '商品库存不足,请减少购买数量';
- }
- return error.message || '支付失败,请重试';
-}
-```
-
-## 🧪 **测试验证**
-
-### 1. **测试场景**
-- [ ] 使用优惠券 + 余额支付
-- [ ] 使用优惠券 + 微信支付
-- [ ] 余额不足的情况
-- [ ] 优惠券失效的情况
-- [ ] 网络异常的情况
-
-### 2. **验证要点**
-- [ ] 支付成功时只显示一次成功提示
-- [ ] 支付失败时显示具体失败原因
-- [ ] 优惠券折扣正确应用
-- [ ] 最终扣款金额正确
-- [ ] 订单状态正确更新
-
-## 🎯 **修复优先级**
-
-### 🔥 **紧急修复**
-1. **移除双重成功提示** - 避免误导用户
-2. **完善支付状态验证** - 确保支付真正成功
-3. **修复余额支付逻辑** - 检查实际扣款状态
-
-### 🔶 **重要改进**
-1. **优化错误提示** - 提供具体失败原因
-2. **优惠券ID类型修复** - 确保数据类型正确
-3. **增强日志记录** - 便于问题排查
-
-## 🚨 **临时解决方案**
-
-在完整修复之前,可以:
-
-1. **禁用优惠券功能** - 避免支付问题
-2. **添加支付确认步骤** - 让用户确认支付结果
-3. **增加订单状态检查** - 支付后验证订单状态
-
-**建议立即修复此问题,避免用户资金损失和投诉!** 🚨
diff --git a/docs/COUPON_STATUS_DEBUG.md b/docs/COUPON_STATUS_DEBUG.md
deleted file mode 100644
index 5b34076..0000000
--- a/docs/COUPON_STATUS_DEBUG.md
+++ /dev/null
@@ -1,224 +0,0 @@
-# 🐛 优惠券状态显示问题调试
-
-## 🔍 问题描述
-
-用户反馈优惠券显示"1过期"状态不对,应该显示正确的状态文本。
-
-## 📊 问题分析
-
-### 可能的原因
-
-1. **数据转换问题**:后端数据转换为前端格式时出错
-2. **状态文本缺失**:后端没有返回`statusText`字段
-3. **显示逻辑错误**:CouponCard组件的显示逻辑有问题
-4. **类型不匹配**:优惠券类型值不匹配导致显示异常
-
-## 🔧 已实施的修复
-
-### 1. **更新CouponCard组件状态显示逻辑**
-
-```typescript
-// 格式化有效期显示
-const formatValidityPeriod = () => {
- // 第一优先级:使用后端返回的状态文本
- if (statusText) {
- return statusText
- }
-
- // 第二优先级:根据状态码显示
- if (status === 2) {
- return '已过期'
- }
-
- if (status === 1) {
- return '已使用'
- }
-
- // 第三优先级:使用后端计算的剩余时间
- if (isExpiringSoon && daysRemaining !== undefined) {
- if (daysRemaining <= 0 && hoursRemaining !== undefined) {
- return `${hoursRemaining}小时后过期`
- }
- return `${daysRemaining}天后过期`
- }
-
- // 兜底逻辑:使用前端计算
- // ...
-}
-```
-
-### 2. **统一数据转换函数**
-
-```typescript
-// 使用统一的转换函数
-const transformCouponDataWithAction = (coupon: ShopUserCoupon): CouponCardProps => {
- console.log('原始优惠券数据:', coupon)
-
- // 使用统一的转换函数
- const transformedCoupon = transformCouponData(coupon)
-
- console.log('转换后的优惠券数据:', transformedCoupon)
-
- // 添加使用按钮和点击事件
- const result = {
- ...transformedCoupon,
- showUseBtn: transformedCoupon.status === 0,
- onUse: () => handleUseCoupon(coupon)
- }
-
- console.log('最终优惠券数据:', result)
- return result
-}
-```
-
-### 3. **修复类型值匹配**
-
-```typescript
-// CouponCardProps接口更新
-export interface CouponCardProps {
- type?: 10 | 20 | 30; // 更新为后端使用的类型值
- statusText?: string; // 添加状态文本字段
- // ...其他字段
-}
-
-// CouponCard组件类型处理更新
-const formatAmount = () => {
- switch (type) {
- case 10: // 满减券
- return `¥${amount}`
- case 20: // 折扣券
- return `${amount}折`
- case 30: // 免费券
- return '免费'
- default:
- return `¥${amount}`
- }
-}
-```
-
-## 🧪 调试步骤
-
-### 1. **检查后端数据**
-
-在浏览器开发者工具中查看网络请求:
-
-```javascript
-// 检查API返回的数据结构
-{
- "code": 0,
- "data": [
- {
- "id": "123",
- "name": "测试优惠券",
- "type": 10, // 优惠券类型
- "status": 0, // 使用状态
- "statusText": "可用", // 状态文本 ← 检查这个字段
- "isExpire": 0, // 是否过期
- "reducePrice": "5", // 减免金额
- "minPrice": "20", // 最低消费
- "startTime": "2024-01-01",
- "endTime": "2024-12-31",
- "isExpiringSoon": false,
- "daysRemaining": 30,
- "hoursRemaining": null
- }
- ]
-}
-```
-
-### 2. **检查控制台日志**
-
-查看浏览器控制台中的调试信息:
-
-```javascript
-// 应该看到这些日志
-原始优惠券数据: { id: "123", name: "测试优惠券", ... }
-转换后的优惠券数据: { id: "123", amount: 5, type: 10, statusText: "可用", ... }
-最终优惠券数据: { id: "123", amount: 5, type: 10, statusText: "可用", showUseBtn: true, ... }
-```
-
-### 3. **检查组件渲染**
-
-在CouponCard组件中添加调试信息:
-
-```typescript
-console.log('CouponCard props:', {
- id,
- amount,
- type,
- status,
- statusText,
- title,
- isExpiringSoon,
- daysRemaining
-})
-
-console.log('formatValidityPeriod result:', formatValidityPeriod())
-```
-
-## 🔍 常见问题排查
-
-### 问题1:显示"1过期"而不是"已过期"
-
-**可能原因**:
-- 后端没有返回`statusText`字段
-- `statusText`字段值不正确
-- 前端显示逻辑有误
-
-**排查方法**:
-1. 检查网络请求中的`statusText`字段
-2. 检查控制台中的转换日志
-3. 确认CouponCard组件接收到的props
-
-### 问题2:优惠券类型显示错误
-
-**可能原因**:
-- 类型值不匹配(1,2,3 vs 10,20,30)
-- 转换函数逻辑错误
-
-**排查方法**:
-1. 检查后端返回的`type`字段值
-2. 确认转换函数中的类型映射
-3. 检查CouponCard组件的类型处理
-
-### 问题3:状态判断错误
-
-**可能原因**:
-- `status`和`isExpire`字段逻辑冲突
-- 状态优先级处理错误
-
-**排查方法**:
-1. 检查后端状态字段的含义
-2. 确认前端状态判断逻辑
-3. 验证不同状态的显示效果
-
-## 🎯 预期修复效果
-
-### 修复前
-```
-显示:¥5 无门槛 测试 1过期 [立即使用]
-```
-
-### 修复后
-```
-显示:¥5 无门槛 测试优惠券 1天后过期 [立即使用]
-或者:¥5 无门槛 测试优惠券 已过期 [不显示按钮]
-```
-
-## 📝 测试清单
-
-- [ ] 可用优惠券显示正确的剩余时间
-- [ ] 已使用优惠券显示"已使用"
-- [ ] 已过期优惠券显示"已过期"
-- [ ] 即将过期优惠券显示"X天后过期"
-- [ ] 优惠券类型和金额显示正确
-- [ ] 使用按钮只在可用状态显示
-
-## 🚀 下一步行动
-
-1. **测试修复效果**:重新加载页面,检查优惠券状态显示
-2. **验证数据流**:确认从API到组件的数据传递正确
-3. **完善错误处理**:添加数据异常时的兜底显示
-4. **优化用户体验**:确保状态变化时的实时更新
-
-**如果问题仍然存在,请检查浏览器控制台中的调试日志,并提供具体的错误信息。** 🔍
diff --git a/docs/COUPON_WARNINGS_FIXED.md b/docs/COUPON_WARNINGS_FIXED.md
deleted file mode 100644
index 1c76dfc..0000000
--- a/docs/COUPON_WARNINGS_FIXED.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# 🔧 优惠券组件警告修复
-
-## 🚨 修复的警告
-
-### 1. **类型值不匹配警告**
-
-#### 问题
-CouponCard组件中使用了旧的类型值(1, 2, 3)进行判断,但接口定义已更新为新的类型值(10, 20, 30)。
-
-#### 修复前
-```typescript
-// 错误的类型判断
-{type !== 3 && ¥}
-{title || (type === 1 ? '满减券' : type === 2 ? '折扣券' : '免费券')}
-```
-
-#### 修复后
-```typescript
-// 正确的类型判断
-{type !== 30 && ¥}
-{title || (type === 10 ? '满减券' : type === 20 ? '折扣券' : '免费券')}
-```
-
-### 2. **未使用的函数警告**
-
-#### 问题
-定义了但未使用的函数会产生TypeScript/ESLint警告。
-
-#### 修复前
-```typescript
-// 未使用的函数
-const getValidityText = () => {
- if (startTime && endTime) {
- return `${formatDate(startTime)}-${formatDate(endTime)}`
- }
- return ''
-}
-
-const formatDate = (dateStr?: string) => {
- if (!dateStr) return ''
- const date = new Date(dateStr)
- return `${date.getMonth() + 1}.${date.getDate()}`
-}
-
-console.log(getValidityText) // 错误的调用方式
-```
-
-#### 修复后
-```typescript
-// 删除了未使用的函数
-// getValidityText 和 formatDate 函数已被删除
-```
-
-### 3. **代码清理**
-
-#### 问题
-多余的空行和无用的console.log语句。
-
-#### 修复前
-```typescript
-console.log(getValidityText) // 无意义的日志
-
-
-
-
-
-
-const themeClass = getThemeClass() // 多余的空行
-```
-
-#### 修复后
-```typescript
-const themeClass = getThemeClass() // 清理后的代码
-```
-
-## ✅ 修复结果
-
-### 类型安全性提升
-- ✅ 所有类型判断使用正确的类型值(10, 20, 30)
-- ✅ 与接口定义保持一致
-- ✅ 避免了类型不匹配的运行时错误
-
-### 代码质量提升
-- ✅ 删除了未使用的函数和变量
-- ✅ 清理了无用的console.log语句
-- ✅ 整理了代码格式和空行
-
-### 警告消除
-- ✅ TypeScript类型警告已消除
-- ✅ ESLint未使用变量警告已消除
-- ✅ 代码风格警告已消除
-
-## 🎯 优惠券类型映射
-
-| 后端类型值 | 前端显示 | 说明 |
-|-----------|----------|------|
-| 10 | 满减券 | 满X减Y,显示¥符号 |
-| 20 | 折扣券 | 满X享Y折,显示¥符号 |
-| 30 | 免费券 | 免费使用,不显示¥符号 |
-
-## 🔍 修复的具体位置
-
-### src/components/CouponCard.tsx
-
-1. **第187行**:`{type !== 3 && ...}` → `{type !== 30 && ...}`
-2. **第206行**:`type === 1 ? ... : type === 2 ? ...` → `type === 10 ? ... : type === 20 ? ...`
-3. **第170-176行**:删除未使用的`getValidityText`函数
-4. **第164-168行**:删除未使用的`formatDate`函数
-5. **第178行**:删除无用的`console.log(getValidityText)`
-6. **第160-166行**:清理多余的空行
-
-## 🧪 测试验证
-
-### 功能测试
-- [ ] 满减券正确显示¥符号和金额
-- [ ] 折扣券正确显示¥符号和折扣
-- [ ] 免费券不显示¥符号,显示"免费"
-- [ ] 优惠券标题根据类型正确显示
-
-### 代码质量测试
-- [ ] 没有TypeScript编译警告
-- [ ] 没有ESLint警告
-- [ ] 代码格式整洁
-
-### 浏览器测试
-- [ ] 控制台没有警告信息
-- [ ] 优惠券卡片正常渲染
-- [ ] 不同类型优惠券显示正确
-
-## 📈 预期效果
-
-### 修复前
-```
-⚠️ TypeScript Warning: This condition will always return 'false' since the types '10 | 20 | 30' and '3' have no overlap.
-⚠️ ESLint Warning: 'getValidityText' is defined but never used.
-⚠️ ESLint Warning: 'formatDate' is defined but never used.
-```
-
-### 修复后
-```
-✅ No warnings
-✅ Clean code
-✅ Type-safe operations
-```
-
-## 🚀 后续建议
-
-1. **代码审查**:建立代码审查流程,避免类似问题
-2. **类型检查**:启用严格的TypeScript检查
-3. **代码规范**:使用ESLint和Prettier保持代码质量
-4. **单元测试**:为组件添加单元测试,确保类型安全
-
-**现在CouponCard组件应该没有任何警告,并且类型安全!** ✨
diff --git a/docs/DEALER_OPTIMIZATION.md b/docs/DEALER_OPTIMIZATION.md
deleted file mode 100644
index d6f384a..0000000
--- a/docs/DEALER_OPTIMIZATION.md
+++ /dev/null
@@ -1,137 +0,0 @@
-# 分销中心页面优化总结
-
-## 🔍 原始问题分析
-
-### 主要问题
-1. **数据展示错误**:成为经销商时间显示的是 `money` 字段
-2. **功能缺失**:缺少导航到其他分销功能的入口
-3. **用户体验差**:页面单调,缺少视觉层次
-4. **代码问题**:路径错误,硬编码数据
-
-## 🚀 优化方案
-
-### 1. 分销中心首页 (`/dealer/index.tsx`)
-
-#### 优化内容
-- **视觉升级**:使用渐变背景和卡片设计
-- **功能导航**:添加4个核心功能的快捷入口
-- **数据可视化**:佣金和团队数据的直观展示
-- **状态区分**:非经销商和经销商状态的不同展示
-
-#### 新增功能
-- 用户头像和基本信息展示
-- 佣金统计(可提现、冻结中、累计收益)
-- 团队统计(一级、二级、三级成员)
-- 功能导航网格(分销订单、提现申请、我的团队、推广二维码)
-
-### 2. 分销订单页面 (`/dealer/orders/index.tsx`)
-
-#### 优化内容
-- **标签页设计**:全部、待结算、已完成
-- **统计面板**:总订单、总佣金、待结算金额
-- **下拉刷新**:支持手动刷新数据
-- **订单卡片**:清晰的订单信息展示
-
-#### 新增功能
-- 订单状态标签和颜色区分
-- 佣金预计和实际到账显示
-- 客户信息和订单时间
-
-### 3. 提现申请页面 (`/dealer/withdraw/index.tsx`)
-
-#### 优化内容
-- **双标签页**:申请提现 + 提现记录
-- **余额卡片**:渐变设计显示可提现余额
-- **快捷金额**:预设金额按钮和全部提现
-- **提现方式**:微信、支付宝、银行卡选择
-
-#### 新增功能
-- 提现规则说明(最低金额、手续费)
-- 提现记录状态跟踪
-- 表单验证和用户体验优化
-
-### 4. 团队管理页面 (`/dealer/team/index.tsx`)
-
-#### 优化内容
-- **团队总览**:统计卡片和层级分布图
-- **成员分级**:按一级、二级、三级分类显示
-- **成员卡片**:头像、等级、贡献数据
-- **进度条**:可视化层级分布比例
-
-#### 新增功能
-- 成员活跃状态标识
-- 贡献佣金和订单数统计
-- 团队成员数量显示
-- 等级图标和颜色区分
-
-## 📊 技术改进
-
-### 1. 数据处理
-```typescript
-// 格式化金额
-const formatMoney = (money?: string) => {
- if (!money) return '0.00'
- return parseFloat(money).toFixed(2)
-}
-
-// 格式化时间
-const formatTime = (time?: string) => {
- if (!time) return '-'
- return new Date(time).toLocaleDateString()
-}
-```
-
-### 2. 状态管理
-- 使用真实的 `dealerUser` 数据
-- 正确的字段映射和显示
-- 错误处理和加载状态
-
-### 3. 导航优化
-- 修复路径错误:`/dealer/apply/add` 而不是 `/pages/dealer/apply/add`
-- 统一的页面跳转方法
-- 清晰的功能入口
-
-## 🎨 UI/UX 改进
-
-### 1. 视觉设计
-- **渐变背景**:增加视觉吸引力
-- **卡片设计**:信息分组和层次感
-- **图标系统**:统一的图标风格
-- **颜色系统**:语义化的颜色使用
-
-### 2. 交互体验
-- **下拉刷新**:实时数据更新
-- **快捷操作**:减少用户操作步骤
-- **状态反馈**:清晰的状态提示
-- **响应式布局**:适配不同屏幕尺寸
-
-### 3. 信息架构
-- **功能分组**:相关功能集中展示
-- **数据层次**:重要信息突出显示
-- **导航清晰**:明确的页面结构
-
-## 🔧 待完善功能
-
-### 1. 数据接口集成
-- 连接真实的分销订单 API
-- 实现提现申请和记录查询
-- 团队成员数据的实时获取
-
-### 2. 功能增强
-- 推广二维码生成和分享
-- 佣金明细和结算记录
-- 团队成员邀请和管理
-
-### 3. 性能优化
-- 列表虚拟化(大量数据时)
-- 图片懒加载
-- 缓存策略优化
-
-## 📱 移动端适配
-
-- 响应式设计确保各种屏幕尺寸下的良好体验
-- 触摸友好的交互元素
-- 合适的字体大小和间距
-- 底部安全区域处理
-
-这次优化大幅提升了分销中心的用户体验和功能完整性,为后续的功能扩展奠定了良好基础。
diff --git a/docs/ERROR_UNKNOWN_TYPE_FIX.md b/docs/ERROR_UNKNOWN_TYPE_FIX.md
deleted file mode 100644
index 3eb48ef..0000000
--- a/docs/ERROR_UNKNOWN_TYPE_FIX.md
+++ /dev/null
@@ -1,258 +0,0 @@
-# 🚨 Error Unknown类型警告修复
-
-## 问题描述
-
-TypeScript警告:`Property 'message' does not exist on type 'unknown'`
-
-错误位置:`src/hooks/useUser.ts` 第100行
-
-## 🔍 问题分析
-
-### 错误原因
-在TypeScript的catch块中,`error`参数的类型默认是`unknown`,而不是`Error`类型。这是TypeScript 4.4+的严格错误处理特性:
-
-```typescript
-try {
- // 一些可能抛出错误的代码
-} catch (error) { // error的类型是unknown
- // ❌ 直接访问error.message会报错
- if (error.message?.includes('401')) {
- // TypeScript不知道unknown类型是否有message属性
- }
-}
-```
-
-### 为什么error是unknown类型?
-- JavaScript中可以抛出任何类型的值,不仅仅是Error对象
-- 可能抛出字符串、数字、null、undefined等
-- TypeScript使用`unknown`类型确保类型安全
-
-### 常见的错误抛出情况
-```javascript
-throw new Error('错误信息') // Error对象
-throw '字符串错误' // 字符串
-throw 404 // 数字
-throw { code: 500 } // 对象
-throw null // null值
-```
-
-## 🔧 修复内容
-
-### src/hooks/useUser.ts
-
-#### 修复前 ❌
-```typescript
-} catch (error) {
- console.error('获取用户信息失败:', error);
- // 如果获取失败,可能是token过期,清除登录状态
- if (error.message?.includes('401') || error.message?.includes('未授权')) {
- // ❌ Property 'message' does not exist on type 'unknown'
- logoutUser();
- }
- return null;
-}
-```
-
-#### 修复后 ✅
-```typescript
-} catch (error) {
- console.error('获取用户信息失败:', error);
- // 如果获取失败,可能是token过期,清除登录状态
- const errorMessage = error instanceof Error ? error.message : String(error);
- if (errorMessage?.includes('401') || errorMessage?.includes('未授权')) {
- logoutUser();
- }
- return null;
-}
-```
-
-## 📊 类型安全处理方案
-
-### 方案1:instanceof检查(推荐)
-```typescript
-catch (error) {
- const errorMessage = error instanceof Error ? error.message : String(error);
- // 现在errorMessage是string类型,可以安全使用
-}
-```
-
-### 方案2:类型断言(不推荐)
-```typescript
-catch (error) {
- const err = error as Error;
- // 强制断言,但不安全,如果error不是Error对象会出问题
-}
-```
-
-### 方案3:类型守卫函数
-```typescript
-function isError(error: unknown): error is Error {
- return error instanceof Error;
-}
-
-catch (error) {
- if (isError(error)) {
- console.log(error.message); // 类型安全
- }
-}
-```
-
-### 方案4:完整的错误处理
-```typescript
-function getErrorMessage(error: unknown): string {
- if (error instanceof Error) {
- return error.message;
- }
- if (typeof error === 'string') {
- return error;
- }
- if (error && typeof error === 'object' && 'message' in error) {
- return String(error.message);
- }
- return String(error);
-}
-
-catch (error) {
- const message = getErrorMessage(error);
- console.error('错误:', message);
-}
-```
-
-## ✅ 修复效果
-
-### 修复前
-```
-❌ Property 'message' does not exist on type 'unknown'
-❌ 类型不安全
-❌ 可能的运行时错误
-❌ IDE红色警告
-```
-
-### 修复后
-```
-✅ 类型检查通过
-✅ 类型安全的错误处理
-✅ 支持各种类型的错误
-✅ 没有TypeScript警告
-```
-
-## 🔍 错误处理最佳实践
-
-### 1. **统一错误处理工具函数**
-```typescript
-// utils/errorHandler.ts
-export function getErrorMessage(error: unknown): string {
- if (error instanceof Error) {
- return error.message;
- }
- if (typeof error === 'string') {
- return error;
- }
- return '未知错误';
-}
-
-export function isHttpError(error: unknown, status: number): boolean {
- const message = getErrorMessage(error);
- return message.includes(String(status));
-}
-```
-
-### 2. **在Hook中使用**
-```typescript
-import { getErrorMessage, isHttpError } from '@/utils/errorHandler';
-
-catch (error) {
- console.error('获取用户信息失败:', error);
-
- if (isHttpError(error, 401)) {
- logoutUser();
- }
-
- const message = getErrorMessage(error);
- Taro.showToast({
- title: message,
- icon: 'error'
- });
-}
-```
-
-### 3. **API错误处理**
-```typescript
-// api/request.ts
-export class ApiError extends Error {
- constructor(
- message: string,
- public status: number,
- public code?: string
- ) {
- super(message);
- this.name = 'ApiError';
- }
-}
-
-// 在API调用中
-if (response.status === 401) {
- throw new ApiError('未授权', 401, 'UNAUTHORIZED');
-}
-```
-
-## 🧪 验证方法
-
-### 1. **IDE验证**
-- 在VS Code中打开`src/hooks/useUser.ts`
-- 检查第100行是否还有红色波浪线
-- 确认没有TypeScript错误
-
-### 2. **编译验证**
-```bash
-npm run build:weapp
-```
-应该没有unknown类型相关的错误。
-
-### 3. **功能验证**
-- 模拟401错误,确认自动登出功能正常
-- 模拟网络错误,确认错误处理正常
-- 检查控制台日志输出正确
-
-## 📈 TypeScript配置建议
-
-### tsconfig.json
-```json
-{
- "compilerOptions": {
- "strict": true,
- "useUnknownInCatchVariables": true, // 启用catch中的unknown类型
- "exactOptionalPropertyTypes": true
- }
-}
-```
-
-### ESLint规则
-```json
-{
- "rules": {
- "@typescript-eslint/no-explicit-any": "error",
- "@typescript-eslint/prefer-unknown-to-any": "error"
- }
-}
-```
-
-## 🎯 相关文件检查
-
-在这个项目中,其他catch块的状态:
-- ✅ `loadUserFromStorage` - 只用于日志,安全
-- ✅ `saveUserToStorage` - 只用于日志,安全
-- ✅ `logoutUser` - 只用于日志,安全
-- ✅ `fetchUserInfo` - 已修复
-- ✅ `updateUser` - 只用于日志,安全
-
-## 🎉 总结
-
-通过使用`instanceof Error`检查和类型安全的错误处理:
-
-- ✅ **类型安全**:消除了unknown类型访问属性的警告
-- ✅ **健壮性**:支持各种类型的错误对象
-- ✅ **可维护性**:错误处理逻辑清晰明确
-- ✅ **用户体验**:401错误自动登出功能正常
-
-**现在Error unknown类型警告已完全修复!** 🎯
diff --git a/docs/FINAL_TYPE_ERROR_FIX.md b/docs/FINAL_TYPE_ERROR_FIX.md
deleted file mode 100644
index 03b325f..0000000
--- a/docs/FINAL_TYPE_ERROR_FIX.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# 🎯 最终TypeScript类型错误修复
-
-## 🚨 问题描述
-
-在VS Code中显示的TypeScript错误:
-```
-TS2322: Type '2' is not assignable to type '10 | 20 | 30 | undefined'
-Type '2' is not assignable to type '10 | 20 | 30 | undefined'
-```
-
-错误位置:`src/user/gift/receive.tsx` 第82、85行
-
-## 🔍 问题分析
-
-### 错误根因
-在`transformCouponData`函数中,定义了错误的类型:
-```typescript
-let type: 1 | 2 | 3 = 1 // ❌ 旧的类型值
-```
-
-但是CouponCard组件期望的是新的类型值:
-```typescript
-type?: 10 | 20 | 30 // ✅ 新的类型值
-```
-
-### 类型不匹配
-```typescript
-if (coupon.type === 10) {
- type = 1 // ❌ 试图将1赋值给期望10|20|30的变量
-}
-```
-
-## 🔧 修复内容
-
-### src/user/gift/receive.tsx
-
-#### 修复前 ❌
-```typescript
-const transformCouponData = (coupon: ShopCoupon): CouponCardProps => {
- let amount = 0
- let type: 1 | 2 | 3 = 1 // ❌ 错误的类型定义
-
- if (coupon.type === 10) { // 满减券
- type = 1 // ❌ 错误的赋值
- amount = parseFloat(coupon.reducePrice || '0')
- } else if (coupon.type === 20) { // 折扣券
- type = 2 // ❌ 错误的赋值
- amount = coupon.discount || 0
- } else if (coupon.type === 30) { // 免费券
- type = 3 // ❌ 错误的赋值
- amount = 0
- }
-}
-```
-
-#### 修复后 ✅
-```typescript
-const transformCouponData = (coupon: ShopCoupon): CouponCardProps => {
- let amount = 0
- let type: 10 | 20 | 30 = 10 // ✅ 正确的类型定义
-
- if (coupon.type === 10) { // 满减券
- type = 10 // ✅ 正确的赋值
- amount = parseFloat(coupon.reducePrice || '0')
- } else if (coupon.type === 20) { // 折扣券
- type = 20 // ✅ 正确的赋值
- amount = coupon.discount || 0
- } else if (coupon.type === 30) { // 免费券
- type = 30 // ✅ 正确的赋值
- amount = 0
- }
-}
-```
-
-## 📊 类型映射对比
-
-| 后端类型 | 修复前(错误) | 修复后(正确) | 说明 |
-|----------|-------------|-------------|------|
-| 10 | type = 1 ❌ | type = 10 ✅ | 满减券 |
-| 20 | type = 2 ❌ | type = 20 ✅ | 折扣券 |
-| 30 | type = 3 ❌ | type = 30 ✅ | 免费券 |
-
-## ✅ 修复效果
-
-### 修复前
-```
-❌ TS2322: Type '2' is not assignable to type '10 | 20 | 30 | undefined'
-❌ 红色错误提示
-❌ 类型不匹配
-❌ 编译可能失败
-```
-
-### 修复后
-```
-✅ 类型检查通过
-✅ 没有TypeScript错误
-✅ 类型完全匹配
-✅ 编译成功
-```
-
-## 🔍 完整修复清单
-
-现在所有相关文件都已修复:
-
-### ✅ 已修复的文件
-1. **src/components/CouponCard.tsx** - 组件内部类型判断
-2. **src/user/coupon/receive.tsx** - 优惠券领取页面
-3. **src/user/coupon/coupon.tsx** - 优惠券管理页面
-4. **src/user/gift/receive.tsx** - 礼品领取页面 ← 刚修复
-5. **src/pages/user/components/UserCard.tsx** - 用户卡片组件
-
-### ✅ 统一的类型系统
-所有文件现在都使用统一的类型值:
-- **10** = 满减券
-- **20** = 折扣券
-- **30** = 免费券
-
-## 🧪 验证方法
-
-### 1. **IDE验证**
-- 在VS Code中打开`src/user/gift/receive.tsx`
-- 检查第82、85行是否还有红色波浪线
-- 确认没有TypeScript错误提示
-
-### 2. **编译验证**
-```bash
-npm run build:weapp
-```
-应该没有任何TypeScript编译错误。
-
-### 3. **功能验证**
-- 礼品领取页面正常加载
-- 优惠券卡片正确显示类型
-- 满减券和折扣券显示¥符号
-- 免费券不显示¥符号
-
-## 🎯 技术总结
-
-### 问题本质
-这是一个**类型系统不一致**的问题:
-- 后端使用:10, 20, 30
-- 前端组件期望:10, 20, 30
-- 但转换函数使用:1, 2, 3
-
-### 解决方案
-**统一类型系统**:
-- 所有地方都使用10, 20, 30
-- 删除旧的1, 2, 3映射
-- 保持前后端类型一致
-
-### 最佳实践
-1. **类型一致性**:前后端使用相同的类型值
-2. **接口规范**:严格按照接口定义传参
-3. **代码审查**:确保类型映射正确
-4. **工具辅助**:使用TypeScript严格模式
-
-## 🎉 最终状态
-
-**现在所有TypeScript类型错误都已完全修复!**
-
-- ✅ **编译成功**:没有TypeScript错误
-- ✅ **类型安全**:所有类型定义一致
-- ✅ **功能正常**:优惠券显示和选择正常
-- ✅ **代码质量**:统一的类型系统
-
-**项目现在可以正常编译和运行了!** 🚀
diff --git a/docs/GRADIENT_DESIGN_GUIDE.md b/docs/GRADIENT_DESIGN_GUIDE.md
deleted file mode 100644
index 926cbf6..0000000
--- a/docs/GRADIENT_DESIGN_GUIDE.md
+++ /dev/null
@@ -1,206 +0,0 @@
-# 🎨 渐变设计指南
-
-## 概述
-
-我为你的分销中心设计了一套完整的渐变主题系统,包含多种美观的渐变方案和统一的设计语言。
-
-## 🌈 渐变主题方案
-
-### 1. 预设主题
-
-| 主题名称 | 颜色搭配 | 适用场景 | 视觉效果 |
-|---------|---------|---------|---------|
-| **ocean** | 蓝紫渐变 | 科技、专业 | 🌊 海洋般的深邃感 |
-| **sunset** | 橙红渐变 | 活力、热情 | 🌅 日落般的温暖感 |
-| **fresh** | 蓝绿渐变 | 清新、活力 | 🍃 清新自然的感觉 |
-| **nature** | 绿青渐变 | 生机、成长 | 🌱 生机勃勃的活力 |
-| **warm** | 金粉渐变 | 温馨、友好 | ☀️ 温暖亲和的感觉 |
-| **elegant** | 淡彩渐变 | 优雅、精致 | 💎 优雅精致的品味 |
-| **royal** | 皇家紫蓝 | 高贵、权威 | 👑 高贵典雅的气质 |
-| **fire** | 火焰粉红 | 激情、浪漫 | 🔥 激情浪漫的氛围 |
-
-### 2. 业务场景渐变
-
-```typescript
-// 分销商相关
-dealer: {
- header: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
- card: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
- success: 'linear-gradient(135deg, #10b981 0%, #34d399 100%)',
- warning: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
- danger: 'linear-gradient(135deg, #ef4444 0%, #f87171 100%)',
- info: 'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)'
-}
-
-// 金额相关
-money: {
- available: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', // 可提现 - 绿色
- frozen: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', // 冻结中 - 蓝色
- total: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)' // 累计 - 橙色
-}
-```
-
-## 🎯 设计特点
-
-### 1. 视觉层次
-- **主背景**:动态渐变,根据用户ID自动选择
-- **卡片背景**:微妙的白色渐变,增加层次感
-- **装饰元素**:半透明圆形,增加空间感
-
-### 2. 色彩心理学
-- **蓝色系**:信任、专业、稳定
-- **绿色系**:成长、财富、安全
-- **橙色系**:活力、温暖、友好
-- **紫色系**:高贵、创新、神秘
-
-### 3. 交互反馈
-- **悬停效果**:轻微的亮度变化
-- **选中状态**:边框高亮
-- **加载状态**:渐变动画
-
-## 🛠️ 使用方法
-
-### 1. 基础使用
-
-```tsx
-import { businessGradients, cardGradients } from '@/styles/gradients'
-
-// 使用预设的业务渐变
-
- 内容
-
-
-// 使用卡片渐变
-
- 卡片内容
-
-```
-
-### 2. 动态主题
-
-```tsx
-import { gradientUtils } from '@/styles/gradients'
-
-// 根据用户ID获取主题
-const userTheme = gradientUtils.getThemeByUserId(userId)
-
-
- 个性化内容
-
-```
-
-### 3. 自定义渐变
-
-```tsx
-import { gradientUtils } from '@/styles/gradients'
-
-// 创建自定义渐变
-const customGradient = gradientUtils.createGradient('#ff6b6b', '#4ecdc4', '45deg')
-
-
- 自定义内容
-
-```
-
-## 🎨 设计原则
-
-### 1. 一致性
-- 所有页面使用统一的渐变系统
-- 相同功能使用相同的颜色语义
-- 保持视觉风格的连贯性
-
-### 2. 可访问性
-- 确保文字与背景有足够的对比度
-- 支持深色模式适配
-- 考虑色盲用户的体验
-
-### 3. 性能优化
-- 使用 CSS 渐变而非图片
-- 避免过度复杂的渐变效果
-- 合理使用动画和过渡
-
-## 📱 移动端适配
-
-### 1. 响应式设计
-```css
-/* 小屏幕优化 */
-@media (max-width: 768px) {
- .gradient-header {
- background-size: 200% 200%;
- animation: gradientShift 8s ease infinite;
- }
-}
-```
-
-### 2. 性能考虑
-- 在低端设备上简化渐变效果
-- 使用 `will-change` 属性优化动画
-- 避免在滚动时使用复杂渐变
-
-## 🔧 高级功能
-
-### 1. 主题切换器
-```tsx
-import GradientThemeSelector from '@/components/GradientThemeSelector'
-
- setShowThemeSelector(false)}
- onSelect={handleThemeSelect}
- currentTheme={currentTheme}
-/>
-```
-
-### 2. 动画渐变
-```tsx
-import { animatedGradients } from '@/styles/gradients'
-
-
- 动态流动的渐变背景
-
-```
-
-### 3. 玻璃态效果
-```tsx
-import { cardGradients } from '@/styles/gradients'
-
-
- 毛玻璃效果卡片
-
-```
-
-## 🎯 最佳实践
-
-### 1. 渐变方向
-- **135度**:最常用,自然舒适
-- **90度**:垂直渐变,适合长条内容
-- **45度**:对角渐变,动感活泼
-
-### 2. 颜色选择
-- 选择色相相近的颜色
-- 避免过于强烈的对比
-- 考虑品牌色的融入
-
-### 3. 层次搭配
-- 主背景:鲜艳渐变
-- 卡片背景:淡雅渐变
-- 文字:纯色或微渐变
-
-## 🚀 未来扩展
-
-### 1. 主题商店
-- 更多预设主题
-- 用户自定义主题
-- 主题分享功能
-
-### 2. 智能推荐
-- 基于使用习惯推荐主题
-- 根据时间自动切换主题
-- 情境感知的主题选择
-
-### 3. 高级效果
-- 3D 渐变效果
-- 粒子背景
-- 交互式渐变
-
-这套渐变系统不仅美观,还具有很强的扩展性和可维护性,为你的应用提供了专业级的视觉体验!
diff --git a/docs/HEADER_MIGRATION_COMPLETE.md b/docs/HEADER_MIGRATION_COMPLETE.md
deleted file mode 100644
index f995efb..0000000
--- a/docs/HEADER_MIGRATION_COMPLETE.md
+++ /dev/null
@@ -1,224 +0,0 @@
-# ✅ Header组件迁移完成!
-
-## 🎯 迁移总结
-
-已成功将`src/pages/index/Header.tsx`组件迁移到使用`useShopInfo` Hook的方式。
-
-## 🔄 主要修改内容
-
-### 1. **导入更新**
-
-#### 修改前 ❌
-```typescript
-import {getShopInfo, getUserInfo, getWxOpenId} from "@/api/layout";
-import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
-```
-
-#### 修改后 ✅
-```typescript
-import {getUserInfo, getWxOpenId} from "@/api/layout";
-import { useShopInfo } from '@/hooks/useShopInfo';
-```
-
-**变化说明:**
-- ✅ 移除了`getShopInfo`的直接导入
-- ✅ 移除了`CmsWebsite`类型导入(Hook内部处理)
-- ✅ 添加了`useShopInfo` Hook导入
-
-### 2. **状态管理简化**
-
-#### 修改前 ❌
-```typescript
-const [config, setConfig] = useState()
-
-const reload = async () => {
- // 获取站点信息
- getShopInfo().then((data) => {
- setConfig(data);
- console.log(userInfo)
- })
- // ...
-}
-```
-
-#### 修改后 ✅
-```typescript
-// 使用新的useShopInfo Hook
-const {
- getWebsiteName,
- getWebsiteLogo,
- loading: shopLoading
-} = useShopInfo();
-
-const reload = async () => {
- // 注意:商店信息现在通过useShopInfo自动管理,不需要手动获取
- // ...
-}
-```
-
-**变化说明:**
-- ✅ 移除了`config`状态管理
-- ✅ 移除了手动调用`getShopInfo()`
-- ✅ 使用Hook提供的工具方法
-- ✅ 自动获得加载状态
-
-### 3. **类型安全改进**
-
-#### 修改前 ❌
-```typescript
-// @ts-ignore
-const handleGetPhoneNumber = ({detail}) => {
-```
-
-#### 修改后 ✅
-```typescript
-const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
-```
-
-**变化说明:**
-- ✅ 移除了`@ts-ignore`注释
-- ✅ 添加了正确的类型定义
-- ✅ 提高了类型安全性
-
-### 4. **UI渲染优化**
-
-#### 修改前 ❌
-```typescript
-
-{config?.websiteName}111
-```
-
-#### 修改后 ✅
-```typescript
-
-{getWebsiteName()}
-```
-
-**变化说明:**
-- ✅ 使用Hook提供的工具方法
-- ✅ 内置默认值处理
-- ✅ 移除了测试用的"111"、"2222"文本
-- ✅ 更简洁的代码
-
-## 📊 迁移效果对比
-
-### 代码行数
-- **修改前**: 193行
-- **修改后**: 194行
-- **变化**: +1行(主要是格式调整)
-
-### 导入依赖
-- **减少**: 2个直接API导入
-- **增加**: 1个Hook导入
-- **净减少**: 1个导入
-
-### 状态管理
-- **减少**: 1个状态变量(`config`)
-- **减少**: 1个手动API调用
-- **增加**: 自动缓存和错误处理
-
-## 🚀 获得的优势
-
-### 1. **自动缓存**
-- ✅ 30分钟智能缓存
-- ✅ 减少重复网络请求
-- ✅ 离线时使用缓存数据
-
-### 2. **错误处理**
-- ✅ 自动错误处理
-- ✅ 网络失败时的降级策略
-- ✅ 加载状态管理
-
-### 3. **代码简化**
-- ✅ 移除手动状态管理
-- ✅ 移除手动API调用
-- ✅ 内置默认值处理
-
-### 4. **类型安全**
-- ✅ 完整的TypeScript支持
-- ✅ 移除`@ts-ignore`注释
-- ✅ 编译时错误检查
-
-### 5. **性能优化**
-- ✅ 避免重复渲染
-- ✅ 智能缓存机制
-- ✅ 内存使用优化
-
-## 🧪 功能验证
-
-### 验证项目
-- ✅ **Logo显示**: `getWebsiteLogo()`正常返回Logo URL
-- ✅ **网站名称**: `getWebsiteName()`正常返回网站名称,默认"商城"
-- ✅ **登录状态**: 未登录和已登录状态下的UI正常显示
-- ✅ **手机号授权**: `handleGetPhoneNumber`类型安全,功能正常
-- ✅ **缓存机制**: 商店信息自动缓存,减少网络请求
-
-### 测试场景
-1. **首次加载**: 从服务器获取商店信息并缓存
-2. **再次访问**: 使用缓存数据,快速显示
-3. **网络异常**: 使用缓存数据,保证基本功能
-4. **缓存过期**: 自动刷新数据
-
-## 🔍 代码对比示例
-
-### 获取网站名称
-```typescript
-// 修改前 ❌
-const websiteName = config?.websiteName || '默认名称';
-
-// 修改后 ✅
-const websiteName = getWebsiteName(); // 自动处理默认值
-```
-
-### 获取Logo
-```typescript
-// 修改前 ❌
-const logo = config?.websiteLogo || config?.websiteIcon || '';
-
-// 修改后 ✅
-const logo = getWebsiteLogo(); // 自动处理多个字段的优先级
-```
-
-### 加载状态
-```typescript
-// 修改前 ❌
-// 没有统一的加载状态管理
-
-// 修改后 ✅
-const { loading: shopLoading } = useShopInfo();
-if (shopLoading) {
- return 加载中...
;
-}
-```
-
-## 🎯 后续建议
-
-### 1. **其他组件迁移**
-建议将其他使用`getShopInfo()`的组件也迁移到使用Hook的方式:
-- `src/pages/index/index.tsx`
-- 其他需要商店信息的组件
-
-### 2. **用户信息Hook**
-考虑创建`useUser` Hook来管理用户信息,进一步简化代码。
-
-### 3. **统一错误处理**
-可以在Hook中添加更多的错误处理和重试机制。
-
-## 🎉 迁移完成
-
-Header组件已成功迁移到使用`useShopInfo` Hook的方式!
-
-**主要收益:**
-- ✅ **代码更简洁**:移除了手动状态管理
-- ✅ **性能更好**:智能缓存减少网络请求
-- ✅ **更可靠**:自动错误处理和降级策略
-- ✅ **类型安全**:完整的TypeScript支持
-- ✅ **易维护**:统一的商店信息管理
-
-现在Header组件可以享受到Hook带来的所有优势,包括自动缓存、错误处理和性能优化!🚀
diff --git a/docs/HEADER_UNUSED_VARIABLES_FIX.md b/docs/HEADER_UNUSED_VARIABLES_FIX.md
deleted file mode 100644
index 7c02e99..0000000
--- a/docs/HEADER_UNUSED_VARIABLES_FIX.md
+++ /dev/null
@@ -1,256 +0,0 @@
-# 🔧 Header组件未使用变量修复
-
-## 问题描述
-
-在`src/pages/index/Header.tsx`中存在多个未使用的变量和导入,导致TypeScript警告。
-
-## 🔍 发现的问题
-
-### 1. **未使用的Hook返回值**
-```typescript
-// 问题代码 ❌
-const {
- getWebsiteName,
- getWebsiteLogo,
- loading: shopLoading // ❌ 未使用
-} = useShopInfo();
-```
-
-### 2. **未使用的状态变量**
-```typescript
-// 问题代码 ❌
-const [userInfo, setUserInfo] = useState() // ❌ 未使用
-const [showBasic, setShowBasic] = useState(false) // ❌ 基本未使用
-```
-
-### 3. **未使用的导入**
-```typescript
-// 问题代码 ❌
-import {Popup, Avatar, NavBar} from '@nutui/nutui-react-taro' // Popup未使用
-import {User} from "@/api/system/user/model"; // User类型未使用
-```
-
-### 4. **未使用的组件**
-```typescript
-// 问题代码 ❌
- {
- setShowBasic(false)
- }}
->
- 车辆信息
// 内容也不相关
-
-```
-
-## 🔧 修复方案
-
-### 1. **移除未使用的Hook返回值**
-
-#### 修复前 ❌
-```typescript
-const {
- getWebsiteName,
- getWebsiteLogo,
- loading: shopLoading // 未使用
-} = useShopInfo();
-```
-
-#### 修复后 ✅
-```typescript
-const {
- getWebsiteName,
- getWebsiteLogo
-} = useShopInfo();
-```
-
-### 2. **移除未使用的状态变量**
-
-#### 修复前 ❌
-```typescript
-const [userInfo, setUserInfo] = useState() // 未使用
-const [IsLogin, setIsLogin] = useState(true)
-const [showBasic, setShowBasic] = useState(false) // 基本未使用
-const [statusBarHeight, setStatusBarHeight] = useState()
-```
-
-#### 修复后 ✅
-```typescript
-const [IsLogin, setIsLogin] = useState(true)
-const [statusBarHeight, setStatusBarHeight] = useState()
-```
-
-### 3. **清理用户信息处理逻辑**
-
-#### 修复前 ❌
-```typescript
-getUserInfo().then((data) => {
- if (data) {
- setIsLogin(true);
- setUserInfo(data) // 设置未使用的状态
- console.log('用户信息>>>', data.phone)
- // ...
- }
-})
-```
-
-#### 修复后 ✅
-```typescript
-getUserInfo().then((data) => {
- if (data) {
- setIsLogin(true);
- console.log('用户信息>>>', data.phone)
- // ...
- }
-})
-```
-
-### 4. **移除未使用的组件**
-
-#### 修复前 ❌
-```typescript
-
- {
- setShowBasic(false)
- }}
->
- 车辆信息
-
-```
-
-#### 修复后 ✅
-```typescript
-
-```
-
-### 5. **清理导入语句**
-
-#### 修复前 ❌
-```typescript
-import {Popup, Avatar, NavBar} from '@nutui/nutui-react-taro'
-import {User} from "@/api/system/user/model";
-```
-
-#### 修复后 ✅
-```typescript
-import {Avatar, NavBar} from '@nutui/nutui-react-taro'
-```
-
-## 📊 修复统计
-
-| 项目 | 修复前 | 修复后 | 减少 |
-|------|--------|--------|------|
-| **代码行数** | 194行 | 179行 | -15行 |
-| **状态变量** | 4个 | 2个 | -2个 |
-| **导入项** | 多个未使用 | 只保留使用的 | 清理完成 |
-| **组件** | 包含未使用Popup | 只保留必要组件 | 简化完成 |
-
-## ✅ 修复效果
-
-### 修复前 ❌
-```
-⚠️ 多个TypeScript警告
-🔧 未使用的变量和导入
-📝 代码冗余,可读性差
-🐛 潜在的维护问题
-```
-
-### 修复后 ✅
-```
-✅ 无TypeScript警告
-🔧 代码简洁,只保留必要部分
-📝 提高代码可读性
-🚀 减少维护负担
-```
-
-## 🎯 保留的功能
-
-修复后保留的核心功能:
-
-### 1. **商店信息显示**
-```typescript
-const { getWebsiteName, getWebsiteLogo } = useShopInfo();
-
-// 在UI中使用
-
-{getWebsiteName()}
-```
-
-### 2. **用户登录状态管理**
-```typescript
-const [IsLogin, setIsLogin] = useState(true)
-
-// 根据登录状态显示不同UI
-{!IsLogin ? (
- // 未登录UI
-) : (
- // 已登录UI
-)}
-```
-
-### 3. **手机号授权功能**
-```typescript
-const handleGetPhoneNumber = ({detail}) => {
- // 处理手机号授权逻辑
-};
-
-
)}
diff --git a/src/pages/index/login.scss b/src/pages/index/login.scss
deleted file mode 100644
index 97f2ab4..0000000
--- a/src/pages/index/login.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-// 微信授权按钮的特殊样式
-button[open-type="getPhoneNumber"] {
- width: 100%;
- padding: 8px 0 !important;
- height: 80px;
- color: #ffffff !important;
- margin: 0 !important;
- border: none !important;
- border-radius: 50px !important;
-}
diff --git a/src/pages/user/components/IsDealer.tsx b/src/pages/user/components/IsDealer.tsx
deleted file mode 100644
index 933e4dd..0000000
--- a/src/pages/user/components/IsDealer.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import {Cell} from '@nutui/nutui-react-taro'
-import navTo from "@/utils/common";
-import {View, Text} from '@tarojs/components'
-import {ArrowRight, Reward, Setting} from '@nutui/icons-react-taro'
-import {useUser} from '@/hooks/useUser'
-import {useDealerUser} from "@/hooks/useDealerUser";
-import {useThemeStyles} from "@/hooks/useTheme";
-import { useConfig } from "@/hooks/useConfig"; // 使用新的自定义Hook
-import Taro from '@tarojs/taro'
-
-const IsDealer = () => {
- const themeStyles = useThemeStyles();
- const { config } = useConfig(); // 使用新的Hook
- const {isSuperAdmin} = useUser();
- const {dealerUser, loading: dealerLoading} = useDealerUser()
-
- /**
- * 管理中心
- */
- if (isSuperAdmin()) {
- return (
- <>
-
-
-
- 管理中心
- |
- }
- extra={}
- onClick={() => navTo('/admin/index', true)}
- />
-
- >
- )
- }
-
- /**
- * 分销中心
- */
- if (dealerUser) {
- return (
- <>
-
-
-
- {config?.vipText || '易赊宝分享中心'}
- {/*门店核销*/}
- |
- }
- extra={}
- onClick={() => navTo('/dealer/index', true)}
- />
-
- >
- )
- }
-
- /**
- * 普通用户
- */
- return (
- <>
-
-
-
- {config?.vipText || '易赊宝分享中心'}
- {config?.vipComments || ''}
- |
- }
- extra={}
- onClick={() => {
- if (dealerLoading) {
- Taro.showToast({ title: '正在加载信息,请稍等...', icon: 'none' })
- return
- }
- navTo('/dealer/apply/add', true)
- }}
- />
-
- >
- )
-}
-export default IsDealer
diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx
index 5713a25..bf60c54 100644
--- a/src/pages/user/components/UserCard.tsx
+++ b/src/pages/user/components/UserCard.tsx
@@ -12,7 +12,6 @@ import {checkAndHandleInviteRelation, getStoredInviteParams, hasPendingInvite} f
import UnifiedQRButton from "@/components/UnifiedQRButton";
import {useThemeStyles} from "@/hooks/useTheme";
import {getRootDomain} from "@/utils/domain";
-import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
import { saveStorageByLoginUser } from '@/utils/server'
const UserCard = forwardRef((_, ref) => {
@@ -20,7 +19,6 @@ const UserCard = forwardRef((_, ref) => {
const {loadUserFromStorage} = useUser();
const [IsLogin, setIsLogin] = useState(false)
const [userInfo, setUserInfo] = useState()
- const [ticketTotal, setTicketTotal] = useState(0)
const themeStyles = useThemeStyles();
const canShowScanButton = (() => {
@@ -41,7 +39,6 @@ const UserCard = forwardRef((_, ref) => {
// 下拉刷新
const reloadStats = async (showToast = false) => {
await refresh()
- reloadTicketTotal()
if (showToast) {
Taro.showToast({
title: '刷新成功',
@@ -93,9 +90,6 @@ const UserCard = forwardRef((_, ref) => {
}))
useEffect(() => {
- // 独立于用户信息授权:只要有登录 token,就可以拉取水票总数
- reloadTicketTotal()
-
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
Taro.getSetting({
success: (res) => {
@@ -112,23 +106,6 @@ const UserCard = forwardRef((_, ref) => {
});
}, []);
- const reloadTicketTotal = () => {
- const token = Taro.getStorageSync('access_token')
- const userIdRaw = Taro.getStorageSync('UserId')
- const userId = Number(userIdRaw)
- const hasUserId = Number.isFinite(userId) && userId > 0
- if (!token && !hasUserId) {
- setTicketTotal(0)
- return
- }
- getMyGltUserTicketTotal(hasUserId ? userId : undefined)
- .then((total) => setTicketTotal(typeof total === 'number' ? total : 0))
- .catch((err) => {
- console.error('个人中心水票总数加载失败:', err)
- setTicketTotal(0)
- })
- }
-
const reload = () => {
Taro.getUserInfo({
success: (res) => {
@@ -140,9 +117,8 @@ const UserCard = forwardRef((_, ref) => {
})
reloadUserInfo()
.then(() => {
- // 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
+ // 登录态已就绪后刷新卡片统计(余额/积分/券等)
refresh().then()
- reloadTicketTotal()
})
.catch(() => {
console.log('未登录')
@@ -229,9 +205,8 @@ const UserCard = forwardRef((_, ref) => {
saveStorageByLoginUser(res.data.data.access_token, res.data.data.user)
setUserInfo(res.data.data.user)
setIsLogin(true)
- // 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
+ // 登录态已就绪后刷新卡片统计(余额/积分/券等)
refresh().then()
- reloadTicketTotal()
// 登录成功后(可能是新注册用户),检查是否存在待处理的邀请关系并尝试绑定
if (hasPendingInvite()) {
diff --git a/src/pages/user/components/UserCell.tsx b/src/pages/user/components/UserCell.tsx
deleted file mode 100644
index 9354b40..0000000
--- a/src/pages/user/components/UserCell.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import {Cell} from '@nutui/nutui-react-taro'
-import navTo from "@/utils/common";
-import Taro from '@tarojs/taro'
-import {View, Text} from '@tarojs/components'
-import {ArrowRight, ShieldCheck, LogisticsError, Location, Tips, Ask} from '@nutui/icons-react-taro'
-import {useUser} from '@/hooks/useUser'
-
-const UserCell = () => {
- const {logoutUser, isCertified} = useUser();
-
- const onLogout = () => {
- Taro.showModal({
- title: '提示',
- content: '确定要退出登录吗?',
- success: function (res) {
- if (res.confirm) {
- // 使用 useUser hook 的 logoutUser 方法
- logoutUser();
- Taro.reLaunch({
- url: '/pages/index/index'
- })
- }
- }
- })
- }
-
- return (
- <>
-
-
-
- 我的服务
-
- }>
-
-
- 我的钱包
-
- }
- align="center"
- extra={}
- onClick={() => {
- navTo('/user/wallet/index', true)
- }}
- />
-
-
- 配送地址
-
- }
- align="center"
- extra={}
- onClick={() => {
- navTo('/user/address/index', true)
- }}
- />
-
-
- 实名认证
- {isCertified() && (
- 已认证
- )}
-
- }
- align="center"
- extra={}
- onClick={() => {
- navTo('/user/userVerify/index', true)
- }}
- />
-
-
- 常见问题
-
- }
- align="center"
- extra={}
- onClick={() => {
- navTo('/user/help/index')
- }}
- />
-
-
- 关于我们
-
- }
- align="center"
- extra={}
- onClick={() => {
- navTo('/user/about/index')
- }}
- />
-
-
- 账号管理
-
- }>
- }
- onClick={() => navTo('/user/profile/profile', true)}
- />
- }
- onClick={() => navTo('/user/theme/index', true)}
- />
- | }
- onClick={onLogout}
- />
- | | |
-
- >
- )
-}
-export default UserCell
diff --git a/src/pages/user/components/UserGrid.tsx b/src/pages/user/components/UserGrid.tsx
index df725bc..10b90d6 100644
--- a/src/pages/user/components/UserGrid.tsx
+++ b/src/pages/user/components/UserGrid.tsx
@@ -8,14 +8,12 @@ import {
Agenda,
// AfterSaleService,
Logout,
- Shop,
- Jdl,
Service
} from '@nutui/icons-react-taro'
import {useUser} from "@/hooks/useUser";
const UserCell = () => {
- const {logoutUser, hasRole} = useUser();
+ const {logoutUser} = useUser();
const onLogout = () => {
Taro.showModal({
@@ -48,26 +46,6 @@ const UserCell = () => {
} as React.CSSProperties}
>
- {hasRole('store') && (
- navTo('/store/index', true)}>
-
-
-
-
-
-
- )}
-
- {hasRole('rider') && (
- navTo('/rider/index', true)}>
-
-
-
-
-
-
- )}
-
navTo('/credit/order/index', true)}>
diff --git a/src/pages/user/components/UserOrder.tsx b/src/pages/user/components/UserOrder.tsx
deleted file mode 100644
index 56da5cd..0000000
--- a/src/pages/user/components/UserOrder.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import navTo from "@/utils/common";
-import {View, Text} from '@tarojs/components';
-import {Badge} from '@nutui/nutui-react-taro';
-import {ArrowRight, Wallet, Comment, Transit, Refund, Package} from '@nutui/icons-react-taro';
-import {useOrderStats} from "@/hooks/useOrderStats";
-
-function UserOrder() {
- const { orderStats, refreshOrderStats } = useOrderStats();
-
- // 处理长按刷新
- const handleLongPress = () => {
- refreshOrderStats();
- };
-
- return (
- <>
-
-
-
- 商城订单
- navTo('/user/order/order', true)}
- onLongPress={handleLongPress}
- >
- 全部订单
-
-
-
-
- {/* 待付款 */}
- {orderStats.pending > 0 ? (
-
-
- navTo('/user/order/order?statusFilter=0', true)}/>
- 待付款
-
-
- ) : (
- navTo('/user/order/order?statusFilter=0', true)}>
-
- 待付款
-
- )}
-
- {/* 待发货 */}
- {orderStats.paid > 0 ? (
-
- navTo('/user/order/order?statusFilter=1', true)}>
-
- 待发货
-
-
- ) : (
- navTo('/user/order/order?statusFilter=1', true)}>
-
- 待发货
-
- )}
-
- {/* 待收货 */}
- {orderStats.shipped > 0 ? (
-
- navTo('/user/order/order?statusFilter=3', true)}>
-
- 待收货
-
-
- ) : (
- navTo('/user/order/order?statusFilter=3', true)}>
-
- 待收货
-
- )}
-
- {/* 已完成 - 不显示badge */}
- navTo('/user/order/order?statusFilter=5', true)}>
-
- 已完成
-
-
- {/* 退货/售后 */}
- {orderStats.refund > 0 ? (
-
- navTo('/user/order/order?statusFilter=6', true)}>
-
- 退货/售后
-
-
- ) : (
- navTo('/user/order/order?statusFilter=6', true)}>
-
- 退货/售后
-
- )}
-
-
-
- >
-
- )
-}
-
-export default UserOrder;
diff --git a/src/pages/user/user.tsx b/src/pages/user/user.tsx
index 0b496bf..af920e8 100644
--- a/src/pages/user/user.tsx
+++ b/src/pages/user/user.tsx
@@ -1,4 +1,4 @@
-import {useEffect, useRef, useState} from 'react'
+import {useEffect, useRef} from 'react'
import {PullToRefresh} from '@nutui/nutui-react-taro'
import UserCard from "./components/UserCard";
import UserFooter from "./components/UserFooter";
@@ -17,15 +17,11 @@ function User() {
...themeStyles.primaryBackground,
background: '#cf1313'
}
- // TabBar 页在小程序里通常不会销毁;从“注册/申请”页返回时需要触发子组件重新初始化/拉取最新状态。
- const [dealerViewKey, setDealerViewKey] = useState(0)
- console.log(dealerViewKey)
// 下拉刷新处理
const handleRefresh = async () => {
if (userCardRef.current?.handleRefresh) {
await userCardRef.current.handleRefresh()
}
- setDealerViewKey(v => v + 1)
}
useEffect(() => {
@@ -36,7 +32,6 @@ function User() {
userCardRef.current?.reloadStats?.()
// 个人资料(头像/昵称)可能在其它页面被修改,这里确保返回时立刻刷新
userCardRef.current?.reloadUserInfo?.()
- setDealerViewKey(v => v + 1)
})
return (
diff --git a/src/passport/register.tsx b/src/passport/register.tsx
index 9162a43..027b07a 100644
--- a/src/passport/register.tsx
+++ b/src/passport/register.tsx
@@ -87,7 +87,6 @@ function isTabBarUrl(url: string) {
const pure = url.split('?')[0]
return (
pure === '/pages/index/index' ||
- pure === '/pages/find/find' ||
pure === '/pages/user/user'
)
}
diff --git a/src/passport/sms-login.tsx b/src/passport/sms-login.tsx
index 1c37e7d..4dd0017 100644
--- a/src/passport/sms-login.tsx
+++ b/src/passport/sms-login.tsx
@@ -36,7 +36,6 @@ const SmsLogin = () => {
const pure = url.split('?')[0]
return (
pure === '/pages/index/index' ||
- pure === '/pages/find/find' ||
pure === '/pages/user/user'
)
}
diff --git a/src/rider/index.config.ts b/src/rider/index.config.ts
deleted file mode 100644
index 1293fcb..0000000
--- a/src/rider/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '配送中心'
-})
diff --git a/src/rider/index.scss b/src/rider/index.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/rider/index.tsx b/src/rider/index.tsx
deleted file mode 100644
index 665800e..0000000
--- a/src/rider/index.tsx
+++ /dev/null
@@ -1,304 +0,0 @@
-import React from 'react'
-import {View, Text} from '@tarojs/components'
-import {ConfigProvider, Button, Grid, Avatar} from '@nutui/nutui-react-taro'
-import {
- User,
- Shopping,
- Dongdong,
- ArrowRight,
- Purse,
- People,
- Scan
-} from '@nutui/icons-react-taro'
-import {useDealerUser} from '@/hooks/useDealerUser'
-import { useThemeStyles } from '@/hooks/useTheme'
-import {businessGradients, cardGradients, gradientUtils} from '@/styles/gradients'
-import Taro from '@tarojs/taro'
-
-const DealerIndex: React.FC = () => {
- const {
- dealerUser,
- error,
- refresh,
- } = useDealerUser()
-
- // 使用主题样式
- const themeStyles = useThemeStyles()
-
- // 导航到各个功能页面
- const navigateToPage = (url: string) => {
- Taro.navigateTo({url})
- }
-
- // 格式化金额
- const formatMoney = (money?: string) => {
- if (!money) return '0.00'
- return parseFloat(money).toFixed(2)
- }
-
- // 格式化时间
- const formatTime = (time?: string) => {
- if (!time) return '-'
- return new Date(time).toLocaleDateString()
- }
-
- // 获取用户主题
- const userTheme = gradientUtils.getThemeByUserId(dealerUser?.userId)
-
- // 获取渐变背景
- const getGradientBackground = (themeColor?: string) => {
- if (themeColor) {
- const darkerColor = gradientUtils.adjustColorBrightness(themeColor, -30)
- return gradientUtils.createGradient(themeColor, darkerColor)
- }
- return userTheme.background
- }
-
- console.log(getGradientBackground(),'getGradientBackground()')
-
- if (error) {
- return (
-
-
- {error}
-
-
- 重试
-
-
- )
- }
-
- return (
-
-
- {/*头部信息*/}
- {dealerUser && (
-
- {/* 装饰性背景元素 - 小程序兼容版本 */}
-
-
-
-
- }
- className="mr-4"
- style={{
- border: '2px solid rgba(255, 255, 255, 0.3)'
- }}
- />
-
-
- {dealerUser?.realName || '分销商'}
-
-
- ID: {dealerUser.userId}
-
-
-
- 加入时间
-
- {formatTime(dealerUser.createTime)}
-
-
-
-
- )}
-
- {/* 佣金统计卡片 */}
- {dealerUser && (
-
-
- 工资统计
-
-
-
-
- {formatMoney(dealerUser.money)}
-
- 本月配送佣金
-
-
-
- {formatMoney(dealerUser.freezeMoney)}
-
- 桶数
-
-
-
- {formatMoney(dealerUser.totalMoney)}
-
- 累计收入
-
-
-
- )}
-
- {/* 团队统计 */}
- {dealerUser && (
-
-
- 我的邀请
- navigateToPage('/dealer/team/index')}
- >
- 查看详情
-
-
-
-
-
-
- {dealerUser.firstNum || 0}
-
- 一级成员
-
-
-
- {dealerUser.secondNum || 0}
-
- 二级成员
-
-
-
- {dealerUser.thirdNum || 0}
-
- 三级成员
-
-
-
- )}
-
- {/* 功能导航 */}
-
- 配送工具
-
-
- navigateToPage('/rider/orders/index')}>
-
-
-
-
-
-
-
- navigateToPage('/rider/withdraw/index')}>
-
-
-
-
-
-
-
- navigateToPage('/rider/team/index')}>
-
-
-
-
-
-
-
- navigateToPage('/rider/qrcode/index')}>
-
-
-
-
-
-
-
- navigateToPage('/rider/ticket/verification/index?auto=1')}>
-
-
-
-
-
-
-
-
- {/* 第二行功能 */}
- {/**/}
- {/* navigateToPage('/dealer/invite-stats/index')}>*/}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
-
- {/* /!* 预留其他功能位置 *!/*/}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
-
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
-
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/* */}
- {/**/}
-
-
-
-
- {/* 底部安全区域 */}
-
-
- )
-}
-
-export default DealerIndex
diff --git a/src/rider/orders/index.config.ts b/src/rider/orders/index.config.ts
deleted file mode 100644
index 4a1a611..0000000
--- a/src/rider/orders/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default {
- navigationBarTitleText: '送水订单',
- navigationBarTextStyle: 'black'
-}
diff --git a/src/rider/orders/index.tsx b/src/rider/orders/index.tsx
deleted file mode 100644
index 5ad11eb..0000000
--- a/src/rider/orders/index.tsx
+++ /dev/null
@@ -1,610 +0,0 @@
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import Taro, { useDidShow } from '@tarojs/taro'
-import {
- Tabs,
- TabPane,
- Cell,
- Space,
- Button,
- Dialog,
- Radio,
- RadioGroup,
- Image,
- Empty,
- InfiniteLoading,
- PullToRefresh,
- Loading
-} from '@nutui/nutui-react-taro'
-import { View, Text } from '@tarojs/components'
-import dayjs from 'dayjs'
-import { pageGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder'
-import type { GltTicketOrder, GltTicketOrderParam } from '@/api/glt/gltTicketOrder/model'
-import { uploadFile } from '@/api/system/file'
-
-export default function RiderOrders() {
- const PAGE_SIZE = 10
-
- const riderId = useMemo(() => {
- const v = Number(Taro.getStorageSync('UserId'))
- return Number.isFinite(v) && v > 0 ? v : undefined
- }, [])
-
- const pageRef = useRef(1)
- const listRef = useRef([])
- const [tabIndex, setTabIndex] = useState(0)
- const [list, setList] = useState([])
- const [hasMore, setHasMore] = useState(true)
- const [loading, setLoading] = useState(false)
- const [error, setError] = useState(null)
-
- const [deliverDialogVisible, setDeliverDialogVisible] = useState(false)
- const [deliverSubmitting, setDeliverSubmitting] = useState(false)
- const [deliverOrder, setDeliverOrder] = useState(null)
- const [deliverImg, setDeliverImg] = useState(undefined)
-
- type DeliverConfirmMode = 'photoComplete' | 'waitCustomerConfirm'
- const [deliverConfirmMode, setDeliverConfirmMode] = useState('photoComplete')
-
- const riderTabs = useMemo(
- () => [
- { index: 0, title: '全部' },
- { index: 1, title: '待配送', deliveryStatus: 10 },
- { index: 2, title: '配送中', deliveryStatus: 20 },
- { index: 3, title: '待确认', deliveryStatus: 30 },
- { index: 4, title: '已完成', deliveryStatus: 40 }
- ],
- []
- )
-
- const getOrderStatusText = (order: GltTicketOrder) => {
- if (order.status === 1) return '已冻结'
-
- const deliveryStatus = order.deliveryStatus
- if (deliveryStatus === 40) return '已完成'
- if (deliveryStatus === 30) return '待客户确认'
- if (deliveryStatus === 20) return '配送中'
- if (deliveryStatus === 10) return '待配送'
-
- // 兼容:如果后端暂未下发 deliveryStatus,就用时间字段推断
- if (order.receiveConfirmTime) return '已完成'
- if (order.sendEndTime) return '待客户确认'
- if (order.sendStartTime) return '配送中'
- if (order.riderId) return '待配送'
- return '待派单'
- }
-
- const getOrderStatusColor = (order: GltTicketOrder) => {
- const text = getOrderStatusText(order)
- if (text === '已完成') return 'text-green-600'
- if (text === '待客户确认') return 'text-purple-600'
- if (text === '配送中') return 'text-blue-600'
- if (text === '待配送') return 'text-amber-600'
- if (text === '已冻结') return 'text-orange-600'
- return 'text-gray-500'
- }
-
- const canStartDeliver = (order: GltTicketOrder) => {
- if (!order.id) return false
- if (order.status === 1) return false
- if (!riderId || order.riderId !== riderId) return false
- if (order.deliveryStatus && order.deliveryStatus !== 10) return false
- return !order.sendStartTime && !order.sendEndTime
- }
-
- const canConfirmDelivered = (order: GltTicketOrder) => {
- if (!order.id) return false
- if (order.status === 1) return false
- if (!riderId || order.riderId !== riderId) return false
- if (order.receiveConfirmTime) return false
- if (order.deliveryStatus === 40) return false
- if (order.sendEndTime) return false
-
- // 只允许在“配送中”阶段确认送达
- if (typeof order.deliveryStatus === 'number') return order.deliveryStatus === 20
- return !!order.sendStartTime
- }
-
- const canCompleteByPhoto = (order: GltTicketOrder) => {
- if (!order.id) return false
- if (order.status === 1) return false
- if (!riderId || order.riderId !== riderId) return false
- if (order.receiveConfirmTime) return false
- if (order.deliveryStatus === 40) return false
- // 已送达但未完成:允许补传照片并直接完成
- return !!order.sendEndTime
- }
-
- const filterByTab = useCallback(
- (orders: GltTicketOrder[]) => {
- if (tabIndex === 0) return orders
-
- const current = riderTabs.find(t => t.index === tabIndex)
- const status = current?.deliveryStatus
- if (!status) return orders
-
- // 如果后端已实现 deliveryStatus 筛选,这里基本不会再过滤;否则用兼容逻辑兜底。
- return orders.filter(o => {
- const ds = o.deliveryStatus
- if (typeof ds === 'number') return ds === status
- if (status === 10) return !!o.riderId && !o.sendStartTime && !o.sendEndTime
- if (status === 20) return !!o.sendStartTime && !o.sendEndTime
- if (status === 30) return !!o.sendEndTime && !o.receiveConfirmTime
- if (status === 40) return !!o.receiveConfirmTime
- return true
- })
- },
- [riderTabs, tabIndex]
- )
-
- const reload = useCallback(
- async (resetPage = false) => {
- if (!riderId) return
- if (loading) return
- setLoading(true)
- setError(null)
-
- const currentPage = resetPage ? 1 : pageRef.current
- const currentTab = riderTabs.find(t => t.index === tabIndex)
- const params: GltTicketOrderParam = {
- page: currentPage,
- limit: PAGE_SIZE,
- riderId,
- deliveryStatus: currentTab?.deliveryStatus
- }
-
- try {
- const res = await pageGltTicketOrder(params as any)
- const incomingAll = (res?.list || []) as GltTicketOrder[]
-
- // 兼容:后端若暂未实现 riderId 过滤,前端兜底过滤掉非本人的订单
- const incoming = incomingAll.filter(o => o?.deleted !== 1 && o?.riderId === riderId)
-
- const prev = resetPage ? [] : listRef.current
- const next = resetPage ? incoming : prev.concat(incoming)
- listRef.current = next
- setList(next)
-
- const total = typeof res?.count === 'number' ? res.count : undefined
- const filteredOut = incomingAll.length - incoming.length
- if (typeof total === 'number' && filteredOut === 0) {
- setHasMore(next.length < total)
- } else {
- setHasMore(incomingAll.length >= PAGE_SIZE)
- }
-
- pageRef.current = currentPage + 1
- } catch (e) {
- console.error('加载配送订单失败:', e)
- setError('加载失败,请重试')
- setHasMore(false)
- } finally {
- setLoading(false)
- }
- },
- [PAGE_SIZE, loading, riderId, riderTabs, tabIndex]
- )
-
- const reloadMore = useCallback(async () => {
- if (loading || !hasMore) return
- await reload(false)
- }, [hasMore, loading, reload])
-
- const openDeliverDialog = (order: GltTicketOrder, opts?: { mode?: DeliverConfirmMode }) => {
- setDeliverOrder(order)
- setDeliverImg(order.sendEndImg)
- setDeliverConfirmMode(opts?.mode || (order.sendEndImg ? 'photoComplete' : 'waitCustomerConfirm'))
- setDeliverDialogVisible(true)
- }
-
- const handleChooseDeliverImg = async () => {
- try {
- const file = await uploadFile()
- setDeliverImg(file?.url)
- } catch (e) {
- console.error('上传送达照片失败:', e)
- Taro.showToast({ title: '上传失败,请重试', icon: 'none' })
- }
- }
-
- const handleStartDeliver = async (order: GltTicketOrder) => {
- if (!order?.id) return
- if (!canStartDeliver(order)) return
- try {
- await updateGltTicketOrder({
- id: order.id,
- deliveryStatus: 20,
- sendStartTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
- })
- Taro.showToast({ title: '已开始配送', icon: 'success' })
- pageRef.current = 1
- listRef.current = []
- setList([])
- setHasMore(true)
- await reload(true)
- } catch (e) {
- console.error('开始配送失败:', e)
- Taro.showToast({ title: '开始配送失败', icon: 'none' })
- }
- }
-
- const handleConfirmDelivered = async () => {
- if (!deliverOrder?.id) return
- if (deliverSubmitting) return
- if (deliverConfirmMode === 'photoComplete' && !deliverImg) {
- Taro.showToast({ title: '请先拍照/上传送达照片', icon: 'none' })
- return
- }
- setDeliverSubmitting(true)
- try {
- const now = dayjs().format('YYYY-MM-DD HH:mm:ss')
- // 送达时间:首次“确认送达”写入;补传照片时不要覆盖原送达时间
- const deliveredAt = deliverOrder.sendEndTime || now
- // - waitCustomerConfirm:只标记“已送达”,进入待客户确认
- // - photoComplete:拍照留档后可直接完成(是否允许由后端策略决定)
- const payload: GltTicketOrder =
- deliverConfirmMode === 'photoComplete'
- ? {
- id: deliverOrder.id,
- deliveryStatus: 40,
- sendEndTime: deliveredAt,
- sendEndImg: deliverImg,
- receiveConfirmTime: now,
- receiveConfirmType: 20
- }
- : {
- id: deliverOrder.id,
- deliveryStatus: 30,
- sendEndTime: deliveredAt,
- sendEndImg: deliverImg
- }
-
- await updateGltTicketOrder(payload)
-
- Taro.showToast({ title: '已确认送达', icon: 'success' })
- setDeliverDialogVisible(false)
- setDeliverOrder(null)
- setDeliverImg(undefined)
- setDeliverConfirmMode('photoComplete')
- pageRef.current = 1
- listRef.current = []
- setList([])
- setHasMore(true)
- await reload(true)
- } catch (e) {
- console.error('确认送达失败:', e)
- Taro.showToast({ title: '确认送达失败', icon: 'none' })
- } finally {
- setDeliverSubmitting(false)
- }
- }
-
- useEffect(() => {
- listRef.current = list
- }, [list])
-
- useDidShow(() => {
- pageRef.current = 1
- listRef.current = []
- setList([])
- setHasMore(true)
- void reload(true)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- })
-
- useEffect(() => {
- pageRef.current = 1
- listRef.current = []
- setList([])
- setHasMore(true)
- void reload(true)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [tabIndex, riderId])
-
- if (!riderId) {
- return (
-
- 请先登录
-
- )
- }
-
- const displayList = filterByTab(list)
-
- return (
-
-
-
- setTabIndex(Number(paneKey))}
- >
- {riderTabs.map(t => (
-
- ))}
-
-
- {
- pageRef.current = 1
- listRef.current = []
- setList([])
- setHasMore(true)
- await reload(true)
- }}
- headHeight={60}
- >
-
- {error ? (
-
- {error}
- reload(true)}>
- 重新加载
-
-
- ) : (
-
-
- 加载中...
-
- }
- loadMoreText={
- displayList.length === 0 ? (
-
- ) : (
- 没有更多了
- )
- }
- >
- {displayList.map(o => {
- const timeText = o.createTime ? dayjs(o.createTime).format('YYYY-MM-DD HH:mm') : '-'
- const addr = o.address || (o.addressId ? `地址ID:${o.addressId}` : '-')
- const remark = o.buyerRemarks || o.comments || ''
- const qty = Number(o.totalNum || 0)
-
- const flow1Done = !!o.riderId
- const flow2Done = !!o.sendStartTime || (typeof o.deliveryStatus === 'number' && o.deliveryStatus >= 20)
- const flow3Done = !!o.sendEndTime || (typeof o.deliveryStatus === 'number' && o.deliveryStatus >= 30)
- const flow4Done = !!o.receiveConfirmTime || o.deliveryStatus === 40
-
- const phoneToCall = o.phone
- const storePhone = o.storePhone
- const pickupName = o.warehouseName || o.storeName
- const pickupAddr = o.warehouseAddress || o.storeAddress
-
- return (
-
-
-
-
- {o.userTicketId ? `票号#${o.userTicketId}` : '送水订单'}
-
- {getOrderStatusText(o)}
-
-
- 下单时间:{timeText}
-
-
-
- 客户:
- {o.nickname || '-'} {o.phone ? `(${o.phone})` : ''}
-
-
- 收货地址:
- {addr}
-
- {!!remark && (
-
- 买家留言:
- {remark}
-
- )}
-
-
- 数量:
- {Number.isFinite(qty) ? qty : '-'}
- 金额:
- ¥{o.price || '-'}
-
-
-
- 配送时间:
- {o.sendTime ? dayjs(o.sendTime).format('YYYY-MM-DD HH:mm') : '-'}
-
-
-
- 取水点:
- {pickupName || '-'}
-
-
- 取水地址:
- {pickupAddr || '-'}
-
-
- {!!o.sendStartTime && (
-
- 开始配送:
- {dayjs(o.sendStartTime).format('YYYY-MM-DD HH:mm')}
-
- )}
- {!!o.sendEndTime && (
-
- 送达时间:
- {dayjs(o.sendEndTime).format('YYYY-MM-DD HH:mm')}
-
- )}
- {!!o.receiveConfirmTime && (
-
- 完成时间:
- {dayjs(o.receiveConfirmTime).format('YYYY-MM-DD HH:mm')}
-
- )}
-
- {o.sendEndImg ? (
-
- 送达照片:
-
-
-
-
- ) : null}
-
-
- {/* 配送流程 */}
-
- 流程:
- 1 派单
- {'>'}
- 2 配送中
- {'>'}
- 3 送达留档
- {'>'}
- 4 完成
-
-
-
-
- {!!phoneToCall && (
- {
- e.stopPropagation()
- Taro.makePhoneCall({ phoneNumber: phoneToCall })
- }}
- >
- 联系客户
-
- )}
- {!!addr && addr !== '-' && (
- {
- e.stopPropagation()
- void Taro.setClipboardData({ data: addr })
- Taro.showToast({ title: '地址已复制', icon: 'none' })
- }}
- >
- 复制地址
-
- )}
- {!!storePhone && (
- {
- e.stopPropagation()
- Taro.makePhoneCall({ phoneNumber: storePhone })
- }}
- >
- 联系门店
-
- )}
- {canStartDeliver(o) && (
- {
- e.stopPropagation()
- void handleStartDeliver(o)
- }}
- >
- 开始配送
-
- )}
- {canConfirmDelivered(o) && (
- {
- e.stopPropagation()
- openDeliverDialog(o, { mode: 'waitCustomerConfirm' })
- }}
- >
- 确认送达
-
- )}
- {canCompleteByPhoto(o) && (
- {
- e.stopPropagation()
- openDeliverDialog(o, { mode: 'photoComplete' })
- }}
- >
- 补传照片完成
-
- )}
-
-
-
- |
- )
- })}
-
- )}
-
-
-
-
-
-
- )
-}
diff --git a/src/rider/ticket/verification/index.config.ts b/src/rider/ticket/verification/index.config.ts
deleted file mode 100644
index afd29c5..0000000
--- a/src/rider/ticket/verification/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '水票核销'
-})
-
diff --git a/src/rider/ticket/verification/index.tsx b/src/rider/ticket/verification/index.tsx
deleted file mode 100644
index 545e4ac..0000000
--- a/src/rider/ticket/verification/index.tsx
+++ /dev/null
@@ -1,280 +0,0 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react'
-import { View, Text } from '@tarojs/components'
-import Taro, { useDidShow, useRouter } from '@tarojs/taro'
-import { Button, Card, ConfigProvider } from '@nutui/nutui-react-taro'
-import { Scan, Success, Failure, Tips } from '@nutui/icons-react-taro'
-
-import { decryptQrData } from '@/api/shop/shopGift'
-import { getGltUserTicket, updateGltUserTicket } from '@/api/glt/gltUserTicket'
-import type { GltUserTicket } from '@/api/glt/gltUserTicket/model'
-import { isValidJSON } from '@/utils/jsonUtils'
-import { useUser } from '@/hooks/useUser'
-
-type TicketPayload = {
- userTicketId: number
- qty?: number
- userId?: number
- t?: number
-}
-
-type VerifyRecord = {
- id: number
- time: string
- success: boolean
- message: string
- ticketName?: string
- userInfo?: string
- qty?: number
-}
-
-const RiderTicketVerificationPage: React.FC = () => {
- const { hasRole, isAdmin } = useUser()
- const router = useRouter()
- const [loading, setLoading] = useState(false)
- const [lastTicket, setLastTicket] = useState(null)
- const [lastQty, setLastQty] = useState(1)
- const [records, setRecords] = useState([])
-
- const autoScanOnceRef = useRef(false)
-
- const canVerify = useMemo(() => {
- return (
- hasRole('rider') ||
- hasRole('store') ||
- hasRole('staff') ||
- hasRole('admin') ||
- isAdmin()
- )
- }, [hasRole, isAdmin])
-
- const autoScanEnabled = useMemo(() => {
- const p: any = router?.params || {}
- return p.auto === '1' || p.auto === 'true'
- }, [router])
-
- const addRecord = (rec: Omit) => {
- const item: VerifyRecord = {
- id: Date.now(),
- time: new Date().toLocaleString(),
- ...rec
- }
- setRecords(prev => [item, ...prev].slice(0, 10))
- }
-
- const parsePayload = (raw: string): TicketPayload => {
- const trimmed = raw.trim()
- if (!isValidJSON(trimmed)) throw new Error('无效的水票核销信息')
- const payload = JSON.parse(trimmed) as TicketPayload
- const userTicketId = Number(payload.userTicketId)
- const qty = Math.max(1, Number(payload.qty || 1))
- if (!Number.isFinite(userTicketId) || userTicketId <= 0) {
- throw new Error('水票核销信息无效')
- }
- return { ...payload, userTicketId, qty }
- }
-
- const extractPayloadFromScanResult = async (scanResult: string): Promise => {
- const trimmed = scanResult.trim()
-
- // 1) 加密二维码:{ businessType, token, data }
- if (isValidJSON(trimmed)) {
- const json = JSON.parse(trimmed) as any
- if (json?.businessType && json?.token && json?.data) {
- if (json.businessType !== 'ticket') {
- throw new Error('请扫描水票核销码')
- }
- const decrypted = await decryptQrData({
- token: String(json.token),
- encryptedData: String(json.data)
- })
- return parsePayload(String(decrypted || ''))
- }
-
- // 2) 明文 payload(内部调试/非加密二维码)
- if (json?.userTicketId) {
- return parsePayload(trimmed)
- }
- }
-
- throw new Error('无效的水票核销码')
- }
-
- const verifyTicket = async (payload: TicketPayload) => {
- const userTicketId = Number(payload.userTicketId)
- const qty = Math.max(1, Number(payload.qty || 1))
-
- const ticket = await getGltUserTicket(userTicketId)
- if (!ticket) throw new Error('水票不存在')
- if (ticket.status === 1) throw new Error('该水票已冻结')
- const available = Number(ticket.availableQty || 0)
- const used = Number(ticket.usedQty || 0)
- if (available < qty) throw new Error('水票可用次数不足')
-
- const lines: string[] = []
- lines.push(`水票:${ticket.templateName || '水票'}`)
- lines.push(`本次核销:${qty} 次`)
- lines.push(`剩余可用:${available - qty} 次`)
- if (ticket.phone) lines.push(`用户手机号:${ticket.phone}`)
- if (ticket.nickname) lines.push(`用户昵称:${ticket.nickname}`)
-
- const modalRes = await Taro.showModal({
- title: '确认核销',
- content: lines.join('\n')
- })
- if (!modalRes.confirm) return
-
- await updateGltUserTicket({
- ...ticket,
- availableQty: available - qty,
- usedQty: used + qty
- })
-
- setLastTicket({
- ...ticket,
- availableQty: available - qty,
- usedQty: used + qty
- })
- setLastQty(qty)
-
- addRecord({
- success: true,
- message: `核销成功(${qty}次)`,
- ticketName: ticket.templateName || '水票',
- userInfo: [ticket.nickname, ticket.phone].filter(Boolean).join(' / ') || undefined,
- qty
- })
- Taro.showToast({ title: '核销成功', icon: 'success' })
- }
-
- const handleScan = async () => {
- if (loading) return
- if (!canVerify) {
- Taro.showToast({ title: '您没有核销权限', icon: 'none' })
- return
- }
-
- try {
- setLoading(true)
- const res = await Taro.scanCode({})
- const scanResult = res?.result
- if (!scanResult) throw new Error('未识别到二维码内容')
-
- const payload = await extractPayloadFromScanResult(scanResult)
- await verifyTicket(payload)
- } catch (e: any) {
- const msg = e?.message || '核销失败'
- addRecord({ success: false, message: msg })
- Taro.showToast({ title: msg, icon: 'none' })
- } finally {
- setLoading(false)
- }
- }
-
- // If navigated in "auto" mode, open scan on first show when user has permission.
- useDidShow(() => {
- // Reset the flag when user manually re-enters the page via navigation again.
- // (This runs on every show; only the first show with auto enabled will trigger scan.)
- if (!autoScanEnabled) autoScanOnceRef.current = false
- })
-
- useEffect(() => {
- if (!autoScanEnabled) return
- if (autoScanOnceRef.current) return
- if (!canVerify) return
- autoScanOnceRef.current = true
- // Defer to ensure page is fully mounted before opening camera.
- setTimeout(() => {
- handleScan().catch(() => {})
- }, 80)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [autoScanEnabled, canVerify])
-
- return (
-
-
-
-
-
- 水票核销
-
-
- 扫描用户出示的“水票核销码”完成核销
-
-
-
-
-
-
-
- }
- onClick={handleScan}
- >
- 扫码核销
-
-
-
- {lastTicket && (
-
-
- 最近一次核销
- 使用 {lastQty} 次
-
-
-
- {lastTicket.templateName || '水票'}(剩余 {lastTicket.availableQty ?? 0} 次)
-
-
-
- )}
-
-
-
-
- 核销记录
- 仅保留最近10条
-
- {records.length === 0 ? (
-
- 暂无记录
-
- ) : (
-
- {records.map(r => (
-
-
-
- {r.success ? (
-
- ) : (
-
- )}
- {r.message}
-
-
-
- {r.time}
- {r.ticketName ? ` · ${r.ticketName}` : ''}
- {typeof r.qty === 'number' ? ` · ${r.qty}次` : ''}
-
-
- {r.userInfo && (
-
- {r.userInfo}
-
- )}
-
-
- ))}
-
- )}
-
-
-
- )
-}
-
-export default RiderTicketVerificationPage
diff --git a/src/shop/category/components/Banner.tsx b/src/shop/category/components/Banner.tsx
deleted file mode 100644
index 7f3942d..0000000
--- a/src/shop/category/components/Banner.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { useEffect, useState } from 'react'
-import { Swiper } from '@nutui/nutui-react-taro'
-import {CmsAd} from "@/api/cms/cmsAd/model";
-import {Image} from '@nutui/nutui-react-taro'
-import {getCmsAd} from "@/api/cms/cmsAd";
-
-const MyPage = () => {
- const [item, setItem] = useState()
- const reload = () => {
- getCmsAd(439).then(data => {
- setItem(data)
- })
- }
-
- useEffect(() => {
- reload()
- }, [])
-
- return (
- <>
-
- {item?.imageList?.map((item) => (
-
-
-
- ))}
-
- >
- )
-}
-export default MyPage
diff --git a/src/shop/category/components/GoodsList.scss b/src/shop/category/components/GoodsList.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/shop/category/components/GoodsList.tsx b/src/shop/category/components/GoodsList.tsx
deleted file mode 100644
index f84ae9e..0000000
--- a/src/shop/category/components/GoodsList.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import {Image} from '@nutui/nutui-react-taro'
-import {Share} from '@nutui/icons-react-taro'
-import {View, Text} from '@tarojs/components'
-import Taro from '@tarojs/taro'
-import './GoodsList.scss'
-
-
-const GoodsList = (props: any) => {
-
- return (
- <>
-
-
- {props.data?.map((item: any, index: number) => {
- return (
-
- Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
-
-
- {item.name}
-
- {item.comments}
- 已售 {item.sales}
-
-
-
- ¥
- {item.price}
- 会员价
- ¥{item.salePrice}
-
-
-
- Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
-
- Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>购买
-
-
-
-
-
-
- )
- })}
-
-
- >
- )
-}
-export default GoodsList
diff --git a/src/shop/category/index.config.ts b/src/shop/category/index.config.ts
deleted file mode 100644
index 9b73741..0000000
--- a/src/shop/category/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '商品分类',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/shop/category/index.scss b/src/shop/category/index.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/shop/category/index.tsx b/src/shop/category/index.tsx
deleted file mode 100644
index 45cdb88..0000000
--- a/src/shop/category/index.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import Taro from '@tarojs/taro'
-import GoodsList from './components/GoodsList'
-import {useShareAppMessage} from "@tarojs/taro"
-import {Loading} from '@nutui/nutui-react-taro'
-import {useEffect, useState} from "react"
-import {useRouter} from '@tarojs/taro'
-import './index.scss'
-import {pageShopGoods} from "@/api/shop/shopGoods"
-import {ShopGoods} from "@/api/shop/shopGoods/model"
-import {getCmsNavigation} from "@/api/cms/cmsNavigation";
-import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
-
-function Category() {
- const {params} = useRouter();
- const [categoryId, setCategoryId] = useState(0)
- const [loading, setLoading] = useState(true)
- const [nav, setNav] = useState()
- const [list, setList] = useState([])
-
- const reload = async () => {
- // 1.加载远程数据
- const id = Number(params.id)
- const nav = await getCmsNavigation(id)
- const shopGoods = await pageShopGoods({categoryId: id})
-
- // 2.处理业务逻辑
- setCategoryId(id)
- setNav(nav)
- setList(shopGoods?.list || [])
-
- // 3.设置标题
- Taro.setNavigationBarTitle({
- title: `${nav?.categoryName}`
- })
- };
-
- useEffect(() => {
- reload().then(() => {
- setLoading(false)
- })
- }, []);
-
- useShareAppMessage(() => {
- return {
- title: `${nav?.categoryName}_易赊宝`,
- path: `/shop/category/index?id=${categoryId}`,
- success: function () {
- console.log('分享成功');
- },
- fail: function () {
- console.log('分享失败');
- }
- };
- });
-
- if (loading) {
- return (
- 加载中
- )
- }
-
- return (
- <>
-
-
-
- >
- )
-}
-
-export default Category
diff --git a/src/shop/comments/index.config.ts b/src/shop/comments/index.config.ts
deleted file mode 100644
index 6d901e3..0000000
--- a/src/shop/comments/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '全部评论',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/shop/comments/index.tsx b/src/shop/comments/index.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/shop/goodsDetail/index.config.ts b/src/shop/goodsDetail/index.config.ts
deleted file mode 100644
index a59775f..0000000
--- a/src/shop/goodsDetail/index.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '商品详情',
- navigationBarTextStyle: 'black',
- navigationStyle: 'custom'
-})
diff --git a/src/shop/goodsDetail/index.scss b/src/shop/goodsDetail/index.scss
deleted file mode 100644
index aa22055..0000000
--- a/src/shop/goodsDetail/index.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.cart-icon{
- background: linear-gradient(to bottom, #bbe094, #4ee265);
- border-radius: 100px 0 0 100px;
- height: 70px;
-}
-
-/* 去掉 RichText 中图片的间距 */
-rich-text img {
- margin: 0 !important;
- padding: 0 !important;
- display: block;
-}
-
-/* 在全局样式或组件样式文件中 */
-.no-margin {
- margin: 0 !important; /* 使用 !important 来确保覆盖默认样式 */
-}
-
-/* 文本截断样式 */
-.truncate {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- display: inline-block;
-}
\ No newline at end of file
diff --git a/src/shop/goodsDetail/index.tsx b/src/shop/goodsDetail/index.tsx
deleted file mode 100644
index c62b9a4..0000000
--- a/src/shop/goodsDetail/index.tsx
+++ /dev/null
@@ -1,486 +0,0 @@
-import {useEffect, useState} from "react";
-import {Image, Badge, Popup, CellGroup, Cell} from "@nutui/nutui-react-taro";
-import {ArrowLeft, Headphones, Share, Cart, ArrowRight} from "@nutui/icons-react-taro";
-import Taro, {useShareAppMessage} from "@tarojs/taro";
-import {RichText, View, Text} from '@tarojs/components'
-import {ShopGoods} from "@/api/shop/shopGoods/model";
-import {getShopGoods} from "@/api/shop/shopGoods";
-import {listShopGoodsSpec} from "@/api/shop/shopGoodsSpec";
-import {ShopGoodsSpec} from "@/api/shop/shopGoodsSpec/model";
-import {listShopGoodsSku} from "@/api/shop/shopGoodsSku";
-import {ShopGoodsSku} from "@/api/shop/shopGoodsSku/model";
-import {Swiper} from '@nutui/nutui-react-taro'
-import navTo, {wxParse} from "@/utils/common";
-import SpecSelector from "@/components/SpecSelector";
-import "./index.scss";
-import {useCart} from "@/hooks/useCart";
-import {useConfig} from "@/hooks/useConfig";
-import {parseInviteParams, saveInviteParams, trackInviteSource} from "@/utils/invite";
-import { ensureLoggedIn } from '@/utils/auth'
-import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
-import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
-
-const GoodsDetail = () => {
- const [statusBarHeight, setStatusBarHeight] = useState(44);
- const [windowWidth, setWindowWidth] = useState(390)
- const [goods, setGoods] = useState(null);
- const [files, setFiles] = useState([]);
- const [specs, setSpecs] = useState([]);
- const [skus, setSkus] = useState([]);
- const [showSpecSelector, setShowSpecSelector] = useState(false);
- const [specAction, setSpecAction] = useState<'cart' | 'buy'>('cart');
- const [showBottom, setShowBottom] = useState(false)
- const [bottomItem, setBottomItem] = useState({
- title: '',
- content: ''
- })
- // 水票套票模板:存在时该商品不允许加入购物车(购物车无法支付此类商品)
- const [ticketTemplate, setTicketTemplate] = useState(null)
- const [ticketTemplateChecked, setTicketTemplateChecked] = useState(false)
- // const [selectedSku, setSelectedSku] = useState(null);
- const [loading, setLoading] = useState(false);
- const router = Taro.getCurrentInstance().router;
- const goodsId = router?.params?.id;
-
- // 使用购物车Hook
- const {cartCount, addToCart} = useCart()
- const {config} = useConfig()
-
- // 如果从分享链接进入(携带 inviter/source/t),且当前未登录,则暂存邀请信息用于注册后绑定关系
- useEffect(() => {
- try {
- const currentUserId = Taro.getStorageSync('UserId')
- if (currentUserId) return
-
- const inviteParams = parseInviteParams({query: router?.params})
- if (inviteParams?.inviter) {
- saveInviteParams(inviteParams)
- trackInviteSource(inviteParams.source || 'share', parseInt(inviteParams.inviter))
- }
- } catch (e) {
- // 邀请参数解析/存储失败不影响正常浏览商品
- console.error('商品详情页处理邀请参数失败:', e)
- }
- // router 在 Taro 中可能不稳定;这里仅在 goodsId 变化时尝试处理一次即可
- }, [goodsId])
-
- // 处理加入购物车
- const handleAddToCart = async () => {
- if (!goods) return;
-
- // 水票套票商品:不允许加入购物车(购物车无法支付)
- // 优先使用已加载的 ticketTemplate;若尚未加载则补一次查询
- let tpl = ticketTemplate
- let checked = ticketTemplateChecked
- if (!tpl && goods?.goodsId) {
- try {
- tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId))
- setTicketTemplate(tpl)
- setTicketTemplateChecked(true)
- checked = true
- } catch (_e) {
- tpl = null
- setTicketTemplateChecked(true)
- checked = true
- }
- }
- if (!checked || tpl) {
- return
- }
-
- if (!ensureLoggedIn(`/shop/goodsDetail/index?id=${goods.goodsId}`)) return
-
- // 如果有规格,显示规格选择器
- if (specs.length > 0) {
- setSpecAction('cart');
- setShowSpecSelector(true);
- return;
- }
-
- // 没有规格,直接加入购物车
- addToCart({
- goodsId: goods.goodsId!,
- name: goods.name || '',
- price: goods.price || '0',
- image: goods.image || ''
- });
- };
-
- // 处理立即购买
- const handleBuyNow = () => {
- if (!goods) return;
-
- if (!ensureLoggedIn(`/shop/orderConfirm/index?goodsId=${goods.goodsId}`)) return
-
- // 如果有规格,显示规格选择器
- if (specs.length > 0) {
- setSpecAction('buy');
- setShowSpecSelector(true);
- return;
- }
-
- // 没有规格,直接购买
- navTo(`/shop/orderConfirm/index?goodsId=${goods?.goodsId}`, true);
- };
-
- // 规格选择确认回调
- const handleSpecConfirm = async (sku: ShopGoodsSku, quantity: number, action?: 'cart' | 'buy') => {
- // setSelectedSku(sku);
- setShowSpecSelector(false);
-
- if (action === 'cart') {
- // 水票套票商品:不允许加入购物车(购物车无法支付)
- let tpl = ticketTemplate
- let checked = ticketTemplateChecked
- if (!tpl && goods?.goodsId) {
- try {
- tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId))
- setTicketTemplate(tpl)
- setTicketTemplateChecked(true)
- checked = true
- } catch (_e) {
- tpl = null
- setTicketTemplateChecked(true)
- checked = true
- }
- }
- if (!checked || tpl) {
- return
- }
-
- // 加入购物车
- addToCart({
- goodsId: goods!.goodsId!,
- skuId: sku.id,
- name: goods!.name || '',
- price: sku.price || goods!.price || '0',
- image: goods!.image || '',
- specInfo: sku.sku, // sku字段包含规格信息
- }, quantity);
- } else if (action === 'buy') {
- // 立即购买
- const orderData = {
- goodsId: goods!.goodsId!,
- skuId: sku.id,
- quantity,
- price: sku.price || goods!.price || '0'
- };
- navTo(`/shop/orderConfirm/index?orderData=${encodeURIComponent(JSON.stringify(orderData))}`, true);
- } else {
- // 默认情况:如果action未定义,默认为立即购买
- const orderData = {
- goodsId: goods!.goodsId!,
- skuId: sku.id,
- quantity,
- price: sku.price || goods!.price || '0'
- };
- navTo(`/shop/orderConfirm/index?orderData=${encodeURIComponent(JSON.stringify(orderData))}`, true);
- }
- };
-
- const openBottom = (title: string, content: string) => {
- setBottomItem({
- title,
- content
- })
- setShowBottom(true)
- }
-
- useEffect(() => {
- let alive = true
- Taro.getSystemInfo({
- success: (res) => {
- if (!alive) return
- setWindowWidth(res.windowWidth)
- setStatusBarHeight(Number(res.statusBarHeight) + 5)
- },
- });
- if (goodsId) {
- setLoading(true);
- // 切换商品时先重置套票模板,避免复用上一个商品状态
- setTicketTemplate(null)
- setTicketTemplateChecked(false)
-
- // 加载商品详情
- getShopGoods(Number(goodsId))
- .then((res) => {
- // 处理富文本内容,去掉图片间距
- if (res.content) {
- res.content = wxParse(res.content);
- }
- if (!alive) return
- 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(() => {
- if (!alive) return
- setLoading(false);
- });
-
- // 查询商品是否绑定水票模板(失败/无数据时不影响正常浏览)
- getGltTicketTemplateByGoodsId(Number(goodsId))
- .then((tpl) => {
- if (!alive) return
- setTicketTemplate(tpl)
- setTicketTemplateChecked(true)
- })
- .catch((_e) => {
- if (!alive) return
- setTicketTemplate(null)
- setTicketTemplateChecked(true)
- })
-
- // 加载商品规格
- listShopGoodsSpec({goodsId: Number(goodsId)} as any)
- .then((data) => {
- if (!alive) return
- setSpecs(data || []);
- })
- .catch((error) => {
- console.error("Failed to fetch goods specs:", error);
- });
-
- // 加载商品SKU
- listShopGoodsSku({goodsId: Number(goodsId)} as any)
- .then((data) => {
- if (!alive) return
- setSkus(data || []);
- })
- .catch((error) => {
- console.error("Failed to fetch goods skus:", error);
- });
- }
- return () => {
- alive = false
- }
- }, [goodsId]);
-
- // 分享给好友
- useShareAppMessage(() => {
- const inviter = Taro.getStorageSync('UserId')
- const sharePath =
- inviter
- ? `/shop/goodsDetail/index?id=${goodsId}&inviter=${inviter}&source=goods_share&t=${Date.now()}`
- : `/shop/goodsDetail/index?id=${goodsId}`
-
- return {
- title: goods?.name || '精选商品',
- path: sharePath,
- imageUrl: goods?.image ? `${goods.image}?x-oss-process=image/resize,w_500,h_400,m_fill` : undefined, // 分享图片,调整为5:4比例
- 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
- });
- }
- };
- });
-
- if (!goods || loading) {
- return 加载中...;
- }
-
- const showAddToCart = ticketTemplateChecked && !ticketTemplate
-
- return (
-
- Taro.navigateBack()}
- >
-
-
- Taro.switchTab({url: `/pages/cart/cart`})}>
-
-
-
-
-
-
- {
- files.length > 0 && (
-
- {files.map((item) => (
-
-
-
- ))}
-
- )
- }
- {
- files.length == 0 && (
-
- )
- }
-
-
-
- <>
-
-
- ¥
- {goods.price}
- 会员价
- ¥{goods.salePrice}
-
- 已售 {goods.sales}
-
-
-
-
- {goods.name}
-
-
-
- {goods.comments}
-
-
-
-
-
-
- 分享
-
-
-
- >
-
-
-
- {
- config?.deliveryText && (
-
-
- {config?.deliveryText || '14:30下单,明天配送'}
-
- |
- } onClick={() => openBottom('配送', `${config?.deliveryText}`)}/>
-
- {config?.guaranteeText || '支持7天无理由退货'}
-
- |
- } onClick={() => openBottom('保障', `${config?.guaranteeText}`)}/>
-
- )
- }
- {config?.openComments == '1' && (
-
-
- 查看全部
-
- >
- } onClick={() => navTo(`/shop/comments/index`)}/>
- |
- 暂无评价
- |
- |
- )}
-
-
-
-
-
-
- {/*底部弹窗*/}
- {
- setShowBottom(false)
- }}
- lockScroll
- >
-
- {bottomItem.title}
- {bottomItem.content}
-
-
- {/*底部购买按钮*/}
-
-
-
-
- 咨询
-
-
-
- {showAddToCart && (
- handleAddToCart()}>加入购物车
-
- )}
- handleBuyNow()}>立即购买
-
-
-
-
-
- {/* 规格选择器 */}
- {showSpecSelector && (
- setShowSpecSelector(false)}
- />
- )}
-
- );
-};
-
-export default GoodsDetail;
diff --git a/src/shop/orderConfirm/index.config.ts b/src/shop/orderConfirm/index.config.ts
deleted file mode 100644
index 0478775..0000000
--- a/src/shop/orderConfirm/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '订单确认',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/shop/orderConfirm/index.scss b/src/shop/orderConfirm/index.scss
deleted file mode 100644
index 3f80bc9..0000000
--- a/src/shop/orderConfirm/index.scss
+++ /dev/null
@@ -1,116 +0,0 @@
-.order-confirm-page {
- padding-bottom: 100px; // 留出底部固定按钮的空间
-
- .error-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 60px 20px;
- text-align: center;
-
- .error-text {
- color: #999;
- margin-bottom: 20px;
- font-size: 14px;
- }
- }
-
- .fixed-bottom {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 10px 20px;
- background-color: #fff;
- border-top: 1px solid #eee;
- box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
-
- .total-price {
- display: flex;
- align-items: center;
- }
-
- .submit-btn {
- width: 150px;
- }
- }
-}
-.address-bottom-line{
- width: 100%;
- border-radius: 12rpx 12rpx 0 0;
- background: #fff;
- padding: 26rpx 49rpx 0 34rpx;
- position: relative;
- &:before {
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- height: 5px;
- background: repeating-linear-gradient(-45deg, #ff6c6c, #ff6c6c 20%, transparent 0, transparent 25%, #1989fa 0,
- #1989fa 45%, transparent 0, transparent 50%);
- background-size: 120px;
- content: "";
- }
-}
-
-// 优惠券弹窗样式
-.coupon-popup {
- height: 100%;
- display: flex;
- flex-direction: column;
-
- &__header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16px;
- border-bottom: 1px solid #f0f0f0;
- }
-
- &__title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- }
-
- &__content {
- flex: 1;
- overflow-y: auto;
- }
-
- &__loading {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 200px;
- color: #999;
- font-size: 14px;
- }
-
- &__current {
- padding: 16px;
- background: #f8f9fa;
- border-bottom: 1px solid #f0f0f0;
-
- &-title {
- font-size: 28rpx;
- color: #666;
- margin-bottom: 8px;
- }
-
- &-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8rpx 12rpx;
- background: #fff;
- border-radius: 6rpx;
- font-size: 28rpx;
- }
- }
-}
diff --git a/src/shop/orderConfirm/index.tsx b/src/shop/orderConfirm/index.tsx
deleted file mode 100644
index 44b0804..0000000
--- a/src/shop/orderConfirm/index.tsx
+++ /dev/null
@@ -1,1162 +0,0 @@
-import {useEffect, useMemo, useState} from "react";
-import {
- Image,
- Button,
- Cell,
- CellGroup,
- Input,
- Space,
- ActionSheet,
- Popup,
- InputNumber,
- DatePicker,
- ConfigProvider
-} from '@nutui/nutui-react-taro'
-import {Location, ArrowRight} from '@nutui/icons-react-taro'
-import Taro, {useDidShow} from '@tarojs/taro'
-import {ShopGoods} from "@/api/shop/shopGoods/model";
-import {getShopGoods} from "@/api/shop/shopGoods";
-import {View, Text} from '@tarojs/components';
-import {listShopUserAddress} from "@/api/shop/shopUserAddress";
-import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
-import './index.scss'
-import Gap from "@/components/Gap";
-import {selectPayment} from "@/api/system/payment";
-import {Payment} from "@/api/system/payment/model";
-import {PaymentHandler, PaymentType, buildSingleGoodsOrder} from "@/utils/payment";
-import OrderConfirmSkeleton from "@/components/OrderConfirmSkeleton";
-import CouponList from "@/components/CouponList";
-import {CouponCardProps} from "@/components/CouponCard";
-import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
-import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
-import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
-import {
- transformCouponData,
- calculateCouponDiscount,
- isCouponUsable,
- getCouponUnusableReason,
- sortCoupons,
- filterUsableCoupons,
- filterUnusableCoupons
-} from "@/utils/couponUtils";
-import dayjs from 'dayjs'
-import type {ShopStore} from "@/api/shop/shopStore/model";
-import {getShopStore, listShopStore} from "@/api/shop/shopStore";
-import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection";
-import { ensureLoggedIn, isLoggedIn } from '@/utils/auth'
-
-
-const OrderConfirm = () => {
- const [goods, setGoods] = useState(null);
- const [address, setAddress] = useState()
- const [payments, setPayments] = useState([])
- const [payment, setPayment] = useState()
- const [isVisible, setIsVisible] = useState(false)
- const [quantity, setQuantity] = useState(1)
- const [orderRemark, setOrderRemark] = useState('')
- const [loading, setLoading] = useState(true)
- const [error, setError] = useState('')
- const [payLoading, setPayLoading] = useState(false)
- // 配送时间(仅水票套票商品需要)
- // 当日截单时间:超过该时间下单,最早配送日顺延到次日(避免 21:00 下单仍显示“当天配送”)
- const DELIVERY_CUTOFF_HOUR = 21
- const getMinSendDate = () => {
- const now = dayjs()
- const cutoff = now.hour(DELIVERY_CUTOFF_HOUR).minute(0).second(0).millisecond(0)
- const startOfToday = now.startOf('day')
- // >= 截单时间则最早只能选次日
- return now.isSame(cutoff) || now.isAfter(cutoff) ? startOfToday.add(1, 'day') : startOfToday
- }
- const [sendTime, setSendTime] = useState(() => getMinSendDate().toDate())
- const [sendTimePickerVisible, setSendTimePickerVisible] = useState(false)
-
- // 水票套票活动(若存在则按规则限制最小购买量等)
- const [ticketTemplate, setTicketTemplate] = useState(null)
-
- // InputNumber 主题配置
- const customTheme = {
- nutuiInputnumberButtonWidth: '28px',
- nutuiInputnumberButtonHeight: '28px',
- nutuiInputnumberInputWidth: '40px',
- nutuiInputnumberInputHeight: '28px',
- nutuiInputnumberInputBorderRadius: '4px',
- nutuiInputnumberButtonBorderRadius: '4px',
- }
-
- // 优惠券相关状态
- const [selectedCoupon, setSelectedCoupon] = useState(null)
- const [couponVisible, setCouponVisible] = useState(false)
- const [availableCoupons, setAvailableCoupons] = useState([])
- const [couponLoading, setCouponLoading] = useState(false)
-
- // 门店选择:用于在下单页展示当前“已选门店”,并允许用户切换(写入 SelectedStore Storage)
- const [storePopupVisible, setStorePopupVisible] = useState(false)
- const [stores, setStores] = useState([])
- const [storeLoading, setStoreLoading] = useState(false)
- const [selectedStore, setSelectedStore] = useState(getSelectedStoreFromStorage())
-
- const router = Taro.getCurrentInstance().router;
- const goodsId = router?.params?.goodsId;
-
- // 页面级兜底:未登录直接进入下单页时,引导去注册/登录并回跳
- useEffect(() => {
- if (!goodsId) {
- // 也可能是 orderData 模式;这里只做最小兜底
- if (!ensureLoggedIn('/shop/orderConfirm/index')) return
- return
- }
- if (!ensureLoggedIn(`/shop/orderConfirm/index?goodsId=${goodsId}`)) return
- }, [goodsId])
-
- const isTicketTemplateActive =
- !!ticketTemplate &&
- ticketTemplate.enabled !== false &&
- ticketTemplate.status !== 1 &&
- ticketTemplate.deleted !== 1
- const hasTicketTemplate = !!ticketTemplate
-
- // 套票活动最低购买量:优先取模板配置
- const ticketMinBuyQty = (() => {
- const n = Number(ticketTemplate?.minBuyQty)
- return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1
- })()
- const minBuyQty = isTicketTemplateActive ? ticketMinBuyQty : 1
-
- const sendTimeText = useMemo(() => {
- return dayjs(sendTime).format('YYYY-MM-DD')
- }, [sendTime])
-
- const getGiftTicketQty = (buyQty: number) => {
- if (!isTicketTemplateActive) return 0
- const multiplier = Number(ticketTemplate?.giftMultiplier || 0)
- const startSend = Number(ticketTemplate?.startSendQty || 0)
- if (multiplier > 0) return Math.max(0, buyQty) * multiplier
- return Math.max(0, startSend)
- }
-
- const loadStores = async () => {
- if (storeLoading) return
- try {
- setStoreLoading(true)
- const list = await listShopStore()
- setStores((list || []).filter(s => s?.isDelete !== 1))
- } catch (e) {
- console.error('获取门店列表失败:', e)
- setStores([])
- Taro.showToast({title: '获取门店列表失败', icon: 'none'})
- } finally {
- setStoreLoading(false)
- }
- }
-
- // @ts-ignore
- const openStorePopup = async () => {
- setStorePopupVisible(true)
- if (!stores.length) {
- await loadStores()
- }
- }
-
- // 计算商品总价
- const getGoodsTotal = () => {
- if (!goods) return 0
- const price = parseFloat(goods.price || '0')
- // const total = price * quantity
-
- // 🔍 详细日志,用于排查数值精度问题
- // console.log('💵 商品总价计算:', {
- // goodsPrice: goods.price,
- // goodsPriceType: typeof goods.price,
- // parsedPrice: price,
- // quantity: quantity,
- // total: total,
- // totalFixed2: total.toFixed(2),
- // totalString: total.toString()
- // })
-
- return price * quantity
- }
-
- // 计算优惠券折扣
- const getCouponDiscount = () => {
- if (!selectedCoupon || !goods) return 0
- const total = getGoodsTotal()
- return calculateCouponDiscount(selectedCoupon, total)
- }
-
- // 计算实付金额
- const getFinalPrice = () => {
- const total = getGoodsTotal()
- const discount = getCouponDiscount()
- return Math.max(0, total - discount)
- }
-
-
- const handleSelect = (item: any) => {
- setPayment(payments.find(payment => payment.name === item.name))
- setIsVisible(false)
- }
-
- // 处理数量变化
- const handleQuantityChange = (value: string | number) => {
- const fallback = isTicketTemplateActive ? minBuyQty : 1
- const newQuantity = typeof value === 'string' ? parseInt(value, 10) || fallback : value
- const finalQuantity = Math.max(fallback, Math.min(newQuantity, goods?.stock || 999))
- setQuantity(finalQuantity)
-
- // 数量变化时,重新排序优惠券并检查当前选中的优惠券是否还可用
- if (availableCoupons.length > 0) {
- const newTotal = parseFloat(goods?.price || '0') * finalQuantity
- const sortedCoupons = sortCoupons(availableCoupons, newTotal)
- const usableCoupons = filterUsableCoupons(sortedCoupons, newTotal)
- setAvailableCoupons(sortedCoupons)
-
- // 检查当前选中的优惠券是否还可用
- if (selectedCoupon && !isCouponUsable(selectedCoupon, newTotal)) {
- setSelectedCoupon(null)
- Taro.showToast({
- title: '当前优惠券不满足使用条件,已自动取消',
- icon: 'none'
- })
-
- // 🎯 自动推荐新的最优优惠券
- if (usableCoupons.length > 0) {
- const bestCoupon = usableCoupons[0]
- const discount = calculateCouponDiscount(bestCoupon, newTotal)
-
- if (discount > 0) {
- setSelectedCoupon(bestCoupon)
- Taro.showToast({
- title: `已为您重新推荐最优优惠券,可省¥${discount.toFixed(2)}`,
- icon: 'success',
- duration: 3000
- })
- }
- }
- } else if (!selectedCoupon && usableCoupons.length > 0) {
- // 🔔 如果没有选中优惠券但有可用的,推荐最优的
- const bestCoupon = usableCoupons[0]
- const discount = calculateCouponDiscount(bestCoupon, newTotal)
-
- if (discount > 0) {
- setSelectedCoupon(bestCoupon)
- Taro.showToast({
- title: `已为您推荐最优优惠券,可省¥${discount.toFixed(2)}`,
- icon: 'success',
- duration: 3000
- })
- }
- } else if (selectedCoupon && usableCoupons.length > 0) {
- // 🔍 检查是否有更好的优惠券
- const bestCoupon = usableCoupons[0]
- const currentDiscount = calculateCouponDiscount(selectedCoupon, newTotal)
- const bestDiscount = calculateCouponDiscount(bestCoupon, newTotal)
-
- // 如果有更好的优惠券(优惠超过0.01元)
- if (bestDiscount > currentDiscount + 0.01 && bestCoupon.id !== selectedCoupon.id) {
- Taro.showModal({
- title: '发现更优惠的优惠券',
- content: `有更好的优惠券可用,额外节省¥${(bestDiscount - currentDiscount).toFixed(2)},是否更换?`,
- success: (res) => {
- if (res.confirm) {
- setSelectedCoupon(bestCoupon)
- Taro.showToast({
- title: '优惠券已更换',
- icon: 'success'
- })
- }
- }
- })
- }
- }
- }
- }
-
- // 处理优惠券选择
- const handleCouponSelect = (coupon: CouponCardProps) => {
- const total = getGoodsTotal()
-
- // 🔍 详细日志记录,用于排查问题
- console.log('🎫 手动选择优惠券详细信息:', {
- coupon: {
- id: coupon.id,
- title: coupon.title,
- type: coupon.type,
- amount: coupon.amount,
- minAmount: coupon.minAmount,
- status: coupon.status
- },
- orderInfo: {
- goodsPrice: goods?.price,
- quantity: quantity,
- total: total,
- totalFixed: total.toFixed(2)
- },
- validation: {
- isUsable: isCouponUsable(coupon, total),
- discount: calculateCouponDiscount(coupon, total),
- reason: getCouponUnusableReason(coupon, total)
- }
- })
-
- // 检查是否可用
- if (!isCouponUsable(coupon, total)) {
- const reason = getCouponUnusableReason(coupon, total)
-
- // 🚨 记录手动选择失败的详细信息
- console.error('🚨 手动选择优惠券失败:', {
- reason,
- coupon,
- total,
- minAmount: coupon.minAmount,
- comparison: {
- totalVsMinAmount: `${total} < ${coupon.minAmount}`,
- result: total < (coupon.minAmount || 0)
- }
- })
-
- Taro.showToast({
- title: reason || '优惠券不可用',
- icon: 'none'
- })
- return
- }
-
- setSelectedCoupon(coupon)
- setCouponVisible(false)
- Taro.showToast({
- title: '优惠券选择成功',
- icon: 'success'
- })
- }
-
- // 取消选择优惠券
- const handleCouponCancel = () => {
- setSelectedCoupon(null)
- Taro.showToast({
- title: '已取消使用优惠券',
- icon: 'success'
- })
- }
-
- // 加载用户优惠券
- const loadUserCoupons = async (totalOverride?: number) => {
- try {
- setCouponLoading(true)
-
- // 使用新的API获取可用优惠券
- const res = await getMyAvailableCoupons()
-
- if (res && res.length > 0) {
- // 转换数据格式
- const transformedCoupons = res.map(transformCouponData)
-
- // 按优惠金额排序
- const total = totalOverride ?? getGoodsTotal()
- const sortedCoupons = sortCoupons(transformedCoupons, total)
- const usableCoupons = filterUsableCoupons(sortedCoupons, total)
-
- setAvailableCoupons(sortedCoupons)
-
- // 🎯 智能推荐:自动应用最优惠的可用优惠券
- if (usableCoupons.length > 0 && !selectedCoupon) {
- const bestCoupon = usableCoupons[0] // 已经按优惠金额排序,第一个就是最优的
- const discount = calculateCouponDiscount(bestCoupon, total)
-
- // 🔍 详细日志记录自动推荐的信息
- console.log('🤖 自动推荐优惠券详细信息:', {
- coupon: {
- id: bestCoupon.id,
- title: bestCoupon.title,
- type: bestCoupon.type,
- amount: bestCoupon.amount,
- minAmount: bestCoupon.minAmount,
- status: bestCoupon.status
- },
- orderInfo: {
- goodsPrice: goods?.price,
- quantity: quantity,
- total: total,
- totalFixed: total.toFixed(2)
- },
- validation: {
- isUsable: isCouponUsable(bestCoupon, total),
- discount: discount,
- reason: getCouponUnusableReason(bestCoupon, total)
- }
- })
-
- if (discount > 0) {
- setSelectedCoupon(bestCoupon)
-
- // 显示智能推荐提示
- Taro.showToast({
- title: `已为您推荐最优优惠券,可省¥${discount.toFixed(2)}`,
- icon: 'success',
- duration: 3000
- })
- }
- }
-
- // 🔔 优惠券提示:如果有可用优惠券,显示提示
- if (usableCoupons.length > 0) {
- console.log(`发现${usableCoupons.length}张可用优惠券,已为您推荐最优惠券`)
- }
-
- console.log('加载优惠券成功:', {
- originalData: res,
- transformedData: transformedCoupons,
- sortedData: sortedCoupons,
- usableCoupons: usableCoupons,
- recommendedCoupon: usableCoupons[0] || null
- })
- } else {
- setAvailableCoupons([])
- console.log('暂无可用优惠券')
- }
- } catch (error) {
- console.error('加载优惠券失败:', error)
- setAvailableCoupons([])
- Taro.showToast({
- title: '加载优惠券失败',
- icon: 'none'
- })
- } finally {
- setCouponLoading(false)
- }
- }
-
- /**
- * 统一支付入口
- */
- const onPay = async (goods: ShopGoods) => {
- try {
- setPayLoading(true)
-
- // 基础校验
- if (!address) {
- Taro.showToast({
- title: '请选择收货地址',
- icon: 'error'
- })
- return;
- }
-
- if (!payment) {
- Taro.showToast({
- title: '请选择支付方式',
- icon: 'error'
- })
- return;
- }
-
- // 水票套票商品:保存配送时间到 ShopOrder.sendStartTime
- if (hasTicketTemplate && !sendTime) {
- Taro.showToast({ title: '请选择配送时间', icon: 'none' })
- return
- }
- if (hasTicketTemplate) {
- const min = getMinSendDate()
- if (dayjs(sendTime).isBefore(min, 'day')) {
- setSendTime(min.toDate())
- Taro.showToast({
- title: `已过当日${DELIVERY_CUTOFF_HOUR}点截单,最早配送:${min.format('YYYY-MM-DD')}`,
- icon: 'none'
- })
- return
- }
- }
-
- // 水票套票活动:最小购买量校验
- if (isTicketTemplateActive && quantity < minBuyQty) {
- Taro.showToast({
- title: `最低购买量:${minBuyQty}桶`,
- icon: 'none'
- })
- return
- }
-
- // 库存校验
- if (goods.stock !== undefined && quantity > goods.stock) {
- Taro.showToast({
- title: '商品库存不足',
- icon: 'error'
- })
- return;
- }
-
- // 优惠券校验
- if (selectedCoupon) {
- const total = getGoodsTotal()
- if (!isCouponUsable(selectedCoupon, total)) {
- const reason = getCouponUnusableReason(selectedCoupon, total)
- Taro.showToast({
- title: reason || '优惠券不可用',
- icon: 'error'
- })
- return;
- }
- } else {
- // 🔔 支付前最后一次检查:提醒用户是否有可用优惠券
- const total = getGoodsTotal()
- const usableCoupons = filterUsableCoupons(availableCoupons, total)
-
- if (usableCoupons.length > 0) {
- const bestCoupon = usableCoupons[0]
- const discount = calculateCouponDiscount(bestCoupon, total)
-
- if (discount > 0) {
- // 用模态框提醒用户
- const confirmResult = await new Promise((resolve) => {
- Taro.showModal({
- title: '发现可用优惠券',
- content: `您有优惠券可使用,可省¥${discount.toFixed(2)},是否使用?`,
- success: (res) => resolve(res.confirm),
- fail: () => resolve(false)
- })
- })
-
- if (confirmResult) {
- setSelectedCoupon(bestCoupon)
- // 🔄 使用优惠券后需要重新构建订单数据,这里直接递归调用支付函数
- // 但要确保传递最新的优惠券信息
- const currentPaymentType = payment.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT;
- const updatedOrderData = buildSingleGoodsOrder(
- goods.goodsId!,
- quantity,
- address.id,
- {
- comments: goods.name,
- deliveryType: 0,
- buyerRemarks: orderRemark,
- sendStartTime: hasTicketTemplate
- ? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
- : undefined,
- couponId: parseInt(String(bestCoupon.id), 10)
- }
- );
-
- console.log('🎯 使用推荐优惠券的订单数据:', updatedOrderData);
-
- // 执行支付
- await PaymentHandler.pay(updatedOrderData, currentPaymentType);
- return; // 提前返回,避免重复执行支付
- } else {
- // 用户选择不使用优惠券,继续支付
- }
- }
- }
- }
-
- // 构建订单数据
- const orderData = buildSingleGoodsOrder(
- goods.goodsId!,
- quantity,
- address.id,
- {
- comments: '易赊宝',
- deliveryType: 0,
- buyerRemarks: orderRemark,
- sendStartTime: hasTicketTemplate
- ? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
- : undefined,
- // 🔧 确保 couponId 是正确的数字类型,且不传递 undefined
- couponId: selectedCoupon ? parseInt(String(selectedCoupon.id), 10) : undefined
- }
- );
-
- // 根据支付方式选择支付类型
- const paymentType = payment.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT;
-
- // 🔍 支付前的详细信息记录
- console.log('💰 开始支付 - 详细信息:', {
- orderData,
- paymentType,
- selectedCoupon: selectedCoupon ? {
- id: selectedCoupon.id,
- title: selectedCoupon.title,
- type: selectedCoupon.type,
- amount: selectedCoupon.amount,
- minAmount: selectedCoupon.minAmount,
- discount: getCouponDiscount()
- } : null,
- priceCalculation: {
- goodsPrice: goods?.price,
- quantity: quantity,
- goodsTotal: getGoodsTotal(),
- couponDiscount: getCouponDiscount(),
- finalPrice: getFinalPrice()
- },
- couponValidation: selectedCoupon ? {
- isUsable: isCouponUsable(selectedCoupon, getGoodsTotal()),
- reason: getCouponUnusableReason(selectedCoupon, getGoodsTotal())
- } : null
- });
-
- // 执行支付 - 移除这里的成功提示,让PaymentHandler统一处理
- await PaymentHandler.pay(orderData, paymentType);
-
- // ✅ 移除双重成功提示 - PaymentHandler会处理成功提示
- // Taro.showToast({
- // title: '支付成功',
- // icon: 'success'
- // })
- } catch (error: any) {
- const message = String(error?.message || '')
- const isOutOfDeliveryRange =
- message.includes('不在配送范围') ||
- message.includes('配送范围') ||
- message.includes('电子围栏') ||
- message.includes('围栏')
-
- // “配送范围”类错误给出更友好的解释,并提供快捷入口去更换收货地址
- if (isOutOfDeliveryRange) {
- try {
- const res = await Taro.showModal({
- title: '暂不支持配送',
- content: '当前收货地址超出配送范围。您可以更换收货地址后再下单,或联系门店确认配送范围。',
- confirmText: '更换地址',
- cancelText: '我知道了'
- })
- if (res?.confirm) {
- Taro.navigateTo({ url: '/user/address/index' })
- }
- } catch (_e) {
- // ignore
- }
- return
- }
-
- // 兜底:仅在 PaymentHandler 未弹过提示时再提示一次
- if (!error?.handled) {
- Taro.showToast({ title: message || '支付失败,请重试', icon: 'none' })
- }
- } finally {
- setPayLoading(false)
- }
- };
-
- // 统一的数据加载函数
- const loadAllData = async () => {
- // 未登录时不发起接口请求;页面会被登录兜底逻辑引导走注册/登录页
- if (!isLoggedIn()) return
-
- try {
- setLoading(true)
- setError('')
-
- // 分别加载数据,避免类型推断问题
- let goodsRes: ShopGoods | null = null
- if (goodsId) {
- goodsRes = await getShopGoods(Number(goodsId))
- }
-
- const [addressRes, paymentRes] = await Promise.all([
- listShopUserAddress({isDefault: true}),
- selectPayment({})
- ])
-
- // 设置商品信息
- // 查询当前商品是否存在水票套票活动(失败/无数据时不影响正常下单)
- let tpl: GltTicketTemplate | null = null
- if (goodsId) {
- try {
- tpl = await getGltTicketTemplateByGoodsId(Number(goodsId))
- } catch (e) {
- tpl = null
- }
- }
-
- const tplActive =
- !!tpl &&
- tpl.enabled !== false &&
- tpl.status !== 1 &&
- tpl.deleted !== 1
-
- const tplMinBuyQty = (() => {
- const n = Number(tpl?.minBuyQty)
- return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1
- })()
-
- // 设置商品信息(若存在套票模板,则默认 canBuyNumber 使用模板最小购买量)
- if (goodsRes) {
- const patchedGoods: ShopGoods = { ...goodsRes }
- if (tplActive && ((patchedGoods.canBuyNumber ?? 0) === 0)) {
- patchedGoods.canBuyNumber = tplMinBuyQty
- }
- setGoods(patchedGoods)
-
- // 设置默认购买数量:优先使用 canBuyNumber,否则使用 1
- const initQty = (patchedGoods.canBuyNumber ?? 0) > 0 ? (patchedGoods.canBuyNumber as number) : 1
- setQuantity(initQty)
- }
-
- setTicketTemplate(tpl)
-
- // 设置默认收货地址
- if (addressRes && addressRes.length > 0) {
- setAddress(addressRes[0])
- }
-
- // 设置支付方式
- if (paymentRes && paymentRes.length > 0) {
- setPayments(paymentRes.map((d) => ({
- type: d.type,
- name: d.name
- })))
- setPayment(paymentRes[0])
- }
-
- // 加载优惠券:使用“初始数量”对应的总价做推荐,避免默认数量变化导致推荐不准
- if (goodsRes) {
- const initQty = (() => {
- const n = Number(goodsRes?.canBuyNumber)
- if (Number.isFinite(n) && n > 0) return Math.floor(n)
- if (tplActive) return tplMinBuyQty
- return 1
- })()
- const total = parseFloat(goodsRes.price || '0') * initQty
- await loadUserCoupons(total)
- }
- } catch (err) {
- console.error('加载数据失败:', err)
- setError('加载数据失败,请重试')
- } finally {
- setLoading(false)
- }
- }
-
- useDidShow(() => {
- // 返回/切换到该页面时,刷新一下当前已选门店
- if (!isLoggedIn()) return
- setSelectedStore(getSelectedStoreFromStorage())
- loadAllData()
- })
-
- useEffect(() => {
- // 切换商品时重置配送时间,避免沿用上一次选择
- if (!isLoggedIn()) return
- setSendTime(getMinSendDate().toDate())
- setSendTimePickerVisible(false)
- loadAllData()
- }, [goodsId]);
-
- // 重新加载数据
- const handleRetry = () => {
- loadAllData()
- }
-
- // 错误状态
- if (error) {
- return (
-
-
- {error}
- 重新加载
-
-
- )
- }
-
- // 加载状态
- if (loading || !goods) {
- return
- }
-
- return (
-
-
- {
- address && (
- Taro.navigateTo({url: '/user/address/index'})}>
-
-
-
-
- 送至
-
- {address.province} {address.city} {address.region} {address.address}
-
-
-
- {address.name} {address.phone}
-
-
- |
- )
- }
- {!address && (
- Taro.navigateTo({url: '/user/address/index'})}>
-
-
- 添加收货地址
-
- |
- )}
-
-
- {hasTicketTemplate && (
-
-
- {sendTimeText}
-
-
- )}
- onClick={() => {
- // 若页面停留跨过截单时间,打开选择器前再校正一次最早可选日期
- const min = getMinSendDate()
- if (dayjs(sendTime).isBefore(min, 'day')) {
- setSendTime(min.toDate())
- }
- setSendTimePickerVisible(true)
- }}
- />
- |
- )}
-
- {/* */}
- {/* */}
- {/* */}
- {/* 门店*/}
- {/* */}
- {/* )}*/}
- {/* extra={(*/}
- {/* */}
- {/* */}
- {/* {selectedStore?.name || '请选择门店'}*/}
- {/* */}
- {/* */}
- {/* */}
- {/* )}*/}
- {/* onClick={openStorePopup}*/}
- {/* />*/}
- {/* | */}
-
-
-
-
-
-
-
-
- {goods.name}
- {/*80g/袋*/}
-
- ¥{goods.price}
-
-
-
-
- {goods.stock !== undefined && (
-
- 库存 {goods.stock} 件
-
- )}
- {isTicketTemplateActive && (
-
- 最低购买量:{minBuyQty}桶
- 赠送水票:{getGiftTicketQty(quantity)}张
-
- )}
-
-
-
-
- |
-
-
-
-
- {payment?.name}
-
-
- )}
- onClick={() => setIsVisible(true)}
- />
- |
-
-
- ¥{getGoodsTotal().toFixed(2)}}
- />
-
-
- {selectedCoupon ? `-¥${getCouponDiscount().toFixed(2)}` : '暂未使用'}
-
- {(() => {
- const usableCoupons = filterUsableCoupons(availableCoupons, getGoodsTotal())
- if (usableCoupons.length > 0 && !selectedCoupon) {
- return (
-
-
- {usableCoupons.length}张可用
-
-
-
- )
- } else if (usableCoupons.length > 0) {
- return (
-
-
- 已选择
-
-
-
- )
- } else {
- return
- }
- })()
- }
-
- )}
- onClick={() => setCouponVisible(true)}
- />
-
- |
- 已优惠
- ¥{getCouponDiscount().toFixed(2)}
- 实付
- ¥{getFinalPrice().toFixed(2)}
-
- )}/>
- | | | |
-
-
- setOrderRemark(value)}
- maxLength={100}
- />
- )}/>
- |
-
- {ticketTemplate && (
-
-
- 注意事项:
- 最低起送量≥{ticketTemplate.startSendQty}桶;
- 配送范围要在电子围栏内;
- 上楼费暂不收取,收费另行通知。
- |
- )}/>
-
- )}
-
-
- {/* 支付方式选择 */}
- setIsVisible(false)}
- />
-
- {/* 优惠券选择弹窗 */}
- setCouponVisible(false)}
- style={{height: '60vh'}}
- >
-
-
- 选择优惠券
- setCouponVisible(false)}
- >
- 关闭
-
-
-
-
- {couponLoading ? (
-
- 加载优惠券中...
-
- ) : (
- <>
- {selectedCoupon && (
-
- 当前使用
-
- {selectedCoupon.title} -¥{calculateCouponDiscount(selectedCoupon, getGoodsTotal()).toFixed(2)}
- 取消使用
-
-
- )}
-
- {(() => {
- const total = getGoodsTotal()
- const usableCoupons = filterUsableCoupons(availableCoupons, total)
- const unusableCoupons = filterUnusableCoupons(availableCoupons, total)
-
- return (
- <>
-
-
- {unusableCoupons.length > 0 && (
- ({
- ...coupon,
- status: 2 as const
- }))}
- layout="vertical"
- showEmpty={false}
- />
- )}
- >
- )
- })()}
- >
- )}
-
-
-
-
- {/* 门店选择弹窗 */}
- setStorePopupVisible(false)}
- >
-
-
- 选择门店
- setStorePopupVisible(false)}
- >
- 关闭
-
-
-
- {storeLoading ? (
-
- 加载中...
-
- ) : (
-
- {stores.map((s) => {
- const isActive = !!selectedStore?.id && selectedStore.id === s.id
- return (
- {s.name || `门店${s.id}`}}
- description={s.address || ''}
- onClick={async () => {
- let storeToSave: ShopStore = s
- if (s?.id) {
- try {
- const full = await getShopStore(s.id)
- if (full) storeToSave = full
- } catch (_e) {
- // keep base item
- }
- }
- setSelectedStore(storeToSave)
- saveSelectedStoreToStorage(storeToSave)
- setStorePopupVisible(false)
- Taro.showToast({title: '门店已切换', icon: 'success'})
- }}
- />
- )
- })}
- {!stores.length && (
- | 暂无门店数据} />
- )}
- | |
- )}
-
-
-
-
-
- setSendTimePickerVisible(false)}
- onCancel={() => setSendTimePickerVisible(false)}
- onConfirm={(_options, selectedValue) => {
- const [y, m, d] = (selectedValue || []).map(v => Number(v))
- const next = new Date(y, (m || 1) - 1, d || 1, 0, 0, 0)
- setSendTime(next)
- setSendTimePickerVisible(false)
- }}
- />
-
-
-
-
-
- 实付金额:
- ¥{getFinalPrice().toFixed(2)}
-
- {selectedCoupon && (
-
- 已优惠 ¥{getCouponDiscount().toFixed(2)}
-
- )}
-
-
- onPay(goods)}
- >
- {payLoading ? '支付中...' : '立即付款'}
-
-
-
-
-
- );
-};
-
-export default OrderConfirm;
diff --git a/src/shop/orderConfirmCart/index.config.ts b/src/shop/orderConfirmCart/index.config.ts
deleted file mode 100644
index 0478775..0000000
--- a/src/shop/orderConfirmCart/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '订单确认',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/shop/orderConfirmCart/index.scss b/src/shop/orderConfirmCart/index.scss
deleted file mode 100644
index 54ec1b6..0000000
--- a/src/shop/orderConfirmCart/index.scss
+++ /dev/null
@@ -1,44 +0,0 @@
-.order-confirm-page {
- padding-bottom: 100px; // 留出底部固定按钮的空间
-
- .fixed-bottom {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 10px 20px;
- background-color: #fff;
- border-top: 1px solid #eee;
- box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
-
- .total-price {
- display: flex;
- align-items: center;
- }
-
- .submit-btn {
- width: 150px;
- }
- }
-}
-.address-bottom-line{
- width: 100%;
- border-radius: 24rpx 24rpx 0 0;
- background: #fff;
- padding: 26rpx 49rpx 0 34rpx;
- position: relative;
- &:before {
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- height: 5px;
- background: repeating-linear-gradient(-45deg, #ff6c6c, #ff6c6c 20%, transparent 0, transparent 25%, #1989fa 0,
- #1989fa 45%, transparent 0, transparent 50%);
- background-size: 120px;
- content: "";
- }
-}
diff --git a/src/shop/orderConfirmCart/index.tsx b/src/shop/orderConfirmCart/index.tsx
deleted file mode 100644
index 63acb18..0000000
--- a/src/shop/orderConfirmCart/index.tsx
+++ /dev/null
@@ -1,209 +0,0 @@
-import {useEffect, useState} from "react";
-import {Image, Button, Cell, CellGroup, Input, Space} from '@nutui/nutui-react-taro'
-import {Location, ArrowRight} from '@nutui/icons-react-taro'
-import Taro from '@tarojs/taro'
-import {View} from '@tarojs/components';
-import {listShopUserAddress} from "@/api/shop/shopUserAddress";
-import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
-import './index.scss'
-import {useCart, CartItem} from "@/hooks/useCart";
-import Gap from "@/components/Gap";
-import {Payment} from "@/api/system/payment/model";
-import {PaymentHandler, PaymentType, buildCartOrder} from "@/utils/payment";
-import { ensureLoggedIn } from '@/utils/auth'
-
-const OrderConfirm = () => {
- const [address, setAddress] = useState()
- const [payment] = useState()
- const [checkoutItems, setCheckoutItems] = useState([]);
-
- const {
- cartItems,
- removeFromCart
- } = useCart();
-
- const reload = async () => {
- const addressList = await listShopUserAddress({isDefault: true});
- if (addressList.length > 0) {
- setAddress(addressList[0])
- }
- }
-
- // 页面级兜底:防止未登录时进入结算页导致接口报错/仅提示“请先登录”
- useEffect(() => {
- // redirect 到当前结算页,登录成功后返回继续支付
- if (!ensureLoggedIn('/shop/orderConfirmCart/index')) return
- }, [])
-
- // 加载结算商品数据
- const loadCheckoutItems = () => {
- try {
- const checkoutData = Taro.getStorageSync('checkout_items');
- if (checkoutData) {
- const items = JSON.parse(checkoutData) as CartItem[];
- setCheckoutItems(items);
- // 清除临时存储的数据
- Taro.removeStorageSync('checkout_items');
- } else {
- // 如果没有选中商品数据,使用全部购物车商品
- setCheckoutItems(cartItems);
- }
- } catch (error) {
- console.error('加载结算商品失败:', error);
- setCheckoutItems(cartItems);
- }
- };
-
- /**
- * 统一支付入口
- */
- const onPay = async () => {
- if (!ensureLoggedIn('/shop/orderConfirmCart/index')) return
-
- // 基础校验
- if (!address) {
- Taro.showToast({
- title: '请选择收货地址',
- icon: 'error'
- });
- return;
- }
-
- if (!checkoutItems || checkoutItems.length === 0) {
- Taro.showToast({
- title: '没有要结算的商品',
- icon: 'error'
- });
- return;
- }
-
- // 构建订单数据
- const orderData = buildCartOrder(
- checkoutItems.map(item => ({
- goodsId: item.goodsId,
- quantity: item.quantity || 1
- })),
- address.id,
- {
- comments: '购物车下单',
- deliveryType: 0
- }
- );
-
- // 根据支付方式选择支付类型,默认微信支付
- const paymentType = payment?.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT;
-
- // 执行支付
- await PaymentHandler.pay(orderData, paymentType, {
- onSuccess: () => {
- // 支付成功后,从购物车中移除已下单的商品
- checkoutItems.forEach(item => {
- removeFromCart(item.goodsId);
- });
- }
- });
- };
-
- useEffect(() => {
- if (!ensureLoggedIn('/shop/orderConfirmCart/index')) return
-
- reload().then();
- loadCheckoutItems();
- }, [cartItems]);
-
- // 计算总价
- const getTotalPrice = () => {
- return checkoutItems.reduce((total, item) => {
- return total + (parseFloat(item.price) * item.quantity);
- }, 0).toFixed(2);
- };
-
- // 计算商品总数量
- const getTotalQuantity = () => {
- return checkoutItems.reduce((total, item) => total + item.quantity, 0);
- };
-
- return (
-
-
- {
- address && (
- Taro.navigateTo({url: '/user/address/index'})}>
-
-
-
-
- 送至
- {address.province} {address.city} {address.region} {address.address}
-
-
- {address.name} {address.phone}
-
-
- |
- )
- }
- {!address && (
- Taro.navigateTo({url: '/user/address/index'})}>
-
-
- 添加收货地址
-
- |
- )}
-
-
-
- {checkoutItems.map((item) => (
-
-
-
-
- {item.name}
- {/*80g/袋*/}
-
- ¥{item.price}
- x {item.quantity}
-
-
-
- |
- ))}
-
-
-
- {'¥' + getTotalPrice()}}/>
-
- -¥0.00
-
-
- )}/>
- {/* | */}
- |
- )}/>
- | |
-
-
-
-
-
-
- 实付金额:
- ¥{getTotalPrice()}
-
-
- 立即付款
-
-
-
-
- );
-};
-
-export default OrderConfirm;
diff --git a/src/shop/orderDetail/index.config.ts b/src/shop/orderDetail/index.config.ts
deleted file mode 100644
index cb489bb..0000000
--- a/src/shop/orderDetail/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '订单详情',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/shop/orderDetail/index.scss b/src/shop/orderDetail/index.scss
deleted file mode 100644
index 2679e97..0000000
--- a/src/shop/orderDetail/index.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-.order-detail-page {
- padding-bottom: 80px; // 留出底部固定按钮的空间
-
- .nut-cell-group__title {
- padding: 10px 16px;
- color: #999;
- }
-
- .fixed-bottom {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding: 10px 20px;
- background-color: #fff;
- border-top: 1px solid #eee;
- box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
-
- .nut-button {
- margin-left: 10px;
- }
- }
-}
diff --git a/src/shop/orderDetail/index.tsx b/src/shop/orderDetail/index.tsx
deleted file mode 100644
index 96e2128..0000000
--- a/src/shop/orderDetail/index.tsx
+++ /dev/null
@@ -1,282 +0,0 @@
-import {useEffect, useState} from "react";
-import {Cell, CellGroup, Image, Space, Button, Dialog} from '@nutui/nutui-react-taro'
-import Taro from '@tarojs/taro'
-import {View} from '@tarojs/components'
-import {ShopOrder} from "@/api/shop/shopOrder/model";
-import {getShopOrder, updateShopOrder, refundShopOrder} from "@/api/shop/shopOrder";
-import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
-import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
-import dayjs from "dayjs";
-import PaymentCountdown from "@/components/PaymentCountdown";
-import './index.scss'
-
-// 申请退款:支付成功后仅允许在指定时间窗内发起(前端展示层限制,后端仍应校验)
-const isWithinRefundWindow = (payTime?: string, windowMinutes: number = 60): boolean => {
- if (!payTime) return false;
- const raw = String(payTime).trim();
- const t = /^\d+$/.test(raw)
- ? dayjs(Number(raw) < 1e12 ? Number(raw) * 1000 : Number(raw)) // 兼容秒/毫秒时间戳
- : dayjs(raw);
- if (!t.isValid()) return false;
- return dayjs().diff(t, 'minute') <= windowMinutes;
-};
-
-const OrderDetail = () => {
- const [order, setOrder] = useState(null);
- const [orderGoodsList, setOrderGoodsList] = useState([]);
- const [confirmReceiveDialogVisible, setConfirmReceiveDialogVisible] = useState(false)
- const router = Taro.getCurrentInstance().router;
- const orderId = router?.params?.orderId;
-
- // 处理支付超时
- const handlePaymentExpired = async () => {
- if (!order) return;
- if (!order.orderId) return;
-
- try {
- // 自动取消过期订单
- await updateShopOrder({
- // 只传最小字段,避免误取消/误走售后流程
- orderId: order.orderId,
- orderStatus: 2 // 已取消
- });
-
- // 更新本地状态
- setOrder(prev => prev ? {...prev, orderStatus: 2} : null);
-
- Taro.showToast({
- title: '订单已自动取消',
- icon: 'none',
- duration: 2000
- });
- } catch (error) {
- console.error('自动取消订单失败:', error);
- }
- };
-
- // 申请退款
- const handleApplyRefund = async () => {
- if (order) {
- try {
- const confirm = await Taro.showModal({
- title: '申请退款',
- content: '确认要申请退款吗?',
- confirmText: '确认',
- cancelText: '取消'
- })
- if (!confirm?.confirm) return
-
- Taro.showLoading({ title: '提交中...' })
-
- // 退款相关操作使用退款接口:PUT /api/shop/shop-order/refund
- await refundShopOrder({
- orderId: order.orderId,
- refundMoney: order.payPrice || order.totalPrice,
- orderStatus: 7
- })
-
- // 乐观更新本地状态
- setOrder(prev => prev ? { ...prev, orderStatus: 7 } : null)
-
- Taro.showToast({ title: '退款申请已提交', icon: 'success' })
- } catch (error) {
- console.error('申请退款失败:', error);
- Taro.showToast({
- title: '操作失败,请重试',
- icon: 'none'
- });
- } finally {
- try {
- Taro.hideLoading()
- } catch (_e) {
- // ignore
- }
- }
- }
- };
-
- // 确认收货(客户)
- const handleConfirmReceive = async () => {
- if (!order?.orderId) return
- try {
- setConfirmReceiveDialogVisible(false)
- await updateShopOrder({
- orderId: order.orderId,
- deliveryStatus: order.deliveryStatus, // 10未发货 20已发货 30部分发货
- orderStatus: 1 // 已完成
- })
- Taro.showToast({title: '确认收货成功', icon: 'success'})
- setOrder(prev => (prev ? {...prev, orderStatus: 1} : prev))
- } catch (e) {
- console.error('确认收货失败:', e)
- Taro.showToast({title: '确认收货失败', icon: 'none'})
- setConfirmReceiveDialogVisible(true)
- }
- }
-
- const getOrderStatusText = (order: ShopOrder) => {
- // 优先检查订单状态
- if (order.orderStatus === 2) return '已取消';
- if (order.orderStatus === 3) return '取消中';
- if (order.orderStatus === 4) return '退款申请中';
- if (order.orderStatus === 5) return '退款被拒绝';
- if (order.orderStatus === 6) return '退款成功';
- if (order.orderStatus === 7) return '客户端申请退款';
-
- // 检查支付状态 (payStatus为boolean类型)
- if (!order.payStatus) return '待付款';
-
- // 已付款后检查发货状态
- if (order.deliveryStatus === 10) return '待发货';
- if (order.deliveryStatus === 20) {
- // 若订单有配送员,则以配送员送达时间作为“可确认收货”的依据
- if (order.riderId) {
- if (order.sendEndTime && order.orderStatus !== 1) return '待确认收货';
- return '配送中';
- }
- return '待收货';
- }
- if (order.deliveryStatus === 30) return '部分发货';
-
- // 最后检查订单完成状态
- if (order.orderStatus === 1) return '已完成';
- if (order.orderStatus === 0) return '未使用';
-
- return '未知状态';
- };
-
- const getPayTypeText = (payType?: number) => {
- switch (payType) {
- case 0:
- return '余额支付';
- case 1:
- return '微信支付';
- case 102:
- return '微信Native';
- case 2:
- return '会员卡支付';
- case 3:
- return '支付宝';
- case 4:
- return '现金';
- case 5:
- return 'POS机';
- default:
- return '未知支付方式';
- }
- };
-
- useEffect(() => {
- if (orderId) {
- console.log('shop-goods', orderId)
- getShopOrder(Number(orderId)).then(async (res) => {
- setOrder(res);
-
- // 获取订单商品列表
- const goodsRes = await listShopOrderGoods({orderId: Number(orderId)});
- if (goodsRes && goodsRes.length > 0) {
- setOrderGoodsList(goodsRes);
- }
- }).catch(error => {
- console.error("Failed to fetch order detail:", error);
- });
- }
- }, [orderId]);
-
- if (!order) {
- return 加载中... ;
- }
-
- const currentUserId = Number(Taro.getStorageSync('UserId'))
- const isOwner = !!currentUserId && currentUserId === order.userId
- const canConfirmReceive =
- isOwner &&
- order.payStatus &&
- order.orderStatus !== 1 &&
- order.deliveryStatus === 20 &&
- (!order.riderId || !!order.sendEndTime)
-
- return (
-
- {/* 支付倒计时显示 - 详情页实时更新 */}
- {!order.payStatus && order.orderStatus !== 2 && (
-
- )}
-
-
- {orderGoodsList.map((item, index) => (
-
-
-
-
- {item.goodsName}
- {item.spec && 规格:{item.spec} }
- 数量:{item.totalNum}
- ¥{item.price}
-
-
- |
- ))}
-
-
-
-
- |
-
- | |
-
-
-
-
- |
- | | |
-
- {order.payStatus && (
-
-
- |
- | |
- )}
-
-
-
-
- {!order.payStatus && console.log('取消订单')}>取消订单}
- {!order.payStatus && console.log('立即支付')}>立即支付}
- {order.orderStatus === 1 && order.payStatus && isWithinRefundWindow(order.payTime, 60) && (
- 申请退款
- )}
- {canConfirmReceive && (
- setConfirmReceiveDialogVisible(true)}>
- 确认收货
-
- )}
-
-
-
-
-
-
- );
-};
-
-export default OrderDetail;
diff --git a/src/shop/search/components/GoodsItem.scss b/src/shop/search/components/GoodsItem.scss
deleted file mode 100644
index 7d04fe0..0000000
--- a/src/shop/search/components/GoodsItem.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-// 使用与首页相同的样式,主要依赖Tailwind CSS类名
-.buy-btn {
- background: linear-gradient(to right, #1cd98a, #24ca94);
- border-radius: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- position: relative;
- overflow: hidden;
-
- .cart-icon {
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 40px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.1);
- border-radius: 20px 0 0 20px;
- }
-}
-
-.car-no {
- font-weight: 500;
- color: #333;
- line-height: 1.4;
- display: -webkit-box;
- -webkit-box-orient: vertical;
- -webkit-line-clamp: 2;
- overflow: hidden;
-}
diff --git a/src/shop/search/components/GoodsItem.tsx b/src/shop/search/components/GoodsItem.tsx
deleted file mode 100644
index ae7d29b..0000000
--- a/src/shop/search/components/GoodsItem.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { View } from '@tarojs/components'
-import { Image } from '@nutui/nutui-react-taro'
-import { Share } from '@nutui/icons-react-taro'
-import Taro from '@tarojs/taro'
-import { ShopGoods } from '@/api/shop/shopGoods/model'
-import './GoodsItem.scss'
-
-interface GoodsItemProps {
- goods: ShopGoods
-}
-
-const GoodsItem = ({ goods }: GoodsItemProps) => {
- // 跳转到商品详情
- const goToDetail = () => {
- Taro.navigateTo({
- url: `/shop/goodsDetail/index?id=${goods.goodsId}`
- })
- }
-
- return (
-
-
-
-
- {goods.name || goods.goodsName}
-
- {goods.comments || ''}
- 已售 {goods.sales || 0}
-
-
-
- ¥
- {goods.price || '0.00'}
-
-
-
-
-
- 购买
-
-
-
-
-
-
- )
-}
-
-export default GoodsItem
diff --git a/src/shop/search/index.config.ts b/src/shop/search/index.config.ts
deleted file mode 100644
index 1473122..0000000
--- a/src/shop/search/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '商品搜索'
-})
diff --git a/src/shop/search/index.scss b/src/shop/search/index.scss
deleted file mode 100644
index 30a45f4..0000000
--- a/src/shop/search/index.scss
+++ /dev/null
@@ -1,103 +0,0 @@
-.search-page {
- min-height: 100vh;
- background: #f5f5f5;
-
- // 搜索输入框样式
- .search-input-wrapper {
- flex: 1;
- display: flex;
- align-items: center;
- background: #f5f5f5;
- border-radius: 20px;
- padding: 0 12px;
-
- .search-icon {
- color: #999;
- margin-right: 8px;
- }
-
- .search-input {
- flex: 1;
- border: none;
- background: transparent;
- font-size: 14px;
-
- input {
- background: transparent !important;
- }
- }
- }
-
- .search-btn {
- padding: 0 16px;
- height: 36px;
- border-radius: 18px;
- font-size: 14px;
- }
-
- .search-content {
- padding-top: calc(32px + env(safe-area-inset-top));
-
- .search-history {
- background: #fff;
- margin-bottom: 8px;
-
- .history-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16px;
- border-bottom: 1px solid #f5f5f5;
-
- .history-title {
- font-size: 16px;
- font-weight: 500;
- color: #333;
- }
-
- .clear-btn {
- font-size: 14px;
- color: #999;
- cursor: pointer;
- }
- }
-
- .history-list {
- padding: 16px;
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
-
- .history-item {
- padding: 8px 16px;
- background: #f5f5f5;
- border-radius: 16px;
- color: #666;
- cursor: pointer;
-
- &:active {
- background: #e5e5e5;
- }
- }
- }
- }
-
- .search-results {
- .result-header {
- padding: 16px;
- color: #666;
- background: #fff;
- border-bottom: 1px solid #f5f5f5;
- margin-bottom: 8px;
- }
-
- .loading-wrapper {
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 40px 0;
- background: #fff;
- }
- }
- }
-}
diff --git a/src/shop/search/index.tsx b/src/shop/search/index.tsx
deleted file mode 100644
index 407e141..0000000
--- a/src/shop/search/index.tsx
+++ /dev/null
@@ -1,237 +0,0 @@
-import {SetStateAction, useEffect, useState} from 'react'
-import {useRouter} from '@tarojs/taro'
-import Taro from '@tarojs/taro'
-import {View} from '@tarojs/components'
-import {Loading, Empty, InfiniteLoading, Input, Button} from '@nutui/nutui-react-taro'
-import {Search} from '@nutui/icons-react-taro';
-import {ShopGoods} from '@/api/shop/shopGoods/model'
-import {pageShopGoods} from '@/api/shop/shopGoods'
-import GoodsItem from './components/GoodsItem'
-import './index.scss'
-
-const SearchPage = () => {
- const router = useRouter()
- const [keywords, setKeywords] = useState('')
- const [goodsList, setGoodsList] = useState([])
- const [loading, setLoading] = useState(false)
- const [page, setPage] = useState(1)
- const [hasMore, setHasMore] = useState(true)
- const [total, setTotal] = useState(0)
- const [searchHistory, setSearchHistory] = useState([])
-
- // 从路由参数获取搜索关键词
- useEffect(() => {
- const {keywords: routeKeywords} = router.params || {}
- if (routeKeywords) {
- setKeywords(decodeURIComponent(routeKeywords))
- handleSearch(decodeURIComponent(routeKeywords), 1).then()
- }
-
- // 加载搜索历史
- loadSearchHistory()
- }, [])
-
- // 加载搜索历史
- const loadSearchHistory = () => {
- try {
- const history = Taro.getStorageSync('search_history') || []
- setSearchHistory(history)
- } catch (error) {
- console.error('加载搜索历史失败:', error)
- }
- }
-
- // 保存搜索历史
- const saveSearchHistory = (keyword: string) => {
- try {
- let history = Taro.getStorageSync('search_history') || []
- // 去重并添加到开头
- history = history.filter((item: string) => item !== keyword)
- history.unshift(keyword)
- // 只保留最近10条
- history = history.slice(0, 10)
- Taro.setStorageSync('search_history', history)
- setSearchHistory(history)
- } catch (error) {
- console.error('保存搜索历史失败:', error)
- }
- }
-
- const handleKeywords = (keywords: SetStateAction) => {
- setKeywords(keywords)
- handleSearch(typeof keywords === "string" ? keywords : '').then()
- }
-
- // 搜索商品
- const handleSearch = async (searchKeywords: string, pageNum: number = 1) => {
- if (!searchKeywords.trim()) {
- Taro.showToast({
- title: '请输入搜索关键词',
- icon: 'none'
- })
- return
- }
-
- setLoading(true)
-
- try {
- const params = {
- keywords: searchKeywords.trim(),
- page: pageNum,
- size: 10,
- isShow: 1 // 只搜索上架商品
- }
-
- const result = await pageShopGoods(params)
-
- if (pageNum === 1) {
- setGoodsList(result?.list || [])
- setTotal(result?.count || 0)
- // 保存搜索历史
- saveSearchHistory(searchKeywords.trim())
- } else {
- setGoodsList(prev => [...prev, ...(result?.list || [])])
- }
-
- setHasMore((result?.list?.length || 0) >= 10)
- setPage(pageNum)
-
- } catch (error) {
- console.error('搜索失败:', error)
- Taro.showToast({
- title: '搜索失败,请重试',
- icon: 'none'
- })
- } finally {
- setLoading(false)
- }
- }
-
- // 加载更多
- const loadMore = () => {
- if (!loading && hasMore && keywords.trim()) {
- handleSearch(keywords, page + 1).then()
- }
- }
-
- // 点击历史搜索
- const onHistoryClick = (keyword: string) => {
- setKeywords(keyword)
- setPage(1)
- handleSearch(keyword, 1)
- }
-
- // 清空搜索历史
- const clearHistory = () => {
- Taro.showModal({
- title: '提示',
- content: '确定要清空搜索历史吗?',
- success: (res) => {
- if (res.confirm) {
- try {
- Taro.removeStorageSync('search_history')
- setSearchHistory([])
- } catch (error) {
- console.error('清空搜索历史失败:', error)
- }
- }
- }
- })
- }
-
- return (
-
-
-
-
- handleSearch(keywords)}
- style={{padding: '9px 8px'}}
- />
-
- handleSearch(keywords)}>
- 搜索
-
-
-
-
- {/**/}
-
- {/* 搜索内容 */}
-
- {/* 搜索历史 */}
- {!keywords && searchHistory.length > 0 && (
-
-
- 搜索历史
- 清空
-
-
- {searchHistory.map((item, index) => (
- onHistoryClick(item)}
- >
- {item}
-
- ))}
-
-
- )}
-
- {/* 搜索结果 */}
- {keywords && (
-
- {/* 结果统计 */}
-
- 找到 {total} 件相关商品
-
-
- {/* 商品列表 */}
- {loading && page === 1 ? (
-
- 搜索中...
-
- ) : goodsList.length > 0 ? (
-
-
-
- {goodsList.map((item) => (
-
- ))}
-
-
-
- ) : (
-
- )}
-
- )}
-
-
- )
-}
-
-export default SearchPage
diff --git a/src/store/index.config.ts b/src/store/index.config.ts
deleted file mode 100644
index 5ec6c4d..0000000
--- a/src/store/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '门店中心'
-})
diff --git a/src/store/index.scss b/src/store/index.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/store/index.tsx b/src/store/index.tsx
deleted file mode 100644
index 948f192..0000000
--- a/src/store/index.tsx
+++ /dev/null
@@ -1,282 +0,0 @@
-import React, {useCallback, useState} from 'react'
-import {View, Text} from '@tarojs/components'
-import {Avatar, Button, ConfigProvider, Grid} from '@nutui/nutui-react-taro'
-import {Location, Scan, Shop, Shopping, User} from '@nutui/icons-react-taro'
-import Taro, {useDidShow} from '@tarojs/taro'
-import {useThemeStyles} from '@/hooks/useTheme'
-import {useUser} from '@/hooks/useUser'
-import {getSelectedStoreFromStorage} from '@/utils/storeSelection'
-import {listShopStoreUser} from '@/api/shop/shopStoreUser'
-import {getShopStore} from '@/api/shop/shopStore'
-import type {ShopStore as ShopStoreModel} from '@/api/shop/shopStore/model'
-import { goToRegister } from '@/utils/auth'
-
-const StoreIndex: React.FC = () => {
- const themeStyles = useThemeStyles()
- const {isLoggedIn, loading: userLoading, getAvatarUrl, getDisplayName, getRoleName, hasRole} = useUser()
-
- const [boundStoreId, setBoundStoreId] = useState(undefined)
- const [selectedStore, setSelectedStore] = useState(getSelectedStoreFromStorage())
- const [store, setStore] = useState(selectedStore)
- const [loading, setLoading] = useState(false)
- const [error, setError] = useState(null)
-
- const storeId = boundStoreId || selectedStore?.id
-
- const parseStoreCoords = (s: ShopStoreModel): {lng: number; lat: number} | null => {
- const raw = (s.lngAndLat || s.location || '').trim()
- if (!raw) return null
-
- const parts = raw.split(/[,\s]+/).filter(Boolean)
- if (parts.length < 2) return null
-
- const a = parseFloat(parts[0])
- const b = parseFloat(parts[1])
- if (Number.isNaN(a) || Number.isNaN(b)) return null
-
- // 常见格式是 "lng,lat";这里做一个简单兜底
- const looksLikeLngLat = Math.abs(a) <= 180 && Math.abs(b) <= 90
- const looksLikeLatLng = Math.abs(a) <= 90 && Math.abs(b) <= 180
- if (looksLikeLngLat) return {lng: a, lat: b}
- if (looksLikeLatLng) return {lng: b, lat: a}
- return null
- }
-
- const navigateToPage = (url: string) => {
- if (!isLoggedIn) {
- goToRegister({ redirect: '/store/index' })
- return
- }
- Taro.navigateTo({url})
- }
-
- const refresh = useCallback(async () => {
- setError(null)
- setLoading(true)
- try {
- const latestSelectedStore = getSelectedStoreFromStorage()
- setSelectedStore(latestSelectedStore)
-
- const userIdRaw = Number(Taro.getStorageSync('UserId'))
- const userId = Number.isFinite(userIdRaw) && userIdRaw > 0 ? userIdRaw : undefined
-
- let foundStoreId: number | undefined = undefined
- if (userId) {
- // 优先按“店员绑定关系”确定门店归属
- try {
- const list = await listShopStoreUser({userId})
- const first = (list || []).find(i => i?.isDelete !== 1 && i?.storeId)
- foundStoreId = first?.storeId
- setBoundStoreId(foundStoreId)
- } catch {
- // fallback to SelectedStore
- foundStoreId = undefined
- setBoundStoreId(undefined)
- }
- } else {
- foundStoreId = undefined
- setBoundStoreId(undefined)
- }
-
- const nextStoreId = (foundStoreId || latestSelectedStore?.id)
- if (!nextStoreId) {
- setStore(latestSelectedStore)
- return
- }
-
- // 获取门店详情(用于展示门店名称/地址/仓库等)
- const full = await getShopStore(nextStoreId)
- setStore(full || (latestSelectedStore?.id === nextStoreId ? latestSelectedStore : ({id: nextStoreId} as ShopStoreModel)))
- } catch (e: any) {
- const msg = e?.message || '获取门店信息失败'
- setError(msg)
- } finally {
- setLoading(false)
- }
- }, [])
-
- // 返回/切换到该页面时,同步最新的已选门店与绑定门店
- useDidShow(() => {
- refresh().catch(() => {})
- })
-
- const openStoreLocation = () => {
- if (!store?.id) {
- return Taro.showToast({title: '请先选择门店', icon: 'none'})
- }
- const coords = parseStoreCoords(store)
- if (!coords) {
- return Taro.showToast({title: '门店未配置定位', icon: 'none'})
- }
- Taro.openLocation({
- latitude: coords.lat,
- longitude: coords.lng,
- name: store.name || '门店',
- address: store.address || ''
- })
- }
-
- if (!isLoggedIn && !userLoading) {
- return (
-
-
- 请先登录后再进入门店中心
-
- goToRegister({ redirect: '/store/index' })}>
- 去注册/登录
-
-
-
-
- )
- }
-
- return (
-
- {/* 头部信息 */}
-
-
-
-
-
-
- }
- className="mr-4"
- style={{border: '2px solid rgba(255, 255, 255, 0.3)'}}
- />
-
-
- {getDisplayName()}
-
-
- {hasRole('store') ? '门店' : hasRole('rider') ? '配送员' : getRoleName()}
-
-
-
- 刷新
-
-
-
-
- {/* 门店信息 */}
-
-
- 当前门店
- Taro.switchTab({url: '/pages/index/index'})}
- >
- 切换门店
-
-
-
- {!storeId ? (
-
-
- 未选择门店,请先去首页选择门店。
-
-
- Taro.switchTab({url: '/pages/index/index'})}>
- 去首页选择门店
-
-
-
- ) : (
-
-
- {store?.name || `门店ID: ${storeId}`}
-
- {!!store?.address && (
-
- {store.address}
-
- )}
- {!!store?.warehouseName && (
-
- 默认仓库:{store.warehouseName}
-
- )}
- {!!error && (
-
- {error}
-
- )}
-
- )}
-
-
- {/* 功能入口 */}
-
- 门店工具
-
-
- navigateToPage('/store/orders/index')}>
-
-
-
-
-
-
-
- navigateToPage('/user/store/verification')}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Taro.switchTab({url: '/pages/index/index'})}>
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-export default StoreIndex
diff --git a/src/store/orders/index.config.ts b/src/store/orders/index.config.ts
deleted file mode 100644
index 4de19d9..0000000
--- a/src/store/orders/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default {
- navigationBarTitleText: '门店订单',
- navigationBarTextStyle: 'black'
-}
diff --git a/src/store/orders/index.tsx b/src/store/orders/index.tsx
deleted file mode 100644
index bcd5e8f..0000000
--- a/src/store/orders/index.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import {useEffect, useMemo, useState} from 'react'
-import Taro from '@tarojs/taro'
-import {Button} from '@nutui/nutui-react-taro'
-import {View, Text} from '@tarojs/components'
-import OrderList from '@/user/order/components/OrderList'
-import {getSelectedStoreFromStorage} from '@/utils/storeSelection'
-import {listShopStoreUser} from '@/api/shop/shopStoreUser'
-
-export default function StoreOrders() {
- const [boundStoreId, setBoundStoreId] = useState(undefined)
-
- const isLoggedIn = useMemo(() => {
- return !!Taro.getStorageSync('access_token') && !!Taro.getStorageSync('UserId')
- }, [])
-
- const selectedStore = useMemo(() => getSelectedStoreFromStorage(), [])
- const storeId = boundStoreId || selectedStore?.id
-
- useEffect(() => {
- }, [])
-
- useEffect(() => {
- // 优先按“店员绑定关系”确定门店归属:门店看到的是自己的订单
- const userId = Number(Taro.getStorageSync('UserId'))
- if (!Number.isFinite(userId) || userId <= 0) return
- listShopStoreUser({userId}).then(list => {
- const first = (list || []).find(i => i?.isDelete !== 1 && i?.storeId)
- if (first?.storeId) setBoundStoreId(first.storeId)
- }).catch(() => {
- // fallback to SelectedStore
- })
- }, [])
-
- if (!isLoggedIn) {
- return (
-
-
- 请先登录
-
- Taro.navigateTo({url: '/passport/login'})}>
- 去登录
-
-
-
-
- )
- }
-
- return (
-
-
-
-
- 当前门店:
-
- {boundStoreId
- ? (selectedStore?.id === boundStoreId ? (selectedStore?.name || `门店ID: ${boundStoreId}`) : `门店ID: ${boundStoreId}`)
- : (selectedStore?.name || '未选择门店')}
-
-
-
- {!storeId ? (
-
-
- 请先在首页左上角选择门店,再查看门店订单。
-
-
- Taro.switchTab({url: '/pages/index/index'})}
- >
- 去首页选择门店
-
-
-
- ) : (
-
- )}
-
-
- )
-}
diff --git a/src/user/address/add.config.ts b/src/user/address/add.config.ts
deleted file mode 100644
index 5d97955..0000000
--- a/src/user/address/add.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '新增收货地址',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/address/add.scss b/src/user/address/add.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/user/address/add.tsx b/src/user/address/add.tsx
deleted file mode 100644
index 99c9420..0000000
--- a/src/user/address/add.tsx
+++ /dev/null
@@ -1,646 +0,0 @@
-import {useEffect, useState, useRef} from "react";
-import {useRouter} from '@tarojs/taro'
-import {Button, Loading, CellGroup, Cell, Input, TextArea, Form} from '@nutui/nutui-react-taro'
-import {Scan, ArrowRight} from '@nutui/icons-react-taro'
-import Taro from '@tarojs/taro'
-import {View} from '@tarojs/components'
-import {Address} from '@nutui/nutui-react-taro'
-import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
-import {getShopUserAddress, listShopUserAddress, updateShopUserAddress, addShopUserAddress} from "@/api/shop/shopUserAddress";
-import RegionData from '@/api/json/regions-data.json';
-import FixedButton from "@/components/FixedButton";
-import { parseLngLatFromText } from "@/utils/geofence";
-
-type SelectedLocation = { lng: string; lat: string; name?: string; address?: string }
-
-const isLocationDenied = (e: any) => {
- const msg = String(e?.errMsg || e?.message || e || '')
- return (
- msg.includes('auth deny') ||
- msg.includes('authorize') ||
- msg.includes('permission') ||
- msg.includes('denied') ||
- msg.includes('scope.userLocation')
- )
-}
-
-const isUserCancel = (e: any) => {
- const msg = String(e?.errMsg || e?.message || e || '')
- return msg.includes('cancel')
-}
-
-const hasValidLngLat = (addr?: Partial | null) => {
- if (!addr) return false
- const p = parseLngLatFromText(`${(addr as any)?.lng ?? ''},${(addr as any)?.lat ?? ''}`)
- if (!p) return false
- // Treat "0,0" as missing in this app (typically used as placeholder by backends).
- if (p.lng === 0 && p.lat === 0) return false
- return true
-}
-
-const AddUserAddress = () => {
- const {params} = useRouter();
- const [loading, setLoading] = useState(true)
- const [text, setText] = useState('')
- const [optionsDemo1, setOptionsDemo1] = useState([])
- const [visible, setVisible] = useState(false)
- const [FormData, setFormData] = useState({})
- const [inputText, setInputText] = useState('')
- const [selectedLocation, setSelectedLocation] = useState(null)
- const formRef = useRef(null)
- const wxDraftRef = useRef | null>(null)
- const wxDraftPatchedRef = useRef(false)
-
- // 判断是编辑还是新增模式
- const isEditMode = !!params.id
- const addressId = params.id ? Number(params.id) : undefined
- const fromWx = params.fromWx === '1' || params.fromWx === 'true'
- const skipDefaultCheck =
- fromWx || params.skipDefaultCheck === '1' || params.skipDefaultCheck === 'true'
-
- const reload = async () => {
- // 整理地区数据
- setRegionData()
-
- // 新增模式:若存在“默认地址”但缺少经纬度,则强制引导到编辑页完善定位
- // 目的:确保业务依赖默认地址坐标(选店/配送范围)时不会因为缺失坐标而失败
- if (!isEditMode && !skipDefaultCheck) {
- try {
- const defaultList = await listShopUserAddress({ isDefault: true })
- const defaultAddr = defaultList?.[0]
- if (defaultAddr && !hasValidLngLat(defaultAddr)) {
- await Taro.showModal({
- title: '需要完善定位',
- content: '默认收货地址缺少定位信息,请先进入编辑页面选择定位并保存后再继续。',
- confirmText: '去完善',
- showCancel: false
- })
- if (defaultAddr.id) {
- Taro.navigateTo({ url: `/user/address/add?id=${defaultAddr.id}` })
- } else {
- Taro.navigateTo({ url: '/user/address/index' })
- }
- return
- }
- } catch (_e) {
- // ignore: 新增页不阻塞渲染
- }
- }
-
- // 微信地址导入:先用微信返回的字段预填表单,让用户手动选择定位后再保存
- if (!isEditMode && fromWx && !wxDraftPatchedRef.current) {
- try {
- const draft = Taro.getStorageSync('WxAddressDraft')
- if (draft) {
- wxDraftPatchedRef.current = true
- wxDraftRef.current = draft as any
- Taro.removeStorageSync('WxAddressDraft')
-
- setFormData(prev => ({
- ...prev,
- ...(draft as any)
- }))
-
- const p = String((draft as any)?.province || '').trim()
- const c = String((draft as any)?.city || '').trim()
- const r = String((draft as any)?.region || '').trim()
- const regionText = [p, c, r].filter(Boolean).join(' ')
- if (regionText) setText(regionText)
- }
- } catch (_e) {
- // ignore
- }
- }
-
- // 如果是编辑模式,加载地址数据
- if (isEditMode && addressId) {
- try {
- const address = await getShopUserAddress(addressId)
- setFormData(address)
- // 设置所在地区
- setText(`${address.province} ${address.city} ${address.region}`)
- // 回显已保存的经纬度(编辑模式)
- if (hasValidLngLat(address)) setSelectedLocation({ lng: String(address.lng), lat: String(address.lat) })
- } catch (error) {
- console.error('加载地址失败:', error)
- Taro.showToast({
- title: '加载地址失败',
- icon: 'error'
- });
- }
- }
- }
-
- /**
- * 处理地区数据
- */
- function setRegionData() {
- // @ts-ignore
- setOptionsDemo1(RegionData?.map((a) => {
- return {
- value: a.label,
- text: a.label,
- children: a.children?.map((b) => {
- return {
- value: b.label,
- text: b.label,
- children: b.children?.map((c) => {
- return {
- value: c.label,
- text: c.label
- }
- })
- }
- })
- }
- }))
- }
-
- /**
- * 地址识别功能
- */
- const recognizeAddress = () => {
- if (!inputText.trim()) {
- Taro.showToast({
- title: '请输入要识别的文本',
- icon: 'none'
- });
- return;
- }
-
- try {
- const result = parseAddressText(inputText);
-
- // 更新表单数据
- const newFormData = {
- ...FormData,
- name: result.name || FormData.name,
- phone: result.phone || FormData.phone,
- address: result.address || FormData.address,
- province: result.province || FormData.province,
- city: result.city || FormData.city,
- region: result.region || FormData.region
- };
-
- setFormData(newFormData);
-
- // 更新地区显示文本
- if (result.province && result.city && result.region) {
- setText(`${result.province} ${result.city} ${result.region}`);
- }
-
- // 更新表单字段值
- if (formRef.current) {
- formRef.current.setFieldsValue(newFormData);
- }
-
- Taro.showToast({
- title: '识别成功',
- icon: 'success'
- });
-
- // 清空输入框
- setInputText('');
-
- } catch (error) {
- Taro.showToast({
- title: '识别失败,请检查文本格式',
- icon: 'none'
- });
- }
- };
-
- /**
- * 解析地址文本
- */
- const parseAddressText = (text: string) => {
- const result: any = {};
-
- // 手机号正则 (11位数字)
- const phoneRegex = /1[3-9]\d{9}/;
- const phoneMatch = text.match(phoneRegex);
- if (phoneMatch) {
- result.phone = phoneMatch[0];
- }
-
- // 姓名正则 (2-4个中文字符,通常在开头)
- const nameRegex = /^[\u4e00-\u9fa5]{2,4}/;
- const nameMatch = text.match(nameRegex);
- if (nameMatch) {
- result.name = nameMatch[0];
- }
-
- // 省市区识别
- const regionResult = parseRegion(text);
- if (regionResult) {
- result.province = regionResult.province;
- result.city = regionResult.city;
- result.region = regionResult.region;
- }
-
- // 详细地址提取 (去除姓名、手机号、省市区后的剩余部分)
- let addressText = text;
- if (result.name) {
- addressText = addressText.replace(result.name, '');
- }
- if (result.phone) {
- addressText = addressText.replace(result.phone, '');
- }
- if (result.province) {
- addressText = addressText.replace(result.province, '');
- }
- if (result.city) {
- addressText = addressText.replace(result.city, '');
- }
- if (result.region) {
- addressText = addressText.replace(result.region, '');
- }
-
- // 清理地址文本
- result.address = addressText.replace(/[,,。\s]+/g, '').trim();
-
- return result;
- };
-
- /**
- * 解析省市区
- */
- const parseRegion = (text: string) => {
- // @ts-ignore
- for (const province of RegionData) {
- if (text.includes(province.label)) {
- const result: any = { province: province.label };
-
- // 查找城市
- if (province.children) {
- for (const city of province.children) {
- if (text.includes(city.label)) {
- result.city = city.label;
-
- // 查找区县
- if (city.children) {
- for (const region of city.children) {
- if (text.includes(region.label)) {
- result.region = region.label;
- return result;
- }
- }
- }
- return result;
- }
- }
- }
- return result;
- }
- }
- return null;
- };
-
- // 选择定位:打开地图让用户选点,保存经纬度到表单数据
- const chooseGeoLocation = async () => {
- const applyChosenLocation = (res: any) => {
- if (!res) return
- if (res.latitude === undefined || res.longitude === undefined) {
- Taro.showToast({ title: '定位信息获取失败', icon: 'none' })
- return
- }
-
- const next: SelectedLocation = {
- lng: String(res.longitude),
- lat: String(res.latitude),
- name: res.name,
- address: res.address
- }
- setSelectedLocation(next)
-
- // 尝试从地图返回的 address 文本解析省市区(best-effort)
- const regionResult = res?.provinceName || res?.cityName || res?.adName
- ? {
- province: String(res.provinceName || ''),
- city: String(res.cityName || ''),
- region: String(res.adName || '')
- }
- : parseRegion(String(res.address || ''))
-
- // 将地图选点的地址同步到“收货地址”(不额外拼接省市区字段,省市区由独立字段保存)
- const nextDetailAddress = (() => {
- const rawAddr = String(res.address || '').trim()
- const name = String(res.name || '').trim()
-
- const province = String(regionResult?.province || '').trim()
- const city = String(regionResult?.city || '').trim()
- const region = String(regionResult?.region || '').trim()
-
- // 选择定位返回的 address 往往包含省市区,这里尽量剥离掉,避免和表单的省市区字段重复
- let detail = rawAddr
- for (const part of [province, city, region]) {
- if (part) detail = detail.replace(part, '')
- }
- detail = detail.replace(/[,,]+/g, ' ').replace(/\s+/g, ' ').trim()
-
- const base = detail || rawAddr
- if (!base && !name) return ''
- if (!base) return name
- if (!name) return base
- return base.includes(name) ? base : `${base} ${name}`
- })()
-
- setFormData(prev => ({
- ...prev,
- lng: next.lng,
- lat: next.lat,
- address: nextDetailAddress || prev.address,
- province: regionResult?.province || prev.province,
- city: regionResult?.city || prev.city,
- region: regionResult?.region || prev.region
- }))
-
- if (regionResult?.province && regionResult?.city && regionResult?.region) {
- setText(`${regionResult.province} ${regionResult.city} ${regionResult.region}`)
- }
-
- // 更新表单展示值(Form initialValues 不会跟随 FormData 变化)
- if (formRef.current) {
- const patch: any = {}
- if (nextDetailAddress) patch.address = nextDetailAddress
- if (regionResult?.region) patch.region = regionResult.region
- formRef.current.setFieldsValue(patch)
- }
- }
-
- try {
- const initLat = selectedLocation?.lat ? Number(selectedLocation.lat) : undefined
- const initLng = selectedLocation?.lng ? Number(selectedLocation.lng) : undefined
- const latitude = typeof initLat === 'number' && Number.isFinite(initLat) ? initLat : undefined
- const longitude = typeof initLng === 'number' && Number.isFinite(initLng) ? initLng : undefined
- const res = await Taro.chooseLocation({
- latitude,
- longitude
- })
- applyChosenLocation(res)
- } catch (e: any) {
- console.warn('选择定位失败:', e)
- if (isUserCancel(e)) return
- if (isLocationDenied(e)) {
- try {
- const modal = await Taro.showModal({
- title: '需要定位权限',
- content: '选择定位需要开启定位权限,请在设置中开启后重试。',
- confirmText: '去设置'
- })
- if (modal.confirm) {
- await Taro.openSetting()
- // 权限可能刚被开启:重试一次
- const res = await Taro.chooseLocation({})
- applyChosenLocation(res)
- }
- } catch (_e) {
- // ignore
- }
- return
- }
- try {
- await Taro.showToast({ title: '打开地图失败,请重试', icon: 'none' })
- } catch (_e) {
- // ignore
- }
- }
- }
-
- // 提交表单
- const submitSucceed = async (values: any) => {
- const loc =
- selectedLocation ||
- (hasValidLngLat(FormData) ? { lng: String(FormData.lng), lat: String(FormData.lat) } : null)
- if (!loc) {
- Taro.showToast({ title: '请选择定位', icon: 'none' })
- return
- }
-
- try {
- // 准备提交的数据
- const submitData = {
- ...values,
- country: FormData.country,
- province: FormData.province,
- city: FormData.city,
- region: FormData.region,
- lng: loc.lng,
- lat: loc.lat,
- isDefault: true // 新增或编辑的地址都设为默认地址
- };
-
- // 如果是编辑模式,添加id
- if (isEditMode && addressId) {
- submitData.id = addressId;
- }
-
- // 先处理默认地址逻辑
- const defaultAddress = await listShopUserAddress({isDefault: true});
- if (defaultAddress && defaultAddress.length > 0) {
- // 如果当前编辑的不是默认地址,或者是新增地址,需要取消其他默认地址
- if (!isEditMode || (isEditMode && defaultAddress[0].id !== addressId)) {
- await updateShopUserAddress({
- ...defaultAddress[0],
- isDefault: false
- });
- }
- }
-
- // 执行新增或更新操作
- if (isEditMode) {
- await updateShopUserAddress(submitData);
- } else {
- await addShopUserAddress(submitData);
- }
-
- Taro.showToast({
- title: `${isEditMode ? '更新' : '保存'}成功`,
- icon: 'success'
- });
-
- setTimeout(() => {
- Taro.navigateBack();
- }, 1000);
-
- } catch (error) {
- console.error('保存失败:', error);
- Taro.showToast({
- title: `${isEditMode ? '更新' : '保存'}失败`,
- icon: 'error'
- });
- }
- }
-
- const submitFailed = (error: any) => {
- console.log(error, 'err...')
- }
-
- useEffect(() => {
- // 动态设置页面标题
- Taro.setNavigationBarTitle({
- title: isEditMode ? '编辑收货地址' : (fromWx ? '完善收货地址' : '新增收货地址')
- });
-
- reload().then(() => {
- setLoading(false)
- })
- }, [fromWx, isEditMode]);
-
- // NutUI Form 的 initialValues 在首次渲染后不再响应更新;微信导入时做一次 setFieldsValue 兜底回填。
- useEffect(() => {
- if (loading) return
- if (isEditMode) return
- const draft = wxDraftRef.current
- if (!draft) return
- if (!formRef.current?.setFieldsValue) return
- try {
- formRef.current.setFieldsValue({
- name: (draft as any)?.name,
- phone: (draft as any)?.phone,
- address: (draft as any)?.address,
- region: (draft as any)?.region
- })
- } catch (_e) {
- // ignore
- } finally {
- wxDraftRef.current = null
- }
- }, [fromWx, isEditMode, loading])
-
- if (loading) {
- return 加载中
- }
-
- return (
- <>
-
-
- {
- setFormData({
- ...FormData,
- province: `${value[0]}`,
- city: `${value[1]}`,
- region: `${value[2]}`
- })
- setText(value.join(' '))
- }}
- onClose={() => setVisible(false)}
- />
-
- {/* 底部浮动按钮 */}
- {
- // 触发表单提交
- if (formRef.current) {
- formRef.current.submit();
- }
- }}
- />
- >
- );
-};
-
-export default AddUserAddress;
diff --git a/src/user/address/index.config.ts b/src/user/address/index.config.ts
deleted file mode 100644
index 672b958..0000000
--- a/src/user/address/index.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '配送管理',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/address/index.scss b/src/user/address/index.scss
deleted file mode 100644
index 30e4e77..0000000
--- a/src/user/address/index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-:root {
-
-}
diff --git a/src/user/address/index.tsx b/src/user/address/index.tsx
deleted file mode 100644
index 52c6f34..0000000
--- a/src/user/address/index.tsx
+++ /dev/null
@@ -1,219 +0,0 @@
-import {useState} from "react";
-import Taro, {useDidShow} from '@tarojs/taro'
-import {Button, Cell, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
-import {CheckNormal, Checked} from '@nutui/icons-react-taro'
-import {View} from '@tarojs/components'
-import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
-import {listShopUserAddress, removeShopUserAddress, updateShopUserAddress} from "@/api/shop/shopUserAddress";
-import FixedButton from "@/components/FixedButton";
-import dayjs from "dayjs";
-
-const Address = () => {
- const [list, setList] = useState([])
- const [address, setAddress] = useState()
-
- const safeNavigateBack = async () => {
- try {
- const pages = (Taro as any).getCurrentPages?.() || []
- if (Array.isArray(pages) && pages.length > 1) {
- await Taro.navigateBack()
- return true
- }
- } catch (_e) {
- // ignore
- }
- return false
- }
-
- const parseTime = (raw?: unknown) => {
- if (raw === undefined || raw === null || raw === '') return null;
- // 兼容秒/毫秒时间戳
- if (typeof raw === 'number' || (typeof raw === 'string' && /^\d+$/.test(raw))) {
- const n = Number(raw);
- return dayjs(Number.isFinite(n) ? (n < 1e12 ? n * 1000 : n) : raw as any);
- }
- return dayjs(raw as any);
- }
-
- const canModifyOncePerMonth = (item: ShopUserAddress) => {
- const lastUpdate = parseTime(item.updateTime);
- if (!lastUpdate || !lastUpdate.isValid()) return { ok: true as const };
-
- // 若 updateTime 与 createTime 基本一致,则视为“未修改过”,不做限制
- const createdAt = parseTime(item.createTime);
- if (createdAt && createdAt.isValid() && Math.abs(lastUpdate.diff(createdAt, 'minute')) < 1) {
- return { ok: true as const };
- }
-
- const nextAllowed = lastUpdate.add(1, 'month');
- const now = dayjs();
- if (now.isBefore(nextAllowed)) {
- return { ok: false as const, nextAllowed: nextAllowed.format('YYYY-MM-DD HH:mm') };
- }
- return { ok: true as const };
- }
-
- const reload = () => {
- listShopUserAddress({
- userId: Taro.getStorageSync('UserId')
- })
- .then(data => {
- setList(data || [])
- // 默认地址
- setAddress(data.find(item => item.isDefault))
- })
- .catch(() => {
- Taro.showToast({
- title: '获取地址失败',
- icon: 'error'
- });
- })
- }
-
- const onDefault = async (item: ShopUserAddress) => {
- if (item.isDefault) return
-
- if (address) {
- await updateShopUserAddress({
- ...address,
- isDefault: false
- })
- }
- await updateShopUserAddress({
- ...item,
- isDefault: true,
- })
- Taro.showToast({
- title: '设置成功',
- icon: 'success'
- });
- // 设置默认地址通常是“选择地址”的动作:成功后返回上一页,体验更顺滑
- setTimeout(async () => {
- const backed = await safeNavigateBack()
- if (!backed) reload()
- }, 400)
- }
-
- const onDel = async (id?: number) => {
- await removeShopUserAddress(id)
- Taro.showToast({
- title: '删除成功',
- icon: 'success'
- });
- reload();
- }
-
- const selectAddress = async (item: ShopUserAddress) => {
- if (item.isDefault) {
- const backed = await safeNavigateBack()
- if (!backed) reload()
- return
- }
-
- if (address) {
- await updateShopUserAddress({
- ...address,
- isDefault: false
- })
- }
- await updateShopUserAddress({
- ...item,
- isDefault: true,
- })
- setTimeout(async () => {
- const backed = await safeNavigateBack()
- if (!backed) reload()
- }, 500)
- }
-
- useDidShow(() => {
- reload()
- });
-
- if (list.length == 0) {
- return (
-
-
-
-
- Taro.navigateTo({url: '/user/address/add'})}>新增地址
- {/* Taro.navigateTo({url: '/user/address/wxAddress'})}>获取微信地址*/}
-
-
-
- )
- }
-
- return (
- <>
- {/**/}
- {/* Taro.navigateTo({url: '/user/address/wxAddress'})}*/}
- {/* >*/}
- {/* */}
- {/* */}
- {/* */}
- {/* 获取微信地址 */}
- {/* */}
- {/* */}
- {/* */}
- {/* | */}
- {/**/}
- {list.map((item, _) => (
-
- selectAddress(item)}>
-
- {item.name} {item.phone}
-
-
- {item.province} {item.city} {item.region} {item.address}
-
- |
- onDefault(item)}>
- {item.isDefault ? : }
- 默认地址
-
- }
- extra={
- <>
- onDel(item.id)}>
- 删除
-
-
- {
- const { ok, nextAllowed } = canModifyOncePerMonth(item);
- if (!ok) {
- Taro.showToast({
- title: `一个月只能修改一次${nextAllowed ? ',' + nextAllowed + ' 后可再次修改' : ''}`,
- icon: 'none',
- });
- return;
- }
- Taro.navigateTo({url: '/user/address/add?id=' + item.id})
- }}>
- 修改
-
- >
- }
- />
- |
- ))}
- {/* 底部浮动按钮 */}
- Taro.navigateTo({url: '/user/address/add'})} />
- >
- );
-};
-
-export default Address;
diff --git a/src/user/address/wxAddress.config.ts b/src/user/address/wxAddress.config.ts
deleted file mode 100644
index 59f02a7..0000000
--- a/src/user/address/wxAddress.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '我的地址',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/address/wxAddress.tsx b/src/user/address/wxAddress.tsx
deleted file mode 100644
index 1eb0486..0000000
--- a/src/user/address/wxAddress.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import {useEffect} from "react";
-import Taro from '@tarojs/taro'
-
-const WxAddress = () => {
- /**
- * 从微信API获取用户收货地址
- * 调用微信原生地址选择界面,获取成功后跳转到“新增收货地址”页面,让用户选择定位后再保存
- */
- const getWeChatAddress = () => {
- Taro.chooseAddress()
- .then(async res => {
- // 仅填充微信地址信息,不要用“当前定位”覆盖经纬度(会造成经纬度与地址不匹配)。
- // 选择后跳转到“新增/编辑收货地址”页面,让用户手动选择地图定位后再保存。
- const addressDraft = {
- name: res.userName,
- phone: res.telNumber,
- country: res.nationalCode || '中国',
- province: res.provinceName,
- city: res.cityName,
- region: res.countyName,
- address: res.detailInfo,
- isDefault: false,
- }
- Taro.setStorageSync('WxAddressDraft', addressDraft)
- // 用 redirectTo 替换当前页面,避免保存后 navigateBack 回到空白的 wxAddress 页面。
- await Taro.redirectTo({ url: '/user/address/add?fromWx=1&skipDefaultCheck=1' })
- })
- .catch(err => {
- console.error('获取微信地址失败:', err)
- // 用户取消选择地址:直接返回上一页
- if (String(err?.errMsg || '').includes('cancel')) {
- setTimeout(() => Taro.navigateBack(), 200)
- return
- }
- // 处理用户拒绝授权的情况
- if (String(err?.errMsg || '').includes('auth deny')) {
- Taro.showModal({
- title: '授权失败',
- content: '请在设置中允许获取地址权限',
- showCancel: false
- })
- setTimeout(() => Taro.navigateBack(), 300)
- return
- }
-
- Taro.showToast({ title: '获取微信地址失败', icon: 'none' })
- setTimeout(() => Taro.navigateBack(), 300)
- })
- }
-
- useEffect(() => {
- getWeChatAddress()
- }, []);
-
- return (
- <>
- >
- );
-};
-
-export default WxAddress;
diff --git a/src/user/chat/conversation/index.config.ts b/src/user/chat/conversation/index.config.ts
deleted file mode 100644
index 93dd2b9..0000000
--- a/src/user/chat/conversation/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '站内消息'
-})
diff --git a/src/user/chat/conversation/index.tsx b/src/user/chat/conversation/index.tsx
deleted file mode 100644
index 90b3e6f..0000000
--- a/src/user/chat/conversation/index.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import {useState, useCallback, useEffect} from 'react'
-import {View, Text} from '@tarojs/components'
-import Taro from '@tarojs/taro'
-import {Loading, InfiniteLoading, Empty, Space, Tag} from '@nutui/nutui-react-taro'
-import {pageShopChatConversation} from "@/api/shop/shopChatConversation";
-import FixedButton from "@/components/FixedButton";
-
-const Index = () => {
- const [list, setList] = useState([])
- const [loading, setLoading] = useState(false)
- const [page, setPage] = useState(1)
- const [hasMore, setHasMore] = useState(true)
-
- // 获取消息数据
- const fetchMessageData = useCallback(async (resetPage = false, targetPage?: number, searchKeyword?: string) => {
- setLoading(true);
- try {
- const currentPage = resetPage ? 1 : (targetPage || page);
-
- // 构建API参数,根据状态筛选
- const params: any = {
- page: currentPage
- };
-
- // 添加搜索关键词
- if (searchKeyword && searchKeyword.trim()) {
- params.keywords = searchKeyword.trim();
- }
-
- const res = await pageShopChatConversation(params);
-
- if (res?.list && res.list.length > 0) {
- // 正确映射状态
- const mappedList = res.list.map(customer => ({
- ...customer
- }));
-
- // 如果是重置页面或第一页,直接设置新数据;否则追加数据
- if (resetPage || currentPage === 1) {
- setList(mappedList);
- } else {
- setList(prevList => prevList.concat(mappedList));
- }
-
- // 正确判断是否还有更多数据
- const hasMoreData = res.list.length >= 10; // 假设每页10条数据
- setHasMore(hasMoreData);
- } else {
- if (resetPage || currentPage === 1) {
- setList([]);
- }
- setHasMore(false);
- }
-
- setPage(currentPage);
- } catch (error) {
- console.error('获取消息数据失败:', error);
- Taro.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- });
- } finally {
- setLoading(false);
- }
- }, [page]);
-
- const reloadMore = async () => {
- if (loading || !hasMore) return; // 防止重复加载
- const nextPage = page + 1;
- await fetchMessageData(false, nextPage);
- }
-
-
- // 获取列表数据(现在使用服务端搜索,不需要消息端过滤)
- const getFilteredList = () => {
- return list;
- };
-
- useEffect(() => {
- // 初始化时加载数据
- fetchMessageData(true, 1, '');
- }, []);
-
- // 渲染消息项
- const renderMessageItem = (customer: any) => (
-
-
-
-
-
- 关于XXXX的通知
-
- 未读
- {/*已读*/}
-
-
- {/*统一代码:{customer.dealerCode}*/}
-
- 创建时间:{customer.createTime}
-
-
-
-
-
- );
-
- // 渲染消息列表
- const renderMessageList = () => {
- const filteredList = getFilteredList();
-
- return (
-
- {
- // 滚动事件处理
- }}
- onScrollToUpper={() => {
- // 滚动到顶部事件处理
- }}
- loadingText={
- <>
- 加载中...
- >
- }
- loadMoreText={
- filteredList.length === 0 ? (
-
- ) : (
-
- 没有更多了
-
- )
- }
- >
- {loading && filteredList.length === 0 ? (
-
-
- 加载中...
-
- ) : (
- filteredList.map(renderMessageItem)
- )}
-
-
- );
- };
-
- return (
-
- {/* 消息列表 */}
- {renderMessageList()}
-
-
- );
-};
-
-export default Index;
diff --git a/src/user/chat/message/add.config.ts b/src/user/chat/message/add.config.ts
deleted file mode 100644
index f59b48a..0000000
--- a/src/user/chat/message/add.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '发送消息',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/chat/message/add.tsx b/src/user/chat/message/add.tsx
deleted file mode 100644
index a455fd0..0000000
--- a/src/user/chat/message/add.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import {useEffect, useState, useRef} from "react";
-import {useRouter} from '@tarojs/taro'
-import {Loading, CellGroup, Input, Form, Cell, Avatar} from '@nutui/nutui-react-taro'
-import {ArrowRight} from '@nutui/icons-react-taro'
-import {View, Text} from '@tarojs/components'
-import Taro from '@tarojs/taro'
-import FixedButton from "@/components/FixedButton";
-import {addShopChatMessage} from "@/api/shop/shopChatMessage";
-import {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
-import navTo from "@/utils/common";
-import {getUser} from "@/api/system/user";
-import {User} from "@/api/system/user/model";
-
-const AddMessage = () => {
- const {params} = useRouter();
- const [toUser, setToUser] = useState()
- const [loading, setLoading] = useState(true)
- const [FormData, _] = useState()
- const formRef = useRef(null)
-
- // 判断是编辑还是新增模式
- const isEditMode = !!params.id
- const toUserId = params.id ? Number(params.id) : undefined
-
- const reload = async () => {
- if(toUserId){
- getUser(Number(toUserId)).then(data => {
- setToUser(data)
- })
- }
- }
-
- // 提交表单
- const submitSucceed = async (values: any) => {
- try {
- // 准备提交的数据
- const submitData = {
- ...values
- };
-
- console.log('提交数据:', submitData)
-
- // 参数校验
- if(!toUser){
- Taro.showToast({
- title: `请选择发送对象`,
- icon: 'error'
- });
- return false;
- }
-
- // 判断内容是否为空
- if (!values.content) {
- Taro.showToast({
- title: `请输入内容`,
- icon: 'error'
- });
- return false;
- }
- // 执行新增或更新操作
- await addShopChatMessage({
- toUserId: toUserId,
- formUserId: Taro.getStorageSync('UserId'),
- type: 'text',
- content: values.content
- });
-
- Taro.showToast({
- title: `发送成功`,
- icon: 'success'
- });
-
- setTimeout(() => {
- Taro.navigateBack();
- }, 1000);
-
- } catch (error) {
- console.error('发送失败:', error);
- Taro.showToast({
- title: `发送失败`,
- icon: 'error'
- });
- }
- }
-
- const submitFailed = (error: any) => {
- console.log(error, 'err...')
- }
-
- useEffect(() => {
- reload().then(() => {
- setLoading(false)
- })
- }, [isEditMode]);
-
- if (loading) {
- return 加载中
- }
-
- return (
- <>
-
-
-
- {toUser.alias || toUser.nickname}
- {toUser.mobile}
-
-
- ) : '选择发送对象'} extra={(
-
- )}
- onClick={() => navTo(`/dealer/team/index`, true)}/>
-
-
- {/* 底部浮动按钮 */}
- formRef.current?.submit()}/>
- >
- );
-};
-
-export default AddMessage;
diff --git a/src/user/chat/message/detail.config.ts b/src/user/chat/message/detail.config.ts
deleted file mode 100644
index 5072884..0000000
--- a/src/user/chat/message/detail.config.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '查看消息',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/chat/message/detail.tsx b/src/user/chat/message/detail.tsx
deleted file mode 100644
index a7b9c78..0000000
--- a/src/user/chat/message/detail.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import {useEffect, useState} from "react";
-import {useRouter} from '@tarojs/taro'
-import {CellGroup, Cell, Loading, Avatar} from '@nutui/nutui-react-taro'
-import {View,Text} from '@tarojs/components'
-import {ArrowRight} from '@nutui/icons-react-taro'
-import {getShopChatMessage, updateShopChatMessage} from "@/api/shop/shopChatMessage";
-import {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
-import navTo from "@/utils/common";
-
-const AddMessageDetail = () => {
- const {params} = useRouter();
- const [loading, setLoading] = useState(true)
- const [item, setItem] = useState()
-
- const reload = () => {
- const id = params.id ? Number(params.id) : undefined
- if (id) {
- getShopChatMessage(id).then(data => {
- setItem(data)
- setLoading(false)
- updateShopChatMessage({
- ...data,
- status: 1
- }).then(() => {
- console.log('设为已读')
- })
- })
- }
- }
-
- useEffect(() => {
- reload()
- }, []);
-
- if (loading) {
- return 加载中
- }
-
- return (
- <>
-
-
-
- {item.formUserAlias || item.formUserName}
- {item.formUserPhone}
-
-
- ) : '选择发送对象'} extra={(
-
- )}
- onClick={() => navTo(`/dealer/team/index`, true)}/>
-
- |
-
- |
- {/**/}
- {/* {'消息内容:'}*/}
- {/* {item?.content}*/}
- {/* >*/}
- {/*)} />*/}
- | |
-
- {item?.content}
- )} />
- |
- >
- );
-};
-
-export default AddMessageDetail;
diff --git a/src/user/chat/message/index.config.ts b/src/user/chat/message/index.config.ts
deleted file mode 100644
index 8c6bf6a..0000000
--- a/src/user/chat/message/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '我的消息'
-})
diff --git a/src/user/chat/message/index.tsx b/src/user/chat/message/index.tsx
deleted file mode 100644
index 479e7a5..0000000
--- a/src/user/chat/message/index.tsx
+++ /dev/null
@@ -1,179 +0,0 @@
-import {useState, useCallback, useEffect} from 'react'
-import {View, Text} from '@tarojs/components'
-import Taro from '@tarojs/taro'
-import {Loading, InfiniteLoading, Empty, Avatar, Badge} from '@nutui/nutui-react-taro'
-import FixedButton from "@/components/FixedButton";
-import {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
-import {pageShopChatMessage} from "@/api/shop/shopChatMessage";
-import navTo from "@/utils/common";
-
-const MessageIndex = () => {
- const [list, setList] = useState([])
- const [loading, setLoading] = useState(false)
- const [page, setPage] = useState(1)
- const [hasMore, setHasMore] = useState(true)
-
- // 获取消息数据
- const fetchMessageData = useCallback(async (resetPage = false, targetPage?: number, searchKeyword?: string) => {
- setLoading(true);
- try {
- const currentPage = resetPage ? 1 : (targetPage || page);
-
- // 构建API参数,根据状态筛选
- const params: any = {
- type: 'text',
- page: currentPage,
- toUserId: Taro.getStorageSync('UserId')
- };
-
- // 添加搜索关键词
- if (searchKeyword && searchKeyword.trim()) {
- params.keywords = searchKeyword.trim();
- }
-
- const res = await pageShopChatMessage(params);
-
- if (res?.list && res.list.length > 0) {
- // 正确映射状态
- const mappedList = res.list.map(customer => ({
- ...customer
- }));
-
- // 如果是重置页面或第一页,直接设置新数据;否则追加数据
- if (resetPage || currentPage === 1) {
- setList(mappedList);
- } else {
- setList(prevList => prevList.concat(mappedList));
- }
-
- // 正确判断是否还有更多数据
- const hasMoreData = res.list.length >= 10; // 假设每页10条数据
- setHasMore(hasMoreData);
- } else {
- if (resetPage || currentPage === 1) {
- setList([]);
- }
- setHasMore(false);
- }
-
- setPage(currentPage);
- } catch (error) {
- console.error('获取消息数据失败:', error);
- Taro.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- });
- } finally {
- setLoading(false);
- }
- }, [page]);
-
- const reloadMore = async () => {
- if (loading || !hasMore) return; // 防止重复加载
- const nextPage = page + 1;
- await fetchMessageData(false, nextPage);
- }
-
-
- // 获取列表数据(现在使用服务端搜索,不需要消息端过滤)
- const getFilteredList = () => {
- return list;
- };
-
- useEffect(() => {
- // 初始化时加载数据
- fetchMessageData(true, 1, '');
- }, []);
-
- // 渲染消息项
- const renderMessageItem = (item: any) => (
-
- navTo(`/user/chat/message/detail?id=${item.id}`,true)}>
-
-
-
-
-
-
-
-
- {item.formUserAlias || item.formUserName}
-
- {item.createTime}
-
-
-
- {item.content}
-
-
-
-
-
-
-
- );
-
- // 渲染消息列表
- const renderMessageList = () => {
- const filteredList = getFilteredList();
-
- return (
-
- {
- // 滚动事件处理
- }}
- onScrollToUpper={() => {
- // 滚动到顶部事件处理
- }}
- loadingText={
- <>
- 加载中...
- >
- }
- loadMoreText={
- filteredList.length === 0 ? (
-
- ) : (
-
- 没有更多了
-
- )
- }
- >
- {loading && filteredList.length === 0 ? (
-
-
- 加载中...
-
- ) : (
- filteredList.map(renderMessageItem)
- )}
-
-
- );
- };
-
- return (
-
- {/* 消息列表 */}
- {renderMessageList()}
- navTo(`/user/chat/message/add`,true)}/>
-
- );
-};
-
-export default MessageIndex;
diff --git a/src/user/company/company.config.ts b/src/user/company/company.config.ts
deleted file mode 100644
index cfe6a98..0000000
--- a/src/user/company/company.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '公司资料'
-})
diff --git a/src/user/company/company.tsx b/src/user/company/company.tsx
deleted file mode 100644
index a0edefa..0000000
--- a/src/user/company/company.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import {Cell} from '@nutui/nutui-react-taro';
-import {ArrowRight} from '@nutui/icons-react-taro'
-
-function Company() {
- return (
-
-
- 企业名称
- 南宁市网宿信息科技有限公司
- |
- } align={'center'}/>
- 经营信息
-
- {/**/}
- {/* 商户号 */}
- {/* 1557418831 */}
- {/* */}
- {/*} align={'center'}/>*/}
-
- 企业简称
- 网宿软件
-
- } align={'center'}/>
-
- 联系电话
- 137****8880
-
- } align={'center'}/>
- | | |
- 主体信息
-
- 信息详情
-
-
- } align={'center'} extra={}/>
- 企业负责人
-
-
- 姓名
- 赵*林
-
- } align={'center'}/>
- |
-
- )
-}
-
-export default Company
diff --git a/src/user/coupon/coupon.ts b/src/user/coupon/coupon.ts
deleted file mode 100644
index 3db94fc..0000000
--- a/src/user/coupon/coupon.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '我的优惠券',
- navigationBarTextStyle: 'black'
-})
diff --git a/src/user/coupon/coupon.tsx b/src/user/coupon/coupon.tsx
deleted file mode 100644
index 9dfb724..0000000
--- a/src/user/coupon/coupon.tsx
+++ /dev/null
@@ -1,237 +0,0 @@
-import {useState, useEffect, CSSProperties} from 'react'
-import Taro from '@tarojs/taro'
-import {Cell, InfiniteLoading, Tabs, TabPane, Tag, Empty, ConfigProvider} from '@nutui/nutui-react-taro'
-import {pageShopUserCoupon as pageUserCoupon, getMyAvailableCoupons, getMyUsedCoupons, getMyExpiredCoupons} from "@/api/shop/shopUserCoupon";
-import {ShopUserCoupon as UserCouponType} from "@/api/shop/shopUserCoupon/model";
-import {View} from '@tarojs/components'
-
-const InfiniteUlStyle: CSSProperties = {
- height: '100vh',
- width: '100%',
- padding: '0',
- overflowY: 'auto',
- overflowX: 'hidden',
-}
-
-const UserCoupon = () => {
- const [list, setList] = useState([])
- const [page, setPage] = useState(1)
- const [hasMore, setHasMore] = useState(true)
- const [activeTab, setActiveTab] = useState('0')
- const [couponCount, setCouponCount] = useState({
- total: 0,
- unused: 0,
- used: 0,
- expired: 0
- })
-
- const tabs = [
- { key: '0', title: '全部', status: undefined },
- { key: '1', title: '未使用', status: 0 },
- { key: '2', title: '已使用', status: 1 },
- { key: '3', title: '已过期', status: 2 }
- ]
-
- useEffect(() => {
- reload()
- loadCouponCount()
- }, [])
-
- const loadMore = async () => {
- setPage(page + 1)
- reload();
- }
-
- const reload = () => {
- const userId = Taro.getStorageSync('UserId')
- if (!userId) {
- Taro.showToast({
- title: '请先登录',
- icon: 'error'
- });
- return
- }
-
- const tab = tabs.find(t => t.key === activeTab)
- pageUserCoupon({
- userId: parseInt(userId),
- status: tab?.status,
- page
- }).then(res => {
- console.log(res)
- const newList = res?.list || [];
- setList([...list, ...newList])
- setHasMore(newList.length > 0)
- }).catch(error => {
- console.error('Coupon error:', error)
- Taro.showToast({
- title: error?.message || '获取失败',
- icon: 'error'
- });
- })
- }
-
- const loadCouponCount = async () => {
- const userId = Taro.getStorageSync('UserId')
- if (!userId) return
-
- try {
- // 并行获取各种状态的优惠券数量
- const [availableCoupons, usedCoupons, expiredCoupons] = await Promise.all([
- getMyAvailableCoupons().catch(() => []),
- getMyUsedCoupons().catch(() => []),
- getMyExpiredCoupons().catch(() => [])
- ])
-
- setCouponCount({
- unused: availableCoupons.length || 0,
- used: usedCoupons.length || 0,
- expired: expiredCoupons.length || 0
- })
- } catch (error) {
- console.error('Coupon count error:', error)
- }
- }
-
- const onTabChange = (index: string) => {
- setActiveTab(index)
- setList([]) // 清空列表
- setPage(1) // 重置页码
- setHasMore(true) // 重置hasMore
- // 延迟执行reload,确保状态更新完成
- setTimeout(() => {
- reload()
- }, 0)
- }
-
- const getCouponTypeText = (type?: number) => {
- switch (type) {
- case 10: return '满减券'
- case 20: return '折扣券'
- case 30: return '免费券'
- default: return '优惠券'
- }
- }
-
- const getCouponStatusText = (status?: number) => {
- switch (status) {
- case 0: return '未使用'
- case 1: return '已使用'
- case 2: return '已过期'
- default: return '未知'
- }
- }
-
- const getCouponStatusColor = (status?: number) => {
- switch (status) {
- case 0: return 'success'
- case 1: return 'default'
- case 2: return 'danger'
- default: return 'default'
- }
- }
-
- const formatCouponValue = (type?: number, value?: string) => {
- if (!value) return '0'
- switch (type) {
- case 1: return `¥${value}`
- case 2: return `${parseFloat(value) * 10}折`
- case 3: return '免费'
- default: return value
- }
- }
-
- return (
-
-
-
- {tabs.map(tab => (
-
-
-
- ))}
-
-
-
- );
-};
-
-export default UserCoupon;
diff --git a/src/user/coupon/detail.config.ts b/src/user/coupon/detail.config.ts
deleted file mode 100644
index ffb0d8b..0000000
--- a/src/user/coupon/detail.config.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '优惠券详情',
- navigationBarTextStyle: 'black',
- navigationBarBackgroundColor: '#ffffff',
- navigationStyle: 'custom'
-})
diff --git a/src/user/coupon/detail.tsx b/src/user/coupon/detail.tsx
deleted file mode 100644
index f2993cf..0000000
--- a/src/user/coupon/detail.tsx
+++ /dev/null
@@ -1,259 +0,0 @@
-import {useState, useEffect} from "react";
-import {useRouter} from '@tarojs/taro'
-import {Button, ConfigProvider, Tag, Divider} from '@nutui/nutui-react-taro'
-import {ArrowLeft, Gift, Clock, CartCheck, Share} from '@nutui/icons-react-taro'
-import Taro from '@tarojs/taro'
-import {View, Text} from '@tarojs/components'
-import {ShopCoupon} from "@/api/shop/shopCoupon/model";
-import {getShopCoupon} from "@/api/shop/shopCoupon";
-import CouponShare from "@/components/CouponShare";
-import dayjs from "dayjs";
-
-const CouponDetail = () => {
- const router = useRouter()
- const [coupon, setCoupon] = useState(null)
- const [loading, setLoading] = useState(true)
- const [showShare, setShowShare] = useState(false)
- const couponId = router.params.id
-
- useEffect(() => {
- if (couponId) {
- loadCouponDetail()
- }
- }, [couponId])
-
- const loadCouponDetail = async () => {
- try {
- setLoading(true)
- const data = await getShopCoupon(Number(couponId))
- setCoupon(data)
- } catch (error) {
- console.error('获取优惠券详情失败:', error)
- Taro.showToast({
- title: '获取优惠券详情失败',
- icon: 'error'
- })
- } finally {
- setLoading(false)
- }
- }
-
- // 获取优惠券类型文本
- const getCouponTypeText = (type?: number) => {
- switch (type) {
- case 10: return '满减券'
- case 20: return '折扣券'
- case 30: return '免费券'
- default: return '优惠券'
- }
- }
-
- // 获取优惠券金额显示
- const getCouponAmountDisplay = () => {
- if (!coupon) return ''
-
- switch (coupon.type) {
- case 10: // 满减券
- return `¥${coupon.reducePrice}`
- case 20: // 折扣券
- return `${coupon.discount}折`
- case 30: // 免费券
- return '免费'
- default:
- return `¥${coupon.reducePrice || 0}`
- }
- }
-
- // 获取使用条件文本
- const getConditionText = () => {
- if (!coupon) return ''
-
- if (coupon.type === 30) return '无门槛使用'
- if (coupon.minPrice && parseFloat(coupon.minPrice) > 0) {
- return `满${coupon.minPrice}元可用`
- }
- return '无门槛使用'
- }
-
- // 获取有效期文本
- const getValidityText = () => {
- if (!coupon) return ''
-
- if (coupon.expireType === 10) {
- return `领取后${coupon.expireDay}天内有效`
- } else {
- return `${dayjs(coupon.startTime).format('YYYY年MM月DD日')} 至 ${dayjs(coupon.endTime).format('YYYY年MM月DD日')}`
- }
- }
-
- // 获取适用范围文本
- const getApplyRangeText = () => {
- if (!coupon) return ''
-
- switch (coupon.applyRange) {
- case 10: return '全部商品'
- case 20: return '指定商品'
- case 30: return '指定分类'
- default: return '全部商品'
- }
- }
-
- // 获取优惠券状态
- const getCouponStatus = () => {
- if (!coupon) return { status: 0, text: '未知', color: 'default' }
-
- if (coupon.isExpire === 1) {
- return { status: 2, text: '已过期', color: 'danger' }
- } else if (coupon.status === 1) {
- return { status: 1, text: '已使用', color: 'warning' }
- } else {
- return { status: 0, text: '可使用', color: 'success' }
- }
- }
-
- // 使用优惠券
- const handleUseCoupon = () => {
- if (!coupon) return
-
- Taro.showModal({
- title: '使用优惠券',
- content: `确定要使用"${coupon.name}"吗?`,
- success: (res) => {
- if (res.confirm) {
- // 跳转到商品页面或购物车页面
- Taro.navigateTo({
- url: '/pages/index/index'
- })
- }
- }
- })
- }
-
- // 返回上一页
- const handleBack = () => {
- Taro.navigateBack()
- }
-
- if (loading) {
- return (
-
-
- 加载中...
-
-
- )
- }
-
- if (!coupon) {
- return (
-
-
- 优惠券不存在
- 返回
-
-
- )
- }
-
- const statusInfo = getCouponStatus()
-
- return (
-
- {/* 自定义导航栏 */}
-
-
-
- 优惠券详情
-
-
- setShowShare(true)}>
-
-
- {statusInfo.text}
-
-
-
- {/* 优惠券卡片 */}
-
-
-
- {getCouponAmountDisplay()}
- {getCouponTypeText(coupon.type)}
-
-
-
-
- {coupon.name}
- {getConditionText()}
-
-
- {/* 详细信息 */}
-
- 使用说明
-
-
-
-
-
- 有效期
- {getValidityText()}
-
-
-
-
-
-
-
-
- 适用范围
- {getApplyRangeText()}
-
-
-
- {coupon.description && (
- <>
-
-
- 使用说明
- {coupon.description}
-
- >
- )}
-
-
-
- {/* 底部操作按钮 */}
- {statusInfo.status === 0 && (
-
-
- 立即使用
-
-
- )}
-
- {/* 分享弹窗 */}
- {coupon && (
- setShowShare(false)}
- />
- )}
-
- );
-};
-
-export default CouponDetail;
diff --git a/src/user/coupon/index.config.ts b/src/user/coupon/index.config.ts
deleted file mode 100644
index aa390c6..0000000
--- a/src/user/coupon/index.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '我的优惠券',
- navigationBarTextStyle: 'black',
- navigationBarBackgroundColor: '#ffffff'
-})
diff --git a/src/user/coupon/index.tsx b/src/user/coupon/index.tsx
deleted file mode 100644
index 7c69f7e..0000000
--- a/src/user/coupon/index.tsx
+++ /dev/null
@@ -1,466 +0,0 @@
-import {useState} from "react";
-import Taro, {useDidShow} from '@tarojs/taro'
-import {
- Button,
- Empty,
- ConfigProvider,
- SearchBar,
- InfiniteLoading,
- Loading,
- PullToRefresh,
- Tabs,
- TabPane
-} from '@nutui/nutui-react-taro'
-import {Plus, Filter} from '@nutui/icons-react-taro'
-import {View} from '@tarojs/components'
-import {ShopUserCoupon} from "@/api/shop/shopUserCoupon/model";
-import {pageShopUserCoupon, getMyAvailableCoupons, getMyUsedCoupons, getMyExpiredCoupons} from "@/api/shop/shopUserCoupon";
-import CouponList from "@/components/CouponList";
-import CouponStats from "@/components/CouponStats";
-import CouponGuide from "@/components/CouponGuide";
-import CouponFilter from "@/components/CouponFilter";
-import CouponExpireNotice, {ExpiringSoon} from "@/components/CouponExpireNotice";
-import {CouponCardProps} from "@/components/CouponCard";
-import dayjs from "dayjs";
-import {transformCouponData} from "@/utils/couponUtils";
-
-const CouponManage = () => {
- const [list, setList] = useState([])
- const [loading, setLoading] = useState(false)
- const [hasMore, setHasMore] = useState(true)
- const [searchValue, setSearchValue] = useState('')
- const [page, setPage] = useState(1)
- const [total, setTotal] = useState(0)
- console.log('total = ', total)
- const [activeTab, setActiveTab] = useState('0') // 0-可用 1-已使用 2-已过期
- const [stats, setStats] = useState({
- available: 0,
- used: 0,
- expired: 0
- })
- const [showGuide, setShowGuide] = useState(false)
- const [showFilter, setShowFilter] = useState(false)
- const [showExpireNotice, setShowExpireNotice] = useState(false)
- const [expiringSoonCoupons, setExpiringSoonCoupons] = useState([])
- const [filters, setFilters] = useState({
- type: [] as number[],
- minAmount: undefined as number | undefined,
- sortBy: 'createTime' as 'createTime' | 'amount' | 'expireTime',
- sortOrder: 'desc' as 'asc' | 'desc'
- })
-
- // 获取优惠券状态过滤条件
- const reload = async (isRefresh = false) => {
- // 直接调用reloadWithTab,使用当前的activeTab
- await reloadWithTab(activeTab, isRefresh)
- }
-
- // 搜索功能
- const handleSearch = (value: string) => {
- setSearchValue(value)
- reload(true)
- }
-
- // 下拉刷新
- const handleRefresh = async () => {
- await reload(true)
- }
-
- // Tab切换
- const handleTabChange = (value: string | number) => {
- const tabValue = String(value)
- console.log('Tab切换:', {from: activeTab, to: tabValue})
- setActiveTab(tabValue)
- setPage(1)
- setList([])
- setHasMore(true)
-
- // 直接调用reload,传入新的tab值
- reloadWithTab(tabValue)
- }
-
- // 根据指定tab加载数据
- const reloadWithTab = async (tab: string, isRefresh = true) => {
- if (isRefresh) {
- setPage(1)
- setList([])
- setHasMore(true)
- }
-
- setLoading(true)
- try {
- let res: any = null
-
- // 根据tab选择对应的API
- switch (tab) {
- case '0': // 可用优惠券
- res = await getMyAvailableCoupons()
- break
- case '1': // 已使用优惠券
- res = await getMyUsedCoupons()
- break
- case '2': // 已过期优惠券
- res = await getMyExpiredCoupons()
- break
- default:
- res = await getMyAvailableCoupons()
- }
-
- console.log('使用Tab加载数据:', { tab, data: res })
-
- if (res && res.length > 0) {
- // 应用搜索过滤
- let filteredList = res
- if (searchValue) {
- filteredList = res.filter((item: any) =>
- item.name?.includes(searchValue) ||
- item.description?.includes(searchValue)
- )
- }
-
- // 应用其他筛选条件
- if (filters.type.length > 0) {
- filteredList = filteredList.filter((item: any) =>
- filters.type.includes(item.type)
- )
- }
-
- if (filters.minAmount) {
- filteredList = filteredList.filter((item: any) =>
- parseFloat(item.minPrice || '0') >= filters.minAmount!
- )
- }
-
- // 排序
- filteredList.sort((a: any, b: any) => {
- const aValue = getValueForSort(a, filters.sortBy)
- const bValue = getValueForSort(b, filters.sortBy)
-
- if (filters.sortOrder === 'asc') {
- return aValue - bValue
- } else {
- return bValue - aValue
- }
- })
-
- setList(filteredList)
- setTotal(filteredList.length)
- setHasMore(false) // 一次性加载所有数据,不需要分页
- } else {
- setList([])
- setTotal(0)
- setHasMore(false)
- }
- } catch (error) {
- console.error('获取优惠券失败:', error)
- Taro.showToast({
- title: '获取优惠券失败',
- icon: 'error'
- });
- setList([])
- setTotal(0)
- setHasMore(false)
- } finally {
- setLoading(false)
- }
- }
-
- // 获取排序值的辅助函数
- const getValueForSort = (item: any, sortBy: string) => {
- switch (sortBy) {
- case 'amount':
- return parseFloat(item.reducePrice || item.discount || '0')
- case 'expireTime':
- return new Date(item.endTime || '').getTime()
- case 'createTime':
- default:
- return new Date(item.createTime || '').getTime()
- }
- }
-
- // 转换优惠券数据并添加使用按钮
- const transformCouponDataWithAction = (coupon: ShopUserCoupon): CouponCardProps => {
- console.log('原始优惠券数据:', coupon)
-
- // 使用统一的转换函数
- const transformedCoupon = transformCouponData(coupon)
-
- console.log('转换后的优惠券数据:', transformedCoupon)
-
- // 添加使用按钮和点击事件
- const result = {
- ...transformedCoupon,
- showUseBtn: transformedCoupon.status === 0, // 只有未使用的券显示使用按钮
- onUse: () => handleUseCoupon(coupon)
- }
-
- console.log('最终优惠券数据:', result)
- return result
- }
-
- // 使用优惠券
- const handleUseCoupon = (_: ShopUserCoupon) => {
- // 这里可以跳转到商品页面或购物车页面
- Taro.navigateTo({
- url: '/shop/category/index?id=4326'
- })
- }
-
- // 优惠券点击事件
- const handleCouponClick = (_coupon: CouponCardProps, index: number) => {
- const originalCoupon = list[index]
- if (originalCoupon) {
- // 显示优惠券详情
- showCouponDetail(originalCoupon)
- }
- }
-
- // 显示优惠券详情
- const showCouponDetail = (coupon: ShopUserCoupon) => {
- // 跳转到优惠券详情页
- Taro.navigateTo({
- url: `/user/coupon/detail?id=${coupon.id}`
- })
- }
-
- // 加载优惠券统计数据
- const loadCouponStats = async () => {
- try {
- // 并行获取各状态的优惠券数量
- const [availableRes, usedRes, expiredRes] = await Promise.all([
- getMyAvailableCoupons(),
- getMyUsedCoupons(),
- getMyExpiredCoupons()
- ])
-
- setStats({
- available: availableRes?.length || 0,
- used: usedRes?.length || 0,
- expired: expiredRes?.length || 0
- })
- } catch (error) {
- console.error('获取优惠券统计失败:', error)
- // 设置默认值
- setStats({
- available: 0,
- used: 0,
- expired: 0
- })
- }
- }
-
- // 统计卡片点击事件
- const handleStatsClick = (type: 'available' | 'used' | 'expired') => {
- const tabMap = {
- available: '0',
- used: '1',
- expired: '2'
- }
- handleTabChange(tabMap[type])
- }
-
- // 筛选条件变更
- const handleFiltersChange = (newFilters: any) => {
- setFilters(newFilters)
- reload(true).then()
- }
-
- // 检查即将过期的优惠券
- const checkExpiringSoonCoupons = async () => {
- try {
- // 获取即将过期的优惠券(3天内过期)
- const res = await pageShopUserCoupon({
- page: page,
- limit: 50,
- status: 0, // 未使用
- isExpire: 0 // 未过期
- })
-
- if (res && res.list) {
- const now = dayjs()
- const expiringSoon = res.list
- .map(coupon => {
- const endTime = dayjs(coupon.endTime)
- const daysLeft = endTime.diff(now, 'day')
-
- return {
- id: coupon.id || 0,
- name: coupon.name || '',
- type: coupon.type || 10,
- amount: coupon.type === 10 ? coupon.reducePrice || '0' :
- coupon.type === 20 ? coupon.discount?.toString() || '0' : '0',
- minAmount: coupon.minPrice,
- endTime: coupon.endTime || '',
- daysLeft
- }
- })
- .filter(coupon => coupon.daysLeft >= 0 && coupon.daysLeft <= 3)
- .sort((a, b) => a.daysLeft - b.daysLeft)
-
- if (expiringSoon.length > 0) {
- // @ts-ignore
- setExpiringSoonCoupons(expiringSoon)
- // 延迟显示提醒,避免与页面加载冲突
- setTimeout(() => {
- setShowExpireNotice(true)
- }, 1000)
- }
- }
- } catch (error) {
- console.error('检查即将过期优惠券失败:', error)
- }
- }
-
- // 使用即将过期的优惠券
- const handleUseExpiringSoonCoupon = (coupon: ExpiringSoon) => {
- console.log(coupon, '使用即将过期优惠券')
- setShowExpireNotice(false)
- // 跳转到商品页面
- Taro.navigateTo({
- url: '/pages/index/index'
- })
- }
-
- // 加载更多
- const loadMore = async () => {
- if (!loading && hasMore) {
- await reload(false) // 不刷新,追加数据
- }
- }
-
- useDidShow(() => {
- reload(true).then()
- loadCouponStats().then()
- // 只在可用优惠券tab时检查即将过期的优惠券
- if (activeTab === '0') {
- checkExpiringSoonCoupons().then()
- }
- });
-
- return (
-
- {/* 搜索栏和领取入口 */}
-
-
-
-
-
- }
- onClick={() => Taro.navigateTo({url: '/user/coupon/receive'})}
- >
- 领取
-
- }
- onClick={() => setShowFilter(true)}
- >
- 筛选
-
- setShowGuide(true)}
- >
- 帮助
-
-
-
-
- {/* 优惠券统计 */}
-
-
- {/* Tab切换 */}
-
-
-
-
-
-
-
-
- {/* 优惠券列表 */}
-
-
- {list.length === 0 && !loading ? (
-
-
-
- ) : (
-
-
- 加载中...
-
- }
- loadMoreText={
-
- {list.length === 0 ? "暂无数据" : "没有更多了"}
-
- }
- >
-
-
- )}
-
-
-
- {/* 使用指南弹窗 */}
- setShowGuide(false)}
- />
-
- {/*/!* 筛选弹窗 *!/*/}
- setShowFilter(false)}
- />
-
- {/*/!* 到期提醒弹窗 *!/*/}
- setShowExpireNotice(false)}
- onUseCoupon={handleUseExpiringSoonCoupon}
- />
-
- );
-
-};
-
-export default CouponManage;
diff --git a/src/user/coupon/receive.config.ts b/src/user/coupon/receive.config.ts
deleted file mode 100644
index 784b1d9..0000000
--- a/src/user/coupon/receive.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: '领取优惠券',
- navigationBarTextStyle: 'black',
- navigationBarBackgroundColor: '#ffffff'
-})
diff --git a/src/user/coupon/receive.tsx b/src/user/coupon/receive.tsx
deleted file mode 100644
index 25055cd..0000000
--- a/src/user/coupon/receive.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import {useState} from "react";
-import Taro, {useDidShow} from '@tarojs/taro'
-import {Button, Empty, ConfigProvider, SearchBar, InfiniteLoading, Loading, PullToRefresh} from '@nutui/nutui-react-taro'
-import {Gift} from '@nutui/icons-react-taro'
-import {View} from '@tarojs/components'
-import {ShopCoupon} from "@/api/shop/shopCoupon/model";
-import {pageShopCoupon} from "@/api/shop/shopCoupon";
-import CouponList from "@/components/CouponList";
-import {CouponCardProps} from "@/components/CouponCard";
-
-const CouponReceive = () => {
- const [list, setList] = useState([])
- const [loading, setLoading] = useState(false)
- const [hasMore, setHasMore] = useState(true)
- const [searchValue, setSearchValue] = useState('')
- const [page, setPage] = useState(1)
- const [total, setTotal] = useState(0)
-
- const reload = async (isRefresh = false) => {
- if (isRefresh) {
- setPage(1)
- setList([])
- setHasMore(true)
- }
-
- setLoading(true)
- try {
- const currentPage = isRefresh ? 1 : page
- // 获取可领取的优惠券(启用状态且未过期)
- const res = await pageShopCoupon({
- page: currentPage,
- limit: 10,
- keywords: searchValue,
- enabled: 1, // 启用状态
- isExpire: 0 // 未过期
- })
-
- if (res && res.list) {
- const newList = isRefresh ? res.list : [...list, ...res.list]
- setList(newList)
- setTotal(res.count || 0)
-
- setHasMore(res.list.length === 10)
-
- if (!isRefresh) {
- setPage(currentPage + 1)
- } else {
- setPage(2)
- }
- } else {
- setHasMore(false)
- setTotal(0)
- }
- } catch (error) {
- console.error('获取优惠券失败:', error)
- Taro.showToast({
- title: '获取优惠券失败',
- icon: 'error'
- });
- } finally {
- setLoading(false)
- }
- }
-
- // 搜索功能
- const handleSearch = (value: string) => {
- setSearchValue(value)
- reload(true)
- }
-
- // 下拉刷新
- const handleRefresh = async () => {
- await reload(true)
- }
-
- // 转换优惠券数据为CouponCard组件所需格式
- const transformCouponData = (coupon: ShopCoupon): CouponCardProps => {
- let amount = 0
- let type: 10 | 20 | 30 = 10 // 使用新的类型值
-
- if (coupon.type === 10) { // 满减券
- type = 10
- amount = parseFloat(coupon.reducePrice || '0')
- } else if (coupon.type === 20) { // 折扣券
- type = 20
- amount = coupon.discount || 0
- } else if (coupon.type === 30) { // 免费券
- type = 30
- amount = 0
- }
-
- return {
- amount,
- type,
- status: 0, // 可领取状态
- minAmount: parseFloat(coupon.minPrice || '0'),
- title: coupon.name || '优惠券',
- startTime: coupon.startTime,
- endTime: coupon.endTime,
- showReceiveBtn: true, // 显示领取按钮
- onReceive: () => handleReceiveCoupon(coupon),
- theme: getThemeByType(coupon.type)
- }
- }
-
- // 根据优惠券类型获取主题色
- const getThemeByType = (type?: number): 'red' | 'orange' | 'blue' | 'purple' | 'green' => {
- switch (type) {
- case 10: return 'red' // 满减券
- case 20: return 'orange' // 折扣券
- case 30: return 'green' // 免费券
- default: return 'blue'
- }
- }
-
- // 领取优惠券
- const handleReceiveCoupon = async (_coupon: ShopCoupon) => {
- try {
- // 这里应该调用领取优惠券的API
- // await receiveCoupon(coupon.id)
-
- Taro.showToast({
- title: '领取成功',
- icon: 'success'
- })
-
- // 刷新列表
- reload(true)
- } catch (error) {
- console.error('领取优惠券失败:', error)
- Taro.showToast({
- title: '领取失败',
- icon: 'error'
- })
- }
- }
-
- // 优惠券点击事件
- const handleCouponClick = (_coupon: CouponCardProps, index: number) => {
- const originalCoupon = list[index]
- if (originalCoupon) {
- // 显示优惠券详情
- showCouponDetail(originalCoupon)
- }
- }
-
- // 显示优惠券详情
- const showCouponDetail = (coupon: ShopCoupon) => {
- // 跳转到优惠券详情页
- Taro.navigateTo({
- url: `/user/coupon/detail?id=${coupon.id}`
- })
- }
-
- // 加载更多
- const loadMore = async () => {
- if (!loading && hasMore) {
- await reload(false)
- }
- }
-
- useDidShow(() => {
- reload(true).then()
- });
-
- return (
-
- {/* 搜索栏 */}
-
-
-
-
- {/* 统计信息 */}
- {total > 0 && (
-
- 共找到 {total} 张可领取优惠券
-
- )}
-
- {/* 优惠券列表 */}
-
-
- {list.length === 0 && !loading ? (
-
-
- Taro.navigateTo({url: '/pages/index/index'})}
- >
- 去逛逛
-
-
- ) : (
-
-
- 加载中...
-
- }
- loadMoreText={
-
- {list.length === 0 ? "暂无数据" : "没有更多了"}
-
- }
- >
-
-
- )}
-
-
-
- {/* 底部提示 */}
- {list.length === 0 && !loading && (
-
-
-
-
- 暂无可领取优惠券
- 请关注商家活动获取更多优惠券
-
- )}
-
- );
-};
-
-export default CouponReceive;
diff --git a/src/user/order/components/OrderList.tsx b/src/user/order/components/OrderList.tsx
deleted file mode 100644
index 2962b65..0000000
--- a/src/user/order/components/OrderList.tsx
+++ /dev/null
@@ -1,903 +0,0 @@
-import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog, PullToRefresh, InfiniteLoading} from '@nutui/nutui-react-taro'
-import {useEffect, useState, useCallback, useRef, CSSProperties} from "react";
-import {View, Text} from '@tarojs/components'
-import Taro from '@tarojs/taro';
-import dayjs from "dayjs";
-import {
- pageShopOrder,
- updateShopOrder,
- createOrder,
- getShopOrder,
- prepayShopOrder
-} from "@/api/shop/shopOrder";
-import {OrderCreateRequest, ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
-import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
-import {copyText} from "@/utils/common";
-import PaymentCountdown from "@/components/PaymentCountdown";
-import {PaymentType} from "@/utils/payment";
-import {ErrorType, RequestError} from "@/utils/request";
-
-// 判断订单是否支付已过期
-const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => {
- if (!createTime) return false;
- const createTimeObj = dayjs(createTime);
- const expireTime = createTimeObj.add(timeoutHours, 'hour');
- const now = dayjs();
- return now.isAfter(expireTime);
-};
-
-// 申请退款:支付成功后仅允许在指定时间窗内发起(前端展示层限制,后端仍应校验)
-const isWithinRefundWindow = (payTime?: string, windowMinutes: number = 60): boolean => {
- if (!payTime) return false;
- const raw = String(payTime).trim();
- const t = /^\d+$/.test(raw)
- ? dayjs(Number(raw) < 1e12 ? Number(raw) * 1000 : Number(raw)) // 兼容秒/毫秒时间戳
- : dayjs(raw);
- if (!t.isValid()) return false;
- return dayjs().diff(t, 'minute') <= windowMinutes;
-};
-
-const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({
- marginTop: showSearch ? '0' : '0', // 如果显示搜索框,增加更多的上边距
- height: showSearch ? '75vh' : '84vh', // 相应调整高度
- width: '100%',
- padding: '0',
- overflowY: 'auto',
- overflowX: 'hidden'
- // 注意:小程序不支持 boxShadow
-})
-
-// 统一的订单状态标签配置,与后端 statusFilter 保持一致
-const tabs = [
- {
- index: 0,
- key: '全部',
- title: '全部',
- description: '所有订单',
- statusFilter: -1 // 使用-1表示全部订单
- },
- {
- index: 1,
- key: '待付款',
- title: '待付款',
- description: '等待付款的订单',
- statusFilter: 0 // 对应后端:pay_status = false
- },
- {
- index: 2,
- key: '待发货',
- title: '待发货',
- description: '已付款待发货的订单',
- statusFilter: 1 // 对应后端:pay_status = true AND delivery_status = 10
- },
- {
- index: 3,
- key: '待收货',
- title: '待收货',
- description: '已发货待收货的订单',
- statusFilter: 3 // 对应后端:pay_status = true AND delivery_status = 20
- },
- {
- index: 4,
- key: '已完成',
- title: '已完成',
- description: '已完成的订单',
- statusFilter: 5 // 对应后端:order_status = 1
- },
- {
- index: 5,
- key: '退货/售后',
- title: '退货/售后',
- description: '退货/售后的订单',
- statusFilter: 6 // 对应后端:order_status = 6 (已退款)
- }
-]
-
-interface OrderListProps {
- onReload?: () => void;
- searchParams?: ShopOrderParam;
- showSearch?: boolean;
- onSearchParamsChange?: (params: ShopOrderParam) => void; // 新增:通知父组件参数变化
- // 订单视图模式:用户/门店/骑手
- mode?: 'user' | 'store' | 'rider';
- // 固定过滤条件(例如 storeId / riderId),会合并到每次请求里
- baseParams?: ShopOrderParam;
- // 只读模式:隐藏“支付/取消/确认收货/退款”等用户操作按钮
- readOnly?: boolean;
-}
-
-function OrderList(props: OrderListProps) {
- const [list, setList] = useState([])
- const pageRef = useRef(1)
- const [hasMore, setHasMore] = useState(true)
- const [payingOrderId, setPayingOrderId] = useState(null)
- // 根据传入的statusFilter设置初始tab索引
- const getInitialTabIndex = () => {
- if (props.searchParams?.statusFilter !== undefined) {
- const tab = tabs.find(t => t.statusFilter === props.searchParams?.statusFilter);
- return tab ? tab.index : 0;
- }
- return 0;
- };
- const [tapIndex, setTapIndex] = useState(() => {
- const initialIndex = getInitialTabIndex();
- console.log('初始化tapIndex:', initialIndex, '对应statusFilter:', props.searchParams?.statusFilter);
- return initialIndex;
- })
- const [loading, setLoading] = useState(false)
- const [error, setError] = useState(null)
- const [cancelDialogVisible, setCancelDialogVisible] = useState(false)
- const [orderToCancel, setOrderToCancel] = useState(null)
- const [confirmReceiveDialogVisible, setConfirmReceiveDialogVisible] = useState(false)
- const [orderToConfirmReceive, setOrderToConfirmReceive] = useState(null)
- const isReadOnly = props.readOnly || props.mode === 'store' || props.mode === 'rider'
-
- const toNum = (v: any): number | undefined => {
- if (v === null || v === undefined || v === '') return undefined;
- const n = Number(v);
- return Number.isFinite(n) ? n : undefined;
- };
-
- // “已完成”应以订单状态为准;不要用商品ID等字段推断完成态,否则会造成 Tab(待发货/待收货) 与状态文案不同步
- const isOrderCompleted = (order: ShopOrder) => toNum(order.orderStatus) === 1;
-
- // 获取订单状态文本
- const getOrderStatusText = (order: ShopOrder) => {
- const orderStatus = toNum(order.orderStatus);
- const deliveryStatus = toNum(order.deliveryStatus);
-
- // 优先检查订单状态
- if (orderStatus === 2) return '已取消';
- if (orderStatus === 4) return '退款申请中';
- if (orderStatus === 5) return '退款被拒绝';
- if (orderStatus === 6) return '退款成功';
- if (orderStatus === 7) return '客户端申请退款';
- if (isOrderCompleted(order)) return '已完成';
-
- // 检查支付状态 (payStatus为boolean类型,false/0表示未付款,true/1表示已付款)
- if (!order.payStatus) return '等待买家付款';
-
- // 已付款后检查发货状态
- if (deliveryStatus === 10) return '待发货';
- if (deliveryStatus === 20) {
- // 若订单没有配送员,沿用原“待收货”语义
- if (!order.riderId || Number(order.riderId) === 0) return '待收货';
- // 配送员确认送达后(sendEndTime有值),才进入“待确认收货”
- if (order.sendEndTime && !isOrderCompleted(order)) return '待确认收货';
- return '配送中';
- }
- if (deliveryStatus === 30) return '部分发货';
-
- if (orderStatus === 0) return '未使用';
-
- return '未知状态';
- };
-
- // 获取订单状态颜色
- const getOrderStatusColor = (order: ShopOrder) => {
- const orderStatus = toNum(order.orderStatus);
- const deliveryStatus = toNum(order.deliveryStatus);
- // 优先检查订单状态
- if (orderStatus === 2) return 'text-gray-500'; // 已取消
- if (orderStatus === 4) return 'text-orange-500'; // 退款申请中
- if (orderStatus === 5) return 'text-red-500'; // 退款被拒绝
- if (orderStatus === 6) return 'text-green-500'; // 退款成功
- if (orderStatus === 7) return 'text-orange-500'; // 客户端申请退款
- if (isOrderCompleted(order)) return 'text-green-600'; // 已完成
-
- // 检查支付状态
- if (!order.payStatus) return 'text-orange-500'; // 等待买家付款
-
- // 已付款后检查发货状态
- if (deliveryStatus === 10) return 'text-blue-500'; // 待发货
- if (deliveryStatus === 20) {
- if (!order.riderId || Number(order.riderId) === 0) return 'text-purple-500'; // 待收货
- if (order.sendEndTime && !isOrderCompleted(order)) return 'text-purple-500'; // 待确认收货
- return 'text-blue-500'; // 配送中
- }
- if (deliveryStatus === 30) return 'text-blue-500'; // 部分发货
-
- if (orderStatus === 0) return 'text-gray-500'; // 未使用
-
- return 'text-gray-600'; // 默认颜色
- };
-
- // 使用后端统一的 statusFilter 进行筛选
- const getOrderStatusParams = (index: string | number) => {
- let params: ShopOrderParam = {
- ...(props.baseParams || {})
- };
- // 默认是用户视图:添加 userId 过滤;门店/骑手视图由 baseParams 控制
- if (!props.mode || props.mode === 'user') {
- params.userId = Taro.getStorageSync('UserId');
- }
-
- // 获取当前tab的statusFilter配置
- const currentTab = tabs.find(tab => tab.index === Number(index));
- if (currentTab && currentTab.statusFilter !== undefined) {
- params.statusFilter = currentTab.statusFilter;
- }
- // 注意:当statusFilter为undefined时,不要添加到params中,这样API请求就不会包含这个参数
-
- console.log(`Tab ${index} (${currentTab?.title}) 筛选参数:`, params);
- return params;
- };
-
- const reload = useCallback(async (resetPage = false, targetPage?: number) => {
- setLoading(true);
- setError(null); // 清除之前的错误
- const currentPage = resetPage ? 1 : (targetPage || pageRef.current);
- const statusParams = getOrderStatusParams(tapIndex);
- // 合并搜索条件,tab的statusFilter优先级更高
- const searchConditions: any = {
- page: currentPage,
- ...statusParams,
- ...props.searchParams, // 搜索关键词等其他条件
- };
-
- // Tabs 的 statusFilter 优先级最高;全部(-1)时不传该参数(后端按“无筛选”处理)
- if (statusParams.statusFilter === undefined || statusParams.statusFilter === -1) {
- delete searchConditions.statusFilter;
- } else {
- searchConditions.statusFilter = statusParams.statusFilter;
- }
- console.log('订单筛选条件:', {
- tapIndex,
- statusParams,
- searchConditions,
- finalStatusFilter: searchConditions.statusFilter
- });
-
- try {
- const res = await pageShopOrder(searchConditions);
-
- if (res?.list && res?.list.length > 0) {
- // 订单分页接口已返回 orderGoods:列表直接使用该字段
- const incoming = res.list as ShopOrder[];
-
- // 使用函数式更新避免依赖 list
- setList(prevList => {
- const newList = resetPage ? incoming : (prevList || []).concat(incoming);
- return newList;
- });
-
- // 正确判断是否还有更多数据
- const hasMoreData = incoming.length >= 10; // 假设每页10条数据
- setHasMore(hasMoreData);
- } else {
- setList(prevList => resetPage ? [] : prevList);
- setHasMore(false);
- }
-
- pageRef.current = currentPage;
- setLoading(false);
- } catch (error) {
- console.error('加载订单失败:', error);
- setLoading(false);
- setError('加载订单失败,请重试');
- // 添加错误提示
- Taro.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- });
- }
- }, [tapIndex, props.searchParams]); // 移除 list/page 依赖,避免useEffect触发循环
-
- const reloadMore = useCallback(async () => {
- if (loading || !hasMore) return; // 防止重复加载
- const nextPage = pageRef.current + 1;
- pageRef.current = nextPage;
- await reload(false, nextPage);
- }, [loading, hasMore, reload]);
-
- // 确认收货 - 显示确认对话框
- const confirmReceive = (order: ShopOrder) => {
- setOrderToConfirmReceive(order);
- setConfirmReceiveDialogVisible(true);
- };
-
- // 确认收货 - 执行收货操作
- const handleConfirmReceive = async () => {
- if (!orderToConfirmReceive) return;
-
- try {
- setConfirmReceiveDialogVisible(false);
-
- await updateShopOrder({
- ...orderToConfirmReceive,
- deliveryStatus: orderToConfirmReceive.deliveryStatus, // 10未发货 20已发货 30部分发货(收货由orderStatus控制)
- orderStatus: 1 // 已完成
- });
-
- Taro.showToast({
- title: '确认收货成功',
- icon: 'success'
- });
-
- await reload(true); // 重新加载列表
- props.onReload?.(); // 通知父组件刷新
-
- // 清空状态
- setOrderToConfirmReceive(null);
- } catch (error) {
- console.error('确认收货失败:', error);
- Taro.showToast({
- title: '确认收货失败',
- icon: 'none'
- });
- // 重新显示对话框
- setConfirmReceiveDialogVisible(true);
- }
- };
-
- // 取消确认收货对话框
- const handleCancelReceiveDialog = () => {
- setConfirmReceiveDialogVisible(false);
- setOrderToConfirmReceive(null);
- };
-
- // 申请退款 (待发货状态)
- const applyRefund = (order: ShopOrder) => {
- // 跳转到退款申请页面(订单状态在选择退款原因后再更新)
- Taro.navigateTo({
- url: `/user/order/refund/index?orderId=${order.orderId}&orderNo=${order.orderNo}`
- });
- };
-
- // 查看物流 (待收货状态)
- // const viewLogistics = (order: ShopOrder) => {
- // // 跳转到物流查询页面
- // Taro.navigateTo({
- // url: `/user/order/logistics/index?orderId=${order.orderId}&orderNo=${order.orderNo}&expressNo=${order.transactionId || ''}&expressCompany=SF`
- // });
- // };
-
- // 取消订单
- const cancelOrder = (order: ShopOrder) => {
- setOrderToCancel(order);
- setCancelDialogVisible(true);
- };
-
- // 确认取消订单
- const handleConfirmCancel = async () => {
- if (!orderToCancel) return;
- if (!orderToCancel.orderId) {
- Taro.showToast({
- title: '订单信息错误',
- icon: 'error'
- });
- setOrderToCancel(null);
- setCancelDialogVisible(false);
- return;
- }
-
- try {
- setCancelDialogVisible(false);
-
- // 更新订单状态为已取消,而不是删除订单
- await updateShopOrder({
- // 只传最小字段,避免误取消/误走售后流程
- orderId: orderToCancel.orderId,
- orderStatus: 2 // 已取消
- });
-
- Taro.showToast({
- title: '订单已取消',
- icon: 'success'
- });
- void reload(true); // 重新加载列表
- props.onReload?.(); // 通知父组件刷新
- } catch (error) {
- console.error('取消订单失败:', error);
- Taro.showToast({
- title: '取消订单失败',
- icon: 'error'
- });
- } finally {
- setOrderToCancel(null);
- }
- };
-
- // 取消对话框的取消操作
- const handleCancelDialog = () => {
- setCancelDialogVisible(false);
- setOrderToCancel(null);
- };
-
- // 立即支付
- const payOrder = async (order: ShopOrder) => {
- try {
- if (!order.orderId) {
- Taro.showToast({
- title: '订单信息错误',
- icon: 'error'
- });
- return;
- }
-
- if (payingOrderId === order.orderId) {
- return;
- }
- setPayingOrderId(order.orderId);
-
- // 尽量以服务端最新状态为准,避免“已取消/已支付”但列表未刷新导致误发起支付
- let latestOrder: ShopOrder | null = null;
- try {
- latestOrder = await getShopOrder(order.orderId);
- } catch (_e) {
- // 忽略:网络波动时继续使用列表数据兜底
- }
- const effectiveOrder = latestOrder ? { ...order, ...latestOrder } : order;
-
- if (effectiveOrder.payStatus) {
- Taro.showToast({
- title: '订单已支付',
- icon: 'none'
- });
- // 同步刷新一次,避免列表显示旧状态
- void reload(true);
- return;
- }
- if (effectiveOrder.orderStatus === 2) {
- Taro.showToast({
- title: '订单已取消,无法支付',
- icon: 'error'
- });
- void reload(true);
- return;
- }
-
- // 检查订单是否已过期(以最新 createTime 为准)
- if (effectiveOrder.createTime && isPaymentExpired(effectiveOrder.createTime)) {
- Taro.showToast({
- title: '订单已过期,无法支付',
- icon: 'error'
- });
- return;
- }
-
- Taro.showLoading({title: '发起支付...'});
-
- // 构建商品数据:优先使用订单分页接口返回的 orderGoods;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败
- let orderGoods = effectiveOrder.orderGoods || [];
- if (!orderGoods.length) {
- try {
- orderGoods = (await listShopOrderGoods({orderId: effectiveOrder.orderId})) || [];
- } catch (e) {
- // 继续走下面的校验提示
- console.error('补拉订单商品失败:', e);
- }
- }
-
- const goodsItems = orderGoods
- .filter(g => !!(g as any).goodsId || !!(g as any).itemId)
- .map(goods => ({
- goodsId: (goods.goodsId ?? (goods as any).itemId) as number,
- quantity: ((goods as any).quantity ?? goods.totalNum ?? 1) as number,
- // 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全
- skuId: (goods as any).skuId ?? (goods as any).sku_id,
- specInfo: (goods as any).specInfo ?? (goods as any).spec
- }));
-
- if (!goodsItems.length) {
- Taro.showToast({
- title: '订单商品信息缺失,请稍后重试',
- icon: 'none'
- });
- return;
- }
-
- // 优先:对“已创建但未支付”的订单走“重新发起支付”接口(不应重复创建订单)
- // 若后端未提供该接口,则降级为重新创建订单(此时不传 orderNo,避免出现“相同订单号重复订单”)
- let result: any;
- let usedFallbackCreate = false;
- try {
- result = await prepayShopOrder({
- orderId: effectiveOrder.orderId!,
- payType: PaymentType.WECHAT
- });
- } catch (e) {
- // 订单状态等业务错误:直接提示,不要降级“重新创建订单”导致产生多笔订单
- if (e instanceof RequestError && e.type === ErrorType.BUSINESS_ERROR) {
- throw e;
- }
- usedFallbackCreate = true;
- const orderData: OrderCreateRequest = {
- goodsItems,
- addressId: effectiveOrder.addressId,
- payType: PaymentType.WECHAT,
- couponId: effectiveOrder.couponId,
- deliveryType: effectiveOrder.deliveryType,
- selfTakeMerchantId: effectiveOrder.selfTakeMerchantId,
- comments: effectiveOrder.comments,
- title: effectiveOrder.title,
- storeId: effectiveOrder.storeId,
- storeName: effectiveOrder.storeName,
- riderId: effectiveOrder.riderId,
- warehouseId: effectiveOrder.warehouseId
- };
- result = await createOrder(orderData);
- }
-
- if (!result) {
- throw new Error('支付发起失败');
- }
-
- // 验证微信支付必要参数
- if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) {
- throw new Error('微信支付参数不完整');
- }
-
- // 调用微信支付
- try {
- await Taro.requestPayment({
- timeStamp: result.timeStamp,
- nonceStr: result.nonceStr,
- package: result.package,
- signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256',
- paySign: result.paySign,
- });
- } catch (payError: any) {
- const msg: string = payError?.errMsg || payError?.message || '';
- if (msg.includes('cancel')) {
- // 用户主动取消,不当作“失败”强提示
- Taro.showToast({
- title: '已取消支付',
- icon: 'none'
- });
- return;
- }
- throw payError;
- }
-
- // 支付成功
- Taro.showToast({
- title: '支付成功',
- icon: 'success'
- });
-
- // 若因后端不支持“重新发起支付”而降级“重新创建订单”,则原订单会遗留为待支付,支付成功后自动将其标记为已取消,避免列表堆积
- if (usedFallbackCreate && effectiveOrder.orderId && !effectiveOrder.payStatus && effectiveOrder.orderStatus !== 2) {
- try {
- await updateShopOrder({
- orderId: effectiveOrder.orderId,
- orderStatus: 2
- });
- } catch (e) {
- console.warn('自动取消旧待支付订单失败:', e);
- }
- }
-
- // 重新加载订单列表
- void reload(true);
- props.onReload?.();
-
- // 跳转到订单页面
- setTimeout(() => {
- Taro.navigateTo({url: '/user/order/order'});
- }, 2000);
-
- } catch (error: any) {
- console.error('支付失败:', error);
-
- let errorMessage = '支付失败,请重试';
- const rawMsg: string = error?.errMsg || error?.message || '';
- if (rawMsg) {
- if (rawMsg.includes('cancel')) {
- errorMessage = '用户取消支付';
- } else if (rawMsg.includes('余额不足')) {
- errorMessage = '账户余额不足';
- } else {
- errorMessage = rawMsg;
- }
- }
-
- Taro.showToast({
- title: errorMessage,
- icon: 'error'
- });
- } finally {
- Taro.hideLoading();
- setPayingOrderId(null);
- }
- };
-
- useEffect(() => {
- void reload(true); // 首次加载、tab切换或搜索条件变化时重置页码
- }, [reload]);
-
- // 监听外部statusFilter变化,同步更新tab索引
- useEffect(() => {
- // 获取当前的statusFilter,如果未定义则默认为-1(全部)
- const currentStatusFilter = props.searchParams?.statusFilter !== undefined
- ? props.searchParams.statusFilter
- : -1;
-
- const tab = tabs.find(t => t.statusFilter === currentStatusFilter);
- const targetTabIndex = tab ? tab.index : 0;
-
- console.log('外部statusFilter变化:', {
- statusFilter: currentStatusFilter,
- originalStatusFilter: props.searchParams?.statusFilter,
- currentTapIndex: tapIndex,
- targetTabIndex,
- shouldUpdate: targetTabIndex !== tapIndex
- });
-
- if (targetTabIndex !== tapIndex) {
- setTapIndex(targetTabIndex);
- // 不需要调用reload,因为tapIndex变化会触发reload
- }
- }, [props.searchParams?.statusFilter, tapIndex]); // 监听statusFilter变化
-
-
- return (
- <>
- {
- console.log('Tab切换:', paneKey, '类型:', typeof paneKey);
- const newTapIndex = Number(paneKey);
- setTapIndex(newTapIndex);
-
- // 通知父组件更新 searchParams.statusFilter
- const currentTab = tabs.find(tab => tab.index === newTapIndex);
- if (currentTab && props.onSearchParamsChange) {
- const newSearchParams = {
- ...props.searchParams,
- statusFilter: currentTab.statusFilter
- };
- console.log('通知父组件更新searchParams:', newSearchParams);
- props.onSearchParamsChange(newSearchParams);
- }
- }}
- >
- {
- tabs?.map((item, _) => {
- return (
-
- )
- })
- }
-
- {
- setHasMore(true)
- await reload(true)
- props.onReload?.()
- }}
- headHeight={60}
- >
-
- {error ? (
-
- {error}
- reload(true)}
- >
- 重新加载
-
-
- ) : (
- {
-
- }}
- onScrollToUpper={() => {
-
- }}
- loadingText={
- <>
- 加载中
- >
- }
- loadMoreText={
- list.length === 0 ? (
-
- ) : (
-
- 没有更多了
-
- )
- }
- >
-
- {/* 订单列表 */}
- {list.length > 0 && list
- ?.filter((item) => {
- const orderStatus = toNum(item.orderStatus);
- // “待收货”不展示退款中的/已退款订单,这些订单统一放到“退货/售后”
- if (tapIndex === 3 && (orderStatus === 4 || orderStatus === 6)) {
- return false;
- }
- // “退货/售后”只展示售后相关状态
- if (tapIndex === 5) {
- return orderStatus === 4 || orderStatus === 5 || orderStatus === 6 || orderStatus === 7;
- }
- return true;
- })
- ?.map((item, index) => {
- const orderStatus = toNum(item.orderStatus);
- const deliveryStatus = toNum(item.deliveryStatus);
- return (
- Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
-
-
-
- {
- e.stopPropagation();
- copyText(`${item.orderNo}`)
- }}>{item.orderNo}
-
- {/* 右侧显示合并的状态和倒计时 */}
-
- {!item.payStatus && orderStatus !== 2 ? (
-
- ) : (
- getOrderStatusText(item)
- )}
-
-
- {dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}
-
- {/* 商品信息 */}
-
- {item.orderGoods && item.orderGoods.length > 0 ? (
- item.orderGoods.map((goods, goodsIndex) => (
-
-
-
-
- {goods.goodsName || (goods as any).goodsTitle || (goods as any).title || item.title || '订单商品'}
-
- {(goods.spec || (goods as any).specInfo) && (
- 规格:{goods.spec || (goods as any).specInfo}
- )}
- 数量:{(goods as any).quantity ?? goods.totalNum}
-
- x
- ¥{goods.price || (goods as any).payPrice}
-
- ))
- ) : (
-
-
-
- {item.title || '订单商品'}
- {item.totalNum}件商品
-
-
- )}
-
-
- 实付金额:¥{item.payPrice}
-
- {/* 操作按钮 */}
- {!isReadOnly && (
-
- {/* 待付款状态:显示取消订单和立即支付 */}
- {(!item.payStatus) && orderStatus !== 2 && (
-
- {
- e.stopPropagation();
- void cancelOrder(item);
- }}>取消订单
- {(!item.createTime || !isPaymentExpired(item.createTime, 24)) && (
- {
- e.stopPropagation();
- void payOrder(item);
- }}>立即支付
- )}
-
- )}
-
- {/* 待发货状态:显示申请退款 */}
- {item.payStatus && isWithinRefundWindow(item.payTime, 60) && deliveryStatus === 10 && orderStatus !== 2 && orderStatus !== 4 && orderStatus !== 6 && orderStatus !== 7 && !isOrderCompleted(item) && (
- {
- e.stopPropagation();
- applyRefund(item);
- }}>申请退款
- )}
-
- {/* 待收货状态:显示查看物流和确认收货 */}
- {deliveryStatus === 20 && (!item.riderId || Number(item.riderId) === 0 || !!item.sendEndTime) && orderStatus !== 2 && orderStatus !== 6 && !isOrderCompleted(item) && (
-
- {/* {*/}
- {/* e.stopPropagation();*/}
- {/* viewLogistics(item);*/}
- {/*}}>查看物流*/}
- {
- e.stopPropagation();
- confirmReceive(item);
- }}>确认收货
-
- )}
-
- {/* 退款/售后状态:显示查看进度和撤销申请 */}
- {(orderStatus === 4 || orderStatus === 7) && (
-
- {/* {*/}
- {/* e.stopPropagation();*/}
- {/* viewProgress(item);*/}
- {/*}}>查看进度*/}
-
- )}
-
-
- )}
-
- |
- )
- })}
-
- )}
-
-
-
- {/* 取消订单确认对话框 */}
-
-
- {/* 确认收货确认对话框 */}
-
- >
- )
-}
-
-export default OrderList
diff --git a/src/user/order/evaluate/index.config.ts b/src/user/order/evaluate/index.config.ts
deleted file mode 100644
index 0adc673..0000000
--- a/src/user/order/evaluate/index.config.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default definePageConfig({
- navigationBarTitleText: ''
-})
diff --git a/src/user/order/evaluate/index.scss b/src/user/order/evaluate/index.scss
deleted file mode 100644
index a56b27c..0000000
--- a/src/user/order/evaluate/index.scss
+++ /dev/null
@@ -1,191 +0,0 @@
-.evaluate-page {
- min-height: 100vh;
- background-color: #f5f5f5;
- padding-bottom: 80px;
-
- .loading-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 50vh;
-
- .loading-text {
- margin-top: 16px;
- color: #666;
- }
- }
-
- .order-info {
- background: white;
- padding: 16px 20px;
- margin-bottom: 12px;
-
- .order-no {
- display: block;
- color: #666;
- margin-bottom: 4px;
- }
-
- .order-tip {
- display: block;
- font-weight: 500;
- color: #333;
- }
- }
-
- .goods-list {
- .goods-item {
- background: white;
- margin-bottom: 12px;
- padding: 20px;
-
- .goods-info {
- display: flex;
- margin-bottom: 20px;
- padding-bottom: 16px;
- border-bottom: 1px solid #f0f0f0;
-
- .goods-image {
- width: 80px;
- height: 80px;
- border-radius: 8px;
- margin-right: 12px;
- flex-shrink: 0;
- }
-
- .goods-detail {
- flex: 1;
-
- .goods-name {
- display: block;
- font-weight: 500;
- color: #333;
- line-height: 1.4;
- margin-bottom: 8px;
- }
-
- .goods-sku {
- display: block;
- color: #999;
- margin-bottom: 8px;
- }
-
- .goods-price {
- display: block;
- font-weight: 600;
- color: #ff6b35;
- }
- }
- }
-
- .rating-section {
- margin-bottom: 20px;
-
- .rating-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
-
- .rating-label {
- font-weight: 500;
- color: #333;
- }
-
- .rating-text {
- color: #ff6b35;
- font-weight: 500;
- }
- }
- }
-
- .content-section {
- margin-bottom: 20px;
-
- .content-label {
- display: block;
- font-weight: 500;
- color: #333;
- margin-bottom: 12px;
- }
- }
-
- .image-section {
- .image-label {
- display: block;
- font-weight: 500;
- color: #333;
- margin-bottom: 12px;
- }
- }
- }
- }
-
- .submit-section {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- background: white;
- padding: 12px 16px;
- border-top: 1px solid #f0f0f0;
- z-index: 100;
- }
-}
-
-/* NutUI 组件样式覆盖 */
-.evaluate-page {
- .nut-rate {
- .nut-rate-item {
- margin-right: 8px;
- }
- }
-
- .nut-textarea {
- border: 1px solid #e8e8e8;
- border-radius: 8px;
- padding: 12px;
- background: #fafafa;
-
- &:focus {
- border-color: #1890ff;
- background: white;
- }
- }
-
- .nut-uploader {
- .nut-uploader-slot {
- border: 2px dashed #e8e8e8;
- border-radius: 8px;
- background: #fafafa;
-
- &:hover {
- border-color: #1890ff;
- }
- }
-
- .nut-uploader-preview {
- border-radius: 8px;
- overflow: hidden;
- }
- }
-}
-
-/* 适配不同屏幕尺寸 */
-@media (max-width: 375px) {
- .evaluate-page {
- .goods-list {
- .goods-item {
- padding: 16px;
-
- .goods-info {
- .goods-image {
- width: 70px;
- height: 70px;
- }
- }
- }
- }
- }
-}
diff --git a/src/user/order/evaluate/index.tsx b/src/user/order/evaluate/index.tsx
deleted file mode 100644
index ae9d28f..0000000
--- a/src/user/order/evaluate/index.tsx
+++ /dev/null
@@ -1,304 +0,0 @@
-import React, { useState, useEffect } from 'react'
-import Taro, { useRouter } from '@tarojs/taro'
-import { View, Text, Image } from '@tarojs/components'
-import {
- Rate,
- TextArea,
- Button,
- Uploader,
- Loading,
- Empty
-} from '@nutui/nutui-react-taro'
-import './index.scss'
-
-// 订单商品信息
-interface OrderGoods {
- goodsId: string
- goodsName: string
- goodsImage: string
- goodsPrice: number
- goodsNum: number
- skuInfo?: string
-}
-
-// 评价信息
-interface EvaluateInfo {
- goodsId: string
- rating: number // 评分 1-5
- content: string // 评价内容
- images: string[] // 评价图片
- isAnonymous: boolean // 是否匿名
-}
-
-const EvaluatePage: React.FC = () => {
- const router = useRouter()
- const { orderId, orderNo } = router.params
-
- const [loading, setLoading] = useState(true)
- const [submitting, setSubmitting] = useState(false)
- const [orderGoods, setOrderGoods] = useState([])
- const [evaluates, setEvaluates] = useState | | | | | | | |