Bläddra i källkod

清空代码-初始化yudao

落日晚风 1 år sedan
förälder
incheckning
4cb256af6c
100 ändrade filer med 5068 tillägg och 7929 borttagningar
  1. 673 756
      pages.json
  2. 0 77
      pages/app/score-shop.vue
  3. 339 311
      pages/coupon/detail.vue
  4. 204 247
      pages/coupon/list.vue
  5. 48 112
      pages/goods/comment/add.vue
  6. 0 115
      pages/goods/components/detail/detail-cell-params.vue
  7. 0 121
      pages/goods/components/detail/detail-cell-service.vue
  8. 1 1
      pages/goods/components/detail/detail-cell.vue
  9. 2 1
      pages/goods/components/detail/detail-content-card.vue
  10. 1 0
      pages/goods/components/detail/detail-navbar.vue
  11. 1 0
      pages/goods/components/detail/detail-progress.vue
  12. 22 24
      pages/goods/components/detail/detail-tabbar.vue
  13. 1 1
      pages/goods/components/list/list-goods-card.vue
  14. 1 0
      pages/goods/components/list/list-navbar.vue
  15. 2 8
      pages/goods/groupon.vue
  16. 52 45
      pages/goods/index.vue
  17. 115 103
      pages/goods/list.vue
  18. 0 368
      pages/goods/score.vue
  19. 0 4
      pages/goods/seckill.vue
  20. 0 3
      pages/index/index.vue
  21. 8 2
      pages/index/search.vue
  22. 268 229
      pages/order/aftersale/apply.vue
  23. 87 103
      pages/order/aftersale/detail.vue
  24. 61 106
      pages/order/aftersale/list.vue
  25. 10 32
      pages/order/aftersale/log-item.vue
  26. 10 26
      pages/order/aftersale/log.vue
  27. 381 407
      pages/order/confirm.vue
  28. 601 668
      pages/order/detail.vue
  29. 0 84
      pages/order/dispatch/content.vue
  30. 0 104
      pages/order/express/list.vue
  31. 37 49
      pages/order/express/log.vue
  32. 0 329
      pages/order/invoice.vue
  33. 123 250
      pages/order/list.vue
  34. 54 124
      pages/pay/index.vue
  35. 61 59
      pages/pay/result.vue
  36. 7 4
      pages/public/error.vue
  37. 0 226
      pages/public/feedback.vue
  38. 22 25
      pages/public/setting.vue
  39. 4 1
      pages/public/webview.vue
  40. 91 108
      pages/user/address/edit.vue
  41. 12 16
      pages/user/address/list.vue
  42. 59 70
      pages/user/goods-collect.vue
  43. 45 81
      pages/user/info.vue
  44. 0 277
      pages/user/invoice/edit.vue
  45. 0 82
      pages/user/invoice/list.vue
  46. 0 88
      pages/user/set.vue
  47. 362 337
      pages/user/wallet/money.vue
  48. 0 95
      sheep/api/app.js
  49. 1 17
      sheep/api/coupon.js
  50. 4 1
      sheep/api/index.js
  51. 49 0
      sheep/api/infra/file.js
  52. 45 0
      sheep/api/member/address.js
  53. 42 0
      sheep/api/member/auth.js
  54. 32 0
      sheep/api/member/user.js
  55. 0 6
      sheep/api/order.js
  56. 14 0
      sheep/api/pay/channel.js
  57. 22 0
      sheep/api/pay/order.js
  58. 26 0
      sheep/api/pay/wallet.js
  59. 16 7
      sheep/api/product/category.js
  60. 44 0
      sheep/api/product/favorite.js
  61. 43 1
      sheep/api/promotion/coupon.js
  62. 13 0
      sheep/api/system/area.js
  63. 45 0
      sheep/api/trade/afterSale.js
  64. 47 0
      sheep/api/trade/cart.js
  65. 14 0
      sheep/api/trade/config.js
  66. 103 0
      sheep/api/trade/order.js
  67. 0 112
      sheep/api/user.js
  68. 12 6
      sheep/components/s-address-item/s-address-item.vue
  69. 1 1
      sheep/components/s-auth-modal/components/account-login.vue
  70. 45 60
      sheep/components/s-auth-modal/components/change-password.vue
  71. 0 72
      sheep/components/s-auth-modal/components/change-username.vue
  72. 19 14
      sheep/components/s-auth-modal/components/reset-password.vue
  73. 14 10
      sheep/components/s-auth-modal/components/sms-login.vue
  74. 0 130
      sheep/components/s-auth-modal/components/sms-register.vue
  75. 3 24
      sheep/components/s-auth-modal/s-auth-modal.vue
  76. 2 4
      sheep/components/s-block-item/s-block-item.vue
  77. 10 9
      sheep/components/s-coupon-get/s-coupon-get.vue
  78. 196 186
      sheep/components/s-coupon-list/s-coupon-list.vue
  79. 1 13
      sheep/components/s-float-menu/s-float-menu.vue
  80. 16 15
      sheep/components/s-goods-column/s-goods-column.vue
  81. 2 9
      sheep/components/s-goods-item/s-goods-item.vue
  82. 2 1
      sheep/components/s-goods-scroll/s-goods-scroll.vue
  83. 7 7
      sheep/components/s-hotzone-block/s-hotzone-block.vue
  84. 0 96
      sheep/components/s-invoice-item/s-invoice-item.vue
  85. 4 4
      sheep/components/s-menu-button/s-menu-button.vue
  86. 2 6
      sheep/components/s-menu-tools/s-menu-tools.vue
  87. 1 1
      sheep/components/s-notice-block/s-notice-block.vue
  88. 0 202
      sheep/components/s-score-block/s-score-block.vue
  89. 0 459
      sheep/components/s-score-card/s-score-card.vue
  90. 1 1
      sheep/components/s-statusbar/s-statusbar.vue
  91. 22 36
      sheep/components/s-title-block/s-title-block.vue
  92. 178 20
      sheep/hooks/useGoods.js
  93. 18 9
      sheep/hooks/useModal.js
  94. 99 33
      sheep/platform/pay.js
  95. 8 5
      sheep/request/index.js
  96. 4 3
      sheep/request2/index.js
  97. 48 53
      sheep/store/cart.js
  98. 2 1
      sheep/store/modal.js
  99. 24 15
      sheep/store/user.js
  100. 2 1
      sheep/ui/su-region-picker/su-region-picker.vue

+ 673 - 756
pages.json

@@ -1,758 +1,675 @@
 {
-	"easycom": {
-		"autoscan": true,
-		"custom": {
-			"^s-(.*)": "@/sheep/components/s-$1/s-$1.vue",
-			"^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue"
-		}
-	},
-	"pages": [{
-			"path": "pages/index/index",
-			"aliasPath": "/",
-			"style": {
-				"navigationBarTitleText": "首页",
-				"enablePullDownRefresh": true
-			},
-			"meta": {
-				"auth": false,
-				"sync": true,
-				"title": "首页",
-				"group": "商城"
-			}
-		},
-		{
-			"path": "pages/index/user",
-			"style": {
-				"navigationBarTitleText": "个人中心",
-				"enablePullDownRefresh": true
-			},
-			"meta": {
-				"sync": true,
-				"title": "个人中心",
-				"group": "商城"
-			}
-		},
-		{
-			"path": "pages/index/category",
-			"style": {
-				"navigationBarTitleText": "商品分类"
-			},
-			"meta": {
-				"sync": true,
-				"title": "商品分类",
-				"group": "商城"
-			}
-		},
-		{
-			"path": "pages/index/cart",
-			"style": {
-				"navigationBarTitleText": "购物车"
-			},
-			"meta": {
-				"sync": true,
-				"title": "购物车",
-				"group": "商城"
-			}
-		},
-		{
-			"path": "pages/index/login",
-			"style": {
-				"navigationBarTitleText": "登录"
-			}
-		},
-		{
-			"path": "pages/index/search",
-			"style": {
-				"navigationBarTitleText": "搜索"
-			},
-			"meta": {
-				"sync": true,
-				"title": "搜索",
-				"group": "商城"
-			}
-		},
-		{
-			"path": "pages/index/page",
-			"style": {
-				"navigationBarTitleText": ""
-			},
-			"meta": {
-				"auth": false,
-				"sync": true,
-				"title": "自定义页面",
-				"group": "商城"
-			}
-		}
-	],
-	"subPackages": [{
-			"root": "pages/goods",
-			"pages": [{
-					"path": "index",
-					"style": {
-						"navigationBarTitleText": "商品详情"
-					},
-					"meta": {
-						"sync": true,
-						"title": "普通商品",
-						"group": "商品"
-					}
-				},
-				{
-					"path": "groupon",
-					"style": {
-						"navigationBarTitleText": "拼团商品"
-					},
-					"meta": {
-						"sync": true,
-						"title": "拼团商品",
-						"group": "商品"
-					}
-				},
+  "easycom": {
+    "autoscan": true,
+    "custom": {
+      "^s-(.*)": "@/sheep/components/s-$1/s-$1.vue",
+      "^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue"
+    }
+  },
+  "pages": [
+    {
+      "path": "pages/index/index",
+      "aliasPath": "/",
+      "style": {
+        "navigationBarTitleText": "首页",
+        "enablePullDownRefresh": true
+      },
+      "meta": {
+        "auth": false,
+        "sync": true,
+        "title": "首页",
+        "group": "商城"
+      }
+    },
+    {
+      "path": "pages/index/user",
+      "style": {
+        "navigationBarTitleText": "个人中心",
+        "enablePullDownRefresh": true
+      },
+      "meta": {
+        "sync": true,
+        "title": "个人中心",
+        "group": "商城"
+      }
+    },
+    {
+      "path": "pages/index/category",
+      "style": {
+        "navigationBarTitleText": "商品分类"
+      },
+      "meta": {
+        "sync": true,
+        "title": "商品分类",
+        "group": "商城"
+      }
+    },
+    {
+      "path": "pages/index/cart",
+      "style": {
+        "navigationBarTitleText": "购物车"
+      },
+      "meta": {
+        "sync": true,
+        "title": "购物车",
+        "group": "商城"
+      }
+    },
+    {
+      "path": "pages/index/login",
+      "style": {
+        "navigationBarTitleText": "登录"
+      }
+    },
+    {
+      "path": "pages/index/search",
+      "style": {
+        "navigationBarTitleText": "搜索"
+      },
+      "meta": {
+        "sync": true,
+        "title": "搜索",
+        "group": "商城"
+      }
+    },
+    {
+      "path": "pages/index/page",
+      "style": {
+        "navigationBarTitleText": ""
+      },
+      "meta": {
+        "auth": false,
+        "sync": true,
+        "title": "自定义页面",
+        "group": "商城"
+      }
+    }
+  ],
+  "subPackages": [
+    {
+      "root": "pages/goods",
+      "pages": [
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "商品详情"
+          },
+          "meta": {
+            "sync": true,
+            "title": "普通商品",
+            "group": "商品"
+          }
+        },
+        {
+          "path": "groupon",
+          "style": {
+            "navigationBarTitleText": "拼团商品"
+          },
+          "meta": {
+            "sync": true,
+            "title": "拼团商品",
+            "group": "商品"
+          }
+        },
 
-				{
-					"path": "seckill",
-					"style": {
-						"navigationBarTitleText": "秒杀商品"
-					},
-					"meta": {
-						"sync": true,
-						"title": "秒杀商品",
-						"group": "商品"
-					}
-				},
-
-				{
-					"path": "score",
-					"style": {
-						"navigationBarTitleText": "积分商品"
-					},
-					"meta": {
-						"sync": true,
-						"title": "积分商品",
-						"group": "商品"
-					}
-				},
-				{
-					"path": "list",
-					"style": {
-						"navigationBarTitleText": "商品列表"
-					},
-					"meta": {
-						"sync": true,
-						"title": "商品列表",
-						"group": "商品"
-					}
-				},
-				{
-					"path": "comment/add",
-					"style": {
-						"navigationBarTitleText": "评价商品"
-					},
-					"meta": {
-						"auth": true
-					}
-				},
-				{
-					"path": "comment/list",
-					"style": {
-						"navigationBarTitleText": "商品评价"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/order",
-			"pages": [{
-					"path": "detail",
-					"style": {
-						"navigationBarTitleText": "订单详情"
-					},
-					"meta": {
-						"auth": true,
-						"title": "订单详情"
-					}
-				},
-				{
-					"path": "confirm",
-					"style": {
-						"navigationBarTitleText": "确认订单"
-					},
-					"meta": {
-						"auth": true,
-						"title": "确认订单"
-					}
-				},
-				{
-					"path": "list",
-					"style": {
-						"navigationBarTitleText": "我的订单",
-						"enablePullDownRefresh": true
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "用户订单",
-						"group": "订单中心"
-					}
-				},
-				{
-					"path": "invoice",
-					"style": {
-						"navigationBarTitleText": "发票详情"
-					},
-					"meta": {
-						"auth": true,
-						"title": "发票详情"
-					}
-				},
-				{
-					"path": "dispatch/content",
-					"style": {
-						"navigationBarTitleText": "发货内容"
-					},
-					"meta": {
-						"auth": true,
-						"title": "发货内容"
-					}
-				},
-				{
-					"path": "aftersale/apply",
-					"style": {
-						"navigationBarTitleText": "申请售后"
-					},
-					"meta": {
-						"auth": true,
-						"title": "申请售后"
-					}
-				},
-				{
-					"path": "aftersale/list",
-					"style": {
-						"navigationBarTitleText": "售后列表"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "售后订单",
-						"group": "订单中心"
-					}
-				},
-				{
-					"path": "aftersale/detail",
-					"style": {
-						"navigationBarTitleText": "售后详情"
-					},
-					"meta": {
-						"auth": true,
-						"title": "售后详情"
-					}
-				},
-				{
-					"path": "aftersale/log",
-					"style": {
-						"navigationBarTitleText": "售后进度"
-					},
-					"meta": {
-						"auth": true,
-						"title": "售后进度"
-					}
-				},
-				{
-					"path": "express/log",
-					"style": {
-						"navigationBarTitleText": "物流轨迹"
-					},
-					"meta": {
-						"auth": true,
-						"title": "物流轨迹"
-					}
-				},
-				{
-					"path": "express/list",
-					"style": {
-						"navigationBarTitleText": "订单包裹"
-					},
-					"meta": {
-						"auth": true,
-						"title": "订单包裹"
-					}
-				}, {
-					"path": "aftersale/eturn-delivery",
-					"style": {
-						"navigationBarTitleText": "退货物流"
-					},
-					"meta": {
-						"auth": true
-						// "title": "退货物流"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/user",
-			"pages": [{
-					"path": "info",
-					"style": {
-						"navigationBarTitleText": "我的信息"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "用户信息",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "goods-collect",
-					"style": {
-						"navigationBarTitleText": "我的收藏"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "商品收藏",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "goods-log",
-					"style": {
-						"navigationBarTitleText": "我的足迹"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "浏览记录",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "address/list",
-					"style": {
-						"navigationBarTitleText": "收货地址"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "地址管理",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "address/edit",
-					"style": {
-						"navigationBarTitleText": "编辑地址"
-					},
-					"meta": {
-						"auth": true,
-						"title": "编辑地址"
-					}
-				},
-				{
-					"path": "invoice/list",
-					"style": {
-						"navigationBarTitleText": "发票管理"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "发票管理",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "invoice/edit",
-					"style": {
-						"navigationBarTitleText": "编辑发票"
-					},
-					"meta": {
-						"auth": true,
-						"title": "编辑发票"
-					}
-				},
-				{
-					"path": "wallet/money",
-					"style": {
-						"navigationBarTitleText": "我的余额"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "用户余额",
-						"group": "用户中心"
-					}
-				},
-				{
-					"path": "wallet/commission",
-					"style": {
-						"navigationBarTitleText": "我的佣金"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "用户佣金",
-						"group": "分销中心"
-					}
-				},
-				{
-					"path": "wallet/score",
-					"style": {
-						"navigationBarTitleText": "我的积分"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "用户积分",
-						"group": "用户中心"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/commission",
-			"pages": [{
-					"path": "index",
-					"style": {
-						"navigationBarTitleText": "分销"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "分销中心",
-						"group": "分销商城"
-					}
-				},
-				{
-					"path": "apply",
-					"style": {
-						"navigationBarTitleText": "申请分销商"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "申请分销商",
-						"group": "分销商城"
-					}
-				},
-				{
-					"path": "goods",
-					"style": {
-						"navigationBarTitleText": "推广商品"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "推广商品",
-						"group": "分销商城"
-					}
-				},
-				{
-					"path": "order",
-					"style": {
-						"navigationBarTitleText": "分销订单"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "分销订单",
-						"group": "分销商城"
-					}
-				},
-				{
-					"path": "share-log",
-					"style": {
-						"navigationBarTitleText": "分享记录"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "分享记录",
-						"group": "分销商城"
-					}
-				},
-				{
-					"path": "team",
-					"style": {
-						"navigationBarTitleText": "我的团队"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "我的团队",
-						"group": "分销商城"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/app",
-			"pages": [{
-					"path": "sign",
-					"style": {
-						"navigationBarTitleText": "签到中心"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "签到中心",
-						"group": "应用"
-					}
-				},
-				{
-					"path": "score-shop",
-					"style": {
-						"navigationBarTitleText": "积分商城"
-					},
-					"meta": {
-						"auth": false,
-						"sync": true,
-						"title": "积分商城",
-						"group": "应用"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/public",
-			"pages": [{
-					"path": "setting",
-					"style": {
-						"navigationBarTitleText": "系统设置"
-					},
-					"meta": {
-						"sync": true,
-						"title": "系统设置",
-						"group": "通用"
-					}
-				},
-				{
-					"path": "feedback",
-					"style": {
-						"navigationBarTitleText": "问题反馈"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "问题反馈",
-						"group": "通用"
-					}
-				},
-				{
-					"path": "richtext",
-					"style": {
-						"navigationBarTitleText": "富文本"
-					},
-					"meta": {
-						"sync": true,
-						"title": "富文本",
-						"group": "通用"
-					}
-				},
-				{
-					"path": "faq",
-					"style": {
-						"navigationBarTitleText": "常见问题"
-					},
-					"meta": {
-						"sync": true,
-						"title": "常见问题",
-						"group": "通用"
-					}
-				},
-				{
-					"path": "error",
-					"style": {
-						"navigationBarTitleText": "错误页面"
-					}
-				},
-				{
-					"path": "webview",
-					"style": {
-						"navigationBarTitleText": ""
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/coupon",
-			"pages": [{
-					"path": "list",
-					"style": {
-						"navigationBarTitleText": "领券中心"
-					},
-					"meta": {
-						"sync": true,
-						"title": "领券中心",
-						"group": "优惠券"
-					}
-				},
-				{
-					"path": "detail",
-					"style": {
-						"navigationBarTitleText": "优惠券"
-					},
-					"meta": {
-						"auth": false,
-						"sync": true,
-						"title": "优惠券详情",
-						"group": "优惠券"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/chat",
-			"pages": [{
-				"path": "index",
-				"style": {
-					"navigationBarTitleText": "客服"
-				},
-				"meta": {
-					"auth": true,
-					"sync": true,
-					"title": "客服",
-					"group": "客服"
-				}
-			}]
-		},
-		{
-			"root": "pages/pay",
-			"pages": [{
-					"path": "index",
-					"style": {
-						"navigationBarTitleText": "收银台"
-					}
-				},
-				{
-					"path": "result",
-					"style": {
-						"navigationBarTitleText": "支付结果"
-					}
-				},
-				{
-					"path": "recharge",
-					"style": {
-						"navigationBarTitleText": "充值余额"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "充值余额",
-						"group": "支付"
-					}
-				},
-				{
-					"path": "recharge-log",
-					"style": {
-						"navigationBarTitleText": "充值记录"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "充值记录",
-						"group": "支付"
-					}
-				},
-				{
-					"path": "withdraw",
-					"style": {
-						"navigationBarTitleText": "申请提现"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "申请提现",
-						"group": "支付"
-					}
-				},
-				{
-					"path": "withdraw-log",
-					"style": {
-						"navigationBarTitleText": "提现记录"
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "提现记录",
-						"group": "支付"
-					}
-				}
-			]
-		},
-		{
-			"root": "pages/activity",
-			"pages": [{
-					"path": "groupon/detail",
-					"style": {
-						"navigationBarTitleText": "拼团详情"
-					}
-				},
-				{
-					"path": "groupon/order",
-					"style": {
-						"navigationBarTitleText": "我的拼团",
-						"enablePullDownRefresh": true
-					},
-					"meta": {
-						"auth": true,
-						"sync": true,
-						"title": "拼团订单",
-						"group": "营销活动"
-					}
-				},
-				{
-					"path": "index",
-					"style": {
-						"navigationBarTitleText": "营销商品"
-					},
-					"meta": {
-						"sync": true,
-						"title": "营销商品",
-						"group": "营销活动"
-					}
-				},
-				{
-					"path": "groupon/list",
-					"style": {
-						"navigationBarTitleText": "拼团活动"
-					},
-					"meta": {
-						"sync": true,
-						"title": "拼团活动",
-						"group": "营销活动"
-					}
-				},
-				{
-					"path": "seckill/list",
-					"style": {
-						"navigationBarTitleText": "秒杀活动"
-					},
-					"meta": {
-						"sync": true,
-						"title": "秒杀活动",
-						"group": "营销活动"
-					}
-				}
-			]
-		}
-	],
-	"globalStyle": {
-		"navigationBarTextStyle": "black",
-		"navigationBarTitleText": "星品购",
-		"navigationBarBackgroundColor": "#FFFFFF",
-		"backgroundColor": "#FFFFFF",
-		"navigationStyle": "custom"
-	},
-	"tabBar": {
-		"list": [{
-				"pagePath": "pages/index/index"
-			},
-			{
-				"pagePath": "pages/index/cart"
-			},
-			{
-				"pagePath": "pages/index/user"
-			}
-		]
-	}
-}
+        {
+          "path": "seckill",
+          "style": {
+            "navigationBarTitleText": "秒杀商品"
+          },
+          "meta": {
+            "sync": true,
+            "title": "秒杀商品",
+            "group": "商品"
+          }
+        },
+        {
+          "path": "list",
+          "style": {
+            "navigationBarTitleText": "商品列表"
+          },
+          "meta": {
+            "sync": true,
+            "title": "商品列表",
+            "group": "商品"
+          }
+        },
+        {
+          "path": "comment/add",
+          "style": {
+            "navigationBarTitleText": "评价商品"
+          },
+          "meta": {
+            "auth": true
+          }
+        },
+        {
+          "path": "comment/list",
+          "style": {
+            "navigationBarTitleText": "商品评价"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/order",
+      "pages": [
+        {
+          "path": "detail",
+          "style": {
+            "navigationBarTitleText": "订单详情"
+          },
+          "meta": {
+            "auth": true,
+            "title": "订单详情"
+          }
+        },
+        {
+          "path": "confirm",
+          "style": {
+            "navigationBarTitleText": "确认订单"
+          },
+          "meta": {
+            "auth": true,
+            "title": "确认订单"
+          }
+        },
+        {
+          "path": "list",
+          "style": {
+            "navigationBarTitleText": "我的订单",
+            "enablePullDownRefresh": true
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "用户订单",
+            "group": "订单中心"
+          }
+        },
+        {
+          "path": "aftersale/apply",
+          "style": {
+            "navigationBarTitleText": "申请售后"
+          },
+          "meta": {
+            "auth": true,
+            "title": "申请售后"
+          }
+        },
+        {
+          "path": "aftersale/list",
+          "style": {
+            "navigationBarTitleText": "售后列表"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "售后订单",
+            "group": "订单中心"
+          }
+        },
+        {
+          "path": "aftersale/detail",
+          "style": {
+            "navigationBarTitleText": "售后详情"
+          },
+          "meta": {
+            "auth": true,
+            "title": "售后详情"
+          }
+        },
+        {
+          "path": "aftersale/log",
+          "style": {
+            "navigationBarTitleText": "售后进度"
+          },
+          "meta": {
+            "auth": true,
+            "title": "售后进度"
+          }
+        },
+        {
+          "path": "express/log",
+          "style": {
+            "navigationBarTitleText": "物流轨迹"
+          },
+          "meta": {
+            "auth": true,
+            "title": "物流轨迹"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/user",
+      "pages": [
+        {
+          "path": "info",
+          "style": {
+            "navigationBarTitleText": "我的信息"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "用户信息",
+            "group": "用户中心"
+          }
+        },
+        {
+          "path": "goods-collect",
+          "style": {
+            "navigationBarTitleText": "我的收藏"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "商品收藏",
+            "group": "用户中心"
+          }
+        },
+        {
+          "path": "goods-log",
+          "style": {
+            "navigationBarTitleText": "我的足迹"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "浏览记录",
+            "group": "用户中心"
+          }
+        },
+        {
+          "path": "address/list",
+          "style": {
+            "navigationBarTitleText": "收货地址"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "地址管理",
+            "group": "用户中心"
+          }
+        },
+        {
+          "path": "address/edit",
+          "style": {
+            "navigationBarTitleText": "编辑地址"
+          },
+          "meta": {
+            "auth": true,
+            "title": "编辑地址"
+          }
+        },
+        {
+          "path": "wallet/money",
+          "style": {
+            "navigationBarTitleText": "我的余额"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "用户余额",
+            "group": "用户中心"
+          }
+        },
+        {
+          "path": "wallet/commission",
+          "style": {
+            "navigationBarTitleText": "我的佣金"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "用户佣金",
+            "group": "分销中心"
+          }
+        },
+        {
+          "path": "wallet/score",
+          "style": {
+            "navigationBarTitleText": "我的积分"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "用户积分",
+            "group": "用户中心"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/commission",
+      "pages": [
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "分销"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "分销中心",
+            "group": "分销商城"
+          }
+        },
+        {
+          "path": "apply",
+          "style": {
+            "navigationBarTitleText": "申请分销商"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "申请分销商",
+            "group": "分销商城"
+          }
+        },
+        {
+          "path": "goods",
+          "style": {
+            "navigationBarTitleText": "推广商品"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "推广商品",
+            "group": "分销商城"
+          }
+        },
+        {
+          "path": "order",
+          "style": {
+            "navigationBarTitleText": "分销订单"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "分销订单",
+            "group": "分销商城"
+          }
+        },
+        {
+          "path": "share-log",
+          "style": {
+            "navigationBarTitleText": "分享记录"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "分享记录",
+            "group": "分销商城"
+          }
+        },
+        {
+          "path": "team",
+          "style": {
+            "navigationBarTitleText": "我的团队"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "我的团队",
+            "group": "分销商城"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/app",
+      "pages": [
+        {
+          "path": "sign",
+          "style": {
+            "navigationBarTitleText": "签到中心"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "签到中心",
+            "group": "应用"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/public",
+      "pages": [
+        {
+          "path": "setting",
+          "style": {
+            "navigationBarTitleText": "系统设置"
+          },
+          "meta": {
+            "sync": true,
+            "title": "系统设置",
+            "group": "通用"
+          }
+        },
+        {
+          "path": "richtext",
+          "style": {
+            "navigationBarTitleText": "富文本"
+          },
+          "meta": {
+            "sync": true,
+            "title": "富文本",
+            "group": "通用"
+          }
+        },
+        {
+          "path": "faq",
+          "style": {
+            "navigationBarTitleText": "常见问题"
+          },
+          "meta": {
+            "sync": true,
+            "title": "常见问题",
+            "group": "通用"
+          }
+        },
+        {
+          "path": "error",
+          "style": {
+            "navigationBarTitleText": "错误页面"
+          }
+        },
+        {
+          "path": "webview",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/coupon",
+      "pages": [
+        {
+          "path": "list",
+          "style": {
+            "navigationBarTitleText": "领券中心"
+          },
+          "meta": {
+            "sync": true,
+            "title": "领券中心",
+            "group": "优惠券"
+          }
+        },
+        {
+          "path": "detail",
+          "style": {
+            "navigationBarTitleText": "优惠券"
+          },
+          "meta": {
+            "auth": false,
+            "sync": true,
+            "title": "优惠券详情",
+            "group": "优惠券"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/chat",
+      "pages": [
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "客服"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "客服",
+            "group": "客服"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/pay",
+      "pages": [
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "收银台"
+          }
+        },
+        {
+          "path": "result",
+          "style": {
+            "navigationBarTitleText": "支付结果"
+          }
+        },
+        {
+          "path": "recharge",
+          "style": {
+            "navigationBarTitleText": "充值余额"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "充值余额",
+            "group": "支付"
+          }
+        },
+        {
+          "path": "recharge-log",
+          "style": {
+            "navigationBarTitleText": "充值记录"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "充值记录",
+            "group": "支付"
+          }
+        },
+        {
+          "path": "withdraw",
+          "style": {
+            "navigationBarTitleText": "申请提现"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "申请提现",
+            "group": "支付"
+          }
+        },
+        {
+          "path": "withdraw-log",
+          "style": {
+            "navigationBarTitleText": "提现记录"
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "提现记录",
+            "group": "支付"
+          }
+        }
+      ]
+    },
+    {
+      "root": "pages/activity",
+      "pages": [
+        {
+          "path": "groupon/detail",
+          "style": {
+            "navigationBarTitleText": "拼团详情"
+          }
+        },
+        {
+          "path": "groupon/order",
+          "style": {
+            "navigationBarTitleText": "我的拼团",
+            "enablePullDownRefresh": true
+          },
+          "meta": {
+            "auth": true,
+            "sync": true,
+            "title": "拼团订单",
+            "group": "营销活动"
+          }
+        },
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "营销商品"
+          },
+          "meta": {
+            "sync": true,
+            "title": "营销商品",
+            "group": "营销活动"
+          }
+        },
+        {
+          "path": "groupon/list",
+          "style": {
+            "navigationBarTitleText": "拼团活动"
+          },
+          "meta": {
+            "sync": true,
+            "title": "拼团活动",
+            "group": "营销活动"
+          }
+        },
+        {
+          "path": "seckill/list",
+          "style": {
+            "navigationBarTitleText": "秒杀活动"
+          },
+          "meta": {
+            "sync": true,
+            "title": "秒杀活动",
+            "group": "营销活动"
+          }
+        }
+      ]
+    }
+  ],
+  "globalStyle": {
+    "navigationBarTextStyle": "black",
+    "navigationBarTitleText": "星品购",
+    "navigationBarBackgroundColor": "#FFFFFF",
+    "backgroundColor": "#FFFFFF",
+    "navigationStyle": "custom"
+  },
+  "tabBar": {
+    "list": [
+      {
+        "pagePath": "pages/index/index"
+      },
+      {
+        "pagePath": "pages/index/cart"
+      },
+      {
+        "pagePath": "pages/index/user"
+      }
+    ]
+  }
+}

+ 0 - 77
pages/app/score-shop.vue

@@ -1,77 +0,0 @@
-<!-- 页面  -->
-<template>
-  <s-layout title="积分商城">
-    <view class="ss-p-20">
-      <view v-for="item in state.pagination.data" :key="item.id" class="ss-m-b-20">
-        <s-score-card
-          size="sl"
-          :data="item"
-          priceColor="#FF3000"
-          @tap="sheep.$router.go('/pages/goods/score', { id: item.id })"
-        ></s-score-card>
-      </view>
-    </view>
-    <s-empty
-      v-if="state.pagination.total === 0"
-      icon="/static/goods-empty.png"
-      text="暂无积分商品"
-    ></s-empty>
-    <uni-load-more
-      v-if="state.pagination.total > 0"
-      :status="state.loadStatus"
-      :content-text="{
-        contentdown: '上拉加载更多',
-      }"
-      @tap="loadmore"
-    />
-  </s-layout>
-</template>
-<script setup>
-  import sheep from '@/sheep';
-  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
-  import { reactive } from 'vue';
-  import _ from 'lodash';
-
-  const state = reactive({
-    pagination: {
-      data: [],
-      current_page: 1,
-      total: 1,
-      last_page: 1,
-    },
-    loadStatus: '',
-  });
-  async function getData(page = 1, list_rows = 5) {
-    state.loadStatus = 'loading';
-    let res = await sheep.$api.app.scoreShop.list({
-      list_rows,
-      page,
-    });
-    if (res.error === 0) {
-        let couponlist = _.concat(state.pagination.data, res.data.data);
-        state.pagination = {
-          ...res.data,
-          data: couponlist,
-        };
-      if (state.pagination.current_page < state.pagination.last_page) {
-        state.loadStatus = 'more';
-      } else {
-        state.loadStatus = 'noMore';
-      }
-    }
-  }
-  // 加载更多
-  function loadmore() {
-    if (state.loadStatus !== 'noMore') {
-      getData(state.pagination.current_page + 1);
-    }
-  }
-
-  // 上拉加载更多
-  onReachBottom(() => {
-    loadmore();
-  });
-  onLoad(() => {
-    getData();
-  });
-</script>

+ 339 - 311
pages/coupon/detail.vue

@@ -1,350 +1,378 @@
 <!-- 优惠券详情  -->
 <template>
-	<s-layout title="优惠券详情">
-		<view class="bg-white">
-			<!-- 详情卡片 -->
-			<view class="detail-wrap ss-p-20">
-				<view class="detail-box">
-					<view class="tag-box ss-flex ss-col-center ss-row-center">
-						<image class="tag-image" :src="sheep.$url.static('/static/img/shop/app/coupon_icon.png')"
-							mode="aspectFit"></image>
-					</view>
-					<view class="top ss-flex-col ss-col-center">
-						<view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.list.name }}</view>
-						<view class="subtitle ss-m-b-50">满{{ state.list.enough }}减{{ state.list.amount }}</view>
-						<!-- (state.list.get_status != 'can_get' && state.list.get_status != 'can_use') ||
-						 state.userCouponId -->
-						<button class="ss-reset-button ss-m-b-30" :class="
-                state.list.get_status == 'can_get' || state.list.get_status == 'can_use'
-                  ? 'use-btn'
-                  : 'disable-btn' " :disabled="state.list.status_text=='已过期'" @click="getCoupon">
-							<!-- {{ state.list.get_status_text }} -->
-							立即使用
-						</button>
-						<view class="time ss-m-y-30" v-if="
-                state.list.get_status == 'can_get' ||
-                state.list.get_status == 'cannot_get' ||
-                state.list.get_status == 'get_over'
-              ">
-							领取时间:{{ state.list.get_start_time }}至{{ state.list.get_end_time }}
-						</view>
-						<view class="time ss-m-y-30" v-else>
-							有效期:{{ state.list.use_start_time }}至{{ state.list.use_end_time }}
-						</view>
-						<view class="coupon-line ss-m-t-14"></view>
-					</view>
-					<view class="bottom">
-						<view class="type ss-flex ss-col-center ss-row-between ss-p-x-30">
-							<view>优惠券类型</view>
-							<view>{{ state.list.type_text }}</view>
-						</view>
-						<uni-collapse>
-							<uni-collapse-item title="优惠券说明" v-if="state.list.description">
-								<view class="content ss-p-b-20">
-									<text class="des ss-p-l-30">{{ state.list.description }}</text>
-								</view>
-							</uni-collapse-item>
-						</uni-collapse>
-					</view>
-				</view>
-			</view>
-			<!-- 适用商品 -->
-			<view class="all-user ss-flex ss-row-center ss-col-center" v-if="state.list.use_scope == 'all_use'">
-				{{ state.list.use_scope_text }}
-			</view>
+  <s-layout title="优惠券详情">
+    <view class="bg-white">
+      <!-- 详情卡片 -->
+      <view class="detail-wrap ss-p-20">
+        <view class="detail-box">
+          <view class="tag-box ss-flex ss-col-center ss-row-center">
+            <image
+              class="tag-image"
+              :src="sheep.$url.static('/static/img/shop/app/coupon_icon.png')"
+              mode="aspectFit"
+            />
+          </view>
+          <view class="top ss-flex-col ss-col-center">
+            <view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.coupon.name }}</view>
+            <view class="subtitle ss-m-b-50">
+              满 {{ fen2yuan(state.coupon.usePrice) }} 元,
+              {{ state.coupon.discountType === 1
+                ? '减 ' + fen2yuan(state.coupon.discountPrice) + ' 元'
+                : '打 ' + state.coupon.discountPercent / 10.0 + ' 折' }}
+            </view>
+            <button
+              class="ss-reset-button ss-m-b-30"
+              :class="state.coupon.canTake || state.coupon.status === 1
+                  ? 'use-btn' // 优惠劵模版(可领取)、优惠劵(可使用)
+                  : 'disable-btn'
+              "
+              :disabled="!state.coupon.canTake"
+              @click="getCoupon"
+            >
+              <text v-if="state.id > 0">{{ state.coupon.canTake ? '立即领取' : '已领取' }}</text>
+              <text v-else>
+                {{ state.coupon.status === 1 ? '立即使用' : state.coupon.status === 2 ? '已使用' : '已过期' }}
+              </text>
+            </button>
+            <view class="time ss-m-y-30" v-if="state.coupon.validityType === 2">
+              有效期:领取后 {{ state.coupon.fixedEndTerm }} 天内可用
+            </view>
+            <view class="time ss-m-y-30" v-else>
+              有效期: {{ sheep.$helper.timeFormat(state.coupon.validStartTime, 'yyyy-mm-dd') }} 至
+              {{ sheep.$helper.timeFormat(state.coupon.validEndTime, 'yyyy-mm-dd') }}
+            </view>
+            <view class="coupon-line ss-m-t-14"></view>
+          </view>
+          <view class="bottom">
+            <view class="type ss-flex ss-col-center ss-row-between ss-p-x-30">
+              <view>优惠券类型</view>
+              <view>{{ state.coupon.discountType === 1 ? '满减券' : '折扣券' }}</view>
+            </view>
+            <!-- TODO 芋艿:可优化,增加优惠劵的描述 -->
+            <uni-collapse>
+              <uni-collapse-item title="优惠券说明" v-if="state.coupon.description">
+                <view class="content ss-p-b-20">
+                  <text class="des ss-p-l-30">{{ state.coupon.description }}</text>
+                </view>
+              </uni-collapse-item>
+            </uni-collapse>
+          </view>
+        </view>
+      </view>
 
-			<su-sticky v-else bgColor="#fff">
-				<view class="goods-title ss-p-20">{{ state.list.use_scope_text }}</view>
-				<su-tabs :scrollable="true" :list="state.tabMaps" @change="onTabsChange" :current="state.currentTab"
-					v-if="state.list.use_scope == 'category'"></su-tabs>
-			</su-sticky>
-			<view v-if="state.list.use_scope == 'goods' || state.list.use_scope == 'disabled_goods'">
-				<view v-for="(item, index) in state.list.items_value" :key="index">
-					<s-goods-column class="ss-m-20" size="lg" :data="item"
-						:titleColor="props.goodsFieldsStyle?.title?.color"
-						:subTitleColor="props.goodsFieldsStyle?.subtitle?.color"
-						@click="sheep.$router.go('/pages/goods/index', { id: item.id })" :goodsFields="{
+      <!-- 适用商品 -->
+      <view
+        class="all-user ss-flex ss-row-center ss-col-center"
+        v-if="state.coupon.productScope === 1"
+      >
+        全场通用
+      </view>
+
+      <su-sticky v-else bgColor="#fff">
+        <view class="goods-title ss-p-20">
+          {{ state.coupon.productScope === 2 ? '指定商品可用' : '指定分类可用' }}
+        </view>
+        <su-tabs
+          :scrollable="true"
+          :list="state.tabMaps"
+          @change="onTabsChange"
+          :current="state.currentTab"
+          v-if="state.coupon.productScope === 3"
+        />
+      </su-sticky>
+      <!-- 指定商品 -->
+      <view v-if="state.coupon.productScope === 2">
+        <view v-for="(item, index) in state.pagination.list" :key="index">
+          <s-goods-column
+            class="ss-m-20"
+            size="lg"
+            :data="item"
+            @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
+            :goodsFields="{
               title: { show: true },
               subtitle: { show: true },
               price: { show: true },
               original_price: { show: true },
               sales: { show: true },
               stock: { show: false },
-            }" :buttonShow="state.list.use_scope != 'disabled_goods'"></s-goods-column>
-				</view>
-			</view>
-			<view v-if="state.list.use_scope == 'category'">
-				<view v-for="(item, index) in state.pagination.data" :key="index">
-					<s-goods-column class="ss-m-20" size="lg" :data="item"
-						:titleColor="props.goodsFieldsStyle?.title?.color"
-						:subTitleColor="props.goodsFieldsStyle?.subtitle?.color"
-						@click="sheep.$router.go('/pages/goods/index', { id: item.id })" :goodsFields="{
+            }"
+          />
+        </view>
+      </view>
+      <!-- 指定分类 -->
+      <view v-if="state.coupon.productScope === 3">
+        <view v-for="(item, index) in state.pagination.list" :key="index">
+          <s-goods-column
+            class="ss-m-20"
+            size="lg"
+            :data="item"
+            @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
+            :goodsFields="{
               title: { show: true },
               subtitle: { show: true },
               price: { show: true },
               original_price: { show: true },
               sales: { show: true },
               stock: { show: false },
-            }" :buttonShow="state.list.use_scope != 'disabled_goods'"></s-goods-column>
-				</view>
-			</view>
-			<uni-load-more v-if="state.pagination.total > 0 && state.list.use_scope == 'category'"
-				:status="state.loadStatus" :content-text="{
+            }"
+          ></s-goods-column>
+        </view>
+      </view>
+      <uni-load-more
+        v-if="state.pagination.total > 0 && state.coupon.productScope === 3"
+        :status="state.loadStatus"
+        :content-text="{
           contentdown: '上拉加载更多',
-        }" @tap="loadmore" />
-			<s-empty v-if="state.list.use_scope == 'category' && state.pagination.total === 0" paddingTop="0"
-				icon="/static/soldout-empty.png" text="暂无商品">
-			</s-empty>
-		</view>
-	</s-layout>
+        }"
+        @tap="loadMore"
+      />
+      <s-empty
+        v-if="state.coupon.productScope === 3 && state.pagination.total === 0"
+        paddingTop="0"
+        icon="/static/soldout-empty.png"
+        text="暂无商品"
+      />
+    </view>
+  </s-layout>
 </template>
 
 <script setup>
-	import sheep from '@/sheep';
-	import {
-		onLoad,
-		onReachBottom
-	} from '@dcloudio/uni-app';
-	import {
-		reactive
-	} from 'vue';
-	import _ from 'lodash';
+  import sheep from '@/sheep';
+  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+  import { reactive } from 'vue';
+  import _ from 'lodash';
+  import CouponApi from '@/sheep/api/promotion/coupon';
+  import { fen2yuan } from '@/sheep/hooks/useGoods';
+  import SpuApi from '@/sheep/api/product/spu';
+  import CategoryApi from '@/sheep/api/product/category';
+  import { resetPagination } from '@/sheep/util';
+
+  const state = reactive({
+    id: 0, // 优惠劵模版编号 templateId
+    couponId: 0, // 用户优惠劵编号 couponId
+    coupon: {}, // 优惠劵信息
+
+    pagination: {
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 1,
+    },
+    categoryId: 0, // 选中的商品分类编号
+    tabMaps: [], // 指定分类时,每个分类构成一个 tab
+    currentTab: 0, // 选中的 tabMaps 下标
+    loadStatus: '',
+  });
+
+  function onTabsChange(e) {
+    resetPagination(state.pagination);
+    state.currentTab = e.index;
+    state.categoryId = e.value;
+    getGoodsListByCategory();
+  }
+
+  async function getGoodsListByCategory() {
+    state.loadStatus = 'loading';
+    const { code, data } = await SpuApi.getSpuPage({
+      categoryId: state.categoryId,
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = _.concat(state.pagination.list, data.list);
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+  }
 
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
-	};
-	const state = reactive({
-		list: {},
-		couponId: 0,
-		userCouponId: 0,
-		pagination: {
-			data: [],
-			current_page: 1,
-			total: 1,
-			last_page: 1,
-		},
-		tabMaps: [],
-		loadStatus: '',
-		categoryId: 0,
-	});
+  // 获得商品列表,指定商品范围
+  async function getGoodsListById() {
+    const { data, code } = await SpuApi.getSpuListByIds(state.coupon.productScopeValues.join(','));
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = data;
+  }
 
-	// 接收参数
-	const props = defineProps({
-		includes: {
-			type: Array,
-			default () {
-				return [];
-			},
-		},
-		list: {
-			type: Array,
-			default: () => [],
-		},
-		goodsFieldsStyle: {
-			type: Object,
-			default () {},
-		},
-		buyData: {
-			type: Object,
-			default () {},
-		},
-	});
+  // 获得分类列表
+  async function getCategoryList() {
+    const { data, code } = await CategoryApi.getCategoryListByIds(state.coupon.productScopeValues.join(','));
+    if (code !== 0) {
+      return;
+    }
+    state.tabMaps = data.map((category) => ({ name: category.name, value: category.id }));
+    // 加载第一个分类的商品列表
+    if (state.tabMaps.length > 0) {
+      state.categoryId = state.tabMaps[0].value;
+      await getGoodsListByCategory();
+    }
+  }
 
-	function onTabsChange(e) {
-		state.pagination = pagination;
-		state.currentTab = e.index;
-		state.categoryId = e.value;
-		getGoodsList(state.categoryId);
-	}
-	async function getGoodsList(categoryId, page = 1, list_rows = 5) {
-		state.loadStatus = 'loading';
-		const res = await sheep.$api.goods.list({
-			category_id: categoryId,
-			list_rows,
-			page,
-			is_category_deep: false,
-		});
-		if (res.error === 0) {
-			let couponlist = _.concat(state.pagination.data, res.data.data);
-			state.pagination = {
-				...res.data,
-				data: couponlist,
-			};
-			if (state.pagination.current_page < state.pagination.last_page) {
-				state.loadStatus = 'more';
-			} else {
-				state.loadStatus = 'noMore';
-			}
-		}
-	}
-	async function getCoupon() {
-		const {
-			error,
-			msg
-		} = await sheep.$api.coupon.get(state.couponId);
-		if (error === 0) {
-			uni.showToast({
-				title: msg,
-			});
-			setTimeout(() => {
-				getCouponContent(state.couponId, state.userCouponId);
-			}, 1000);
-		}
-	}
-	async function getCouponContent(id, c) {
-		const {
-			data
-		} = await sheep.$api.coupon.detail(id, c);
-		state.list = data;
-		data.items_value.forEach((i) => {
-			state.tabMaps.push({
-				name: i.name,
-				value: i.id
-			});
-		});
-		state.pagination = pagination;
-		if (state.list.use_scope == 'category') {
-			getGoodsList(state.tabMaps[0].value);
-		}
-	}
-	// 加载更多
-	function loadmore() {
-		if (state.loadStatus !== 'noMore') {
-			getGoodsList(state.categoryId, state.pagination.current_page + 1);
-		}
-	}
-	onLoad((options) => {
-		if (options.data) {
-			let data = JSON.parse(options.data);
-			console.log(data);
-			state.list = data;
-			return;
-		}
-		state.couponId = options.id;
-		state.userCouponId = options.user_coupon_id;
-		getCouponContent(state.couponId, state.userCouponId);
-	});
-	// 上拉加载更多
-	onReachBottom(() => {
-		loadmore();
-	});
+  // 领取优惠劵
+  async function getCoupon() {
+    const { code } = await CouponApi.takeCoupon(state.id);
+    if (code !== 0) {
+      return;
+    }
+    uni.showToast({
+      title: '领取成功',
+    });
+    setTimeout(() => {
+      getCouponContent();
+    }, 1000);
+  }
+
+  // 加载优惠劵信息
+  async function getCouponContent() {
+    const { code, data } = state.id > 0 ? await CouponApi.getCouponTemplate(state.id)
+      : await CouponApi.getCoupon(state.couponId);
+    if (code !== 0) {
+      return;
+    }
+    state.coupon = data;
+    // 不同指定范围,加载不同数据
+    if (state.coupon.productScope === 2) {
+      await getGoodsListById();
+    } else if (state.coupon.productScope === 3) {
+      await getCategoryList();
+    }
+  }
+
+  // 加载更多
+  function loadMore() {
+    if (state.loadStatus === 'noMore') {
+      return;
+    }
+    state.pagination.pageNo++;
+    getGoodsListByCategory();
+  }
+
+  onLoad((options) => {
+    state.id = options.id;
+    state.couponId = options.couponId;
+    getCouponContent(state.id, state.couponId);
+  });
+
+  // 上拉加载更多
+  onReachBottom(() => {
+    loadMore();
+  });
 </script>
 
 <style lang="scss" scoped>
-	.goods-title {
-		font-size: 34rpx;
-		font-weight: bold;
-		color: #333333;
-	}
-
-	.detail-wrap {
-		background: linear-gradient(180deg,
-				var(--ui-BG-Main),
-				var(--ui-BG-Main-gradient),
-				var(--ui-BG-Main),
-				#fff);
-	}
+  .goods-title {
+    font-size: 34rpx;
+    font-weight: bold;
+    color: #333333;
+  }
 
-	.detail-box {
-		// background-color: var(--ui-BG);
-		border-radius: 6rpx;
-		position: relative;
-		margin-top: 100rpx;
+  .detail-wrap {
+    background: linear-gradient(
+      180deg,
+      var(--ui-BG-Main),
+      var(--ui-BG-Main-gradient),
+      var(--ui-BG-Main),
+      #fff
+    );
+  }
 
-		.tag-box {
-			width: 140rpx;
-			height: 140rpx;
-			background: var(--ui-BG);
-			border-radius: 50%;
-			position: absolute;
-			top: -70rpx;
-			left: 50%;
-			z-index: 6;
-			transform: translateX(-50%);
+  .detail-box {
+    // background-color: var(--ui-BG);
+    border-radius: 6rpx;
+    position: relative;
+    margin-top: 100rpx;
+    .tag-box {
+      width: 140rpx;
+      height: 140rpx;
+      background: var(--ui-BG);
+      border-radius: 50%;
+      position: absolute;
+      top: -70rpx;
+      left: 50%;
+      z-index: 6;
+      transform: translateX(-50%);
 
-			.tag-image {
-				width: 104rpx;
-				height: 104rpx;
-				border-radius: 50%;
-			}
-		}
+      .tag-image {
+        width: 104rpx;
+        height: 104rpx;
+        border-radius: 50%;
+      }
+    }
 
-		.top {
-			background-color: #fff;
-			border-radius: 20rpx 20rpx 0 0;
-			-webkit-mask: radial-gradient(circle at 16rpx 100%, #0000 16rpx, red 0) -16rpx;
-			padding: 110rpx 0 0 0;
-			position: relative;
-			z-index: 5;
+    .top {
+      background-color: #fff;
+      border-radius: 20rpx 20rpx 0 0;
+      -webkit-mask: radial-gradient(circle at 16rpx 100%, #0000 16rpx, red 0) -16rpx;
+      padding: 110rpx 0 0 0;
+      position: relative;
+      z-index: 5;
 
-			.title {
-				font-size: 40rpx;
-				color: #333;
-				font-weight: bold;
-			}
+      .title {
+        font-size: 40rpx;
+        color: #333;
+        font-weight: bold;
+      }
 
-			.subtitle {
-				font-size: 28rpx;
-				color: #333333;
-			}
+      .subtitle {
+        font-size: 28rpx;
+        color: #333333;
+      }
 
-			.use-btn {
-				width: 386rpx;
-				height: 80rpx;
-				line-height: 80rpx;
-				background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-				border-radius: 40rpx;
-				color: $white;
-			}
+      .use-btn {
+        width: 386rpx;
+        height: 80rpx;
+        line-height: 80rpx;
+        background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+        border-radius: 40rpx;
+        color: $white;
+      }
 
-			.disable-btn {
-				width: 386rpx;
-				height: 80rpx;
-				line-height: 80rpx;
-				background: #e5e5e5;
-				border-radius: 40rpx;
-				color: $white;
-			}
+      .disable-btn {
+        width: 386rpx;
+        height: 80rpx;
+        line-height: 80rpx;
+        background: #e5e5e5;
+        border-radius: 40rpx;
+        color: $white;
+      }
 
-			.time {
-				font-size: 26rpx;
-				font-weight: 400;
-				color: #999999;
-			}
+      .time {
+        font-size: 26rpx;
+        font-weight: 400;
+        color: #999999;
+      }
 
-			.coupon-line {
-				width: 95%;
-				border-bottom: 2rpx solid #eeeeee;
-			}
-		}
+      .coupon-line {
+        width: 95%;
+        border-bottom: 2rpx solid #eeeeee;
+      }
+    }
 
-		.bottom {
-			background-color: #fff;
-			border-radius: 0 0 20rpx 20rpx;
-			-webkit-mask: radial-gradient(circle at 16rpx 0%, #0000 16rpx, red 0) -16rpx;
-			padding: 40rpx 30rpx;
+    .bottom {
+      background-color: #fff;
+      border-radius: 0 0 20rpx 20rpx;
+      -webkit-mask: radial-gradient(circle at 16rpx 0%, #0000 16rpx, red 0) -16rpx;
+      padding: 40rpx 30rpx;
 
-			.type {
-				height: 96rpx;
-				border-bottom: 2rpx solid #eeeeee;
-			}
-		}
+      .type {
+        height: 96rpx;
+        border-bottom: 2rpx solid #eeeeee;
+      }
+    }
 
-		.des {
-			font-size: 24rpx;
-			font-weight: 400;
-			color: #666666;
-		}
-	}
+    .des {
+      font-size: 24rpx;
+      font-weight: 400;
+      color: #666666;
+    }
+  }
 
-	.all-user {
-		width: 100%;
-		height: 300rpx;
-		font-size: 34rpx;
-		font-weight: bold;
-		color: #333333;
-	}
-</style>
+  .all-user {
+    width: 100%;
+    height: 300rpx;
+    font-size: 34rpx;
+    font-weight: bold;
+    color: #333333;
+  }
+</style>

+ 204 - 247
pages/coupon/list.vue

@@ -1,265 +1,222 @@
 <!-- 优惠券中心  -->
 <template>
-	<s-layout title="优惠券" :bgStyle="{ color: '#f2f2f2' }">
-		<su-sticky bgColor="#fff">
-			<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
-		</su-sticky>
-		<s-empty v-if="state.pagination.total === 0" icon="/static/coupon-empty.png" text="暂无优惠券"></s-empty>
-		<template v-if="state.currentTab == '0'">
-			<view v-for="item in state.pagination.list" :key="item.id">
-				<s-coupon-list :data="item" @tap="
-					            sheep.$router.go('/pages/coupon/detail', {
-					              data: JSON.stringify(item),
-					            })">
+  <s-layout title="优惠券" :bgStyle="{ color: '#f2f2f2' }">
+    <su-sticky bgColor="#fff">
+      <su-tabs
+        :list="tabMaps"
+        :scrollable="false"
+        @change="onTabsChange"
+        :current="state.currentTab"
+      />
+    </su-sticky>
+    <s-empty
+      v-if="state.pagination.total === 0"
+      icon="/static/coupon-empty.png"
+      text="暂无优惠券"
+    />
+    <!-- 情况一:领劵中心 -->
+    <template v-if="state.currentTab === '0'">
+      <view v-for="item in state.pagination.list" :key="item.id">
+        <s-coupon-list
+          :data="item"
+          @tap="sheep.$router.go('/pages/coupon/detail', { id: item.id })"
+        >
+          <template #default>
+            <button
+              class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
+              :class="!item.canTake ? 'border-btn' : ''"
+              @click.stop="getBuy(item.id)"
+              :disabled="!item.canTake"
+            >
+              {{ item.canTake ? '立即领取' : '已领取' }}
+            </button>
+          </template>
+        </s-coupon-list>
+      </view>
+    </template>
+    <!-- 情况二:我的优惠劵 -->
+    <template v-else>
+      <view v-for="item in state.pagination.list" :key="item.id">
+        <s-coupon-list
+          :data="item"
+          type="user"
+          @tap="
+            sheep.$router.go('/pages/coupon/detail', {
+              data: JSON.stringify(item),
+            })
+          "
+        >
+          <template #default>
+            <button
+              class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
+              :class=" item.status !== 1 ? 'disabled-btn': ''"
+              :disabled="item.status !== 1"
+              @click.stop="sheep.$router.go('/pages/coupon/detail', { couponId: item.id })"
+            >
+              {{ item.status === 1 ? '立即使用' : item.status === 2 ? '已使用' : '已过期' }}
+            </button>
+          </template>
+        </s-coupon-list>
+      </view>
+    </template>
 
-					<template #default>
-						<button class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
-							:class="item.get_status != 'can_get' ? 'border-btn' : ''" @click.stop="getBuy(item.id)"
-							:disabled="item.get_status != 'can_get'">
-							<!-- {{ item.status_text }} -->
-							{{item.status_text|| '立即使用' }}
-						</button>
-					</template>
-				</s-coupon-list>
-			</view>
-		</template>
-		<template v-else>
-			<view v-for="item in state.pagination.list" :key="item.id">
-				<s-coupon-list :data="item" type="user" @tap="
-					            sheep.$router.go('/pages/coupon/detail', {
-					              data: JSON.stringify(item),
-					            })
-					          ">
-
-					<template #default>
-						<button class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center" :class="
-                item.status == 'can_get' || item.status == 'can_use'
-                  ? ''
-                  : item.status == 'used' || item.status == 'expired'
-                  ? 'disabled-btn'
-                  : 'border-btn'
-              " :disabled="item.status != 'can_get' && item.status != 'can_use'" @click.stop="
-                sheep.$router.go('/pages/coupon/detail', {
-                  id: item.coupon_id,
-                  user_coupon_id: item.id,
-                })
-              ">
-							<!-- {{ item.status_text }} -->
-							{{item.status_text|| '立即使用' }}
-						</button>
-					</template>
-				</s-coupon-list>
-			</view>
-		</template>
-
-		<!-- <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
+    <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
         contentdown: '上拉加载更多',
-      }" @tap="loadmore" /> -->
-	</s-layout>
+      }" @tap="loadMore" />
+  </s-layout>
 </template>
 
 <script setup>
-	import sheep from '@/sheep';
-	import {
-		onLoad,
-		onReachBottom
-	} from '@dcloudio/uni-app';
-	import {
-		computed,
-		reactive
-	} from 'vue';
-	import _ from 'lodash';
+  import sheep from '@/sheep';
+  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+  import { reactive } from 'vue';
+  import _ from 'lodash';
+  import { resetPagination } from '@/sheep/util';
+  import CouponApi from '@/sheep/api/promotion/coupon';
+
+  // 数据
+  const state = reactive({
+    currentTab: 0, // 当前 tab
+    type: '1',
+    pagination: {
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 5
+    },
+    loadStatus: '',
+  });
+
+  const tabMaps = [
+    {
+      name: '领券中心',
+      value: 'all',
+    },
+    {
+      name: '已领取',
+      value: '1',
+    },
+    {
+      name: '已使用',
+      value: '2',
+    },
+    {
+      name: '已失效',
+      value: '3',
+    },
+  ];
 
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
-	};
-	// 数据
-	const state = reactive({
-		currentTab: 0,
-		pagination: {
-			data: [],
-			current_page: 1,
-			total: 1,
-			last_page: 1,
-		},
-		loadStatus: '',
-		type: '1',
-	});
+  // TODO yunai:
+  function onTabsChange(e) {
+    state.currentTab = e.index;
+    state.type = e.value;
+    resetPagination(state.pagination)
+    if (state.currentTab === 0) {
+    	getData();
+    } else {
+      getCoupon();
+    }
+  }
 
-	const tabMaps = [
-		// {
-		//   name: '领券中心',
-		//   value: 'all',
-		// },
-		{
-			name: '已领取',
-			value: '1',
-		},
-		{
-			name: '已使用',
-			value: '2',
-		},
-		{
-			name: '已失效',
-			value: '3',
-		},
-	];
+  // 获得优惠劵模版列表
+  async function getData() {
+    state.loadStatus = 'loading';
+    const { data, code } = await CouponApi.getCouponTemplatePage({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = _.concat(state.pagination.list, data.list);
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+  }
 
-	function onTabsChange(e) {
-		state.pagination = pagination
-		state.currentTab = e.index;
-		state.type = e.value;
-		// if (state.currentTab == 0) {
-		// 	getData();
-		// } else {
-		getCoupon();
-		// }
-	}
-	async function getData(page = 1, list_rows = 5) {
-		state.loadStatus = 'loading';
-		const res = await sheep.$api.coupon.list({
-			list_rows,
-			page
-		});
-		if (res.error === 0) {
-			let couponlist = _.concat(state.pagination.data, res.data.data);
-			state.pagination = {
-				...res.data,
-				data: couponlist,
-			};
-			if (state.pagination.current_page < state.pagination.last_page) {
-				state.loadStatus = 'more';
-			} else {
-				state.loadStatus = 'noMore';
-			}
-		}
-	}
+  // 获得我的优惠劵
+  async function getCoupon() {
+    state.loadStatus = 'loading';
+    const { data, code } = await CouponApi.getCouponPage({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
+      status: state.type
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = _.concat(state.pagination.list, data.list);
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+  }
 
-	async function getCoupon(page = 1, list_rows = 5) {
-		state.loadStatus = 'loading';
-		let res = await sheep.$api.coupon.userCoupon({
-			status: state.type,
-			pageSize: list_rows,
-			pageNo: page
-		});
-		if (res.code === 0) {
-			// 拦截修改数据
-			let obj = {
-				1: '可用',
-				2: '已用',
-				3: '过期'
-			}
-			let obj2 = {
-				1: '满减',
-				2: '折扣'
-			}
-			res.data.list = res.data.list.map(item => {
-				return {
-					...item,
-					enough: (item.usePrice / 100).toFixed(2),
-					amount: (item.discountPrice / 100).toFixed(2),
-					use_start_time: sheep.$helper.timeFormat(item.validStartTime, 'yyyy-mm-dd hh:MM:ss'),
-					use_end_time: sheep.$helper.timeFormat(item.validEndTime, 'yyyy-mm-dd hh:MM:ss'),
-					status_text: obj[item.status],
-					type_text: obj2[item.discountType]
-				}
-			});
-			if (page >= 2) {
-				let couponlist = _.concat(state.pagination.data, res.data.list);
+  // 领取优惠劵
+  async function getBuy(id) {
+    const { code } = await CouponApi.takeCoupon(id);
+    if (code !== 0) {
+      return;
+    }
+    uni.showToast({
+      title: '领取成功',
+    });
+    setTimeout(() => {
+      resetPagination(state.pagination);
+      getData();
+    }, 1000);
+  }
 
-				state.pagination = {
-					...res.data,
-					data: couponlist,
-				};
-				console.log(state.pagination, '拿到的优惠券数据');
-			} else {
-				state.pagination = res.data;
-				console.log(state.pagination, '拿到的优惠券数据');
-			}
-			// if (state.pagination.current_page < state.pagination.last_page) {
-			// 	state.loadStatus = 'more';
-			// } else {
-			// 	state.loadStatus = 'noMore';
-			// }
-		}
-	}
-	async function getBuy(id) {
-		const {
-			error,
-			msg
-		} = await sheep.$api.coupon.get(id);
-		if (error === 0) {
-			uni.showToast({
-				title: msg,
-			});
-			setTimeout(() => {
-				state.pagination = pagination
-				getData();
-			}, 1000);
-		}
-	}
+  // 加载更多
+  function loadMore() {
+    if (state.loadStatus === 'noMore') {
+      return;
+    }
+    state.pagination.pageNo++;
+    if (state.currentTab === 0) {
+      getData();
+    } else {
+      getCoupon();
+    }
+  }
 
-	// 加载更多
-	function loadmore() {
-		if (state.loadStatus !== 'noMore') {
-			if (state.currentTab == 0) {
-				getData(state.pagination.current_page + 1);
-			} else {
-				getCoupon(state.pagination.current_page + 1);
-			}
-		}
-	}
-	onLoad((Option) => {
-		// if (Option.type === 'all' || !Option.type) {
-		// 	getData();
-		// } else {
-		// state.type = Option.type;
-		// Option.type === 'geted' ?
-		// 	() :
-		// 	Option.type === 'used' ?
-		// 	(state.currentTab = 1 && state.type = 2) :
-		// 	(state.currentTab = 2 && state.type = 3);
+  onLoad((Option) => {
+    // 领劵中心
+    if (Option.type === 'all' || !Option.type) {
+      getData();
+    // 我的优惠劵
+    } else {
+      state.type = Option.type;
+      Option.type === 'geted'
+        ? (state.currentTab = 1)
+        : Option.type === 'used'
+          ? (state.currentTab = 2)
+          : (state.currentTab = 3);
+      getCoupon();
+    }
+  });
 
-		if (Option.type == 'geted') {
-			state.currentTab = 0
-			state.type = 1
-		} else if (Option.type == 'used') {
-			state.currentTab = 1
-			state.type = 2
-		} else {
-			state.currentTab = 2
-			state.type = 3
-		}
-		getCoupon();
-		// }
-	});
-	onReachBottom(() => {
-		loadmore();
-	});
+  onReachBottom(() => {
+    loadMore();
+  });
 </script>
 <style lang="scss" scoped>
-	.card-btn {
-		// width: 144rpx;
-		padding: 0 16rpx;
-		height: 50rpx;
-		border-radius: 40rpx;
-		background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-		color: #ffffff;
-		font-size: 24rpx;
-		font-weight: 400;
-	}
+  .card-btn {
+    // width: 144rpx;
+    padding: 0 16rpx;
+    height: 50rpx;
+    border-radius: 40rpx;
+    background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+    color: #ffffff;
+    font-size: 24rpx;
+    font-weight: 400;
+  }
 
-	.border-btn {
-		background: linear-gradient(90deg, var(--ui-BG-Main-opacity-4), var(--ui-BG-Main-light));
-		color: #fff !important;
-	}
+  .border-btn {
+    background: linear-gradient(90deg, var(--ui-BG-Main-opacity-4), var(--ui-BG-Main-light));
+    color: #fff !important;
+  }
 
-	.disabled-btn {
-		background: #cccccc;
-		background-color: #cccccc !important;
-		color: #fff !important;
-	}
-</style>
+  .disabled-btn {
+    background: #cccccc;
+    background-color: #cccccc !important;
+    color: #fff !important;
+  }
+</style>

+ 48 - 112
pages/goods/comment/add.vue

@@ -3,35 +3,34 @@
 	<s-layout title="评价">
 		<view>
 			<view v-for="(item, index) in state.orderInfo.items" :key="item.id">
-				<view v-if="item.btns.includes('comment')">
+				<view>
 					<view class="commont-from-wrap">
 						<!-- 评价商品 -->
-						<s-goods-item :img="item.goods_image" :title="item.goods_title" :skuText="item.goods_sku_text"
-							:price="item.goods_price" :num="item.goods_num"></s-goods-item>
+						<s-goods-item
+              :img="item.picUrl"
+              :title="item.spuName"
+              :skuText="item.properties.map((property) => property.valueName).join(' ')"
+							:price="item.payPrice"
+              :num="item.count"
+            />
 					</view>
 
 					<view class="form-item">
 						<!-- 评分 -->
 						<view class="star-box ss-flex ss-col-center">
-							<view class="star-title ss-m-r-40">
-								<!-- {{ rateMap[state.commentList[index].level] }} -->
-								商品质量
-							</view>
-							<uni-rate v-model="state.commentList[index].level" />
+							<view class="star-title ss-m-r-40">商品质量</view>
+							<uni-rate v-model="state.commentList[index].descriptionScores" />
 						</view>
 						<view class="star-box ss-flex ss-col-center">
-							<view class="star-title ss-m-r-40">
-								<!-- {{ rateMap[state.commentList[index].level] }} -->
-								服务态度
-							</view>
-							<uni-rate v-model="state.commentList[index].level2" />
+							<view class="star-title ss-m-r-40">服务态度</view>
+							<uni-rate v-model="state.commentList[index].benefitScores" />
 						</view>
 						<!-- 评价 -->
 						<view class="area-box">
 							<uni-easyinput :inputBorder="false" type="textarea" maxlength="120" autoHeight
 								v-model="state.commentList[index].content"
-								placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~"></uni-easyinput>
-
+								placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~" />
+              <!-- TODO 芋艿:文件上传 -->
 							<view class="img-box">
 								<s-uploader v-model:url="state.commentList[index].images" fileMediatype="image"
 									limit="9" mode="grid" :imageStyles="{ width: '168rpx', height: '168rpx' }" />
@@ -41,6 +40,7 @@
 				</view>
 			</view>
 		</view>
+    <!-- TODO 芋艿:是否匿名 -->
 
 		<su-fixed bottom placeholder>
 			<view class="foot_box ss-flex ss-row-center ss-col-center">
@@ -54,113 +54,49 @@
 
 <script setup>
 	import sheep from '@/sheep';
-	import {
-		onLoad
-	} from '@dcloudio/uni-app';
-	import {
-		computed,
-		reactive
-	} from 'vue';
+	import { onLoad } from '@dcloudio/uni-app';
+	import { reactive } from 'vue';
+  import OrderApi from '@/sheep/api/trade/order';
 
 	const state = reactive({
 		orderInfo: {},
 		commentList: [],
-		orderId: null
+		id: null
 	});
 
-	const rateMap = {
-		1: '糟糕',
-		2: '差评',
-		3: '一般',
-		4: '良好',
-		5: '好评',
-	};
-
 	async function onSubmit() {
-		// 对接商品评价
-		// console.log(state.orderInfo);
-		// return;
-		let obj = {
-			anonymous: false,
-			benefitScores: state.commentList[0].level2,
-			content: state.commentList[0].content,
-			descriptionScores: state.commentList[0].level,
-			orderItemId: state.commentList[0].item_id,
-			picUrls: 'https://t7.baidu.com/it/u=2531125946,3055766435&fm=193&f=GIF'
-		}
-		const {
-			code
-		} = await sheep.$api.order.comment(obj);
-		if (code === 0) {
-			sheep.$router.back();
-		}
+    // 顺序提交评论
+    for (const comment of state.commentList) {
+      await OrderApi.createOrderItemComment(comment);
+    }
+    // 都评论好,返回
+    sheep.$router.back();
 	}
 
 	onLoad(async (options) => {
-		let id = '';
-		if (options.orderSN) {
-			id = options.orderSN;
-		}
-		if (options.id) {
-			id = options.id;
-		}
-		if (options.orderId) {
-			state.orderId = options.orderId
-		}
-
-		const res = await sheep.$api.order.detail(id);
-		if (res.code === 0) {
-			let obj = {
-				10: ['待发货', '等待买家付款', ["apply_refund"]],
-				30: ['待评价', '等待买家评价', ["express", "comment"]]
-			}
-
-			res.data.status_text = obj[res.data.status][0];
-			res.data.status_desc = obj[res.data.status][1];
-			res.data.btns = obj[res.data.status][2];
-			res.data.address = {
-				province_name: res.data.receiverAreaName.split(' ')[0],
-				district_name: res.data.receiverAreaName.split(' ')[2],
-				city_name: res.data.receiverAreaName.split(' ')[1],
-				address: res.data.receiverDetailAddress,
-				consignee: res.data.receiverName,
-				mobile: res.data.receiverMobile,
-			}
-			res.data.pay_fee = res.data.payPrice / 100
-			res.data.create_time = sheep.$helper.timeFormat(res.data.createTime, 'yyyy-mm-dd hh:MM:ss')
-			res.data.order_sn = res.data.no
-			res.data.id = res.data.id
-			res.data.goods_amount = res.data.totalPrice / 100
-			res.data.dispatch_amount = res.data.deliveryPrice / 100
-			res.data.pay_types_text = res.data.payChannelName.split(',')
-			res.data.items = res.data.items.map(ite => {
-				return {
-					...ite,
-					btns: obj[res.data.status][2],
-					goods_title: ite.spuName,
-					goods_num: ite.count,
-					goods_price: ite.price / 100,
-					goods_image: ite.picUrl,
-					goods_sku_text: ite.properties.reduce((it0, it1) => it0 + it1.valueName + ' ', '')
-				}
-			})
-			if (res.data.btns.includes('comment')) {
-				state.orderInfo = res.data;
-				state.orderInfo.items.forEach((item) => {
-					if (item.btns.includes('comment')) {
-						state.commentList.push({
-							item_id: item.id,
-							level: 5,
-							content: '',
-							images: [],
-						});
-					}
-				});
-				console.log(state.orderInfo.items, '循环')
-				return;
-			}
-		}
-		sheep.$helper.toast('无待评价订单');
+    if (!options.id) {
+      sheep.$helper.toast(`缺少订单信息,请检查`);
+      return
+    }
+		state.id = options.id;
+
+		const { code, data } = await sheep.$api.order.detail(state.id);
+    if (code !== 0) {
+      sheep.$helper.toast('无待评价订单');
+      return
+    }
+    // 处理评论
+    data.items.forEach((item) => {
+      state.commentList.push({
+        anonymous: false,
+        orderItemId: item.id,
+        descriptionScores: 5,
+        benefitScores: 5,
+        content: '',
+        picUrls: []
+      });
+    });
+    state.orderInfo = data;
 	});
 </script>
 

+ 0 - 115
pages/goods/components/detail/detail-cell-params.vue

@@ -1,115 +0,0 @@
-<template>
-  <view>
-    <detail-cell
-      v-if="modelValue.length > 0"
-      label="参数"
-      :value="state.paramsTitle"
-      @click="state.show = true"
-    ></detail-cell>
-    <su-popup :show="state.show" round="10" :showClose="true" @close="close">
-      <view class="ss-modal-box bg-white">
-        <view class="modal-header">产品参数</view>
-        <scroll-view
-          class="modal-content ss-p-t-50"
-          scroll-y="true"
-          :scroll-with-animation="true"
-          :show-scrollbar="false"
-          @touchmove.stop
-        >
-          <view class="sale-item ss-flex ss-col-top" v-for="item in modelValue" :key="item.title">
-            <view class="item-title">{{ item.title }}</view>
-            <view class="item-value">{{ item.content }}</view>
-          </view>
-        </scroll-view>
-        <view class="modal-footer ss-flex ss-row-center ss-m-b-20">
-          <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="state.show = false"
-            >确定</button
-          >
-        </view>
-      </view>
-    </su-popup>
-  </view>
-</template>
-
-<script setup>
-  import { reactive, computed } from 'vue';
-
-  import detailCell from './detail-cell.vue';
-  const props = defineProps({
-    modelValue: {
-      type: Object,
-      default() {
-        return [];
-      },
-    },
-  });
-  const state = reactive({
-    show: false,
-    paramsTitle: computed(() => {
-      let titleArray = [];
-      props.modelValue.map((item) => {
-        titleArray.push(item.title);
-      });
-      return titleArray.join(' · ');
-    }),
-  });
-
-  function close() {
-    state.show = false;
-  }
-</script>
-
-<style lang="scss" scoped>
-  .ss-modal-box {
-    border-radius: 30rpx 30rpx 0 0;
-    max-height: 730rpx;
-
-    .modal-header {
-      position: relative;
-      padding: 30rpx 20rpx 40rpx;
-      font-size: 36rpx;
-      font-weight: bold;
-    }
-
-    .modal-content {
-      padding: 0 30rpx;
-      max-height: 500rpx;
-      box-sizing: border-box;
-
-      .sale-item {
-        margin-bottom: 24rpx;
-        padding-bottom: 24rpx;
-        border-bottom: 2rpx solid rgba(#dfdfdf, 0.4);
-
-        .item-title {
-          font-size: 28rpx;
-          font-weight: 500;
-          line-height: 42rpx;
-          width: 120rpx;
-          white-space: normal;
-        }
-
-        .item-value {
-          font-size: 26rpx;
-          font-weight: 400;
-          color: $dark-9;
-          line-height: 42rpx;
-          flex: 1;
-          margin-left: 20rpx;
-        }
-      }
-    }
-
-    .modal-footer {
-      height: 120rpx;
-
-      .save-btn {
-        width: 710rpx;
-        height: 80rpx;
-        border-radius: 40rpx;
-        background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-        color: $white;
-      }
-    }
-  }
-</style>

+ 0 - 121
pages/goods/components/detail/detail-cell-service.vue

@@ -1,121 +0,0 @@
-<template>
-  <view>
-    <detail-cell
-      v-if="modelValue.length > 0"
-      label="保障"
-      :value="state.paramsTitle"
-      @click="state.show = true"
-    >
-    </detail-cell>
-    <su-popup :show="state.show" round="10" :showClose="true" @close="state.show = false">
-      <view class="ss-modal-box">
-        <view class="modal-header">服务保障</view>
-        <scroll-view
-          class="modal-content"
-          scroll-y="true"
-          :scroll-with-animation="true"
-          :show-scrollbar="false"
-          @touchmove.stop
-        >
-          <view class="sale-item ss-flex ss-col-top" v-for="item in modelValue" :key="item.id">
-            <image
-              class="title-icon ss-m-r-14"
-              :src="sheep.$url.cdn(item.image)"
-              mode="aspectFill"
-            ></image>
-            <view class="title-box">
-              <view class="item-title ss-m-b-20">{{ item.name }}</view>
-              <view class="item-value">{{ item.description }}</view>
-            </view>
-          </view>
-        </scroll-view>
-        <view class="modal-footer ss-flex ss-row-center ss-m-b-20">
-          <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="state.show = false"
-            >确定</button
-          >
-        </view>
-      </view>
-    </su-popup>
-  </view>
-</template>
-
-<script setup>
-  import { reactive, computed } from 'vue';
-  import sheep from '@/sheep';
-  import detailCell from './detail-cell.vue';
-  const props = defineProps({
-    modelValue: {
-      type: Object,
-      default() {},
-    },
-  });
-
-  const state = reactive({
-    show: false,
-    paramsTitle: computed(() => {
-      let nameArray = [];
-      props.modelValue.map((item) => {
-        nameArray.push(item.name);
-      });
-      return nameArray.join(' · ');
-    }),
-  });
-</script>
-
-<style lang="scss" scoped>
-  .ss-modal-box {
-    border-radius: 30rpx 30rpx 0 0;
-    max-height: 730rpx;
-
-    .modal-header {
-      position: relative;
-      padding: 30rpx 20rpx 40rpx;
-      font-size: 36rpx;
-      font-weight: bold;
-    }
-
-    .modal-content {
-      padding: 0 30rpx;
-      max-height: 500rpx;
-      box-sizing: border-box;
-
-      .sale-item {
-        margin-bottom: 44rpx;
-
-        .title-icon {
-          width: 36rpx;
-          height: 36rpx;
-        }
-		.title-box{
-			flex: 1;
-		}
-
-        .item-title {
-          font-size: 28rpx;
-          font-weight: 500;
-          line-height: normal;
-        }
-
-        .item-value {
-          font-size: 26rpx;
-          font-weight: 400;
-          color: $dark-9;
-          line-height: 42rpx;
-        }
-      }
-    }
-
-    .modal-footer {
-      height: 120rpx;
-      background-color: #fff;
-
-      .save-btn {
-        width: 710rpx;
-        height: 80rpx;
-        border-radius: 40rpx;
-        background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-        color: $white;
-      }
-    }
-  }
-</style>

+ 1 - 1
pages/goods/components/detail/detail-cell.vue

@@ -1,3 +1,4 @@
+<!-- 商品详情:cell 组件 -->
 <template>
   <view class="detail-cell-wrap ss-flex ss-col-center ss-row-between" @tap="onClick">
     <view class="label-text">{{ label }}</view>
@@ -13,7 +14,6 @@
    * 详情 cell
    *
    */
-
   const props = defineProps({
     label: {
       type: String,

+ 2 - 1
pages/goods/components/detail/detail-content-card.vue

@@ -1,3 +1,4 @@
+<!-- 商品详情:描述卡片 -->
 <template>
   <view class="detail-content-card bg-white ss-m-x-20 ss-p-t-20">
     <view class="card-header ss-flex ss-col-center ss-m-b-30 ss-m-l-20">
@@ -5,7 +6,7 @@
       <view class="title ss-m-l-20 ss-m-r-20">详情</view>
     </view>
     <view class="card-content">
-      <mp-html :content="content"></mp-html>
+      <mp-html :content="content" />
     </view>
   </view>
 </template>

+ 1 - 0
pages/goods/components/detail/detail-navbar.vue

@@ -1,3 +1,4 @@
+<!-- 商品详情:商品/评价/详情的 nav -->
 <template>
   <su-fixed alway :bgStyles="{ background: '#fff' }" :val="0" noNav opacity :placeholder="false">
     <su-status-bar />

+ 1 - 0
pages/goods/components/detail/detail-progress.vue

@@ -1,3 +1,4 @@
+<!-- 秒杀商品:抢购进度 -->
 <template>
   <view class="ss-flex ss-col-center">
     <view class="progress-title ss-m-r-10"> 已抢{{ percent }}% </view>

+ 22 - 24
pages/goods/components/detail/detail-tabbar.vue

@@ -1,3 +1,4 @@
+<!-- 商品详情的底部导航 -->
 <template>
   <su-fixed bottom placeholder bg="bg-white">
     <view class="ui-tabbar-box">
@@ -12,7 +13,7 @@
               class="item-icon"
               :src="sheep.$url.static('/static/img/shop/goods/collect_1.gif')"
               mode="aspectFit"
-            ></image>
+            />
             <view class="item-title">已收藏</view>
           </block>
           <block v-else>
@@ -20,7 +21,7 @@
               class="item-icon"
               :src="sheep.$url.static('/static/img/shop/goods/collect_0.png')"
               mode="aspectFit"
-            ></image>
+            />
             <view class="item-title">收藏</view>
           </block>
         </view>
@@ -33,7 +34,7 @@
             class="item-icon"
             :src="sheep.$url.static('/static/img/shop/goods/message.png')"
             mode="aspectFit"
-          ></image>
+          />
           <view class="item-title">客服</view>
         </view>
         <view
@@ -45,7 +46,7 @@
             class="item-icon"
             :src="sheep.$url.static('/static/img/shop/goods/share.png')"
             mode="aspectFit"
-          ></image>
+          />
           <view class="item-title">分享</view>
         </view>
         <slot></slot>
@@ -63,13 +64,11 @@
    * @property {String} ui 			 			- 自定义样式Class
    * @property {Boolean} noFixed 		 			- 是否定位
    * @property {Boolean} topRadius 		 		- 上圆角
-   *
-   *
    */
-
-  import { computed, reactive } from 'vue';
+  import { reactive } from 'vue';
   import sheep from '@/sheep';
   import { showShareModal } from '@/sheep/hooks/useModal';
+  import FavoriteApi from '@/sheep/api/product/favorite';
 
   // 数据
   const state = reactive({});
@@ -114,25 +113,24 @@
       default: true,
     },
   });
-  const elStyles = computed(() => {
-    return {
-      'border-top-left-radius': props.topRadius + 'rpx',
-      'border-top-right-radius': props.topRadius + 'rpx',
-      overflow: 'hidden',
-    };
-  });
 
-  const tabbarheight = (e) => {
-    uni.setStorageSync('tabbar', e);
-  };
   async function onFavorite() {
-    const { error } = await sheep.$api.user.favorite.do(props.modelValue.id);
-    if (error === 0) {
-      if (props.modelValue.favorite) {
-        props.modelValue.favorite = 0;
-      } else {
-        props.modelValue.favorite = 1;
+    // 情况一:取消收藏
+    if (props.modelValue.favorite) {
+      const { code } = await FavoriteApi.deleteFavorite(props.modelValue.id);
+      if (code !== 0) {
+        return
+      }
+      sheep.$helper.toast('取消收藏');
+      props.modelValue.favorite = false;
+    // 情况二:添加收藏
+    } else {
+      const { code } = await FavoriteApi.createFavorite(props.modelValue.id);
+      if (code !== 0) {
+        return
       }
+      sheep.$helper.toast('收藏成功');
+      props.modelValue.favorite = true;
     }
   }
 

+ 1 - 1
pages/goods/components/list/list-goods-card.vue

@@ -1,4 +1,4 @@
-<!-- 页面  -->
+<!-- 页面;暂时没用到  -->
 <template>
   <view class="list-goods-card ss-flex-col" @tap="onClick">
     <view class="md-img-box">

+ 1 - 0
pages/goods/components/list/list-navbar.vue

@@ -1,3 +1,4 @@
+<!-- 页面;暂时没用到  -->
 <template>
   <su-fixed
     alway

+ 2 - 8
pages/goods/groupon.vue

@@ -86,10 +86,6 @@
             :skus="state.goodsInfo.skus"
             @tap="state.showSelectSku = true"
           />
-          <!-- 服务 -->
-          <detail-cell-service v-model="state.goodsInfo.service" />
-          <!-- 参数 -->
-          <detail-cell-params v-model="state.goodsInfo.params" />
           <!-- 玩法 -->
           <detail-cell
             v-if="state.goodsInfo.activity.richtext_id > 0"
@@ -173,15 +169,13 @@
 </template>
 
 <script setup>
-  import { reactive, getCurrentInstance, computed, ref } from 'vue';
+  import { reactive, computed, ref } from 'vue';
   import { onLoad, onPageScroll } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
   import { isEmpty } from 'lodash';
   import detailNavbar from './components/detail/detail-navbar.vue';
   import detailCell from './components/detail/detail-cell.vue';
   import detailCellSku from './components/detail/detail-cell-sku.vue';
-  import detailCellService from './components/detail/detail-cell-service.vue';
-  import detailCellParams from './components/detail/detail-cell-params.vue';
   import detailTabbar from './components/detail/detail-tabbar.vue';
   import detailSkeleton from './components/detail/detail-skeleton.vue';
   import detailCommentCard from './components/detail/detail-comment-card.vue';
@@ -209,7 +203,7 @@
     grouponId: 0,           // 团购ID
     grouponType: '',        // 团购类型
     grouponNum: 0,          // 团购人数
-    grouponAction: 'create',  // 团购操作  
+    grouponAction: 'create',  // 团购操作
   });
 
   // 商品主价格

+ 52 - 45
pages/goods/index.vue

@@ -56,9 +56,6 @@
 					<view class="detail-cell-card detail-card ss-flex-col">
 						<detail-cell-sku v-model="state.selectedSku.goods_sku_text" :sku="state.selectedSku"
 							@tap="state.showSelectSku = true" />
-						<!-- TODO 芋艿:可能暂时不考虑使用 -->
-						<detail-cell-service v-if="state.goodsInfo.service" v-model="state.goodsInfo.service" />
-						<detail-cell-params v-if="state.goodsInfo.params" v-model="state.goodsInfo.params" />
 					</view>
 
 					<!-- 规格与数量弹框 -->
@@ -91,11 +88,11 @@
 
 				<!-- 优惠劵弹窗 -->
 				<s-coupon-get v-model="state.couponInfo" :show="state.showModel" @close="state.showModel = false"
-					@get="onGet" />
+                      @get="onGet" />
 
 				<!-- 满减送/限时折扣活动弹窗 -->
 				<s-activity-pop v-model="state.activityInfo" :show="state.showActivityModel"
-					@close="state.showActivityModel = false" />
+                        @close="state.showActivityModel = false" />
 			</block>
 		</s-layout>
 	</view>
@@ -113,23 +110,16 @@
 	import sheep from '@/sheep';
 	import CouponApi from '@/sheep/api/promotion/coupon';
 	import ActivityApi from '@/sheep/api/promotion/activity';
-	import {
-		formatSales,
-		formatGoodsSwiper,
-		fen2yuan,
-	} from '@/sheep/hooks/useGoods';
+  import FavoriteApi from '@/sheep/api/product/favorite';
+  import { formatSales, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
 	import detailNavbar from './components/detail/detail-navbar.vue';
 	import detailCellSku from './components/detail/detail-cell-sku.vue';
-	import detailCellService from './components/detail/detail-cell-service.vue';
-	import detailCellParams from './components/detail/detail-cell-params.vue';
 	import detailTabbar from './components/detail/detail-tabbar.vue';
 	import detailSkeleton from './components/detail/detail-skeleton.vue';
 	import detailCommentCard from './components/detail/detail-comment-card.vue';
 	import detailContentCard from './components/detail/detail-content-card.vue';
 	import detailActivityTip from './components/detail/detail-activity-tip.vue';
-	import {
-		isEmpty
-	} from 'lodash';
+	import { isEmpty } from 'lodash';
 
 	onPageScroll(() => {});
 
@@ -153,21 +143,30 @@
 
 	// 添加购物车  TODO 芋艿:待测试
 	function onAddCart(e) {
+    if (!e.id) {
+      sheep.$helper.toast('请选择商品规格');
+      return;
+    }
 		sheep.$store('cart').add(e);
 	}
 
-	// 立即购买  TODO 芋艿:待测试
+	// 立即购买
 	function onBuy(e) {
-		sheep.$router.go('/pages/order/confirm', {
-			data: JSON.stringify({
-				order_type: 'goods',
-				goods_list: [{
-					goods_id: e.goods_id,
-					goods_num: e.goods_num,
-					goods_sku_price_id: e.id,
-				}, ],
-			}),
-		});
+    if (!state.selectedSku.id) {
+      sheep.$helper.toast('请选择商品规格');
+      return;
+    }
+    sheep.$router.go('/pages/order/confirm', {
+      data: JSON.stringify({
+        items: [{
+          skuId: state.selectedSku.id,
+          count: 1
+        }],
+        // TODO 芋艿:后续清理掉这 2 参数
+        deliveryType: 1,
+        pointStatus: false,
+      }),
+    });
 	}
 
 	// 营销活动  TODO 芋艿:待测试
@@ -178,18 +177,16 @@
 
 	// 立即领取  TODO 芋艿:待测试
 	async function onGet(id) {
-		const {
-			error,
-			msg
-		} = await sheep.$api.coupon.get(id);
-		if (error === 0) {
-			uni.showToast({
-				title: msg,
-			});
-			setTimeout(() => {
-				getCoupon();
-			}, 1000);
-		}
+    const { code } = await CouponApi.takeCoupon(id);
+    if (code !== 0) {
+      return;
+    }
+    uni.showToast({
+      title: '领取成功',
+    });
+    setTimeout(() => {
+      getCoupon();
+    }, 1000);
 	}
 
 	//  TODO 芋艿:待测试
@@ -213,7 +210,14 @@
 		}, );
 	});
 
-	onLoad(async (options) => {
+  async function getCoupon() {
+    const { code, data } = await CouponApi.getCouponTemplateList(state.goodsId, 2, 10);
+    if (code === 0) {
+      state.couponInfo = data;
+    }
+  }
+
+	onLoad((options) => {
 		// 非法参数
 		if (!options.id) {
 			state.goodsInfo = null;
@@ -230,15 +234,18 @@
 			// 加载到商品
 			state.skeletonLoading = false;
 			state.goodsInfo = res.data;
+
+      // 加载是否收藏
+      FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
+        if (res.code !== 0) {
+          return;
+        }
+        state.goodsInfo.favorite = res.data;
+      });
 		});
 
 		// 2. 加载优惠劵信息
-		CouponApi.getCouponTemplateList(state.goodsId, 2, 10).then((res) => {
-			if (res.code !== 0) {
-				return;
-			}
-			state.couponInfo = res.data;
-		});
+    getCoupon();
 
 		// 3. 加载营销活动信息
 		ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {

+ 115 - 103
pages/goods/list.vue

@@ -6,11 +6,11 @@
 			<view class="ss-flex">
 				<view class="ss-flex-1">
 					<su-tabs :list="state.tabList" :scrollable="false" @change="onTabsChange"
-						:current="state.currentTab"></su-tabs>
+                   :current="state.currentTab" />
 				</view>
 				<view class="list-icon" @tap="state.iconStatus = !state.iconStatus">
-					<text v-if="state.iconStatus" class="sicon-goods-list"></text>
-					<text v-else class="sicon-goods-card"></text>
+					<text v-if="state.iconStatus" class="sicon-goods-list" />
+					<text v-else class="sicon-goods-card" />
 				</view>
 			</view>
 		</su-sticky>
@@ -20,38 +20,59 @@
 			:zIndex="10" @close="state.showFilter = false">
 			<view class="filter-list-box">
 				<view class="filter-item" v-for="(item, index) in state.tabList[state.currentTab].list"
-					:key="item.value" :class="[{ 'filter-item-active': index == state.curFilter }]"
+					:key="item.value" :class="[{ 'filter-item-active': index === state.curFilter }]"
 					@tap="onFilterItem(index)">
 					{{ item.label }}
 				</view>
 			</view>
 		</su-popup>
+
+    <!-- 情况一:单列布局 -->
 		<view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20">
-			<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.data" :key="item.id">
-				<s-goods-column class="" size="lg" :data="item" :topRadius="10" :bottomRadius="10"
-					@click="sheep.$router.go('/pages/goods/index', { id: item.id })"></s-goods-column>
+			<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id">
+				<s-goods-column
+          class=""
+          size="lg"
+          :data="item"
+          :topRadius="10"
+          :bottomRadius="10"
+					@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
+        />
 			</view>
 		</view>
-		<view v-if="!state.iconStatus && state.pagination.total > 0"
+    <!-- 情况二:双列布局 -->
+    <view v-if="!state.iconStatus && state.pagination.total > 0"
 			class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top">
 			<view class="goods-list-box">
 				<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
-					<s-goods-column class="goods-md-box" size="md" :data="item" :topRadius="10" :bottomRadius="10"
+					<s-goods-column
+            class="goods-md-box"
+            size="md"
+            :data="item"
+            :topRadius="10"
+            :bottomRadius="10"
 						@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
-						@getHeight="mountMasonry($event, 'left')">
+						@getHeight="mountMasonry($event, 'left')"
+          >
 						<template v-slot:cart>
-							<button class="ss-reset-button cart-btn"> </button>
+							<button class="ss-reset-button cart-btn" />
 						</template>
 					</s-goods-column>
 				</view>
 			</view>
 			<view class="goods-list-box">
 				<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
-					<s-goods-column class="goods-md-box" size="md" :topRadius="10" :bottomRadius="10" :data="item"
+					<s-goods-column
+            class="goods-md-box"
+            size="md"
+            :topRadius="10"
+            :bottomRadius="10"
+            :data="item"
 						@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
-						@getHeight="mountMasonry($event, 'right')">
+						@getHeight="mountMasonry($event, 'right')"
+          >
 						<template v-slot:cart>
-							<button class="ss-reset-button cart-btn"> </button>
+							<button class="ss-reset-button cart-btn" />
 						</template>
 					</s-goods-column>
 				</view>
@@ -59,54 +80,43 @@
 		</view>
 		<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
         contentdown: '上拉加载更多',
-      }" @tap="loadmore" />
-		<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品">
-		</s-empty>
+      }" @tap="loadMore" />
+		<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" />
 	</s-layout>
 </template>
 
 <script setup>
-	import {
-		reactive
-	} from 'vue';
+	import { reactive } from 'vue';
 	import {
 		onLoad,
 		onReachBottom
 	} from '@dcloudio/uni-app';
 	import sheep from '@/sheep';
 	import _ from 'lodash';
+  import { resetPagination } from '@/sheep/util';
 
 	const sys_navBar = sheep.$platform.navbar;
 	const emits = defineEmits(['close', 'change']);
 
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
-	};
 	const state = reactive({
 		pagination: {
-			data: [],
-			current_page: 1,
-			total: 1,
-			last_page: 1,
+			list: [],
+      total: 0,
+      pageNo: 1,
+			pageSize: 6,
 		},
-		// currentSort: 'weigh',
-		// currentOrder: 'desc',
-		currentTab: 0,
-		filterParams: {},
-		curFilter: 0,
+		currentSort: undefined,
+		currentOrder: undefined,
+		currentTab: 0, // 当前选中的 tab
+		curFilter: 0, // 当前选中的 list 筛选项
 		showFilter: false,
-		iconStatus: false,
-		categoryId: 0,
+		iconStatus: false, // true - 单列布局;false - 双列布局
+    keyword: '',
+    categoryId: 0,
 		tabList: [{
 				name: '综合推荐',
-				// value: '',
 				list: [{
-						label: '综合推荐',
-						// sort: '',
-						// order: true,
+						label: '综合推荐'
 					},
 					{
 						label: '价格升序',
@@ -123,18 +133,17 @@
 			{
 				name: '销量',
 				sort: 'salesCount',
-				order: false,
-				// value: 'salesCount',
+				order: false
 			},
 			{
 				name: '新品优先',
-				// value: 'create_time',
+				value: 'createTime',
+        order: false
 			},
 		],
 		loadStatus: '',
-		keyword: '',
-		leftGoodsList: [],
-		rightGoodsList: [],
+		leftGoodsList: [], // 双列布局 - 左侧商品
+		rightGoodsList: [], // 双列布局 - 右侧商品
 	});
 
 	// 加载瀑布流
@@ -142,8 +151,11 @@
 	let leftHeight = 0;
 	let rightHeight = 0;
 
+  // 处理双列布局 leftGoodsList + rightGoodsList
 	function mountMasonry(height = 0, where = 'left') {
-		if (!state.pagination.data[count]) return;
+		if (!state.pagination.list[count]) {
+      return;
+    }
 
 		if (where === 'left') {
 			leftHeight += height;
@@ -151,110 +163,110 @@
 			rightHeight += height;
 		}
 		if (leftHeight <= rightHeight) {
-			state.leftGoodsList.push(state.pagination.data[count]);
+			state.leftGoodsList.push(state.pagination.list[count]);
 		} else {
-			state.rightGoodsList.push(state.pagination.data[count]);
+			state.rightGoodsList.push(state.pagination.list[count]);
 		}
 		count++;
 	}
 
+  // 清空列表
 	function emptyList() {
-		state.pagination = pagination
-		state.leftGoodsList = [];
+    resetPagination(state.pagination);
+    state.leftGoodsList = [];
 		state.rightGoodsList = [];
 		count = 0;
 		leftHeight = 0;
 		rightHeight = 0;
 	}
-	//搜索
+
+	// 搜索
 	function onSearch(e) {
 		state.keyword = e;
 		emptyList();
-		getList(state.currentSort, state.currentOrder, state.categoryId, e);
+		getList(state.currentSort, state.currentOrder);
 	}
 
 	// 点击
 	function onTabsChange(e) {
+    // 如果点击的是【综合推荐】,则直接展开或者收起筛选项
 		if (state.tabList[e.index].list) {
 			state.currentTab = e.index;
 			state.showFilter = !state.showFilter;
 			return;
-		} else {
-			state.showFilter = false;
 		}
+    state.showFilter = false;
+
+    // 如果点击的是【销量】或者【新品优先】,则直接切换 tab
 		if (e.index === state.currentTab) {
 			return;
-		} else {
-			state.currentTab = e.index;
 		}
+
+    state.currentTab = e.index;
+    state.currentSort = e.sort;
+    state.currentOrder = e.order;
 		emptyList();
-		console.log(e, '6666')
-		getList(e.sort, e.order, state.categoryId, state.keyword);
+		getList(e.sort, e.order);
 	}
 
-	// 点击筛选项
+	// 点击 tab 的 list 筛选项
 	const onFilterItem = (val) => {
-		console.log(val)
-		if (
-			state.currentSort === state.tabList[0].list[val].sort &&
-			state.currentOrder === state.tabList[0].list[val].order
-		) {
+    // 如果点击的是当前的筛选项,则直接收起筛选项,不要加载数据
+    // 这里选择 tabList[0] 的原因,是目前只有它有 list
+		if (state.currentSort === state.tabList[0].list[val].sort
+      && state.currentOrder === state.tabList[0].list[val].order) {
 			state.showFilter = false;
 			return;
 		}
+    state.showFilter = false;
+
+    // 设置筛选条件
 		state.curFilter = val;
 		state.tabList[0].name = state.tabList[0].list[val].label;
 		state.currentSort = state.tabList[0].list[val].sort;
 		state.currentOrder = state.tabList[0].list[val].order;
-		emptyList();
-		getList(state.currentSort, state.currentOrder, state.categoryId, state.keyword);
-		state.showFilter = false;
-	};
+		// 清空 + 加载数据
+    emptyList();
+		getList();
+	}
 
-	async function getList(Sort, Order, categoryId, keyword, page = 1, list_rows = 6) {
+	async function getList() {
 		state.loadStatus = 'loading';
-		const res = await sheep.$api.goods.list({
-			sortField: Sort,
-			sortAsc: Order,
-			category_id: !keyword ? categoryId : '',
-			pageSize: list_rows,
-			keyword: keyword,
-			pageNo: page,
+		const { code, data } = await sheep.$api.goods.list({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
+			sortField: state.currentSort,
+			sortAsc: state.currentOrder,
+			categoryId: state.categoryId,
+			keyword: state.keyword,
 		});
-		if (res.code === 0) {
-			let couponList = _.concat(state.pagination.data, res.data.list);
-			state.pagination = {
-				...res.data,
-				data: couponList,
-			};
-			mountMasonry();
-			if (state.pagination.current_page < state.pagination.last_page) {
-				state.loadStatus = 'more';
-			} else {
-				state.loadStatus = 'noMore';
-			}
-		}
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = _.concat(state.pagination.list, data.list)
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+    mountMasonry();
 	}
+
 	// 加载更多
-	function loadmore() {
-		if (state.loadStatus !== 'noMore') {
-			getList(
-				state.currentSort,
-				state.currentOrder,
-				state.categoryId,
-				state.keyword,
-				state.pagination.current_page + 1,
-			);
-		}
+	function loadMore() {
+    if (state.loadStatus === 'noMore') {
+      return;
+    }
+    state.pagination.pageNo++;
+    getList(state.currentSort, state.currentOrder);
 	}
+
 	onLoad((options) => {
 		state.categoryId = options.categoryId;
 		state.keyword = options.keyword;
-		getList(state.currentSort, state.currentOrder, state.categoryId, state.keyword);
+		getList(state.currentSort, state.currentOrder);
 	});
+
 	// 上拉加载更多
 	onReachBottom(() => {
-		loadmore();
+		loadMore();
 	});
 </script>
 

+ 0 - 368
pages/goods/score.vue

@@ -1,368 +0,0 @@
-<template>
-  <view>
-    <s-layout :onShareAppMessage="state.shareInfo" navbar="goods">
-      <!-- 标题栏 -->
-      <detailNavbar />
-      <detailSkeleton v-if="state.skeletonLoading" />
-      <!-- 空置页 -->
-
-      <s-empty
-        v-else-if="state.goodsInfo === null"
-        text="商品不存在或已下架"
-        icon="/static/soldout-empty.png"
-        showAction
-        actionText="再逛逛"
-        actionUrl="/pages/goods/list"
-      />
-      <block v-else>
-        <!-- 商品轮播图  -->
-        <su-swiper
-          class="ss-m-b-14 detail-swiper-selector"
-          isPreview
-          :list="state.goodsSwiper"
-          dotStyle="tag"
-          imageMode="widthFix"
-          dotCur="bg-mask-40"
-          :seizeHeight="750"
-        />
-
-        <!-- 价格+标题 -->
-        <view class="title-card detail-card ss-p-y-40 ss-p-x-20">
-          <view class="ss-flex ss-row-between ss-col-center ss-m-b-18">
-            <view class="price-box ss-flex ss-col-bottom">
-              <view v-if="goodsPrice.price > 0" class="price-text"> ¥{{ goodsPrice.price }} </view>
-              <text v-if="goodsPrice.price > 0 && goodsPrice.score > 0">+</text>
-              <image
-                :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-                class="score-img"
-              ></image>
-              <view class="score-text ss-m-r-16">
-                {{ goodsPrice.score }}
-              </view>
-            </view>
-            <view class="sales-text">
-              {{ formatExchange(state.goodsInfo.sales_show_type, state.goodsInfo.sales) }}
-            </view>
-          </view>
-          <view class="origin-price-text ss-m-b-60" v-if="state.goodsInfo.original_price">
-            原价:¥{{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
-          </view>
-          <view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.title }}</view>
-          <view class="subtitle-text ss-line-1">{{ state.goodsInfo.subtitle }}</view>
-        </view>
-
-        <!-- 功能卡片 -->
-        <view class="detail-cell-card detail-card ss-flex-col">
-          <detail-cell-sku
-            v-model="state.selectedSkuPrice.goods_sku_text"
-            :skus="state.goodsInfo.skus"
-            @tap="state.showSelectSku = true"
-          />
-          <detail-cell-service v-model="state.goodsInfo.service" />
-          <detail-cell-params v-model="state.goodsInfo.params" />
-        </view>
-        <!-- 规格与数量弹框 -->
-        <s-select-sku
-          :goodsInfo="state.goodsInfo"
-          :show="state.showSelectSku"
-          :isScore="true"
-          @addCart="onAddCart"
-          @buy="onBuy"
-          @change="onSkuChange"
-          @close="state.showSelectSku = false"
-        />
-
-        <!-- 评价 -->
-        <view class="detail-comment-selector">
-          <detail-comment-card :goodsId="state.goodsId" />
-        </view>
-
-        <!-- 详情 -->
-        <view class="detail-content-selector"></view>
-        <detail-content-card :content="state.goodsInfo.content" />
-
-        <!-- 详情tabbar -->
-        <detail-tabbar v-model="state.goodsInfo" :shareIcon="false" :collectIcon="false">
-          <!-- TODO: 缺货中 已售罄 判断 设计-->
-          <view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
-            <button class="ss-reset-button buy-btn" @tap="state.showSelectSku = true">
-              立即兑换
-            </button>
-          </view>
-          <view class="buy-box ss-flex ss-col-center ss-p-r-20" v-else>
-            <button class="ss-reset-button disabled-btn" disabled> 已兑完 </button>
-          </view>
-        </detail-tabbar>
-      </block>
-    </s-layout>
-  </view>
-</template>
-
-<script setup>
-  import { reactive, computed } from 'vue';
-  import { onLoad, onPageScroll } from '@dcloudio/uni-app';
-  import sheep from '@/sheep';
-  import { isEmpty } from 'lodash';
-  import { formatExchange, formatGoodsSwiper } from '@/sheep/hooks/useGoods';
-  import detailNavbar from './components/detail/detail-navbar.vue';
-  import detailCellSku from './components/detail/detail-cell-sku.vue';
-  import detailCellService from './components/detail/detail-cell-service.vue';
-  import detailCellParams from './components/detail/detail-cell-params.vue';
-  import detailTabbar from './components/detail/detail-tabbar.vue';
-  import detailSkeleton from './components/detail/detail-skeleton.vue';
-  import detailCommentCard from './components/detail/detail-comment-card.vue';
-  import detailContentCard from './components/detail/detail-content-card.vue';
-
-  const headerBg = sheep.$url.css('/static/img/shop/goods/score-bg.png');
-  const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
-  const grouponBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
-
-  onPageScroll(() => {});
-
-  const state = reactive({
-    goodsId: 0,
-    skeletonLoading: true,
-    goodsInfo: {},
-    showSelectSku: false,
-    goodsSwiper: [],
-    selectedSkuPrice: {},
-    shareInfo: {},
-    showModel: false,
-    total: 0,
-    couponInfo: [],
-  });
-
-  const goodsPrice = computed(() => {
-    let price, score;
-    if (isEmpty(state.selectedSkuPrice)) {
-      price = state.goodsInfo.price[0];
-      score = state.goodsInfo.score || 0;
-    } else {
-      price = state.selectedSkuPrice.price;
-      score = state.selectedSkuPrice.score || 0;
-    }
-    return { price, score };
-  });
-
-  // 规格变更
-  function onSkuChange(e) {
-    state.selectedSkuPrice = e;
-  }
-  // 格式化价格
-  function formatPrice(e) {
-    if (Number(e[0]) > 0) {
-      return e.length === 1 ? e[0] : e.join('~');
-    } else {
-      return '';
-    }
-  }
-  // 添加购物车
-  function onAddCart(e) {
-    sheep.$store('cart').add(e);
-  }
-  // 立即购买
-  function onBuy(e) {
-    sheep.$router.go('/pages/order/confirm', {
-      data: JSON.stringify({
-        order_type: 'score',
-        goods_list: [
-          {
-            goods_id: e.goods_id,
-            goods_num: e.goods_num,
-            goods_sku_price_id: e.id,
-          },
-        ],
-      }),
-    });
-  }
-
-  onLoad((options) => {
-    // 非法参数
-    if (!options.id) {
-      state.goodsInfo = null;
-      return;
-    }
-    state.goodsId = options.id;
-    // 加载商品信息
-    sheep.$api.app.scoreShop.detail(state.goodsId).then((res) => {
-      state.skeletonLoading = false;
-      if (res.error === 0) {
-        state.goodsInfo = res.data;
-        state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.images);
-      } else {
-        // 未找到商品
-        state.goodsInfo = null;
-      }
-    });
-  });
-</script>
-
-<style lang="scss" scoped>
-  .detail-card {
-    background-color: #ffff;
-    margin: 14rpx 20rpx;
-    border-radius: 10rpx;
-    overflow: hidden;
-  }
-
-  // 价格标题卡片
-  .title-card {
-    width: 710rpx;
-    box-sizing: border-box;
-    background-size: 100% 100%;
-    border-radius: 10rpx;
-    background-image: v-bind(headerBg);
-    background-repeat: no-repeat;
-    .price-box {
-      .score-img {
-        width: 36rpx;
-        height: 36rpx;
-        margin: 0 4rpx;
-      }
-      .score-text {
-        font-size: 42rpx;
-        font-weight: 500;
-        color: #ff3000;
-        line-height: 36rpx;
-        font-family: OPPOSANS;
-      }
-      .price-text {
-        font-size: 42rpx;
-        font-weight: 500;
-        color: #ff3000;
-        line-height: 36rpx;
-        font-family: OPPOSANS;
-      }
-    }
-    .origin-price-text {
-      font-size: 26rpx;
-      font-weight: 400;
-      text-decoration: line-through;
-      color: $gray-c;
-      font-family: OPPOSANS;
-    }
-
-    .sales-text {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: $gray-c;
-    }
-
-    .discounts-box {
-      .discounts-tag {
-        padding: 4rpx 10rpx;
-        font-size: 24rpx;
-        font-weight: 500;
-        border-radius: 4rpx;
-        color: var(--ui-BG-Main);
-        // background: rgba(#2aae67, 0.05);
-        background: var(--ui-BG-Main-tag);
-      }
-
-      .discounts-title {
-        font-size: 24rpx;
-        font-weight: 500;
-        color: var(--ui-BG-Main);
-        line-height: normal;
-      }
-
-      .cicon-forward {
-        color: var(--ui-BG-Main);
-        font-size: 24rpx;
-        line-height: normal;
-        margin-top: 4rpx;
-      }
-    }
-
-    .title-text {
-      font-size: 30rpx;
-      font-weight: bold;
-      line-height: 42rpx;
-    }
-
-    .subtitle-text {
-      font-size: 26rpx;
-      font-weight: 400;
-      color: $dark-9;
-      line-height: 42rpx;
-    }
-  }
-
-  // 购买
-  .buy-box {
-    .buy-btn {
-      width: 630rpx;
-      height: 80rpx;
-      border-radius: 40rpx;
-      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-      color: $white;
-    }
-    .disabled-btn {
-      width: 630rpx;
-      height: 80rpx;
-      border-radius: 40rpx;
-      background: #999999;
-      color: $white;
-    }
-  }
-
-  //秒杀卡片
-  .seckill-box {
-    background: v-bind(seckillBg) no-repeat;
-    background-size: 100% 100%;
-  }
-
-  .groupon-box {
-    background: v-bind(grouponBg) no-repeat;
-    background-size: 100% 100%;
-  }
-
-  //活动卡片
-  .activity-box {
-    width: 100%;
-    height: 80rpx;
-    box-sizing: border-box;
-    margin-bottom: 10rpx;
-
-    .activity-title {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #ffffff;
-      line-height: 42rpx;
-
-      .activity-icon {
-        width: 38rpx;
-        height: 38rpx;
-      }
-    }
-
-    .activity-go {
-      width: 70rpx;
-      height: 32rpx;
-      background: #ffffff;
-      border-radius: 16rpx;
-      font-weight: 500;
-      color: #ff6000;
-      font-size: 24rpx;
-      line-height: normal;
-    }
-  }
-
-  .model-box {
-    height: 60vh;
-    .model-content {
-      height: 56vh;
-    }
-    .title {
-      font-size: 36rpx;
-      font-weight: bold;
-      color: #333333;
-    }
-
-    .subtitle {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333333;
-    }
-  }
-</style>

+ 0 - 4
pages/goods/seckill.vue

@@ -73,8 +73,6 @@
             :skus="state.goodsInfo.skus"
             @tap="state.showSelectSku = true"
           />
-          <detail-cell-service v-model="state.goodsInfo.service" />
-          <detail-cell-params v-model="state.goodsInfo.params" />
         </view>
         <!-- 规格与数量弹框 -->
         <s-select-seckill-sku
@@ -145,8 +143,6 @@
   import { useDurationTime, formatGoodsSwiper, formatPrice } from '@/sheep/hooks/useGoods';
   import detailNavbar from './components/detail/detail-navbar.vue';
   import detailCellSku from './components/detail/detail-cell-sku.vue';
-  import detailCellService from './components/detail/detail-cell-service.vue';
-  import detailCellParams from './components/detail/detail-cell-params.vue';
   import detailTabbar from './components/detail/detail-tabbar.vue';
   import detailSkeleton from './components/detail/detail-skeleton.vue';
   import detailCommentCard from './components/detail/detail-comment-card.vue';

+ 0 - 3
pages/index/index.vue

@@ -71,9 +71,6 @@
 		if (options.page) {
 			sheep.$router.go(decodeURIComponent(options.page));
 		}
-
-		// TODO 芋艿:测试接口的调用
-		sheep.$api.app.test();
 	});
 
 	// 下拉刷新

+ 8 - 2
pages/index/search.vue

@@ -1,3 +1,4 @@
+<!-- 搜索界面 -->
 <template>
   <s-layout class="set-wrap" title="搜索" :bgStyle="{ color: '#FFF' }">
     <view class="ss-p-x-24">
@@ -33,14 +34,18 @@
   import { reactive } from 'vue';
   import sheep from '@/sheep';
   import { onLoad } from '@dcloudio/uni-app';
+
   const state = reactive({
     historyList: [],
   });
 
   // 搜索
   function onSearch(keyword) {
-    if (!keyword) return;
+    if (!keyword) {
+      return;
+    }
     saveSearchHistory(keyword);
+    // 前往商品列表(带搜索条件)
     sheep.$router.go('/pages/goods/list', { keyword });
   }
 
@@ -53,7 +58,7 @@
     // 置顶关键词
     state.historyList.unshift(keyword);
 
-    // 最多保留10条记录
+    // 最多保留 10 条记录
     if (state.historyList.length >= 10) {
       state.historyList.length = 10;
     }
@@ -72,6 +77,7 @@
       },
     });
   }
+
   onLoad(() => {
     state.historyList = uni.getStorageSync('searchHistory') || [];
   });

+ 268 - 229
pages/order/aftersale/apply.vue

@@ -1,11 +1,15 @@
-<!-- 订单详情 -->
+<!-- 售后申请 -->
 <template>
   <s-layout title="申请售后">
     <!-- 售后商品 -->
     <view class="goods-box">
-      <s-goods-item :img="state.goodsItem.goods_image" :title="state.goodsItem.goods_title"
-        :skuText="state.goodsItem.goods_sku_text" :price="state.goodsItem.goods_price"
-        :num="state.goodsItem.goods_num"></s-goods-item>
+      <s-goods-item
+        :img="state.item.picUrl"
+        :title="state.item.spuName"
+        :skuText="state.item.properties?.map((property) => property.valueName).join(' ')"
+        :price="state.item.price"
+        :num="state.item.count"
+      />
     </view>
 
     <uni-forms ref="form" v-model="formData" :rules="rules" label-position="top">
@@ -14,69 +18,104 @@
         <view class="item-title ss-m-b-20">售后类型</view>
         <view class="ss-flex-col">
           <radio-group @change="onRefundChange">
-            <label class="ss-flex ss-col-center ss-p-y-10" v-for="(item, index) in state.refundTypeList" :key="index">
-              <radio :checked="formData.type === item.value" color="var(--ui-BG-Main)" style="transform: scale(0.8)"
-                :value="item.value" />
+            <label
+              class="ss-flex ss-col-center ss-p-y-10"
+              v-for="(item, index) in state.wayList"
+              :key="index"
+            >
+              <radio
+                :checked="formData.type === item.value"
+                color="var(--ui-BG-Main)"
+                style="transform: scale(0.8)"
+                :value="item.value"
+              />
               <view class="item-value ss-m-l-8">{{ item.text }}</view>
             </label>
           </radio-group>
         </view>
       </view>
+      <!-- 退款金额 -->
+      <view class="refund-item ss-flex ss-col-center ss-row-between" @tap="state.showModal = true">
+        <text class="item-title">退款金额</text>
+        <view class="ss-flex refund-cause ss-col-center">
+          <text class="ss-m-r-20">¥{{ fen2yuan(state.item.payPrice) }}</text>
+        </view>
+      </view>
       <!-- 申请原因 -->
       <view class="refund-item ss-flex ss-col-center ss-row-between" @tap="state.showModal = true">
         <text class="item-title">申请原因</text>
         <view class="ss-flex refund-cause ss-col-center">
-          <text class="ss-m-r-20" v-if="formData.reason">{{ formData.reason }}</text>
+          <text class="ss-m-r-20" v-if="formData.applyReason">{{ formData.applyReason }}</text>
           <text class="ss-m-r-20" v-else>请选择申请原因~</text>
-          <!-- <text class="ss-iconfont _icon-forward" style="color: #666"></text> -->
           <text class="cicon-forward" style="height: 28rpx"></text>
         </view>
       </view>
-      <view class="refund-item u-m-b-20">
-        <view class="item-title ss-m-b-20">联系方式</view>
-        <view class="input-box u-flex">
-          <uni-easyinput :inputBorder="false" type="number" v-model="formData.mobile" placeholder="请输入您的联系电话"
-            paddingLeft="10" />
-        </view>
-      </view>
 
       <!-- 留言 -->
       <view class="refund-item">
         <view class="item-title ss-m-b-20">相关描述</view>
         <view class="describe-box">
-          <uni-easyinput :inputBorder="false" class="describe-content" type="textarea" maxlength="120" autoHeight
-            v-model="formData.content" placeholder="客官~请描述您遇到的问题,建议上传照片"></uni-easyinput>
+          <uni-easyinput
+            :inputBorder="false"
+            class="describe-content"
+            type="textarea"
+            maxlength="120"
+            autoHeight
+            v-model="formData.applyDescription"
+            placeholder="客官~请描述您遇到的问题,建议上传照片"
+          />
+          <!-- TODO 芋艿:上传的测试 -->
           <view class="upload-img">
-            <s-uploader v-model:url="formData.images" fileMediatype="image" limit="9" mode="grid"
-              :imageStyles="{ width: '168rpx', height: '168rpx' }" />
+            <s-uploader
+              v-model:url="formData.images"
+              fileMediatype="image"
+              limit="9"
+              mode="grid"
+              :imageStyles="{ width: '168rpx', height: '168rpx' }"
+            />
           </view>
         </view>
       </view>
     </uni-forms>
+
     <!-- 底部按钮 -->
     <su-fixed bottom placeholder>
       <view class="foot-wrap">
         <view class="foot_box ss-flex ss-col-center ss-row-between ss-p-x-30">
-          <button class="ss-reset-button contcat-btn" @tap="sheep.$router.go('/pages/chat/index')">联系客服</button>
+          <button class="ss-reset-button contcat-btn" @tap="sheep.$router.go('/pages/chat/index')">
+            联系客服
+          </button>
           <button class="ss-reset-button ui-BG-Main-Gradient sub-btn" @tap="submit">提交</button>
         </view>
       </view>
     </su-fixed>
-    <!-- 申请原因弹窗 -->
 
+    <!-- 申请原因弹窗 -->
     <su-popup :show="state.showModal" round="10" :showClose="true" @close="state.showModal = false">
       <view class="modal-box page_box">
-        <view class="modal-head item-title head_box ss-flex ss-row-center ss-col-center">申请原因</view>
+        <view class="modal-head item-title head_box ss-flex ss-row-center ss-col-center">
+          申请原因
+        </view>
         <view class="modal-content content_box">
           <radio-group @change="onChange">
-            <label class="radio ss-flex ss-col-center" v-for="item in state.refundReasonList" :key="item.value">
-              <view class="ss-flex-1 ss-p-20">{{ item.title }}</view>
-              <radio :value="item.value" color="var(--ui-BG-Main)" :checked="item.value === state.currentValue" />
+            <label
+              class="radio ss-flex ss-col-center"
+              v-for="item in state.reasonList"
+              :key="item"
+            >
+              <view class="ss-flex-1 ss-p-20">{{ item }}</view>
+              <radio
+                :value="item"
+                color="var(--ui-BG-Main)"
+                :checked="item === state.currentValue"
+              />
             </label>
           </radio-group>
         </view>
         <view class="modal-foot foot_box ss-flex ss-row-center ss-col-center">
-          <button class="ss-reset-button close-btn ui-BG-Main-Gradient" @tap="onReason">确定</button>
+          <button class="ss-reset-button close-btn ui-BG-Main-Gradient" @tap="onReason">
+            确定
+          </button>
         </view>
       </view>
     </su-popup>
@@ -84,235 +123,235 @@
 </template>
 
 <script setup>
-import sheep from '@/sheep';
-import { onLoad } from '@dcloudio/uni-app';
-import { reactive, ref, unref } from 'vue';
-const form = ref(null);
-const state = reactive({
-  showModal: false,
-  currentValue: 0,
-  goodsItem: {},
-  reasonText: '',
-  //售后类型
-  refundTypeList: [
-    {
-      text: '仅退款',
-      value: 'refund',
-    },
-    {
-      text: '退/换货',
-      value: 'return',
-    },
-    {
-      text: '其他',
-      value: 'other',
-    },
-  ],
-  refundReasonList: [
-    {
-      value: '1',
-      title: '卖家发错货了',
-    },
-    {
-      value: '2',
-      title: '退运费',
-    },
-    {
-      value: '3',
-      title: '大小/重量与商品描述不符',
-    },
-    {
-      value: '4',
-      title: '生产日期/保质期与商品描述不符',
-    },
-    {
-      value: '5',
-      title: '质量问题',
-    },
-    {
-      value: '6',
-      title: '我不想要了',
-    },
-  ],
-});
-const formData = reactive({
-  type: '',
-  reason: '',
-  mobile: '',
-  content: '',
-  images: [],
-});
-const rules = reactive({});
-
-// 提交表单
-async function submit() {
-  // #ifdef MP
-  sheep.$platform.useProvider('wechat').subscribeMessage('order_aftersale_change');
-  // #endif
-  let data = {
-    ...formData,
-    order_id: state.goodsItem.order_id,
-    order_item_id: state.goodsItem.id,
-  };
-  const res = await sheep.$api.order.aftersale.apply(data);
-  if (res.error === 0) {
-    uni.showToast({
-      title: res.msg,
-    });
-    sheep.$router.go('/pages/order/aftersale/list');
+  import sheep from '@/sheep';
+  import { onLoad } from '@dcloudio/uni-app';
+  import { reactive, ref } from 'vue';
+  import OrderApi from '@/sheep/api/trade/order';
+  import TradeConfigApi from '@/sheep/api/trade/config';
+  import { fen2yuan } from '@/sheep/hooks/useGoods';
+  import AfterSaleApi from '@/sheep/api/trade/afterSale';
+
+  const form = ref(null);
+  const state = reactive({
+    orderId: 0, // 订单编号
+    itemId: 0, // 订单项编号
+    order: {}, // 订单
+    item: {}, // 订单项
+    config: {}, // 交易配置
+
+    // 售后类型
+    wayList: [
+      {
+        text: '仅退款',
+        value: '10',
+      },
+      {
+        text: '退款退货',
+        value: '20',
+      },
+    ],
+    reasonList: [], // 可选的申请原因数组
+    showModal: false, // 是否显示申请原因弹窗
+    currentValue: '' // 当前选择的售后原因
+  });
+  const formData = reactive({
+    way: '',
+    applyReason: '',
+    applyDescription: '',
+    images: [],
+  });
+  const rules = reactive({});
+
+  // 提交表单
+  async function submit() {
+    // #ifdef MP
+    sheep.$platform.useProvider('wechat').subscribeMessage('order_aftersale_change');
+    // #endif
+    let data = {
+      orderItemId: state.itemId,
+      refundPrice: state.item.payPrice,
+      ...formData,
+    };
+    const { code } = await AfterSaleApi.createAfterSale(data);
+    if (code === 0) {
+      uni.showToast({
+        title: '申请成功',
+      });
+      sheep.$router.go('/pages/order/aftersale/list');
+    }
+  }
+
+  // 选择售后类型
+  function onRefundChange(e) {
+    formData.way = e.detail.value;
+    // 清理理由
+    state.reasonList =
+      formData.way === '10'
+        ? state.config.afterSaleRefundReasons || []
+        : state.config.afterSaleReturnReasons || [];
+    formData.applyReason = '';
+    state.currentValue = '';
+  }
+
+  // 选择申请原因
+  function onChange(e) {
+    state.currentValue = e.detail.value;
+  }
+
+  // 确定
+  function onReason() {
+    formData.applyReason = state.currentValue;
+    state.showModal = false;
   }
-}
-
-//选择售后类型
-function onRefundChange(e) {
-  formData.type = e.detail.value;
-}
-
-//选择申请原因
-function onChange(e) {
-  state.currentValue = e.detail.value;
-  state.refundReasonList.forEach((item) => {
-    if (item.value === e.detail.value) {
-      state.reasonText = item.title;
+
+  onLoad(async (options) => {
+    // 解析参数
+    if (!options.orderId || !options.itemId) {
+      sheep.$helper.toast(`缺少订单信息,请检查`);
+      return;
+    }
+    state.orderId = options.orderId;
+    state.itemId = parseInt(options.itemId);
+
+    // 读取订单信息
+    const { code, data } = await OrderApi.getOrder(state.orderId);
+    if (code !== 0) {
+      return;
     }
+    state.order = data;
+    state.item = data.items.find((item) => item.id === state.itemId) || {};
+
+    // 设置选项
+    if (state.order.status === 10) {
+      state.wayList.splice(1, 1);
+    }
+
+    // 读取配置
+    state.config = (await TradeConfigApi.getTradeConfig()).data;
   });
-}
-//确定
-function onReason() {
-  formData.reason = state.reasonText;
-  state.showModal = false;
-}
-
-function onTitle(e, title) {
-  state.currentValue = e;
-  state.reasonText = title;
-}
-onLoad((options) => {
-  state.goodsItem = JSON.parse(options.item);
-});
 </script>
 
 <style lang="scss" scoped>
-.item-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: rgba(51, 51, 51, 1);
-  // margin-bottom: 20rpx;
-}
-
-// 售后项目
-.refund-item {
-  background-color: #fff;
-  border-bottom: 1rpx solid #f5f5f5;
-  padding: 30rpx;
-
-  &:last-child {
-    border: none;
+  .item-title {
+    font-size: 30rpx;
+    font-weight: bold;
+    color: rgba(51, 51, 51, 1);
+    // margin-bottom: 20rpx;
   }
 
-  // 留言
-  .describe-box {
-    width: 690rpx;
-    background: rgba(249, 250, 251, 1);
+  // 售后项目
+  .refund-item {
+    background-color: #fff;
+    border-bottom: 1rpx solid #f5f5f5;
     padding: 30rpx;
-    box-sizing: border-box;
-    border-radius: 20rpx;
 
-    .describe-content {
-      height: 200rpx;
-      font-size: 24rpx;
-      font-weight: 400;
-      color: #333;
+    &:last-child {
+      border: none;
     }
-  }
 
-  // 联系方式
-  .input-box {
-    height: 84rpx;
-    background: rgba(249, 250, 251, 1);
-    border-radius: 20rpx;
-  }
-}
-
-.goods-box {
-  background: #fff;
-  padding: 20rpx;
-  margin-bottom: 20rpx;
-}
-
-.foot-wrap {
-  height: 100rpx;
-  width: 100%;
-}
-
-.foot_box {
-  height: 100rpx;
-  background-color: #fff;
-
-  .sub-btn {
-    width: 336rpx;
-    line-height: 74rpx;
-    border-radius: 38rpx;
-    color: rgba(#fff, 0.9);
-    font-size: 28rpx;
-  }
+    // 留言
+    .describe-box {
+      width: 690rpx;
+      background: rgba(249, 250, 251, 1);
+      padding: 30rpx;
+      box-sizing: border-box;
+      border-radius: 20rpx;
 
-  .contcat-btn {
-    width: 336rpx;
-    line-height: 74rpx;
-    background: rgba(238, 238, 238, 1);
-    border-radius: 38rpx;
-    font-size: 28rpx;
-    font-weight: 400;
-    color: rgba(51, 51, 51, 1);
+      .describe-content {
+        height: 200rpx;
+        font-size: 24rpx;
+        font-weight: 400;
+        color: #333;
+      }
+    }
+
+    // 联系方式
+    .input-box {
+      height: 84rpx;
+      background: rgba(249, 250, 251, 1);
+      border-radius: 20rpx;
+    }
   }
-}
 
-.modal-box {
-  width: 750rpx;
-  // height: 680rpx;
-  border-radius: 30rpx 30rpx 0 0;
-  background: #fff;
+  .goods-box {
+    background: #fff;
+    padding: 20rpx;
+    margin-bottom: 20rpx;
+  }
 
-  .modal-head {
+  .foot-wrap {
     height: 100rpx;
-    font-size: 30rpx;
+    width: 100%;
   }
 
-  .modal-content {
-    font-size: 28rpx;
-  }
+  .foot_box {
+    height: 100rpx;
+    background-color: #fff;
 
-  .modal-foot {
-    .close-btn {
-      width: 710rpx;
-      line-height: 80rpx;
-      border-radius: 40rpx;
+    .sub-btn {
+      width: 336rpx;
+      line-height: 74rpx;
+      border-radius: 38rpx;
       color: rgba(#fff, 0.9);
+      font-size: 28rpx;
+    }
+
+    .contcat-btn {
+      width: 336rpx;
+      line-height: 74rpx;
+      background: rgba(238, 238, 238, 1);
+      border-radius: 38rpx;
+      font-size: 28rpx;
+      font-weight: 400;
+      color: rgba(51, 51, 51, 1);
     }
   }
-}
 
-.success-box {
-  width: 600rpx;
-  padding: 90rpx 0 64rpx 0;
+  .modal-box {
+    width: 750rpx;
+    // height: 680rpx;
+    border-radius: 30rpx 30rpx 0 0;
+    background: #fff;
 
-  .cicon-check-round {
-    font-size: 96rpx;
-    color: #04b750;
-  }
+    .modal-head {
+      height: 100rpx;
+      font-size: 30rpx;
+    }
+
+    .modal-content {
+      font-size: 28rpx;
+    }
 
-  .success-title {
-    font-weight: 500;
-    color: #333333;
-    font-size: 32rpx;
+    .modal-foot {
+      .close-btn {
+        width: 710rpx;
+        line-height: 80rpx;
+        border-radius: 40rpx;
+        color: rgba(#fff, 0.9);
+      }
+    }
   }
 
-  .success-btn {
-    width: 492rpx;
-    height: 70rpx;
-    background: linear-gradient(90deg, var(--ui-BG-Main-gradient), var(--ui-BG-Main));
-    border-radius: 35rpx;
+  .success-box {
+    width: 600rpx;
+    padding: 90rpx 0 64rpx 0;
+
+    .cicon-check-round {
+      font-size: 96rpx;
+      color: #04b750;
+    }
+
+    .success-title {
+      font-weight: 500;
+      color: #333333;
+      font-size: 32rpx;
+    }
+
+    .success-btn {
+      width: 492rpx;
+      height: 70rpx;
+      background: linear-gradient(90deg, var(--ui-BG-Main-gradient), var(--ui-BG-Main));
+      border-radius: 35rpx;
+    }
   }
-}
 </style>

+ 87 - 103
pages/order/aftersale/detail.vue

@@ -3,26 +3,22 @@
 	<s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'">
 		<view class="content_box" v-if="!isEmpty(state.info) && state.loading">
 			<!-- 步骤条 -->
-			<!-- 这个没找到替换方案 -->
 			<view class="steps-box ss-flex" :style="[
           {
             marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
             paddingTop: Number(statusBarHeight + 88) + 'rpx',
           },
         ]">
-				<!-- <uni-steps :options="state.list" :active="state.active" active-color="#fff" /> -->
 				<view class="ss-flex">
 					<view class="steps-item" v-for="(item, index) in state.list" :key="index">
 						<view class="ss-flex">
-							<text class="sicon-circleclose" v-if="
-                  (state.list.length - 1 == index && state.info.aftersale_status === -2) ||
-                  (state.list.length - 1 == index && state.info.aftersale_status === -1)
-                "></text>
+							<text class="sicon-circleclose"
+                    v-if="state.list.length - 1 === index && [61, 62, 63].includes(state.info.status)" />
 							<text class="sicon-circlecheck" v-else
-								:class="state.active >= index ? 'activity-color' : 'info-color'"></text>
+                    :class="state.active >= index ? 'activity-color' : 'info-color'" />
 
-							<view v-if="state.list.length - 1 != index" class="line"
-								:class="state.active >= index ? 'activity-bg' : 'info-bg'"></view>
+							<view v-if="state.list.length - 1 !== index" class="line"
+                    :class="state.active >= index ? 'activity-bg' : 'info-bg'" />
 						</view>
 						<view class="steps-item-title" :class="state.active >= index ? 'activity-color' : 'info-color'">
 							{{ item.title }}
@@ -32,28 +28,33 @@
 			</view>
 
 			<!-- 服务状态 -->
-			<!-- 			<view class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
-				@tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.aftersaleId })">
+			<view class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
+            @tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.id })">
 				<view class="">
-					<view class="status-text">{{ state.info.aftersale_status_desc }}</view>
-					<view class="status-time">{{ state.info.update_time }}</view>
+					<view class="status-text">
+            {{ formatAfterSaleStatusDescription(state.info) }}
+          </view>
+					<view class="status-time">
+            {{ sheep.$helper.timeFormat(state.info.updateTime, 'yyyy-mm-dd hh:MM:ss') }}
+          </view>
 				</view>
-				<text class="ss-iconfont _icon-forward" style="color: #666"></text>
-			</view> -->
+				<text class="ss-iconfont _icon-forward" style="color: #666" />
+			</view>
 
 			<!-- 退款金额 -->
 			<view class="aftersale-money ss-flex ss-col-center ss-row-between">
 				<view class="aftersale-money--title">退款总额</view>
-				<view class="aftersale-money--num">¥{{ state.info.refundPrice/100 }}</view>
+				<view class="aftersale-money--num">¥{{ fen2yuan(state.info.refundPrice) }}</view>
 			</view>
 			<!-- 服务商品 -->
 			<view class="order-shop">
-				<!-- 		<s-goods-item :title="state.info.goods_title" :price="state.info.goods_price"
-					:img="state.info.goods_image" priceColor="#333333" :titleWidth="480"
-					:skuText="state.info.goods_sku_text" :num="state.info.goods_num"></s-goods-item> -->
-				<s-goods-item :img=" state.info.picUrl" :title=" state.info.spuName" priceColor="#333333"
-					:titleWidth="480" :skuText=" state.info.properties.reduce((a,b)=>a+b.valueName+' ','')"
-					:price=" state.info.refundPrice/100" :num=" state.info.count"></s-goods-item>
+				<s-goods-item
+          :img=" state.info.picUrl"
+          :title=" state.info.spuName"
+					:titleWidth="480"
+          :skuText="state.info.properties.map((property) => property.valueName).join(' ')"
+          :num=" state.info.count"
+        />
 			</view>
 
 			<!-- 服务内容 -->
@@ -71,7 +72,7 @@
 				</view>
 				<view class="aftersale-item ss-flex ss-col-center">
 					<view class="item-title">售后类型:</view>
-					<view class="item-content">{{ status2[state.info.way] }}</view>
+					<view class="item-content">{{ state.info.way === 10 ? '仅退款' : '退款退货' }}</view>
 				</view>
 				<view class="aftersale-item ss-flex ss-col-center">
 					<view class="item-title">申请原因:</view>
@@ -83,114 +84,97 @@
 				</view>
 			</view>
 		</view>
+
+    <!-- 操作区 -->
 		<s-empty v-if="isEmpty(state.info) && state.loading" icon="/static/order-empty.png" text="暂无该订单售后详情" />
-		<!-- 		<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
+		<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
 			<view class="foot_box">
-				<button class="ss-reset-button btn" v-if="state.info.btns?.includes('cancel')"
-					@tap="onApply(state.info.id)">取消申请</button>
-				<button class="ss-reset-button btn" v-if="state.info.btns?.includes('delete')"
-					@tap="onDelete(state.info.id)">删除</button>
-				<button class="ss-reset-button contcat-btn btn"
-					@tap="sheep.$router.go('/pages/chat/index')">联系客服</button>
+				<!-- TODO 功能缺失:填写退货信息 -->
+        <button class="ss-reset-button btn" v-if="state.info.buttons?.includes('cancel')"
+                @tap="onApply(state.info.id)">
+          取消申请
+        </button>
+				<button class="ss-reset-button contcat-btn btn" @tap="sheep.$router.go('/pages/chat/index')">
+          联系客服
+        </button>
 			</view>
-		</su-fixed> -->
+		</su-fixed>
 	</s-layout>
 </template>
 
 <script setup>
 	import sheep from '@/sheep';
-	import {
-		onLoad
-	} from '@dcloudio/uni-app';
-	import {
-		reactive
-	} from 'vue';
-	import {
-		isEmpty
-	} from 'lodash';
+	import { onLoad } from '@dcloudio/uni-app';
+	import { reactive } from 'vue';
+	import { isEmpty } from 'lodash';
+  import { fen2yuan, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods';
+  import AfterSaleApi from '@/sheep/api/trade/afterSale';
 
 	const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
 	const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
 	const state = reactive({
-		active: 0,
-		aftersaleId: 0,
-		info: {},
-		list: [{
-				title: '提交申请',
-			},
-			{
-				title: '处理中',
-			},
-		],
+    id: 0, // 售后编号
+    info: {}, // 收货信息
 		loading: false,
+    active: 0, // 在 list 的激活位置
+    list: [{
+      title: '提交申请',
+    }, {
+      title: '处理中',
+    }, {
+      title: '完成'
+    }], // 时间轴
 	});
 
-	const status2 = {
-		10: '仅退款',
-		20: '退货退款'
-	}
-
-	function onApply(orderId) {
+	function onApply(id) {
 		uni.showModal({
 			title: '提示',
 			content: '确定要取消此申请吗?',
 			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error
-					} = await sheep.$api.order.aftersale.cancel(orderId);
-					if (error === 0) {
-						getDetail(state.aftersaleId);
-					}
+				if (!res.confirm) {
+					return;
 				}
+        const { code } = await AfterSaleApi.cancelAfterSale(id);
+        if (code === 0) {
+          await getDetail(id);
+        }
 			},
 		});
 	}
 
-	function onDelete(orderId) {
-		uni.showModal({
-			title: '提示',
-			content: '确定要删除吗?',
-			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error
-					} = await sheep.$api.order.aftersale.delete(orderId);
-					if (error === 0) {
-						sheep.$router.back();
-					}
-				}
-			},
-		});
-	}
-	const onCopy = () => {
-		sheep.$helper.copyText(state.info.aftersale_sn);
+  // 复制
+  const onCopy = () => {
+		sheep.$helper.copyText(state.info.no);
 	};
+
 	async function getDetail(id) {
-		const {
-			code,
-			data
-		} = await sheep.$api.order.aftersale.detail(id);
-		state.loading = true;
-		if (code === 0) {
-			state.info = data;
-			if (state.info.aftersale_status === -2 || state.info.aftersale_status === -1) {
-				state.list.push({
-					title: state.info.aftersale_status_text
-				});
-				state.active = 2;
-			} else {
-				state.list.push({
-					title: '完成'
-				});
-				state.active = state.info.aftersale_status;
-			}
-		} else {
-			state.info = null;
-		}
+    state.loading = true;
+    const { code, data } = await AfterSaleApi.getAfterSale(id);
+    if (code !== 0) {
+      state.info = null;
+      return;
+    }
+    state.info = data;
+    handleAfterSaleButtons(state.info);
+
+    // 处理时间轴
+    if ([10].includes(state.info.status)) {
+      state.active = 0;
+    } else if ([20, 30].includes(state.info.status)) {
+      state.active = 1;
+    } else if ([40, 50].includes(state.info.status)) {
+      state.active = 2;
+    } else if ([61, 62, 63].includes(state.info.status)) {
+      state.active = 2;
+    }
 	}
+
 	onLoad((options) => {
-		state.aftersaleId = options.id;
+    if (!options.id) {
+      sheep.$helper.toast(`缺少订单信息,请检查`);
+      return
+    }
+		state.id = options.id;
 		getDetail(options.id);
 	});
 </script>

+ 61 - 106
pages/order/aftersale/list.vue

@@ -3,92 +3,73 @@
 	<s-layout title="售后列表">
 		<!-- tab -->
 		<su-sticky bgColor="#fff">
-			<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
+			<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" />
 		</su-sticky>
-		<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据">
-		</s-empty>
+		<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据" />
 		<!-- 列表 -->
 		<view v-if="state.pagination.total > 0">
-			<view class="list-box ss-m-y-20" v-for="order in state.pagination.data" :key="order.id"
+			<view class="list-box ss-m-y-20" v-for="order in state.pagination.list" :key="order.id"
 				@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })">
 				<view class="order-head ss-flex ss-col-center ss-row-between">
 					<text class="no">服务单号:{{ order.no }}</text>
-					<text class="state">{{ status[order.status] }}</text>
+					<text class="state">{{ formatAfterSaleStatus(order) }}</text>
 				</view>
-				<s-goods-item :img="order.picUrl" :title="order.spuName"
-					:skuText="order.properties.reduce((a,b)=>a+b.valueName+' ','')" :price="order.refundPrice/100"
-					:num="order.count"></s-goods-item>
+				<s-goods-item
+          :img="order.picUrl"
+          :title="order.spuName"
+					:skuText="order.properties.map((property) => property.valueName).join(' ')"
+          :price="order.refundPrice"
+        />
 				<view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20">
 					<view class="ss-flex ss-col-center">
-						<!-- 此处需修改 -->
-						<view class="title ss-m-r-20">{{ status2[order.way] }}</view>
-						<!-- <view class="value">{{ order.aftersale_status_desc }}</view> -->
-						<view class="value">{{ order.applyReason }}</view>
+						<view class="title ss-m-r-20">{{ order.way === 10 ? '仅退款' : '退款退货' }}</view>
+						 <view class="value">{{ formatAfterSaleStatusDescription(order) }}</view>
 					</view>
 					<text class="_icon-forward"></text>
 				</view>
-				<!-- 				<view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
+				<view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
+          <!-- TODO 功能缺失:填写退货信息 -->
 					<view>
 						<button class="ss-reset-button tool-btn" @tap.stop="onApply(order.id)"
-							v-if="order.btns.includes('cancel')">取消申请</button>
+							v-if="order?.buttons.includes('cancel')">取消申请</button>
 					</view>
-					<view>
-						<button class="ss-reset-button tool-btn" @tap.stop="onDelete(order.id)"
-							v-if="order.btns.includes('delete')">删除</button>
-					</view>
-				</view> -->
+				</view>
 			</view>
 		</view>
 		<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
         contentdown: '上拉加载更多',
-      }" @tap="loadmore" />
+      }" @tap="loadMore" />
 	</s-layout>
 </template>
 
 <script setup>
 	import sheep from '@/sheep';
-	import {
-		onLoad,
-		onReachBottom
-	} from '@dcloudio/uni-app';
-	import {
-		computed,
-		reactive
-	} from 'vue';
+	import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+	import { reactive } from 'vue';
 	import _ from 'lodash';
-
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
+  import { formatAfterSaleStatus, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods';
+  import AfterSaleApi from '@/sheep/api/trade/afterSale';
+
+	const paginationNull = {
+		list: [],
+    total: 0,
+    pageNo: 1,
+    pageSize: 10
 	};
+
 	const state = reactive({
 		currentTab: 0,
 		showApply: false,
 		pagination: {
-			data: [],
-			current_page: 1,
-			total: 1,
-			last_page: 1,
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 10
 		},
 		loadStatus: '',
 	});
-	// 字典需要登录 尚未接入 先用固定值代替
-	const status = {
-		10: '申请售后',
-		20: '商品待退货',
-		30: '商家待收货',
-		40: '等待退款',
-		50: '退款成功',
-		61: '买家取消',
-		62: '商家拒绝',
-		63: '商家拒收货'
-	}
-	const status2 = {
-		10: '仅退款',
-		20: '退货退款'
-	}
+
+  // TODO 芋艿:优化点,增加筛选
 	const tabMaps = [{
 			name: '全部',
 			value: 'all',
@@ -110,38 +91,29 @@
 		//   value: 'refuse',
 		// },
 	];
+
 	// 切换选项卡
 	function onTabsChange(e) {
-		state.pagination = pagination
+		state.pagination = paginationNull
 		state.currentTab = e.index;
 		getOrderList();
 	}
 
 	// 获取售后列表
-	async function getOrderList(page = 1, list_rows = 5) {
-		pagination.current_page = page;
+	async function getOrderList() {
 		state.loadStatus = 'loading';
-		let res = await sheep.$api.order.aftersale.list({
+		let { data, code } = await sheep.$api.order.aftersale.list({
 			// type: tabMaps[state.currentTab].value,
-			pageSize: list_rows,
-			pageNo: page,
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
 		});
-		console.log(res, '未处理前售后列表数据')
-		if (res.code === 0) {
-			let orderList = _.concat(state.pagination.data, res.data.list);
-
-			state.pagination = {
-				total: res.data.total,
-				...res.data,
-				data: orderList,
-			};
-			console.log(state.pagination, '售后订单数据')
-			// if (state.pagination.current_page < state.pagination.last_page) {
-			state.loadStatus = 'more';
-			// } else {
-			// state.loadStatus = 'noMore';
-			// }
+		if (code !== 0) {
+      return;
 		}
+    data.list.forEach(order => handleAfterSaleButtons(order));
+    state.pagination.list = _.concat(state.pagination.list, data.list);
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
 	}
 
 	function onApply(orderId) {
@@ -149,33 +121,14 @@
 			title: '提示',
 			content: '确定要取消此申请吗?',
 			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error
-					} = await sheep.$api.order.aftersale.cancel(orderId);
-					if (error === 0) {
-						state.pagination = pagination
-						getOrderList();
-					}
-				}
-			},
-		});
-	}
-
-	function onDelete(orderId) {
-		uni.showModal({
-			title: '提示',
-			content: '确定要删除吗?',
-			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error
-					} = await sheep.$api.order.aftersale.delete(orderId);
-					if (error === 0) {
-						state.pagination = pagination
-						getOrderList();
-					}
+				if (!res.confirm) {
+          return;
 				}
+        const { code } = await AfterSaleApi.cancelAfterSale(orderId);
+        if (code === 0) {
+          state.pagination = paginationNull
+          await getOrderList();
+        }
 			},
 		});
 	}
@@ -184,19 +137,21 @@
 		if (options.type) {
 			state.currentTab = options.type;
 		}
-		getOrderList();
+		await getOrderList();
 	});
 
 	// 加载更多
-	function loadmore() {
-		// if (state.loadStatus !== 'noMore') {
-		getOrderList(pagination.current_page + 1);
-		// }
+	function loadMore() {
+    if (state.loadStatus === 'noMore') {
+      return
+    }
+    state.pagination.pageNo++;
+    getOrderList();
 	}
 
 	// 上拉加载更多
 	onReachBottom(() => {
-		loadmore();
+		loadMore();
 	});
 </script>
 

+ 10 - 32
pages/order/aftersale/log-item.vue

@@ -1,57 +1,35 @@
+<!-- 售后日志的每一项展示 -->
 <template>
   <view class="log-item ss-flex">
     <view class="log-icon ss-flex-col ss-col-center ss-m-r-20">
-      <text class="cicon-title" :class="index === 0 ? 'activity-color' : ''"></text>
-      <view v-if="data.length - 1 != index" class="line"></view>
+      <text class="cicon-title" :class="index === 0 ? 'activity-color' : ''" />
+      <view v-if="data.length - 1 !== index" class="line" />
     </view>
     <view>
-      <view class="text">{{ item.log_type_text }}</view>
-      <mp-html class="richtext" :content="item.content"></mp-html>
-      <view class="" v-if="item.images?.length">
-        <scroll-view class="scroll-box" scroll-x scroll-anchoring>
-          <view class="ss-flex">
-            <view v-for="i in item.images" :key="i" class="ss-m-r-20">
-              <su-image
-                class="content-img"
-                isPreview
-                :previewList="state.commentImages"
-                :current="index"
-                :src="i"
-                :height="120"
-                :width="120"
-                mode="aspectFit"
-              ></su-image>
-            </view>
-          </view>
-        </scroll-view>
+      <view class="text">{{ item.content }}</view>
+      <view class="date">
+        {{ sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}
       </view>
-      <view class="date">{{ item.create_time }}</view>
     </view>
   </view>
 </template>
 <script setup>
   import sheep from '@/sheep';
-  import { reactive } from 'vue';
+
   const props = defineProps({
     item: {
-      type: Object,
+      type: Object, // 当前日志
       default() {},
     },
     index: {
-      type: Number,
+      type: Number, // item 在 data 的下标
       default: 0,
     },
     data: {
-      type: Object,
+      type: Object, // 日志列表
       default() {},
     },
   });
-  const state = reactive({
-    commentImages: [],
-  });
-  props.item.images?.forEach((i) => {
-    state.commentImages.push(sheep.$url.cdn(i));
-  });
 </script>
 <style lang="scss" scoped>
   .log-item {

+ 10 - 26
pages/order/aftersale/log.vue

@@ -1,45 +1,29 @@
-<!-- 售后进度  -->
+<!-- 售后日志列表  -->
 <template>
   <s-layout title="售后进度">
     <view class="log-box">
-      <view  v-for="(item, index) in state.info" :key="item.title">
-        <log-item :item="item" :index="index" :data="state.info"></log-item>
+      <view v-for="(item, index) in state.list" :key="item.id">
+        <log-item :item="item" :index="index" :data="state.list" />
       </view>
     </view>
   </s-layout>
 </template>
 
 <script setup>
-  import sheep from '@/sheep';
   import { onLoad } from '@dcloudio/uni-app';
-  import { computed, reactive } from 'vue';
+  import { reactive } from 'vue';
   import logItem from './log-item.vue';
+  import AfterSaleApi from '@/sheep/api/trade/afterSale';
 
   const state = reactive({
-    active: 1,
-    list: [
-      {
-        title: '买家下单',
-        desc: '2018-11-11',
-      },
-      {
-        title: '卖家发货',
-        desc: '2018-11-12',
-      },
-      {
-        title: '买家签收',
-        desc: '2018-11-13',
-      },
-      {
-        title: '交易完成',
-        desc: '2018-11-14',
-      },
-    ],
+    list: [],
   });
+
   async function getDetail(id) {
-    const { data } = await sheep.$api.order.aftersale.detail(id);
-    state.info = data.logs;
+    const { data } = await AfterSaleApi.getAfterSaleLogList(id);
+    state.list = data;
   }
+
   onLoad((options) => {
     state.aftersaleId = options.id;
     getDetail(options.id);

+ 381 - 407
pages/order/confirm.vue

@@ -1,414 +1,388 @@
 <template>
-	<s-layout title="确认订单">
-		<!-- v-if="state.orderInfo.need_address === 1" -->
-		<!-- 这个判断先删除 -->
-		<view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress">
-			<s-address-item :item="state.addressInfo" :hasBorderBottom="false">
-				<view class="ss-rest-button"><text class="_icon-forward"></text></view>
-			</s-address-item>
-		</view>
-		<view class="order-card-box ss-m-b-14">
-			<s-goods-item v-for="item in state.orderInfo.goods_list" :key="item.goods_id"
-				:img="item.current_sku_price.image || item.goods.image" :title="item.goods.title"
-				:skuText="item.current_sku_price?.goods_sku_text" :price="item.current_sku_price.price"
-				:num="item.goods_num" marginBottom="10">
-				<template #top>
-					<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white">
-						<view class="item-title">配送方式</view>
-						<view class="ss-flex ss-col-center">
-							<text class="item-value">{{ item.dispatch_type_text }}</text>
-						</view>
-					</view>
-				</template>
-			</s-goods-item>
-
-			<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white ss-r-10">
-				<view class="item-title">订单备注</view>
-				<view class="ss-flex ss-col-center">
-					<uni-easyinput maxlength="20" placeholder="建议留言前先与商家沟通" v-model="state.orderPayload.remark"
-						:inputBorder="false" :clearable="false"></uni-easyinput>
-				</view>
-			</view>
-		</view>
-		<!-- 合计 -->
-		<view class="bg-white total-card-box ss-p-20 ss-m-b-14 ss-r-10">
-			<view class="total-box-content border-bottom">
-				<view class="order-item ss-flex ss-col-center ss-row-between">
-					<view class="item-title">商品金额</view>
-					<view class="ss-flex ss-col-center">
-						<text class="item-value ss-m-r-24">¥{{ state.orderInfo.goods_amount }}</text>
-					</view>
-				</view>
-				<view class="order-item ss-flex ss-col-center ss-row-between"
-					v-if="state.orderPayload.order_type === 'score'">
-					<view class="item-title">扣除积分</view>
-					<view class="ss-flex ss-col-center">
-						<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
-						<text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text>
-					</view>
-				</view>
-				<view class="order-item ss-flex ss-col-center ss-row-between">
-					<view class="item-title">运费</view>
-					<view class="ss-flex ss-col-center">
-						<text class="item-value ss-m-r-24">+¥{{ state.orderInfo.dispatch_amount }}</text>
-					</view>
-				</view>
-				<view class="order-item ss-flex ss-col-center ss-row-between"
-					v-if="state.orderPayload.order_type != 'score'">
-					<!-- <view v-if="state.orderInfo.coupon_discount_fee > 0" class="order-item ss-flex ss-col-center ss-row-between"> -->
-					<view class="item-title">优惠券</view>
-					<view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
-						<text class="item-value text-red"
-							v-if="state.orderPayload.coupon_id">-¥{{ state.orderInfo.coupon_discount_fee }}</text>
-						<text class="item-value"
-							:class="state.couponInfo.can_use?.length > 0 ? 'text-red' : 'text-disabled'" v-else>{{
-                state.couponInfo.can_use?.length > 0
-                  ? state.couponInfo.can_use?.length + '张可用'
-                  : '暂无可用优惠券'
-              }}</text>
-
-						<text class="_icon-forward item-icon"></text>
-					</view>
-				</view>
-				<view class="order-item ss-flex ss-col-center ss-row-between"
-					v-if="state.orderInfo.promo_infos?.length">
-					<!-- <view v-if="state.orderInfo.promo_discount_fee > 0" class="order-item ss-flex ss-col-center ss-row-between"> -->
-					<view class="item-title">活动优惠</view>
-					<view class="ss-flex ss-col-center" @tap="state.showDiscount = true">
-						<text class="item-value text-red"> -¥{{ state.orderInfo.promo_discount_fee }} </text>
-						<text class="_icon-forward item-icon"></text>
-					</view>
-				</view>
-			</view>
-			<view class="total-box-footer ss-font-28 ss-flex ss-row-right ss-col-center ss-m-r-28">
-				<view class="total-num ss-m-r-20">共{{ state.totalNumber }}件</view>
-				<view>合计:</view>
-				<view class="total-num text-red"> ¥{{ state.orderInfo.pay_fee }} </view>
-				<view class="ss-flex" v-if="state.orderPayload.order_type === 'score'">
-					<view class="total-num ss-font-30 text-red ss-m-l-4"> + </view>
-					<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
-					<view class="total-num ss-font-30 text-red">{{ state.orderInfo.score_amount }}</view>
-				</view>
-			</view>
-		</view>
-		<!-- 发票 -->
-		<view class="bg-white ss-p-20 ss-r-20">
-			<view class="order-item ss-flex ss-col-center ss-row-between">
-				<view class="item-title">发票申请</view>
-				<view class="ss-flex ss-col-center" @tap="onSelectInvoice">
-					<text class="item-value">{{ state.invoiceInfo.name || '无需开具发票' }}</text>
-					<text class="_icon-forward item-icon"></text>
-				</view>
-			</view>
-		</view>
-		<!-- 选择优惠券弹框 -->
-		<s-coupon-select v-model="state.couponInfo" :show="state.showCoupon" @confirm="onSelectCoupon"
-			@close="state.showCoupon = false" />
-		<!-- 满额折扣弹框  -->
-		<s-discount-list v-model="state.orderInfo" :show="state.showDiscount" @close="state.showDiscount = false" />
-		<!-- 底部 -->
-		<su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
-			<view class="footer-box border-top ss-flex ss-row-between ss-p-x-20 ss-col-center">
-				<view class="total-box-footer ss-flex ss-col-center">
-					<view class="total-num ss-font-30 text-red"> ¥{{ state.orderInfo.pay_fee }} </view>
-					<view v-if="state.orderPayload.order_type === 'score'" class="ss-flex">
-						<view class="total-num ss-font-30 text-red ss-m-l-4">+</view>
-						<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
-						<view class="total-num ss-font-30 text-red">{{ state.orderInfo.score_amount }}</view>
-					</view>
-				</view>
-
-				<button class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main" @tap="onConfirm">
-					{{ exchangeNow ? '立即兑换' : '提交订单' }}
-				</button>
-			</view>
-		</su-fixed>
-	</s-layout>
+  <s-layout title="确认订单">
+    <!-- TODO:这个判断先删除 v-if="state.orderInfo.need_address === 1" -->
+    <view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress">
+      <s-address-item :item="state.addressInfo" :hasBorderBottom="false">
+        <view class="ss-rest-button">
+          <text class="_icon-forward" />
+        </view>
+      </s-address-item>
+    </view>
+
+    <!-- 商品信息 -->
+    <view class="order-card-box ss-m-b-14">
+      <s-goods-item
+        v-for="item in state.orderInfo.items"
+        :key="item.skuId"
+        :img="item.picUrl"
+        :title="item.spuName"
+        :skuText="item.properties.map((property) => property.valueName).join(' ')"
+        :price="item.price"
+        :num="item.count"
+        marginBottom="10"
+      />
+      <view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white ss-r-10">
+        <view class="item-title">订单备注</view>
+        <view class="ss-flex ss-col-center">
+          <uni-easyinput
+            maxlength="20"
+            placeholder="建议留言前先与商家沟通"
+            v-model="state.orderPayload.remark"
+            :inputBorder="false"
+            :clearable="false"
+          />
+        </view>
+      </view>
+    </view>
+
+    <!-- 价格信息 -->
+    <view class="bg-white total-card-box ss-p-20 ss-m-b-14 ss-r-10">
+      <view class="total-box-content border-bottom">
+        <view class="order-item ss-flex ss-col-center ss-row-between">
+          <view class="item-title">商品金额</view>
+          <view class="ss-flex ss-col-center">
+            <text class="item-value ss-m-r-24">
+              ¥{{ fen2yuan(state.orderInfo.price.totalPrice) }}
+            </text>
+          </view>
+        </view>
+        <!-- TODO 芋艿:接入积分 -->
+        <view
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderPayload.order_type === 'score'"
+        >
+          <view class="item-title">扣除积分</view>
+          <view class="ss-flex ss-col-center">
+            <image
+              :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
+              class="score-img"
+            />
+            <text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text>
+          </view>
+        </view>
+        <view class="order-item ss-flex ss-col-center ss-row-between">
+          <view class="item-title">运费</view>
+          <view class="ss-flex ss-col-center">
+            <text class="item-value ss-m-r-24">
+              +¥{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
+            </text>
+          </view>
+        </view>
+        <!-- 优惠劵:只有 type = 0 普通订单(非拼团、秒杀、砍价),才可以使用优惠劵 -->
+        <view
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.type === 0"
+        >
+          <view class="item-title">优惠券</view>
+          <view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
+            <text class="item-value text-red" v-if="state.orderPayload.couponId > 0">
+              -¥{{ fen2yuan(state.orderInfo.price.couponPrice) }}
+            </text>
+            <text
+              class="item-value"
+              :class="state.couponInfo.length > 0 ? 'text-red' : 'text-disabled'"
+              v-else
+            >
+              {{
+                state.couponInfo.length > 0 ? state.couponInfo.length + ' 张可用' : '暂无可用优惠券'
+              }}
+            </text>
+            <text class="_icon-forward item-icon" />
+          </view>
+        </view>
+        <view
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.price.discountPrice > 0"
+        >
+          <view class="item-title">活动优惠</view>
+          <view class="ss-flex ss-col-center">
+            <!--                @tap="state.showDiscount = true" TODO 芋艿:后续要把优惠信息打进去 -->
+            <text class="item-value text-red">
+              -¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
+            </text>
+            <text class="_icon-forward item-icon" />
+          </view>
+        </view>
+      </view>
+      <view class="total-box-footer ss-font-28 ss-flex ss-row-right ss-col-center ss-m-r-28">
+        <view class="total-num ss-m-r-20">
+          共{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}件
+        </view>
+        <view>合计:</view>
+        <view class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }} </view>
+      </view>
+    </view>
+
+    <!-- 选择优惠券弹框 -->
+    <s-coupon-select
+      v-model="state.couponInfo"
+      :show="state.showCoupon"
+      @confirm="onSelectCoupon"
+      @close="state.showCoupon = false"
+    />
+
+    <!-- 满额折扣弹框 TODO 芋艿:后续要把优惠信息打进去 -->
+    <s-discount-list
+      v-model="state.orderInfo"
+      :show="state.showDiscount"
+      @close="state.showDiscount = false"
+    />
+
+    <!-- 底部 -->
+    <su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
+      <view class="footer-box border-top ss-flex ss-row-between ss-p-x-20 ss-col-center">
+        <view class="total-box-footer ss-flex ss-col-center">
+          <view class="total-num ss-font-30 text-red">
+            ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}
+          </view>
+        </view>
+        <button
+          class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main"
+          @tap="onConfirm"
+        >
+          提交订单
+        </button>
+      </view>
+    </su-fixed>
+  </s-layout>
 </template>
 
 <script setup>
-	import {
-		reactive,
-		computed
-	} from 'vue';
-	import {
-		onLoad,
-		onPageScroll,
-		onShow
-	} from '@dcloudio/uni-app';
-	import sheep from '@/sheep';
-	import {
-		isEmpty
-	} from 'lodash';
-
-	const state = reactive({
-		orderPayload: {},
-		orderInfo: {},
-		addressInfo: {},
-		invoiceInfo: {},
-		totalNumber: 0,
-		showCoupon: false,
-		couponInfo: [],
-		showDiscount: false,
-	});
-
-	// 立即兑换(立即兑换无需跳转收银台)
-	const exchangeNow = computed(
-		() => state.orderPayload.order_type === 'score' && state.orderInfo.pay_fee == 0,
-	);
-
-	// 选择地址
-	function onSelectAddress() {
-		uni.$once('SELECT_ADDRESS', (e) => {
-			changeConsignee(e.addressInfo);
-		});
-		sheep.$router.go('/pages/user/address/list');
-	}
-
-	// 更改收货人地址&计算订单信息
-	async function changeConsignee(addressInfo = {}) {
-		if (isEmpty(addressInfo)) {
-			const {
-				code,
-				data
-			} = await sheep.$api.user.address.default();
-			console.log(data, '默认收货地址');
-			if (code === 0 && !isEmpty(data)) {
-				console.log('执行赋值')
-				addressInfo = data;
-			}
-		}
-		if (!isEmpty(addressInfo)) {
-			state.addressInfo = addressInfo;
-			state.orderPayload.address_id = state.addressInfo.id;
-		}
-		getOrderInfo();
-	}
-
-	// 选择优惠券
-	async function onSelectCoupon(e) {
-		state.orderPayload.coupon_id = e || 0;
-		getOrderInfo();
-		state.showCoupon = false;
-	}
-
-	// 选择发票信息
-	function onSelectInvoice() {
-		uni.$once('SELECT_INVOICE', (e) => {
-			state.invoiceInfo = e.invoiceInfo;
-			state.orderPayload.invoice_id = e.invoiceInfo.id || 0;
-		});
-		sheep.$router.go('/pages/user/invoice/list');
-	}
-
-	// 提交订单/立即兑换
-	function onConfirm() {
-		if (!state.orderPayload.address_id && state.orderInfo.need_address === 1) {
-			sheep.$helper.toast('请选择收货地址');
-			return;
-		}
-
-		if (exchangeNow.value) {
-			uni.showModal({
-				title: '提示',
-				content: '确定使用积分立即兑换?',
-				cancelText: '再想想',
-				success: async function(res) {
-					if (res.confirm) {
-						submitOrder();
-					}
-				},
-			});
-		} else {
-			submitOrder();
-		}
-	}
-
-	// 创建订单&跳转
-	async function submitOrder() {
-		const {
-			error,
-			data
-		} = await sheep.$api.order.create(state.orderPayload);
-		if (error === 0) {
-			// 更新购物车列表
-			if (state.orderPayload.from === 'cart') {
-				sheep.$store('cart').getList();
-			}
-			if (exchangeNow.value) {
-				sheep.$router.redirect('/pages/pay/result', {
-					orderSN: data.order_sn,
-				});
-			} else {
-				sheep.$router.redirect('/pages/pay/index', {
-					orderSN: data.order_sn,
-				});
-			}
-		}
-	}
-
-	// 检查库存&计算订单价格
-	async function getOrderInfo() {
-		console.log(state.orderPayload, '计算价格传参')
-		// let {code, data} = await sheep.$api.order.calc(state.orderPayload);
-		// let data = await sheep.$api.order.calc(state.orderPayload);
-		console.log(state.orderPayload.items)
-		let data = await sheep.$api.order.calc({
-			deliveryType: 1,
-			pointStatus: false,
-			items: state.orderPayload.items
-		});
-		console.log(data, '修改后的获取订单详细数据')
-		return;
-		if (error === 0) {
-			state.totalNumber = 0;
-			state.orderInfo = data;
-			state.orderInfo.goods_list.forEach((item) => {
-				state.totalNumber += item.goods_num;
-			});
-		}
-	}
-
-	// 获取可用优惠券
-	async function getCoupons() {
-		const {
-			error,
-			data
-		} = await sheep.$api.order.coupons(state.orderPayload);
-		if (error === 0) {
-			state.couponInfo = data;
-		}
-	}
-
-	onLoad(async (options) => {
-		console.log(options)
-		if (options.data) {
-			state.orderPayload = JSON.parse(options.data);
-			changeConsignee();
-			if (state.orderPayload.order_type !== 'score') {
-				getCoupons();
-			}
-		}
-	});
+  import { reactive } from 'vue';
+  import { onLoad } from '@dcloudio/uni-app';
+  import sheep from '@/sheep';
+  import { isEmpty } from 'lodash';
+  import OrderApi from '@/sheep/api/trade/order';
+  import CouponApi from '@/sheep/api/promotion/coupon';
+  import { fen2yuan } from '@/sheep/hooks/useGoods';
+
+  const state = reactive({
+    orderPayload: {},
+    orderInfo: {
+      items: [], // 商品项列表
+      price: {}, // 价格信息
+    },
+    addressInfo: {}, // 选择的收货地址
+    showCoupon: false, // 是否展示优惠劵
+    couponInfo: [], // 优惠劵列表
+    showDiscount: false, // 是否展示营销活动
+  });
+
+  // 选择地址
+  function onSelectAddress() {
+    uni.$once('SELECT_ADDRESS', (e) => {
+      changeConsignee(e.addressInfo);
+    });
+    sheep.$router.go('/pages/user/address/list');
+  }
+
+  // 更改收货人地址&计算订单信息
+  async function changeConsignee(addressInfo = {}) {
+    if (!isEmpty(addressInfo)) {
+      state.addressInfo = addressInfo;
+    }
+    await getOrderInfo();
+  }
+
+  // 选择优惠券
+  async function onSelectCoupon(couponId) {
+    state.orderPayload.couponId = couponId || 0;
+    await getOrderInfo();
+    state.showCoupon = false;
+  }
+
+  // 提交订单
+  function onConfirm() {
+    if (!state.addressInfo.id) {
+      sheep.$helper.toast('请选择收货地址');
+      return;
+    }
+    submitOrder();
+  }
+
+  // 创建订单&跳转
+  async function submitOrder() {
+    const { code, data } = await OrderApi.createOrder({
+      items: state.orderPayload.items,
+      couponId: state.orderPayload.couponId,
+      addressId: state.addressInfo.id,
+      deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
+      pointStatus: false, // TODO 芋艿:需要支持【积分选择】
+    });
+    if (code !== 0) {
+      return;
+    }
+    // 更新购物车列表,如果来自购物车
+    if (state.orderPayload.items[0].cartId > 0) {
+      sheep.$store('cart').getList();
+    }
+    // 跳转到支付页面
+    sheep.$router.redirect('/pages/pay/index', {
+      id: data.payOrderId,
+    });
+  }
+
+  // 检查库存 & 计算订单价格
+  async function getOrderInfo() {
+    // 计算价格
+    const { data, code } = await OrderApi.settlementOrder({
+      items: state.orderPayload.items,
+      couponId: state.orderPayload.couponId,
+      addressId: state.addressInfo.id,
+      deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
+      pointStatus: false, // TODO 芋艿:需要支持【积分选择】
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.orderInfo = data;
+    // 设置收货地址
+    if (state.orderInfo.address) {
+      state.addressInfo = state.orderInfo.address;
+    }
+  }
+
+  // 获取可用优惠券
+  async function getCoupons() {
+    const { code, data } = await CouponApi.getMatchCouponList(
+      state.orderInfo.price.payPrice,
+      state.orderInfo.items.map((item) => item.spuId),
+      state.orderPayload.items.map((item) => item.skuId),
+      state.orderPayload.items.map((item) => item.categoryId),
+    );
+    if (code === 0) {
+      state.couponInfo = data;
+    }
+  }
+
+  onLoad(async (options) => {
+    if (!options.data) {
+      sheep.$helper.toast('参数不正确,请检查!');
+      return;
+    }
+    state.orderPayload = JSON.parse(options.data);
+    await getOrderInfo();
+    await getCoupons();
+  });
 </script>
 
 <style lang="scss" scoped>
-	:deep() {
-		.uni-input-wrapper {
-			width: 320rpx;
-		}
-
-		.uni-easyinput__content-input {
-			font-size: 28rpx;
-			height: 72rpx;
-			text-align: right !important;
-			padding-right: 0 !important;
-
-			.uni-input-input {
-				font-weight: 500;
-				color: #333333;
-				font-size: 26rpx;
-				height: 32rpx;
-				margin-top: 4rpx;
-			}
-		}
-
-		.uni-easyinput__content {
-			display: flex !important;
-			align-items: center !important;
-			justify-content: right !important;
-		}
-	}
-
-	.score-img {
-		width: 36rpx;
-		height: 36rpx;
-		margin: 0 4rpx;
-	}
-
-	.order-item {
-		height: 80rpx;
-
-		.item-title {
-			font-size: 28rpx;
-			font-weight: 400;
-		}
-
-		.item-value {
-			font-size: 28rpx;
-			font-weight: 500;
-			font-family: OPPOSANS;
-		}
-
-		.text-disabled {
-			color: #bbbbbb;
-		}
-
-		.item-icon {
-			color: $dark-9;
-		}
-
-		.remark-input {
-			text-align: right;
-		}
-
-		.item-placeholder {
-			color: $dark-9;
-			font-size: 26rpx;
-			text-align: right;
-		}
-	}
-
-	.total-box-footer {
-		height: 90rpx;
-
-		.total-num {
-			color: #333333;
-			font-family: OPPOSANS;
-		}
-	}
-
-	.footer-box {
-		height: 100rpx;
-
-		.submit-btn {
-			width: 240rpx;
-			height: 70rpx;
-			font-size: 28rpx;
-			font-weight: 500;
-
-			.goto-pay-text {
-				line-height: 28rpx;
-			}
-		}
-
-		.cancel-btn {
-			width: 240rpx;
-			height: 80rpx;
-			font-size: 26rpx;
-			background-color: #e5e5e5;
-			color: $dark-9;
-		}
-	}
-
-	.title {
-		font-size: 36rpx;
-		font-weight: bold;
-		color: #333333;
-	}
-
-	.subtitle {
-		font-size: 28rpx;
-		color: #999999;
-	}
-
-	.cicon-checkbox {
-		font-size: 36rpx;
-		color: var(--ui-BG-Main);
-	}
-
-	.cicon-box {
-		font-size: 36rpx;
-		color: #999999;
-	}
-</style>
+  :deep() {
+    .uni-input-wrapper {
+      width: 320rpx;
+    }
+
+    .uni-easyinput__content-input {
+      font-size: 28rpx;
+      height: 72rpx;
+      text-align: right !important;
+      padding-right: 0 !important;
+
+      .uni-input-input {
+        font-weight: 500;
+        color: #333333;
+        font-size: 26rpx;
+        height: 32rpx;
+        margin-top: 4rpx;
+      }
+    }
+
+    .uni-easyinput__content {
+      display: flex !important;
+      align-items: center !important;
+      justify-content: right !important;
+    }
+  }
+
+  .score-img {
+    width: 36rpx;
+    height: 36rpx;
+    margin: 0 4rpx;
+  }
+
+  .order-item {
+    height: 80rpx;
+
+    .item-title {
+      font-size: 28rpx;
+      font-weight: 400;
+    }
+
+    .item-value {
+      font-size: 28rpx;
+      font-weight: 500;
+      font-family: OPPOSANS;
+    }
+
+    .text-disabled {
+      color: #bbbbbb;
+    }
+
+    .item-icon {
+      color: $dark-9;
+    }
+
+    .remark-input {
+      text-align: right;
+    }
+
+    .item-placeholder {
+      color: $dark-9;
+      font-size: 26rpx;
+      text-align: right;
+    }
+  }
+
+  .total-box-footer {
+    height: 90rpx;
+
+    .total-num {
+      color: #333333;
+      font-family: OPPOSANS;
+    }
+  }
+
+  .footer-box {
+    height: 100rpx;
+
+    .submit-btn {
+      width: 240rpx;
+      height: 70rpx;
+      font-size: 28rpx;
+      font-weight: 500;
+
+      .goto-pay-text {
+        line-height: 28rpx;
+      }
+    }
+
+    .cancel-btn {
+      width: 240rpx;
+      height: 80rpx;
+      font-size: 26rpx;
+      background-color: #e5e5e5;
+      color: $dark-9;
+    }
+  }
+
+  .title {
+    font-size: 36rpx;
+    font-weight: bold;
+    color: #333333;
+  }
+
+  .subtitle {
+    font-size: 28rpx;
+    color: #999999;
+  }
+
+  .cicon-checkbox {
+    font-size: 36rpx;
+    color: var(--ui-BG-Main);
+  }
+
+  .cicon-box {
+    font-size: 36rpx;
+    color: #999999;
+  }
+</style>

+ 601 - 668
pages/order/detail.vue

@@ -1,692 +1,625 @@
 <!-- 订单详情 -->
 <template>
-	<s-layout title="订单详情" class="index-wrap" navbar="inner">
-		<!-- 订单状态 -->
-		<view class="state-box ss-flex-col ss-col-center ss-row-right" :style="[
+  <s-layout title="订单详情" class="index-wrap" navbar="inner">
+    <!-- 订单状态 TODO -->
+    <view
+      class="state-box ss-flex-col ss-col-center ss-row-right"
+      :style="[
         {
           marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
           paddingTop: Number(statusBarHeight + 88) + 'rpx',
         },
-      ]">
-			<view class="ss-flex ss-m-t-32 ss-m-b-20">
-				<image v-if="
+      ]"
+    >
+      <view class="ss-flex ss-m-t-32 ss-m-b-20">
+        <image
+          v-if="
             state.orderInfo.status_code == 'unpaid' ||
-            state.orderInfo.status_code == 'nosend' ||
+            state.orderInfo.status === 10 || // 待发货
             state.orderInfo.status_code == 'nocomment'
-          " class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_loading.png')">
-				</image>
-				<image v-if="
+          "
+          class="state-img"
+          :src="sheep.$url.static('/static/img/shop/order/order_loading.png')"
+        >
+        </image>
+        <image
+          v-if="
             state.orderInfo.status_code == 'completed' ||
             state.orderInfo.status_code == 'refund_agree'
-          " class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_success.png')">
-				</image>
-				<image v-if="state.orderInfo.status_code == 'cancel' || state.orderInfo.status_code == 'closed'"
-					class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_close.png')">
-				</image>
-				<image v-if="state.orderInfo.status_code == 'noget'" class="state-img"
-					:src="sheep.$url.static('/static/img/shop/order/order_express.png')">
-				</image>
-				<view class="ss-font-30">{{ state.orderInfo.status_text }}</view>
-			</view>
-			<view class="ss-font-26 ss-m-x-20 ss-m-b-70">{{ state.orderInfo.status_desc }}</view>
-		</view>
-
-		<!-- 收货地址 -->
-		<view class="order-address-box" v-if="state.orderInfo.address">
-			<view class="ss-flex ss-col-center">
-				<text class="address-username">
-					{{ state.orderInfo.address.consignee }}
-				</text>
-				<text class="address-phone">{{ state.orderInfo.address.mobile }}</text>
-			</view>
-			<view class="address-detail">{{ addressText }}</view>
-		</view>
-
-		<view class="detail-goods" :style="[{ marginTop: state.orderInfo.address ? '0' : '-40rpx' }]">
-			<!-- 订单信息 -->
-			<view class="order-list" v-for="item in state.orderInfo.items" :key="item.goods_id">
-				<view class="order-card">
-					<s-goods-item @tap="onGoodsDetail(item.goods_id)" :img="item.goods_image" :title="item.goods_title"
-						:skuText="item.goods_sku_text" :price="item.goods_price" :score="state.orderInfo.score_amount"
-						:num="item.goods_num">
-						<!-- 						<template #top>
-							<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white">
-								<view class="item-title">配送方式</view>
-								<view class="ss-flex ss-col-center">
-									<text class="item-value ss-m-r-20">{{ item.dispatch_type_text }}</text>
-									<button class="ss-reset-button copy-btn" @tap="onDetail(item)" v-if="
-                      (item.dispatch_type === 'autosend' || item.dispatch_type === 'custom') &&
-                      item.dispatch_status !== 0
-                    ">详情</button>
-								</view>
-							</view>
-						</template>
-						<template #tool>
-							<view class="ss-flex">
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale')"
-									@tap.stop="
+          "
+          class="state-img"
+          :src="sheep.$url.static('/static/img/shop/order/order_success.png')"
+        >
+        </image>
+        <image
+          v-if="state.orderInfo.status_code == 'cancel' || state.orderInfo.status_code == 'closed'"
+          class="state-img"
+          :src="sheep.$url.static('/static/img/shop/order/order_close.png')"
+        >
+        </image>
+        <image
+          v-if="state.orderInfo.status_code == 'noget'"
+          class="state-img"
+          :src="sheep.$url.static('/static/img/shop/order/order_express.png')"
+        >
+        </image>
+        <view class="ss-font-30">{{ formatOrderStatus(state.orderInfo) }}</view>
+      </view>
+      <view class="ss-font-26 ss-m-x-20 ss-m-b-70">{{
+        formatOrderStatusDescription(state.orderInfo)
+      }}</view>
+    </view>
+
+    <!-- 收货地址 -->
+    <view class="order-address-box" v-if="state.orderInfo.receiverAreaId > 0">
+      <view class="ss-flex ss-col-center">
+        <text class="address-username">
+          {{ state.orderInfo.receiverName }}
+        </text>
+        <text class="address-phone">{{ state.orderInfo.receiverMobile }}</text>
+      </view>
+      <view class="address-detail">
+        {{ state.orderInfo.receiverAreaName }} {{ state.orderInfo.receiverDetailAddress }}
+      </view>
+    </view>
+
+    <view
+      class="detail-goods"
+      :style="[{ marginTop: state.orderInfo.receiverAreaId > 0 ? '0' : '-40rpx' }]"
+    >
+      <!-- 订单信息 TODO 芋艿: -->
+      <view class="order-list" v-for="item in state.orderInfo.items" :key="item.goods_id">
+        <view class="order-card">
+          <s-goods-item
+            @tap="onGoodsDetail(item.skuId)"
+            :img="item.picUrl"
+            :title="item.spuName"
+            :skuText="item.properties.map((property) => property.valueName).join(' ')"
+            :price="item.price"
+            :num="item.count"
+          >
+            <template #tool>
+              <view class="ss-flex">
+                <button
+                  class="ss-reset-button apply-btn"
+                  v-if="[10, 20, 30].includes(state.orderInfo.status) && item.afterSaleStatus === 0"
+                  @tap.stop="
                     sheep.$router.go('/pages/order/aftersale/apply', {
-                      item: JSON.stringify(item),
+                      orderId: state.orderInfo.id,
+                      itemId: item.id,
                     })
-                  ">
-									申请售后
-								</button>
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('re_aftersale')"
-									@tap.stop="
-                    sheep.$router.go('/pages/order/aftersale/apply', {
-                      item: JSON.stringify(item),
-                    })
-                  ">
-									重新售后
-								</button>
-
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale_info')"
-									@tap.stop="
+                  "
+                >
+                  申请售后
+                </button>
+                <button
+                  class="ss-reset-button apply-btn"
+                  v-if="item.afterSaleStatus === 10"
+                  @tap.stop="
                     sheep.$router.go('/pages/order/aftersale/detail', {
-                      id: item.ext.aftersale_id,
+                      id: item.afterSaleId,
                     })
-                  ">
-									售后详情
-								</button>
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('buy_again')"
-									@tap.stop="
-                    sheep.$router.go('/pages/goods/index', {
-                      id: item.goods_id,
+                  "
+                >
+                  退款中
+                </button>
+                <button
+                  class="ss-reset-button apply-btn"
+                  v-if="item.afterSaleStatus === 20"
+                  @tap.stop="
+                    sheep.$router.go('/pages/order/aftersale/detail', {
+                      id: item.afterSaleId,
                     })
-                  ">
-									再次购买
-								</button>
-							</view>
-						</template>
-						<template #priceSuffix>
-							<button class="ss-reset-button tag-btn" v-if="item.status_text">
-								{{ item.status_text }}
-							</button>
-						</template> -->
-					</s-goods-item>
-				</view>
-			</view>
-		</view>
-		<!-- 订单信息  -->
-		<view class="notice-box">
-			<view class="notice-box__content">
-				<view class="notice-item--center">
-					<view class="ss-flex ss-flex-1">
-						<text class="title">订单编号:</text>
-						<text class="detail">{{ state.orderInfo.order_sn }}</text>
-					</view>
-					<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
-				</view>
-				<view class="notice-item">
-					<text class="title">下单时间:</text>
-					<text class="detail">{{ state.orderInfo.create_time }}</text>
-				</view>
-				<view class="notice-item" v-if="state.orderInfo.paid_time">
-					<text class="title">支付时间:</text>
-					<text class="detail">{{ state.orderInfo.paid_time || '-' }}</text>
-				</view>
-				<view class="notice-item">
-					<text class="title">支付方式:</text>
-					<text class="detail">{{ state.orderInfo.pay_types_text?.join(',') || '-' }}</text>
-				</view>
-			</view>
-		</view>
-		<!--  价格信息  -->
-		<view class="order-price-box">
-			<view class="notice-item ss-flex ss-row-between">
-				<text class="title">商品总额</text>
-				<view class="ss-flex">
-					<text class="detail"
-						v-if="Number(state.orderInfo.goods_amount) > 0">¥{{ state.orderInfo.goods_amount }}</text>
-					<view v-if="state.orderInfo.score_amount && Number(state.orderInfo.goods_amount) > 0"
-						class="detail">+</view>
-					<view class="price-text ss-flex ss-col-center" v-if="state.orderInfo.score_amount">
-						<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
-						<view class="detail">{{ state.orderInfo.score_amount }}</view>
-					</view>
-				</view>
-			</view>
-			<view class="notice-item ss-flex ss-row-between">
-				<text class="title">运费</text>
-				<text class="detail">¥{{ state.orderInfo.dispatch_amount }}</text>
-			</view>
-			<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.total_discount_fee > 0">
-				<text class="title">优惠金额</text>
-				<text class="detail">¥{{ state.orderInfo.total_discount_fee }}</text>
-			</view>
-			<view class="notice-item all-rpice-item ss-flex ss-m-t-20">
-				<text class="title">{{
-          ['paid', 'completed'].includes(state.orderInfo.status) ? '已付款' : '需付款'
-        }}</text>
-				<text class="detail all-price"
-					v-if="Number(state.orderInfo.pay_fee) > 0">¥{{ state.orderInfo.pay_fee }}</text>
-				<view v-if="
-            state.orderInfo.score_amount &&
-            Number(state.orderInfo.pay_fee) > 0 &&
-            ['paid', 'completed'].includes(state.orderInfo.status)
-          " class="detail all-price">+</view>
-				<view class="price-text ss-flex ss-col-center" v-if="
-            state.orderInfo.score_amount && ['paid', 'completed'].includes(state.orderInfo.status)
-          ">
-					<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
-					<view class="detail all-price">{{ state.orderInfo.score_amount }}</view>
-				</view>
-			</view>
-			<view class="notice-item all-rpice-item ss-flex ss-m-t-20" v-if="refundFee > 0">
-				<text class="title">已退款</text>
-				<text class="detail all-price">¥{{ refundFee.toFixed(2) }}</text>
-			</view>
-		</view>
-
-		<!-- 底部按钮 -->
-		<!-- TODO: 查看物流、等待成团、评价完后返回页面没刷新页面 -->
-		<su-fixed bottom placeholder bg="bg-white" v-if="state.orderInfo.btns?.length">
-			<view class="footer-box ss-flex ss-col-center ss-row-right">
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('cancel')"
-					@tap="onCancel(state.orderInfo.id)">取消订单</button>
-				<button class="ss-reset-button pay-btn ui-BG-Main-Gradient" v-if="state.orderInfo.btns?.includes('pay')"
-					@tap="onPay(state.orderInfo.order_sn)">继续支付</button>
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('apply_refund')"
-					@tap="onRefund(state.orderInfo.id)">申请退款</button>
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('groupon')" @tap="
+                  "
+                >
+                  退款成功
+                </button>
+              </view>
+            </template>
+            <template #priceSuffix>
+              <button class="ss-reset-button tag-btn" v-if="item.status_text">
+                {{ item.status_text }}
+              </button>
+            </template>
+          </s-goods-item>
+        </view>
+      </view>
+    </view>
+
+    <!-- 订单信息 -->
+    <view class="notice-box">
+      <view class="notice-box__content">
+        <view class="notice-item--center">
+          <view class="ss-flex ss-flex-1">
+            <text class="title">订单编号:</text>
+            <text class="detail">{{ state.orderInfo.no }}</text>
+          </view>
+          <button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
+        </view>
+        <view class="notice-item">
+          <text class="title">下单时间:</text>
+          <text class="detail">
+            {{ sheep.$helper.timeFormat(state.orderInfo.createTime, 'yyyy-mm-dd hh:MM:ss') }}
+          </text>
+        </view>
+        <view class="notice-item" v-if="state.orderInfo.payTime">
+          <text class="title">支付时间:</text>
+          <text class="detail">
+            {{ sheep.$helper.timeFormat(state.orderInfo.payTime, 'yyyy-mm-dd hh:MM:ss') }}
+          </text>
+        </view>
+        <view class="notice-item">
+          <text class="title">支付方式:</text>
+          <text class="detail">{{ state.orderInfo.payChannelName || '-' }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 价格信息 -->
+    <view class="order-price-box">
+      <view class="notice-item ss-flex ss-row-between">
+        <text class="title">商品总额</text>
+        <view class="ss-flex">
+          <text class="detail">¥{{ fen2yuan(state.orderInfo.totalPrice) }}</text>
+        </view>
+      </view>
+      <view class="notice-item ss-flex ss-row-between">
+        <text class="title">运费</text>
+        <text class="detail">¥{{ fen2yuan(state.orderInfo.deliveryPrice) }}</text>
+      </view>
+      <!-- TODO 芋艿:优惠劵抵扣、积分抵扣 -->
+      <view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.discountPrice > 0">
+        <text class="title">优惠金额</text>
+        <text class="detail">¥{{ fen2yuan(state.orderInfo.discountPrice) }}</text>
+      </view>
+      <view class="notice-item all-rpice-item ss-flex ss-m-t-20">
+        <text class="title">{{ state.orderInfo.payStatus ? '已付款' : '需付款' }}</text>
+        <text class="detail all-price">¥{{ fen2yuan(state.orderInfo.payPrice) }}</text>
+      </view>
+      <view
+        class="notice-item all-rpice-item ss-flex ss-m-t-20"
+        v-if="state.orderInfo.refundPrice > 0"
+      >
+        <text class="title">已退款</text>
+        <text class="detail all-price">¥{{ fen2yuan(state.orderInfo.refundPrice) }}</text>
+      </view>
+    </view>
+
+    <!-- 底部按钮 -->
+    <!-- TODO: 查看物流、等待成团、评价完后返回页面没刷新页面 -->
+    <su-fixed bottom placeholder bg="bg-white" v-if="state.orderInfo.buttons?.length">
+      <view class="footer-box ss-flex ss-col-center ss-row-right">
+        <button
+          class="ss-reset-button cancel-btn"
+          v-if="state.orderInfo.buttons?.includes('cancel')"
+          @tap="onCancel(state.orderInfo.id)"
+        >
+          取消订单
+        </button>
+        <button
+          class="ss-reset-button pay-btn ui-BG-Main-Gradient"
+          v-if="state.orderInfo.buttons?.includes('pay')"
+          @tap="onPay(state.orderInfo.payOrderId)"
+        >
+          继续支付
+        </button>
+        <!-- TODO 芋艿:拼团接入 -->
+        <button
+          class="ss-reset-button cancel-btn"
+          v-if="state.orderInfo.buttons?.includes('combination')"
+          @tap="
             sheep.$router.go('/pages/activity/groupon/detail', {
               id: state.orderInfo.ext.groupon_id,
             })
-          ">
-					{{ state.orderInfo.status_code === 'groupon_ing' ? '邀请拼团' : '拼团详情' }}
-				</button>
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('express')"
-					@tap="onExpress(state.orderInfo.id)">查看物流</button>
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('confirm')"
-					@tap="onConfirm(state.orderInfo.id)">确认收货</button>
-				<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('comment')"
-					@tap="onComment(state.orderInfo.id,state.orderInfo)">评价晒单</button>
-				<button v-if="state.orderInfo.btns?.includes('invoice')" class="ss-reset-button cancel-btn"
-					@tap.stop="onOrderInvoice(state.orderInfo.invoice?.id)">
-					查看发票
-				</button>
-				<button v-if="state.orderInfo.btns?.includes('re_apply_refund')" class="ss-reset-button cancel-btn"
-					@tap.stop="onRefund(state.orderInfo.id)">
-					重新退款
-				</button>
-			</view>
-		</su-fixed>
-	</s-layout>
+          "
+        >
+          拼团详情
+        </button>
+        <button
+          class="ss-reset-button cancel-btn"
+          v-if="state.orderInfo.buttons?.includes('express')"
+          @tap="onExpress(state.orderInfo.id)"
+        >
+          查看物流
+        </button>
+        <button
+          class="ss-reset-button cancel-btn"
+          v-if="state.orderInfo.buttons?.includes('confirm')"
+          @tap="onConfirm(state.orderInfo.id)"
+        >
+          确认收货
+        </button>
+        <button
+          class="ss-reset-button cancel-btn"
+          v-if="state.orderInfo.buttons?.includes('comment')"
+          @tap="onComment(state.orderInfo.id)"
+        >
+          评价
+        </button>
+      </view>
+    </su-fixed>
+  </s-layout>
 </template>
 
 <script setup>
-	import sheep from '@/sheep';
-	import {
-		onLoad
-	} from '@dcloudio/uni-app';
-	import {
-		computed,
-		reactive
-	} from 'vue';
-	import {
-		isEmpty
-	} from 'lodash';
-
-	const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
-	const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
-	const tradeManaged = computed(() => sheep.$store('app').has_wechat_trade_managed);
-
-	const state = reactive({
-		orderInfo: {},
-		merchantTradeNo: '', // 商户订单号
-		comeinType: '', // 进入订单详情的来源类型
-	});
-
-	const addressText = computed(() => {
-		let data = state.orderInfo.address;
-		if (data) {
-			return `${data.province_name} ${data.city_name} ${data.district_name} ${data.address}`;
-		}
-		return '';
-	});
-
-	// 复制
-	const onCopy = () => {
-		sheep.$helper.copyText(state.orderInfo.order_sn);
-	};
-	//退款总额
-	const refundFee = computed(() => {
-		let refundFee = 0;
-		state.orderInfo.items?.forEach((i) => {
-			refundFee += Number(i.refund_fee);
-		});
-		return refundFee;
-	});
-	// 去支付
-	function onPay(orderSN) {
-		sheep.$router.go('/pages/pay/index', {
-			orderSN,
-		});
-	}
-
-	function onGoodsDetail(id) {
-		sheep.$router.go('/pages/goods/index', {
-			id
-		});
-	}
-
-	// 取消订单
-	async function onCancel(orderId) {
-		uni.showModal({
-			title: '提示',
-			content: '确定要取消订单吗?',
-			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error,
-						data
-					} = await sheep.$api.order.cancel(orderId);
-					if (error === 0) {
-						getOrderDetail(data.order_sn);
-					}
-				}
-			},
-		});
-	}
-
-	// 申请退款
-	async function onRefund(orderId) {
-		uni.showModal({
-			title: '提示',
-			content: '确定要申请退款吗?',
-			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error,
-						data
-					} = await sheep.$api.order.applyRefund(orderId);
-					if (error === 0) {
-						getOrderDetail(data.order_sn);
-					}
-				}
-			},
-		});
-	}
-
-	// 查看物流
-	async function onExpress(orderId) {
-		sheep.$router.go('/pages/order/express/list', {
-			orderId,
-		});
-	}
-
-	//确认收货
-	async function onConfirm(orderId, ignore = false) {
-		// 需开启确认收货组件
-		// todo:
-		// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
-		// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
-		let isOpenBusinessView = true;
-		if (
-			sheep.$platform.name === 'WechatMiniProgram' &&
-			!isEmpty(state.orderInfo.wechat_extra_data) &&
-			isOpenBusinessView &&
-			!ignore
-		) {
-			mpConfirm(orderId);
-			return;
-		}
-
-		// 正常的确认收货流程
-		const {
-			error,
-			data
-		} = await sheep.$api.order.confirm(orderId);
-		if (error === 0) {
-			getOrderDetail(data.order_sn);
-		}
-	}
-
-	// #ifdef MP-WEIXIN
-	// 小程序确认收货组件
-	function mpConfirm(orderId) {
-		if (!wx.openBusinessView) {
-			sheep.$helper.toast(`请升级微信版本`);
-			return;
-		}
-		wx.openBusinessView({
-			businessType: 'weappOrderConfirm',
-			extraData: {
-				merchant_trade_no: state.orderInfo.wechat_extra_data.merchant_trade_no,
-				transaction_id: state.orderInfo.wechat_extra_data.transaction_id,
-			},
-			success(response) {
-				console.log('success:', response);
-				if (response.errMsg === 'openBusinessView:ok') {
-					if (response.extraData.status === 'success') {
-						onConfirm(orderId, true);
-					}
-				}
-			},
-			fail(error) {
-				console.log('error:', error);
-			},
-			complete(result) {
-				console.log('result:', result);
-			},
-		});
-	}
-	// #endif
-
-	// 查看发票
-	function onOrderInvoice(invoiceId) {
-		sheep.$router.go('/pages/order/invoice', {
-			invoiceId,
-		});
-	}
-
-	// 配送方式详情
-	function onDetail(item) {
-		sheep.$router.go('/pages/order/dispatch/content', {
-			id: item.order_id,
-			item_id: item.id,
-		});
-	}
-
-	// 评价
-	function onComment(orderSN, orderId) {
-		console.log(orderId);
-		// return;
-		uni.$once('SELECT_INVOICE', (e) => {
-			state.invoiceInfo = e.invoiceInfo;
-		});
-		sheep.$router.go('/pages/goods/comment/add', {
-			orderSN,
-			orderId
-		});
-	}
-	async function getOrderDetail(id) {
-		// 对详情数据进行适配
-		let res = {};
-		if (state.comeinType === 'wechat') {
-			res = await sheep.$api.order.detail(id, {
-				merchant_trade_no: state.merchantTradeNo,
-			});
-		} else {
-			res = await sheep.$api.order.detail(id);
-		}
-		console.log(res, '我的订单详情数据');
-		if (res.code === 0) {
-			let obj = {
-				10: ['待发货', '等待买家付款', ["apply_refund"]],
-				30: ['待评价', '等待买家评价', ["express", "comment"]]
-			}
-			res.data.status_text = obj[res.data.status][0];
-			res.data.status_desc = obj[res.data.status][1];
-			res.data.btns = obj[res.data.status][2];
-			res.data.address = {
-				province_name: res.data.receiverAreaName.split(' ')[0],
-				district_name: res.data.receiverAreaName.split(' ')[2],
-				city_name: res.data.receiverAreaName.split(' ')[1],
-				address: res.data.receiverDetailAddress,
-				consignee: res.data.receiverName,
-				mobile: res.data.receiverMobile,
-			}
-			res.data.pay_fee = res.data.payPrice / 100
-			res.data.create_time = sheep.$helper.timeFormat(res.data.createTime, 'yyyy-mm-dd hh:MM:ss')
-			res.data.order_sn = res.data.no
-			res.data.goods_amount = res.data.totalPrice / 100
-			res.data.dispatch_amount = res.data.deliveryPrice / 100
-			res.data.pay_types_text = res.data.payChannelName.split(',')
-			res.data.items = res.data.items.map(ite => {
-
-				return {
-					...ite,
-					goods_title: ite.spuName,
-					goods_num: ite.count,
-					goods_price: ite.price / 100,
-					goods_image: ite.picUrl,
-					goods_sku_text: ite.properties.reduce((it0, it1) => it0 + it1.valueName + ' ', '')
-				}
-			})
-			state.orderInfo = res.data;
-			console.log(state.orderInfo, '修改后数据')
-		} else {
-			sheep.$router.back();
-		}
-	}
-
-	onLoad(async (options) => {
-		let id = 0;
-		if (options.orderSN) {
-			id = options.orderSN;
-		}
-		if (options.id) {
-			id = options.id;
-		}
-		state.comeinType = options.comein_type;
-		if (state.comeinType === 'wechat') {
-			state.merchantTradeNo = options.merchant_trade_no;
-		}
-		getOrderDetail(id);
-	});
+  import sheep from '@/sheep';
+  import { onLoad } from '@dcloudio/uni-app';
+  import { reactive } from 'vue';
+  import { isEmpty } from 'lodash';
+  import {
+    fen2yuan,
+    formatOrderStatus,
+    formatOrderStatusDescription,
+    handleOrderButtons,
+  } from '@/sheep/hooks/useGoods';
+  import OrderApi from '@/sheep/api/trade/order';
+
+  const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
+  const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
+
+  const state = reactive({
+    orderInfo: {},
+    merchantTradeNo: '', // 商户订单号
+    comeinType: '', // 进入订单详情的来源类型
+  });
+
+  // 复制
+  const onCopy = () => {
+    sheep.$helper.copyText(state.orderInfo.sn);
+  };
+
+  // 去支付
+  function onPay(payOrderId) {
+    sheep.$router.go('/pages/pay/index', {
+      id: payOrderId,
+    });
+  }
+
+  // 查看商品
+  function onGoodsDetail(id) {
+    sheep.$router.go('/pages/goods/index', {
+      id,
+    });
+  }
+
+  // 取消订单
+  async function onCancel(orderId) {
+    uni.showModal({
+      title: '提示',
+      content: '确定要取消订单吗?',
+      success: async function (res) {
+        if (!res.confirm) {
+          return;
+        }
+        const { code } = await OrderApi.cancelOrder(orderId);
+        if (code === 0) {
+          await getOrderDetail(orderId);
+        }
+      },
+    });
+  }
+
+  // 查看物流
+  async function onExpress(id) {
+    sheep.$router.go('/pages/order/express/log', {
+      id,
+    });
+  }
+
+  // 确认收货 TODO 芋艿:待测试
+  async function onConfirm(orderId, ignore = false) {
+    // 需开启确认收货组件
+    // todo: 芋艿:待接入微信
+    // 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
+    // 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
+    let isOpenBusinessView = true;
+    if (
+      sheep.$platform.name === 'WechatMiniProgram' &&
+      !isEmpty(state.orderInfo.wechat_extra_data) &&
+      isOpenBusinessView &&
+      !ignore
+    ) {
+      mpConfirm(orderId);
+      return;
+    }
+
+    // 正常的确认收货流程
+    const { code } = await OrderApi.receiveOrder(orderId);
+    if (code === 0) {
+      await getOrderDetail(orderId);
+    }
+  }
+
+  // #ifdef MP-WEIXIN
+  // 小程序确认收货组件
+  function mpConfirm(orderId) {
+    if (!wx.openBusinessView) {
+      sheep.$helper.toast(`请升级微信版本`);
+      return;
+    }
+    wx.openBusinessView({
+      businessType: 'weappOrderConfirm',
+      extraData: {
+        merchant_trade_no: state.orderInfo.wechat_extra_data.merchant_trade_no,
+        transaction_id: state.orderInfo.wechat_extra_data.transaction_id,
+      },
+      success(response) {
+        console.log('success:', response);
+        if (response.errMsg === 'openBusinessView:ok') {
+          if (response.extraData.status === 'success') {
+            onConfirm(orderId, true);
+          }
+        }
+      },
+      fail(error) {
+        console.log('error:', error);
+      },
+      complete(result) {
+        console.log('result:', result);
+      },
+    });
+  }
+  // #endif
+
+  // 评价
+  function onComment(id) {
+    sheep.$router.go('/pages/goods/comment/add', {
+      id
+    });
+  }
+
+  async function getOrderDetail(id) {
+    // 对详情数据进行适配
+    let res;
+    if (state.comeinType === 'wechat') {
+      res = await sheep.$api.order.detail(id, {
+        merchant_trade_no: state.merchantTradeNo,
+      });
+    } else {
+      res = await OrderApi.getOrder(id);
+    }
+    if (res.code === 0) {
+      state.orderInfo = res.data;
+      handleOrderButtons(state.orderInfo);
+    } else {
+      sheep.$router.back();
+    }
+  }
+
+  onLoad(async (options) => {
+    let id = 0;
+    if (options.id) {
+      id = options.id;
+    }
+    // TODO 芋艿:下面两个变量,后续接入
+    state.comeinType = options.comein_type;
+    if (state.comeinType === 'wechat') {
+      state.merchantTradeNo = options.merchant_trade_no;
+    }
+    await getOrderDetail(id);
+  });
 </script>
 
 <style lang="scss" scoped>
-	.score-img {
-		width: 36rpx;
-		height: 36rpx;
-		margin: 0 4rpx;
-	}
-
-	.apply-btn {
-		width: 140rpx;
-		height: 50rpx;
-		border-radius: 25rpx;
-		font-size: 24rpx;
-		border: 2rpx solid #dcdcdc;
-		line-height: normal;
-		margin-left: 16rpx;
-	}
-
-	.state-box {
-		color: rgba(#fff, 0.9);
-		width: 100%;
-		background: v-bind(headerBg) no-repeat,
-			linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-		background-size: 750rpx 100%;
-		box-sizing: border-box;
-
-		.state-img {
-			width: 60rpx;
-			height: 60rpx;
-			margin-right: 20rpx;
-		}
-	}
-
-	.order-address-box {
-		background-color: #fff;
-		border-radius: 10rpx;
-		margin: -50rpx 20rpx 16rpx 20rpx;
-		padding: 44rpx 34rpx 42rpx 20rpx;
-		font-size: 30rpx;
-		box-sizing: border-box;
-		font-weight: 500;
-		color: rgba(51, 51, 51, 1);
-
-		.address-username {
-			margin-right: 20rpx;
-		}
-
-		.address-detail {
-			font-size: 26rpx;
-			font-weight: 500;
-			color: rgba(153, 153, 153, 1);
-			margin-top: 20rpx;
-		}
-	}
-
-	.detail-goods {
-		border-radius: 10rpx;
-		margin: 0 20rpx 20rpx 20rpx;
-
-		.order-list {
-			margin-bottom: 20rpx;
-			background-color: #fff;
-
-			.order-card {
-				padding: 20rpx 0;
-
-				.order-sku {
-					font-size: 24rpx;
-
-					font-weight: 400;
-					color: rgba(153, 153, 153, 1);
-					width: 450rpx;
-					margin-bottom: 20rpx;
-
-					.order-num {
-						margin-right: 10rpx;
-					}
-				}
-
-				.tag-btn {
-					margin-left: 16rpx;
-					font-size: 24rpx;
-					height: 36rpx;
-					color: var(--ui-BG-Main);
-					border: 2rpx solid var(--ui-BG-Main);
-					border-radius: 14rpx;
-					padding: 0 4rpx;
-				}
-			}
-		}
-	}
-
-	// 订单信息。
-	.notice-box {
-		background: #fff;
-		border-radius: 10rpx;
-		margin: 0 20rpx 20rpx 20rpx;
-
-		.notice-box__head {
-			font-size: 30rpx;
-
-			font-weight: 500;
-			color: rgba(51, 51, 51, 1);
-			line-height: 80rpx;
-			border-bottom: 1rpx solid #dfdfdf;
-			padding: 0 25rpx;
-		}
-
-		.notice-box__content {
-			padding: 20rpx;
-
-			.self-pickup-box {
-				width: 100%;
-
-				.self-pickup--img {
-					width: 200rpx;
-					height: 200rpx;
-					margin: 40rpx 0;
-				}
-			}
-		}
-
-		.notice-item,
-		.notice-item--center {
-			display: flex;
-			align-items: center;
-			line-height: normal;
-			margin-bottom: 24rpx;
-
-			.title {
-				font-size: 28rpx;
-				color: #999;
-			}
-
-			.detail {
-				font-size: 28rpx;
-				color: #333;
-				flex: 1;
-			}
-		}
-	}
-
-	.copy-btn {
-		width: 100rpx;
-		line-height: 50rpx;
-		border-radius: 25rpx;
-		padding: 0;
-		background: rgba(238, 238, 238, 1);
-		font-size: 22rpx;
-		font-weight: 400;
-		color: rgba(51, 51, 51, 1);
-	}
-
-	// 订单价格信息
-	.order-price-box {
-		background-color: #fff;
-		border-radius: 10rpx;
-		padding: 20rpx;
-		margin: 0 20rpx 20rpx 20rpx;
-
-		.notice-item {
-			line-height: 70rpx;
-
-			.title {
-				font-size: 28rpx;
-				color: #999;
-			}
-
-			.detail {
-				font-size: 28rpx;
-				color: #333;
-				font-family: OPPOSANS;
-			}
-		}
-
-		.all-rpice-item {
-			justify-content: flex-end;
-			align-items: center;
-
-			.title {
-				font-size: 26rpx;
-				font-weight: 500;
-				color: #333333;
-				line-height: normal;
-			}
-
-			.all-price {
-				font-size: 26rpx;
-				font-family: OPPOSANS;
-				line-height: normal;
-				color: $red;
-			}
-		}
-	}
-
-	// 底部
-	.footer-box {
-		height: 100rpx;
-		width: 100%;
-		box-sizing: border-box;
-		border-radius: 10rpx;
-		padding-right: 20rpx;
-
-		.cancel-btn {
-			width: 160rpx;
-			height: 60rpx;
-			background: #eeeeee;
-			border-radius: 30rpx;
-			margin-right: 20rpx;
-			font-size: 26rpx;
-			font-weight: 400;
-			color: #333333;
-		}
-
-		.pay-btn {
-			width: 160rpx;
-			height: 60rpx;
-			font-size: 26rpx;
-			border-radius: 30rpx;
-			font-weight: 500;
-			color: #fff;
-		}
-	}
-</style>
+  .score-img {
+    width: 36rpx;
+    height: 36rpx;
+    margin: 0 4rpx;
+  }
+
+  .apply-btn {
+    width: 140rpx;
+    height: 50rpx;
+    border-radius: 25rpx;
+    font-size: 24rpx;
+    border: 2rpx solid #dcdcdc;
+    line-height: normal;
+    margin-left: 16rpx;
+  }
+
+  .state-box {
+    color: rgba(#fff, 0.9);
+    width: 100%;
+    background: v-bind(headerBg) no-repeat,
+      linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+    background-size: 750rpx 100%;
+    box-sizing: border-box;
+
+    .state-img {
+      width: 60rpx;
+      height: 60rpx;
+      margin-right: 20rpx;
+    }
+  }
+
+  .order-address-box {
+    background-color: #fff;
+    border-radius: 10rpx;
+    margin: -50rpx 20rpx 16rpx 20rpx;
+    padding: 44rpx 34rpx 42rpx 20rpx;
+    font-size: 30rpx;
+    box-sizing: border-box;
+    font-weight: 500;
+    color: rgba(51, 51, 51, 1);
+
+    .address-username {
+      margin-right: 20rpx;
+    }
+
+    .address-detail {
+      font-size: 26rpx;
+      font-weight: 500;
+      color: rgba(153, 153, 153, 1);
+      margin-top: 20rpx;
+    }
+  }
+
+  .detail-goods {
+    border-radius: 10rpx;
+    margin: 0 20rpx 20rpx 20rpx;
+
+    .order-list {
+      margin-bottom: 20rpx;
+      background-color: #fff;
+
+      .order-card {
+        padding: 20rpx 0;
+
+        .order-sku {
+          font-size: 24rpx;
+
+          font-weight: 400;
+          color: rgba(153, 153, 153, 1);
+          width: 450rpx;
+          margin-bottom: 20rpx;
+
+          .order-num {
+            margin-right: 10rpx;
+          }
+        }
+
+        .tag-btn {
+          margin-left: 16rpx;
+          font-size: 24rpx;
+          height: 36rpx;
+          color: var(--ui-BG-Main);
+          border: 2rpx solid var(--ui-BG-Main);
+          border-radius: 14rpx;
+          padding: 0 4rpx;
+        }
+      }
+    }
+  }
+
+  // 订单信息。
+  .notice-box {
+    background: #fff;
+    border-radius: 10rpx;
+    margin: 0 20rpx 20rpx 20rpx;
+
+    .notice-box__head {
+      font-size: 30rpx;
+
+      font-weight: 500;
+      color: rgba(51, 51, 51, 1);
+      line-height: 80rpx;
+      border-bottom: 1rpx solid #dfdfdf;
+      padding: 0 25rpx;
+    }
+
+    .notice-box__content {
+      padding: 20rpx;
+
+      .self-pickup-box {
+        width: 100%;
+
+        .self-pickup--img {
+          width: 200rpx;
+          height: 200rpx;
+          margin: 40rpx 0;
+        }
+      }
+    }
+
+    .notice-item,
+    .notice-item--center {
+      display: flex;
+      align-items: center;
+      line-height: normal;
+      margin-bottom: 24rpx;
+
+      .title {
+        font-size: 28rpx;
+        color: #999;
+      }
+
+      .detail {
+        font-size: 28rpx;
+        color: #333;
+        flex: 1;
+      }
+    }
+  }
+
+  .copy-btn {
+    width: 100rpx;
+    line-height: 50rpx;
+    border-radius: 25rpx;
+    padding: 0;
+    background: rgba(238, 238, 238, 1);
+    font-size: 22rpx;
+    font-weight: 400;
+    color: rgba(51, 51, 51, 1);
+  }
+
+  // 订单价格信息
+  .order-price-box {
+    background-color: #fff;
+    border-radius: 10rpx;
+    padding: 20rpx;
+    margin: 0 20rpx 20rpx 20rpx;
+
+    .notice-item {
+      line-height: 70rpx;
+
+      .title {
+        font-size: 28rpx;
+        color: #999;
+      }
+
+      .detail {
+        font-size: 28rpx;
+        color: #333;
+        font-family: OPPOSANS;
+      }
+    }
+
+    .all-rpice-item {
+      justify-content: flex-end;
+      align-items: center;
+
+      .title {
+        font-size: 26rpx;
+        font-weight: 500;
+        color: #333333;
+        line-height: normal;
+      }
+
+      .all-price {
+        font-size: 26rpx;
+        font-family: OPPOSANS;
+        line-height: normal;
+        color: $red;
+      }
+    }
+  }
+
+  // 底部
+  .footer-box {
+    height: 100rpx;
+    width: 100%;
+    box-sizing: border-box;
+    border-radius: 10rpx;
+    padding-right: 20rpx;
+
+    .cancel-btn {
+      width: 160rpx;
+      height: 60rpx;
+      background: #eeeeee;
+      border-radius: 30rpx;
+      margin-right: 20rpx;
+      font-size: 26rpx;
+      font-weight: 400;
+      color: #333333;
+    }
+
+    .pay-btn {
+      width: 160rpx;
+      height: 60rpx;
+      font-size: 26rpx;
+      border-radius: 30rpx;
+      font-weight: 500;
+      color: #fff;
+    }
+  }
+</style>

+ 0 - 84
pages/order/dispatch/content.vue

@@ -1,84 +0,0 @@
-<template>
-  <s-layout title="发货内容">
-    <view class="order-card ss-m-x-20 ss-r-20">
-      <s-goods-item
-        :img="state.data.goods_image"
-        :title="state.data.goods_title"
-        :skuText="state.data.goods_sku_text"
-        :price="state.data.goods_price"
-        :num="state.data.goods_num"
-        radius="20"
-      >
-        <template #priceSuffix>
-          <button class="ss-reset-button tag-btn" v-if="state.data.status_text">
-            {{ state.data.status_text }}
-          </button>
-        </template>
-      </s-goods-item>
-    </view>
-    <view class="bg-white ss-p-20 ss-m-x-20 ss-r-20">
-      <view class="title ss-m-b-26">发货信息</view>
-      <view v-if="state.data.ext?.dispatch_content_type === 'params'">
-        <view class="desc ss-m-b-20" v-for="item in state.data.ext.dispatch_content" :key="item">
-          {{ item.title }}: {{ item.content }}
-        </view>
-      </view>
-      <view class="desc" v-else>{{ state.data.ext?.dispatch_content }}</view>
-    </view>
-  </s-layout>
-</template>
-<script setup>
-  import { onLoad } from '@dcloudio/uni-app';
-  import { reactive } from 'vue';
-  import sheep from '@/sheep';
-
-  const state = reactive({
-    data: [],
-  });
-  async function getDetail(id, item_id) {
-    const { error, data } = await sheep.$api.order.itemDetail(id,item_id);
-    if (error === 0) {
-      state.data = data;
-    }
-  }
-  onLoad((options) => {
-    getDetail(options.id, options.item_id);
-  });
-</script>
-<style lang="scss" scoped>
-  .order-card {
-    padding: 20rpx 0;
-
-    .order-sku {
-      font-size: 24rpx;
-
-      font-weight: 400;
-      color: rgba(153, 153, 153, 1);
-      width: 450rpx;
-      margin-bottom: 20rpx;
-
-      .order-num {
-        margin-right: 10rpx;
-      }
-    }
-    .tag-btn {
-      margin-left: 16rpx;
-      font-size: 24rpx;
-      height: 36rpx;
-      color: var(--ui-BG-Main);
-      border: 2rpx solid var(--ui-BG-Main);
-      border-radius: 14rpx;
-      padding: 0 4rpx;
-    }
-  }
-  .title {
-    font-size: 28rpx;
-    font-weight: bold;
-    color: #333333;
-  }
-  .desc {
-    font-size: 26rpx;
-    font-weight: 400;
-    color: #333333;
-  }
-</style>

+ 0 - 104
pages/order/express/list.vue

@@ -1,104 +0,0 @@
-<!-- 物流包裹-->
-<template>
-  <s-layout title="物流包裹">
-    <view class="express-wrap">
-      <su-sticky bgColor="#FFE2B6">
-        <view class="header ss-flex ss-p-l-24">{{ state.list.length }}个包裹已派送</view>
-      </su-sticky>
-      <view
-        class="express-box"
-        v-for="item in state.list"
-        :key="item.type"
-        @tap="sheep.$router.go('/pages/order/express/log', { id: item.id, orderId: state.orderId })"
-      >
-        <view class="express-box-header ss-flex ss-row-between">
-          <view class="express-box-header-type">{{ item.status_text }}</view>
-          <view class="express-box-header-num">{{
-            item.express_name + ' : ' + item.express_no
-          }}</view>
-        </view>
-        <view class="express-box-content">
-          <view class="content-address">{{ item.logs[0]?.content }}</view>
-          <view class="" v-if="item.items?.length">
-            <scroll-view class="scroll-box" scroll-x scroll-anchoring>
-              <view class="ss-flex">
-                <view v-for="i in item.items" :key="i" class="ss-m-r-20"
-                  ><image class="content-img" :src="sheep.$url.static(i.goods_image)" />
-                </view>
-              </view>
-            </scroll-view>
-          </view>
-        </view>
-        <view class="express-box-foot">共{{ item.items.length }}件商品</view>
-      </view>
-    </view>
-  </s-layout>
-</template>
-
-<script setup>
-  import sheep from '@/sheep';
-  import { onLoad } from '@dcloudio/uni-app';
-  import { computed, reactive } from 'vue';
-  const state = reactive({
-    list: [],
-    orderId: '',
-  });
-  async function getExpressList(id) {
-    const { data } = await sheep.$api.order.express(id, '');
-    state.list = data;
-  }
-  onLoad((Option) => {
-    state.orderId = Option.orderId;
-    getExpressList(state.orderId);
-  });
-</script>
-
-<style lang="scss" scoped>
-  .header {
-    height: 84rpx;
-    font-size: 30rpx;
-    font-weight: 500;
-    color: #a8700d;
-  }
-  .express-box {
-    background: #fff;
-    padding-bottom: 30rpx;
-    box-sizing: border-box;
-    margin-bottom: 20rpx;
-    .express-box-header {
-      height: 76rpx;
-      padding: 0 24rpx;
-      border-bottom: 2rpx solid rgba(#dfdfdf, 0.5);
-      .express-box-header-type {
-        font-size: 26rpx;
-        font-weight: 500;
-        color: #999;
-      }
-      .express-box-header-num {
-        font-size: 26rpx;
-        font-weight: 400;
-        color: #999999;
-      }
-    }
-    .express-box-content {
-      padding: 20rpx 24rpx;
-      .content-address {
-        font-size: 28rpx;
-        font-weight: 400;
-        color: #333333;
-        line-height: normal;
-        margin-bottom: 20rpx;
-      }
-      .content-img {
-        width: 180rpx;
-        height: 180rpx;
-      }
-    }
-    .express-box-foot {
-      padding: 0 24rpx;
-      font-size: 24rpx;
-      font-weight: 400;
-      color: #999999;
-    }
-  }
-</style>

+ 37 - 49
pages/order/express/log.vue

@@ -2,65 +2,46 @@
 <template>
   <s-layout title="物流追踪">
     <view class="log-wrap">
+      <!-- 商品信息 -->
       <view class="log-card ss-flex ss-m-20 ss-r-10" v-if="goodsImages.length > 0">
         <uni-swiper-dot :info="goodsImages" :current="state.current" mode="round">
-          <swiper class="swiper-box" @change="change">
+          <swiper class="swiper-box">
             <swiper-item v-for="(item, index) in goodsImages" :key="index">
-              <image class="log-card-img" :src="sheep.$url.static(item.image)"></image>
+              <image class="log-card-img" :src="sheep.$url.static(item.image)" />
             </swiper-item>
           </swiper>
         </uni-swiper-dot>
-
         <view class="log-card-msg">
-          <view class="ss-flex ss-m-b-8">
-            <view>物流状态:</view>
-            <view class="warning-color">{{ state.info.status_text }}</view>
-          </view>
-          <view class="ss-m-b-8">快递单号:{{ state.info.express_no }}</view>
-          <view>快递公司:{{ state.info.express_name }}</view>
+          <!-- TODO 芋艿:优化点:展示状态 -->
+<!--          <view class="ss-flex ss-m-b-8">-->
+<!--            <view>物流状态:</view>-->
+<!--            <view class="warning-color">{{ state.info.status_text }}</view>-->
+<!--          </view>-->
+          <view class="ss-m-b-8">快递单号:{{ state.info.logisticsNo }}</view>
+          <view>快递公司:{{ state.info.logisticsName }}</view>
         </view>
       </view>
+
+      <!-- 物流轨迹 -->
       <view class="log-content ss-m-20 ss-r-10">
         <view
           class="log-content-box ss-flex"
-          v-for="(item, index) in state.info.logs"
+          v-for="(item, index) in state.tracks"
           :key="item.title"
         >
           <view class="log-icon ss-flex-col ss-col-center ss-m-r-20">
-            <text
-              v-if="state.info.logs[index].status === state.info.logs[index - 1]?.status"
-              class="cicon-title"
-            ></text>
-            <text
-              v-if="state.info.logs[index].status != state.info.logs[index - 1]?.status"
-              :class="[
-                index === 0 ? 'activity-color' : 'info-color',
-                item.status === 'transport'
-                  ? 'sicon-transport'
-                  : item.status === 'delivery'
-                  ? 'sicon-delivery'
-                  : item.status === 'collect'
-                  ? 'sicon-a-collectmaterials'
-                  : item.status === 'fail' || item.status === 'back' || item.status === 'refuse'
-                  ? 'sicon-circleclose'
-                  : item.status === 'signfor'
-                  ? 'sicon-circlecheck'
-                  : 'sicon-warning-outline',
-              ]"
-            ></text>
-            <view v-if="state.info.logs.length - 1 != index" class="line"></view>
+            <text class="cicon-title" />
+            <view v-if="state.tracks.length - 1 !== index" class="line" />
           </view>
           <view class="log-content-msg">
-            <view
-              v-if="
-                item.status_text &&
-                state.info.logs[index].status != state.info.logs[index - 1]?.status
-              "
-              class="log-msg-title ss-m-b-20"
-              >{{ item.status_text }}</view
-            >
+            <!-- TODO 芋艿:优化点:展示状态 -->
+<!--            <view class="log-msg-title ss-m-b-20">-->
+<!--              {{ item.status_text }}-->
+<!--            </view>-->
             <view class="log-msg-desc ss-m-b-16">{{ item.content }}</view>
-            <view class="log-msg-date ss-m-b-40">{{ item.change_date }}</view>
+            <view class="log-msg-date ss-m-b-40">
+              {{ sheep.$helper.timeFormat(item.time, 'yyyy-mm-dd hh:MM:ss') }}
+            </view>
           </view>
         </view>
       </view>
@@ -72,31 +53,38 @@
   import sheep from '@/sheep';
   import { onLoad } from '@dcloudio/uni-app';
   import { computed, reactive } from 'vue';
+  import OrderApi from '@/sheep/api/trade/order';
 
   const state = reactive({
     info: [],
-    current: 0,
+    tracks: [],
   });
+
   const goodsImages = computed(() => {
     let array = [];
     if (state.info.items) {
       state.info.items.forEach((item) => {
         array.push({
-          image: item.goods_image,
+          image: item.picUrl,
         });
       });
     }
     return array;
   });
-  function change(e) {
-    state.current = e.detail.current;
+
+  async function getExpressDetail(id) {
+    const { data } = await OrderApi.getOrderExpressTrackList(id);
+    state.tracks = data.reverse();
   }
-  async function getExpressdetail(id, orderId) {
-    const { data } = await sheep.$api.order.express(id, orderId);
+
+  async function getOrderDetail(id) {
+    const { data } = await OrderApi.getOrder(id)
     state.info = data;
   }
-  onLoad((Option) => {
-    getExpressdetail(Option.id, Option.orderId);
+
+  onLoad((options) => {
+    getExpressDetail(options.id);
+    getOrderDetail(options.id);
   });
 </script>
 

+ 0 - 329
pages/order/invoice.vue

@@ -1,329 +0,0 @@
-<!-- 订单详情 -->
-<template>
-  <s-layout title="发票详情" class="invoice-wrap" navbar="inner">
-    <view
-      class="invoice-heard ss-flex-col ss-row-right ss-col-center"
-      :style="[
-        {
-          marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
-          paddingTop: Number(statusBarHeight + 88) + 'rpx',
-        },
-      ]"
-    >
-      <view class="ss-flex ss-m-t-32 ss-m-b-32">
-        <text
-          class="sicon-warning-line"
-          v-if="state.data.status === 'waiting' || state.data.status === 'unpaid'"
-        ></text>
-        <text class="sicon-check-line" v-if="state.data.status === 'finish'"></text>
-        <view class="invoice-heard-title">{{ state.data.status_text }}</view>
-      </view>
-      <view class="ss-flex ss-m-b-52">
-        <view class="ss-m-r-20 invoice-heard-desc">预计可开发票金额:</view>
-        <view class="invoice-heard-price">¥{{ state.data.amount }}</view>
-      </view>
-    </view>
-    <view class="invoice-content ss-flex-col ss-col-center">
-      <view class="ss-m-t-50 ss-m-b-42 invoice-content-title">增值税电子普通发票</view>
-      <view class="ss-flex ss-m-b-64">
-        <view v-for="(item, index) in state.info" :key="item.title">
-          <view class="log-icon ss-flex">
-            <text class="sicon-circlecheck" v-if="statusNum >= index"></text>
-            <text class="sicon-unchecked" v-else></text>
-            <view
-              v-if="state.info.length - 1 != index"
-              class="line"
-              :class="statusNum >= index ? 'activity-color' : ''"
-            ></view>
-          </view>
-          <view class="log-title">{{ item.title }}</view>
-        </view>
-      </view>
-      <view class="invoice-content-list ss-flex ss-row-between ss-col-top">
-        <view class="">
-          <view class="ss-flex">
-            <view class="list-title">发票类型</view>
-            <view class="list-desc">{{ state.data.type_text }}</view>
-          </view>
-          <view class="ss-flex">
-            <view class="list-title">发票抬头</view>
-            <view class="list-desc">{{ state.data.name }}</view>
-          </view>
-          <view class="ss-flex" v-if="state.data.type === 'company'">
-            <view class="list-title">发票税号</view>
-            <view class="list-desc">{{ state.data.tax_no }}</view>
-          </view>
-          <view class="ss-flex" v-if="state.data.status === 'finish'">
-            <view class="list-title">实开金额</view>
-            <view class="list-desc">¥{{ state.data.invoice_amount }}</view>
-          </view>
-          <view class="ss-flex" v-if="state.data.status === 'finish'">
-            <view class="list-title">开票时间</view>
-            <view class="list-desc">{{ state.data.finish_time }}</view>
-          </view>
-          <view class="ss-flex">
-            <view class="list-title">申请时间</view>
-            <view class="list-desc">{{ state.data.create_time }}</view>
-          </view>
-        </view>
-        <view
-          class="invoice-content-img ss-flex-col ss-col-center"
-          v-if="state.data.status === 'finish'"
-        >
-          <su-image
-            class="invoice-img"
-            isPreview
-            :previewList="state.jointImage"
-            :current="0"
-            :src="sheep.$url.static('/static/img/shop/order/invoice_thumb.png')"
-            :height="110"
-            mode="scaleToFill"
-            v-if="state.jointImage[0].substr(-4) != '.pdf'"
-          ></su-image>
-          <!-- TODO: 发票为多个pdf时 -->
-          <view v-if="state.jointImage[0].substr(-4) == '.pdf'" @tap="onInvoice">
-            <image
-              :src="sheep.$url.static('/static/img/shop/order/invoice_thumb.png')"
-              class="invoice-img"
-            ></image>
-          </view>
-          <view class="invoice-img-num">共{{ state.numImage }}张</view>
-          <view class="invoice-img-title">点击预览发票</view>
-        </view>
-      </view>
-    </view>
-    <view class="invoice-order ss-m-t-20">
-      <view class="goods-box" v-for="item in state.data.order_items" :key="item.id">
-        <s-goods-item
-          :img="item.goods_image"
-          :title="item.goods_title"
-          :skuText="item.goods_sku_text"
-          :price="item.goods_price"
-          :num="item.goods_num"
-        />
-      </view>
-      <view class="invoice-order-list">
-        <view class="ss-flex">
-          <view class="list-title">订单状态</view>
-          <view class="list-desc">{{ state.data.order?.status_text }}</view>
-        </view>
-        <view class="ss-flex">
-          <view class="list-title">订单编号</view>
-          <view class="list-desc">{{ state.data.order?.order_sn }}</view>
-        </view>
-        <view class="ss-flex">
-          <view class="list-title">下单时间</view>
-          <view class="list-desc">{{ state.data.order?.create_time }}</view>
-        </view>
-      </view>
-    </view>
-  </s-layout>
-</template>
-
-<script setup>
-  import sheep from '@/sheep';
-  import { onLoad } from '@dcloudio/uni-app';
-  import { computed, reactive } from 'vue';
-
-  const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
-  const headerBg = sheep.$url.css('/static/img/shop/order/invoice_bg.png');
-  const state = reactive({
-    info: [
-      {
-        title: '订单提交',
-      },
-      {
-        title: '等待开票',
-      },
-      {
-        title: '开票完成',
-      },
-    ],
-    data: {},
-    jointImage: [],
-    numImage: 0,
-  });
-  const statusNum = computed(() => {
-    if (state.data.status === 'finish') {
-      return 2;
-    } else if (state.data.status === 'waiting') {
-      return 1;
-    } else {
-      return 0;
-    }
-  });
-  function onInvoice() {
-    // #ifdef H5
-    window.open(state.jointImage);
-    // #endif
-    // #ifdef MP || APP-PLUS
-    uni.downloadFile({
-      url: state.jointImage[0],
-      success: function (res) {
-        var filePath = res.tempFilePath;
-        uni.openDocument({
-          filePath: filePath,
-          showMenu: true,
-          success: function (res) {
-            console.log('打开文档成功');
-          },
-        });
-      },
-    });
-    // #endif
-  }
-  async function getInvoiceDetail(id) {
-    const { data } = await sheep.$api.order.invoice(id);
-    state.data = data;
-    state.data.download_urls?.forEach((i, index) => {
-      state.numImage = index + 1;
-      if (i.substr(-4) != '.pdf') {
-        state.jointImage.push(sheep.$url.static(i));
-      } else {
-        state.jointImage.push(sheep.$url.static(i));
-      }
-    });
-  }
-  onLoad((options) => {
-    getInvoiceDetail(options.invoiceId);
-  });
-</script>
-
-<style lang="scss" scoped>
-  .invoice-heard {
-    width: 100%;
-    box-sizing: border-box;
-    background: v-bind(headerBg) no-repeat,
-      linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-    background-size: 750rpx 100%;
-    .sicon-warning-line {
-      color: #fff;
-      font-size: 34rpx;
-    }
-    .sicon-check-line {
-      color: #fff;
-      font-size: 34rpx;
-    }
-    .invoice-heard-title {
-      font-size: 34rpx;
-      font-weight: 500;
-      color: #ffffff;
-      margin-left: 8rpx;
-      line-height: normal;
-    }
-    .invoice-heard-desc {
-      font-size: 24rpx;
-      font-weight: 500;
-      color: #ffffff;
-    }
-    .invoice-heard-price {
-      font-size: 28rpx;
-      font-family: OPPOSANS;
-      font-weight: 500;
-      color: #ffffff;
-    }
-  }
-  .invoice-content {
-    width: 100%;
-    position: relative;
-    z-index: 3;
-    background: #ffffff;
-    border-radius: 20rpx;
-    margin-top: -16rpx;
-    .invoice-content-title {
-      font-size: 30rpx;
-      font-weight: 500;
-      color: #333333;
-    }
-    .log-icon {
-      .sicon-unchecked {
-        color: #c2bec2;
-        font-size: 44rpx;
-      }
-      .sicon-circlecheck {
-        color: #e60a00;
-        font-size: 44rpx;
-      }
-      .line {
-        width: 158rpx;
-        height: 6rpx;
-        background: #f2f2f2;
-        border: 2rpx solid #ffffff;
-      }
-      .activity-color {
-        background: #e60a00;
-      }
-    }
-    .log-title {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333333;
-      margin-left: -26rpx;
-      margin-top: 30rpx;
-    }
-    .invoice-content-list {
-      width: 100%;
-      padding: 0 46rpx 0 30rpx;
-      box-sizing: border-box;
-    }
-    .list-title {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #999999;
-      margin-right: 44rpx;
-      margin-bottom: 36rpx;
-    }
-    .list-desc {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333333;
-      margin-bottom: 36rpx;
-    }
-    .invoice-img {
-      width: 200rpx;
-      height: 110rpx;
-    }
-    .invoice-img-num {
-      width: 216rpx;
-      height: 40rpx;
-      background: rgba(#000000, 0.45);
-      font-size: 24rpx;
-      font-weight: 500;
-      color: #ffffff;
-      text-align: center;
-      margin-top: -30rpx;
-      z-index: 1;
-    }
-    .invoice-img-title {
-      font-size: 24rpx;
-      font-weight: 500;
-      color: #999999;
-    }
-  }
-  .invoice-order {
-    width: 100%;
-    padding-top: 30rpx;
-    box-sizing: border-box;
-    background: #fff;
-    border-radius: 20rpx;
-  }
-  .goods-box {
-    border-bottom: 2rpx solid #dfdfdf;
-  }
-  .invoice-order-list {
-    padding: 40rpx 24rpx 0 24rpx;
-    .list-title {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #999999;
-      margin-right: 44rpx;
-      margin-bottom: 36rpx;
-    }
-    .list-desc {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333333;
-      margin-bottom: 36rpx;
-    }
-  }
-</style>

+ 123 - 250
pages/order/list.vue

@@ -1,153 +1,71 @@
-<!-- 页面 -->
+<!-- 订单列表 -->
 <template>
 	<s-layout title="我的订单">
 		<su-sticky bgColor="#fff">
-			<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
+			<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" />
 		</su-sticky>
-		<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单"></s-empty>
+		<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单" />
 		<view v-if="state.pagination.total > 0">
-			<view class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20" v-for="order in state.pagination.data"
-				:key="order.id" @tap="onOrderDetail(order.id)">
+			<view class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20" v-for="order in state.pagination.list"
+            :key="order.id" @tap="onOrderDetail(order.id)">
 				<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
 					<view class="order-no">订单号:{{ order.no }}</view>
-					<view class="order-state ss-font-26" :class="formatOrderColor(order.status_code)">{{
-            order.status
-          }}</view>
+					<view class="order-state ss-font-26" :class="formatOrderColor(order)">
+            {{ formatOrderStatus(order) }}
+          </view>
 				</view>
 				<view class="border-bottom" v-for="item in order.items" :key="item.id">
-					<s-goods-item :img="item.picUrl" :title="item.spuName"
-						:skuText="item.properties.length>1? item.properties.reduce((items2,items)=>items2.valueName+' '+items.valueName):item.properties[0].valueName"
-						:price="item.price/100" :score="order.score_amount" :num="item.count">
-						<template #tool>
-							<view class="ss-flex">
-								<!-- <button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale')"
-									@tap.stop="
-                    sheep.$router.go('/pages/order/aftersale/apply', {
-                      item: JSON.stringify(item),
-                    })
-                  ">
-									申请售后
-								</button>
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('re_aftersale')"
-									@tap.stop="
-                    sheep.$router.go('/pages/order/aftersale/apply', {
-                      item: JSON.stringify(item),
-                    })
-                  ">
-									重新售后
-								</button>
-
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale_info')"
-									@tap.stop="
-                    sheep.$router.go('/pages/order/aftersale/detail', {
-                      id: item.ext.aftersale_id,
-                    })
-                  ">
-									售后详情
-								</button>
-								<button class="ss-reset-button apply-btn" v-if="item.btns.includes('buy_again')"
-									@tap.stop="
-                    sheep.$router.go('/pages/goods/index', {
-                      id: item.goods_id,
-                    })
-                  ">
-									再次购买
-								</button> -->
-							</view>
-						</template>
-					</s-goods-item>
+					<s-goods-item
+            :img="item.picUrl"
+            :title="item.spuName"
+						:skuText="item.properties.map((property) => property.valueName).join(' ')"
+						:price="item.price"
+            :num="item.count"
+          />
 				</view>
 				<view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20">
-					<!-- <view v-if="order.total_discount_fee > 0" class="ss-flex ss-col-center ss-m-r-8">
-						<view class="discounts-title">优惠:¥</view>
-						<view class="discounts-money">{{ order.total_discount_fee }}</view>
-					</view> -->
-					<!-- 	<view class="ss-flex ss-col-center ss-m-r-8">
-						<view class="discounts-title">运费:¥</view>
-						<view class="discounts-money">{{ order.dispatch_amount }}</view>
-					</view> -->
 					<view class="ss-flex ss-col-center">
-						<view class="discounts-title pay-color">共{{count}}件商品,总金额:</view>
-						<view class="discounts-money pay-color" v-if="Number(order.payPrice) > 0">
-							¥{{ order.payPrice/100 }}</view>
-						<view v-if="order.score_amount && Number(order.payPrice) > 0">+</view>
-						<view class="discounts-money pay-color ss-flex ss-col-center" v-if="order.score_amount">
-							<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img">
-							</image>
-							<view>{{ order.score_amount }}</view>
-						</view>
+						<view class="discounts-title pay-color">共 {{ order.productCount }} 件商品,总金额:</view>
+						<view class="discounts-money pay-color">
+							¥{{ fen2yuan(order.payPrice) }}
+            </view>
 					</view>
 				</view>
-				<!-- :class="order.btns.length > 3 ? 'ss-row-between' : 'ss-row-right'" -->
-				<view class="order-card-footer ss-flex ss-col-center ss-p-x-20">
-					<!-- <su-popover>
-            <button class="more-btn ss-reset-button" @click.stop>更多</button>
-            <template #content>
-              <view class="more-item-box">
-                <view class="more-item ss-flex ss-col-center ss-reset-button">
-                  <view class="item-title">删除订单</view>
-                </view>
-                <view class="more-item ss-flex ss-col-center ss-reset-button">
-                  <view class="item-title">查看发票</view>
-                </view>
-                <view class="more-item ss-flex ss-col-center ss-reset-button">
-                  <view class="item-title">评价晒单</view>
-                </view>
-              </view>
-            </template>
-          </su-popover> -->
+				<view class="order-card-footer ss-flex ss-col-center ss-p-x-20"
+              :class="order.buttons.length > 3 ? 'ss-row-between' : 'ss-row-right'">
 					<view class="ss-flex ss-col-center">
-						<!-- 				<button v-if="order.btns.includes('groupon')" class="tool-btn ss-reset-button"
+						<button v-if="order.buttons.includes('combination')" class="tool-btn ss-reset-button"
 							@tap.stop="onOrderGroupon(order)">
-							{{ order.status_code === 'groupon_ing' ? '邀请拼团' : '拼团详情' }}
+              拼团详情
 						</button>
-						<button v-if="order.btns.includes('invoice')" class="tool-btn ss-reset-button"
-							@tap.stop="onOrderInvoice(order.invoice?.id)">
-							查看发票
+						<button v-if="order.buttons.length === 0" class="tool-btn ss-reset-button"
+                    @tap.stop="onOrderDetail(order.id)">
+              查看详情
 						</button>
-						<button v-if="order.btns.length === 0" class="tool-btn ss-reset-button"
-							@tap.stop="onOrderDetail(order.order_sn)">
-							查看详情
+						<button v-if="order.buttons.includes('confirm')" class="tool-btn ss-reset-button"
+                    @tap.stop="onConfirm(order)">
+              确认收货
 						</button>
-
-						<button v-if="order.btns.includes('confirm')" class="tool-btn ss-reset-button"
-							@tap.stop="onConfirm(order)">
-							确认收货
-						</button>
-
-						<button v-if="order.btns.includes('express')" class="tool-btn ss-reset-button"
-							@tap.stop="onExpress(order.id)">
+						<button v-if="order.buttons.includes('express')" class="tool-btn ss-reset-button"
+                    @tap.stop="onExpress(order.id)">
 							查看物流
 						</button>
-
-						<button v-if="order.btns.includes('apply_refund')" class="tool-btn ss-reset-button"
-							@tap.stop="onRefund(order.id)">
-							申请退款
-						</button>
-						<button v-if="order.btns.includes('re_apply_refund')" class="tool-btn ss-reset-button"
-							@tap.stop="onRefund(order.id)">
-							重新退款
-						</button>
-
-						<button v-if="order.btns.includes('cancel')" class="tool-btn ss-reset-button"
-							@tap.stop="onCancel(order.id)">
+						<button v-if="order.buttons.includes('cancel')" class="tool-btn ss-reset-button"
+                    @tap.stop="onCancel(order.id)">
 							取消订单
 						</button>
-
-						<button v-if="order.btns.includes('comment')" class="tool-btn ss-reset-button"
-							@tap.stop="onComment(order.order_sn)">
-							评价晒单
+						<button v-if="order.buttons.includes('comment')" class="tool-btn ss-reset-button"
+                    @tap.stop="onComment(order.id)">
+							评价
 						</button>
-
-						<button v-if="order.btns.includes('delete')" class="delete-btn ss-reset-button"
-							@tap.stop="onDelete(order.id)">
+						<button v-if="order.buttons.includes('delete')" class="delete-btn ss-reset-button"
+                    @tap.stop="onDelete(order.id)">
 							删除订单
 						</button>
-
-						<button v-if="order.btns.includes('pay')" class="tool-btn ss-reset-button ui-BG-Main-Gradient"
-							@tap.stop="onPay(order.order_sn)">
+						<button v-if="order.buttons.includes('pay')" class="tool-btn ss-reset-button ui-BG-Main-Gradient"
+                    @tap.stop="onPay(order.payOrderId)">
 							继续支付
-						</button> -->
+						</button>
 					</view>
 				</view>
 			</view>
@@ -156,52 +74,45 @@
 		<!-- 加载更多 -->
 		<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
         contentdown: '上拉加载更多',
-      }" @tap="loadmore" />
+      }" @tap="loadMore" />
 	</s-layout>
 </template>
 
 <script setup>
-	import {
-		computed,
-		reactive
-	} from 'vue';
-	import {
-		onLoad,
-		onReachBottom,
-		onPullDownRefresh
-	} from '@dcloudio/uni-app';
-	import {
-		formatOrderColor
-	} from '@/sheep/hooks/useGoods';
+	import { reactive } from 'vue';
+	import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
+  import {
+    fen2yuan,
+    formatOrderColor, formatOrderStatus, handleOrderButtons,
+  } from '@/sheep/hooks/useGoods';
 	import sheep from '@/sheep';
 	import _ from 'lodash';
 	import {
 		isEmpty
 	} from 'lodash';
+  import OrderApi from '@/sheep/api/trade/order';
 
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
+	const paginationNull = {
+		list: [],
+    total: 0,
+		pageNo: 1,
+    pageSize: 5,
 	};
+
 	// 数据
 	const state = reactive({
-		currentTab: 0,
+		currentTab: 0, // 选中的 tabMaps 下标
 		pagination: {
-			data: [],
-			current_page: 1,
-			total: 1,
-			last_page: 1,
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 5,
 		},
-		loadStatus: '',
-		deleteOrderId: 0,
-		error: 0,
+		loadStatus: ''
 	});
 
 	const tabMaps = [{
-			name: '全部',
-			// value: 'all',
+			name: '全部'
 		},
 		{
 			name: '待付款',
@@ -223,53 +134,47 @@
 
 	// 切换选项卡
 	function onTabsChange(e) {
-		if (state.currentTab === e.index) return;
-
-		state.pagination = pagination;
+		if (state.currentTab === e.index) {
+      return;
+    }
+    // 重头加载代码
+		state.pagination = paginationNull;
 		state.currentTab = e.index;
-
 		getOrderList();
 	}
 
 	// 订单详情
-	function onOrderDetail(orderSN) {
+	function onOrderDetail(id) {
 		sheep.$router.go('/pages/order/detail', {
-			orderSN,
+			id,
 		});
 	}
 
-	// 分享拼团
+	// 分享拼团 TODO 芋艿:待测试
 	function onOrderGroupon(order) {
 		sheep.$router.go('/pages/activity/groupon/detail', {
 			id: order.ext.groupon_id,
 		});
 	}
 
-	// 查看发票
-	function onOrderInvoice(invoiceId) {
-		sheep.$router.go('/pages/order/invoice', {
-			invoiceId,
-		});
-	}
-
 	// 继续支付
-	function onPay(orderSN) {
+	function onPay(payOrderId) {
 		sheep.$router.go('/pages/pay/index', {
-			orderSN,
+			id: payOrderId,
 		});
 	}
 
 	// 评价
-	function onComment(orderSN) {
+	function onComment(id) {
 		sheep.$router.go('/pages/goods/comment/add', {
-			orderSN,
+			id,
 		});
 	}
 
-	// 确认收货
+	// 确认收货 TODO 芋艿:待测试
 	async function onConfirm(order, ignore = false) {
 		// 需开启确认收货组件
-		// todo:
+		// todo: 芋艿:需要后续接入微信收货组件
 		// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
 		// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
 		let isOpenBusinessView = true;
@@ -284,18 +189,15 @@
 		}
 
 		// 正常的确认收货流程
-
-		const {
-			error
-		} = await sheep.$api.order.confirm(order.id);
-		if (error === 0) {
-			state.pagination = pagination;
-			getOrderList();
+		const { code } = await OrderApi.receiveOrder(order.id);
+		if (code === 0) {
+			state.pagination = paginationNull;
+			await getOrderList();
 		}
 	}
 
 	// #ifdef MP-WEIXIN
-	// 小程序确认收货组件
+	// 小程序确认收货组件 TODO 芋艿:后续再接入
 	function mpConfirm(order) {
 		if (!wx.openBusinessView) {
 			sheep.$helper.toast(`请升级微信版本`);
@@ -327,9 +229,9 @@
 	// #endif
 
 	// 查看物流
-	async function onExpress(orderId) {
-		sheep.$router.go('/pages/order/express/list', {
-			orderId,
+	async function onExpress(id) {
+		sheep.$router.go('/pages/order/express/log', {
+      id,
 		});
 	}
 
@@ -339,16 +241,17 @@
 			title: '提示',
 			content: '确定要取消订单吗?',
 			success: async function(res) {
-				if (res.confirm) {
-					const {
-						error,
-						data
-					} = await sheep.$api.order.cancel(orderId);
-					if (error === 0) {
-						let index = state.pagination.data.findIndex((order) => order.id === orderId);
-						state.pagination.data[index] = data;
-					}
+				if (!res.confirm) {
+          return;
 				}
+        const { code } = await OrderApi.cancelOrder(orderId);
+        if (code === 0) {
+          // 修改数据的状态
+          let index = state.pagination.list.findIndex((order) => order.id === orderId);
+          const orderInfo = state.pagination.list[index];
+          orderInfo.status = 40;
+          handleOrderButtons(orderInfo);
+        }
 			},
 		});
 	}
@@ -360,36 +263,11 @@
 			content: '确定要删除订单吗?',
 			success: async function(res) {
 				if (res.confirm) {
-					const {
-						error,
-						data
-					} = await sheep.$api.order.delete(orderId);
-					if (error === 0) {
-						let index = state.pagination.data.findIndex((order) => order.id === orderId);
-						state.pagination.data.splice(index, 1);
-					}
-				}
-			},
-		});
-	}
-
-	// 申请退款
-	async function onRefund(orderId) {
-		uni.showModal({
-			title: '提示',
-			content: '确定要申请退款吗?',
-			success: async function(res) {
-				if (res.confirm) {
-					// #ifdef MP
-					sheep.$platform.useProvider('wechat').subscribeMessage('order_refund');
-					// #endif
-					const {
-						error,
-						data
-					} = await sheep.$api.order.applyRefund(orderId);
-					if (error === 0) {
-						let index = state.pagination.data.findIndex((order) => order.id === orderId);
-						state.pagination.data[index] = data;
+					const { code } = await OrderApi.deleteOrder(orderId);
+					if (code === 0) {
+            // 删除数据
+						let index = state.pagination.list.findIndex((order) => order.id === orderId);
+						state.pagination.list.splice(index, 1);
 					}
 				}
 			},
@@ -397,52 +275,47 @@
 	}
 
 	// 获取订单列表
-	async function getOrderList(page = 1, list_rows = 5) {
+	async function getOrderList() {
 		state.loadStatus = 'loading';
-		let res = await sheep.$api.order.list({
-			status: tabMaps[state.currentTab].value,
-			pageSize: list_rows,
-			pageNo: page,
-			commentStatus: tabMaps[state.currentTab].value == 30 ? false : null
+		let { code, data } = await OrderApi.getOrderPage({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
+      status: tabMaps[state.currentTab].value,
+			commentStatus: tabMaps[state.currentTab].value === 30 ? false : null
 		});
-		state.error = res.code;
-		if (res.code === 0) {
-			let orderList = _.concat(state.pagination.data, res.data.list);
-			state.pagination = {
-				...res.data,
-				data: orderList,
-			};
-			console.log(state.pagination)
-			if (state.pagination.data.length < state.pagination.total) {
-				state.loadStatus = 'more';
-			} else {
-				state.loadStatus = 'noMore';
-			}
-		}
-	}
+    if (code !== 0) {
+      return;
+    }
+    data.list.forEach(order => handleOrderButtons(order));
+    state.pagination.list = _.concat(state.pagination.list, data.list)
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+  }
 
 	onLoad(async (options) => {
 		if (options.type) {
 			state.currentTab = options.type;
 		}
-		getOrderList();
+		await getOrderList();
 	});
 
 	// 加载更多
-	function loadmore() {
-		if (state.loadStatus !== 'noMore') {
-			getOrderList(parseInt((state.pagination.data.length / 5) + 1));
+	function loadMore() {
+		if (state.loadStatus === 'noMore') {
+      return
 		}
-	}
+    state.pagination.pageNo++;
+    getOrderList();
+  }
 
 	// 上拉加载更多
 	onReachBottom(() => {
-		loadmore();
+		loadMore();
 	});
 
-	//下拉刷新
+	// 下拉刷新
 	onPullDownRefresh(() => {
-		state.pagination = pagination;
+		state.pagination = paginationNull;
 		getOrderList();
 		setTimeout(function() {
 			uni.stopPullDownRefresh();

+ 54 - 124
pages/pay/index.vue

@@ -2,14 +2,17 @@
 <template>
   <s-layout title="收银台">
     <view class="bg-white ss-modal-box ss-flex-col">
+      <!-- 订单信息 -->
       <view class="modal-header ss-flex-col ss-col-center ss-row-center">
         <view class="money-box ss-m-b-20">
-          <text class="money-text">{{ state.orderInfo.pay_fee }}</text>
+          <text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
         </view>
         <view class="time-text">
           <text>{{ payDescText }}</text>
         </view>
       </view>
+
+      <!-- 支付方式 -->
       <view class="modal-content ss-flex-1">
         <view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
         <radio-group @change="onTapPay">
@@ -17,7 +20,6 @@
             <view
               class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
               :class="{ 'disabled-pay-item': item.disabled }"
-              v-if="allowedPayment.includes(item.value)"
             >
               <view class="ss-flex ss-col-center">
                 <image
@@ -25,25 +27,19 @@
                   v-if="item.disabled"
                   :src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
                   mode="aspectFit"
-                ></image>
+                />
                 <image
                   class="pay-icon"
                   v-else
                   :src="sheep.$url.static(item.icon)"
                   mode="aspectFit"
-                ></image>
+                />
                 <text class="pay-title">{{ item.title }}</text>
               </view>
               <view class="check-box ss-flex ss-col-center ss-p-l-10">
-                <view class="userInfo-money ss-m-r-10" v-if="item.value == 'money'">
+                <view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
                   余额: {{ userInfo.money }}元
                 </view>
-                <view
-                  class="userInfo-money ss-m-r-10"
-                  v-if="item.value == 'offline' && item.disabled"
-                >
-                  部分商品不支持
-                </view>
                 <radio
                   :value="item.value"
                   color="var(--ui-BG-Main)"
@@ -56,6 +52,7 @@
           </label>
         </radio-group>
       </view>
+
       <!-- 工具 -->
       <view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
         <button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
@@ -81,95 +78,49 @@
   import { computed, reactive } from 'vue';
   import { onLoad } from '@dcloudio/uni-app';
   import sheep from '@/sheep';
-  import { useDurationTime } from '@/sheep/hooks/useGoods';
+  import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
+  import PayOrderApi from '@/sheep/api/pay/order';
+  import PayChannelApi from '@/sheep/api/pay/channel';
+  import { getPayMethods } from '@/sheep/platform/pay';
 
   const userInfo = computed(() => sheep.$store('user').userInfo);
 
   // 检测支付环境
   const state = reactive({
-    orderType: 'goods',
-    payment: '',
-    orderInfo: {},
+    orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
+    orderInfo: {}, // 支付单信息
     payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付,2=订单已支付
-    payMethods: [],
+    payMethods: [], // 可选的支付方式
+    payment: '', // 选中的支付方式
   });
 
-  const allowedPayment = computed(() => {
-    if(state.orderType === 'recharge') {
-      return sheep.$store('app').platform.recharge_payment
-    }
-    return sheep.$store('app').platform.payment
-    });
-
-  const payMethods = [
-    {
-      icon: '/static/img/shop/pay/wechat.png',
-      title: '微信支付',
-      value: 'wechat',
-      disabled: false,
-    },
-    {
-      icon: '/static/img/shop/pay/alipay.png',
-      title: '支付宝支付',
-      value: 'alipay',
-      disabled: false,
-    },
-    {
-      icon: '/static/img/shop/pay/wallet.png',
-      title: '余额支付',
-      value: 'money',
-      disabled: false,
-    },
-    {
-      icon: '/static/img/shop/pay/apple.png',
-      title: 'Apple Pay',
-      value: 'apple',
-      disabled: false,
-    },
-    {
-      icon: '/static/img/shop/pay/cod.png',
-      title: '货到付款',
-      value: 'offline',
-      disabled: false,
-    },
-  ];
-
   const onPay = () => {
     if (state.payment === '') {
       sheep.$helper.toast('请选择支付方式');
       return;
     }
-    if (state.payment === 'money') {
+    if (state.payment === 'wallet') {
       uni.showModal({
         title: '提示',
         content: '确定要支付吗?',
         success: function (res) {
           if (res.confirm) {
-            sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
-          }
-        },
-      });
-    } else if (state.payment === 'offline') {
-      uni.showModal({
-        title: '提示',
-        content: '确定要下单吗?',
-        success: function (res) {
-          if (res.confirm) {
-            sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
+            sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id);
           }
         },
       });
     } else {
-      sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
+      sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id);
     }
   };
 
+  // 支付文案提示
   const payDescText = computed(() => {
     if (state.payStatus === 2) {
       return '该订单已支付';
     }
-    if (state.payStatus === 1 && state.orderInfo.ext.expired_time !== 0) {
-      const time = useDurationTime(state.orderInfo.ext.expired_time);
+    if (state.payStatus === 1) {
+      const time = useDurationTime(state.orderInfo.expireTime);
       if (time.ms <= 0) {
         state.payStatus = -1;
         return '';
@@ -179,86 +130,65 @@
     if (state.payStatus === -2) {
       return '未查询到支付单信息';
     }
-
     return '';
   });
 
+  // 状态转换:payOrder.status => payStatus
   function checkPayStatus() {
-    if (state.orderInfo.status === 'unpaid') {
-      state.payStatus = 1;
+    if (state.orderInfo.status === 10
+      || state.orderInfo.status === 20 ) { // 支付成功
+      state.payStatus = 2;
       return;
     }
-    if (state.orderInfo.status === 'closed') {
+    if (state.orderInfo.status === 30) { // 支付关闭
       state.payStatus = -1;
       return;
     }
-    state.payStatus = 2;
+    state.payStatus = 1; // 待支付
   }
 
+  // 切换支付方式
   function onTapPay(e) {
     state.payment = e.detail.value;
   }
 
-  async function setRechargeOrder(id) {
-    const { data, error } = await sheep.$api.trade.order(id);
-    if (error === 0) {
-      state.orderInfo = data;
-      state.payMethods = payMethods;
-      checkPayStatus();
-    } else {
+  // 设置支付订单信息
+  async function setOrder(id) {
+    // 获得支付订单信息
+    const { data, code } = await PayOrderApi.getOrder(id);
+    if (code !== 0 || !data) {
       state.payStatus = -2;
+      return;
     }
+    state.orderInfo = data;
+    // 获得支付方式
+    await setPayMethods();
+    // 设置支付状态
+    checkPayStatus();
   }
 
-  async function setGoodsOrder(id) {
-    const { data, error } = await sheep.$api.order.detail(id);
-    if (error === 0) {
-      state.orderInfo = data;
-      if (state.orderInfo.ext.offline_status === 'none') {
-        payMethods.forEach((item, index, array) => {
-          if (item.value === 'offline') {
-            array.splice(index, 1);
-          }
-        });
-      } else if (state.orderInfo.ext.offline_status === 'disabled') {
-        payMethods.forEach((item) => {
-          if (item.value === 'offline') {
-            item.disabled = true;
-          }
-        });
-      }
-      state.payMethods = payMethods;
-      checkPayStatus();
-    } else {
-      state.payStatus = -2;
+  // 获得支付方式
+  async function setPayMethods() {
+    const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
+    if (code !== 0) {
+      return
     }
+    state.payMethods = getPayMethods(data)
   }
 
   onLoad((options) => {
-    if (
-      sheep.$platform.name === 'WechatOfficialAccount' &&
-      sheep.$platform.os === 'ios' &&
-      !sheep.$platform.landingPage.includes('pages/pay/index')
-    ) {
+    if (sheep.$platform.name === 'WechatOfficialAccount'
+      && sheep.$platform.os === 'ios'
+      && !sheep.$platform.landingPage.includes('pages/pay/index')) {
       location.reload();
       return;
     }
-    let id = '';
-    if (options.orderSN) {
-      id = options.orderSN;
-    }
-    if (options.id) {
-      id = options.id;
-    }
-    if (options.type === 'recharge') {
-      state.orderType = 'recharge';
-      // 充值订单
-      setRechargeOrder(id);
-    } else {
-      state.orderType = 'goods';
-      // 商品订单
-      setGoodsOrder(id);
+    // 获得支付订单信息
+    let id = options.id;
+    if (options.orderType) {
+      state.orderType = options.orderType;
     }
+    setOrder(id);
   });
 </script>
 

+ 61 - 59
pages/pay/result.vue

@@ -2,34 +2,27 @@
 <template>
   <s-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
     <view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
-      <view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'"> </view>
+      <!-- 信息展示 -->
+      <view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'" />
       <image
         class="pay-img ss-m-b-30"
         v-if="payResult === 'success'"
         :src="sheep.$url.static('/static/img/shop/order/order_pay_success.gif')"
-      ></image>
+      />
       <image
         class="pay-img ss-m-b-30"
         v-if="['failed', 'closed'].includes(payResult)"
         :src="sheep.$url.static('/static/img/shop/order/order_paty_fail.gif')"
-      ></image>
-      <view class="tip-text ss-m-b-30" v-if="payResult == 'success'">{{
-        state.orderInfo.pay_mode === 'offline' ? '下单成功' : '支付成功'
-      }}</view>
-      <view class="tip-text ss-m-b-30" v-if="payResult == 'failed'">支付失败</view>
-      <view class="tip-text ss-m-b-30" v-if="payResult == 'closed'">该订单已关闭</view>
-      <view class="tip-text ss-m-b-30" v-if="payResult == 'waiting'">检测支付结果...</view>
+      />
+      <view class="tip-text ss-m-b-30" v-if="payResult === 'success'">支付成功</view>
+      <view class="tip-text ss-m-b-30" v-if="payResult === 'failed'">支付失败</view>
+      <view class="tip-text ss-m-b-30" v-if="payResult === 'closed'">该订单已关闭</view>
+      <view class="tip-text ss-m-b-30" v-if="payResult === 'waiting'">检测支付结果...</view>
       <view class="pay-total-num ss-flex" v-if="payResult === 'success'">
-        <view v-if="Number(state.orderInfo.pay_fee) > 0">¥{{ state.orderInfo.pay_fee }}</view>
-        <view v-if="state.orderInfo.score_amount && Number(state.orderInfo.pay_fee) > 0">+</view>
-        <view class="price-text ss-flex ss-col-center" v-if="state.orderInfo.score_amount">
-          <image
-            :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-            class="score-img"
-          ></image>
-          <view>{{ state.orderInfo.score_amount }}</view>
-        </view>
+        <view>¥{{ fen2yuan(state.orderInfo.price) }}</view>
       </view>
+
+      <!-- 操作区 -->
       <view class="btn-box ss-flex ss-row-center ss-m-t-50">
         <button class="back-btn ss-reset-button" @tap="sheep.$router.go('/pages/index/index')">
           返回首页
@@ -37,30 +30,29 @@
         <button
           class="check-btn ss-reset-button"
           v-if="payResult === 'failed'"
-          @tap="sheep.$router.redirect('/pages/pay/index', { orderSN: state.orderId })"
+          @tap="
+            sheep.$router.redirect('/pages/pay/index', { id: state.id, orderType: state.orderType })
+          "
         >
           重新支付
         </button>
         <button class="check-btn ss-reset-button" v-if="payResult === 'success'" @tap="onOrder">
           查看订单
         </button>
+        <!-- TODO 芋艿:拼团接入 -->
         <button
           class="check-btn ss-reset-button"
-          v-if="
-            payResult === 'success' &&
-            ['groupon', 'groupon_ladder'].includes(state.orderInfo.activity_type)
-          "
+          v-if="payResult === 'success' && state.tradeOrder.type === 3"
           @tap="sheep.$router.redirect('/pages/activity/groupon/order')"
         >
           我的拼团
         </button>
       </view>
+
+      <!-- TODO 芋艿:订阅 -->
       <!-- #ifdef MP -->
       <view class="subscribe-box ss-flex ss-m-t-44">
-        <image
-          class="subscribe-img"
-          :src="sheep.$url.static('/static/img/shop/order/cargo.png')"
-        ></image>
+        <image class="subscribe-img" :src="sheep.$url.static('/static/img/shop/order/cargo.png')" />
         <view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view>
         <view class="subscribe-start" @tap="subscribeMessage">立即订阅</view>
       </view>
@@ -74,15 +66,20 @@
   import { reactive, computed } from 'vue';
   import { isEmpty } from 'lodash';
   import sheep from '@/sheep';
+  import PayOrderApi from '@/sheep/api/pay/order';
+  import { fen2yuan } from '../../sheep/hooks/useGoods';
+  import OrderApi from '@/sheep/api/trade/order';
 
   const state = reactive({
-    orderId: 0,
-    orderType: 'goods',
+    id: 0, // 支付单号
+    orderType: 'goods', // 订单类型
     result: 'unpaid', // 支付状态
-    orderInfo: {}, // 订单详情
+    orderInfo: {}, // 支付订单信息
+    tradeOrder: {}, // 商品订单信息,只有在 orderType 为 goods 才会请求。目的:【我的拼团】按钮的展示
     counter: 0, // 获取结果次数
   });
 
+  // 支付结果 result => payResult
   const payResult = computed(() => {
     if (state.result === 'unpaid') {
       return 'waiting';
@@ -93,57 +90,65 @@
     if (state.result === 'failed') {
       return 'failed';
     }
-
     if (state.result === 'closed') {
       return 'closed';
     }
   });
-  async function getOrderInfo(orderId) {
-    let checkPayResult;
+
+  // 获得订单信息
+  async function getOrderInfo(id) {
     state.counter++;
-    if (state.orderType === 'recharge') {
-      checkPayResult = sheep.$api.trade.order;
-    } else {
-      checkPayResult = sheep.$api.order.detail;
-    }
-    const { data, error } = await checkPayResult(orderId);
-    if (error === 0) {
+    // 1. 加载订单信息
+    const { data, code } = await PayOrderApi.getOrder(id);
+    if (code === 0) {
       state.orderInfo = data;
-      if (state.orderInfo.status === 'closed') {
+      if (!state.orderInfo || state.orderInfo.status === 30) {
+        // 支付关闭
         state.result = 'closed';
         return;
       }
-      if (state.orderInfo.status !== 'unpaid') {
+      if (state.orderInfo.status !== 0) {
+        // 非待支付,可能是已支付,可能是已退款
         state.result = 'paid';
         // #ifdef MP
         subscribeMessage();
         // #endif
+        // 特殊:获得商品订单信息
+        if (state.orderType === 'goods') {
+          const { data, code } = await OrderApi.getOrder(state.orderInfo.merchantOrderId);
+          if (code === 0) {
+            state.tradeOrder = data;
+          }
+        }
         return;
       }
     }
+    // 2.1 情况三一:未支付,且轮询次数小于三次,则继续轮询
     if (state.counter < 3 && state.result === 'unpaid') {
       setTimeout(() => {
-        getOrderInfo(orderId);
+        getOrderInfo(id);
       }, 1500);
     }
-    // 超过三次检测才判断为支付失败
+    // 2.2 情况二:超过三次检测才判断为支付失败
     if (state.counter >= 3) {
       state.result = 'failed';
     }
   }
 
   function onOrder() {
-    if ((state.orderType === 'recharge')) {
+    // TODO 芋艿:待测试
+    if (state.orderType === 'recharge') {
       sheep.$router.redirect('/pages/pay/recharge-log');
     } else {
       sheep.$router.redirect('/pages/order/list');
     }
   }
 
+  // TODO 芋艿:待测试
   // #ifdef MP
   function subscribeMessage() {
     let event = ['order_dispatched'];
-    if (['groupon', 'groupon_ladder'].includes(state.orderInfo.activity_type)) {
+    if (state.tradeOrder.type === 3) {
       event.push('groupon_finish');
       event.push('groupon_fail');
     }
@@ -152,18 +157,13 @@
   // #endif
 
   onLoad(async (options) => {
-    let id = '';
     // 支付订单号
-    if (options.orderSN) {
-      id = options.orderSN;
-    }
     if (options.id) {
-      id = options.id;
+      state.id = options.id;
     }
-    state.orderId = id;
-
-    if (options.orderType === 'recharge') {
-      state.orderType = 'recharge';
+    // 订单类型
+    if (options.orderType) {
+      state.orderType = options.orderType;
     }
 
     // 支付结果传值过来是失败,则直接显示失败界面
@@ -171,14 +171,16 @@
       state.result = 'failed';
     } else {
       // 轮询三次检测订单支付结果
-      getOrderInfo(state.orderId);
+      await getOrderInfo(state.id);
     }
   });
 
   onShow(() => {
-    if(isEmpty(state.orderInfo)) return;
-    getOrderInfo(state.orderId);
-  })
+    if (isEmpty(state.orderInfo)) {
+      return;
+    }
+    getOrderInfo(state.id);
+  });
 
   onHide(() => {
     state.result = 'unpaid';

+ 7 - 4
pages/public/error.vue

@@ -1,3 +1,4 @@
+<!-- 错误界面 -->
 <template>
   <view class="error-page">
     <s-empty
@@ -8,7 +9,7 @@
       actionText="重新连接"
       @clickAction="onReconnect"
       buttonColor="#ff3000"
-    ></s-empty>
+    />
     <s-empty
       v-else-if="errCode === 'TemplateError'"
       icon="/static/internet-empty.png"
@@ -17,7 +18,7 @@
       actionText="重新加载"
       @clickAction="onReconnect"
       buttonColor="#ff3000"
-    ></s-empty>
+    />
     <s-empty
       v-else-if="errCode !== ''"
       icon="/static/internet-empty.png"
@@ -26,7 +27,7 @@
       actionText="重新加载"
       @clickAction="onReconnect"
       buttonColor="#ff3000"
-    ></s-empty>
+    />
   </view>
 </template>
 
@@ -37,16 +38,18 @@
 
   const errCode = ref('');
   const errMsg = ref('');
+
   onLoad((options) => {
     errCode.value = options.errCode;
     errMsg.value = options.errMsg;
   });
+
   // 重新连接
   async function onReconnect() {
     uni.reLaunch({
       url: '/pages/index/index',
     });
-    ShoproInit();
+    await ShoproInit();
   }
 </script>
 

+ 0 - 226
pages/public/feedback.vue

@@ -1,226 +0,0 @@
-<template>
-  <s-layout class="set-wrap" title="问题反馈">
-    <uni-forms ref="form" :modelValue="state.formData" border>
-      <view class="bg-white type-box ss-p-x-20 ss-p-y-30">
-        <view class="title ss-m-b-44">请选择类型</view>
-        <view class="ss-m-l-12">
-          <radio-group @change="radioChange">
-            <label
-              class="ss-flex ss-col-center ss-m-b-40"
-              v-for="item in state.radioList"
-              :key="item.type"
-            >
-              <radio :value="item.type" color="var(--ui-BG-Main)" style="transform: scale(0.8)" />
-              <view class="radio-subtitle">{{ item.type }}</view>
-            </label>
-          </radio-group>
-        </view>
-      </view>
-      <view class="bg-white ss-p-x-20 ss-p-y-30 ss-m-t-20">
-        <view class="title ss-m-b-30"> 相关描述 </view>
-        <view class="textarea">
-          <uni-easyinput
-            :inputBorder="false"
-            type="textarea"
-            v-model="state.formData.content"
-            placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-            placeholder="客官~请描述您遇到的问题,建议上传照片"
-            clearable
-          ></uni-easyinput>
-          <s-uploader
-            v-model:url="state.formData.images"
-            fileMediatype="image"
-            limit="9"
-            mode="grid"
-            :imageStyles="{ width: '168rpx', height: '168rpx' }"
-          ></s-uploader>
-        </view>
-      </view>
-      <view class="bg-white ss-p-x-20 ss-p-y-30 ss-m-t-20">
-        <view class="title ss-m-b-30"> 联系方式 </view>
-        <view class="mobile-box">
-          <uni-easyinput
-            :inputBorder="false"
-            type="number"
-            v-model="state.formData.phone"
-            paddingLeft="10"
-            placeholder="请输入您的联系电话"
-          />
-        </view>
-      </view>
-    </uni-forms>
-    <su-fixed bottom placeholder>
-      <view class="ss-flex ss-row-between ss-p-x-30 ss-p-y-10">
-        <button class="kefu-btn ss-reset-button" @tap="sheep.$router.go('/pages/chat/index')">
-          联系客服
-        </button>
-        <button class="submit-btn ss-reset-button ui-BG-Main ui-Shadow-Main" @tap="onSubmit">
-          提交
-        </button>
-      </view>
-    </su-fixed>
-  </s-layout>
-</template>
-
-<script setup>
-  import { onLoad } from '@dcloudio/uni-app';
-  import { computed, reactive, ref, unref } from 'vue';
-  import sheep from '@/sheep';
-
-  const filesRef = ref(null);
-  const state = reactive({
-    radioList: [
-      {
-        type: '产品功能问题反馈',
-      },
-      {
-        type: '建议及意见反馈',
-      },
-      {
-        type: '投诉客服其他问题',
-      },
-    ],
-    formData: {
-      content: '',
-      phone: '',
-      images: [],
-      type: '',
-    },
-    imageFiles: [],
-    current: 0,
-  });
-
-  async function onSubmit() {
-    if (!state.formData.type) {
-      sheep.$helper.toast('请选择类型');
-      return;
-    }
-    if (!state.formData.content) {
-      sheep.$helper.toast('请描述您遇到的问题');
-      return;
-    }
-    if (!state.formData.phone) {
-      sheep.$helper.toast('请输入您的联系方式');
-      return;
-    }
-
-    const { error } = await sheep.$api.app.feedback(state.formData);
-    if (error === 0) {
-      sheep.$router.back();
-    }
-  }
-
-  function radioChange(e) {
-    state.formData.type = e.detail.value;
-  }
-</script>
-
-<style lang="scss" scoped>
-  .type-box {
-    border-top: 2rpx solid #f9fafb;
-  }
-
-  .uni-forms {
-    width: 100%;
-  }
-
-  .title {
-    font-size: 30rpx;
-    font-weight: bold;
-    color: #333333;
-    line-height: normal;
-  }
-
-  :deep() {
-    .uni-easyinput__placeholder-class {
-      color: #bbbbbb !important;
-      font-size: 28rpx !important;
-      font-weight: 400 !important;
-      line-height: normal !important;
-    }
-
-    .uni-forms-item__label .label-text {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-    }
-
-    .uni-list-item__content-title {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-    }
-
-    .uni-easyinput__content-textarea {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-      margin-top: 4rpx !important;
-      padding-left: 20rpx !important;
-    }
-
-    .uni-icons {
-      font-size: 40rpx !important;
-    }
-
-    .icon-del-box {
-      width: 32rpx;
-      height: 32rpx;
-      top: 0;
-      right: 0;
-
-      .icon-del {
-        width: 24rpx;
-      }
-    }
-  }
-
-  .radio-subtitle {
-    font-size: 28rpx;
-    font-weight: 500;
-    color: #333333;
-    line-height: 42rpx;
-  }
-
-  .textarea {
-    min-height: 322rpx;
-    background: #f9fafb;
-    border-radius: 20rpx;
-    padding: 20rpx;
-    margin: 30rpx 20rpx 46rpx 0;
-
-    .area {
-      height: 238rpx;
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333;
-      line-height: 50rpx;
-      width: 100%;
-    }
-
-    .pl-style {
-      font-size: 24rpx;
-      color: #b1b3c7;
-      font-weight: 500;
-    }
-  }
-
-  .mobile-box {
-    background: #f9fafb;
-    border-radius: 20rpx;
-  }
-
-  .submit-btn {
-    width: 334rpx;
-    height: 74rpx;
-    border-radius: 37rpx;
-  }
-
-  .kefu-btn {
-    width: 334rpx;
-    height: 74rpx;
-    border-radius: 37rpx;
-    background: #eeeeee;
-    color: #333333;
-  }
-</style>

+ 22 - 25
pages/public/setting.vue

@@ -19,22 +19,15 @@
           :border="false"
           class="list-border"
           @tap="onCheckUpdate"
-        ></uni-list-item>
+        />
         <uni-list-item
           title="本地缓存"
           :rightText="storageSize"
           showArrow
           :border="false"
           class="list-border"
-        ></uni-list-item>
-        <uni-list-item
-          title="意见反馈"
-          showArrow
-          clickable
-          :border="false"
-          class="list-border"
-          @tap="sheep.$router.go('/pages/public/feedback')"
-        ></uni-list-item>
+        />
+        <!-- TODO 芋艿:统一的配置界面 -->
         <uni-list-item
           title="关于我们"
           showArrow
@@ -47,8 +40,8 @@
               title: appInfo.about_us.title,
             })
           "
-        ></uni-list-item>
-        <!-- 为了过审 只有iOS-App有注销账号功能 -->
+        />
+        <!-- 为了过审 只有 iOS-App 有注销账号功能 -->
         <uni-list-item
           v-if="isLogin && sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
           title="注销账号"
@@ -58,10 +51,10 @@
           :border="false"
           class="list-border"
           @click="onLogoff"
-        ></uni-list-item>
+        />
       </uni-list>
     </view>
-
+    <!-- TODO 芋艿:统一的配置界面 -->
     <view class="set-footer ss-flex-col ss-row-center ss-col-center">
       <view class="agreement-box ss-flex ss-col-center ss-m-b-40">
         <view class="ss-flex ss-col-center ss-m-b-10">
@@ -124,32 +117,36 @@
     // H5实时更新无需检查
     // App 1.跳转应用市场更新 2.手动热更新 3.整包更新
   }
+
+  // 注销账号
   function onLogoff() {
     uni.showModal({
       title: '提示',
       content: '确认注销账号?',
       success: async function (res) {
-        if (res.confirm) {
-          const { error } = await sheep.$api.user.logoff();
-          if (error === 0) {
-            sheep.$store('user').logout();
-            sheep.$router.go('/pages/index/user');
-          }
+        if (!res.confirm) {
+          return;
+        }
+        const result = await sheep.$store('user').logout();
+        if (result) {
+          sheep.$router.go('/pages/index/user');
         }
       },
     });
   }
 
+  // 退出账号
   function onLogout() {
     uni.showModal({
       title: '提示',
       content: '确认退出账号?',
       success: async function (res) {
-        if (res.confirm) {
-          const result = await sheep.$store('user').logout();
-          if (result) {
-            sheep.$router.go('/pages/index/user');
-          }
+        if (!res.confirm) {
+          return;
+        }
+        const result = await sheep.$store('user').logout();
+        if (result) {
+          sheep.$router.go('/pages/index/user');
         }
       },
     });

+ 4 - 1
pages/public/webview.vue

@@ -1,5 +1,8 @@
+<!-- 网页加载 -->
 <template>
-  <view><web-view :src="url"></web-view></view>
+  <view>
+    <web-view :src="url" />
+  </view>
 </template>
 
 <script setup>

+ 91 - 108
pages/user/address/edit.vue

@@ -1,10 +1,11 @@
+<!-- 收货地址的新增/编辑 -->
 <template>
 	<s-layout :title="state.model.id ? '编辑地址' : '新增地址'">
-		<uni-forms ref="addressFormRef" v-model="state.model" :rules="state.rules" validateTrigger="bind"
+		<uni-forms ref="addressFormRef" v-model="state.model" :rules="rules" validateTrigger="bind"
 			labelWidth="160" labelAlign="left" border :labelStyle="{ fontWeight: 'bold' }">
 			<view class="bg-white form-box ss-p-x-30">
-				<uni-forms-item name="consignee" label="收货人" class="form-item">
-					<uni-easyinput v-model="state.model.consignee" placeholder="请填写收货人姓名" :inputBorder="false"
+				<uni-forms-item name="name" label="收货人" class="form-item">
+					<uni-easyinput v-model="state.model.name" placeholder="请填写收货人姓名" :inputBorder="false"
 						placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" />
 				</uni-forms-item>
 
@@ -13,178 +14,160 @@
 						placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal">
 					</uni-easyinput>
 				</uni-forms-item>
-				<uni-forms-item name="region" label="省市区" @tap="state.showRegion = true" class="form-item">
-					<uni-easyinput v-model="state.model.region" disabled :inputBorder="false"
+				<uni-forms-item name="areaName" label="省市区" @tap="state.showRegion = true" class="form-item">
+					<uni-easyinput v-model="state.model.areaName" disabled :inputBorder="false"
 						:styles="{ disableColor: '#fff', color: '#333' }"
 						placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
 						placeholder="请选择省市区">
 						<template v-slot:right>
-							<uni-icons type="right"></uni-icons>
+							<uni-icons type="right" />
 						</template>
 					</uni-easyinput>
 				</uni-forms-item>
-				<uni-forms-item name="address" label="详细地址" :formItemStyle="{ alignItems: 'flex-start' }"
+				<uni-forms-item name="detailAddress" label="详细地址" :formItemStyle="{ alignItems: 'flex-start' }"
 					:labelStyle="{ lineHeight: '5em' }" class="textarea-item">
-					<uni-easyinput :inputBorder="false" type="textarea" v-model="state.model.address"
+					<uni-easyinput :inputBorder="false" type="textarea" v-model="state.model.detailAddress"
 						placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-						placeholder="请输入详细地址" clearable></uni-easyinput>
+						placeholder="请输入详细地址" clearable />
 				</uni-forms-item>
 			</view>
-
 			<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box">
 				<view class="default-box-title"> 设为默认地址 </view>
-				<su-switch style="transform: scale(0.8)" v-model="state.model.is_default"></su-switch>
+				<su-switch style="transform: scale(0.8)" v-model="state.model.defaultStatus" />
 			</view>
 		</uni-forms>
 		<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
 			<view class="footer-box ss-flex-col ss-row-between ss-p-20">
-				<view class="ss-m-b-20"><button class="ss-reset-button save-btn ui-Shadow-Main"
-						@tap="onSave">保存</button></view>
+				<view class="ss-m-b-20">
+          <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="onSave">保存</button>
+        </view>
 				<button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
 					删除
 				</button>
 			</view>
 		</su-fixed>
+
 		<!-- 省市区弹窗 -->
-		<su-region-picker :show="state.showRegion" @cancel="state.showRegion = false" @confirm="onRegionConfirm">
-		</su-region-picker>
+		<su-region-picker :show="state.showRegion" @cancel="state.showRegion = false" @confirm="onRegionConfirm" />
 	</s-layout>
 </template>
 
 <script setup>
-	import {
-		computed,
-		watch,
-		ref,
-		reactive,
-		unref
-	} from 'vue';
+	import { ref, reactive, unref } from 'vue';
 	import sheep from '@/sheep';
-	import {
-		onLoad,
-		onPageScroll
-	} from '@dcloudio/uni-app';
+	import { onLoad } from '@dcloudio/uni-app';
 	import _ from 'lodash';
-	import {
-		consignee,
-		mobile,
-		address,
-		region
-	} from '@/sheep/validate/form';
+	import { mobile } from '@/sheep/validate/form';
+  import AreaApi from '@/sheep/api/system/area';
+  import AddressApi from '@/sheep/api/member/address';
 
 	const addressFormRef = ref(null);
 	const state = reactive({
 		showRegion: false,
 		model: {
-			consignee: '',
+			name: '',
 			mobile: '',
-			address: '',
-			is_default: false,
-			region: '',
-		},
-		rules: {
-			consignee,
-			mobile,
-			address,
-			region,
+      detailAddress: '',
+			defaultStatus: false,
+      areaName: '',
 		},
+    rules: {},
 	});
-	watch(
-		() => state.model.province_name,
-		(newValue) => {
-			if (newValue) {
-				state.model.region =
-					`${state.model.province_name}-${state.model.city_name}-${state.model.district_name}`;
-			}
-		}, {
-			deep: true,
-		},
-	);
+
+  const rules = {
+    name: {
+      rules: [
+        {
+          required: true,
+          errorMessage: '请输入收货人姓名',
+        },
+      ],
+    },
+    mobile,
+    detailAddress: {
+      rules: [{
+        required: true,
+        errorMessage: '请输入详细地址',
+      }]
+    },
+    areaName: {
+      rules: [{
+        required: true,
+        errorMessage: '请选择您的位置'
+      }]
+    },
+  };
+
+  // 确认选择地区
 	const onRegionConfirm = (e) => {
-		console.log(e);
-		state.model = {
-			...state.model,
-			...e,
-		};
+    state.model.areaName = `${e.province_name} ${e.city_name} ${e.district_name}`
+    state.model.areaId = e.district_id;
 		state.showRegion = false;
 	};
+
+  // 获得地区数据
 	const getAreaData = () => {
 		if (_.isEmpty(uni.getStorageSync('areaData'))) {
-			sheep.$api.data.area().then((res) => {
-				if (res.code === 0) {
-					uni.setStorageSync('areaData', res.data);
-				}
-			});
+      AreaApi.getAreaTree().then((res) => {
+        if (res.code === 0) {
+          uni.setStorageSync('areaData', res.data);
+        }
+      });
 		}
 	};
+
+  // 保存收货地址
 	const onSave = async () => {
+    // 参数校验
 		const validate = await unref(addressFormRef)
 			.validate()
 			.catch((error) => {
 				console.log('error: ', error);
 			});
-		if (!validate) return;
-
-		let res = null;
-		if (state.model.id) {
-			res = await sheep.$api.user.address.update({
-				id: state.model.id,
-				areaId: state.model.district_id,
-				defaultStatus: state.model.is_default,
-				detailAddress: state.model.address,
-				mobile: state.model.mobile,
-				name: state.model.consignee
-			});
-		} else {
-			res = await sheep.$api.user.address.create({
-				areaId: state.model.district_id,
-				defaultStatus: state.model.is_default,
-				detailAddress: state.model.address,
-				mobile: state.model.mobile,
-				name: state.model.consignee
-			});
-		}
-		if (res.code === 0) {
+		if (!validate) {
+      return;
+    }
+
+    // 提交请求
+    const formData = {
+      ...state.model
+    }
+    const {code } = state.model.id > 0 ? await AddressApi.updateAddress(formData)
+      : await AddressApi.createAddress(formData);
+		if (code === 0) {
 			sheep.$router.back();
 		}
 	};
 
+  // 删除收货地址
 	const onDelete = () => {
 		uni.showModal({
 			title: '提示',
 			content: '确认删除此收货地址吗?',
 			success: async function(res) {
-				if (res.confirm) {
-					const {
-						code
-					} = await sheep.$api.user.address.delete(state.model.id);
-					if (code === 0) {
-						sheep.$router.back();
-					}
+				if (!res.confirm) {
+					return;
 				}
+        const { code } = await AddressApi.deleteAddress(state.model.id);
+        if (code === 0) {
+          sheep.$router.back();
+        }
 			},
 		});
 	};
+
 	onLoad(async (options) => {
+    // 获得地区数据
 		getAreaData();
+    // 情况一:基于 id 获得收件地址
 		if (options.id) {
-			let res = await sheep.$api.user.address.detail(options.id);
-			if (res.code === 0) {
-				state.model = {
-					...state.model,
-					district_id: res.data.areaId,
-					is_default: res.data.defaultStatus,
-					address: res.data.detailAddress,
-					mobile: res.data.mobile,
-					consignee: res.data.name,
-					id: res.data.id,
-					province_name: res.data.areaName.split(' ')[0],
-					city_name: res.data.areaName.split(' ')[1],
-					district_name: res.data.areaName.split(' ')[2]
-				};
-			}
+			let { code, data} = await AddressApi.getAddress(options.id);
+      if (code !== 0) {
+        return;
+      }
+      state.model = data;
 		}
-
+    // 情况二:微信导入 TODO 芋艿:待接入
 		if (options.data) {
 			let data = JSON.parse(options.data);
 			const areaData = uni.getStorageSync('areaData');

+ 12 - 16
pages/user/address/list.vue

@@ -1,9 +1,9 @@
+<!-- 收件地址列表 -->
 <template>
 	<s-layout title="收货地址" :bgStyle="{ color: '#FFF' }">
 		<view v-if="state.list.length">
 			<s-address-item hasBorderBottom v-for="item in state.list" :key="item.id" :item="item"
-				@tap="onSelect(item)">
-			</s-address-item>
+                      @tap="onSelect(item)" />
 		</view>
 
 		<su-fixed bottom placeholder>
@@ -26,20 +26,15 @@
 </template>
 
 <script setup>
-	import {
-		reactive,
-		onBeforeMount
-	} from 'vue';
-	import {
-		onShow
-	} from '@dcloudio/uni-app';
+	import { reactive, onBeforeMount } from 'vue';
+	import { onShow } from '@dcloudio/uni-app';
 	import sheep from '@/sheep';
-	import {
-		isEmpty
-	} from 'lodash';
+	import { isEmpty } from 'lodash';
+  import AreaApi from '@/sheep/api/system/area';
+  import AddressApi from '@/sheep/api/member/address';
 
 	const state = reactive({
-		list: [],
+		list: [], // 地址列表
 		loading: true,
 	});
 
@@ -52,6 +47,7 @@
 	};
 
 	// 导入微信地址
+  // TODO 芋艿:未测试
 	function importWechatAddress() {
 		let wechatAddress = {};
 		// #ifdef MP
@@ -102,7 +98,7 @@
 	}
 
 	onShow(async () => {
-		state.list = (await sheep.$api.user.address.list()).data;
+		state.list = (await AddressApi.getAddressList()).data;
 		state.loading = false;
 	});
 
@@ -111,8 +107,8 @@
 			return;
 		}
 		// 提前加载省市区数据
-		sheep.$api.data.area().then((res) => {
-			if (res.error === 0) {
+    AreaApi.getAreaTree().then((res) => {
+			if (res.code === 0) {
 				uni.setStorageSync('areaData', res.data);
 			}
 		});

+ 59 - 70
pages/user/goods-collect.vue

@@ -1,12 +1,11 @@
+<!-- 我的商品收藏 -->
 <template>
   <s-layout title="商品收藏">
     <view class="cart-box ss-flex ss-flex-col ss-row-between">
       <!-- 头部 -->
       <view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
         <view class="header-left ss-flex ss-col-center ss-font-26">
-          共
-          <text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text>
-          件商品
+          共 <text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text> 件商品
         </view>
         <view class="header-right">
           <button
@@ -20,15 +19,16 @@
             v-if="!state.editMode && state.pagination.total"
             class="ss-reset-button ui-TC-Main"
             @tap="state.editMode = true"
-            >编辑</button
           >
+            编辑
+          </button>
         </view>
       </view>
       <!-- 内容 -->
       <view class="cart-content">
         <view
           class="goods-box ss-r-10 ss-m-b-14"
-          v-for="item in state.pagination.data"
+          v-for="item in state.pagination.list"
           :key="item.id"
         >
           <view class="ss-flex ss-col-center">
@@ -44,12 +44,10 @@
                 @tap.stop="onSelect(item.spuId)"
               />
             </label>
-			  <!-- :skuText="item.goods.subtitle" -->
             <s-goods-item
               :title="item.spuName"
               :img="item.picUrl"
               :price="item.price"
-            
               priceColor="#FF3000"
               :titleWidth="400"
               @tap="
@@ -57,11 +55,11 @@
                   id: item.spuId,
                 })
               "
-            >
-            </s-goods-item>
+            />
           </view>
         </view>
       </view>
+
       <!-- 底部 -->
       <su-fixed bottom :val="0" placeholder v-show="state.editMode">
         <view class="cart-footer ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom">
@@ -79,9 +77,9 @@
           <view class="footer-right">
             <button
               class="ss-reset-button ui-BG-Main-Gradient pay-btn ss-font-28 ui-Shadow-Main"
-              @tap="onCancel"
-              >取消收藏</button
-            >
+              @tap="onCancel">
+              取消收藏
+            </button>
           </view>
         </view>
       </su-fixed>
@@ -92,7 +90,7 @@
       :content-text="{
         contentdown: '上拉加载更多',
       }"
-      @tap="loadmore"
+      @tap="loadMore"
     />
     <s-empty v-if="state.pagination.total === 0" text="暂无收藏" icon="/static/collect-empty.png" />
   </s-layout>
@@ -103,98 +101,89 @@
   import { reactive } from 'vue';
   import { onLoad, onReachBottom } from '@dcloudio/uni-app';
   import _ from 'lodash';
+  import FavoriteApi from '@/sheep/api/product/favorite';
+  import { resetPagination } from '@/sheep/util';
 
   const sys_navBar = sheep.$platform.navbar;
-  const pagination = {
-    data: [],
-    current_page: 1,
-    total: 1,
-    last_page: 1,
-  };
+
   const state = reactive({
     pagination: {
-      data: [],
-      current_page: 1,
-      total: 1,
-      last_page: 1,
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 6,
     },
     loadStatus: '',
+
     editMode: false,
-    selectedCollectList: [],
+    selectedCollectList: [], // 选中的 SPU 数组
     selectAll: false,
   });
 
-  async function getData(page = 1, list_rows = 6) {
+  async function getData() {
     state.loadStatus = 'loading';
-    let res = await sheep.$api.user.favorite.list({
-      pageSize:list_rows,
-      pageNo:page,
+    const { code, data } = await FavoriteApi.getFavoritePage({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
     });
-    if (res.code === 0) {
-		console.log('yudao收藏列表',res)
-      let orderList = _.concat(state.pagination.data, res.data.list);
-      state.pagination = {
-        ...res.data,
-        data: orderList,
-      };
-	  // 没有原接口文档不太理解这字段意思
-      if (state.pagination.current_page < state.pagination.last_page) {
-        state.loadStatus = 'more';
-      } else {
-        state.loadStatus = 'noMore';
-      }
+    if (code !== 0) {
+      return;
     }
-  }
-  // 格式化价格
-  function formatPrice(e) {
-    return e.length === 1 ? e[0] : e.join('~');
+    state.pagination.list = _.concat(state.pagination.list, data.list)
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
   }
 
   // 单选选中
-  const onSelect = (id) => {
-    if (!state.selectedCollectList.includes(id)) {
-      state.selectedCollectList.push(id);
+  const onSelect = (spuId) => {
+    if (!state.selectedCollectList.includes(spuId)) {
+      state.selectedCollectList.push(spuId);
     } else {
-      state.selectedCollectList.splice(state.selectedCollectList.indexOf(id), 1);
+      state.selectedCollectList.splice(state.selectedCollectList.indexOf(spuId), 1);
     }
-    state.selectAll = state.selectedCollectList.length === state.pagination.data.length;
+    state.selectAll = state.selectedCollectList.length === state.pagination.list.length;
   };
+
   // 全选
   const onSelectAll = () => {
     state.selectAll = !state.selectAll;
     if (!state.selectAll) {
       state.selectedCollectList = [];
     } else {
-      state.pagination.data.forEach((item) => {
-        if (state.selectedCollectList.includes(item.goods_id)) {
-          state.selectedCollectList.splice(state.selectedCollectList.indexOf(item.goods_id), 1);
-        }
-        state.selectedCollectList.push(item.goods_id);
-      });
+      state.selectedCollectList = state.pagination.list.map((item) => item.spuId);
     }
   };
+
   async function onCancel() {
-    if (state.selectedCollectList) {
-      state.selectedCollectList = state.selectedCollectList.toString();
-      const { code } = await sheep.$api.user.favorite.cancel(state.selectedCollectList);
-      if (code === 0) {
-        state.editMode = false;
-        state.selectedCollectList = [];
-        state.selectAll = false;
-        state.pagination = pagination;
-        getData();
-      }
+    if (!state.selectedCollectList) {
+      return;
     }
+    // 取消收藏
+    for (const spuId of state.selectedCollectList) {
+      await FavoriteApi.deleteFavorite(spuId);
+    }
+
+    // 清空选择 + 重新加载
+    state.editMode = false;
+    state.selectedCollectList = [];
+    state.selectAll = false;
+    resetPagination(state.pagination);
+    await getData();
   }
+
   // 加载更多
-  function loadmore() {
-    if (state.loadStatus !== 'noMore') {
-      getData(state.pagination.current_page + 1);
+  function loadMore() {
+    if (state.loadStatus === 'noMore') {
+      return
     }
+    state.pagination.pageNo++;
+    getData();
   }
+
   onReachBottom(() => {
-    loadmore();
+    loadMore();
   });
+
   onLoad(() => {
     getData();
   });

+ 45 - 81
pages/user/info.vue

@@ -1,3 +1,4 @@
+<!-- 用户信息 -->
 <template>
   <s-layout title="用户信息" class="set-userinfo-wrap">
     <uni-forms
@@ -7,26 +8,27 @@
       border
       class="form-box"
     >
+      <!-- 头像 -->
       <view class="ss-flex ss-row-center ss-col-center ss-p-t-60 ss-p-b-0 bg-white">
         <view class="header-box-content">
           <su-image
             class="content-img"
             isPreview
             :current="0"
-            :src="sheep.$url.cdn(state.model.avatar)"
+            :src="state.model?.avatar"
             :height="160"
             :width="160"
             :radius="80"
             mode="scaleToFill"
-          ></su-image>
+          />
           <view class="avatar-action">
             <!-- #ifdef MP -->
             <button
               class="ss-reset-button avatar-action-btn"
               open-type="chooseAvatar"
-              @chooseavatar="onChooseAvatar"
-              >修改</button
-            >
+              @chooseavatar="onChooseAvatar">
+              修改
+            </button>
             <!-- #endif -->
             <!-- #ifndef MP -->
             <button class="ss-reset-button avatar-action-btn" @tap="onChangeAvatar">修改</button>
@@ -36,25 +38,7 @@
       </view>
 
       <view class="bg-white ss-p-x-30">
-      <!--  <uni-forms-item name="username" label="用户名" @tap="onChangeUsername" class="label-box">
-          <uni-easyinput
-            v-model="userInfo.username"
-            disabled
-            :inputBorder="false"
-            :styles="{ disableColor: '#fff' }"
-            placeholder="设置用户名"
-            :clearable="false"
-            :placeholderStyle="placeholderStyle"
-          >
-            <template v-slot:right>
-              <su-radio class="ss-flex" v-if="userInfo.verification?.username" :modelValue="true" />
-              <button v-else class="ss-reset-button">
-                <text class="_icon-forward" style="color: #bbbbbb; font-size: 26rpx"></text>
-              </button>
-            </template>
-          </uni-easyinput>
-        </uni-forms-item> -->
-
+        <!-- 昵称 + 性别 -->
         <uni-forms-item name="nickname" label="昵称">
           <uni-easyinput
             v-model="state.model.nickname"
@@ -64,24 +48,23 @@
             :placeholderStyle="placeholderStyle"
           />
         </uni-forms-item>
-
-<!--        <uni-forms-item name="gender" label="性别">
+        <uni-forms-item name="sex" label="性别">
           <view class="ss-flex ss-col-center ss-h-100">
             <radio-group @change="onChangeGender" class="ss-flex ss-col-center">
-              <label class="radio" v-for="item in genderRadioMap" :key="item.value">
+              <label class="radio" v-for="item in sexRadioMap" :key="item.value">
                 <view class="ss-flex ss-col-center ss-m-r-32">
                   <radio
                     :value="item.value"
                     color="var(--ui-BG-Main)"
                     style="transform: scale(0.8)"
-                    :checked="item.value == state.model.gender"
+                    :checked="parseInt(item.value) === state.model?.sex"
                   />
                   <view class="gender-name">{{ item.name }}</view>
                 </view>
               </label>
             </radio-group>
           </view>
-        </uni-forms-item> -->
+        </uni-forms-item>
 
         <uni-forms-item name="mobile" label="手机号" @tap="onChangeMobile">
           <uni-easyinput
@@ -107,7 +90,7 @@
         <uni-forms-item name="password" label="登录密码" @tap="onSetPassword">
           <uni-easyinput
             v-model="userInfo.password"
-            :placeholder="userInfo.verification?.password ? '修改登录密码' : '点击设置登录密码'"
+            placeholder="点击修改登录密码"
             :inputBorder="false"
             :styles="{ disableColor: '#fff' }"
             disabled
@@ -121,9 +104,8 @@
                   v-if="userInfo.verification?.password"
                   :modelValue="true"
                 />
-
                 <button v-else class="ss-reset-button ss-flex ss-col-center ss-row-center">
-                  <text class="_icon-forward" style="color: #bbbbbb; font-size: 26rpx"></text>
+                  <text class="_icon-forward" style="color: #bbbbbb; font-size: 26rpx" />
                 </button>
               </view>
             </template>
@@ -140,15 +122,7 @@
             showArrow
             :border="false"
             class="list-border"
-          ></uni-list-item>
-          <uni-list-item
-            clickable
-            @tap="sheep.$router.go('/pages/user/invoice/list')"
-            title="发票管理"
-            showArrow
-            :border="false"
-            class="list-border"
-          ></uni-list-item>
+          />
         </uni-list>
       </view>
     </uni-forms>
@@ -209,82 +183,74 @@
 </template>
 
 <script setup>
-  import { computed, ref, reactive, onBeforeMount, unref } from 'vue';
+  import { computed, ref, reactive, onBeforeMount } from 'vue';
   import { mobile, password, username } from '@/sheep/validate/form';
   import sheep from '@/sheep';
   import { clone } from 'lodash';
   import { showAuthModal } from '@/sheep/hooks/useModal';
+  import FileApi from '@/sheep/api/infra/file';
 
   const state = reactive({
-    model: {},
+    model: {}, // 个人信息
     rules: {},
     thirdOauthInfo: null,
   });
 
   const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal';
 
-  const genderRadioMap = [
-    {
+  const sexRadioMap = [{
       name: '男',
       value: '1',
     },
     {
       name: '女',
       value: '2',
-    },
-    {
-      name: '未知',
-      value: '0',
-    },
+    }
   ];
 
   const userInfo = computed(() => sheep.$store('user').userInfo);
 
-  // 选择性别
+  // 选择性别 TODO
   function onChangeGender(e) {
-    state.model.gender = e.detail.value;
+    state.model.sex = e.detail.value;
   }
-  // 修改用户名
-  const onChangeUsername = () => {
-    !state.model.verification?.username && showAuthModal('changeUsername');
-  };
 
-  // 修改手机号
+  // 修改手机号 TODO
   const onChangeMobile = () => {
     showAuthModal('changeMobile');
   };
 
+  // TODO 芋艿:微信公众号的处理,暂时忽略;后续再说
   function onChooseAvatar(e) {
     const tempUrl = e.detail.avatarUrl || '';
     uploadAvatar(tempUrl);
   }
 
-  //修改头像
+  // 手动选择头像,进行上传
   function onChangeAvatar() {
     uni.chooseImage({
       success: async (chooseImageRes) => {
         const tempUrl = chooseImageRes.tempFilePaths[0];
-        uploadAvatar(tempUrl);
+        await uploadAvatar(tempUrl);
       },
     });
   }
 
+  // 上传头像文件
   async function uploadAvatar(tempUrl) {
-    if (!tempUrl) return;
-    let { path } = await sheep.$api.app.upload(tempUrl, 'ugc');
-    state.model.avatar = path;
+    if (!tempUrl) {
+      return;
+    }
+    let { data } = await FileApi.uploadFile(tempUrl);
+    state.model.avatar = data;
   }
 
-  // 修改/设置密码
+  // 修改密码 TODO
   function onSetPassword() {
-    if (state.model.verification.password) {
-      showAuthModal('changePassword');
-    } else {
-      showAuthModal('resetPassword');
-    }
+    showAuthModal('changePassword');
   }
 
-  // 绑定第三方账号
+  // 绑定第三方账号 TODO
   async function bindThirdOauth() {
     let result = await sheep.$platform.useProvider('wechat').bind();
     if (result) {
@@ -292,7 +258,7 @@
     }
   }
 
-  // 解绑第三方账号
+  // 解绑第三方账号 TODO
   function unBindThirdOauth() {
     uni.showModal({
       title: '解绑提醒',
@@ -312,28 +278,26 @@
 
   // 保存信息
   async function onSubmit() {
-    // const { error, data } = await sheep.$api.user.update({
-    //   avatar: state.model.avatar,
-    //   nickname: state.model.nickname,
-    //   gender: state.model.gender,
-    // });  
-	 const { code, data } = await sheep.$api.user.update({
+	 const { code } = await sheep.$api.user.update({
       avatar: state.model.avatar,
       nickname: state.model.nickname,
-      // gender: state.model.gender,
+      sex: state.model.sex,
     });
     if (code === 0) {
-      getUserInfo();
+      await getUserInfo();
     }
   }
 
+  // 获得用户信息
   const getUserInfo = async () => {
+    // 个人信息
     const userInfo = await sheep.$store('user').getInfo();
     state.model = clone(userInfo);
 
+    // TODO 芋艿:第三方授权信息,待搞
     if (sheep.$platform.name !== 'H5') {
-		return;
-		// 这个先注释,要不然小程序保存个人信息有问题,
+		  return;
+		  // 这个先注释,要不然小程序保存个人信息有问题,
       let { data, error } = await sheep.$api.user.thirdOauthInfo();
       if (error === 0) {
         state.thirdOauthInfo = data;
@@ -341,7 +305,7 @@
     }
   };
 
-  onBeforeMount(async () => {
+  onBeforeMount(() => {
     getUserInfo();
   });
 </script>

+ 0 - 277
pages/user/invoice/edit.vue

@@ -1,277 +0,0 @@
-<template>
-  <s-layout :title="state.model.id ? '编辑发票' : '添加发票'">
-    <uni-forms
-      ref="invoiceFormRef"
-      v-model="state.model"
-      :rules="state.rules"
-      validateTrigger="bind"
-      labelWidth="160"
-      labelAlign="left"
-      border
-      :labelStyle="{ fontWeight: 'bold' }"
-    >
-      <view class="bg-white form-box ss-p-x-30">
-        <uni-forms-item name="type" label="发票类型">
-          <view class="ss-flex ss-col-center ss-h-100">
-            <radio-group @change="onChange" class="ss-flex ss-col-center">
-              <label class="radio" v-for="item in invoiceTypeList" :key="item.value">
-                <view class="ss-flex ss-col-center ss-m-r-32">
-                  <radio
-                    :value="item.value"
-                    color="var(--ui-BG-Main)"
-                    style="transform: scale(0.8)"
-                    :checked="item.value === state.model.type"
-                  />
-                  <view class="radio-name">
-                    {{ item.name }}
-                  </view>
-                </view>
-              </label>
-            </radio-group>
-          </view>
-        </uni-forms-item>
-        <view v-if="state.model.type === 'person'">
-          <uni-forms-item name="name" label="姓名">
-            <uni-easyinput
-              v-model="state.model.name"
-              type="text"
-              placeholder="请输入您的姓名(必填)"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item name="mobile" label="手机号">
-            <uni-easyinput
-              v-model="state.model.mobile"
-              type="number"
-              placeholder="请输入手机号(必填)"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-        </view>
-        <view v-if="state.model.type === 'company'">
-          <uni-forms-item name="name" label="单位名称">
-            <uni-easyinput
-              v-model="state.model.name"
-              type="text"
-              placeholder="请输入单位名称(必填)"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item name="mobile" label="手机号">
-            <uni-easyinput
-              v-model="state.model.mobile"
-              type="number"
-              placeholder="请输入手机号(必填)"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item name="tax_no" label="税号">
-            <uni-easyinput
-              v-model="state.model.tax_no"
-              type="text"
-              placeholder="请输入单位税号(必填)"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item name="bank_name" label="开户银行">
-            <uni-easyinput
-              v-model="state.model.bank_name"
-              type="text"
-              placeholder="请输入对公账户开户银行"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item name="bank_no" label="银行账号">
-            <uni-easyinput
-              v-model="state.model.bank_no"
-              type="text"
-              placeholder="请输入对公账户银行账号"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              :inputBorder="false"
-            ></uni-easyinput>
-          </uni-forms-item>
-          <uni-forms-item
-            name="address"
-            label="详细地址"
-            :formItemStyle="{ alignItems: 'flex-start' }"
-            :labelStyle="{ lineHeight: '5em' }"
-            class="textarea-item"
-          >
-            <uni-easyinput
-              :inputBorder="false"
-              type="textarea"
-              v-model="state.model.address"
-              placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
-              placeholder="请输入详细地址"
-              clearable
-            ></uni-easyinput>
-          </uni-forms-item>
-        </view>
-      </view>
-    </uni-forms>
-    <su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
-      <view class="footer-box ss-flex-col ss-row-between ss-p-20">
-        <view class="ss-m-b-20">
-          <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="onSave">保存</button>
-        </view>
-        <button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
-          删除
-        </button>
-      </view>
-    </su-fixed>
-  </s-layout>
-</template>
-
-<script setup>
-  import { computed, watch, ref, reactive, unref } from 'vue';
-  import sheep from '@/sheep';
-  import { onLoad, onPageScroll } from '@dcloudio/uni-app';
-  import _ from 'lodash';
-  import { realName, mobile, taxNo, taxName } from '@/sheep/validate/form';
-
-  const invoiceFormRef = ref(null);
-  const invoiceTypeList = [
-    {
-      name: '个人',
-      value: 'person',
-    },
-    {
-      name: '企/事业单位',
-      value: 'company',
-    },
-  ];
-  const state = reactive({
-    model: {
-      type: '',
-      name: '',
-      mobile: '',
-      tax_no: '',
-      bank_name: '',
-      bank_no: '',
-      address: '',
-    },
-    rules: {
-      name: taxName,
-      mobile,
-      tax_no: taxNo,
-    },
-  });
-
-  //发票
-  function onChange(e) {
-    state.model.type = e.detail.value;
-  }
-
-  const onSave = async () => {
-    const validate = await unref(invoiceFormRef)
-      .validate()
-      .catch((error) => {
-        console.log('error: ', error);
-      });
-    if (!validate) return;
-
-    let res = null;
-    if (state.model.id) {
-      res = await sheep.$api.user.invoice.update(state.model.id, state.model);
-    } else {
-      res = await sheep.$api.user.invoice.create(state.model);
-    }
-    if (res.error === 0) {
-      sheep.$router.back();
-    }
-  };
-  const onDelete = () => {
-    uni.showModal({
-      title: '提示',
-      content: '确认删除此发票信息吗?',
-      success: async function (res) {
-        if (res.confirm) {
-          const { error } = await sheep.$api.user.invoice.delete(state.model.id);
-          if (res.error === 0) {
-            sheep.$router.back();
-          }
-        }
-      },
-    });
-  };
-  onLoad(async (options) => {
-    if (options.id) {
-      let res = await sheep.$api.user.invoice.detail(options.id);
-      if (res.error === 0) {
-        state.model = {
-          ...state.model,
-          ...res.data,
-        };
-      }
-    } else {
-      state.model.type = 'person';
-    }
-
-    if (options.data) {
-      let data = JSON.parse(options.data);
-      state.model = {
-        ...state.model,
-        ...data,
-      };
-    }
-  });
-</script>
-
-<style lang="scss" scoped>
-  :deep() {
-    .uni-forms-item__label .label-text {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-    }
-
-    .uni-easyinput__content-input {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-      padding-left: 0 !important;
-    }
-
-    .uni-easyinput__content-textarea {
-      font-size: 28rpx !important;
-      color: #333333 !important;
-      line-height: normal !important;
-      margin-top: 4rpx;
-    }
-
-    .uni-icons {
-      font-size: 40rpx !important;
-    }
-
-    .is-textarea-icon {
-      margin-top: 14rpx;
-    }
-
-    .is-disabled {
-      color: #333333;
-    }
-  }
-
-  .footer-box {
-    .save-btn {
-      width: 710rpx;
-      height: 80rpx;
-      border-radius: 40rpx;
-      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-      color: $white;
-    }
-
-    .cancel-btn {
-      width: 710rpx;
-      height: 80rpx;
-      border-radius: 40rpx;
-      background: var(--ui-BG);
-    }
-  }
-</style>

+ 0 - 82
pages/user/invoice/list.vue

@@ -1,82 +0,0 @@
-<template>
-  <s-layout title="发票管理" :bgStyle="{ color: '#FFF' }">
-    <view v-if="state.list.length">
-      <s-invoice-item
-        v-for="item in state.list"
-        hasBorderBottom
-        :key="item.id"
-        :item="item"
-        :isDefault="item.is_default"
-        @tap="onSelect(item)"
-      ></s-invoice-item>
-    </view>
-
-    <su-fixed bottom placeholder>
-      <view class="footer-box ss-flex ss-row-between ss-p-20">
-        <button
-          class="add-btn ss-reset-button ui-Shadow-Main"
-          @tap="sheep.$router.go('/pages/user/invoice/edit')"
-        >
-          新增发票抬头
-        </button>
-      </view>
-    </su-fixed>
-    <s-empty
-      v-if="state.list.length === 0 && !state.loading"
-      text="暂无发票"
-      icon="/static/data-empty.png"
-    />
-  </s-layout>
-</template>
-
-<script setup>
-  import { reactive } from 'vue';
-  import { onShow } from '@dcloudio/uni-app';
-  import sheep from '@/sheep';
-  import _ from 'lodash';
-  const state = reactive({
-    list: [],
-    loading: true,
-  });
-  const onSelect = (invoiceInfo) => {
-    uni.$emit('SELECT_INVOICE', {
-      invoiceInfo,
-    });
-    sheep.$router.back();
-  };
-
-  onShow(async () => {
-    state.list = (await sheep.$api.user.invoice.list()).data;
-    state.loading = false;
-  });
-</script>
-
-<style lang="scss" scoped>
-  // page{
-  //   background-color: red;
-  // }
-  .footer-box {
-    .add-btn {
-      flex: 1;
-      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-      border-radius: 80rpx;
-      font-size: 30rpx;
-      font-weight: 500;
-      line-height: 80rpx;
-      color: $white;
-      position: relative;
-      z-index: 1;
-    }
-
-    .sync-wxaddress {
-      flex: 1;
-      line-height: 80rpx;
-      background: $white;
-      border-radius: 80rpx;
-      font-size: 30rpx;
-      font-weight: 500;
-      color: $dark-6;
-      margin-right: 16rpx;
-    }
-  }
-</style>

+ 0 - 88
pages/user/set.vue

@@ -1,88 +0,0 @@
-<template>
-  <s-layout class="set-wrap" title="编辑资料">
-    <view class="header-box ss-flex-col ss-row-center ss-col-center">
-      <image
-        class="logo-img ss-m-b-40"
-        src="/static/img/shop/tabbar/find2.png"
-        mode="aspectFit"
-      ></image>
-      <view class="name ss-m-b-24">SHEEP商城</view>
-      <view class="version">V1.3.0</view>
-    </view>
-
-    <uni-list :border="true">
-      <uni-list-item title="清除缓存" rightText="2M" showArrow></uni-list-item>
-      <uni-list-item title="当前版本" rightText="V1.3.1" showArrow></uni-list-item>
-      <uni-list-item title="意见反馈" showArrow></uni-list-item>
-      <uni-list-item title="关于我们" showArrow></uni-list-item>
-    </uni-list>
-
-    <view class="set-footer ss-flex-col ss-row-center ss-col-center">
-      <view class="agreement-box ss-flex ss-col-center ss-m-b-30">
-        <view class="ss-flex ss-col-center ss-m-b-10">
-          <view class="tcp-text">《用户协议》</view>
-          <view class="agreement-text">与</view>
-          <view class="tcp-text">《隐私协议》</view>
-        </view>
-      </view>
-      <view class="copyright-text ss-m-b-10">******版权所有</view>
-      <view class="copyright-text">Copyright© 2018-2022</view>
-    </view>
-  </s-layout>
-</template>
-
-<script setup></script>
-
-<style lang="scss" scoped>
-  .set-title {
-    margin: 0 30rpx;
-  }
-
-  .header-box {
-    padding: 100rpx 0;
-
-    .logo-img {
-      width: 160rpx;
-      height: 160rpx;
-    }
-
-    .name {
-      font-size: 42rpx;
-      line-height: 42rpx;
-      font-weight: bold;
-      color: $dark-3;
-    }
-
-    .version {
-      font-size: 32rpx;
-      font-weight: 500;
-      line-height: 32rpx;
-      color: $gray-b;
-    }
-  }
-
-  .set-footer {
-    margin: 200rpx 0;
-
-    .copyright-text {
-      font-size: 22rpx;
-      font-weight: 500;
-      color: $gray-c;
-      line-height: 30rpx;
-    }
-
-    .agreement-box {
-      margin: 80rpx auto 0;
-
-      .tcp-text {
-        color: var(--ui-BG-Main);
-      }
-
-      .agreement-text {
-        font-size: 26rpx;
-        font-weight: 500;
-        color: $dark-9;
-      }
-    }
-  }
-</style>

+ 362 - 337
pages/user/wallet/money.vue

@@ -1,345 +1,370 @@
+<!-- 我的钱包 -->
 <template>
-	<s-layout class="wallet-wrap" title="钱包">
-		<!-- 钱包卡片 -->
-		<view class="header-box ss-flex ss-row-center ss-col-center">
-			<view class="card-box ui-BG-Main ui-Shadow-Main">
-				<view class="card-head ss-flex ss-col-center">
-					<view class="card-title ss-m-r-10">钱包余额(元)</view>
-					<view @tap="state.showMoney = !state.showMoney" class="ss-eye-icon"
-						:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"></view>
-				</view>
-				<view class="ss-flex ss-row-between ss-col-center ss-m-t-64">
-					<view class="money-num">{{ state.showMoney ? userInfo.money : '*****' }}</view>
-					<button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')">
-						充值
-					</button>
-				</view>
-			</view>
-		</view>
-
-		<su-sticky>
-			<!-- 统计 -->
-			<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
-				<!-- 				<uni-datetime-picker v-model="state.data" type="daterange" @change="onChangeTime" :end="state.today">
-					<button class="ss-reset-button date-btn">
-						<text>{{ dateFilterText }}</text>
-						<text class="cicon-drop-down ss-seldate-icon"></text>
-					</button>
-				</uni-datetime-picker> -->
-
-				<view class="total-box">
-					<!-- state.pagination.income.toFixed(2) -->
-					<!-- 		<view class="ss-m-b-10">总收入¥{{ }}</view>
-					<view>总支出¥{{  }}</view> -->
-					<!-- (-state.pagination.expense).toFixed(2) -->
-				</view>
-			</view>
-			<su-tabs :list="tabMaps" @change="onChange" :scrollable="false" :current="state.currentTab"></su-tabs>
-		</su-sticky>
-		<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
-		<!-- 钱包记录 -->
-		<view v-if="state.pagination.total > 0">
-			<view class="wallet-list ss-flex border-bottom" v-for="item in state.pagination.data" :key="item.id">
-				<view class="list-content">
-					<view class="title-box ss-flex ss-row-between ss-m-b-20">
-						<!-- <text class="title ss-line-1">{{ item.event_text }}{{ item.memo ? '-' + item.memo : '' }}</text> -->
-						<text class="title ss-line-1">{{ item.title }}</text>
-						<view class="money">
-							<text v-if="(item.amount >= 0||item.price>=0)"
-								class="add">+{{ item.amount||item.price }}</text>
-							<text v-else class="minus">{{ item.price }}</text>
-						</view>
-					</view>
-					<text class="time">{{ item.createTime }}</text>
-				</view>
-			</view>
-		</view>
-		<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
+  <s-layout class="wallet-wrap" title="钱包">
+    <!-- 钱包卡片 -->
+    <view class="header-box ss-flex ss-row-center ss-col-center">
+      <view class="card-box ui-BG-Main ui-Shadow-Main">
+        <view class="card-head ss-flex ss-col-center">
+          <view class="card-title ss-m-r-10">钱包余额(元)</view>
+          <view
+            @tap="state.showMoney = !state.showMoney"
+            class="ss-eye-icon"
+            :class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
+          />
+        </view>
+        <view class="ss-flex ss-row-between ss-col-center ss-m-t-64">
+          <view class="money-num">{{ state.showMoney ? fen2yuan(userInfo.money) : '*****' }}</view>
+          <button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')">
+            充值
+          </button>
+        </view>
+      </view>
+    </view>
+
+    <su-sticky>
+      <!-- 统计 -->
+      <view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
+        <uni-datetime-picker v-model="state.data" type="daterange" @change="onChangeTime" :end="state.today">
+          <button class="ss-reset-button date-btn">
+            <text>{{ dateFilterText }}</text>
+            <text class="cicon-drop-down ss-seldate-icon"></text>
+          </button>
+        </uni-datetime-picker>
+        <view class="total-box">
+          <view class="ss-m-b-10">总收入¥{{ fen2yuan(state.summary.totalIncome) }}</view>
+          <view>总支出¥{{ fen2yuan(state.summary.totalExpense) }}</view>
+        </view>
+      </view>
+      <su-tabs
+        :list="tabMaps"
+        @change="onChange"
+        :scrollable="false"
+        :current="state.currentTab"
+      ></su-tabs>
+    </su-sticky>
+    <s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
+
+    <!-- 钱包记录 -->
+    <view v-if="state.pagination.total > 0">
+      <view
+        class="wallet-list ss-flex border-bottom"
+        v-for="item in state.pagination.list"
+        :key="item.id"
+      >
+        <view class="list-content">
+          <view class="title-box ss-flex ss-row-between ss-m-b-20">
+            <text class="title ss-line-1">
+              {{ item.title }}
+            </text>
+            <view class="money">
+              <text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text>
+              <text v-else class="minus">{{ fen2yuan(item.price) }}</text>
+            </view>
+          </view>
+          <text class="time">
+            {{ sheep.$helper.timeFormat(state.createTime, 'yyyy-mm-dd hh:MM:ss') }}
+          </text>
+        </view>
+      </view>
+    </view>
+    <uni-load-more
+      v-if="state.pagination.total > 0"
+      :status="state.loadStatus"
+      :content-text="{
         contentdown: '上拉加载更多',
-      }" />
-	</s-layout>
+      }"
+    />
+  </s-layout>
 </template>
 
 <script setup>
-	import {
-		computed,
-		watch,
-		reactive
-	} from 'vue';
-	import {
-		onLoad,
-		onReachBottom
-	} from '@dcloudio/uni-app';
-	import sheep from '@/sheep';
-	import dayjs from 'dayjs';
-	import _ from 'lodash';
-
-	const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png');
-
-	const pagination = {
-		data: [],
-		current_page: 1,
-		total: 1,
-		last_page: 1,
-		expense: 0,
-		income: 0,
-	};
-	// 数据
-	const state = reactive({
-		showMoney: false,
-		date: [],
-		currentTab: 0,
-		pagination,
-		loadStatus: '',
-		today: '',
-	});
-
-	const tabMaps = [{
-			name: '全部',
-			// value: 'all',
-		},
-		{
-			name: '收入',
-			value: '1',
-		},
-		{
-			name: '支出',
-			value: '2',
-		},
-	];
-	const userInfo = computed(() => sheep.$store('user').userInfo);
-	console.log(userInfo)
-	const dateFilterText = computed(() => {
-		if (state.date[0] === state.date[1]) {
-			return state.date[0];
-		} else {
-			return state.date.join('~');
-		}
-	});
-
-	async function getLogList(page = 1, list_rows = 8) {
-		// state.loadStatus = 'loading';
-		let res = await sheep.$api.user.wallet.log({
-			// type: 'money',
-			type: tabMaps[state.currentTab].value,
-			pageSize: list_rows,
-			pageNo: page,
-			// date: appendTimeHMS(state.date),
-		});
-		if (res.code === 0) {
-			let list = _.concat(state.pagination.data, res.data.list);
-			state.pagination = {
-				...res.data,
-				data: list,
-				income: res.data.income,
-				expense: res.data.expense,
-			};
-			console.log('交易数据', state.pagination)
-			if (state.pagination.current_page < state.pagination.last_page) {
-				state.loadStatus = 'more';
-			} else {
-				state.loadStatus = 'noMore';
-			}
-		}
-	}
-	onLoad(async (options) => {
-		state.today = dayjs().format('YYYY-MM-DD');
-		state.date = [state.today, state.today];
-		getLogList();
-	});
-
-	function onChange(e) {
-		state.pagination = pagination;
-		state.currentTab = e.index;
-		getLogList();
-	}
-
-	function onChangeTime(e) {
-		state.date[0] = e[0];
-		state.date[1] = e[e.length - 1];
-		state.pagination = pagination;
-		getLogList();
-	}
-
-	function appendTimeHMS(arr) {
-		return [arr[0] + ' 00:00:00', arr[1] + ' 23:59:59'];
-	}
-
-	onReachBottom(() => {
-		if (state.loadStatus !== 'noMore') {
-			getLogList(state.pagination.current_page + 1);
-		}
-	});
+  import { computed, reactive } from 'vue';
+  import { onLoad, onReachBottom } from '@dcloudio/uni-app';
+  import sheep from '@/sheep';
+  import dayjs from 'dayjs';
+  import _ from 'lodash';
+  import PayWalletApi from '@/sheep/api/pay/wallet';
+  import { fen2yuan } from '@/sheep/hooks/useGoods';
+  import { resetPagination } from '@/sheep/util';
+
+  const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png');
+
+  // 数据
+  const state = reactive({
+    showMoney: false,
+    date: [], // 筛选的时间段
+    currentTab: 0,
+    pagination: {
+      list: [],
+      total: 0,
+      pageNo: 1,
+      pageSize: 8
+    },
+    summary: {
+      totalIncome: 0,
+      totalExpense: 0,
+    },
+    loadStatus: '',
+    today: '',
+  });
+
+  const tabMaps = [
+    {
+      name: '全部',
+      value: '',
+    },
+    {
+      name: '收入',
+      value: '1',
+    },
+    {
+      name: '支出',
+      value: '2',
+    },
+  ];
+  const userInfo = computed(() => sheep.$store('user').userInfo);
+
+  // 格式化时间段
+  const dateFilterText = computed(() => {
+    if (state.date[0] === state.date[1]) {
+      return state.date[0];
+    } else {
+      return state.date.join('~');
+    }
+  });
+
+  // 获得钱包记录分页
+  async function getLogList() {
+    state.loadStatus = 'loading';
+    const { data, code } = await PayWalletApi.getWalletTransactionPage({
+      pageNo: state.pagination.pageNo,
+      pageSize: state.pagination.pageSize,
+      type: tabMaps[state.currentTab].value,
+      'createTime[0]': state.date[0] + ' 00:00:00',
+      'createTime[1]': state.date[1] + ' 23:59:59',
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.pagination.list = _.concat(state.pagination.list, data.list);
+    state.pagination.total = data.total;
+    state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+  }
+
+  // 获得钱包统计
+  async function getSummary() {
+    const { data, code } = await PayWalletApi.getWalletTransactionSummary({
+      'createTime': [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
+    });
+    if (code !== 0) {
+      return;
+    }
+    state.summary = data;
+  }
+
+  onLoad(() => {
+    state.today = dayjs().format('YYYY-MM-DD');
+    state.date = [state.today, state.today];
+    getLogList();
+    getSummary();
+  });
+
+  // 处理 tab 切换
+  function onChange(e) {
+    state.currentTab = e.index;
+    // 重新加载列表
+    resetPagination(state.pagination);
+    getLogList();
+    getSummary();
+  }
+
+  // 处理时间筛选
+  function onChangeTime(e) {
+    state.date[0] = e[0];
+    state.date[1] = e[e.length - 1];
+    // 重新加载列表
+    resetPagination(state.pagination);
+    getLogList();
+    getSummary();
+  }
+
+  onReachBottom(() => {
+    if (state.loadStatus === 'noMore') {
+      return;
+    }
+    state.pagination.pageNo++;
+    getLogList();
+  });
 </script>
 
 <style lang="scss" scoped>
-	// 钱包
-	.header-box {
-		background-color: $white;
-		padding: 30rpx;
-
-		.card-box {
-			width: 100%;
-			min-height: 300rpx;
-			padding: 40rpx;
-			background-size: 100% 100%;
-			border-radius: 30rpx;
-			overflow: hidden;
-			position: relative;
-			z-index: 1;
-			box-sizing: border-box;
-
-			&::after {
-				content: '';
-				display: block;
-				width: 100%;
-				height: 100%;
-				z-index: 2;
-				position: absolute;
-				top: 0;
-				left: 0;
-				background: v-bind(headerBg) no-repeat;
-				pointer-events: none;
-			}
-
-			.card-head {
-				color: $white;
-				font-size: 30rpx;
-			}
-
-			.ss-eye-icon {
-				font-size: 40rpx;
-				color: $white;
-			}
-
-			.money-num {
-				font-size: 70rpx;
-				line-height: 70rpx;
-				font-weight: 500;
-				color: $white;
-				font-family: OPPOSANS;
-			}
-
-			.reduce-num {
-				font-size: 26rpx;
-				font-weight: 400;
-				color: $white;
-			}
-
-			.topup-btn {
-				width: 120rpx;
-				height: 60rpx;
-				line-height: 60rpx;
-				border-radius: 30px;
-				font-size: 26rpx;
-				font-weight: 500;
-				background-color: $white;
-				color: var(--ui-BG-Main);
-			}
-		}
-	}
-
-	// 筛选
-
-	.filter-box {
-		height: 114rpx;
-		background-color: $bg-page;
-
-		.total-box {
-			font-size: 24rpx;
-			font-weight: 500;
-			color: $dark-9;
-		}
-
-		.date-btn {
-			background-color: $white;
-			line-height: 54rpx;
-			border-radius: 27rpx;
-			padding: 0 20rpx;
-			font-size: 24rpx;
-			font-weight: 500;
-			color: $dark-6;
-
-			.ss-seldate-icon {
-				font-size: 50rpx;
-				color: $dark-9;
-			}
-		}
-	}
-
-	.tabs-box {
-		background: $white;
-		border-bottom: 2rpx solid #eeeeee;
-	}
-
-	// tab
-	.wallet-tab-card {
-		.tab-item {
-			height: 80rpx;
-			position: relative;
-
-			.tab-title {
-				font-size: 30rpx;
-			}
-
-			.cur-tab-title {
-				font-weight: $font-weight-bold;
-			}
-
-			.tab-line {
-				width: 60rpx;
-				height: 6rpx;
-				border-radius: 6rpx;
-				position: absolute;
-				left: 50%;
-				transform: translateX(-50%);
-				bottom: 2rpx;
-				background-color: var(--ui-BG-Main);
-			}
-		}
-	}
-
-	// 钱包记录
-	.wallet-list {
-		padding: 30rpx;
-		background-color: #ffff;
-
-		.head-img {
-			width: 70rpx;
-			height: 70rpx;
-			border-radius: 50%;
-			background: $gray-c;
-		}
-
-		.list-content {
-			justify-content: space-between;
-			align-items: flex-start;
-			flex: 1;
-
-			.title {
-				font-size: 28rpx;
-				color: $dark-3;
-				width: 400rpx;
-			}
-
-			.time {
-				color: $gray-c;
-				font-size: 22rpx;
-			}
-		}
-
-		.money {
-			font-size: 28rpx;
-			font-weight: bold;
-			font-family: OPPOSANS;
-
-			.add {
-				color: var(--ui-BG-Main);
-			}
-
-			.minus {
-				color: $dark-3;
-			}
-		}
-	}
-</style>
+  // 钱包
+  .header-box {
+    background-color: $white;
+    padding: 30rpx;
+
+    .card-box {
+      width: 100%;
+      min-height: 300rpx;
+      padding: 40rpx;
+      background-size: 100% 100%;
+      border-radius: 30rpx;
+      overflow: hidden;
+      position: relative;
+      z-index: 1;
+      box-sizing: border-box;
+
+      &::after {
+        content: '';
+        display: block;
+        width: 100%;
+        height: 100%;
+        z-index: 2;
+        position: absolute;
+        top: 0;
+        left: 0;
+        background: v-bind(headerBg)
+          no-repeat;
+        pointer-events: none;
+      }
+
+      .card-head {
+        color: $white;
+        font-size: 30rpx;
+      }
+
+      .ss-eye-icon {
+        font-size: 40rpx;
+        color: $white;
+      }
+
+      .money-num {
+        font-size: 70rpx;
+        line-height: 70rpx;
+        font-weight: 500;
+        color: $white;
+        font-family: OPPOSANS;
+      }
+
+      .reduce-num {
+        font-size: 26rpx;
+        font-weight: 400;
+        color: $white;
+      }
+
+      .topup-btn {
+        width: 120rpx;
+        height: 60rpx;
+        line-height: 60rpx;
+        border-radius: 30px;
+        font-size: 26rpx;
+        font-weight: 500;
+        background-color: $white;
+        color: var(--ui-BG-Main);
+      }
+    }
+  }
+
+  // 筛选
+
+  .filter-box {
+    height: 114rpx;
+    background-color: $bg-page;
+
+    .total-box {
+      font-size: 24rpx;
+      font-weight: 500;
+      color: $dark-9;
+    }
+
+    .date-btn {
+      background-color: $white;
+      line-height: 54rpx;
+      border-radius: 27rpx;
+      padding: 0 20rpx;
+      font-size: 24rpx;
+      font-weight: 500;
+      color: $dark-6;
+
+      .ss-seldate-icon {
+        font-size: 50rpx;
+        color: $dark-9;
+      }
+    }
+  }
+
+  .tabs-box {
+    background: $white;
+    border-bottom: 2rpx solid #eeeeee;
+  }
+
+  // tab
+  .wallet-tab-card {
+    .tab-item {
+      height: 80rpx;
+      position: relative;
+
+      .tab-title {
+        font-size: 30rpx;
+      }
+
+      .cur-tab-title {
+        font-weight: $font-weight-bold;
+      }
+
+      .tab-line {
+        width: 60rpx;
+        height: 6rpx;
+        border-radius: 6rpx;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        bottom: 2rpx;
+        background-color: var(--ui-BG-Main);
+      }
+    }
+  }
+
+  // 钱包记录
+  .wallet-list {
+    padding: 30rpx;
+    background-color: #ffff;
+
+    .head-img {
+      width: 70rpx;
+      height: 70rpx;
+      border-radius: 50%;
+      background: $gray-c;
+    }
+
+    .list-content {
+      justify-content: space-between;
+      align-items: flex-start;
+      flex: 1;
+
+      .title {
+        font-size: 28rpx;
+        color: $dark-3;
+        width: 400rpx;
+      }
+
+      .time {
+        color: $gray-c;
+        font-size: 22rpx;
+      }
+    }
+
+    .money {
+      font-size: 28rpx;
+      font-weight: bold;
+      font-family: OPPOSANS;
+      .add {
+        color: var(--ui-BG-Main);
+      }
+
+      .minus {
+        color: $dark-3;
+      }
+    }
+  }
+</style>

+ 0 - 95
sheep/api/app.js

@@ -1,19 +1,6 @@
 import request from '@/sheep/request';
-import { baseUrl } from '@/sheep/config';
 
 export default {
-  // TODO 芋艿:测试
-  test: () =>
-    request({
-      url: '/app-api/promotion/decorate/list',
-      params: {
-        page: 1
-      },
-      custom: {
-        showError: false,
-        showLoading: false,
-      },
-    }),
   // 系统初始化
   init: (templateId) =>
     request({
@@ -39,50 +26,12 @@ export default {
         showLoading: false,
       },
     }),
-  // 发送短信
-  sendSms: (data) =>
-    request({
-      url: 'sendSms',
-      method: 'POST',
-      data,
-      custom: {
-        showSuccess: true,
-        loadingMsg: '发送中',
-      },
-    }),
-  //意见反馈
-  feedback: (data) =>
-    request({
-      url: 'feedback',
-      method: 'POST',
-      data,
-    }),
   // 自定义页面
   page: (id) =>
     request({
       url: 'page/' + id,
       method: 'GET',
     }),
-  //积分商城
-  scoreShop: {
-    list: (params) =>
-      request({
-        url: 'app/scoreShop',
-        method: 'GET',
-        params,
-      }),
-    ids: (params = {}) =>
-      request({
-        url: 'app/scoreShop/ids',
-        method: 'GET',
-        params,
-      }),
-    detail: (id) =>
-      request({
-        url: 'app/scoreShop/' + id,
-        method: 'GET',
-      }),
-  },
   //小程序直播
   mplive: {
     getRoomList: (ids) =>
@@ -99,48 +48,4 @@ export default {
         method: 'GET'
       }),
   },
-
-  //上传
-  upload: (file, group = 'ugc', callback) => {
-    const token = uni.getStorageSync('token');
-    uni.showLoading({
-      title: '上传中',
-    });
-    return new Promise((resolve, reject) => {
-		// 此处先换成yudao
-      uni.uploadFile({
-        url: 'http://api-dashboard.yudao.iocoder.cn' + '/app-api/infra/file/upload',
-        filePath: file,
-        name: 'file',
-        formData: {
-          group,
-        },
-        header: {
-          // Accept: 'text/json',
-          // Authorization: token,
-		  Accept : '*/*',
-		  'tenant-id' :'1',
-		  Authorization:  'Bearer test247',
-        },
-        success: (uploadFileRes) => {
-          let result = JSON.parse(uploadFileRes.data);
-          if (result.error === 1) {
-            uni.showToast({
-              icon: 'none',
-              title: result.msg,
-            });
-          } else {
-            return resolve(result.data);
-          }
-        },
-        fail: (error) => {
-          console.log('上传失败:', error);
-          return resolve(false);
-        },
-        complete: () => {
-          uni.hideLoading();
-        },
-      });
-    });
-  },
 };

+ 1 - 17
sheep/api/coupon.js

@@ -12,18 +12,7 @@ export default {
 				showLoading: false,
 			},
 		}),
-	userCoupon: (params) =>
-		request2({
-			url: 'promotion/coupon/page',
-			method: 'GET',
-			params,
-		}),
-	// userCoupon: (params) =>
-	//   request({
-	//     url: 'user/coupon',
-	//     method: 'GET',
-	//     params,
-	//   }),
+
 	detail: (id, user_coupon_id) =>
 		request({
 			url: 'coupon/' + id,
@@ -37,9 +26,4 @@ export default {
 			url: 'coupon/get/' + id,
 			method: 'POST',
 		}),
-	listByGoods: (id) =>
-		request({
-			url: 'coupon/listByGoods/' + id,
-			method: 'GET',
-		}),
 };

+ 4 - 1
sheep/api/index.js

@@ -7,4 +7,7 @@ Object.keys(files).forEach((key) => {
   };
 });
 
-export default api;
+// TODO 芋艿:直接在 useModal 引入 AuthUtil 会报错,所以采用这用这方式先
+api.AuthUtil = import.meta.globEager('./member/auth.js')['./member/auth.js'].default;
+
+export default api;

+ 49 - 0
sheep/api/infra/file.js

@@ -0,0 +1,49 @@
+import request2 from '@/sheep/request2';
+
+const FileApi = {
+  // 上传文件
+  uploadFile: (file) => {
+    const token = uni.getStorageSync('token');
+    uni.showLoading({
+      title: '上传中',
+    });
+    return new Promise((resolve, reject) => {
+      // 此处先换成yudao
+      // TODO 芋艿:后续搞下
+      uni.uploadFile({
+        // url: 'http://api-dashboard.yudao.iocoder.cn' + '/app-api/infra/file/upload',
+        url: 'http://127.0.0.1:48080' + '/app-api/infra/file/upload',
+        filePath: file,
+        name: 'file',
+        header: {
+          // Accept: 'text/json',
+          // Authorization: token,
+          Accept : '*/*',
+          'tenant-id' :'1',
+          Authorization:  'Bearer test247',
+        },
+        success: (uploadFileRes) => {
+          debugger
+          let result = JSON.parse(uploadFileRes.data);
+          if (result.error === 1) {
+            uni.showToast({
+              icon: 'none',
+              title: result.msg,
+            });
+          } else {
+            return resolve(result);
+          }
+        },
+        fail: (error) => {
+          console.log('上传失败:', error);
+          return resolve(false);
+        },
+        complete: () => {
+          uni.hideLoading();
+        },
+      });
+    });
+  },
+};
+
+export default FileApi;

+ 45 - 0
sheep/api/member/address.js

@@ -0,0 +1,45 @@
+import request2 from '@/sheep/request2';
+
+const AddressApi = {
+  // 获得用户收件地址列表
+  getAddressList: () => {
+    return request2({
+      url: '/app-api/member/address/list',
+      method: 'GET'
+    });
+  },
+  // 创建用户收件地址
+  createAddress: (data) => {
+    return request2({
+      url: '/app-api/member/address/create',
+      method: 'POST',
+      data
+    });
+  },
+  // 更新用户收件地址
+  updateAddress: (data) => {
+    return request2({
+      url: '/app-api/member/address/update',
+      method: 'PUT',
+      data
+    });
+  },
+  // 获得用户收件地址
+  getAddress: (id) => {
+    return request2({
+      url: '/app-api/member/address/get',
+      method: 'GET',
+      params: { id }
+    });
+  },
+  // 删除用户收件地址
+  deleteAddress: (id) => {
+    return request2({
+      url: '/app-api/member/address/delete',
+      method: 'DELETE',
+      params: { id }
+    });
+  },
+};
+
+export default AddressApi;

+ 42 - 0
sheep/api/member/auth.js

@@ -0,0 +1,42 @@
+import request from '@/sheep/request';
+
+const AuthUtil = {
+  // 使用手机 + 验证码登录
+  smsLogin: (data) => {
+    return request({
+      url: '/app-api/member/auth/sms-login',
+      method: 'POST',
+      data,
+      custom: {
+        showSuccess: true,
+        loadingMsg: '登录中',
+        successMsg: '登录成功',
+      },
+    });
+  },
+  // 发送手机验证码
+  sendSmsCode: (mobile, scene) => {
+    return request({
+      url: '/app-api/member/auth/send-sms-code',
+      method: 'POST',
+      data: {
+        mobile,
+        scene,
+      },
+      custom: {
+        loadingMsg: '发送中',
+        showSuccess: true,
+        successMsg: '发送成功',
+      },
+    });
+  },
+  // 登出系统
+  logout: () => {
+    return request({
+      url: '/app-api/member/auth/logout',
+      method: 'POST',
+    });
+  },
+};
+
+export default AuthUtil;

+ 32 - 0
sheep/api/member/user.js

@@ -0,0 +1,32 @@
+import request from '@/sheep/request2';
+
+const UserApi = {
+  // 修改密码
+  updateUserPassword: (data) => {
+    return request({
+      url: '/app-api/member/user/update-password',
+      method: 'PUT',
+      data,
+      custom: {
+        loadingMsg: '验证中',
+        showSuccess: true,
+        successMsg: '修改成功'
+      },
+    });
+  },
+  // 重置密码
+  resetUserPassword: (data) => {
+    return request({
+      url: '/app-api/member/user/reset-password',
+      method: 'PUT',
+      data,
+      custom: {
+        loadingMsg: '验证中',
+        showSuccess: true,
+        successMsg: '修改成功'
+      }
+    });
+  },
+};
+
+export default UserApi;

+ 0 - 6
sheep/api/order.js

@@ -15,12 +15,6 @@ export default {
 	// 		method: 'GET',
 	// 		params,
 	// 	}),
-	// 发票详情
-	invoice: (id) =>
-		request({
-			url: 'order/invoice/' + id,
-			method: 'GET',
-		}),
 	// 获取支付结果
 	payResult: (id) =>
 		request({

+ 14 - 0
sheep/api/pay/channel.js

@@ -0,0 +1,14 @@
+import request from '@/sheep/request';
+
+const PayChannelApi = {
+  // 获得指定应用的开启的支付渠道编码列表
+  getEnableChannelCodeList: (appId) => {
+    return request({
+      url: '/app-api/pay/channel/get-enable-code-list',
+      method: 'GET',
+      params: { appId }
+    });
+  },
+};
+
+export default PayChannelApi;

+ 22 - 0
sheep/api/pay/order.js

@@ -0,0 +1,22 @@
+import request from '@/sheep/request';
+
+const PayOrderApi = {
+  // 获得支付订单
+  getOrder: (id) => {
+    return request({
+      url: '/app-api/pay/order/get',
+      method: 'GET',
+      params: { id }
+    });
+  },
+  // 提交支付订单
+  submitOrder: (data) => {
+    return request({
+      url: '/app-api/pay/order/submit',
+      method: 'POST',
+      data
+    });
+  }
+};
+
+export default PayOrderApi;

+ 26 - 0
sheep/api/pay/wallet.js

@@ -0,0 +1,26 @@
+import request from '@/sheep/request';
+
+const PayWalletApi = {
+  // 获得钱包流水分页
+  getWalletTransactionPage: (params) => {
+    const queryString = Object.keys(params)
+      .map((key) => encodeURIComponent(key) + '=' + params[key])
+      .join('&');
+    return request({
+      url: `/app-api/pay/wallet-transaction/page?${queryString}`,
+      method: 'GET'
+    });
+  },
+  // 获得钱包流水统计
+  getWalletTransactionSummary: (params) => {
+    const queryString = `createTime=${params.createTime[0]}&createTime=${params.createTime[1]}`
+    return request({
+      url: `/app-api/pay/wallet-transaction/get-summary?${queryString}`,
+      // url: `/app-api/pay/wallet-transaction/get-summary`,
+      method: 'GET',
+      // params: params
+    });
+  },
+};
+
+export default PayWalletApi;

+ 16 - 7
sheep/api/product/category.js

@@ -1,12 +1,21 @@
 import request from '@/sheep/request';
 
 const CategoryApi = {
-    // 查询分类列表
-    getCategoryList: () => {
-        return request({
-            url: '/app-api/product/category/list',
-            method: 'GET'
-        });
-    }
+  // 查询分类列表
+  getCategoryList: () => {
+    return request({
+      url: '/app-api/product/category/list',
+      method: 'GET',
+    });
+  },
+  // 查询分类列表,指定编号
+  getCategoryListByIds: (ids) => {
+    return request({
+      url: '/app-api/product/category/list-by-ids',
+      method: 'GET',
+      params: { ids },
+    });
+  },
 };
+
 export default CategoryApi;

+ 44 - 0
sheep/api/product/favorite.js

@@ -0,0 +1,44 @@
+import request from '@/sheep/request';
+
+const FavoriteApi = {
+  // 获得商品收藏分页
+  getFavoritePage: (data) => {
+    return request({
+      url: '/app-api/product/favorite/page',
+      method: 'GET',
+      params: data
+    });
+  },
+  // 检查是否收藏过商品
+  isFavoriteExists: (spuId) => {
+    return request({
+      url: '/app-api/product/favorite/exits',
+      method: 'GET',
+      params: {
+        spuId
+      }
+    });
+  },
+  // 添加商品收藏
+  createFavorite: (spuId) => {
+    return request({
+      url: '/app-api/product/favorite/create',
+      method: 'POST',
+      data: {
+        spuId
+      }
+    });
+  },
+  // 取消商品收藏
+  deleteFavorite: (spuId) => {
+    return request({
+      url: '/app-api/product/favorite/delete',
+      method: 'DELETE',
+      data: {
+        spuId
+      }
+    });
+  }
+};
+
+export default FavoriteApi;

+ 43 - 1
sheep/api/promotion/coupon.js

@@ -1,6 +1,6 @@
 import request from '@/sheep/request';
 
-export default {
+const CouponApi = {
   // 获得优惠劵模板列表
   getCouponTemplateListByIds: (ids) => {
     return request({
@@ -17,4 +17,46 @@ export default {
       params: { spuId, productScope, count },
     });
   },
+  // 获得优惠劵模版分页
+  getCouponTemplatePage: (params) => {
+    return request({
+      url: '/app-api/promotion/coupon-template/page',
+      method: 'GET',
+      params,
+    });
+  },
+  // 获得优惠劵模版
+  getCouponTemplate: (id) => {
+    return request({
+      url: '/app-api/promotion/coupon-template/get',
+      method: 'GET',
+      params: { id },
+    });
+  },
+  // 我的优惠劵列表
+  getCouponPage: (params) => {
+    return request({
+      url: '/app-api/promotion/coupon/page',
+      method: 'GET',
+      params,
+    });
+  },
+  // 领取优惠券
+  takeCoupon: (templateId) => {
+    return request({
+      url: '/app-api/promotion/coupon/take',
+      method: 'POST',
+      data: { templateId },
+    });
+  },
+  // 获得优惠劵
+  getCoupon: (id) => {
+    return request({
+      url: '/app-api/promotion/coupon/get',
+      method: 'GET',
+      params: { id },
+    });
+  },
 };
+
+export default CouponApi;

+ 13 - 0
sheep/api/system/area.js

@@ -0,0 +1,13 @@
+import request2 from '@/sheep/request2';
+
+const AreaApi = {
+  // 获得地区树
+  getAreaTree: () => {
+    return request2({
+      url: '/app-api/system/area/tree',
+      method: 'GET'
+    });
+  },
+};
+
+export default AreaApi;

+ 45 - 0
sheep/api/trade/afterSale.js

@@ -0,0 +1,45 @@
+import request2 from '@/sheep/request2';
+import request from '@/sheep/request';
+
+const AfterSaleApi = {
+  // 创建售后
+  createAfterSale: (data) => {
+    return request2({
+      url: `/app-api/trade/after-sale/create`,
+      method: 'POST',
+      data,
+    });
+  },
+  // 获得售后
+  getAfterSale: (id) => {
+    return request2({
+      url: `/app-api/trade/after-sale/get`,
+      method: 'GET',
+      params: {
+        id,
+      },
+    });
+  },
+  // 取消售后
+  cancelAfterSale: (id) => {
+    return request2({
+      url: `/app-api/trade/after-sale/cancel`,
+      method: 'DELETE',
+      params: {
+        id,
+      },
+    });
+  },
+  // 获得售后日志列表
+  getAfterSaleLogList: (afterSaleId) => {
+    return request2({
+      url: `/app-api/trade/after-sale-log/list`,
+      method: 'GET',
+      params: {
+        afterSaleId,
+      },
+    });
+  }
+};
+
+export default AfterSaleApi;

+ 47 - 0
sheep/api/trade/cart.js

@@ -0,0 +1,47 @@
+import request2 from '@/sheep/request2';
+
+const CartApi = {
+  addCart: (data) => {
+    return request2({
+      url: '/app-api/trade/cart/add',
+      method: 'POST',
+      data: data,
+      // TODO 芋艿:这里没提示
+      custom: {
+        showSuccess: true,
+        successMsg: '已添加到购物车~',
+      }
+    });
+  },
+  updateCartCount: (data) => {
+    return request2({
+      url: '/app-api/trade/cart/update-count',
+      method: 'PUT',
+      data: data
+    });
+  },
+  updateCartSelected: (data) => {
+    return request2({
+      url: '/app-api/trade/cart/update-selected',
+      method: 'PUT',
+      data: data
+    });
+  },
+  deleteCart: (ids) => {
+    return request2({
+      url: '/app-api/trade/cart/delete',
+      method: 'DELETE',
+      params: {
+        ids
+      }
+    });
+  },
+  getCartList: () => {
+    return request2({
+      url: '/app-api/trade/cart/list',
+      method: 'GET',
+    });
+  },
+};
+
+export default CartApi;

+ 14 - 0
sheep/api/trade/config.js

@@ -0,0 +1,14 @@
+import request2 from '@/sheep/request2';
+import request from '@/sheep/request';
+
+const TradeConfigApi = {
+  // 获得交易配置
+  getTradeConfig: () => {
+    return request2({
+      url: `/app-api/trade/config/get`,
+      method: 'GET',
+    });
+  },
+};
+
+export default TradeConfigApi;

+ 103 - 0
sheep/api/trade/order.js

@@ -0,0 +1,103 @@
+import request2 from '@/sheep/request2';
+import request from '@/sheep/request';
+
+const OrderApi = {
+  // 计算订单信息
+  settlementOrder: (data) => {
+    const data2 = {
+      ...data,
+    };
+    // 移除多余字段
+    if (!(data.couponId > 0)) {
+      delete data2.couponId;
+    }
+    if (!(data.addressId > 0)) {
+      delete data2.addressId;
+    }
+    // 解决 SpringMVC 接受 List<Item> 参数的问题
+    delete data2.items;
+    for (let i = 0; i < data.items.length; i++) {
+      data2[encodeURIComponent('items[' + i + '' + '].skuId')] = data.items[i].skuId + '';
+      data2[encodeURIComponent('items[' + i + '' + '].count')] = data.items[i].count + '';
+      if (data.items[i].cartId) {
+        data2[encodeURIComponent('items[' + i + '' + '].cartId')] = data.items[i].cartId + '';
+      }
+    }
+    const queryString = Object.keys(data2)
+      .map((key) => key + '=' + data2[key])
+      .join('&');
+    return request2({
+      url: `trade/order/settlement?${queryString}`,
+      method: 'GET',
+    });
+  },
+  // 创建订单
+  createOrder: (data) => {
+    return request2({
+      url: `trade/order/create`,
+      method: 'POST',
+      data,
+    });
+  },
+  // 获得订单
+  getOrder: (id) => {
+    return request2({
+      url: `trade/order/get-detail`,
+      method: 'GET',
+      params: {
+        id,
+      },
+    });
+  },
+  // 订单列表
+  getOrderPage: (params) => {
+    return request({
+      url: '/app-api/trade/order/page',
+      method: 'GET',
+      params,
+      custom: {
+        showLoading: false,
+      },
+    });
+  },
+  // 确认收货
+  receiveOrder: (id) => {
+    return request2({
+      url: `/app-api/trade/order/receive`,
+      method: 'PUT',
+      params: {
+        id,
+      },
+    });
+  },
+  // 取消订单
+  cancelOrder: (id) => {
+    return request2({
+      url: `/app-api/trade/order/cancel`,
+      method: 'DELETE',
+      params: {
+        id,
+      },
+    });
+  },
+  // 删除订单
+  deleteOrder: (id) => {
+    return request2({
+      url: `/app-api/trade/order/delete`,
+      method: 'DELETE',
+      params: {
+        id,
+      },
+    });
+  },
+  // 创建单个评论
+  createOrderItemComment: (data) => {
+    return request2({
+      url: `/app-api/trade/order/item/create-comment`,
+      method: 'POST',
+      data,
+    });
+  },
+};
+
+export default OrderApi;

+ 0 - 112
sheep/api/user.js

@@ -92,29 +92,6 @@ export default {
 				loadingMsg: '正在注册',
 			},
 		}),
-	// 重置密码
-	resetPassword: (data) =>
-		request({
-			url: '/user/api/user/resetPassword',
-			method: 'POST',
-			data,
-			custom: {
-				showSuccess: true,
-				loadingMsg: '验证中',
-			},
-		}),
-
-	// 修改密码
-	changePassword: (data) =>
-		request({
-			url: '/user/api/user/changePassword',
-			method: 'POST',
-			data,
-			custom: {
-				showSuccess: true,
-				loadingMsg: '验证中',
-			},
-		}),
 
 	// 绑定、更换手机号
 	changeMobile: (data) =>
@@ -128,18 +105,6 @@ export default {
 			},
 		}),
 
-	// 修改用户名
-	changeUsername: (data) =>
-		request({
-			url: '/user/api/user/changeUsername',
-			method: 'POST',
-			data,
-			custom: {
-				showSuccess: true,
-				loadingMsg: '验证中',
-			},
-		}),
-
 	// 更新小程序信息
 	updateMpUserInfo: (data) =>
 		request({
@@ -218,12 +183,6 @@ export default {
 				method: 'GET',
 				custom: {},
 			}),
-		// list: () =>
-		//   request({
-		//     url: 'user/address',
-		//     method: 'GET',
-		//     custom: {},
-		//   }),
 		create: (data) =>
 			request2({
 				url: 'member/address/create',
@@ -233,15 +192,6 @@ export default {
 					showSuccess: true,
 				},
 			}),
-		// create: (data) =>
-		//   request({
-		//     url: 'user/address',
-		//     method: 'POST',
-		//     data,
-		//     custom: {
-		//       showSuccess: true,
-		//     },
-		//   }),
 		update: (data) =>
 			request2({
 				url: 'member/address/update',
@@ -251,71 +201,16 @@ export default {
 					showSuccess: true,
 				},
 			}),
-		// update: (id, data) =>
-		// 	request({
-		// 		url: 'user/address/' + id,
-		// 		method: 'PUT',
-		// 		data,
-		// 		custom: {
-		// 			showSuccess: true,
-		// 		},
-		// 	}),
 		detail: (id) =>
 			request2({
 				url: 'member/address/get?id=' + id,
 				method: 'GET',
 			}),
-		// detail: (id) =>
-		//   request({
-		//     url: 'user/address/' + id,
-		//     method: 'GET',
-		//   }),
 		delete: (id) =>
 			request2({
 				url: 'member/address/delete?id=' + id,
 				method: 'DELETE',
 			}),
-		// delete: (id) =>
-		// 	request({
-		// 		url: 'user/address/' + id,
-		// 		method: 'DELETE',
-		// 	}),
-	},
-	invoice: {
-		list: () =>
-			request({
-				url: 'user/invoice',
-				method: 'GET',
-				custom: {},
-			}),
-		create: (data) =>
-			request({
-				url: 'user/invoice',
-				method: 'POST',
-				data,
-				custom: {
-					showSuccess: true,
-				},
-			}),
-		update: (id, data) =>
-			request({
-				url: 'user/invoice/' + id,
-				method: 'PUT',
-				data,
-				custom: {
-					showSuccess: true,
-				},
-			}),
-		detail: (id) =>
-			request({
-				url: 'user/invoice/' + id,
-				method: 'GET',
-			}),
-		delete: (id) =>
-			request({
-				url: 'user/invoice/' + id,
-				method: 'DELETE',
-			}),
 	},
 	favorite: {
 		list: (params) =>
@@ -398,13 +293,6 @@ export default {
 				params,
 				custom: {},
 			}),
-		// log: (params) =>
-		// request({
-		// 	url: '/user/api/walletLog',
-		// 	method: 'GET',
-		// 	params,
-		// 	custom: {},
-		// }),
 	},
 	account: {
 		info: (params) =>

+ 12 - 6
sheep/components/s-address-item/s-address-item.vue

@@ -1,3 +1,4 @@
+<!-- 地址卡片 -->
 <template>
   <view
     class="address-item ss-flex ss-row-between ss-col-center"
@@ -11,18 +12,23 @@
           custom-style="background-color: var(--ui-BG-Main); border-color: var(--ui-BG-Main); color: #fff;"
           v-if="props.item.defaultStatus"
           text="默认"
-        ></uni-tag>
+        />
         {{ props.item.areaName }}
-		<!-- {{ props.item.city_name }} {{ props.item.district_name }} -->
       </view>
-      <view class="address-text">{{ props.item.detailAddress }}</view>
-      <view class="person-text">{{ props.item.name }} {{ props.item.mobile }}</view>
+      <view class="address-text">
+        {{ props.item.detailAddress }}
+      </view>
+      <view class="person-text">
+        {{ props.item.name }} {{ props.item.mobile }}
+      </view>
+    </view>
+    <view v-else>
+      <view class="address-text ss-m-b-10">请选择收货地址</view>
     </view>
-    <view v-else><view class="address-text ss-m-b-10">请选择收货地址</view></view>
     <slot>
       <button class="ss-reset-button edit-btn" @tap.stop="onEdit">
         <view class="edit-icon ss-flex ss-row-center ss-col-center">
-          <image :src="sheep.$url.static('/static/img/shop/user/address/edit.png')"></image>
+          <image :src="sheep.$url.static('/static/img/shop/user/address/edit.png')" />
         </view>
       </button>
     </slot>

+ 1 - 1
sheep/components/s-auth-modal/components/account-login.vue

@@ -4,10 +4,10 @@
     <!-- 标题栏 -->
     <view class="head-box ss-m-b-60 ss-flex-col">
       <view class="ss-flex ss-m-b-20">
-        <view class="head-title ss-m-r-40 head-title-animation">账号登录</view>
         <view class="head-title-active head-title-line" @tap="showAuthModal('smsLogin')">
           短信登录
         </view>
+        <view class="head-title ss-m-r-40 head-title-animation">账号登录</view>
       </view>
       <view class="head-subtitle">如果未设置过密码,请点击忘记密码</view>
     </view>

+ 45 - 60
sheep/components/s-auth-modal/components/change-password.vue

@@ -1,4 +1,4 @@
-<!-- 修改密码 - changePassword  -->
+<!-- 修改密码(登录时)  -->
 <template>
   <view>
     <!-- 标题栏 -->
@@ -16,29 +16,32 @@
       labelWidth="140"
       labelAlign="center"
     >
-      <uni-forms-item name="oldPassword" label="旧密码">
+      <uni-forms-item name="code" label="验证码">
         <uni-easyinput
-          placeholder="请输入旧密码"
-          v-model="state.model.oldPassword"
-          type="text"
+          placeholder="请输入验证码"
+          v-model="state.model.code"
+          type="number"
+          maxlength="4"
           :inputBorder="false"
-        />
-      </uni-forms-item>
-
-      <uni-forms-item name="newPassword" label="新密码">
-        <uni-easyinput
-          type="password"
-          placeholder="请输入新密码"
-          v-model="state.model.newPassword"
-          :inputBorder="false"
-        />
+        >
+          <template v-slot:right>
+            <button
+              class="ss-reset-button code-btn code-btn-start"
+              :disabled="state.isMobileEnd"
+              :class="{ 'code-btn-end': state.isMobileEnd }"
+              @tap="getSmsCode('changePassword')"
+            >
+              {{ getSmsTimer('resetPassword') }}
+            </button>
+          </template>
+        </uni-easyinput>
       </uni-forms-item>
 
-      <uni-forms-item name="reNewPassword" label="确认密码">
+      <uni-forms-item name="reNewPassword" label="密码">
         <uni-easyinput
           type="password"
-          placeholder="请重新输入您的新密码"
-          v-model="state.model.reNewPassword"
+          placeholder="请输入密码"
+          v-model="state.model.password"
           :inputBorder="false"
         >
           <template v-slot:right>
@@ -50,69 +53,51 @@
       </uni-forms-item>
     </uni-forms>
 
-    <view class="editPwd-btn-box ss-m-t-80">
-      <button class="ss-reset-button forgot-btn" @tap="showAuthModal('resetPassword')">
-        忘记密码
-      </button>
-    </view>
+    <button class="ss-reset-button type-btn" @tap="closeAuthModal">
+      取消修改
+    </button>
   </view>
 </template>
 
 <script setup>
-  import { computed, watch, ref, reactive, unref } from 'vue';
-  import sheep from '@/sheep';
-  import { password } from '@/sheep/validate/form';
-  import { showAuthModal, closeAuthModal } from '@/sheep/hooks/useModal';
+  import { ref, reactive, unref } from 'vue';
+  import { code, password } from '@/sheep/validate/form';
+  import { closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
+  import UserApi from '@/sheep/api/member/user';
 
   const changePasswordRef = ref(null);
 
   // 数据
   const state = reactive({
-    isMobileEnd: false, // 手机号输入完毕
     model: {
-      oldPassword: '', //旧密码
-      newPassword: '', //新密
-      reNewPassword: '', //确认密码
+      mobile: '', // 手机号
+      code: '', // 验证
+      password: '', // 密码
     },
     rules: {
-      oldPassword: password,
-      newPassword: password,
-      reNewPassword: {
-        rules: [
-          {
-            required: true,
-            errorMessage: '请确认密码',
-          },
-          {
-            validateFunction: function (rule, value, data, callback) {
-              if (value !== state.model.newPassword) {
-                callback('两次输入的密码不一致');
-              }
-              if (value === state.model.oldPassword) {
-                callback('新密码不能与旧密码相同');
-              }
-              return true;
-            },
-          },
-        ],
-      },
+      code,
+      password,
     },
   });
 
-  // 6.更改密码
+  // 更改密码
   async function changePasswordSubmit() {
+    // 参数校验
     const validate = await unref(changePasswordRef)
       .validate()
       .catch((error) => {
         console.log('error: ', error);
       });
-    if (!validate) return;
-    sheep.$api.user.changePassword(state.model).then((res) => {
-      if (res.error === 0) {
-        sheep.$store('user').getInfo();
-        closeAuthModal();
-      }
-    });
+    if (!validate) {
+      return;
+    }
+    // 发起请求
+    const { code } = await UserApi.updateUserPassword(state.model);
+    if (code !== 0) {
+      return;
+    }
+    // 成功后,只需要关闭弹窗
+    closeAuthModal();
   }
 </script>
 

+ 0 - 72
sheep/components/s-auth-modal/components/change-username.vue

@@ -1,72 +0,0 @@
-<!-- 修改用户名 changeUsername  -->
-<template>
-  <view>
-    <!-- 标题栏 -->
-    <view class="head-box ss-m-b-60">
-      <view class="head-title ss-m-b-20">修改用户名</view>
-      <view class="head-subtitle">用户名仅限修改一次</view>
-    </view>
-
-    <!-- 表单项 -->
-    <uni-forms
-      ref="formRef"
-      v-model="state.model"
-      :rules="state.rules"
-      validateTrigger="bind"
-      labelWidth="140"
-      labelAlign="center"
-    >
-      <uni-forms-item name="username" label="用户名">
-        <uni-easyinput
-          placeholder="请输入用户名"
-          v-model="state.model.username"
-          :inputBorder="false"
-        ></uni-easyinput>
-      </uni-forms-item>
-
-      <view class="editPwd-btn-box ss-m-t-80">
-        <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="changeUsernameSubmit">
-          保存
-        </button>
-      </view>
-    </uni-forms>
-  </view>
-</template>
-
-<script setup>
-  import { computed, watch, ref, reactive, unref } from 'vue';
-  import sheep from '@/sheep';
-  import { username } from '@/sheep/validate/form';
-  import { showAuthModal, closeAuthModal } from '@/sheep/hooks/useModal';
-  const formRef = ref(null);
-
-  // 数据
-  const state = reactive({
-    model: {
-      username: '',
-    },
-    rules: {
-      username,
-    },
-  });
-
-  // 7.修改用户名
-  async function changeUsernameSubmit() {
-    const validate = await unref(formRef)
-      .validate()
-      .catch((error) => {
-        console.log('error: ', error);
-      });
-    if (!validate) return;
-    sheep.$api.user.changeUsername(state.model).then((res) => {
-      if (res.error === 0) {
-        sheep.$store('user').getInfo();
-        closeAuthModal();
-      }
-    });
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import '../index.scss';
-</style>

+ 19 - 14
sheep/components/s-auth-modal/components/reset-password.vue

@@ -1,4 +1,4 @@
-<!-- 重置密码 - resetPassword--  -->
+<!-- 重置密码(未登录时)  -->
 <template>
   <view>
     <!-- 标题栏 -->
@@ -43,7 +43,7 @@
           type="number"
           maxlength="4"
           :inputBorder="false"
-        ></uni-easyinput>
+        />
       </uni-forms-item>
 
       <uni-forms-item name="password" label="密码">
@@ -69,10 +69,11 @@
 </template>
 
 <script setup>
-  import { computed, watch, ref, reactive, unref } from 'vue';
+  import { computed, ref, reactive, unref } from 'vue';
   import sheep from '@/sheep';
   import { code, mobile, password } from '@/sheep/validate/form';
   import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
+  import UserApi from '@/sheep/api/member/user';
 
   const resetPasswordRef = ref(null);
   const isLogin = computed(() => sheep.$store('user').isLogin);
@@ -81,9 +82,9 @@
   const state = reactive({
     isMobileEnd: false, // 手机号输入完毕
     model: {
-      mobile: '', //手机号
-      code: '', //验证码
-      password: '', //密码
+      mobile: '', // 手机号
+      code: '', // 验证码
+      password: '', // 密码
     },
     rules: {
       code,
@@ -92,20 +93,24 @@
     },
   });
 
-  // 4.重置密码
+  // 重置密码
   const resetPasswordSubmit = async () => {
+    // 参数校验
     const validate = await unref(resetPasswordRef)
       .validate()
       .catch((error) => {
         console.log('error: ', error);
       });
-    if (!validate) return;
-    sheep.$api.user.resetPassword(state.model).then((res) => {
-      if (res.error === 0) {
-        sheep.$store('user').getInfo();
-        closeAuthModal();
-      }
-    });
+    if (!validate) {
+      return;
+    }
+    // 发起请求
+    const { code } = await UserApi.resetUserPassword(state.model);
+    if (code !== 0) {
+      return;
+    }
+    // 成功后,用户重新登录
+    showAuthModal('accountLogin')
   };
 </script>
 

+ 14 - 10
sheep/components/s-auth-modal/components/sms-login.vue

@@ -4,12 +4,12 @@
     <!-- 标题栏 -->
     <view class="head-box ss-m-b-60">
       <view class="ss-flex ss-m-b-20">
-        <view class="head-title-active ss-m-r-40" @tap="showAuthModal('accountLogin')"
-          >账号登录</view
-        >
         <view class="head-title head-title-line head-title-animation">短信登录</view>
+        <view class="head-title-active ss-m-r-40" @tap="showAuthModal('accountLogin')">
+          账号登录
+        </view>
       </view>
-      <view class="head-subtitle">未注册手机号请先点击下方立即注册</view>
+      <view class="head-subtitle">未注册的手机号,验证后自动注册账号</view>
     </view>
 
     <!-- 表单项 -->
@@ -59,10 +59,11 @@
 </template>
 
 <script setup>
-  import { computed, watch, ref, reactive, unref } from 'vue';
+  import { ref, reactive, unref } from 'vue';
   import sheep from '@/sheep';
   import { code, mobile } from '@/sheep/validate/form';
   import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
+  import AuthUtil from '@/sheep/api/member/auth';
 
   const smsLoginRef = ref(null);
 
@@ -89,22 +90,25 @@
     },
   });
 
-  // 2.短信登录
+  // 短信登录
   async function smsLoginSubmit() {
+    // 参数校验
     const validate = await unref(smsLoginRef)
       .validate()
       .catch((error) => {
         console.log('error: ', error);
       });
-    if (!validate) return;
-
+    if (!validate) {
+      return;
+    }
     if (!props.agreeStatus) {
       emits('onConfirm', true)
       sheep.$helper.toast('请勾选同意');
       return;
     }
-    const { error } = await sheep.$api.user.smsLogin(state.model);
-    if (error === 0) {
+    // 提交数据
+    const { code } = await AuthUtil.smsLogin(state.model);
+    if (code === 0) {
       closeAuthModal();
     }
   }

+ 0 - 130
sheep/components/s-auth-modal/components/sms-register.vue

@@ -1,130 +0,0 @@
-<!-- 短信注册 - smsRegister  -->
-<template>
-  <view>
-    <!-- 标题栏 -->
-    <view class="head-box ss-m-b-60">
-      <view class="head-title ss-m-b-20">注册</view>
-      <view class="head-subtitle">请使用本人手机号完成注册</view>
-    </view>
-
-    <!-- 表单项 -->
-    <uni-forms
-      ref="smsRegisterRef"
-      v-model="state.model"
-      :rules="state.rules"
-      validateTrigger="bind"
-      labelWidth="140"
-      labelAlign="center"
-    >
-      <uni-forms-item name="mobile" label="手机号">
-        <uni-easyinput
-          placeholder="请输入手机号"
-          v-model="state.model.mobile"
-          type="number"
-          :inputBorder="false"
-        >
-          <template v-slot:right>
-            <button
-              class="ss-reset-button code-btn code-btn-start"
-              :disabled="state.isMobileEnd"
-              :class="{ 'code-btn-end': state.isMobileEnd }"
-              @tap="getSmsCode('smsRegister', state.model.mobile)"
-            >
-              {{ getSmsTimer('smsRegister') }}
-            </button>
-          </template>
-        </uni-easyinput>
-      </uni-forms-item>
-
-      <uni-forms-item name="code" label="验证码">
-        <uni-easyinput
-          placeholder="请输入验证码"
-          v-model="state.model.code"
-          :inputBorder="false"
-          type="number"
-          maxlength="4"
-        ></uni-easyinput>
-      </uni-forms-item>
-
-      <uni-forms-item name="password" label="密码">
-        <uni-easyinput
-          type="password"
-          placeholder="请输入密码"
-          v-model="state.model.password"
-          :inputBorder="false"
-        >
-          <template v-slot:right>
-            <button class="ss-reset-button login-btn-start" @tap="smsRegisterSubmit"> 注册 </button>
-          </template>
-        </uni-easyinput>
-      </uni-forms-item>
-    </uni-forms>
-
-    <button class="ss-reset-button type-btn" @tap="showAuthModal('accountLogin')">
-      返回登录
-    </button>
-  </view>
-</template>
-
-<script setup>
-  import { computed, ref, reactive, unref } from 'vue';
-  import sheep from '@/sheep';
-  import { code, mobile, password } from '@/sheep/validate/form';
-  import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
-
-  const props = defineProps({
-    agreeStatus: {
-      type: Boolean,
-      default: false,
-    },
-  });
-
-  const smsRegisterRef = ref(null);
-  
-  const isLogin = computed(() => sheep.$store('user').isLogin);
-
-  const emits = defineEmits(['onConfirm']);
-
-  // 数据
-  const state = reactive({
-    isMobileEnd: false, // 手机号输入完毕
-    model: {
-      mobile: '', // 手机号
-      code: '', // 验证码
-      password: '', // 密码
-    },
-    rules: {
-      code,
-      mobile,
-      password,
-    },
-  });
-
-  // 3.短信注册
-  async function smsRegisterSubmit() {
-    const validate = await unref(smsRegisterRef)
-      .validate()
-      .catch((error) => {
-        console.log('error: ', error);
-      });
-    if (!validate) return;
-
-    if (!props.agreeStatus) {
-      emits('onConfirm',true);
-      sheep.$helper.toast('请勾选同意');
-      return;
-    }
-
-    const { error } = await sheep.$api.user.smsRegister({
-      ...state.model,
-      shareInfo: uni.getStorageSync('shareLog') || {},
-    });
-    if (error === 0) {
-      closeAuthModal();
-    }
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import '../index.scss';
-</style>

+ 3 - 24
sheep/components/s-auth-modal/s-auth-modal.vue

@@ -12,13 +12,6 @@
       <!-- 2.短信登录  smsLogin -->
       <sms-login v-if="authType === 'smsLogin'" :agreeStatus="state.protocol" @onConfirm="onConfirm" />
 
-      <!-- 3.短信注册 smsRegister-->
-      <sms-register
-        v-if="authType === 'smsRegister'"
-        :agreeStatus="state.protocol"
-        @onConfirm="onConfirm"
-      />
-
       <!-- 4.忘记密码 resetPassword-->
       <reset-password v-if="authType === 'resetPassword'" />
 
@@ -26,10 +19,7 @@
       <change-mobile v-if="authType === 'changeMobile'" />
 
       <!-- 6.修改密码 changePassword-->
-      <change-passwrod v-if="authType === 'changePassword'" />
-
-      <!-- 7.修改用户名 changeUsername-->
-      <change-username v-if="authType === 'changeUsername'" />
+      <changePassword v-if="authType === 'changePassword'" />
 
       <!-- 8.微信小程序授权 changeUsername-->
       <mp-authorization v-if="authType === 'mpAuthorization'" />
@@ -42,22 +32,13 @@
         <!-- 立即注册&快捷登录 TextButton -->
         <view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
           <view class="register-title">还没有账号?</view>
-          <button class="ss-reset-button register-btn" @tap="showAuthModal('smsRegister')"
-            >立即注册</button
+          <button class="ss-reset-button register-btn" @tap="showAuthModal('smsRegister')">立即注册</button
           >
           <view class="or-title">或</view>
           <button class="ss-reset-button login-btn" @tap="thirdLogin('wechat')">快捷登录</button>
           <view class="circle"></view>
         </view>
 
-        <button
-          v-if="sheep.$platform.name !== 'WechatMiniProgram'"
-          class="ss-reset-button type-btn"
-          @tap="showAuthModal('smsRegister')"
-        >
-          立即注册
-        </button>
-
         <!-- 公众号|App微信登录 -->
         <button
           v-if="
@@ -126,11 +107,9 @@
   import sheep from '@/sheep';
   import accountLogin from './components/account-login.vue';
   import smsLogin from './components/sms-login.vue';
-  import smsRegister from './components/sms-register.vue';
   import resetPassword from './components/reset-password.vue';
   import changeMobile from './components/change-mobile.vue';
-  import changePasswrod from './components/change-password.vue';
-  import changeUsername from './components/change-username.vue';
+  import changePassword from './components/change-password.vue';
   import mpAuthorization from './components/mp-authorization.vue';
   import { closeAuthModal, showAuthModal } from '@/sheep/hooks/useModal';
 

+ 2 - 4
sheep/components/s-block-item/s-block-item.vue

@@ -20,7 +20,7 @@
     <!-- 图文组件:图片轮播 -->
     <s-image-banner v-if="type === 'Carousel'" :data="data" :styles="styles" />
     <!-- 基础组件:标题栏 -->
-    <s-title-block v-if="type === 'titleBlock'" :data="data" :styles="styles" />
+    <s-title-block v-if="type === 'TitleBar'" :data="data" :styles="styles" />
     <!-- 图文组件:广告魔方 -->
     <s-image-cube v-if="type === 'MagicCube'" :data="data" :styles="styles" />
     <!-- 图文组件:视频播放 -->
@@ -28,7 +28,7 @@
     <!-- 基础组件:分割线 -->
     <s-line-block v-if="type === 'Divider'" :data="data" />
     <!-- 图文组件:热区 -->
-    <s-hotzone-block v-if="type === 'hotzone'" :data="data" :styles="styles" />
+    <s-hotzone-block v-if="type === 'HotZone'" :data="data" :styles="styles" />
 
     <!-- 商品组件:商品卡片 -->
     <s-goods-card v-if="type === 'ProductCard'" :data="data" :styles="styles" />
@@ -39,8 +39,6 @@
     <s-groupon-block v-if="type === 'PromotionCombination'" :data="data" :styles="styles" />
     <!-- 营销组件:秒杀 -->
     <s-seckill-block v-if="type === 'PromotionSeckill'" :data="data" :styles="styles" />
-    <!-- 营销组件:积分商城(模式不一样,无法适配) -->
-    <s-score-block v-if="type === 'PromotionPoint'" :data="data" :styles="styles" />
     <!-- 营销组件:小程序直播(暂时没有这个功能) -->
     <s-live-block v-if="type === 'MpLive'" :data="data" :styles="styles" />
     <!-- 营销组件:优惠券 -->

+ 10 - 9
sheep/components/s-coupon-get/s-coupon-get.vue

@@ -1,3 +1,4 @@
+<!-- 商品详情 - 优惠劵领取 -->
 <template>
   <su-popup
     :show="show"
@@ -21,13 +22,11 @@
             <template #default>
               <button
                 class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
-                :class="
-                  item.get_status != 'can_get' && item.get_status != 'can_use' ? 'boder-btn' : ''
-                "
+                :class="!item.canTake ? 'boder-btn' : ''"
                 @click.stop="getBuy(item.id)"
-                :disabled="item.get_status != 'can_get' && item.get_status != 'can_use'"
+                :disabled="!item.canTake"
               >
-                {{ item.get_status_text }}
+                {{ item.canTake ? '立即领取' : '已领取' }}
               </button>
             </template>
           </s-coupon-list>
@@ -38,6 +37,7 @@
 </template>
 <script setup>
   import { computed, reactive } from 'vue';
+
   const props = defineProps({
     modelValue: {
       type: Object,
@@ -48,16 +48,17 @@
       default: false,
     },
   });
+
   const emits = defineEmits(['get', 'close']);
+
   const state = reactive({
-    couponInfo: computed(() => props.modelValue),
-    currentValue: -1,
-    couponId: '',
+    couponInfo: computed(() => props.modelValue)
   });
+
+  // 领取优惠劵
   const getBuy = (id) => {
     emits('get', id);
   };
-  //立即领取
 </script>
 <style lang="scss" scoped>
   .model-box {

+ 196 - 186
sheep/components/s-coupon-list/s-coupon-list.vue

@@ -1,195 +1,205 @@
 <template>
-	<view class="ss-m-20" :style="{ opacity: disabled ? '0.5' : '1' }">
-		<view class="content">
-			<!--      <view
+  <view class="ss-m-20" :style="{ opacity: disabled ? '0.5' : '1' }">
+    <view class="content">
+      <view
         class="tag ss-flex ss-row-center"
-        :class="
-          data.status == 'expired' || data.status == 'used' ? 'disabled-bg-color' : 'info-bg-color'
-        "
-        >{{ data.type_text }}</view
-      > -->
-			<view class="title ss-m-x-30 ss-p-t-18">
-				<view class="ss-flex ss-row-between">
-					<view class="value-text ss-flex-1 ss-m-r-10" :class="
-              data.status == 'expired' || data.status == 'used' ? 'disabled-color' : 'info-color'
-            ">{{ data.name }}</view>
-					<view>
-						<view class="ss-flex ss-col-bottom" :class="
-                data.status != 'expired' && data.status != 'used' ? 'price-text' : 'disabled-color'
-              ">
-							<view class="value-reduce ss-m-b-10" v-if="data.type === 'reduce'">¥</view>
-							<view class="value-price">{{ data.amount }}</view>
-							<view class="value-discount ss-m-b-10 ss-m-l-4" v-if="data.type === 'discount'">折</view>
-						</view>
-					</view>
-				</view>
-				<view class="ss-flex ss-row-between ss-m-t-16">
-					<view class="sellby-text" :class="
-              data.status == 'expired' || data.status == 'used'
-                ? 'disabled-color'
-                : 'subtitle-color'
-            ">
-						{{'有效期:' + data.use_start_time.substring(0, 11) }}至
-						{{ data.use_end_time.substring(0, 11)  }}
-						<!-- 				{{
-              type === 'user'
-                ? '有效期:' + data.use_start_time.substring(0, 11)
-                : '领取时间:' + data.get_start_time.substring(0, 11)
-            }}至
-						{{
-              type === 'user'
-                ? data.use_end_time.substring(0, 11)
-                : data.get_end_time.substring(0, 11)
-            }} -->
-					</view>
-					<view class="value-enough" :class="
-              data.status == 'expired' || data.status == 'used'
-                ? 'disabled-color'
-                : 'subtitle-color'
-            ">满{{ data.enough }}可用</view>
-				</view>
-			</view>
-		</view>
-
-		<view class="desc ss-flex ss-row-between">
-			<view>
-				<view class="desc-title">
-					{{ data.description }}
-				</view>
-				<view>
-					<slot name="reason">
-					</slot>
-				</view>
-			</view>
-			<view>
-				<slot></slot>
-			</view>
-		</view>
-	</view>
+        :class="isDisable ? 'disabled-bg-color' : 'info-bg-color'"
+      >
+        {{ data.discountType === 1 ? '满减券' : '折扣券' }}
+      </view>
+      <view class="title ss-m-x-30 ss-p-t-18">
+        <view class="ss-flex ss-row-between">
+          <view
+            class="value-text ss-flex-1 ss-m-r-10"
+            :class="isDisable ? 'disabled-color' : 'info-color'"
+          >
+            {{ data.name }}
+          </view>
+          <view>
+            <view
+              class="ss-flex ss-col-bottom"
+              :class="isDisable ? 'disabled-color' : 'price-text'"
+            >
+              <view class="value-reduce ss-m-b-10" v-if="data.discountType === 1">¥</view>
+              <view class="value-price">
+                {{
+                  data.discountType === 1
+                    ? fen2yuan(data.discountPrice)
+                    : data.discountPercent / 10.0
+                }}
+              </view>
+              <view class="value-discount ss-m-b-10 ss-m-l-4" v-if="data.discountType === 2"
+                >折</view
+              >
+            </view>
+          </view>
+        </view>
+        <view class="ss-flex ss-row-between ss-m-t-16">
+          <view
+            class="sellby-text"
+            :class=" isDisable ? 'disabled-color' : 'subtitle-color'"
+            v-if="data.validityType === 2"
+          >
+            有效期:领取后 {{ data.fixedEndTerm }} 天内可用
+          </view>
+          <view
+            class="sellby-text"
+            :class=" isDisable ? 'disabled-color' : 'subtitle-color'"
+            v-else
+          >
+            有效期: {{ sheep.$helper.timeFormat(data.validStartTime, 'yyyy-mm-dd') }} 至
+            {{ sheep.$helper.timeFormat(data.validEndTime, 'yyyy-mm-dd') }}
+          </view>
+          <view
+            class="value-enough"
+            :class="isDisable ? 'disabled-color' : 'subtitle-color'"
+          >
+            满 {{ fen2yuan(data.usePrice) }} 可用
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- TODO 芋艿:可优化,增加优惠劵的描述 -->
+    <view class="desc ss-flex ss-row-between">
+      <view>
+        <view class="desc-title">{{ data.description }}</view>
+        <view>
+          <slot name="reason" />
+        </view>
+      </view>
+      <view>
+        <slot />
+      </view>
+    </view>
+  </view>
 </template>
 
 <script setup>
-	import {
-		reactive
-	} from 'vue';
-	import sheep from '@/sheep';
-	const state = reactive({
-		stateMap: {
-			0: '立即领取',
-			1: '去使用',
-		},
-	});
-	// 接受参数
-	const props = defineProps({
-		data: {
-			type: Object,
-			default: {},
-		},
-		disabled: {
-			type: Boolean,
-			default: false,
-		},
-		type: {
-			type: String,
-			default: 'coupon',
-		},
-	});
+  import { computed, reactive } from 'vue';
+  import { fen2yuan } from '../../hooks/useGoods';
+  import sheep from '../../index';
+
+  const state = reactive({});
+
+  const isDisable = computed(() => {
+    if (props.type === 'coupon') {
+      return false;
+    }
+    return props.data.status !== 1;
+  });
+
+  // 接受参数
+  const props = defineProps({
+    data: {
+      type: Object,
+      default: {},
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    type: {
+      type: String,
+      default: 'coupon', // coupon 优惠劵模版;user 用户优惠劵
+    },
+  });
 </script>
 
 <style lang="scss" scoped>
-	.info-bg-color {
-		background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-	}
-
-	.disabled-bg-color {
-		background: #999;
-	}
-
-	.info-color {
-		color: #333;
-	}
-
-	.subtitle-color {
-		color: #666;
-	}
-
-	.disabled-color {
-		color: #999;
-	}
-
-	.content {
-		width: 100%;
-		background: #fff;
-		border-radius: 20rpx 20rpx 0 0;
-		-webkit-mask: radial-gradient(circle at 12rpx 100%, #0000 12rpx, red 0) -12rpx;
-		box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
-
-		.tag {
-			width: 100rpx;
-
-			color: #fff;
-			height: 40rpx;
-			font-size: 24rpx;
-			border-radius: 20rpx 0 20rpx 0;
-		}
-
-		.title {
-			padding-bottom: 22rpx;
-			border-bottom: 2rpx dashed #d3d3d3;
-
-			.value-text {
-				font-size: 32rpx;
-				font-weight: 600;
-			}
-
-			.sellby-text {
-				font-size: 24rpx;
-				font-weight: 400;
-			}
-
-			.value-price {
-				font-size: 64rpx;
-				font-weight: 500;
-				line-height: normal;
-				font-family: OPPOSANS;
-			}
-
-			.value-reduce {
-				line-height: normal;
-				font-size: 32rpx;
-			}
-
-			.value-discount {
-				line-height: normal;
-				font-size: 28rpx;
-			}
-
-			.value-enough {
-				font-size: 24rpx;
-				font-weight: 400;
-				font-family: OPPOSANS;
-			}
-		}
-	}
-
-	.desc {
-		width: 100%;
-		background: #fff;
-		-webkit-mask: radial-gradient(circle at 12rpx 0%, #0000 12rpx, red 0) -12rpx;
-		box-shadow: rgba(#000, 0.1);
-		box-sizing: border-box;
-		padding: 24rpx 30rpx;
-		box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
-		border-radius: 0 0 20rpx 20rpx;
-
-		.desc-title {
-			font-size: 24rpx;
-			color: #999;
-			font-weight: 400;
-		}
-	}
-
-	.price-text {
-		color: #ff0000;
-	}
+  .info-bg-color {
+    background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+  }
+
+  .disabled-bg-color {
+    background: #999;
+  }
+
+  .info-color {
+    color: #333;
+  }
+
+  .subtitle-color {
+    color: #666;
+  }
+
+  .disabled-color {
+    color: #999;
+  }
+
+  .content {
+    width: 100%;
+    background: #fff;
+    border-radius: 20rpx 20rpx 0 0;
+    -webkit-mask: radial-gradient(circle at 12rpx 100%, #0000 12rpx, red 0) -12rpx;
+    box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
+
+    .tag {
+      width: 100rpx;
+
+      color: #fff;
+      height: 40rpx;
+      font-size: 24rpx;
+      border-radius: 20rpx 0 20rpx 0;
+    }
+
+    .title {
+      padding-bottom: 22rpx;
+      border-bottom: 2rpx dashed #d3d3d3;
+
+      .value-text {
+        font-size: 32rpx;
+        font-weight: 600;
+      }
+
+      .sellby-text {
+        font-size: 24rpx;
+        font-weight: 400;
+      }
+
+      .value-price {
+        font-size: 64rpx;
+        font-weight: 500;
+        line-height: normal;
+        font-family: OPPOSANS;
+      }
+
+      .value-reduce {
+        line-height: normal;
+        font-size: 32rpx;
+      }
+
+      .value-discount {
+        line-height: normal;
+        font-size: 28rpx;
+      }
+
+      .value-enough {
+        font-size: 24rpx;
+        font-weight: 400;
+        font-family: OPPOSANS;
+      }
+    }
+  }
+
+  .desc {
+    width: 100%;
+    background: #fff;
+    -webkit-mask: radial-gradient(circle at 12rpx 0%, #0000 12rpx, red 0) -12rpx;
+    box-shadow: rgba(#000, 0.1);
+    box-sizing: border-box;
+    padding: 24rpx 30rpx;
+    box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
+    border-radius: 0 0 20rpx 20rpx;
+
+    .desc-title {
+      font-size: 24rpx;
+      color: #999;
+      font-weight: 400;
+    }
+  }
+
+  .price-text {
+    color: #ff0000;
+  }
 </style>

+ 1 - 13
sheep/components/s-float-menu/s-float-menu.vue

@@ -25,19 +25,7 @@
   const props = defineProps({
     data: {
       type: Object,
-      default() {
-        return {
-          // horizontal vertical
-          direction: 'vertical',
-          showText: true,
-          list: [{
-            imgUrl: 'http://localhost/logo.gif',
-            url: '',
-            text: '客服',
-            textColor: '',
-          }],
-        }
-      },
+      default() {},
     }
   })
 

+ 16 - 15
sheep/components/s-goods-column/s-goods-column.vue

@@ -29,7 +29,7 @@
           :style="[{ color: goodsFields.price.color }]"
         >
           <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-          {{ isArray(data.price) ? data.price[0] : data.price }}
+          {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
         </view>
       </view>
     </view>
@@ -55,7 +55,7 @@
           :style="[{ color: goodsFields.price.color }]"
         >
           <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-          {{ isArray(data.price) ? data.price[0] : data.price }}
+          {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
         </view>
       </view>
     </view>
@@ -102,7 +102,7 @@
             :style="[{ color: goodsFields.price.color }]"
           >
             <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-            {{ isArray(data.price) ? data.price[0] : data.price }}
+            {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
           </view>
 
           <view
@@ -111,7 +111,7 @@
             :style="[{ color: originPriceColor }]"
           >
             <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-            <view class="ss-m-l-8">{{ data.original_price||data.marketPrice }}</view>
+            <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
           </view>
         </view>
 
@@ -122,7 +122,7 @@
 
       <slot name="cart">
         <view class="cart-box ss-flex ss-col-center ss-row-center">
-          <image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode=""></image>
+          <image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode="" />
         </view>
       </slot>
     </view>
@@ -174,7 +174,7 @@
               :style="[{ color: goodsFields.price.color }]"
             >
               <text class="ss-font-24">{{ priceUnit }}</text>
-              {{ isArray(data.price) ? data.price[0] : data.price }}
+              {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
             </view>
             <view
               v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)"
@@ -182,7 +182,7 @@
               :style="[{ color: originPriceColor }]"
             >
               <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-              <view class="ss-m-l-8">{{ data.original_price||data.marketPrice }}</view>
+              <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
             </view>
           </view>
           <view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
@@ -191,11 +191,11 @@
         </view>
       </view>
 
-      <slot name="cart"
-        ><view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"
-          >去购买</view
-        ></slot
-      >
+      <slot name="cart">
+        <view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow">
+          去购买
+        </view>
+      </slot>
     </view>
 
     <!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
@@ -238,7 +238,7 @@
           <view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
             <view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
               <text class="price-unit ss-font-24">{{ priceUnit }}</text>
-              {{ isArray(data.price) ? data.price[0] : data.price }}
+              {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
             </view>
             <view
               v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)"
@@ -246,7 +246,7 @@
               :style="[{ color: originPriceColor }]"
             >
               <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-              <view class="ss-m-l-8">{{ data.original_price||data.marketPrice }}</view>
+              <view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
             </view>
           </view>
           <view class="ss-m-t-16 ss-flex ss-flex-wrap">
@@ -293,7 +293,7 @@
    */
   import { computed, reactive, getCurrentInstance, onMounted, nextTick } from 'vue';
   import sheep from '@/sheep';
-  import { formatSales } from '@/sheep/hooks/useGoods';
+  import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods';
   import { formatStock } from '@/sheep/hooks/useGoods';
   import goodsCollectVue from '@/pages/user/goods-collect.vue';
   import { isArray } from 'lodash';
@@ -307,6 +307,7 @@
       type: [Array, Object],
       default() {
         return {
+          // TODO @疯狂:旧的要不剔除掉,后续都用新的
           // 商品名称(旧)
           title: { show: true },
           // 商品介绍(旧)

+ 2 - 9
sheep/components/s-goods-item/s-goods-item.vue

@@ -26,15 +26,7 @@
               :style="[{ color: priceColor }]"
               v-if="price && Number(price) > 0"
             >
-              ¥{{ price }}
-            </view>
-            <view v-if="score && Number(price) > 0">+</view>
-            <view class="price-text ss-flex ss-col-center" v-if="score">
-              <image
-                :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-                class="score-img"
-              ></image>
-              <view>{{ score }}</view>
+              ¥{{ fen2yuan(price) }}
             </view>
             <view v-if="num" class="total-text ss-flex ss-col-center">x {{ num }}</view>
             <slot name="priceSuffix"></slot>
@@ -54,6 +46,7 @@
 <script setup>
   import sheep from '@/sheep';
   import { computed } from 'vue';
+  import { fen2yuan } from '@/sheep/hooks/useGoods';
   /**
    * 订单卡片
    *

+ 2 - 1
sheep/components/s-goods-scroll/s-goods-scroll.vue

@@ -1,3 +1,4 @@
+<!-- 商品组 - 横向滚动商品(目前暂时没用到) -->
 <template>
   <view class="goods-scroll-box">
     <scroll-view class="scroll-box" scroll-x scroll-anchoring>
@@ -8,7 +9,7 @@
             size="sm"
             :data="item"
             :titleWidth="200 - marginLeft - marginRight"
-          ></s-goods-column>
+          />
         </view>
       </view>
     </scroll-view>

+ 7 - 7
sheep/components/s-hotzone-block/s-hotzone-block.vue

@@ -1,16 +1,16 @@
 <template>
   <view class="hotzone-wrap">
-    <image :src="sheep.$url.cdn(data.src)" style="width: 100%" mode="widthFix"></image>
+    <image :src="sheep.$url.cdn(data.imgUrl)" style="width: 100%" mode="widthFix"></image>
     <view
       class="hotzone-box"
-      v-for="item in data.list"
-      :key="item.width"
+      v-for="(item, index) in data.list"
+      :key="index"
       :style="[
         {
-          top: item.top + 'rpx',
-          left: item.left + 'rpx',
-          width: item.width + 'rpx',
-          height: item.height + 'rpx',
+          top: `${item.top}px`,
+          left: `${item.left}px`,
+          width: `${item.width}px`,
+          height: `${item.height}px`,
         },
       ]"
       @tap.stop="sheep.$router.go(item.url)"

+ 0 - 96
sheep/components/s-invoice-item/s-invoice-item.vue

@@ -1,96 +0,0 @@
-<template>
-  <view
-    class="address-item ss-flex ss-row-between ss-col-center"
-    :class="[{ 'border-bottom': props.hasBorderBottom }]"
-  >
-    <view class="item-left" v-if="!_.isEmpty(props.item)">
-      <view class="address-text">{{ props.item.name }}</view>
-      <view class="person-text">{{ props.item.mobile }}</view>
-    </view>
-    <view v-else>
-      <view class="address-text">请选择收货地址</view>
-    </view>
-    <slot>
-      <button class="ss-reset-button edit-btn" @tap.stop="onEdit">
-        <view class="edit-icon ss-flex ss-row-center ss-col-center">
-          <image :src="sheep.$url.static('/static/img/shop/user/address/edit.png')"></image>
-        </view>
-      </button>
-    </slot>
-  </view>
-</template>
-
-<script setup>
-  /**
-   * 基础组件 - 地址卡片
-   *
-   * @param {String}  icon = _icon-edit    - icon
-   *
-   * @event {Function()} click			 - 点击
-   * @event {Function()} actionClick		 - 点击工具栏
-   *
-   * @slot 								 - 默认插槽
-   */
-  import sheep from '@/sheep';
-  import _ from 'lodash';
-  const props = defineProps({
-    item: {
-      type: Object,
-      default() {},
-    },
-    hasBorderBottom: {
-      type: Boolean,
-      defult: true,
-    },
-  });
-
-  const onEdit = () => {
-    sheep.$router.go('/pages/user/invoice/edit', {
-      id: props.item.id,
-    });
-  };
-</script>
-
-<style lang="scss" scoped>
-  .address-item {
-    padding: 30rpx;
-
-    .item-left {
-      width: 600rpx;
-    }
-
-    .area-text {
-      font-size: 26rpx;
-      font-weight: 400;
-      color: $dark-9;
-    }
-
-    .address-text {
-      font-size: 32rpx;
-      font-weight: 600;
-      color: #333333;
-      line-height: 48rpx;
-    }
-
-    .person-text {
-      font-size: 28rpx;
-      font-weight: 400;
-      color: $dark-9;
-    }
-  }
-
-  .edit-btn {
-    width: 44rpx;
-    height: 44rpx;
-    background: $gray-f;
-    border-radius: 50%;
-    .edit-icon {
-      width: 24rpx;
-      height: 24rpx;
-    }
-  }
-  image {
-    width: 100%;
-    height: 100%;
-  }
-</style>

+ 4 - 4
sheep/components/s-menu-button/s-menu-button.vue

@@ -197,7 +197,7 @@
     },
     iconSize: {
       type: Number,
-      default: 96,
+      default: 80,
     },
     color: {
       type: String,
@@ -264,13 +264,13 @@
 
     .menu-icon {
       transform: translate(0, 0);
-      width: 98rpx;
-      height: 98rpx;
+      width: 80rpx;
+      height: 80rpx;
       padding-bottom: 10rpx;
     }
 
     .menu-title {
-      font-size: 30rpx;
+      font-size: 24rpx;
       color: #333;
     }
   }

+ 2 - 6
sheep/components/s-menu-tools/s-menu-tools.vue

@@ -1,3 +1,4 @@
+<!-- 全局 - 快捷入口 -->
 <template>
   <su-popup :show="show" type="top" round="20" backgroundColor="#F0F0F0" @close="closeMenuTools">
     <su-status-bar />
@@ -10,7 +11,7 @@
               class="ss-reset-button list-image ss-flex ss-row-center ss-col-center"
               @tap="onClick(item)"
             >
-              <image v-if="show" :src="sheep.$url.static(item.icon)" class="list-icon"></image>
+              <image v-if="show" :src="sheep.$url.static(item.icon)" class="list-icon" />
             </button>
             <view class="list-title ss-m-t-20">{{ item.title }}</view>
           </view>
@@ -63,11 +64,6 @@
       icon: '/static/img/shop/tools/collect.png',
       title: '我的收藏',
     },
-    {
-      url: '/pages/public/feedback',
-      icon: '/static/img/shop/tools/feedback.png',
-      title: '意见反馈',
-    },
     {
       url: '/pages/chat/index',
       icon: '/static/img/shop/tools/service.png',

+ 1 - 1
sheep/components/s-notice-block/s-notice-block.vue

@@ -32,7 +32,7 @@
 <style lang="scss" scoped>
   .notice-wrap {
     .icon-img {
-      height: 60rpx;
+      height: 56rpx;
     }
   }
 </style>

+ 0 - 202
sheep/components/s-score-block/s-score-block.vue

@@ -1,202 +0,0 @@
-<template>
-  <view>
-    <view
-      v-if="mode === 1 && state.scoreList.length"
-      class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
-    >
-      <view class="goods-list-box">
-        <view
-          class="left-list"
-          :style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
-          v-for="item in state.leftScoreList"
-          :key="item.id"
-        >
-          <s-score-card
-            class="goods-md-box"
-            size="md"
-            :goodsFields="goodsFields"
-            :data="item"
-            :titleColor="goodsFields.title?.color"
-            :subTitleColor="goodsFields.subtitle.color"
-            :topRadius="data.borderRadiusTop"
-            :bottomRadius="data.borderRadiusBottom"
-            :titleWidth="330 - marginLeft - marginRight"
-            @click="sheep.$router.go('/pages/goods/score', { id: item.id })"
-            @getHeight="mountMasonry($event, 'left')"
-          >
-            <template v-slot:cart>
-              <button class="ss-reset-button cart-btn" :style="[buyStyle]">
-                {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
-              </button>
-            </template>
-          </s-score-card>
-        </view>
-      </view>
-      <view class="goods-list-box">
-        <view
-          class="right-list"
-          :style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
-          v-for="item in state.rightScoreList"
-          :key="item.id"
-        >
-          <s-score-card
-            class="goods-md-box"
-            size="md"
-            :goodsFields="goodsFields"
-            :data="item"
-            :titleColor="goodsFields.title?.color"
-            :subTitleColor="goodsFields.subtitle.color"
-            :topRadius="data.borderRadiusTop"
-            :bottomRadius="data.borderRadiusBottom"
-            :titleWidth="330 - marginLeft - marginRight"
-            @click="sheep.$router.go('/pages/goods/score', { id: item.id })"
-            @getHeight="mountMasonry($event, 'right')"
-          >
-            <template v-slot:cart>
-              <button class="ss-reset-button cart-btn" :style="[buyStyle]">
-                {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
-              </button>
-            </template>
-          </s-score-card>
-        </view>
-      </view>
-
-      <!-- <view class="goods-hack" v-if="state.scoreList.length % 2 == 1" style="width: 345rpx"></view> -->
-    </view>
-    <view v-if="mode === 2 && state.scoreList.length" class="goods-lg-box">
-      <view
-        class="goods-box"
-        :style="[{ marginBottom: data.space + 'px' }]"
-        v-for="item in state.scoreList"
-        :key="item.id"
-      >
-        <s-score-card
-          class="goods-card"
-          size="lg"
-          :goodsFields="goodsFields"
-          :data="item"
-          :titleColor="goodsFields.title?.color"
-          :subTitleColor="goodsFields.subtitle.color"
-          :topRadius="data.borderRadiusTop"
-          :bottomRadius="data.borderRadiusBottom"
-          @tap="sheep.$router.go('/pages/goods/score', { id: item.id })"
-        >
-          <template v-slot:cart>
-            <button class="ss-reset-button cart-btn" :style="[buyStyle]">
-              {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
-            </button>
-          </template>
-        </s-score-card>
-      </view>
-    </view>
-  </view>
-</template>
-<script setup>
-  import { computed, reactive, onMounted } from 'vue';
-  import sheep from '@/sheep';
-
-  const state = reactive({
-    scoreList: [],
-    leftScoreList: [],
-    rightScoreList: [],
-  });
-  const props = defineProps({
-    data: {
-      type: Object,
-      default() {},
-    },
-    styles: {
-      type: Object,
-      default() {},
-    },
-  });
-  const { mode, buyNowStyle, goodsFields, goodsIds } = props.data ?? {};
-  const { marginLeft, marginRight } = props.styles ?? {};
-  async function getScoreListByIds(ids) {
-    let { data } = await sheep.$api.app.scoreShop.ids({ ids });
-    return data;
-  }
-
-  onMounted(async () => {
-    state.scoreList = await getScoreListByIds(goodsIds.join(','));
-    if (mode === 1) {
-      mountMasonry();
-    }
-  });
-
-  // 加载瀑布流
-  let count = 0;
-  let leftHeight = 0;
-  let rightHeight = 0;
-
-  function mountMasonry(height = 0, where = 'left') {
-    if (!state.scoreList[count]) return;
-    if (where === 'left') leftHeight += height;
-    if (where === 'right') rightHeight += height;
-    if (leftHeight <= rightHeight) {
-      state.leftScoreList.push(state.scoreList[count]);
-    } else {
-      state.rightScoreList.push(state.scoreList[count]);
-    }
-    count++;
-  }
-  // 购买按钮样式
-  const buyStyle = computed(() => {
-    if (buyNowStyle.mode == 1) {
-      // button
-      return {
-        background: `linear-gradient(to right, ${buyNowStyle.color1}, ${buyNowStyle.color2})`,
-      };
-    }
-
-    if (buyNowStyle.mode == 2) {
-      // image
-      return {
-        width: '54rpx',
-        height: '54rpx',
-        background: `url(${sheep.$url.cdn(buyNowStyle.src)}) no-repeat`,
-        backgroundSize: '100% 100%',
-      };
-    }
-  });
-</script>
-<style lang="scss" scoped>
-  .goods-md-wrap {
-    width: 100%;
-  }
-
-  .goods-list-box {
-    width: 50%;
-    box-sizing: border-box;
-    .left-list {
-      &:nth-last-child(1) {
-        margin-bottom: 0 !important;
-      }
-    }
-  }
-
-  .goods-box {
-    &:nth-last-of-type(1) {
-      margin-bottom: 0 !important;
-    }
-  }
-
-  .goods-md-box,
-  .goods-sl-box,
-  .goods-lg-box {
-    position: relative;
-
-    .cart-btn {
-      position: absolute;
-      bottom: 10rpx;
-      right: 20rpx;
-      z-index: 11;
-      height: 50rpx;
-      line-height: 50rpx;
-      padding: 0 20rpx;
-      border-radius: 25rpx;
-      font-size: 24rpx;
-      color: #fff;
-    }
-  }
-</style>

+ 0 - 459
sheep/components/s-score-card/s-score-card.vue

@@ -1,459 +0,0 @@
-<template>
-  <view>
-    <!-- md卡片:竖向,一行放两个,图上内容下 -->
-    <view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
-      <image
-        class="md-img-box"
-        :src="sheep.$url.cdn(data.image)"
-        mode="widthFix"
-        @load="calculatePanelHeight"
-      ></image>
-      <view
-        class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
-        :id="elId"
-      >
-        <view
-          v-if="goodsFields.title?.show"
-          class="md-goods-title ss-line-1"
-          :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
-        >
-          {{ data.title }}
-        </view>
-        <view
-          v-if="goodsFields.subtitle?.show"
-          class="md-goods-subtitle ss-m-t-16 ss-line-1"
-          :style="[{ color: subTitleColor }]"
-        >
-          {{ data.subtitle }}
-        </view>
-        <view class="ss-col-bottom">
-          <view
-            v-if="goodsFields.score_price?.show"
-            class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10 ss-flex"
-            :style="[{ color: goodsFields.score_price.color }]"
-          >
-            <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
-            <image
-              :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-              class="score-img"
-            ></image>
-            {{ data.score }}
-          </view>
-
-          <view
-            v-if="goodsFields.price?.show && data.original_price > 0"
-            class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
-            :style="[{ color: goodsFields.price.color }]"
-          >
-            <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-            <view class="ss-m-l-8">{{ data.original_price }}</view>
-          </view>
-        </view>
-
-        <view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
-          <view class="sales-text">{{ salesAndStock }}</view>
-        </view>
-      </view>
-
-      <slot name="cart">
-        <view class="cart-box ss-flex ss-col-center ss-row-center">
-          <image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode=""></image>
-        </view>
-      </slot>
-    </view>
-    <!-- lg卡片:横向型,一行放一个,图片左内容右边  -->
-    <view
-      v-if="size === 'lg'"
-      class="lg-goods-card ss-flex ss-col-stretch"
-      :style="[elStyles]"
-      @tap="onClick"
-    >
-      <image class="lg-img-box" :src="sheep.$url.cdn(data.image)" mode="aspectFill"></image>
-      <view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
-        <view class="ss-m-r-20">
-          <view
-            v-if="goodsFields.title?.show"
-            class="lg-goods-title ss-line-2"
-            :style="[{ color: titleColor }]"
-          >
-            {{ data.title }}
-          </view>
-          <view
-            v-if="goodsFields.subtitle?.show"
-            class="lg-goods-subtitle ss-m-t-10 ss-line-1"
-            :style="[{ color: subTitleColor }]"
-          >
-            {{ data.subtitle }}
-          </view>
-        </view>
-        <view>
-          <view class="ss-m-t-10">
-            <view
-              v-if="goodsFields.score_price?.show"
-              class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
-              :style="[{ color: goodsFields.score_price.color }]"
-            >
-              <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
-              <image
-                :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-                class="score-img"
-              ></image>
-              {{ data.score }}
-            </view>
-            <view
-              v-if="goodsFields.price?.show && data.original_price > 0"
-              class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
-              :style="[{ color: goodsFields.price.color }]"
-            >
-              <text class="price-unit ss-font-20">{{ priceUnit }}</text>
-              <view class="ss-m-l-8">{{ data.original_price }}</view>
-            </view>
-          </view>
-          <view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
-            <view class="sales-text">{{ salesAndStock }}</view>
-          </view>
-        </view>
-      </view>
-
-      <slot name="cart"
-        ><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
-      >
-    </view>
-    <!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
-    <view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" @tap="onClick">
-      <image class="sl-img-box" :src="sheep.$url.cdn(data.image)" mode="aspectFill"></image>
-
-      <view class="sl-goods-content ss-flex-col ss-row-between ss-p-b-20 ss-p-t-20">
-        <view class="ss-m-b-20">
-          <view class="sl-goods-title ss-line-1 ss-p-l-16 ss-p-r-16">
-            {{ data.title }}
-          </view>
-          <view v-if="data.subtitle" class="sl-goods-subtitle ss-p-l-16 ss-p-r-16 ss-m-t-16">
-            {{ data.subtitle }}
-          </view>
-        </view>
-        <view>
-          <slot name="activity">
-            <view
-              v-if="data.promos?.length"
-              class="tag-box ss-flex ss-col-center ss-flex-wrap ss-p-l-16 ss-p-r-16"
-            >
-              <view
-                class="activity-tag ss-m-r-10 ss-m-t-16"
-                v-for="item in data.promos"
-                :key="item.id"
-              >
-                {{ item.title }}
-              </view>
-            </view>
-          </slot>
-          <view class="ss-flex ss-col-bottom ss-p-l-16 ss-p-r-16 font-OPPOSANS">
-            <view class="sl-goods-price ss-m-r-12 ss-flex">
-              <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
-              <image
-                :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
-                class="score-img"
-              ></image>
-              <view>{{ data.score ? data.score : '' }}</view>
-            </view>
-            <view
-              v-if="data.original_price > 0"
-              class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
-            >
-              <text class="price-unit ss-font-20">¥</text>
-              <view class="ss-m-l-8">{{ data.original_price }}</view>
-            </view>
-          </view>
-          <view class="ss-p-l-16 ss-p-r-16 ss-m-t-16 ss-flex ss-flex-wrap">
-            <view class="sales-text">{{ salesAndStock }}</view>
-          </view>
-        </view>
-      </view>
-
-      <slot name="cart"
-        ><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
-      >
-    </view>
-  </view>
-</template>
-<script setup>
-  import { computed, getCurrentInstance } from 'vue';
-  import sheep from '@/sheep';
-  import { formatSales } from '@/sheep/hooks/useGoods';
-  import { formatStock } from '@/sheep/hooks/useGoods';
-  import goodsCollectVue from '@/pages/user/goods-collect.vue';
-  /**
-   * 订单卡片
-   *
-   * @property {String} img 											- 图片
-   * @property {String} title 										- 标题
-   * @property {Number} titleWidth = 0								- 标题宽度,默认0,单位rpx
-   * @property {String} skuText 										- 规格
-   * @property {String | Number} score 								- 积分
-   * @property {String | Number} price 								- 价格
-   * @property {String | Number} originalPrice 						- 单购价
-   * @property {String} priceColor 									- 价格颜色
-   * @property {Number | String} num									- 数量
-   *
-   */
-  const props = defineProps({
-    goodsFields: {
-      type: [Array, Object],
-      default() {
-        return {
-          title: { show: true },
-          subtitle: { show: true },
-          price: { show: true },
-          original_price: { show: true },
-          sales: { show: true },
-          stock: { show: true },
-        };
-      },
-    },
-    tagStyle: {
-      type: Object,
-      default: {},
-    },
-    data: {
-      type: Object,
-      default: {},
-    },
-    size: {
-      type: String,
-      default: 'sl',
-    },
-    background: {
-      type: String,
-      default: '',
-    },
-    topRadius: {
-      type: Number,
-      default: 0,
-    },
-    bottomRadius: {
-      type: Number,
-      default: 0,
-    },
-    titleWidth: {
-      type: Number,
-      default: 0,
-    },
-    titleColor: {
-      type: String,
-      default: '#333',
-    },
-    priceUnit: {
-      type: String,
-      default: '¥',
-    },
-    subTitleColor: {
-      type: String,
-      default: '#999999',
-    },
-  });
-  // 组件样式
-  const elStyles = computed(() => {
-    return {
-      background: props.background,
-      'border-top-left-radius': props.topRadius + 'px',
-      'border-top-right-radius': props.topRadius + 'px',
-      'border-bottom-left-radius': props.bottomRadius + 'px',
-      'border-bottom-right-radius': props.bottomRadius + 'px',
-    };
-  });
-  const emits = defineEmits(['click', 'getHeight']);
-  const onClick = () => {
-    emits('click');
-  };
-  // 格式化销量、库存信息
-  const salesAndStock = computed(() => {
-    let text = [];
-    text.push(formatSales(props.data.sales_show_type, props.data.sales));
-    text.push(formatStock(props.data.stock_show_type, props.data.stock));
-    return text.join(' | ');
-  });
-  // 获取实时卡片高度
-  const { proxy } = getCurrentInstance();
-  const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
-  function calculatePanelHeight(e) {
-    if (props.size === 'md') {
-      const view = uni.createSelectorQuery().in(proxy);
-      view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
-      view.exec((data) => {
-        const goodsPriceCard = data[0];
-        const card = {
-          width: goodsPriceCard.width,
-          height: (goodsPriceCard.width / e.detail.width) * e.detail.height + goodsPriceCard.height,
-        };
-        emits('getHeight', card.height);
-      });
-    }
-  }
-</script>
-
-<style lang="scss" scoped>
-  .price-unit {
-    margin-right: -4px;
-  }
-  .sales-text {
-    display: table;
-    font-size: 24rpx;
-    transform: scale(0.8);
-    margin-left: -16rpx;
-    color: #c4c4c4;
-  }
-
-  // md
-  .md-goods-card {
-    overflow: hidden;
-    width: 100%;
-    position: relative;
-    z-index: 1;
-    background-color: $white;
-    position: relative;
-
-    .md-img-box {
-      width: 100%;
-    }
-
-    .md-goods-title {
-      font-size: 26rpx;
-      color: #333;
-      width: 100%;
-    }
-    .md-goods-subtitle {
-      font-size: 24rpx;
-      font-weight: 400;
-      color: #999999;
-    }
-
-    .md-goods-price {
-      font-size: 30rpx;
-      color: $red;
-      line-height: 36rpx;
-    }
-
-    .cart-box {
-      width: 54rpx;
-      height: 54rpx;
-      background: linear-gradient(90deg, #fe8900, #ff5e00);
-      border-radius: 50%;
-      position: absolute;
-      bottom: 50rpx;
-      right: 20rpx;
-      z-index: 2;
-
-      .cart-icon {
-        width: 30rpx;
-        height: 30rpx;
-      }
-    }
-  }
-
-  // lg
-  .lg-goods-card {
-    overflow: hidden;
-    position: relative;
-    z-index: 1;
-    background-color: $white;
-    height: 280rpx;
-
-    .lg-img-box {
-      width: 280rpx;
-      height: 280rpx;
-      margin-right: 20rpx;
-    }
-
-    .lg-goods-title {
-      font-size: 28rpx;
-      font-weight: 500;
-      color: #333333;
-      // line-height: 36rpx;
-      // width: 410rpx;
-    }
-    .lg-goods-subtitle {
-      font-size: 24rpx;
-      font-weight: 400;
-      color: #999999;
-      line-height: 30rpx;
-      // width: 410rpx;
-    }
-
-    .lg-goods-price {
-      font-size: 30rpx;
-      color: $red;
-      line-height: 36rpx;
-    }
-
-    .buy-box {
-      position: absolute;
-      bottom: 20rpx;
-      right: 20rpx;
-      z-index: 2;
-      width: 120rpx;
-      height: 50rpx;
-      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-      border-radius: 25rpx;
-      font-size: 24rpx;
-      color: #ffffff;
-    }
-    .tag-box {
-      width: 100%;
-    }
-  }
-  .sl-goods-card {
-    overflow: hidden;
-    position: relative;
-    z-index: 1;
-    width: 100%;
-    background-color: $white;
-
-    .sl-img-box {
-      width: 100%;
-      height: 360rpx;
-    }
-
-    .sl-goods-title {
-      font-size: 26rpx;
-      color: #333;
-      width: 100%;
-      box-sizing: border-box;
-    }
-    .sl-goods-subtitle {
-      font-size: 24rpx;
-      font-weight: 400;
-      color: #999999;
-      line-height: 30rpx;
-      width: 100%;
-      box-sizing: border-box;
-    }
-
-    .sl-goods-price {
-      font-size: 30rpx;
-      color: $red;
-    }
-
-    .buy-box {
-      position: absolute;
-      bottom: 20rpx;
-      right: 20rpx;
-      z-index: 2;
-      width: 148rpx;
-      height: 50rpx;
-      background: linear-gradient(90deg, #fe8900, #ff5e00);
-      border-radius: 25rpx;
-      font-size: 24rpx;
-      color: #ffffff;
-    }
-  }
-  .goods-origin-price {
-    font-size: 20rpx;
-    color: #c4c4c4;
-    text-decoration: line-through;
-  }
-  .score-img {
-    width: 36rpx;
-    height: 36rpx;
-    margin: 0 4rpx;
-  }
-</style>

+ 1 - 1
sheep/components/s-statusbar/s-statusbar.vue

@@ -1,5 +1,5 @@
 <template>
-  <view class="status_bar"></view>
+  <view class="status_bar" />
 </template>
 
 <style>

+ 22 - 36
sheep/components/s-title-block/s-title-block.vue

@@ -2,36 +2,29 @@
 <template>
   <view
     class="ss-title-wrap ss-flex ss-col-center"
-    :class="[state.typeMap[data.location]]"
+    :class="[state.typeMap[data.textAlign]]"
     :style="[elStyles]"
   >
     <view class="title-content">
-      <view v-if="data.title.text" class="title-text" :style="[titleStyles]">{{
-        data.title.text
-      }}</view>
-      <view v-if="data.subtitle.text" :style="[subtitleStyles]" class="sub-title-text">
-        {{ data.subtitle.text }}
-      </view>
+      <!-- 主标题 -->
+      <view v-if="data.title" class="title-text" :style="[titleStyles]">{{ data.title }}</view>
+      <!-- 副标题 -->
+      <view v-if="data.description" :style="[descStyles]" class="sub-title-text">{{ data.description }}</view>
     </view>
-    <view v-if="data.more.show" class="more-box ss-flex ss-col-center" @tap="sheep.$router.go(data.more.url)">
-      <view class="more-text">查看更多</view>
-      <text class="_icon-forward"></text>
+    <!-- 查看更多 -->
+    <view v-if="data.more?.show" class="more-box ss-flex ss-col-center" @tap="sheep.$router.go(data.more.url)"
+          :style="{color: data.descriptionColor}">
+      <view class="more-text" v-if="data.more.type !== 'icon'">{{ data.more.text }} </view>
+      <text class="_icon-forward" v-if="data.more.type !== 'text'"></text>
     </view>
   </view>
 </template>
 
 <script setup>
   /**
-   *
    * 标题栏
-   *
-   * @property {String} title 				- 标题
-   * @property {String} subTitle 				- 副标题
-   * @property {Number} height 				- 高度
-   * @property {String} Type = [left | right | center | between]					- 样式
-   *
    */
-  import { computed, reactive } from 'vue';
+  import { reactive } from 'vue';
   import sheep from '@/sheep';
 
   // 数据
@@ -52,35 +45,28 @@
       type: Object,
       default() {},
     },
-    height: {
-      type: Number,
-      default: 100,
-    },
   });
 
   // 组件样式
   const elStyles = {
-    background: `url(${sheep.$url.cdn(props.data.src)}) no-repeat top center / 100% auto`,
-    height: props.styles.height + 'px',
-    fontStyle: props.data.title.other.includes('italic') ? 'italic' : 'normal',
-    fontWeight: props.data.title.other.includes('bold') ? 'bold' : '',
+    background: `url(${sheep.$url.cdn(props.data.bgImgUrl)}) no-repeat top center / 100% auto`,
+    fontSize: `${props.data.titleSize}px`,
+    fontWeight: `${props.data.titleWeight}px`,
   };
 
   // 标题样式
   const titleStyles = {
-    color: props.data.title.color,
-    fontSize: props.data.title.textFontSize + 'px',
-    textAlign: props.data.location,
-    marginLeft: props.data.skew + 'px',
+    color: props.data.titleColor,
+    fontSize: `${props.data.titleSize}px`,
+    textAlign: props.data.textAlign
   };
 
   // 副标题
-  const subtitleStyles = {
-    color: props.data.subtitle.color,
-    fontSize: props.data.subtitle.textFontSize + 'px',
-    textAlign: props.data.location,
-    fontStyle: props.data.subtitle.other.includes('italic') ? 'italic' : 'normal',
-    fontWeight: props.data.subtitle.other.includes('bold') ? 'bold' : '',
+  const descStyles = {
+    color: props.data.descriptionColor,
+    textAlign: props.data.textAlign,
+    fontSize: `${props.data.descriptionSize}px`,
+    fontWeight: `${props.data.descriptionWeight}px`,
   };
 </script>
 

+ 178 - 20
sheep/hooks/useGoods.js

@@ -1,6 +1,7 @@
 import { ref } from 'vue';
 import dayjs from 'dayjs';
 import $url from '@/sheep/url';
+import { formatDate } from '@/sheep/util';
 
 /**
  * 格式化销量
@@ -9,7 +10,7 @@ import $url from '@/sheep/url';
  * @return {string} 格式化后的销量字符串
  */
 export function formatSales(type, num) {
-  let prefix = type!=='exact' && num<10 ? '销量': '已售';
+  let prefix = type !== 'exact' && num < 10 ? '销量' : '已售';
   return formatNum(prefix, type, num)
 }
 
@@ -65,6 +66,7 @@ export function formatPrice(e) {
 
 // 视频格式后缀列表
 const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
+
 /**
  * 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接
  *
@@ -74,7 +76,7 @@ const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
 export function formatGoodsSwiper(urlList) {
   return urlList.map((url, key) => {
     const isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
-    const type = isVideo ? 'video' :'image'
+    const type = isVideo ? 'video' : 'image'
     const src = $url.cdn(url);
     return { type, src }
   });
@@ -82,26 +84,182 @@ export function formatGoodsSwiper(urlList) {
 
 /**
  * 格式化订单状态的颜色
- * @param type 订单类型
+ *
+ * @param order 订单
  * @return {string} 颜色的 class 名称
  */
-export function formatOrderColor(type) {
-  switch (type) {
-    case 'apply_refund':
-    case 'groupon_ing':
-    case 'nocomment':
-    case 'noget':
-    case 'nosend':
-      return 'warning-color';
-    case 'closed':
-    case 'groupon_invalid':
-    case 'cancel':
-    case 'refund_agree':
-      return 'danger-color';
-    case 'completed':
-      return 'success-color';
-    case 'unpaid':
-      return 'info-color';
+export function formatOrderColor(order) {
+  if (order.status === 0) {
+    return 'info-color';
+  }
+  if (order.status === 10
+    || order.status === 20
+    || (order.status === 30 && !order.commentStatus)) {
+    return 'warning-color';
+  }
+  if (order.status === 30 && order.commentStatus) {
+    return 'success-color';
+  }
+  return 'danger-color';
+}
+
+/**
+ * 格式化订单状态
+ *
+ * @param order 订单
+ */
+export function formatOrderStatus(order) {
+  if (order.status === 0) {
+    return '待付款';
+  }
+  if (order.status === 10 && order.deliveryType === 1) {
+    return '待发货';
+  }
+  if (order.status === 10 && order.deliveryType === 2) {
+    return '待核销';
+  }
+  if (order.status === 20) {
+    return '待收货';
+  }
+  if (order.status === 30 && !order.commentStatus) {
+    return '待评价';
+  }
+  if (order.status === 30 && order.commentStatus) {
+    return '已完成';
+  }
+  return '已关闭';
+}
+
+/**
+ * 格式化订单状态的描述
+ *
+ * @param order 订单
+ */
+export function formatOrderStatusDescription(order) {
+  if (order.status === 0) {
+    return `请在 ${ formatDate(orderInfo.payExpireTime) } 前完成支付`;
+  }
+  if (order.status === 10) {
+    return '商家未发货,请耐心等待';
+  }
+  if (order.status === 20) {
+    return '商家已发货,请耐心等待';
+  }
+  if (order.status === 30 && !order.commentStatus) {
+    return '已收货,快去评价一下吧';
+  }
+  if (order.status === 30 && order.commentStatus) {
+    return '交易完成,感谢您的支持';
+  }
+  return '交易关闭';
+}
+
+/**
+ * 处理订单的 button 操作按钮数组
+ *
+ * @param order 订单
+ */
+export function handleOrderButtons(order) {
+  order.buttons = []
+  if (order.type === 3) { // 查看拼团
+    order.buttons.push('combination');
+  }
+  if (order.status === 20) { // 确认收货
+    order.buttons.push('confirm');
+  }
+  if (order.logisticsId > 0) { // 查看物流
+    order.buttons.push('express');
+  }
+  if (order.status === 0) { // 取消订单 / 发起支付
+    order.buttons.push('cancel');
+    order.buttons.push('pay');
+  }
+  if (order.status === 30 && !order.commentStatus) { // 发起评价
+    order.buttons.push('comment');
+  }
+  if (order.status === 40) { // 删除订单
+    order.buttons.push('delete');
+  }
+}
+
+/**
+ * 格式化售后状态
+ *
+ * @param afterSale 售后
+ */
+export function formatAfterSaleStatus(afterSale) {
+  if (afterSale.status === 10) {
+    return '申请售后';
+  }
+  if (afterSale.status === 20) {
+    return '商品待退货';
+  }
+  if (afterSale.status === 30) {
+    return '商家待收货';
+  }
+  if (afterSale.status === 40) {
+    return '等待退款';
+  }
+  if (afterSale.status === 50) {
+    return '退款成功';
+  }
+  if (afterSale.status === 61) {
+    return '买家取消';
+  }
+  if (afterSale.status === 62) {
+    return '商家拒绝';
+  }
+  if (afterSale.status === 63) {
+    return '商家拒收货';
+  }
+  return '未知状态';
+}
+
+/**
+ * 格式化售后状态的描述
+ *
+ * @param afterSale 售后
+ */
+export function formatAfterSaleStatusDescription(afterSale) {
+  if (afterSale.status === 10) {
+    return '退款申请待商家处理';
+  }
+  if (afterSale.status === 20) {
+    return '请退货并填写物流信息';
+  }
+  if (afterSale.status === 30) {
+    return '退货退款申请待商家处理';
+  }
+  if (afterSale.status === 40) {
+    return '等待退款';
+  }
+  if (afterSale.status === 50) {
+    return '退款成功';
+  }
+  if (afterSale.status === 61) {
+    return '退款关闭';
+  }
+  if (afterSale.status === 62) {
+    return `商家不同意退款申请,拒绝原因:${afterSale.auditReason}`;
+  }
+  if (afterSale.status === 63) {
+    return `商家拒绝收货,不同意退款,拒绝原因:${afterSale.auditReason}`;
+  }
+  return '未知状态';
+}
+
+/**
+ * 处理售后的 button 操作按钮数组
+ *
+ * @param afterSale 售后
+ */
+export function handleAfterSaleButtons(afterSale) {
+  afterSale.buttons = [];
+  if ([10, 20, 30].includes(afterSale.status)) { // 取消订单
+    afterSale.buttons.push('cancel');
+  }
+  if (afterSale.status === 20) { // 退货信息
+    afterSale.buttons.push('delivery');
   }
 }
 

+ 18 - 9
sheep/hooks/useModal.js

@@ -6,7 +6,7 @@ import test from '@/sheep/helper/test.js';
 import $api from '@/sheep/api';
 
 // 打开授权弹框
-export function showAuthModal(type = 'accountLogin') {
+export function showAuthModal(type = 'smsLogin') {
   const modal = $store('modal');
   if (modal.auth !== '') {
     closeAuthModal();
@@ -58,10 +58,9 @@ export function closeMenuTools() {
 }
 
 // 发送短信验证码  60秒
-export function getSmsCode(event, mobile = '') {
+export function getSmsCode(event, mobile) {
   const modalStore = $store('modal');
   const lastSendTimer = modalStore.lastTimer[event];
-
   if (typeof lastSendTimer === 'undefined') {
     $helper.toast('短信发送事件错误');
     return;
@@ -69,26 +68,36 @@ export function getSmsCode(event, mobile = '') {
 
   const duration = dayjs().unix() - lastSendTimer;
   const canSend = duration >= 60;
-
   if (!canSend) {
     $helper.toast('请稍后再试');
     return;
   }
-
-  if (!test.mobile(mobile)) {
+  // 只有 mobile 非空时才校验。因为部分场景(修改密码),不需要输入手机
+  if (mobile && !test.mobile(mobile)) {
     $helper.toast('手机号码格式不正确');
     return;
   }
 
   // 发送验证码 + 更新上次发送验证码时间
-  $api.app.sendSms({ mobile, event }).then((res) => {
-    if (res.error === 0) {
+  let scene = -1;
+  switch (event) {
+    case 'resetPassword':
+      scene = 4;
+      break;
+    case 'changePassword':
+      scene = 3;
+      break;
+    case 'smsLogin':
+      scene = 1;
+      break;
+  }
+  $api.AuthUtil.sendSmsCode(mobile, scene).then((res) => {
+    if (res.code === 0) {
       modalStore.$patch((state) => {
         state.lastTimer[event] = dayjs().unix();
       });
     }
   });
-
 }
 
 // 获取短信验证码倒计时 -- 60秒

+ 99 - 33
sheep/platform/pay.js

@@ -3,19 +3,20 @@ import sheep from '@/sheep';
 import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
 // #endif
 import { getRootUrl } from '@/sheep/helper';
+import PayOrderApi from '@/sheep/api/pay/order';
 
 /**
  * 支付
  *
  * @param {String} payment = ['wechat','alipay','wallet','offline']  	- 支付方式
  * @param {String} orderType = ['goods','recharge','groupon']  	- 订单类型
- * @param {String} orderSN					- 订单号
+ * @param {String} id					- 订单号
  */
 
 export default class SheepPay {
-  constructor(payment, orderType, orderSN) {
+  constructor(payment, orderType, id) {
     this.payment = payment;
-    this.orderSN = orderSN;
+    this.id = id;
     this.orderType = orderType;
     this.payAction();
   }
@@ -29,8 +30,8 @@ export default class SheepPay {
         alipay: () => {
           this.redirectPay(); // 现在公众号可以直接跳转支付宝页面
         },
-        money: () => {
-          this.moneyPay();
+        wallet: () => {
+          this.walletPay();
         },
         offline: () => {
           this.offlinePay();
@@ -43,8 +44,8 @@ export default class SheepPay {
         alipay: () => {
           this.copyPayLink();
         },
-        money: () => {
-          this.moneyPay();
+        wallet: () => {
+          this.walletPay();
         },
         offline: () => {
           this.offlinePay();
@@ -57,8 +58,8 @@ export default class SheepPay {
         alipay: () => {
           this.alipay();
         },
-        money: () => {
-          this.moneyPay();
+        wallet: () => {
+          this.walletPay();
         },
         offline: () => {
           this.offlinePay();
@@ -71,8 +72,8 @@ export default class SheepPay {
         alipay: () => {
           this.redirectPay();
         },
-        money: () => {
-          this.moneyPay();
+        wallet: () => {
+          this.walletPay();
         },
         offline: () => {
           this.offlinePay();
@@ -83,18 +84,21 @@ export default class SheepPay {
   }
 
   // 预支付
-  prepay() {
+  prepay(channel) {
     return new Promise((resolve, reject) => {
       let data = {
-        order_sn: this.orderSN,
-        payment: this.payment,
+        id: this.id,
+        channelCode: channel,
+        channelExtras: {}
       };
       if (uni.getStorageSync('openid')) {
         data.openid = uni.getStorageSync('openid');
       }
-      sheep.$api.pay.prepay(data).then((res) => {
-        res.error === 0 && resolve(res);
-        if (res.error === -1 && res.msg === 'miss_openid') {
+      PayOrderApi.submitOrder(data).then((res) => {
+        // 成功时
+        res.code === 0 && resolve(res);
+        // 失败时
+        if (res.code !== 0 && res.msg === 'miss_openid') {
           uni.showModal({
             title: '微信支付',
             content: '请先绑定微信再使用微信支付',
@@ -109,7 +113,7 @@ export default class SheepPay {
     });
   }
   // #ifdef H5
-  // 微信公众号JSSDK支付
+  // 微信公众号JSSDK支付 TODO 芋艿:待接入
   async wechatOfficialAccountPay() {
     let that = this;
     let { error, data, msg } = await this.prepay();
@@ -130,21 +134,21 @@ export default class SheepPay {
     });
   }
 
-  //浏览器微信H5支付
+  //浏览器微信H5支付 TODO 芋艿:待接入
   async wechatWapPay() {
     const { error, data } = await this.prepay();
     if (error === 0) {
-      const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+      const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${this.payment
         }&orderType=${this.orderType}`;
       location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
     }
   }
 
-  // 支付链接
+  // 支付链接  TODO 芋艿:待接入
   async redirectPay() {
     let { error, data } = await this.prepay();
     if (error === 0) {
-      const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+      const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${this.payment
         }&orderType=${this.orderType}`;
       location.href = data.pay_data + encodeURIComponent(redirect_url);
     }
@@ -152,7 +156,7 @@ export default class SheepPay {
 
   // #endif
 
-  // 微信小程序支付
+  // 微信小程序支付  TODO 芋艿:待接入
   async wechatMiniProgramPay() {
     let that = this;
     let result = await this.prepay();
@@ -173,18 +177,18 @@ export default class SheepPay {
   }
 
   // 余额支付
-  async moneyPay() {
-    const { error } = await this.prepay();
-    error === 0 && this.payResult('success');
+  async walletPay() {
+    const { code } = await this.prepay('wallet');
+    code === 0 && this.payResult('success');
   }
 
-  // 货到付款
+  // 货到付款  TODO 芋艿:待接入
   async offlinePay() {
     const { error } = await this.prepay();
     error === 0 && this.payResult('success');
   }
 
-  // 支付宝复制链接支付
+  // 支付宝复制链接支付  TODO 芋艿:待接入
   async copyPayLink() {
     let that = this;
     let { error, data } = await this.prepay();
@@ -203,7 +207,7 @@ export default class SheepPay {
     }
   }
 
-  // 支付宝支付
+  // 支付宝支付  TODO 芋艿:待接入
   async alipay() {
     let that = this;
     const { error, data } = await this.prepay();
@@ -225,7 +229,7 @@ export default class SheepPay {
     }
   }
 
-  // 微信支付
+  // 微信支付  TODO 芋艿:待接入
   async wechatAppPay() {
     let that = this;
     let { error, data } = await this.prepay();
@@ -246,10 +250,72 @@ export default class SheepPay {
   // 支付结果跳转,success:成功,fail:失败
   payResult(resultType) {
     sheep.$router.redirect('/pages/pay/result', {
-      orderSN: this.orderSN,
-      payment: this.payment, //重新支付的时候使用
-      payState: resultType,
+      id: this.id,
       orderType: this.orderType,
+      payState: resultType
     });
   }
 }
+
+export function getPayMethods(channels) {
+  const payMethods = [
+    {
+      icon: '/static/img/shop/pay/wechat.png',
+      title: '微信支付',
+      value: 'wechat',
+      disabled: true,
+    },
+    {
+      icon: '/static/img/shop/pay/alipay.png',
+      title: '支付宝支付',
+      value: 'alipay',
+      disabled: true,
+    },
+    {
+      icon: '/static/img/shop/pay/wallet.png',
+      title: '余额支付',
+      value: 'wallet',
+      disabled: true,
+    },
+    {
+      icon: '/static/img/shop/pay/apple.png',
+      title: 'Apple Pay',
+      value: 'apple',
+      disabled: true,
+    },
+    {
+      icon: '/static/img/shop/pay/wallet.png',
+      title: '模拟支付',
+      value: 'mock',
+      disabled: true,
+    }
+  ];
+  const platform = sheep.$platform.name
+
+  // 1. 处理【微信支付】
+  const wechatMethod = payMethods[0];
+  if ((platform === 'WechatOfficialAccount' && channels.includes('wx_pub'))
+    || platform === 'WechatMiniProgram' && channels.includes('wx_lite')
+    || platform === 'App' && channels.includes('wx_app')) {
+    wechatMethod.disabled = false;
+  }
+  // 2. 处理【支付宝支付】
+  const alipayMethod = payMethods[1];
+  if ((platform === 'WechatOfficialAccount' && channels.includes('alipay_wap'))
+    || platform === 'WechatMiniProgram' && channels.includes('alipay_wap')
+    || platform === 'App' && channels.includes('alipay_app')) {
+    alipayMethod.disabled = false;
+  }
+  // 3. 处理【余额支付】
+  const walletMethod = payMethods[2];
+  if (channels.includes('wallet')) {
+    walletMethod.disabled = false;
+  }
+  // 4. 处理【苹果支付】TODO 芋艿:未来接入
+  // 5. 处理【模拟支付】
+  const mockMethod = payMethods[4];
+  if (channels.includes('mock')) {
+    mockMethod.disabled = false;
+  }
+  return payMethods;
+}

+ 8 - 5
sheep/request/index.js

@@ -94,6 +94,7 @@ http.interceptors.request.use(
 		if (config.url.indexOf('/app-api/') !== -1) {
 			config.header['Accept'] = '*/*'
 			config.header['tenant-id'] = '1';
+      config.header['terminal'] = '20';
 			config.header['Authorization'] = 'Bearer test247';
 		}
 		return config;
@@ -112,9 +113,10 @@ http.interceptors.response.use(
 		if (response.header.authorization || response.header.Authorization) {
 			$store('user').setToken(response.header.authorization || response.header.Authorization);
 		}
+    // TODO 芋艿:如果是登录的 API,则自动设置 token
 
 		response.config.custom.showLoading && closeLoading();
-		if (response.data.error !== 0) {
+		if (response.data.error !== 0 && response.data.code !== 0) {
 			if (response.config.custom.showError)
 				uni.showToast({
 					title: response.data.msg || '服务器开小差啦,请稍后再试~',
@@ -123,9 +125,10 @@ http.interceptors.response.use(
 				});
 			return Promise.resolve(response.data);
 		}
+    // 成功时的提示
 		if (
-			response.data.error === 0 &&
-			response.data.msg !== '' &&
+			(response.data.error === 0 || response.data.code === 0) &&
+      ( response.data.msg !== '' || response.config.custom.successMsg !== '' ) &&
 			response.config.custom.showSuccess
 		) {
 			uni.showToast({
@@ -212,8 +215,8 @@ const request = (config) => {
 	}
 	// TODO 芋艿:额外拼接
 	if (config.url.indexOf('/app-api/') >= 0) {
-		config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
-		// config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+		// config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+		config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
 	}
 	return http.middleware(config);
 };

+ 4 - 3
sheep/request2/index.js

@@ -95,7 +95,8 @@ http.interceptors.request.use(
 		if (config.url.indexOf('/app-api/') !== -1) {
 			config.header['Accept'] = '*/*'
 			config.header['tenant-id'] = '1';
-			config.header['Authorization'] = 'Bearer test247';
+      config.header['terminal'] = '20';
+      config.header['Authorization'] = 'Bearer test247';
 		}
 		return config;
 	},
@@ -214,9 +215,9 @@ const request = (config) => {
 	// TODO 芋艿:额外拼接
 	if (config.url.indexOf('/app-api/') >= 0) {
 		// 设置接口地址
-		config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+		// config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
 		// config.url = 'https://app.test.huizhizao.vip/prod-api' + config.url; // 调用【云端】
-		// config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+		config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
 	}
 	return http.middleware(config);
 };

+ 48 - 53
sheep/store/cart.js

@@ -1,98 +1,93 @@
 import { defineStore } from 'pinia';
-import cartApi from '@/sheep/api/cart';
+import CartApi from '@/sheep/api/trade/cart';
 
 const cart = defineStore({
   id: 'cart',
   state: () => ({
     list: [], // 购物车列表
     selectedIds: [], // 已选列表
-    isAllSelected: false, //是否全选
-    cartSelectedTotalPrice: '0.00', // 选中项总金额
+    isAllSelected: false, // 是否全选
+    totalPriceSelected: 0, // 选中项总金额
   }),
-  getters: {
-    totalPriceSelected: (state) => {
-      let price = 0;
-      if (!state.selectedIds.length) return price.toFixed(2);
-      state.list.forEach((item) => {
-        price += state.selectedIds.includes(item.id)
-          ? Number(item.sku.price/100) * item.count
-          : 0;
-      });
-      return price.toFixed(2);
-    },
-  },
   actions: {
     // 获取购物车列表
     async getList() {
-      const { data, code } = await cartApi.list();
+      const { data, code } = await CartApi.getCartList();
       if (code === 0) {
         this.list = data.validList;
+
+        // 计算各种关联属性
+        this.selectedIds = [];
+        this.isAllSelected = true;
+        this.totalPriceSelected = 0;
+        this.list.forEach((item) => {
+          if (item.selected) {
+            this.selectedIds.push(item.id);
+            this.totalPriceSelected += item.count * item.sku.price;
+          } else {
+            this.isAllSelected = false;
+          }
+        });
       }
     },
+
     // 添加购物车
     async add(goodsInfo) {
-      const { error } = await cartApi.append({
-        goods_id: goodsInfo.goods_id,
-        goods_num: goodsInfo.goods_num,
-        goods_sku_price_id: goodsInfo.id,
+      // 添加购物项
+      const { code } = await CartApi.addCart({
+        skuId: goodsInfo.id,
+        count: goodsInfo.goods_num,
       });
-      if (error === 0) {
-        this.getList();
+      // 刷新购物车列表
+      if (code === 0) {
+        await this.getList();
       }
     },
 
     // 更新购物车
     async update(goodsInfo) {
-      const { error } = await cartApi.update({
+      const { code } = await CartApi.updateCartCount({
         id: goodsInfo.goods_id,
         count: goodsInfo.goods_num,
-        goods_sku_price_id: goodsInfo.goods_sku_price_id,
       });
-      if (error === 0) {
-        // this.getList();
+      if (code === 0) {
+        await this.getList();
       }
     },
 
     // 移除购物车
     async delete(ids) {
-      if (typeof ids === 'array') {
-        ids = ids.join(',');
-      }
-      const { code } = await cartApi.delete(ids);
+      const { code } = await CartApi.deleteCart(ids.join(','));
       if (code === 0) {
-        this.selectAll(false);
-        this.getList();
+        await this.getList();
       }
     },
 
-    // 选择购物车商品
-    selectSingle(goodsId) {
-      if (!this.selectedIds.includes(goodsId)) {
-        this.selectedIds.push(goodsId);
-      } else {
-        this.selectedIds.splice(this.selectedIds.indexOf(goodsId), 1);
+    // 单选购物车商品
+    async selectSingle(goodsId) {
+      const { code } = await CartApi.updateCartSelected({
+        ids: [goodsId],
+        selected: !this.selectedIds.includes(goodsId), // 取反
+      });
+      if (code === 0) {
+        await this.getList();
       }
-      this.isAllSelected = this.selectedIds.length === this.list.length;
     },
 
-    // 全选
-    selectAll(flag) {
-      this.isAllSelected = flag;
-      if (!flag) {
-        this.selectedIds = [];
-      } else {
-        this.list.forEach((item) => {
-          this.selectedIds.push(item.id);
-        });
+    // 全选购物车商品
+    async selectAll(flag) {
+      const { code } = await CartApi.updateCartSelected({
+        ids: this.list.map((item) => item.id),
+        selected: flag
+      });
+      if (code === 0) {
+        await this.getList();
       }
     },
 
     // 清空购物车
-    emptyList() {
-      this.list = [];
-      this.selectedIds = [];
-      this.isAllSelected = false;
-      this.cartSelectedTotalPrice = '0.00';
+    async emptyList() {
+      await this.delete(this.list.map((item) => item.id));
     },
   },
   persist: {

+ 2 - 1
sheep/store/modal.js

@@ -13,10 +13,11 @@ const modal = defineStore({
       smsRegister: 0,
       changeMobile: 0,
       resetPassword: 0,
+      changePassword: 0,
     }
   }),
   persist: {
-    enabled: true, 
+    enabled: true,
     strategies: [
       {
         key: 'modal-store',

+ 24 - 15
sheep/store/user.js

@@ -14,6 +14,7 @@ import app from './app';
 import {
 	showAuthModal
 } from '@/sheep/hooks/useModal';
+import AuthUtil from '@/sheep/api/member/auth';
 
 // 默认用户信息
 const defaultUserInfo = {
@@ -51,6 +52,7 @@ const user = defineStore({
 
 	actions: {
 		// 获取个人信息
+    // TODO 芋艿:整理下;
 		async getInfo() {
 			const {
 				code,
@@ -58,20 +60,21 @@ const user = defineStore({
 			} = await userApi.profile();
 
 			// 为了兼容 获取用户余额 可能还会用到其他参数
-			// 优惠券数量,积分数量 应该在这里	
+			// 优惠券数量,积分数量 应该在这里
 			const {
 				code: code2,
 				data: data2
 			} = await userApi.balance();
 			if (code !== 0 || code2 != 0) return;
-			data.money = data2.balance / 100;
+			data.money = data2.balance;
 			this.userInfo = data;
 			console.log(data2, '信息')
 			return Promise.resolve(data);
 		},
 
 		// 获取分销商信息
-		async getAgentInfo() {
+    // TODO 芋艿:整理下;
+    async getAgentInfo() {
 			const res = await commissionApi.agent();
 			if (res.error === 0) {
 				this.agentInfo = res.data;
@@ -80,7 +83,8 @@ const user = defineStore({
 		},
 
 		// 获取订单、优惠券等其他资产信息
-		async getNumData() {
+    // TODO 芋艿:整理下;
+    async getNumData() {
 			const {
 				code,
 				data
@@ -103,7 +107,8 @@ const user = defineStore({
 		},
 
 		// 添加分享记录
-		async addShareLog(params) {
+    // TODO 芋艿:整理下;
+    async addShareLog(params) {
 			const {
 				error
 			} = await userApi.addShareLog(params);
@@ -111,7 +116,8 @@ const user = defineStore({
 		},
 
 		// 设置token
-		setToken(token = '') {
+    // TODO 芋艿:整理下;
+    setToken(token = '') {
 			if (token === '') {
 				this.isLogin = false;
 				uni.removeStorageSync('token');
@@ -124,7 +130,8 @@ const user = defineStore({
 		},
 
 		// 更新用户相关信息 (手动限流 5秒之内不刷新)
-		async updateUserData() {
+    // TODO 芋艿:整理下;
+    async updateUserData() {
 			if (!this.isLogin) {
 				this.resetUserData();
 				return;
@@ -138,7 +145,8 @@ const user = defineStore({
 		},
 
 		// 重置用户默认数据
-		resetUserData() {
+    // TODO 芋艿:整理下;
+    resetUserData() {
 			this.setToken();
 			this.userInfo = clone(defaultUserInfo);
 			this.numData = cloneDeep(defaultNumData);
@@ -147,7 +155,8 @@ const user = defineStore({
 		},
 
 		// 登录后
-		async loginAfter() {
+    // TODO 芋艿:整理下;
+    async loginAfter() {
 			await this.updateUserData();
 			cart().getList();
 			// 登录后设置全局分享参数
@@ -158,7 +167,8 @@ const user = defineStore({
 			// }
 
 			// 添加分享记录
-			const shareLog = uni.getStorageSync('shareLog');
+      // TODO 芋艿:整理下;
+      const shareLog = uni.getStorageSync('shareLog');
 			if (!isEmpty(shareLog)) {
 				this.addShareLog({
 					...shareLog,
@@ -167,12 +177,11 @@ const user = defineStore({
 		},
 
 		// 登出
-		async logout(force = false) {
+    // TODO 芋艿:整理下;
+    async logout(force = false) {
 			if (!force) {
-				const {
-					error
-				} = await userApi.logout();
-				if (error === 0) {
+				const { code } = AuthUtil.logout();
+				if (code === 0) {
 					this.resetUserData();
 				}
 			}

+ 2 - 1
sheep/ui/su-region-picker/su-region-picker.vue

@@ -1,3 +1,4 @@
+<!-- 省市区选择弹窗 -->
 <template>
   <su-popup :show="show" @close="onCancel" round="20">
     <view class="ui-region-picker">
@@ -9,7 +10,7 @@
         title="选择区域"
         @cancel="onCancel"
         @confirm="onConfirm('confirm')"
-      ></su-toolbar>
+      />
       <view class="ui-picker-body">
         <picker-view
           :value="state.currentIndex"

Vissa filer visades inte eftersom för många filer har ändrats