diff --git a/.pages-backup/pages.consumer.2026-05-21T04-00-37-252Z.json b/.pages-backup/pages.consumer.2026-05-21T04-00-37-252Z.json
new file mode 100644
index 00000000..60afb4ad
--- /dev/null
+++ b/.pages-backup/pages.consumer.2026-05-21T04-00-37-252Z.json
@@ -0,0 +1,461 @@
+{
+ "pages": [
+ {
+ "path": "pages/main/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "pages/user/boot",
+ "style": {
+ "navigationBarTitleText": ""
+ }
+ },
+ {
+ "path": "pages/user/login",
+ "style": {
+ "navigationBarTitleText": "用户登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/user/register",
+ "style": {
+ "navigationBarTitleText": "注册"
+ }
+ },
+ {
+ "path": "pages/user/forgot-password",
+ "style": {
+ "navigationBarTitleText": "忘记密码"
+ }
+ },
+ {
+ "path": "pages/user/terms",
+ "style": {
+ "navigationBarTitleText": "用户协议与隐私政策"
+ }
+ },
+ {
+ "path": "pages/user/center",
+ "style": {
+ "navigationBarTitleText": "用户中心"
+ }
+ },
+ {
+ "path": "pages/user/profile",
+ "style": {
+ "navigationBarTitleText": "个人资料"
+ }
+ },
+ {
+ "path": "pages/user/change-password",
+ "style": {
+ "navigationBarTitleText": "修改密码"
+ }
+ },
+ {
+ "path": "pages/user/bind-phone",
+ "style": {
+ "navigationBarTitleText": "绑定手机"
+ }
+ },
+ {
+ "path": "pages/user/bind-email",
+ "style": {
+ "navigationBarTitleText": "绑定邮箱"
+ }
+ },
+ {
+ "path": "pages/main/messages",
+ "style": {
+ "navigationBarTitleText": "消息",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/main/cart",
+ "style": {
+ "navigationBarTitleText": "购物车",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/profile",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/category",
+ "style": {
+ "navigationBarTitleText": "分类",
+ "navigationStyle": "custom"
+ }
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "pages/mall/consumer",
+ "pages": [
+ {
+ "path": "settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "edit-profile",
+ "style": {
+ "navigationBarTitleText": "编辑资料"
+ }
+ },
+ {
+ "path": "wallet",
+ "style": {
+ "navigationBarTitleText": "我的钱包"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "余额提现"
+ }
+ },
+ {
+ "path": "search",
+ "style": {
+ "navigationBarTitleText": "搜索",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-detail",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "channel-detail",
+ "style": {
+ "navigationBarTitleText": "频道详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "shop-detail",
+ "style": {
+ "navigationBarTitleText": "店铺详情"
+ }
+ },
+ {
+ "path": "coupons",
+ "style": {
+ "navigationBarTitleText": "我的优惠券"
+ }
+ },
+ {
+ "path": "favorites",
+ "style": {
+ "navigationBarTitleText": "我的收藏"
+ }
+ },
+ {
+ "path": "footprint",
+ "style": {
+ "navigationBarTitleText": "我的足迹"
+ }
+ },
+ {
+ "path": "address",
+ "style": {
+ "navigationBarTitleText": "地址"
+ }
+ },
+ {
+ "path": "address-list",
+ "style": {
+ "navigationBarTitleText": "收货地址"
+ }
+ },
+ {
+ "path": "address-edit",
+ "style": {
+ "navigationBarTitleText": "编辑地址"
+ }
+ },
+ {
+ "path": "checkout",
+ "style": {
+ "navigationBarTitleText": "确认订单"
+ }
+ },
+ {
+ "path": "payment",
+ "style": {
+ "navigationBarTitleText": "收银台"
+ }
+ },
+ {
+ "path": "payment-success",
+ "style": {
+ "navigationBarTitleText": "支付成功",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "orders",
+ "style": {
+ "navigationBarTitleText": "我的订单",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "logistics",
+ "style": {
+ "navigationBarTitleText": "物流详情"
+ }
+ },
+ {
+ "path": "review",
+ "style": {
+ "navigationBarTitleText": "评价晒单"
+ }
+ },
+ {
+ "path": "refund",
+ "style": {
+ "navigationBarTitleText": "退款/售后"
+ }
+ },
+ {
+ "path": "apply-refund",
+ "style": {
+ "navigationBarTitleText": "申请售后"
+ }
+ },
+ {
+ "path": "refund-review",
+ "style": {
+ "navigationBarTitleText": "服务评价"
+ }
+ },
+ {
+ "path": "chat",
+ "style": {
+ "navigationBarTitleText": "客服聊天",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chat_new",
+ "style": {
+ "navigationBarTitleText": "客服聊天(新版)"
+ }
+ },
+ {
+ "path": "subscription/plan-list",
+ "style": {
+ "navigationBarTitleText": "软件订阅"
+ }
+ },
+ {
+ "path": "subscription/plan-detail",
+ "style": {
+ "navigationBarTitleText": "订阅详情"
+ }
+ },
+ {
+ "path": "subscription/subscribe-checkout",
+ "style": {
+ "navigationBarTitleText": "确认订阅"
+ }
+ },
+ {
+ "path": "subscription/my-subscriptions",
+ "style": {
+ "navigationBarTitleText": "我的订阅"
+ }
+ },
+ {
+ "path": "subscription/followed-shops",
+ "style": {
+ "navigationBarTitleText": "关注店铺"
+ }
+ },
+ {
+ "path": "points/index",
+ "style": {
+ "navigationBarTitleText": "积分管理"
+ }
+ },
+ {
+ "path": "points/signin",
+ "style": {
+ "navigationBarTitleText": "签到"
+ }
+ },
+ {
+ "path": "points/exchange",
+ "style": {
+ "navigationBarTitleText": "积分兑换"
+ }
+ },
+ {
+ "path": "points/exchange-records",
+ "style": {
+ "navigationBarTitleText": "兑换记录"
+ }
+ },
+ {
+ "path": "red-packets/index",
+ "style": {
+ "navigationBarTitleText": "我的红包"
+ }
+ },
+ {
+ "path": "bank-cards/index",
+ "style": {
+ "navigationBarTitleText": "银行卡管理"
+ }
+ },
+ {
+ "path": "bank-cards/add",
+ "style": {
+ "navigationBarTitleText": "添加银行卡"
+ }
+ },
+ {
+ "path": "home-service/index",
+ "style": {
+ "navigationBarTitleText": "居家上门服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/apply",
+ "style": {
+ "navigationBarTitleText": "提交服务申请",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/service-detail",
+ "style": {
+ "navigationBarTitleText": "预约服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/order-detail",
+ "style": {
+ "navigationBarTitleText": "服务单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/feedback",
+ "style": {
+ "navigationBarTitleText": "验收反馈",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bank-cards/verify",
+ "style": {
+ "navigationBarTitleText": "银行卡验证"
+ }
+ },
+ {
+ "path": "balance/index",
+ "style": {
+ "navigationBarTitleText": "余额"
+ }
+ },
+ {
+ "path": "my-reviews",
+ "style": {
+ "navigationBarTitleText": "我的评价"
+ }
+ },
+ {
+ "path": "message-detail",
+ "style": {
+ "navigationBarTitleText": "消息详情"
+ }
+ },
+ {
+ "path": "member/index",
+ "style": {
+ "navigationBarTitleText": "会员中心"
+ }
+ },
+ {
+ "path": "product-reviews",
+ "style": {
+ "navigationBarTitleText": "商品评价"
+ }
+ }
+ ]
+ }
+ ],
+ "tabBar": {
+ "color": "#999999",
+ "selectedColor": "#ff5000",
+ "backgroundColor": "#ffffff",
+ "borderStyle": "black",
+ "list": [
+ {
+ "pagePath": "pages/main/index",
+ "text": "首页",
+ "iconPath": "static/tabbar/home.png",
+ "selectedIconPath": "static/tabbar/home-active.png"
+ },
+ {
+ "pagePath": "pages/main/messages",
+ "text": "消息",
+ "iconPath": "static/tabbar/message.png",
+ "selectedIconPath": "static/tabbar/message.png"
+ },
+ {
+ "pagePath": "pages/main/cart",
+ "text": "购物车",
+ "iconPath": "static/tabbar/cart.png",
+ "selectedIconPath": "static/tabbar/cart.png"
+ },
+ {
+ "pagePath": "pages/main/profile",
+ "text": "我的",
+ "iconPath": "static/tabbar/user.png",
+ "selectedIconPath": "static/tabbar/user.png"
+ }
+ ]
+ },
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "mall",
+ "navigationBarBackgroundColor": "#FFFFFF",
+ "backgroundColor": "#F8F8F8"
+ },
+ "condition": {
+ "current": 0,
+ "list": [
+ {
+ "name": "consumer端",
+ "path": "pages/main/index",
+ "query": "role=consumer"
+ }
+ ]
+ }
+}
diff --git a/.pages-backup/pages.consumer.2026-05-25T01-22-40-895Z.json b/.pages-backup/pages.consumer.2026-05-25T01-22-40-895Z.json
new file mode 100644
index 00000000..6a72c27f
--- /dev/null
+++ b/.pages-backup/pages.consumer.2026-05-25T01-22-40-895Z.json
@@ -0,0 +1,1952 @@
+{
+ "pages": [
+ {
+ "path": "pages/user/boot",
+ "style": {
+ "navigationBarTitleText": ""
+ }
+ },
+ {
+ "path": "pages/user/login",
+ "style": {
+ "navigationBarTitleText": "用户登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/address/address-edit",
+ "style": {
+ "navigationBarTitleText": "服务地址"
+ }
+ },
+ {
+ "path": "pages/address/address-list",
+ "style": {
+ "navigationBarTitleText": "选择服务地址"
+ }
+ },
+ {
+ "path": "pages/address/address-map-select",
+ "style": {
+ "navigationBarTitleText": "地图选点"
+ }
+ },
+ {
+ "path": "pages/mall/admin/homePage/index",
+ "style": {
+ "navigationBarTitleText": "管理后台",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/admin/userCenter/index",
+ "style": {
+ "navigationBarTitleText": "个人中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/user/register",
+ "style": {
+ "navigationBarTitleText": "注册"
+ }
+ },
+ {
+ "path": "pages/user/forgot-password",
+ "style": {
+ "navigationBarTitleText": "忘记密码"
+ }
+ },
+ {
+ "path": "pages/user/terms",
+ "style": {
+ "navigationBarTitleText": "用户协议与隐私政策"
+ }
+ },
+ {
+ "path": "pages/user/center",
+ "style": {
+ "navigationBarTitleText": "用户中心"
+ }
+ },
+ {
+ "path": "pages/user/profile",
+ "style": {
+ "navigationBarTitleText": "个人资料"
+ }
+ },
+ {
+ "path": "pages/user/change-password",
+ "style": {
+ "navigationBarTitleText": "修改密码"
+ }
+ },
+ {
+ "path": "pages/user/bind-phone",
+ "style": {
+ "navigationBarTitleText": "绑定手机"
+ }
+ },
+ {
+ "path": "pages/user/bind-email",
+ "style": {
+ "navigationBarTitleText": "绑定邮箱"
+ }
+ },
+ {
+ "path": "pages/main/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "pages/main/category",
+ "style": {
+ "navigationBarTitleText": "分类",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/messages",
+ "style": {
+ "navigationBarTitleText": "消息",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/main/cart",
+ "style": {
+ "navigationBarTitleText": "购物车",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/profile",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/messages",
+ "style": {
+ "navigationBarTitleText": "消息",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/orders",
+ "style": {
+ "navigationBarTitleText": "订单",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/growth",
+ "style": {
+ "navigationBarTitleText": "成长",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/profile",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/products",
+ "style": {
+ "navigationBarTitleText": "商品管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/product-detail",
+ "style": {
+ "navigationBarTitleText": "商品详情",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "pages/mall/merchant/product-edit",
+ "style": {
+ "navigationBarTitleText": "编辑商品",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/reviews",
+ "style": {
+ "navigationBarTitleText": "评价管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/inventory",
+ "style": {
+ "navigationBarTitleText": "库存管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/shop-edit",
+ "style": {
+ "navigationBarTitleText": "店铺设置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/promotions",
+ "style": {
+ "navigationBarTitleText": "营销活动",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/statistics",
+ "style": {
+ "navigationBarTitleText": "数据统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/finance",
+ "style": {
+ "navigationBarTitleText": "财务结算",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/members",
+ "style": {
+ "navigationBarTitleText": "会员管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/chat",
+ "style": {
+ "navigationBarTitleText": "客服聊天",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/merchant/exclusive-discounts",
+ "style": {
+ "navigationBarTitleText": "专属折扣",
+ "navigationStyle": "custom"
+ }
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "pages/mall/consumer",
+ "pages": [
+ {
+ "path": "settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "edit-profile",
+ "style": {
+ "navigationBarTitleText": "编辑资料"
+ }
+ },
+ {
+ "path": "wallet",
+ "style": {
+ "navigationBarTitleText": "我的钱包"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "余额提现"
+ }
+ },
+ {
+ "path": "search",
+ "style": {
+ "navigationBarTitleText": "搜索",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-detail",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "channel-detail",
+ "style": {
+ "navigationBarTitleText": "频道详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "shop-detail",
+ "style": {
+ "navigationBarTitleText": "店铺详情"
+ }
+ },
+ {
+ "path": "coupons",
+ "style": {
+ "navigationBarTitleText": "我的优惠券"
+ }
+ },
+ {
+ "path": "favorites",
+ "style": {
+ "navigationBarTitleText": "我的收藏"
+ }
+ },
+ {
+ "path": "footprint",
+ "style": {
+ "navigationBarTitleText": "我的足迹"
+ }
+ },
+ {
+ "path": "address",
+ "style": {
+ "navigationBarTitleText": "地址"
+ }
+ },
+ {
+ "path": "address-list",
+ "style": {
+ "navigationBarTitleText": "收货地址"
+ }
+ },
+ {
+ "path": "address-edit",
+ "style": {
+ "navigationBarTitleText": "编辑地址"
+ }
+ },
+ {
+ "path": "checkout",
+ "style": {
+ "navigationBarTitleText": "确认订单"
+ }
+ },
+ {
+ "path": "payment",
+ "style": {
+ "navigationBarTitleText": "收银台"
+ }
+ },
+ {
+ "path": "payment-success",
+ "style": {
+ "navigationBarTitleText": "支付成功",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "orders",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f5f5f5"
+ }
+ },
+ {
+ "path": "order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "logistics",
+ "style": {
+ "navigationBarTitleText": "物流详情"
+ }
+ },
+ {
+ "path": "review",
+ "style": {
+ "navigationBarTitleText": "评价晒单"
+ }
+ },
+ {
+ "path": "refund",
+ "style": {
+ "navigationBarTitleText": "退款/售后"
+ }
+ },
+ {
+ "path": "apply-refund",
+ "style": {
+ "navigationBarTitleText": "申请售后"
+ }
+ },
+ {
+ "path": "refund-review",
+ "style": {
+ "navigationBarTitleText": "服务评价"
+ }
+ },
+ {
+ "path": "chat",
+ "style": {
+ "navigationBarTitleText": "客服聊天",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chat_new",
+ "style": {
+ "navigationBarTitleText": "客服聊天(新版)"
+ }
+ },
+ {
+ "path": "subscription/plan-list",
+ "style": {
+ "navigationBarTitleText": "软件订阅"
+ }
+ },
+ {
+ "path": "subscription/plan-detail",
+ "style": {
+ "navigationBarTitleText": "订阅详情"
+ }
+ },
+ {
+ "path": "subscription/subscribe-checkout",
+ "style": {
+ "navigationBarTitleText": "确认订阅"
+ }
+ },
+ {
+ "path": "subscription/my-subscriptions",
+ "style": {
+ "navigationBarTitleText": "我的订阅"
+ }
+ },
+ {
+ "path": "subscription/followed-shops",
+ "style": {
+ "navigationBarTitleText": "关注店铺"
+ }
+ },
+ {
+ "path": "points/index",
+ "style": {
+ "navigationBarTitleText": "积分管理"
+ }
+ },
+ {
+ "path": "points/signin",
+ "style": {
+ "navigationBarTitleText": "签到"
+ }
+ },
+ {
+ "path": "points/exchange",
+ "style": {
+ "navigationBarTitleText": "积分兑换"
+ }
+ },
+ {
+ "path": "points/exchange-records",
+ "style": {
+ "navigationBarTitleText": "兑换记录"
+ }
+ },
+ {
+ "path": "red-packets/index",
+ "style": {
+ "navigationBarTitleText": "我的红包"
+ }
+ },
+ {
+ "path": "bank-cards/index",
+ "style": {
+ "navigationBarTitleText": "银行卡管理"
+ }
+ },
+ {
+ "path": "bank-cards/add",
+ "style": {
+ "navigationBarTitleText": "添加银行卡"
+ }
+ },
+ {
+ "path": "home-service/index",
+ "style": {
+ "navigationBarTitleText": "居家上门服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/apply",
+ "style": {
+ "navigationBarTitleText": "提交服务申请",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/service-detail",
+ "style": {
+ "navigationBarTitleText": "预约服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/order-detail",
+ "style": {
+ "navigationBarTitleText": "服务单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/feedback",
+ "style": {
+ "navigationBarTitleText": "验收反馈",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bank-cards/verify",
+ "style": {
+ "navigationBarTitleText": "银行卡验证"
+ }
+ },
+ {
+ "path": "balance/index",
+ "style": {
+ "navigationBarTitleText": "余额"
+ }
+ },
+ {
+ "path": "my-reviews",
+ "style": {
+ "navigationBarTitleText": "我的评价"
+ }
+ },
+ {
+ "path": "message-detail",
+ "style": {
+ "navigationBarTitleText": "消息详情"
+ }
+ },
+ {
+ "path": "member/index",
+ "style": {
+ "navigationBarTitleText": "会员中心"
+ }
+ },
+ {
+ "path": "product-reviews",
+ "style": {
+ "navigationBarTitleText": "商品评价"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/delivery",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "配送中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情页",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "profile",
+ "style": {
+ "navigationBarTitleText": "配送个人中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "order-history",
+ "style": {
+ "navigationBarTitleText": "历史记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "all",
+ "style": {
+ "navigationBarTitleText": "待接取任务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "earnings",
+ "style": {
+ "navigationBarTitleText": "收入明细",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "tasks",
+ "style": {
+ "navigationBarTitleText": "全部任务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "task-detail",
+ "style": {
+ "navigationBarTitleText": "任务详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "profile-edit",
+ "style": {
+ "navigationBarTitleText": "编辑个人资料",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "ratings",
+ "style": {
+ "navigationBarTitleText": "评价",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "vehicle",
+ "style": {
+ "navigationBarTitleText": "车辆管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "vehicle-add",
+ "style": {
+ "navigationBarTitleText": "添加车辆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "vehicle-edit",
+ "style": {
+ "navigationBarTitleText": "编辑车辆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "help-center",
+ "style": {
+ "navigationBarTitleText": "帮助中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "about",
+ "style": {
+ "navigationBarTitleText": "关于我们",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "feedback",
+ "style": {
+ "navigationBarTitleText": "意见反馈",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test",
+ "style": {
+ "navigationBarTitleText": "test",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "settings",
+ "style": {
+ "navigationBarTitleText": "设置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/index",
+ "style": {
+ "navigationBarTitleText": "配送模块测试索引",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/merchant-order-list",
+ "style": {
+ "navigationBarTitleText": "商家发货管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/merchant-order-detail",
+ "style": {
+ "navigationBarTitleText": "订单物流详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/platform-tracking-query",
+ "style": {
+ "navigationBarTitleText": "轨迹排障查询",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/platform-webhook-logs",
+ "style": {
+ "navigationBarTitleText": "Webhook日志",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/api-simulator",
+ "style": {
+ "navigationBarTitleText": "第三方接口模拟器",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/platform-config-center",
+ "style": {
+ "navigationBarTitleText": "配送配置中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/consumer-logistics-detail",
+ "style": {
+ "navigationBarTitleText": "用户查快递",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "test/consumer-order-list",
+ "style": {
+ "navigationBarTitleText": "我的快递",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/analytics",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "数据分析",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "profile",
+ "style": {
+ "navigationBarTitleText": "数据分析个人中心"
+ }
+ },
+ {
+ "path": "sales-report",
+ "style": {
+ "navigationBarTitleText": "销售报表"
+ }
+ },
+ {
+ "path": "user-analysis",
+ "style": {
+ "navigationBarTitleText": "用户分析"
+ }
+ },
+ {
+ "path": "product-insights",
+ "style": {
+ "navigationBarTitleText": "商品洞察"
+ }
+ },
+ {
+ "path": "delivery-analysis",
+ "style": {
+ "navigationBarTitleText": "配送效率分析"
+ }
+ },
+ {
+ "path": "coupon-analysis",
+ "style": {
+ "navigationBarTitleText": "优惠券效果分析"
+ }
+ },
+ {
+ "path": "market-trends",
+ "style": {
+ "navigationBarTitleText": "市场趋势"
+ }
+ },
+ {
+ "path": "custom-report",
+ "style": {
+ "navigationBarTitleText": "自定义报表"
+ }
+ },
+ {
+ "path": "report-detail",
+ "style": {
+ "navigationBarTitleText": "报表详情",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "data-detail",
+ "style": {
+ "navigationBarTitleText": "数据分析详情",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "insight-detail",
+ "style": {
+ "navigationBarTitleText": "数据洞察详情",
+ "enablePullDownRefresh": false
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/order",
+ "pages": [
+ {
+ "path": "order-management/index",
+ "style": {
+ "navigationBarTitleText": "订单管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "order-statistics/index",
+ "style": {
+ "navigationBarTitleText": "订单统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "order-configuration/index",
+ "style": {
+ "navigationBarTitleText": "订单配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "aftersales-order/index",
+ "style": {
+ "navigationBarTitleText": "售后订单",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "write-off-records/index",
+ "style": {
+ "navigationBarTitleText": "核销记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "cashier-order/index",
+ "style": {
+ "navigationBarTitleText": "收銀台订单",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/product",
+ "pages": [
+ {
+ "path": "product-management/index",
+ "style": {
+ "navigationBarTitleText": "商品管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-statistics/index",
+ "style": {
+ "navigationBarTitleText": "商品统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "classification/index",
+ "style": {
+ "navigationBarTitleText": "商品分类",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "reviews/index",
+ "style": {
+ "navigationBarTitleText": "商品评分",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "specifications/index",
+ "style": {
+ "navigationBarTitleText": "商品规格",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "parameters/index",
+ "style": {
+ "navigationBarTitleText": "商品参数",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "labels/index",
+ "style": {
+ "navigationBarTitleText": "商品标签",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "protection/index",
+ "style": {
+ "navigationBarTitleText": "商品保障",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/user",
+ "pages": [
+ {
+ "path": "management/index",
+ "style": {
+ "navigationBarTitleText": "用户管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "statistics/index",
+ "style": {
+ "navigationBarTitleText": "用户统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "grouping/index",
+ "style": {
+ "navigationBarTitleText": "用户分组",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "label/index",
+ "style": {
+ "navigationBarTitleText": "用户标签",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "level/index",
+ "style": {
+ "navigationBarTitleText": "用户等级",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "config/index",
+ "style": {
+ "navigationBarTitleText": "用户配置",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/subscription",
+ "pages": [
+ {
+ "path": "plan-management",
+ "style": {
+ "navigationBarTitleText": "订阅方案管理"
+ }
+ },
+ {
+ "path": "user-subscriptions",
+ "style": {
+ "navigationBarTitleText": "用户订阅管理"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/distribution",
+ "pages": [
+ {
+ "path": "setting/index",
+ "style": {
+ "navigationBarTitleText": "分销设置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "distributor-management/index",
+ "style": {
+ "navigationBarTitleText": "分销员管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "level/index",
+ "style": {
+ "navigationBarTitleText": "分销等级管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "business-division/business-division-list/index",
+ "style": {
+ "navigationBarTitleText": "事业部列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "business-division/agent-list/index",
+ "style": {
+ "navigationBarTitleText": "代理商列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "business-division/agent-application/index",
+ "style": {
+ "navigationBarTitleText": "代理商申请",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/marketing",
+ "pages": [
+ {
+ "path": "coupon/coupon-list/index",
+ "style": {
+ "navigationBarTitleText": "优惠券列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "coupon/claim-record/index",
+ "style": {
+ "navigationBarTitleText": "用户领取记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/statistics/index",
+ "style": {
+ "navigationBarTitleText": "积分统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/products/index",
+ "style": {
+ "navigationBarTitleText": "积分商品",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/orders/index",
+ "style": {
+ "navigationBarTitleText": "积分订单",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/record/index",
+ "style": {
+ "navigationBarTitleText": "积分记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/config/index",
+ "style": {
+ "navigationBarTitleText": "积分配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lottery/list/index",
+ "style": {
+ "navigationBarTitleText": "抽奖列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lottery/configuration/index",
+ "style": {
+ "navigationBarTitleText": "抽奖配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "combination/products/index",
+ "style": {
+ "navigationBarTitleText": "拼团商品",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "combination/list/index",
+ "style": {
+ "navigationBarTitleText": "拼团列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "combination/index",
+ "style": {
+ "navigationBarTitleText": "拼团活动",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "seckill/list/index",
+ "style": {
+ "navigationBarTitleText": "秒杀列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "seckill/products/index",
+ "style": {
+ "navigationBarTitleText": "秒杀商品",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "seckill/config/index",
+ "style": {
+ "navigationBarTitleText": "秒杀配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "member/type/index",
+ "style": {
+ "navigationBarTitleText": "会员类型",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "member/right/index",
+ "style": {
+ "navigationBarTitleText": "会员权益",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "member/kami-membership/index",
+ "style": {
+ "navigationBarTitleText": "卡密会员",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "member/record/index",
+ "style": {
+ "navigationBarTitleText": "会员记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "member/config/index",
+ "style": {
+ "navigationBarTitleText": "会员配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "live/live-management/index",
+ "style": {
+ "navigationBarTitleText": "直播間管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "live/products-management/index",
+ "style": {
+ "navigationBarTitleText": "直播商品管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "live/streamer-management/index",
+ "style": {
+ "navigationBarTitleText": "主播管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "live/index",
+ "style": {
+ "navigationBarTitleText": "直播首页",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "recharge/amount-setting/index",
+ "style": {
+ "navigationBarTitleText": "充値额度设置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "recharge/config/index",
+ "style": {
+ "navigationBarTitleText": "充値配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "checkin/config/index",
+ "style": {
+ "navigationBarTitleText": "签到配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "checkin/reward/index",
+ "style": {
+ "navigationBarTitleText": "签到奖励",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bargain/list/index",
+ "style": {
+ "navigationBarTitleText": "砍价列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bargain/products/index",
+ "style": {
+ "navigationBarTitleText": "砍价商品",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "newcomer/index",
+ "style": {
+ "navigationBarTitleText": "新人礼包",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "marketing-statistics/index",
+ "style": {
+ "navigationBarTitleText": "营销统计",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/maintain",
+ "pages": [
+ {
+ "path": "dev-tools/database/index",
+ "style": {
+ "navigationBarTitleText": "数据库管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-tools/file/index",
+ "style": {
+ "navigationBarTitleText": "文件管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-tools/interface/index",
+ "style": {
+ "navigationBarTitleText": "接口管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-tools/data-dictionary/index",
+ "style": {
+ "navigationBarTitleText": "数据字典",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/category/index",
+ "style": {
+ "navigationBarTitleText": "配置分类",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/combination-data/index",
+ "style": {
+ "navigationBarTitleText": "组合数据",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/cron-job/index",
+ "style": {
+ "navigationBarTitleText": "定时任务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/permission/index",
+ "style": {
+ "navigationBarTitleText": "权限配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/module-config/index",
+ "style": {
+ "navigationBarTitleText": "模块配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "dev-config/custom-event/index",
+ "style": {
+ "navigationBarTitleText": "自定义事件",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "security/refresh-cache/index",
+ "style": {
+ "navigationBarTitleText": "刷新缓存",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "security/system-log/index",
+ "style": {
+ "navigationBarTitleText": "系统日志",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "security/online-upgrade/index",
+ "style": {
+ "navigationBarTitleText": "在线升级",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "data/logistics/index",
+ "style": {
+ "navigationBarTitleText": "物流数据",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "data/city-data/index",
+ "style": {
+ "navigationBarTitleText": "城市数据",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "data/clear/index",
+ "style": {
+ "navigationBarTitleText": "清除数据",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "api/account/index",
+ "style": {
+ "navigationBarTitleText": "API账号",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lang/list/index",
+ "style": {
+ "navigationBarTitleText": "语言列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lang/detail/index",
+ "style": {
+ "navigationBarTitleText": "语言详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lang/region/index",
+ "style": {
+ "navigationBarTitleText": "语言地区",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "lang/config/index",
+ "style": {
+ "navigationBarTitleText": "语言配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "sys/info/index",
+ "style": {
+ "navigationBarTitleText": "系统信息",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/setting",
+ "pages": [
+ {
+ "path": "message/index",
+ "style": {
+ "navigationBarTitleText": "消息管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "agreement/index",
+ "style": {
+ "navigationBarTitleText": "协议设置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "receipt/index",
+ "style": {
+ "navigationBarTitleText": "小票配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "auth/admin-management/index",
+ "style": {
+ "navigationBarTitleText": "管理员管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "auth/role-management/index",
+ "style": {
+ "navigationBarTitleText": "角色管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "auth/menu-management/index",
+ "style": {
+ "navigationBarTitleText": "菜单管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "delivery/management/index",
+ "style": {
+ "navigationBarTitleText": "配送员管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "delivery/setting/station/index",
+ "style": {
+ "navigationBarTitleText": "提货点",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "delivery/setting/verifier/index",
+ "style": {
+ "navigationBarTitleText": "核销员",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "delivery/setting/template/index",
+ "style": {
+ "navigationBarTitleText": "运费模板",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/onepass/index",
+ "style": {
+ "navigationBarTitleText": "一号通账户",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/onepass/config/index",
+ "style": {
+ "navigationBarTitleText": "一号通配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/storage/index",
+ "style": {
+ "navigationBarTitleText": "系统存储配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/collect/index",
+ "style": {
+ "navigationBarTitleText": "商品采集配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/logistics/index",
+ "style": {
+ "navigationBarTitleText": "物流查询配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/e-sheet/index",
+ "style": {
+ "navigationBarTitleText": "电子面单配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/sms/index",
+ "style": {
+ "navigationBarTitleText": "短信功能配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "interface/payment/index",
+ "style": {
+ "navigationBarTitleText": "商城支付配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "system/index",
+ "style": {
+ "navigationBarTitleText": "系统配置",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/app",
+ "pages": [
+ {
+ "path": "wechat/menu/index",
+ "style": {
+ "navigationBarTitleText": "微信菜单",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "wechat/management/index",
+ "style": {
+ "navigationBarTitleText": "图文管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "wechat/reply/follow/index",
+ "style": {
+ "navigationBarTitleText": "微信关注回复",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "wechat/reply/keyword/index",
+ "style": {
+ "navigationBarTitleText": "关键字回复",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "wechat/reply/invalid/index",
+ "style": {
+ "navigationBarTitleText": "无效关键词回复",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "wechat/config/index",
+ "style": {
+ "navigationBarTitleText": "公众号配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "routine/download/index",
+ "style": {
+ "navigationBarTitleText": "小程序下载",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "routine/config/index",
+ "style": {
+ "navigationBarTitleText": "小程序配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "mobile/config/index",
+ "style": {
+ "navigationBarTitleText": "APP配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "mobile/version/index",
+ "style": {
+ "navigationBarTitleText": "版本管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pc/design/index",
+ "style": {
+ "navigationBarTitleText": "PC端装修",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pc/config/index",
+ "style": {
+ "navigationBarTitleText": "PC端配置",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/kefu",
+ "pages": [
+ {
+ "path": "list/index",
+ "style": {
+ "navigationBarTitleText": "客服列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "rhetoric/index",
+ "style": {
+ "navigationBarTitleText": "话术管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "user-message/index",
+ "style": {
+ "navigationBarTitleText": "用户消息",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "auto-reply/index",
+ "style": {
+ "navigationBarTitleText": "自动回复",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "config/index",
+ "style": {
+ "navigationBarTitleText": "客服配置",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/decoration",
+ "pages": [
+ {
+ "path": "homepage-decoration/index",
+ "style": {
+ "navigationBarTitleText": "首页装修",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-category/index",
+ "style": {
+ "navigationBarTitleText": "商品分类装修",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "personal-center/index",
+ "style": {
+ "navigationBarTitleText": "个人中心装修",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "data-config/index",
+ "style": {
+ "navigationBarTitleText": "数据配置",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "theme-style/index",
+ "style": {
+ "navigationBarTitleText": "主题风格",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "material-management/index",
+ "style": {
+ "navigationBarTitleText": "素材管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "link-management/index",
+ "style": {
+ "navigationBarTitleText": "链接管理",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/finance",
+ "pages": [
+ {
+ "path": "transaction-statistics/index",
+ "style": {
+ "navigationBarTitleText": "交易统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "finance-operations/request/index",
+ "style": {
+ "navigationBarTitleText": "提现申请",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "finance-operations/management/index",
+ "style": {
+ "navigationBarTitleText": "发票管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "finance-record/recharge-record/index",
+ "style": {
+ "navigationBarTitleText": "充值记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "finance-record/flow/index",
+ "style": {
+ "navigationBarTitleText": "资金流水",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "finance-record/billing-record/index",
+ "style": {
+ "navigationBarTitleText": "账单记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "commission-record/index",
+ "style": {
+ "navigationBarTitleText": "佣金记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "balance-record/statistics/index",
+ "style": {
+ "navigationBarTitleText": "余额统计",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "balance-record/record/index",
+ "style": {
+ "navigationBarTitleText": "余额记录",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/cms",
+ "pages": [
+ {
+ "path": "article/index",
+ "style": {
+ "navigationBarTitleText": "文章管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "category/index",
+ "style": {
+ "navigationBarTitleText": "文章分类",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/admin/shop",
+ "pages": [
+ {
+ "path": "shop-manage",
+ "style": {
+ "navigationBarTitleText": "店铺列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "manage",
+ "style": {
+ "navigationBarTitleText": "店铺管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "create",
+ "style": {
+ "navigationBarTitleText": "创建店铺",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/mall/service",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "客服工作台",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "profile",
+ "style": {
+ "navigationBarTitleText": "客服个人中心"
+ }
+ },
+ {
+ "path": "ticket-detail",
+ "style": {
+ "navigationBarTitleText": "工单详情",
+ "enablePullDownRefresh": false
+ }
+ }
+ ]
+ }
+ ],
+ "tabBar": {
+ "color": "#999999",
+ "selectedColor": "#ff5000",
+ "backgroundColor": "#ffffff",
+ "borderStyle": "black",
+ "list": [
+ {
+ "pagePath": "pages/main/index",
+ "text": "首页",
+ "iconPath": "static/tabbar/home.png",
+ "selectedIconPath": "static/tabbar/home-active.png"
+ },
+ {
+ "pagePath": "pages/main/messages",
+ "text": "消息",
+ "iconPath": "static/tabbar/message.png",
+ "selectedIconPath": "static/tabbar/message.png"
+ },
+ {
+ "pagePath": "pages/main/cart",
+ "text": "购物车",
+ "iconPath": "static/tabbar/cart.png",
+ "selectedIconPath": "static/tabbar/cart.png"
+ },
+ {
+ "pagePath": "pages/main/profile",
+ "text": "我的",
+ "iconPath": "static/tabbar/user.png",
+ "selectedIconPath": "static/tabbar/user.png"
+ }
+ ]
+ },
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "mall",
+ "navigationBarBackgroundColor": "#FFFFFF",
+ "backgroundColor": "#F8F8F8"
+ }
+}
diff --git a/.pages-backup/pages.consumer.2026-05-25T03-16-26-126Z.json b/.pages-backup/pages.consumer.2026-05-25T03-16-26-126Z.json
new file mode 100644
index 00000000..b2ea96e6
--- /dev/null
+++ b/.pages-backup/pages.consumer.2026-05-25T03-16-26-126Z.json
@@ -0,0 +1,464 @@
+{
+ "pages": [
+ {
+ "path": "pages/main/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": false
+ }
+ },
+ {
+ "path": "pages/user/boot",
+ "style": {
+ "navigationBarTitleText": ""
+ }
+ },
+ {
+ "path": "pages/user/login",
+ "style": {
+ "navigationBarTitleText": "用户登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/user/register",
+ "style": {
+ "navigationBarTitleText": "注册"
+ }
+ },
+ {
+ "path": "pages/user/forgot-password",
+ "style": {
+ "navigationBarTitleText": "忘记密码"
+ }
+ },
+ {
+ "path": "pages/user/terms",
+ "style": {
+ "navigationBarTitleText": "用户协议与隐私政策"
+ }
+ },
+ {
+ "path": "pages/user/center",
+ "style": {
+ "navigationBarTitleText": "用户中心"
+ }
+ },
+ {
+ "path": "pages/user/profile",
+ "style": {
+ "navigationBarTitleText": "个人资料"
+ }
+ },
+ {
+ "path": "pages/user/change-password",
+ "style": {
+ "navigationBarTitleText": "修改密码"
+ }
+ },
+ {
+ "path": "pages/user/bind-phone",
+ "style": {
+ "navigationBarTitleText": "绑定手机"
+ }
+ },
+ {
+ "path": "pages/user/bind-email",
+ "style": {
+ "navigationBarTitleText": "绑定邮箱"
+ }
+ },
+ {
+ "path": "pages/main/messages",
+ "style": {
+ "navigationBarTitleText": "消息",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/main/cart",
+ "style": {
+ "navigationBarTitleText": "购物车",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/profile",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/main/category",
+ "style": {
+ "navigationBarTitleText": "分类",
+ "navigationStyle": "custom"
+ }
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "pages/mall/consumer",
+ "pages": [
+ {
+ "path": "settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "edit-profile",
+ "style": {
+ "navigationBarTitleText": "编辑资料"
+ }
+ },
+ {
+ "path": "wallet",
+ "style": {
+ "navigationBarTitleText": "我的钱包"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "余额提现"
+ }
+ },
+ {
+ "path": "search",
+ "style": {
+ "navigationBarTitleText": "搜索",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-detail",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "channel-detail",
+ "style": {
+ "navigationBarTitleText": "频道详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "shop-detail",
+ "style": {
+ "navigationBarTitleText": "店铺详情"
+ }
+ },
+ {
+ "path": "coupons",
+ "style": {
+ "navigationBarTitleText": "我的优惠券"
+ }
+ },
+ {
+ "path": "favorites",
+ "style": {
+ "navigationBarTitleText": "我的收藏"
+ }
+ },
+ {
+ "path": "footprint",
+ "style": {
+ "navigationBarTitleText": "我的足迹"
+ }
+ },
+ {
+ "path": "address",
+ "style": {
+ "navigationBarTitleText": "地址"
+ }
+ },
+ {
+ "path": "address-list",
+ "style": {
+ "navigationBarTitleText": "收货地址"
+ }
+ },
+ {
+ "path": "address-edit",
+ "style": {
+ "navigationBarTitleText": "编辑地址"
+ }
+ },
+ {
+ "path": "checkout",
+ "style": {
+ "navigationBarTitleText": "确认订单"
+ }
+ },
+ {
+ "path": "payment",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "payment-success",
+ "style": {
+ "navigationBarTitleText": "支付成功",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "orders",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f5f5f5"
+ }
+ },
+ {
+ "path": "order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "logistics",
+ "style": {
+ "navigationBarTitleText": "物流详情"
+ }
+ },
+ {
+ "path": "review",
+ "style": {
+ "navigationBarTitleText": "评价晒单"
+ }
+ },
+ {
+ "path": "refund",
+ "style": {
+ "navigationBarTitleText": "退款/售后"
+ }
+ },
+ {
+ "path": "apply-refund",
+ "style": {
+ "navigationBarTitleText": "申请售后"
+ }
+ },
+ {
+ "path": "refund-review",
+ "style": {
+ "navigationBarTitleText": "服务评价"
+ }
+ },
+ {
+ "path": "chat",
+ "style": {
+ "navigationBarTitleText": "客服聊天",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chat_new",
+ "style": {
+ "navigationBarTitleText": "客服聊天(新版)"
+ }
+ },
+ {
+ "path": "subscription/plan-list",
+ "style": {
+ "navigationBarTitleText": "软件订阅"
+ }
+ },
+ {
+ "path": "subscription/plan-detail",
+ "style": {
+ "navigationBarTitleText": "订阅详情"
+ }
+ },
+ {
+ "path": "subscription/subscribe-checkout",
+ "style": {
+ "navigationBarTitleText": "确认订阅"
+ }
+ },
+ {
+ "path": "subscription/my-subscriptions",
+ "style": {
+ "navigationBarTitleText": "我的订阅"
+ }
+ },
+ {
+ "path": "subscription/followed-shops",
+ "style": {
+ "navigationBarTitleText": "关注店铺"
+ }
+ },
+ {
+ "path": "points/index",
+ "style": {
+ "navigationBarTitleText": "积分管理"
+ }
+ },
+ {
+ "path": "points/signin",
+ "style": {
+ "navigationBarTitleText": "签到"
+ }
+ },
+ {
+ "path": "points/exchange",
+ "style": {
+ "navigationBarTitleText": "积分兑换"
+ }
+ },
+ {
+ "path": "points/exchange-records",
+ "style": {
+ "navigationBarTitleText": "兑换记录"
+ }
+ },
+ {
+ "path": "red-packets/index",
+ "style": {
+ "navigationBarTitleText": "我的红包"
+ }
+ },
+ {
+ "path": "bank-cards/index",
+ "style": {
+ "navigationBarTitleText": "银行卡管理"
+ }
+ },
+ {
+ "path": "bank-cards/add",
+ "style": {
+ "navigationBarTitleText": "添加银行卡"
+ }
+ },
+ {
+ "path": "home-service/index",
+ "style": {
+ "navigationBarTitleText": "居家上门服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/apply",
+ "style": {
+ "navigationBarTitleText": "提交服务申请",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/service-detail",
+ "style": {
+ "navigationBarTitleText": "预约服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/order-detail",
+ "style": {
+ "navigationBarTitleText": "服务单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/feedback",
+ "style": {
+ "navigationBarTitleText": "验收反馈",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bank-cards/verify",
+ "style": {
+ "navigationBarTitleText": "银行卡验证"
+ }
+ },
+ {
+ "path": "balance/index",
+ "style": {
+ "navigationBarTitleText": "余额"
+ }
+ },
+ {
+ "path": "my-reviews",
+ "style": {
+ "navigationBarTitleText": "我的评价"
+ }
+ },
+ {
+ "path": "message-detail",
+ "style": {
+ "navigationBarTitleText": "消息详情"
+ }
+ },
+ {
+ "path": "member/index",
+ "style": {
+ "navigationBarTitleText": "会员中心"
+ }
+ },
+ {
+ "path": "product-reviews",
+ "style": {
+ "navigationBarTitleText": "商品评价"
+ }
+ }
+ ]
+ }
+ ],
+ "tabBar": {
+ "color": "#999999",
+ "selectedColor": "#ff5000",
+ "backgroundColor": "#ffffff",
+ "borderStyle": "black",
+ "list": [
+ {
+ "pagePath": "pages/main/index",
+ "text": "首页",
+ "iconPath": "static/tabbar/home.png",
+ "selectedIconPath": "static/tabbar/home-active.png"
+ },
+ {
+ "pagePath": "pages/main/messages",
+ "text": "消息",
+ "iconPath": "static/tabbar/message.png",
+ "selectedIconPath": "static/tabbar/message.png"
+ },
+ {
+ "pagePath": "pages/main/cart",
+ "text": "购物车",
+ "iconPath": "static/tabbar/cart.png",
+ "selectedIconPath": "static/tabbar/cart.png"
+ },
+ {
+ "pagePath": "pages/main/profile",
+ "text": "我的",
+ "iconPath": "static/tabbar/user.png",
+ "selectedIconPath": "static/tabbar/user.png"
+ }
+ ]
+ },
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "mall",
+ "navigationBarBackgroundColor": "#FFFFFF",
+ "backgroundColor": "#F8F8F8"
+ },
+ "condition": {
+ "current": 0,
+ "list": [
+ {
+ "name": "consumer端",
+ "path": "pages/main/index",
+ "query": "role=consumer"
+ }
+ ]
+ }
+}
diff --git a/components/home/HomeMallContent.uvue b/components/home/HomeMallContent.uvue
index f400063f..5a1375a4 100644
--- a/components/home/HomeMallContent.uvue
+++ b/components/home/HomeMallContent.uvue
@@ -52,13 +52,13 @@
:key="product.id + '-' + pIndex"
class="hmall-recommend-product"
>
-
- {{ product.shortName }}
+
+ {{ getChannelProductTitle(product) }}
- {{ product.tag }}
- ¥{{ formatChannelPrice(product.price) }}
+ {{ getChannelProductTag(product) }}
+ ¥{{ formatChannelPrice(getChannelSalePrice(product)) }}
- ¥{{ formatChannelPrice(product.marketPrice) }}
+ ¥{{ formatChannelPrice(getChannelMarketPrice(product)) }}
@@ -167,6 +167,7 @@ import type { Category, Product } from '@/utils/supabaseService.uts'
import type { MarketingChannel, ChannelProduct, SimpleCategoryChannel } from '@/utils/mockChannelData.uts'
const failedProductImageIds = ref([])
+const failedChannelImageIds = ref([])
type SecondaryCategoryPage = {
id: string
@@ -266,9 +267,50 @@ function getCategoryDisplayIcon(category: Category): string {
}
function getChannelProductImage(product: ChannelProduct): string {
+ if (failedChannelImageIds.value.indexOf(product.id) != -1) {
+ return '/static/images/default.png'
+ }
return product.image != '' ? product.image : '/static/images/default.png'
}
+function handleChannelProductImageError(productId: string): void {
+ if (productId == '') {
+ return
+ }
+ if (failedChannelImageIds.value.indexOf(productId) == -1) {
+ failedChannelImageIds.value.push(productId)
+ }
+}
+
+function getChannelProductTitle(product: ChannelProduct): string {
+ if (product.shortName != null && product.shortName != '') {
+ return product.shortName
+ }
+ if (product.name != null && product.name != '') {
+ return product.name
+ }
+ return '商品补充中'
+}
+
+function getChannelProductTag(product: ChannelProduct): string {
+ if (product.tag != null && product.tag != '') {
+ return product.tag
+ }
+ return '活动价'
+}
+
+function getChannelSalePrice(product: ChannelProduct): number {
+ return product.price > 0 ? product.price : 0
+}
+
+function getChannelMarketPrice(product: ChannelProduct): number {
+ return product.marketPrice > 0 ? product.marketPrice : 0
+}
+
+function showChannelMarketPrice(product: ChannelProduct): boolean {
+ return getChannelMarketPrice(product) > getChannelSalePrice(product)
+}
+
function formatChannelPrice(price: number): string {
const rounded = Math.round(price)
if (Math.abs(price - rounded) < 0.001) {
diff --git a/components/mall/GuessYouLike/GuessYouLike.uvue b/components/mall/GuessYouLike/GuessYouLike.uvue
new file mode 100644
index 00000000..5f08343b
--- /dev/null
+++ b/components/mall/GuessYouLike/GuessYouLike.uvue
@@ -0,0 +1,363 @@
+
+
+
+
+
+
+
+
+ {{ product.name }}
+
+ ¥{{ product.price.toFixed(2) }}
+ ¥{{ product.originalPrice.toFixed(2) }}
+
+ {{ product.salesText }}
+
+
+
+
+
+ 暂无推荐商品
+
+
+
+ 加载中...
+
+
+ {{ errorText }}
+
+
+ 没有更多了
+
+
+
+
+
+
+
diff --git a/mall_sql/migrations/20260522_hss_service_catalog.sql b/mall_sql/migrations/20260522_hss_service_catalog.sql
new file mode 100644
index 00000000..9e8fb25c
--- /dev/null
+++ b/mall_sql/migrations/20260522_hss_service_catalog.sql
@@ -0,0 +1,114 @@
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS public.hss_service_catalog (
+ id TEXT PRIMARY KEY,
+ name TEXT NOT NULL,
+ category TEXT NOT NULL DEFAULT '',
+ price NUMERIC(10, 2) NOT NULL DEFAULT 0,
+ duration_text TEXT NOT NULL DEFAULT '',
+ summary TEXT NOT NULL DEFAULT '',
+ tags_json JSONB NOT NULL DEFAULT '[]'::jsonb,
+ suitable_for TEXT NOT NULL DEFAULT '',
+ sort_no INTEGER NOT NULL DEFAULT 0,
+ status SMALLINT NOT NULL DEFAULT 1,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ deleted_at TIMESTAMPTZ
+);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_catalog_status_sort
+ ON public.hss_service_catalog(status, sort_no)
+ WHERE deleted_at IS NULL;
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_catalog_category
+ ON public.hss_service_catalog(category)
+ WHERE deleted_at IS NULL;
+
+CREATE OR REPLACE FUNCTION public.tg_hss_service_catalog_updated_at()
+RETURNS trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ NEW.updated_at = now();
+ RETURN NEW;
+END;
+$$;
+
+DROP TRIGGER IF EXISTS trg_hss_service_catalog_updated_at ON public.hss_service_catalog;
+CREATE TRIGGER trg_hss_service_catalog_updated_at
+BEFORE UPDATE ON public.hss_service_catalog
+FOR EACH ROW
+EXECUTE FUNCTION public.tg_hss_service_catalog_updated_at();
+
+ALTER TABLE public.hss_service_catalog ENABLE ROW LEVEL SECURITY;
+
+DROP POLICY IF EXISTS hss_service_catalog_public_select ON public.hss_service_catalog;
+CREATE POLICY hss_service_catalog_public_select
+ ON public.hss_service_catalog
+ FOR SELECT
+ USING (deleted_at IS NULL AND status = 1);
+
+INSERT INTO public.hss_service_catalog (
+ id,
+ name,
+ category,
+ price,
+ duration_text,
+ summary,
+ tags_json,
+ suitable_for,
+ sort_no,
+ status
+) VALUES
+ (
+ 'svc-001',
+ '基础上门护理',
+ '日常照护',
+ 168,
+ '约 2 小时',
+ '覆盖生命体征监测、基础照护、风险提醒。',
+ '["适老化", "护理员上门", "支持家属陪同"]'::jsonb,
+ '行动不便、术后恢复、慢病随访老人',
+ 10,
+ 1
+ ),
+ (
+ 'svc-002',
+ '康复训练指导',
+ '康复支持',
+ 260,
+ '约 3 小时',
+ '提供肢体训练、步态练习和居家康复建议。',
+ '["康复师", "步骤清晰", "可连续预约"]'::jsonb,
+ '卒中恢复、术后康复、失能半失能老人',
+ 20,
+ 1
+ ),
+ (
+ 'svc-003',
+ '慢病健康随访',
+ '健康管理',
+ 128,
+ '约 90 分钟',
+ '完成血压血糖监测、用药核对与健康宣教。',
+ '["随访", "慢病", "可生成记录"]'::jsonb,
+ '高血压、糖尿病等长期管理老人',
+ 30,
+ 1
+ )
+ON CONFLICT (id) DO UPDATE SET
+ name = EXCLUDED.name,
+ category = EXCLUDED.category,
+ price = EXCLUDED.price,
+ duration_text = EXCLUDED.duration_text,
+ summary = EXCLUDED.summary,
+ tags_json = EXCLUDED.tags_json,
+ suitable_for = EXCLUDED.suitable_for,
+ sort_no = EXCLUDED.sort_no,
+ status = EXCLUDED.status,
+ deleted_at = NULL,
+ updated_at = now();
+
+COMMENT ON TABLE public.hss_service_catalog IS '居家上门服务目录表';
+
+COMMIT;
\ No newline at end of file
diff --git a/mall_sql/migrations/20260522_hss_service_mvp_p0.sql b/mall_sql/migrations/20260522_hss_service_mvp_p0.sql
new file mode 100644
index 00000000..72a0166f
--- /dev/null
+++ b/mall_sql/migrations/20260522_hss_service_mvp_p0.sql
@@ -0,0 +1,274 @@
+BEGIN;
+
+ALTER TABLE public.ml_user_addresses
+ ADD COLUMN IF NOT EXISTS latitude DOUBLE PRECISION,
+ ADD COLUMN IF NOT EXISTS longitude DOUBLE PRECISION,
+ ADD COLUMN IF NOT EXISTS coordinate_type TEXT NOT NULL DEFAULT 'gcj02';
+
+CREATE TABLE IF NOT EXISTS public.hss_service_orders (
+ id TEXT PRIMARY KEY,
+ order_no TEXT NOT NULL UNIQUE,
+ user_id UUID NOT NULL,
+ service_id TEXT NOT NULL,
+ service_name TEXT NOT NULL,
+ service_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb,
+ service_address_id UUID,
+ address_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb,
+ recipient_name TEXT NOT NULL DEFAULT '',
+ recipient_phone TEXT NOT NULL DEFAULT '',
+ contact_name TEXT NOT NULL DEFAULT '',
+ contact_phone TEXT NOT NULL DEFAULT '',
+ appointment_time TIMESTAMPTZ,
+ remark TEXT NOT NULL DEFAULT '',
+ status TEXT NOT NULL DEFAULT 'created',
+ current_assignment_id TEXT NOT NULL DEFAULT '',
+ current_staff_id UUID,
+ accepted_at TIMESTAMPTZ,
+ departed_at TIMESTAMPTZ,
+ arrived_at TIMESTAMPTZ,
+ service_started_at TIMESTAMPTZ,
+ completed_at TIMESTAMPTZ,
+ pending_acceptance_at TIMESTAMPTZ,
+ accepted_by_user_at TIMESTAMPTZ,
+ reviewed_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ deleted_at TIMESTAMPTZ,
+ deleted_by UUID,
+ CONSTRAINT chk_hss_service_orders_status CHECK (
+ status IN (
+ 'created', 'paid', 'assigned', 'accepted', 'rejected', 'departed', 'arrived',
+ 'in_service', 'completed', 'pending_acceptance', 'accepted_by_user',
+ 'reviewed', 'settled', 'cancelled', 'exception'
+ )
+ )
+);
+
+CREATE TABLE IF NOT EXISTS public.hss_service_assignments (
+ id TEXT PRIMARY KEY,
+ order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE,
+ staff_id UUID NOT NULL REFERENCES public.ml_delivery_staff(id) ON DELETE RESTRICT,
+ station_id UUID REFERENCES public.ml_delivery_stations(id) ON DELETE SET NULL,
+ status TEXT NOT NULL DEFAULT 'assigned',
+ assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ accepted_at TIMESTAMPTZ,
+ rejected_at TIMESTAMPTZ,
+ reject_reason TEXT NOT NULL DEFAULT '',
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ CONSTRAINT chk_hss_service_assignments_status CHECK (
+ status IN ('assigned', 'accepted', 'rejected', 'departed', 'arrived', 'in_service', 'pending_acceptance', 'completed', 'cancelled', 'exception')
+ )
+);
+
+CREATE TABLE IF NOT EXISTS public.hss_service_execution_records (
+ id TEXT PRIMARY KEY,
+ order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE,
+ assignment_id TEXT NOT NULL REFERENCES public.hss_service_assignments(id) ON DELETE CASCADE,
+ checkin_time TIMESTAMPTZ,
+ checkin_latitude DOUBLE PRECISION,
+ checkin_longitude DOUBLE PRECISION,
+ checkin_address TEXT NOT NULL DEFAULT '',
+ service_started_at TIMESTAMPTZ,
+ service_finished_at TIMESTAMPTZ,
+ actual_duration_minutes INTEGER NOT NULL DEFAULT 0,
+ service_items_json JSONB NOT NULL DEFAULT '[]'::jsonb,
+ summary TEXT NOT NULL DEFAULT '',
+ remark TEXT NOT NULL DEFAULT '',
+ track_points_json JSONB NOT NULL DEFAULT '[]'::jsonb,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS public.hss_service_evidence_files (
+ id TEXT PRIMARY KEY,
+ order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE,
+ execution_record_id TEXT REFERENCES public.hss_service_execution_records(id) ON DELETE CASCADE,
+ phase TEXT NOT NULL DEFAULT 'service',
+ file_type TEXT NOT NULL DEFAULT 'image',
+ storage_path TEXT NOT NULL DEFAULT '',
+ file_url TEXT NOT NULL DEFAULT '',
+ latitude DOUBLE PRECISION,
+ longitude DOUBLE PRECISION,
+ captured_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS public.hss_service_order_status_logs (
+ id TEXT PRIMARY KEY,
+ order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE,
+ from_status TEXT NOT NULL DEFAULT '',
+ to_status TEXT NOT NULL,
+ operator_id UUID,
+ operator_role TEXT NOT NULL DEFAULT '',
+ remark TEXT NOT NULL DEFAULT '',
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS public.hss_service_reviews (
+ id TEXT PRIMARY KEY,
+ order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE,
+ user_id UUID NOT NULL,
+ rating INTEGER NOT NULL DEFAULT 5,
+ tags_json JSONB NOT NULL DEFAULT '[]'::jsonb,
+ content TEXT NOT NULL DEFAULT '',
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_orders_user_status
+ ON public.hss_service_orders(user_id, status)
+ WHERE deleted_at IS NULL;
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_orders_staff_status
+ ON public.hss_service_orders(current_staff_id, status)
+ WHERE deleted_at IS NULL;
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_assignments_staff_status
+ ON public.hss_service_assignments(staff_id, status);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_execution_records_order
+ ON public.hss_service_execution_records(order_id);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_evidence_files_order
+ ON public.hss_service_evidence_files(order_id);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_logs_order_created
+ ON public.hss_service_order_status_logs(order_id, created_at DESC);
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_reviews_order
+ ON public.hss_service_reviews(order_id);
+
+CREATE OR REPLACE FUNCTION public.tg_hss_set_updated_at()
+RETURNS trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ NEW.updated_at = now();
+ RETURN NEW;
+END;
+$$;
+
+DROP TRIGGER IF EXISTS trg_hss_service_orders_updated_at ON public.hss_service_orders;
+CREATE TRIGGER trg_hss_service_orders_updated_at
+BEFORE UPDATE ON public.hss_service_orders
+FOR EACH ROW
+EXECUTE FUNCTION public.tg_hss_set_updated_at();
+
+DROP TRIGGER IF EXISTS trg_hss_service_assignments_updated_at ON public.hss_service_assignments;
+CREATE TRIGGER trg_hss_service_assignments_updated_at
+BEFORE UPDATE ON public.hss_service_assignments
+FOR EACH ROW
+EXECUTE FUNCTION public.tg_hss_set_updated_at();
+
+DROP TRIGGER IF EXISTS trg_hss_service_execution_records_updated_at ON public.hss_service_execution_records;
+CREATE TRIGGER trg_hss_service_execution_records_updated_at
+BEFORE UPDATE ON public.hss_service_execution_records
+FOR EACH ROW
+EXECUTE FUNCTION public.tg_hss_set_updated_at();
+
+ALTER TABLE public.hss_service_orders ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.hss_service_assignments ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.hss_service_execution_records ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.hss_service_evidence_files ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.hss_service_order_status_logs ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.hss_service_reviews ENABLE ROW LEVEL SECURITY;
+
+DROP POLICY IF EXISTS hss_service_orders_user_select ON public.hss_service_orders;
+CREATE POLICY hss_service_orders_user_select
+ ON public.hss_service_orders
+ FOR SELECT
+ TO authenticated
+ USING (
+ deleted_at IS NULL AND (
+ user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())
+ OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)
+ )
+ );
+
+DROP POLICY IF EXISTS hss_service_orders_user_insert ON public.hss_service_orders;
+CREATE POLICY hss_service_orders_user_insert
+ ON public.hss_service_orders
+ FOR INSERT
+ TO authenticated
+ WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()));
+
+DROP POLICY IF EXISTS hss_service_orders_user_update ON public.hss_service_orders;
+CREATE POLICY hss_service_orders_user_update
+ ON public.hss_service_orders
+ FOR UPDATE
+ TO authenticated
+ USING (
+ deleted_at IS NULL AND (
+ user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())
+ OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)
+ )
+ )
+ WITH CHECK (
+ deleted_at IS NULL AND (
+ user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())
+ OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)
+ )
+ );
+
+DROP POLICY IF EXISTS hss_service_assignments_staff_select ON public.hss_service_assignments;
+CREATE POLICY hss_service_assignments_staff_select
+ ON public.hss_service_assignments
+ FOR SELECT
+ TO authenticated
+ USING (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_assignments_staff_update ON public.hss_service_assignments;
+CREATE POLICY hss_service_assignments_staff_update
+ ON public.hss_service_assignments
+ FOR UPDATE
+ TO authenticated
+ USING (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL))
+ WITH CHECK (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_assignments_staff_insert ON public.hss_service_assignments;
+CREATE POLICY hss_service_assignments_staff_insert
+ ON public.hss_service_assignments
+ FOR INSERT
+ TO authenticated
+ WITH CHECK (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_execution_records_order_access ON public.hss_service_execution_records;
+CREATE POLICY hss_service_execution_records_order_access
+ ON public.hss_service_execution_records
+ FOR ALL
+ TO authenticated
+ USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL))
+ WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_evidence_files_order_access ON public.hss_service_evidence_files;
+CREATE POLICY hss_service_evidence_files_order_access
+ ON public.hss_service_evidence_files
+ FOR ALL
+ TO authenticated
+ USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL))
+ WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_order_status_logs_order_access ON public.hss_service_order_status_logs;
+CREATE POLICY hss_service_order_status_logs_order_access
+ ON public.hss_service_order_status_logs
+ FOR ALL
+ TO authenticated
+ USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL))
+ WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL));
+
+DROP POLICY IF EXISTS hss_service_reviews_order_access ON public.hss_service_reviews;
+CREATE POLICY hss_service_reviews_order_access
+ ON public.hss_service_reviews
+ FOR ALL
+ TO authenticated
+ USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL))
+ WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL));
+
+COMMENT ON TABLE public.hss_service_orders IS '居家上门服务订单主表';
+COMMENT ON TABLE public.hss_service_assignments IS '居家上门服务派单表';
+COMMENT ON TABLE public.hss_service_execution_records IS '居家上门服务执行记录表';
+COMMENT ON TABLE public.hss_service_evidence_files IS '居家上门服务证据文件表';
+COMMENT ON TABLE public.hss_service_order_status_logs IS '居家上门服务状态日志表';
+COMMENT ON TABLE public.hss_service_reviews IS '居家上门服务评价表';
+
+COMMIT;
\ No newline at end of file
diff --git a/mall_sql/migrations/20260525_consumer_order_soft_delete.sql b/mall_sql/migrations/20260525_consumer_order_soft_delete.sql
new file mode 100644
index 00000000..d8f250e4
--- /dev/null
+++ b/mall_sql/migrations/20260525_consumer_order_soft_delete.sql
@@ -0,0 +1,17 @@
+BEGIN;
+
+ALTER TABLE public.ml_orders
+ ADD COLUMN IF NOT EXISTS cancelled_at TIMESTAMPTZ NULL,
+ ADD COLUMN IF NOT EXISTS consumer_deleted_at TIMESTAMPTZ NULL;
+
+COMMENT ON COLUMN public.ml_orders.cancelled_at IS '订单取消时间,包含用户取消与支付超时取消';
+COMMENT ON COLUMN public.ml_orders.consumer_deleted_at IS '消费者侧软删除时间,仅影响消费者订单列表展示';
+
+CREATE INDEX IF NOT EXISTS idx_ml_orders_consumer_deleted_at
+ON public.ml_orders(user_id, consumer_deleted_at, created_at DESC);
+
+CREATE INDEX IF NOT EXISTS idx_ml_orders_consumer_visible
+ON public.ml_orders(user_id, created_at DESC)
+WHERE consumer_deleted_at IS NULL;
+
+COMMIT;
\ No newline at end of file
diff --git a/mall_sql/migrations/20260525_hss_service_order_payment_fields.sql b/mall_sql/migrations/20260525_hss_service_order_payment_fields.sql
new file mode 100644
index 00000000..874c5544
--- /dev/null
+++ b/mall_sql/migrations/20260525_hss_service_order_payment_fields.sql
@@ -0,0 +1,35 @@
+BEGIN;
+
+ALTER TABLE public.hss_service_orders
+ ADD COLUMN IF NOT EXISTS payment_status SMALLINT NOT NULL DEFAULT 1;
+
+ALTER TABLE public.hss_service_orders
+ ADD COLUMN IF NOT EXISTS pay_expire_at TIMESTAMPTZ;
+
+ALTER TABLE public.hss_service_orders
+ ADD COLUMN IF NOT EXISTS cancel_reason TEXT NOT NULL DEFAULT '';
+
+ALTER TABLE public.hss_service_orders
+ ADD COLUMN IF NOT EXISTS cancelled_at TIMESTAMPTZ;
+
+ALTER TABLE public.hss_service_orders
+ ADD COLUMN IF NOT EXISTS consumer_deleted_at TIMESTAMPTZ;
+
+CREATE INDEX IF NOT EXISTS idx_hss_service_orders_user_visible
+ON public.hss_service_orders(user_id, created_at DESC)
+WHERE consumer_deleted_at IS NULL;
+
+UPDATE public.hss_service_orders
+SET pay_expire_at = created_at + INTERVAL '10 minutes'
+WHERE pay_expire_at IS NULL
+ AND status = 'created'
+ AND payment_status = 1;
+
+ALTER TABLE public.ml_orders
+ ADD COLUMN IF NOT EXISTS pay_expire_at TIMESTAMPTZ,
+ ADD COLUMN IF NOT EXISTS payment_status SMALLINT NOT NULL DEFAULT 1,
+ ADD COLUMN IF NOT EXISTS cancel_reason TEXT NOT NULL DEFAULT '',
+ ADD COLUMN IF NOT EXISTS cancelled_at TIMESTAMPTZ,
+ ADD COLUMN IF NOT EXISTS consumer_deleted_at TIMESTAMPTZ;
+
+COMMIT;
diff --git a/mall_sql/migrations/20260525_order_timeout_status.sql b/mall_sql/migrations/20260525_order_timeout_status.sql
new file mode 100644
index 00000000..b0bd62bc
--- /dev/null
+++ b/mall_sql/migrations/20260525_order_timeout_status.sql
@@ -0,0 +1,77 @@
+BEGIN;
+
+ALTER TABLE public.ml_orders
+ ADD COLUMN IF NOT EXISTS pay_expire_at TIMESTAMPTZ;
+
+ALTER TABLE public.ml_orders
+ DROP CONSTRAINT IF EXISTS chk_ml_order_status;
+
+ALTER TABLE public.ml_orders
+ DROP CONSTRAINT IF EXISTS chk_ml_payment_status;
+
+ALTER TABLE public.ml_orders
+ ADD CONSTRAINT chk_ml_order_status CHECK (order_status IN (1,2,3,4,5,6,7,8));
+
+ALTER TABLE public.ml_orders
+ ADD CONSTRAINT chk_ml_payment_status CHECK (payment_status IN (1,2,3,4,5));
+
+COMMENT ON COLUMN public.ml_orders.pay_expire_at IS '支付截止时间,超过后订单进入已超时';
+
+UPDATE public.ml_orders
+SET pay_expire_at = created_at + INTERVAL '10 minutes'
+WHERE pay_expire_at IS NULL
+ AND order_status = 1
+ AND payment_status = 1;
+
+UPDATE public.ml_orders
+SET order_status = 8,
+ payment_status = 5,
+ cancel_reason = CASE
+ WHEN cancel_reason IS NULL OR cancel_reason = '' THEN '支付超时自动关闭'
+ ELSE cancel_reason
+ END,
+ updated_at = NOW()
+WHERE order_status = 1
+ AND payment_status = 1
+ AND pay_expire_at IS NOT NULL
+ AND pay_expire_at <= NOW();
+
+CREATE INDEX IF NOT EXISTS idx_ml_orders_pay_expire_at
+ON public.ml_orders(pay_expire_at);
+
+DROP VIEW IF EXISTS public.ml_orders_detail_view;
+
+CREATE VIEW public.ml_orders_detail_view AS
+SELECT
+ o.*,
+ u.username as customer_name,
+ u.phone as customer_phone,
+ m.username as merchant_name,
+ s.shop_name,
+ CASE
+ WHEN o.order_status = 1 THEN '待付款'
+ WHEN o.order_status = 2 THEN '待发货'
+ WHEN o.order_status = 3 THEN '待收货'
+ WHEN o.order_status = 4 THEN '已完成'
+ WHEN o.order_status = 5 THEN '已取消'
+ WHEN o.order_status = 6 THEN '退款中'
+ WHEN o.order_status = 7 THEN '已退款'
+ WHEN o.order_status = 8 THEN '已超时'
+ ELSE '未知'
+ END as order_status_name,
+ CASE
+ WHEN o.payment_status = 1 THEN '未付款'
+ WHEN o.payment_status = 2 THEN '已付款'
+ WHEN o.payment_status = 3 THEN '部分退款'
+ WHEN o.payment_status = 4 THEN '全额退款'
+ WHEN o.payment_status = 5 THEN '已关闭'
+ ELSE '未知'
+ END as payment_status_name
+FROM public.ml_orders o
+LEFT JOIN public.ak_users u ON o.user_id = u.id
+LEFT JOIN public.ak_users m ON o.merchant_id = m.id
+LEFT JOIN public.ml_shops s ON o.merchant_id = s.merchant_id;
+
+COMMENT ON VIEW public.ml_orders_detail_view IS '订单详情视图';
+
+COMMIT;
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index 64972729..5b4d93a8 100644
--- a/manifest.json
+++ b/manifest.json
@@ -47,7 +47,13 @@
"optimization": {
"subPackages": true
},
- "lazyCodeLoading": "requiredComponents"
+ "lazyCodeLoading": "requiredComponents",
+ "requiredPrivateInfos": ["getLocation", "chooseLocation"],
+ "permission": {
+ "scope.userLocation": {
+ "desc": "用于获取您的服务地址,方便服务人员上门服务"
+ }
+ }
},
"mp-alipay": {
"appid": "",
diff --git a/pages.consumer.json b/pages.consumer.json
index 60afb4ad..b2ea96e6 100644
--- a/pages.consumer.json
+++ b/pages.consumer.json
@@ -198,7 +198,8 @@
{
"path": "payment",
"style": {
- "navigationBarTitleText": "收银台"
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
}
},
{
@@ -211,8 +212,10 @@
{
"path": "orders",
"style": {
- "navigationBarTitleText": "我的订单",
- "enablePullDownRefresh": true
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f5f5f5"
}
},
{
diff --git a/pages.full.json b/pages.full.json
index 35f253cc..d24edc4c 100644
--- a/pages.full.json
+++ b/pages.full.json
@@ -1,4 +1,4 @@
-{
+{
"pages": [
{
"path": "pages/user/boot",
@@ -13,6 +13,24 @@
"navigationStyle": "custom"
}
},
+ {
+ "path": "pages/address/address-edit",
+ "style": {
+ "navigationBarTitleText": "服务地址"
+ }
+ },
+ {
+ "path": "pages/address/address-list",
+ "style": {
+ "navigationBarTitleText": "选择服务地址"
+ }
+ },
+ {
+ "path": "pages/address/address-map-select",
+ "style": {
+ "navigationBarTitleText": "地图选点"
+ }
+ },
{
"path": "pages/mall/admin/homePage/index",
"style": {
@@ -338,7 +356,8 @@
{
"path": "payment",
"style": {
- "navigationBarTitleText": "收银台"
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
}
},
{
@@ -351,8 +370,10 @@
{
"path": "orders",
"style": {
- "navigationBarTitleText": "我的订单",
- "enablePullDownRefresh": true
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f5f5f5"
}
},
{
diff --git a/pages.json b/pages.json
index 7d0d4a37..27210a93 100644
--- a/pages.json
+++ b/pages.json
@@ -71,13 +71,321 @@
}
}
],
- "subPackages": [],
- "globalStyle": {
- "navigationBarTextStyle": "black",
- "navigationBarTitleText": "delivery",
- "navigationBarBackgroundColor": "#FFFFFF",
- "backgroundColor": "#F3F7F9"
- },
+ "subPackages": [
+ {
+ "root": "pages/mall/consumer",
+ "pages": [
+ {
+ "path": "settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "edit-profile",
+ "style": {
+ "navigationBarTitleText": "编辑资料"
+ }
+ },
+ {
+ "path": "wallet",
+ "style": {
+ "navigationBarTitleText": "我的钱包"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "余额提现"
+ }
+ },
+ {
+ "path": "search",
+ "style": {
+ "navigationBarTitleText": "搜索",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "product-detail",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "channel-detail",
+ "style": {
+ "navigationBarTitleText": "频道详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "shop-detail",
+ "style": {
+ "navigationBarTitleText": "店铺详情"
+ }
+ },
+ {
+ "path": "coupons",
+ "style": {
+ "navigationBarTitleText": "我的优惠券"
+ }
+ },
+ {
+ "path": "favorites",
+ "style": {
+ "navigationBarTitleText": "我的收藏"
+ }
+ },
+ {
+ "path": "footprint",
+ "style": {
+ "navigationBarTitleText": "我的足迹"
+ }
+ },
+ {
+ "path": "address",
+ "style": {
+ "navigationBarTitleText": "地址"
+ }
+ },
+ {
+ "path": "address-list",
+ "style": {
+ "navigationBarTitleText": "收货地址"
+ }
+ },
+ {
+ "path": "address-edit",
+ "style": {
+ "navigationBarTitleText": "编辑地址"
+ }
+ },
+ {
+ "path": "checkout",
+ "style": {
+ "navigationBarTitleText": "确认订单"
+ }
+ },
+ {
+ "path": "payment",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "payment-success",
+ "style": {
+ "navigationBarTitleText": "支付成功",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "orders",
+ "style": {
+ "navigationBarTitleText": "",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f5f5f5"
+ }
+ },
+ {
+ "path": "order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "logistics",
+ "style": {
+ "navigationBarTitleText": "物流详情"
+ }
+ },
+ {
+ "path": "review",
+ "style": {
+ "navigationBarTitleText": "评价晒单"
+ }
+ },
+ {
+ "path": "refund",
+ "style": {
+ "navigationBarTitleText": "退款/售后"
+ }
+ },
+ {
+ "path": "apply-refund",
+ "style": {
+ "navigationBarTitleText": "申请售后"
+ }
+ },
+ {
+ "path": "refund-review",
+ "style": {
+ "navigationBarTitleText": "服务评价"
+ }
+ },
+ {
+ "path": "chat",
+ "style": {
+ "navigationBarTitleText": "客服聊天",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chat_new",
+ "style": {
+ "navigationBarTitleText": "客服聊天(新版)"
+ }
+ },
+ {
+ "path": "subscription/plan-list",
+ "style": {
+ "navigationBarTitleText": "软件订阅"
+ }
+ },
+ {
+ "path": "subscription/plan-detail",
+ "style": {
+ "navigationBarTitleText": "订阅详情"
+ }
+ },
+ {
+ "path": "subscription/subscribe-checkout",
+ "style": {
+ "navigationBarTitleText": "确认订阅"
+ }
+ },
+ {
+ "path": "subscription/my-subscriptions",
+ "style": {
+ "navigationBarTitleText": "我的订阅"
+ }
+ },
+ {
+ "path": "subscription/followed-shops",
+ "style": {
+ "navigationBarTitleText": "关注店铺"
+ }
+ },
+ {
+ "path": "points/index",
+ "style": {
+ "navigationBarTitleText": "积分管理"
+ }
+ },
+ {
+ "path": "points/signin",
+ "style": {
+ "navigationBarTitleText": "签到"
+ }
+ },
+ {
+ "path": "points/exchange",
+ "style": {
+ "navigationBarTitleText": "积分兑换"
+ }
+ },
+ {
+ "path": "points/exchange-records",
+ "style": {
+ "navigationBarTitleText": "兑换记录"
+ }
+ },
+ {
+ "path": "red-packets/index",
+ "style": {
+ "navigationBarTitleText": "我的红包"
+ }
+ },
+ {
+ "path": "bank-cards/index",
+ "style": {
+ "navigationBarTitleText": "银行卡管理"
+ }
+ },
+ {
+ "path": "bank-cards/add",
+ "style": {
+ "navigationBarTitleText": "添加银行卡"
+ }
+ },
+ {
+ "path": "home-service/index",
+ "style": {
+ "navigationBarTitleText": "居家上门服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/apply",
+ "style": {
+ "navigationBarTitleText": "提交服务申请",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/service-detail",
+ "style": {
+ "navigationBarTitleText": "预约服务",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/order-detail",
+ "style": {
+ "navigationBarTitleText": "服务单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "home-service/feedback",
+ "style": {
+ "navigationBarTitleText": "验收反馈",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "bank-cards/verify",
+ "style": {
+ "navigationBarTitleText": "银行卡验证"
+ }
+ },
+ {
+ "path": "balance/index",
+ "style": {
+ "navigationBarTitleText": "余额"
+ }
+ },
+ {
+ "path": "my-reviews",
+ "style": {
+ "navigationBarTitleText": "我的评价"
+ }
+ },
+ {
+ "path": "message-detail",
+ "style": {
+ "navigationBarTitleText": "消息详情"
+ }
+ },
+ {
+ "path": "member/index",
+ "style": {
+ "navigationBarTitleText": "会员中心"
+ }
+ },
+ {
+ "path": "product-reviews",
+ "style": {
+ "navigationBarTitleText": "商品评价"
+ }
+ }
+ ]
+ }
+ ],
"tabBar": {
"color": "#6B7280",
"selectedColor": "#0F766E",
diff --git a/pages/address/address-edit.uvue b/pages/address/address-edit.uvue
new file mode 100644
index 00000000..1f7dcb24
--- /dev/null
+++ b/pages/address/address-edit.uvue
@@ -0,0 +1,636 @@
+
+
+
+
+
+ 当前选择的位置
+ {{ displayLocationTitle }}
+ {{ displayLocationDetail }}
+ 请选择小区、医院、养老院或街道位置
+
+
+
+
+ 联系人
+
+
+
+ 手机号
+
+
+
+
+ 所在位置
+
+ {{ displayLocationTitle }}
+ {{ displayLocationDetail }}
+
+ 请选择小区/医院/养老院/街道
+
+ 重新选择
+
+
+ 自定义地图选点
+ >
+
+
+ 详细门牌号
+
+
+
+ 服务备注
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/address/address-list.uvue b/pages/address/address-list.uvue
new file mode 100644
index 00000000..cf3dc652
--- /dev/null
+++ b/pages/address/address-list.uvue
@@ -0,0 +1,390 @@
+
+
+
+
+
+ 📍
+ 还没有服务地址
+ 新增一个常用服务地址,预约时会更快
+
+
+
+
+
+ {{ item.contactName }}
+ {{ getPhone(item) }}
+ 默认
+ 当前选择
+
+ {{ getLocationTitle(item) }}
+ {{ getFullAddress(item) }}
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/address/address-map-select.uvue b/pages/address/address-map-select.uvue
new file mode 100644
index 00000000..536bf80b
--- /dev/null
+++ b/pages/address/address-map-select.uvue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+ 附近地址
+
+ {{ item.name }}
+ {{ item.address }}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/main/cart-search/cart-search.uvue b/pages/main/cart-search/cart-search.uvue
index 3ff27cc1..7b1baca0 100644
--- a/pages/main/cart-search/cart-search.uvue
+++ b/pages/main/cart-search/cart-search.uvue
@@ -1,35 +1,35 @@
-
+
@@ -44,13 +44,13 @@
- 暂无历史搜索
+ 鏆傛棤鍘嗗彶鎼滅储
@@ -66,10 +66,10 @@
-
+
- 购物车内相关商品
+ 璐墿杞﹀唴鐩稿叧鍟嗗搧
@@ -79,7 +79,7 @@
class="cart-result-card"
>
- ✓
+ 鉁?/text>
@@ -90,7 +90,7 @@
{{ item.name }}
{{ item.spec }}
-
-
-
-
-
-
-
-
- {{ product.name }}
-
- ¥{{ product.price }}
-
- +
-
-
-
-
-
- 正在加载更多...
- 没有更多了
- 上拉加载更多
-
-
+
@@ -280,6 +253,7 @@ import { ref, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { supabaseService, type CartItem as SupabaseCartItem, type Product } from '@/utils/supabaseService.uts'
import { goToLogin } from '@/utils/utils.uts'
+import GuessYouLike from '@/components/mall/GuessYouLike/GuessYouLike.uvue'
type ModalSuccess = { confirm: boolean; cancel: boolean }
@@ -375,6 +349,7 @@ const recommendBottomLocked = ref(false)
const recommendViewportHeight = ref(0)
const pageWindowHeight = ref(0)
const isAndroidApp = ref(false)
+const guessLoadMoreKey = ref(0)
const serviceMockItems = ref([
{
id: 'service-1',
@@ -725,27 +700,6 @@ const mergeRecommendProducts = (oldList: RecommendProduct[], newList: RecommendP
return result
}
-const mockRecommendProducts = (page: number, pageSize: number): RecommendProduct[] => {
- const list: RecommendProduct[] = []
- if (page >= 5) {
- return list
- }
- for (let i = 0; i < pageSize; i++) {
- const index = (page - 1) * pageSize + i + 1
- list.push({
- id: 'mock-recommend-' + index,
- shopId: 'mock-shop-' + index,
- shopName: '商城推荐',
- name: '猜你喜欢 推荐商品 ' + index,
- price: 9.9 + index,
- image: '/static/images/default.png',
- skuId: '',
- merchant_id: ''
- })
- }
- return list
-}
-
const fetchRecommendProducts = async (page: number, pageSize: number): Promise => {
console.log('[cart推荐] fetchRecommendProducts 请求 page=', page, 'pageSize=', pageSize)
const hotResp = await supabaseService.searchProducts('', page, pageSize, 'sales')
@@ -851,13 +805,7 @@ async function loadRecommendProducts(reset: boolean): Promise {
recommendInitialized.value = true
} catch (error) {
console.error('加载推荐商品失败:', error)
- if (reset && recommendProducts.value.length === 0) {
- const mockList = mockRecommendProducts(1, recommendPageSize.value)
- recommendProducts.value = mockList
- recommendPage.value = 2
- recommendHasMore.value = true
- recommendInitialized.value = true
- }
+ recommendHasMore.value = false
} finally {
recommendLoading.value = false
if (!reset && recommendPendingLoad.value && recommendHasMore.value) {
@@ -869,59 +817,13 @@ async function loadRecommendProducts(reset: boolean): Promise {
}
function onRecommendScrollToLower(): void {
- console.log('[cart推荐] scrolltolower 触发 currentCartType=', currentCartType.value, 'initialized=', recommendInitialized.value)
- if (currentCartType.value != 'goods') {
- console.log('[cart推荐] 跳过:当前不是 goods')
- return
+ if (currentCartType.value == 'goods') {
+ guessLoadMoreKey.value = guessLoadMoreKey.value + 1
}
- recommendBottomLocked.value = true
- loadRecommendProducts(false)
}
function onRecommendScroll(event: any): void {
- if (currentCartType.value != 'goods' || recommendLoading.value || !recommendHasMore.value) {
- return
- }
- try {
- const eventObj = toRecommendScrollJson(event)
- let detailObj: UTSJSONObject | null = null
- if (eventObj != null) {
- detailObj = toRecommendScrollJson(eventObj.get('detail'))
- }
- if (detailObj == null) {
- return
- }
-
- const scrollTop = readRecommendScrollMetric(detailObj, 'scrollTop')
- const scrollHeight = readRecommendScrollMetric(detailObj, 'scrollHeight')
- let clientHeight = readRecommendScrollMetric(detailObj, 'clientHeight')
-
- if (clientHeight <= 0) {
- clientHeight = recommendViewportHeight.value
- }
-
- console.log('[cart推荐] scroll事件 scrollTop=', scrollTop, 'scrollHeight=', scrollHeight, 'clientHeight=', clientHeight)
-
- if (scrollHeight <= 0 || clientHeight <= 0) {
- return
- }
-
- const distanceToBottom = scrollHeight - scrollTop - clientHeight
- if (distanceToBottom > 260) {
- recommendBottomLocked.value = false
- }
- if (distanceToBottom <= 180) {
- console.log('[cart推荐] scroll 兜底触底 distanceToBottom=', distanceToBottom)
- if (recommendBottomLocked.value) {
- recommendPendingLoad.value = true
- return
- }
- recommendBottomLocked.value = true
- loadRecommendProducts(false)
- }
- } catch (e) {
- console.error('[cart推荐] 处理推荐滚动失败:', e)
- }
+ return
}
// 加载数据
@@ -981,9 +883,6 @@ const loadCartData = async () => {
console.log('Transformed items count:', transformedItems.length);
cartItems.value = transformedItems
- if (!recommendInitialized.value) {
- await loadRecommendProducts(true)
- }
} catch (error) {
console.error('加载购物车数据失败:', error)
cartItems.value = []
@@ -996,6 +895,10 @@ onShow(() => {
loadCartData()
})
+const handleGuessProductClick = (productId: string) => {
+ navigateToProduct({ id: productId, productId: productId, price: 0 })
+}
+
// 商品操作 - 更新选中状态到Supabase
const toggleSelect = async (itemId: string) => {
// 乐观更新
@@ -2593,4 +2496,3 @@ const goToCheckout = () => {
background-color: transparent;
}
-
diff --git a/pages/main/index.uvue b/pages/main/index.uvue
index dbcc0b87..28d53e36 100644
--- a/pages/main/index.uvue
+++ b/pages/main/index.uvue
@@ -436,68 +436,6 @@ function buildServiceImageText(categoryId: string): string {
return '服'
}
-function buildMockServiceProducts(): Array {
- // TODO: 后续替换为服务首页专用接口,当前仅在真实服务目录为空时兜底。
- return [
- {
- id: 'svc-001',
- title: '基础上门照护',
- subtitle: '协助起居、日常陪护、健康观察',
- categoryId: 'basic_care',
- price: 99,
- unit: '次',
- tags: ['平台认证', '可预约'],
- salesText: '已服务230+',
- imageText: '护',
- coverGradient: getServiceGradient('basic_care'),
- detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-001',
- bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-001&mode=booking'
- },
- {
- id: 'svc-002',
- title: '居家康复指导',
- subtitle: '术后恢复、动作训练、康复评估',
- categoryId: 'rehab',
- price: 129,
- unit: '次',
- tags: ['康复指导', '上门服务'],
- salesText: '已服务180+',
- imageText: '康',
- coverGradient: getServiceGradient('rehab'),
- detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-002',
- bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-002&mode=booking'
- },
- {
- id: 'svc-mock-escort',
- title: '陪诊陪护服务',
- subtitle: '挂号陪同、检查陪同、取药协助',
- categoryId: 'escort',
- price: 168,
- unit: '次',
- tags: ['陪诊服务', '安心陪护'],
- salesText: '已服务320+',
- imageText: '陪',
- coverGradient: getServiceGradient('escort'),
- detailPath: '',
- bookingPath: ''
- },
- {
- id: 'svc-003',
- title: '慢病随访服务',
- subtitle: '血压血糖记录、健康建议、定期回访',
- categoryId: 'chronic',
- price: 79,
- unit: '次',
- tags: ['慢病管理', '健康随访'],
- salesText: '已服务150+',
- imageText: '访',
- coverGradient: getServiceGradient('chronic'),
- detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-003',
- bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-003&mode=booking'
- }
- ]
-}
-
function buildServiceProductsFromCatalog(catalog: Array): Array {
const result: Array = []
for (let i = 0; i < catalog.length; i++) {
@@ -525,14 +463,10 @@ async function loadServiceHomeData(): Promise {
serviceLoading.value = true
try {
const catalog = await fetchHomeServiceCatalog()
- if (catalog.length > 0) {
- allServiceProducts.value = buildServiceProductsFromCatalog(catalog)
- } else {
- allServiceProducts.value = buildMockServiceProducts()
- }
+ allServiceProducts.value = buildServiceProductsFromCatalog(catalog)
} catch (error) {
console.error('加载服务首页数据失败', error)
- allServiceProducts.value = buildMockServiceProducts()
+ allServiceProducts.value = [] as Array
} finally {
serviceLoading.value = false
}
@@ -620,6 +554,8 @@ const hotProducts = ref([])
const recommendedProducts = ref([])
const hotKeywords = ref([])
const defaultLoadLimit: number = 6
+const recommendChannelLoadLimit: number = 16
+const categoryChannelLoadLimit: number = 12
// 屏幕尺寸检测
const isMobile = ref(false)
@@ -922,10 +858,376 @@ function buildSimpleChannelCoverImages(startIndex: number): string[] {
return covers
}
-function buildSimpleCategoryChannels(categoryId: string): SimpleCategoryChannel[] {
- return []
+function getRealProductImage(product: Product): string {
+ if (product.main_image_url != null && product.main_image_url !== '') {
+ return product.main_image_url
+ }
+ if (product.images != null && product.images.length > 0 && product.images[0] !== '') {
+ return product.images[0]
+ }
+ if (product.image_url != null && product.image_url !== '') {
+ return product.image_url
+ }
+ return '/static/images/default.png'
}
+function getRealSalePrice(product: Product): number {
+ return product.base_price ?? product.price ?? 0
+}
+
+
+function getRealMarketPrice(product: Product): number {
+ return product.market_price ?? product.original_price ?? 0
+ }
+
+function toChannelProduct(product: Product, labelPrefix: string): ChannelProduct {
+ const salePrice = getRealSalePrice(product)
+ const marketPrice = getRealMarketPrice(product)
+ const shortName = product.short_title != null && product.short_title !== ''
+ ? product.short_title
+ : (product.name != null && product.name !== '' ? product.name : product.id)
+ return {
+ id: product.id,
+ name: product.name != null && product.name !== '' ? product.name : product.id,
+ shortName,
+ image: getRealProductImage(product),
+ price: salePrice,
+ marketPrice,
+ tag: labelPrefix
+ } as ChannelProduct
+ }
+
+function getProductDiscountScore(product: Product): number {
+ const salePrice = getRealSalePrice(product)
+ const marketPrice = getRealMarketPrice(product)
+ if (marketPrice <= salePrice || marketPrice <= 0) {
+ return 0
+ }
+ const discountValue = marketPrice - salePrice
+ const discountRate = discountValue / marketPrice
+ return discountRate * 100000 + discountValue
+ }
+
+function getProductQualityScore(product: Product): number {
+ let score = 0
+ if (product.is_featured == true) {
+ score = score + 100000
+ }
+ if (product.is_hot == true) {
+ score = score + 50000
+ }
+ score = score + (product.sale_count ?? 0)
+ return score
+ }
+
+function getProductHotScore(product: Product): number {
+ let score = product.sale_count ?? 0
+ if (product.is_hot == true) {
+ score = score + 100000
+ }
+ if (product.is_featured == true) {
+ score = score + 50000
+ }
+ score = score + getProductDiscountScore(product)
+ return score
+ }
+
+function cloneProductArray(source: Array): Array {
+ const result: Array = []
+ for (let i = 0; i < source.length; i++) {
+ result.push(source[i])
+ }
+ return result
+ }
+
+function sortProductsByScoreDesc(source: Array, scoreType: string): Array {
+ const result = cloneProductArray(source)
+ for (let i = 0; i < result.length; i++) {
+ for (let j = i + 1; j < result.length; j++) {
+ let leftScore = 0
+ let rightScore = 0
+ if (scoreType == 'discount') {
+ leftScore = getProductDiscountScore(result[i])
+ rightScore = getProductDiscountScore(result[j])
+ } else if (scoreType == 'quality') {
+ leftScore = getProductQualityScore(result[i])
+ rightScore = getProductQualityScore(result[j])
+ } else {
+ leftScore = getProductHotScore(result[i])
+ rightScore = getProductHotScore(result[j])
+ }
+ if (rightScore > leftScore) {
+ const temp = result[i]
+ result[i] = result[j]
+ result[j] = temp
+ }
+ }
+ }
+ return result
+ }
+
+function sortProductsByPriceAsc(source: Array): Array {
+ const result = cloneProductArray(source)
+ for (let i = 0; i < result.length; i++) {
+ for (let j = i + 1; j < result.length; j++) {
+ const leftPrice = getRealSalePrice(result[i])
+ const rightPrice = getRealSalePrice(result[j])
+ if (rightPrice < leftPrice) {
+ const temp = result[i]
+ result[i] = result[j]
+ result[j] = temp
+ }
+ }
+ }
+ return result
+ }
+
+function filterProductsByMode(source: Array, mode: string): Array {
+ const result: Array = []
+ for (let i = 0; i < source.length; i++) {
+ const item = source[i]
+ const salePrice = getRealSalePrice(item)
+ const marketPrice = getRealMarketPrice(item)
+ if (mode == 'discount' && marketPrice > salePrice) {
+ result.push(item)
+ continue
+ }
+ if (mode == 'quality' && (item.is_featured == true || item.is_hot == true)) {
+ result.push(item)
+ continue
+ }
+ if (mode == 'cheap-9' && salePrice > 0 && salePrice <= 9.9) {
+ result.push(item)
+ continue
+ }
+ if (mode == 'cheap-19' && salePrice > 0 && salePrice <= 19.9) {
+ result.push(item)
+ continue
+ }
+ if (mode == 'live' && (item.is_hot == true || (item.sale_count ?? 0) > 0)) {
+ result.push(item)
+ }
+ }
+ return result
+ }
+
+function mergeUniqueProductLists(first: Array, second: Array, third: Array): Array {
+ const result: Array = []
+ const seenIds: Array = []
+ const sources: Array> = [first, second, third]
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
+ const source = sources[sourceIndex]
+ for (let i = 0; i < source.length; i++) {
+ const item = source[i]
+ const productId = item.id ?? ''
+ if (productId != '' && seenIds.indexOf(productId) != -1) {
+ continue
+ }
+ if (productId != '') {
+ seenIds.push(productId)
+ }
+ result.push(item)
+ }
+ }
+ return result
+ }
+
+function appendChannelProducts(source: Array, result: Array, selectedIds: Array, desiredCount: number, allowRepeat: boolean): void {
+ for (let i = 0; i < source.length; i++) {
+ if (result.length >= desiredCount) {
+ return
+ }
+ const item = source[i]
+ const productId = item.id ?? ''
+ let existsInResult = false
+ for (let j = 0; j < result.length; j++) {
+ if (result[j].id == productId) {
+ existsInResult = true
+ break
+ }
+ }
+ if (existsInResult) {
+ continue
+ }
+ if (!allowRepeat && productId != '' && selectedIds.indexOf(productId) != -1) {
+ continue
+ }
+ result.push(item)
+ if (!allowRepeat && productId != '') {
+ selectedIds.push(productId)
+ }
+ }
+ }
+
+function selectChannelProducts(primary: Array, secondary: Array, fallback: Array, selectedIds: Array, desiredCount: number): Array {
+ const result: Array = []
+ appendChannelProducts(primary, result, selectedIds, desiredCount, false)
+ appendChannelProducts(secondary, result, selectedIds, desiredCount, false)
+ appendChannelProducts(fallback, result, selectedIds, desiredCount, false)
+ appendChannelProducts(primary, result, selectedIds, desiredCount, true)
+ appendChannelProducts(secondary, result, selectedIds, desiredCount, true)
+ appendChannelProducts(fallback, result, selectedIds, desiredCount, true)
+ return result
+ }
+
+function buildChannelFromTemplate(template: MarketingChannel, products: Array, labelPrefix: string): MarketingChannel {
+ const mappedProducts: Array = []
+ for (let i = 0; i < products.length; i++) {
+ mappedProducts.push(toChannelProduct(products[i], labelPrefix))
+ }
+ return {
+ id: template.id,
+ title: template.title,
+ subtitle: template.subtitle,
+ badge: template.badge,
+ themeColor: template.themeColor,
+ bgColor: template.bgColor,
+ routeType: template.routeType,
+ layoutType: template.layoutType,
+ products: mappedProducts,
+ moreProducts: mappedProducts
+ } as MarketingChannel
+ }
+
+function logChannelProducts(channelTitle: string, products: Array): void {
+ for (let i = 0; i < products.length; i++) {
+ const item = products[i]
+ console.log('[home-channel] ' + channelTitle + ' product:', item.id, item.name ?? '', getRealProductImage(item), getRealSalePrice(item), getRealMarketPrice(item))
+ }
+ }
+
+function buildRealRecommendMarketingChannels(products: Array): MarketingChannel[] {
+ console.log('[home-channel] buildRealRecommendMarketingChannels input count:', products.length)
+ const templates = getRecommendMarketingChannels()
+ if (products.length == 0 || templates.length == 0) {
+ console.log('[home-channel] fallback to mock channel data')
+ return templates
+ }
+ const uniqueProducts = dedupeProducts(products)
+ if (uniqueProducts.length == 0) {
+ console.log('[home-channel] fallback to mock channel data')
+ return templates
+ }
+ const selectedIds: Array = []
+ const discountCandidates = sortProductsByScoreDesc(filterProductsByMode(uniqueProducts, 'discount'), 'discount')
+ const qualityCandidates = sortProductsByScoreDesc(filterProductsByMode(uniqueProducts, 'quality'), 'quality')
+ const cheapCandidates = mergeUniqueProductLists(
+ sortProductsByPriceAsc(filterProductsByMode(uniqueProducts, 'cheap-9')),
+ sortProductsByPriceAsc(filterProductsByMode(uniqueProducts, 'cheap-19')),
+ sortProductsByPriceAsc(uniqueProducts)
+ )
+ const liveCandidates = mergeUniqueProductLists(
+ sortProductsByScoreDesc(filterProductsByMode(uniqueProducts, 'live'), 'hot'),
+ sortProductsByScoreDesc(discountCandidates, 'discount'),
+ sortProductsByScoreDesc(uniqueProducts, 'hot')
+ )
+ const hotFallback = sortProductsByScoreDesc(uniqueProducts, 'hot')
+ const cheapFallback = sortProductsByPriceAsc(uniqueProducts)
+
+ const subsidyProducts = selectChannelProducts(discountCandidates, hotFallback, hotFallback, selectedIds, 2)
+ const qualityProducts = selectChannelProducts(qualityCandidates, hotFallback, hotFallback, selectedIds, 2)
+ const cheapProducts = selectChannelProducts(cheapCandidates, cheapFallback, hotFallback, selectedIds, 2)
+ const liveProducts = selectChannelProducts(liveCandidates, discountCandidates, hotFallback, selectedIds, 2)
+
+ logChannelProducts('百亿补贴', subsidyProducts)
+ logChannelProducts('品质生活', qualityProducts)
+ logChannelProducts('9.9包邮', cheapProducts)
+ logChannelProducts('直播低价', liveProducts)
+
+ const mappedChannels: Array = []
+ for (let i = 0; i < templates.length; i++) {
+ const template = templates[i]
+ if (template.id == 'subsidy') {
+ mappedChannels.push(buildChannelFromTemplate(template, subsidyProducts, '补贴价'))
+ continue
+ }
+ if (template.id == 'quality-life') {
+ mappedChannels.push(buildChannelFromTemplate(template, qualityProducts, '实惠'))
+ continue
+ }
+ if (template.id == 'cheap-mail') {
+ const cheapMappedProducts: Array = []
+ for (let j = 0; j < cheapProducts.length; j++) {
+ const cheapProduct = cheapProducts[j]
+ const label = getRealSalePrice(cheapProduct) <= 9.9 ? '9.9包邮' : '特价'
+ cheapMappedProducts.push(toChannelProduct(cheapProduct, label))
+ }
+ mappedChannels.push({
+ id: template.id,
+ title: template.title,
+ subtitle: template.subtitle,
+ badge: template.badge,
+ themeColor: template.themeColor,
+ bgColor: template.bgColor,
+ routeType: template.routeType,
+ layoutType: template.layoutType,
+ products: cheapMappedProducts,
+ moreProducts: cheapMappedProducts
+ } as MarketingChannel)
+ continue
+ }
+ if (template.id == 'live-low-price') {
+ mappedChannels.push(buildChannelFromTemplate(template, liveProducts, '直播价'))
+ continue
+ }
+ mappedChannels.push(template)
+ }
+ return mappedChannels
+ }
+
+function buildSimpleCategoryChannels(categoryId: string, products: Array = []): SimpleCategoryChannel[] {
+ const dedupedProducts = dedupeProducts(products)
+ if (dedupedProducts.length == 0) {
+ return [] as Array
+ }
+ const hotProductsForCategory = sortProductsByScoreDesc(dedupedProducts, 'hot')
+ const qualityProductsForCategory = sortProductsByScoreDesc(dedupedProducts, 'quality')
+ const firstChannelCovers: Array = []
+ const secondChannelCovers: Array = []
+ for (let i = 0; i < hotProductsForCategory.length && firstChannelCovers.length < 2; i++) {
+ firstChannelCovers.push(getRealProductImage(hotProductsForCategory[i]))
+ }
+ for (let i = 0; i < qualityProductsForCategory.length && secondChannelCovers.length < 2; i++) {
+ secondChannelCovers.push(getRealProductImage(qualityProductsForCategory[i]))
+ }
+ while (firstChannelCovers.length < 2) {
+ firstChannelCovers.push('/static/images/default.png')
+ }
+ while (secondChannelCovers.length < 2) {
+ secondChannelCovers.push('/static/images/default.png')
+ }
+ return [
+ {
+ id: categoryId + '-rank',
+ title: '热销榜',
+ subtitle: '真实商品热度精选',
+ routeType: 'rank',
+ icon: '热',
+ coverImages: firstChannelCovers,
+ categoryId
+ } as SimpleCategoryChannel,
+ {
+ id: categoryId + '-quality',
+ title: '品质优选',
+ subtitle: '真实好物口碑推荐',
+ routeType: 'quality',
+ icon: '精',
+ coverImages: secondChannelCovers,
+ categoryId
+ } as SimpleCategoryChannel
+ ]
+ }
+
+async function loadCategoryChannelCards(categoryId: string): Promise {
+ try {
+ const channelResult = await supabaseService.getMedicalMallProductsByCategory(categoryId, 1, categoryChannelLoadLimit)
+ categorySimpleChannels.value = buildSimpleCategoryChannels(categoryId, channelResult.data)
+ } catch (error) {
+ console.error('[home-channel] 加载分类频道卡片失败', categoryId, error)
+ categorySimpleChannels.value = [] as Array
+ }
+ }
+
function buildVisibleRecommendChannels(): MarketingChannel[] {
const source = getRecommendMarketingChannels()
const visible: MarketingChannel[] = []
@@ -937,16 +1239,16 @@ function buildVisibleRecommendChannels(): MarketingChannel[] {
visible.push(channel)
}
return visible
-}
+ }
function applyChannelDisplay(categoryId: string): void {
if (categoryId === 'recommend') {
- marketingChannels.value = buildVisibleRecommendChannels()
- categorySimpleChannels.value = []
+ marketingChannels.value = [] as Array
+ categorySimpleChannels.value = [] as Array
return
}
- marketingChannels.value = []
- categorySimpleChannels.value = buildSimpleCategoryChannels(categoryId)
+ marketingChannels.value = [] as Array
+ categorySimpleChannels.value = [] as Array
}
function buildChannelDetailUrl(channelId: string, routeType: string, categoryId: string): string {
@@ -1440,11 +1742,18 @@ async function loadHotProducts(page: number, limit: number): Promise {
}
}
setHotProducts(products)
+ if (currentFeedCategoryId.value === 'recommend' && page <= 1) {
+ marketingChannels.value = buildRealRecommendMarketingChannels(products)
+ }
hasMore.value = result.hasmore
currentPage.value = page
} catch (error) {
console.error('加载热销商品失败:', error)
hotProducts.value = []
+ if (currentFeedCategoryId.value === 'recommend') {
+ console.log('[home-channel] fallback to mock channel data')
+ marketingChannels.value = buildVisibleRecommendChannels()
+ }
hasMore.value = false
}
}
@@ -1476,14 +1785,18 @@ async function loadCategoryGoods(categoryId: string): Promise {
await syncCategoryLayout(categoryId)
if (categoryId === 'recommend') {
try {
- const result = await supabaseService.getMedicalMallSmartRecommendations(1, defaultLoadLimit)
+ const result = await supabaseService.getMedicalMallSmartRecommendations(1, recommendChannelLoadLimit)
+ console.log('[home-channel] 推荐商品接口返回数量:', result.data.length)
failedProductImageIds.value = []
setHotProducts(result.data)
+ marketingChannels.value = buildRealRecommendMarketingChannels(result.data)
hasMore.value = result.hasmore
currentPage.value = 1
} catch (error) {
console.error('加载热销商品失败:', error)
hotProducts.value = []
+ console.log('[home-channel] fallback to mock channel data')
+ marketingChannels.value = buildVisibleRecommendChannels()
hasMore.value = false
}
} else {
@@ -1492,10 +1805,12 @@ async function loadCategoryGoods(categoryId: string): Promise {
const result = await supabaseService.getMedicalMallProductsByCategory(categoryId, 1, defaultLoadLimit)
failedProductImageIds.value = []
setHotProducts(result.data)
+ await loadCategoryChannelCards(categoryId)
hasMore.value = result.hasmore
} catch (e) {
console.error('分类商品加载失败', e)
hotProducts.value = []
+ categorySimpleChannels.value = [] as Array
hasMore.value = false
} finally {
loading.value = false
@@ -1520,12 +1835,16 @@ async function refreshHomeCategory(item: CategoryItem): Promise {
secondaryCategoryDisplay.value = buildSecondaryCategoryDisplay(item.id)
applyChannelDisplay(item.id)
try {
- const result = await supabaseService.getMedicalMallSmartRecommendations(1, defaultLoadLimit)
+ const result = await supabaseService.getMedicalMallSmartRecommendations(1, recommendChannelLoadLimit)
+ console.log('[home-channel] 推荐商品接口返回数量:', result.data.length)
setHotProducts(result.data)
+ marketingChannels.value = buildRealRecommendMarketingChannels(result.data)
hasMore.value = result.hasmore
} catch (error) {
console.error('加载推荐商品失败:', error)
hotProducts.value = []
+ console.log('[home-channel] fallback to mock channel data')
+ marketingChannels.value = buildVisibleRecommendChannels()
hasMore.value = false
} finally {
loading.value = false
@@ -1545,10 +1864,12 @@ async function refreshHomeCategory(item: CategoryItem): Promise {
try {
const result = await supabaseService.getMedicalMallProductsByCategory(item.id, 1, defaultLoadLimit)
setHotProducts(result.data)
+ await loadCategoryChannelCards(item.id)
hasMore.value = result.hasmore
} catch (error) {
console.error('分类商品加载失败', error)
hotProducts.value = []
+ categorySimpleChannels.value = [] as Array
hasMore.value = false
} finally {
loading.value = false
@@ -1857,7 +2178,8 @@ const switchSort = (sortId: string) => {
}
hasMore.value = true // 重置加载更多状态
// 重新加载热销商品,排序由 Supabase 服务处理
- loadHotProducts(1, defaultLoadLimit)
+ const nextLimit = currentFeedCategoryId.value === 'recommend' ? recommendChannelLoadLimit : defaultLoadLimit
+ loadHotProducts(1, nextLimit)
}
// 切换筛选器
@@ -1909,12 +2231,13 @@ const loadMore = async () => {
showLoadMore.value = true
loading.value = true
try {
+ const pageLimit = currentFeedCategoryId.value === 'recommend' ? recommendChannelLoadLimit : defaultLoadLimit
const nextPage = currentPage.value + 1
const currentCount = hotProducts.value.length
console.log('开始加载更多,当前数量:', currentCount, '页码:', nextPage, '分类:', currentFeedCategoryId.value)
if (currentFeedCategoryId.value === 'recommend') {
- const result = await fetchSortedProductsPage(nextPage, defaultLoadLimit)
+ const result = await fetchSortedProductsPage(nextPage, pageLimit)
const newProducts = result.data
if (newProducts.length == 0) {
@@ -2025,7 +2348,7 @@ const onScan = (): void => {
})
},
fail: (err) => {
- console.error('扫码失败:', err)
+ console.error('扫码失败:', err)
}
})
}
diff --git a/pages/main/profile.uvue b/pages/main/profile.uvue
index 5f00aa03..7aae57dd 100644
--- a/pages/main/profile.uvue
+++ b/pages/main/profile.uvue
@@ -195,29 +195,12 @@
-
-
-
-
-
-
- {{ item.name }}
-
- ¥{{ item.price }}
- {{ item.tag }}
-
-
-
-
-
- 正在加载更多...
- 没有更多了
- 上拉加载更多
-
-
+
@@ -229,6 +212,7 @@
import { UserType } from '@/types/mall-types.uts'
import supabaseService from '@/utils/supabaseService.uts'
import { goToLogin } from '@/utils/utils.uts'
+import GuessYouLike from '@/components/mall/GuessYouLike/GuessYouLike.uvue'
type UserStatsType = {
points: number
@@ -290,6 +274,9 @@ type PendingReceiptGoodsType = {
type ModalSuccessResult = { confirm: boolean; cancel: boolean }
export default {
+ components: {
+ GuessYouLike
+ },
data() {
return {
userInfo: {
@@ -345,6 +332,7 @@ export default {
recommendBottomLocked: false,
recommendViewportHeight: 0,
pageWindowHeight: 0,
+ guessLoadMoreKey: 0,
statusBarHeight: 0,
isAndroidApp: false,
capsuleTop: 0,
@@ -376,7 +364,6 @@ export default {
this.initPage()
this.loadUserProfile()
this.loadOrders()
- this.loadRecommendProducts(true)
// 监听订单更新事件
uni.$on('orderUpdated', this.handleOrderUpdated)
@@ -474,24 +461,6 @@ export default {
return result
},
- mockRecommendProducts(page: number, pageSize: number): Array {
- const list: Array = []
- if (page >= 5) {
- return list
- }
- for (let i: number = 0; i < pageSize; i++) {
- const index = (page - 1) * pageSize + i + 1
- list.push({
- id: 'mock-recommend-' + index,
- name: '猜你喜欢 推荐商品 ' + index,
- image: '/static/images/default.png',
- price: 9.9 + index,
- tag: '已售' + (100 + index) + '+'
- } as RecommendProductType)
- }
- return list
- },
-
async fetchRecommendProducts(page: number, pageSize: number): Promise> {
console.log('[profile推荐] fetchRecommendProducts 请求 page=', page, 'pageSize=', pageSize)
const result = await supabaseService.searchProducts('', page, pageSize, 'sales')
@@ -578,14 +547,7 @@ export default {
this.recommendInitialized = true
} catch (e) {
console.error('加载推荐商品失败:', e)
-
- if (reset && this.recommendProducts.length === 0) {
- const mockList = this.mockRecommendProducts(1, this.recommendPageSize)
- this.recommendProducts = mockList
- this.recommendPage = 2
- this.recommendHasMore = true
- this.recommendInitialized = true
- }
+ this.recommendHasMore = false
} finally {
this.recommendLoading = false
if (!reset && this.recommendPendingLoad && this.recommendHasMore) {
@@ -597,55 +559,20 @@ export default {
},
onRecommendScrollToLower() {
- console.log('[profile推荐] scrolltolower 触发')
- this.recommendBottomLocked = true
- this.loadRecommendProducts(false)
+ this.guessLoadMoreKey = this.guessLoadMoreKey + 1
},
onRecommendScroll(event: any) {
- if (this.recommendLoading || !this.recommendHasMore) {
+ return
+ },
+
+ handleGuessProductClick(productId: string) {
+ if (productId == null || productId === '') {
return
}
- try {
- const eventObj = this.toRecommendScrollJson(event)
- let detailObj: UTSJSONObject | null = null
- if (eventObj != null) {
- detailObj = this.toRecommendScrollJson(eventObj.get('detail'))
- }
- if (detailObj == null) {
- return
- }
-
- const scrollTop = this.readRecommendScrollMetric(detailObj, 'scrollTop')
- const scrollHeight = this.readRecommendScrollMetric(detailObj, 'scrollHeight')
- let clientHeight = this.readRecommendScrollMetric(detailObj, 'clientHeight')
-
- if (clientHeight <= 0) {
- clientHeight = this.recommendViewportHeight
- }
-
- console.log('[profile推荐] scroll事件 scrollTop=', scrollTop, 'scrollHeight=', scrollHeight, 'clientHeight=', clientHeight)
-
- if (scrollHeight <= 0 || clientHeight <= 0) {
- return
- }
-
- const distanceToBottom = scrollHeight - scrollTop - clientHeight
- if (distanceToBottom > 260) {
- this.recommendBottomLocked = false
- }
- if (distanceToBottom <= 180) {
- console.log('[profile推荐] scroll 兜底触底 distanceToBottom=', distanceToBottom)
- if (this.recommendBottomLocked) {
- this.recommendPendingLoad = true
- return
- }
- this.recommendBottomLocked = true
- this.loadRecommendProducts(false)
- }
- } catch (e) {
- console.error('[profile推荐] 处理推荐滚动失败', e)
- }
+ uni.navigateTo({
+ url: `/pages/mall/consumer/product-detail?id=${productId}&productId=${productId}`
+ })
},
resetGuestProfileState() {
@@ -1045,9 +972,6 @@ export default {
const userId = supabaseService.getCurrentUserId()
if (userId == null || userId === '') {
this.resetGuestProfileState()
- if (!this.recommendInitialized) {
- this.loadRecommendProducts(true)
- }
return
}
@@ -1055,9 +979,6 @@ export default {
this.loadUserProfile()
this.loadOrders()
this.updateCouponCount() // 更新优惠券数量
- if (!this.recommendInitialized) {
- this.loadRecommendProducts(true)
- }
},
async updateCouponCount() {
@@ -1545,13 +1466,14 @@ export default {
},
payOrder(order: OrderItemType) {
+ const paymentAmount = order.actual_amount
const userId = supabaseService.getCurrentUserId()
if (userId == null || userId === '') {
- goToLogin(`/pages/mall/consumer/payment?orderId=${order.id}`)
+ goToLogin(`/pages/mall/consumer/payment?orderId=${order.id}&amount=${paymentAmount}`)
return
}
uni.navigateTo({
- url: `/pages/mall/consumer/payment?orderId=${order.id}`
+ url: `/pages/mall/consumer/payment?orderId=${order.id}&amount=${paymentAmount}`
})
},
@@ -2693,4 +2615,3 @@ export default {
}
}
-
diff --git a/pages/mall/consumer/address-edit.uvue b/pages/mall/consumer/address-edit.uvue
index 0caa768e..c18ad70e 100644
--- a/pages/mall/consumer/address-edit.uvue
+++ b/pages/mall/consumer/address-edit.uvue
@@ -17,6 +17,15 @@
›
+
+
+ 获取当前位置
+
+
+ 地图选点
+
+
+ {{ locationHint }}
详细地址
@@ -85,6 +94,9 @@ type Address = {
detail: string
isDefault: boolean
label?: string
+ latitude?: number
+ longitude?: number
+ coordinateType?: string
}
const isEdit = ref(false)
@@ -92,6 +104,9 @@ const addressId = ref('')
const regionString = ref('')
const tags = ['家', '公司', '学校']
const smartInput = ref('')
+const locationHint = ref('')
+const latitude = ref(0)
+const longitude = ref(0)
type AddressForm = {
name: string
@@ -120,6 +135,9 @@ const loadAddress = async (id: string) => {
formData.isDefault = address.is_default
formData.label = address.label ?? ''
regionString.value = `${address.province} ${address.city} ${address.district}`.trim()
+ latitude.value = address.latitude ?? 0
+ longitude.value = address.longitude ?? 0
+ locationHint.value = latitude.value != 0 || longitude.value != 0 ? `已定位:${latitude.value}, ${longitude.value}` : ''
} else {
// 如果Supabase没有找到,尝试从本地存储加载
const storedAddresses = uni.getStorageSync('addresses')
@@ -133,6 +151,9 @@ const loadAddress = async (id: string) => {
formData.isDefault = localAddress.isDefault
formData.label = localAddress.label ?? ''
regionString.value = `${localAddress.province} ${localAddress.city} ${localAddress.district}`.trim()
+ latitude.value = localAddress.latitude ?? 0
+ longitude.value = localAddress.longitude ?? 0
+ locationHint.value = latitude.value != 0 || longitude.value != 0 ? `已定位:${latitude.value}, ${longitude.value}` : ''
}
}
}
@@ -151,6 +172,9 @@ const loadAddress = async (id: string) => {
formData.isDefault = address.isDefault
formData.label = address.label ?? ''
regionString.value = `${address.province} ${address.city} ${address.district}`.trim()
+ latitude.value = address.latitude ?? 0
+ longitude.value = address.longitude ?? 0
+ locationHint.value = latitude.value != 0 || longitude.value != 0 ? `已定位:${latitude.value}, ${longitude.value}` : ''
}
} catch (e) {
console.error('解析本地地址数据失败', e)
@@ -159,6 +183,42 @@ const loadAddress = async (id: string) => {
}
}
+const applyLocation = (latitudeValue: number, longitudeValue: number, addressText: string, locationName: string) => {
+ latitude.value = latitudeValue
+ longitude.value = longitudeValue
+ locationHint.value = `已定位:${latitudeValue}, ${longitudeValue}`
+ if (addressText != '') {
+ regionString.value = addressText
+ }
+ if (locationName != '' && formData.detail == '') {
+ formData.detail = locationName
+ }
+}
+
+const fillCurrentLocation = () => {
+ uni.getLocation({
+ type: 'gcj02',
+ success: (res) => {
+ applyLocation(res.latitude, res.longitude, regionString.value, '')
+ uni.showToast({ title: '已获取当前位置', icon: 'success' })
+ },
+ fail: () => {
+ uni.showToast({ title: '定位失败,请手动输入地址', icon: 'none' })
+ }
+ })
+}
+
+const pickLocation = () => {
+ uni.chooseLocation({
+ success: (res) => {
+ applyLocation(res.latitude, res.longitude, res.address ?? '', res.name ?? '')
+ },
+ fail: () => {
+ uni.showToast({ title: '当前环境不支持地图选点,可手动输入', icon: 'none' })
+ }
+ })
+}
+
onLoad((options) => {
if (options == null) return
const optionsObj = options as UTSJSONObject
@@ -216,7 +276,10 @@ const saveAddress = async () => {
detail_address: formData.detail,
postal_code: '', // 如果需要可以添加邮政编码字段
is_default: formData.isDefault,
- label: formData.label
+ label: formData.label,
+ latitude: latitude.value,
+ longitude: longitude.value,
+ coordinate_type: 'gcj02'
} as AddAddressParams
let success = false
@@ -231,8 +294,11 @@ const saveAddress = async () => {
district: district,
detail_address: formData.detail,
postal_code: '',
- is_default: formData.isDefault,
- label: formData.label
+ is_default: formData.isDefault,
+ label: formData.label,
+ latitude: latitude.value,
+ longitude: longitude.value,
+ coordinate_type: 'gcj02'
} as UpdateAddressParams
success = await supabaseService.updateAddress(addressId.value, updateData)
} else {
@@ -271,7 +337,10 @@ const saveAddress = async () => {
district: district,
detail: formData.detail,
isDefault: formData.isDefault,
- label: formData.label
+ label: formData.label,
+ latitude: latitude.value,
+ longitude: longitude.value,
+ coordinateType: 'gcj02'
}
}
} else {
@@ -284,7 +353,10 @@ const saveAddress = async () => {
district: district,
detail: formData.detail,
isDefault: formData.isDefault,
- label: formData.label
+ label: formData.label,
+ latitude: latitude.value,
+ longitude: longitude.value,
+ coordinateType: 'gcj02'
}
addresses.push(newAddress)
}
@@ -437,6 +509,38 @@ const deleteAddress = () => {
border-radius: 16px; /* 详细地址区域也增加圆角 */
}
+.location-action-row {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 12px;
+}
+
+.location-action-btn {
+ flex: 1;
+ min-height: 40px;
+ justify-content: center;
+ align-items: center;
+ background-color: #f1f5f9;
+ border-radius: 20px;
+ margin-right: 10px;
+}
+
+.location-action-btn:last-child {
+ margin-right: 0;
+}
+
+.location-action-text {
+ font-size: 13px;
+ color: #2563eb;
+}
+
+.location-hint {
+ display: block;
+ margin-bottom: 12px;
+ font-size: 12px;
+ color: #64748b;
+}
+
.detail-item .label {
margin-bottom: 8px;
}
diff --git a/pages/mall/consumer/address-list.uvue b/pages/mall/consumer/address-list.uvue
index 69ba5c43..846f68b6 100644
--- a/pages/mall/consumer/address-list.uvue
+++ b/pages/mall/consumer/address-list.uvue
@@ -48,6 +48,9 @@ type Address = {
detail: string
isDefault: boolean
label?: string
+ latitude?: number
+ longitude?: number
+ coordinateType?: string
}
const addresses = ref([])
@@ -71,7 +74,10 @@ const loadAddresses = async () => {
district: item.district,
detail: item.detail_address,
isDefault: item.is_default,
- label: ''
+ label: '',
+ latitude: item.latitude ?? 0,
+ longitude: item.longitude ?? 0,
+ coordinateType: item.coordinate_type ?? 'gcj02'
} as Address
transformedAddresses.push(addr)
}
@@ -99,8 +105,8 @@ const loadAddresses = async () => {
onLoad((options) => {
if (options == null) return
- const optionsObj = options as UTSJSONObject
- if ((optionsObj.getString('selectMode') ?? '') == 'true') {
+ const selectMode = options['selectMode']
+ if (selectMode != null && String(selectMode) == 'true') {
selectionMode.value = true
}
})
@@ -166,12 +172,23 @@ const selectAddress = (item: Address) => {
if (selectionMode.value) {
uni.$emit('addressSelected', {
id: item.id,
+ addressId: item.id,
+ userId: '',
recipient_name: item.name,
+ contactName: item.name,
phone: item.phone,
+ contactPhone: item.phone,
province: item.province,
city: item.city,
district: item.district,
detail: item.detail,
+ addressDetail: item.detail,
+ houseNumber: item.detail,
+ fullAddress: getFullAddress(item),
+ remark: item.label ?? '',
+ latitude: item.latitude ?? 0,
+ longitude: item.longitude ?? 0,
+ coordinateType: item.coordinateType ?? 'gcj02',
is_default: item.isDefault
})
uni.navigateBack()
diff --git a/pages/mall/consumer/home-service/apply.uvue b/pages/mall/consumer/home-service/apply.uvue
index f096e76a..2aae131d 100644
--- a/pages/mall/consumer/home-service/apply.uvue
+++ b/pages/mall/consumer/home-service/apply.uvue
@@ -149,6 +149,13 @@ async function loadData() {
bookingDays.value = getBookingDayOptions()
bookingSlots.value = getBookingTimeSlots()
services.value = await fetchHomeServiceCatalog()
+ if (services.value.length > 0) {
+ selectService(services.value[0].id, services.value[0].name)
+ return
+ }
+ selectedServiceId.value = ''
+ form.serviceId = ''
+ form.serviceName = ''
}
function selectService(serviceId: string, serviceName: string) {
@@ -192,6 +199,10 @@ function selectSlot(slotId: string, available: boolean) {
}
async function submitApplication() {
+ if (form.serviceId == '' || form.serviceName == '') {
+ uni.showToast({ title: '当前没有可预约的服务项目', icon: 'none' })
+ return
+ }
if (form.applicantName == '' || form.elderName == '' || form.phone == '' || form.address == '' || form.preferredTime == '') {
uni.showToast({ title: '请补全申请信息', icon: 'none' })
return
@@ -200,6 +211,10 @@ async function submitApplication() {
const parsedAge = parseInt(ageText.value)
form.age = isNaN(parsedAge) ? 0 : parsedAge
const created = await createHomeServiceApplication(form)
+ if (created == null) {
+ uni.showToast({ title: '申请提交失败,请检查登录和预约信息', icon: 'none' })
+ return
+ }
uni.showToast({ title: '申请已提交', icon: 'success' })
uni.navigateTo({ url: '/pages/mall/consumer/home-service/order-detail?id=' + created.id })
}
diff --git a/pages/mall/consumer/home-service/feedback.uvue b/pages/mall/consumer/home-service/feedback.uvue
index 8f671cd8..3fa114a7 100644
--- a/pages/mall/consumer/home-service/feedback.uvue
+++ b/pages/mall/consumer/home-service/feedback.uvue
@@ -53,6 +53,8 @@ import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uv
import ServicePanel from '@/components/homeService/ServicePanel.uvue'
import { fetchConsumerAcceptanceDetail, submitConsumerAcceptance } from '@/services/homeServiceService.uts'
import { HomeServiceAcceptanceType } from '@/types/home-service.uts'
+import { getCurrentUser, getCurrentUserId } from '@/utils/store.uts'
+import { goToLogin } from '@/utils/utils.uts'
const caseId = ref('')
const detail = ref(null)
@@ -62,17 +64,31 @@ const selectedTags = ref>([])
const scores = [1, 2, 3, 4, 5]
const allTags = ['准时上门', '沟通清楚', '动作规范', '记录完整', '需进一步整改']
+async function ensureLogin(): Promise {
+ const user = await getCurrentUser()
+ if (user == null || getCurrentUserId() == '') {
+ goToLogin('/pages/mall/consumer/home-service/feedback?id=' + caseId.value)
+ return false
+ }
+ return true
+}
+
onLoad((options) => {
const id = options['id']
if (id != null) {
caseId.value = id as string
- fetchConsumerAcceptanceDetail(caseId.value).then((res) => {
- if (res != null) {
- detail.value = res
- rating.value = res.rating
- feedback.value = res.feedback
- selectedTags.value = res.tags.slice(0)
+ ensureLogin().then((ok) => {
+ if (!ok) {
+ return
}
+ fetchConsumerAcceptanceDetail(caseId.value).then((res) => {
+ if (res != null) {
+ detail.value = res
+ rating.value = res.rating
+ feedback.value = res.feedback
+ selectedTags.value = res.tags.slice(0)
+ }
+ })
})
}
})
@@ -87,6 +103,9 @@ function toggleTag(tag: string) {
}
async function submitResult(approved: boolean) {
+ if (!(await ensureLogin())) {
+ return
+ }
if (caseId.value == '' || feedback.value == '') {
uni.showToast({ title: '请填写反馈说明', icon: 'none' })
return
diff --git a/pages/mall/consumer/home-service/index.uvue b/pages/mall/consumer/home-service/index.uvue
index 26fa610e..422e589f 100644
--- a/pages/mall/consumer/home-service/index.uvue
+++ b/pages/mall/consumer/home-service/index.uvue
@@ -89,6 +89,8 @@ import ServicePanel from '@/components/homeService/ServicePanel.uvue'
import ServiceStatusTag from '@/components/homeService/ServiceStatusTag.uvue'
import { fetchConsumerHomeServiceCases, fetchHomeServiceCatalog } from '@/services/homeServiceService.uts'
import { HomeServiceCatalogType, HomeServiceCaseType } from '@/types/home-service.uts'
+import { getCurrentUser, getCurrentUserId } from '@/utils/store.uts'
+import { goToLogin } from '@/utils/utils.uts'
import {
HomeServiceCategoryType,
HomeServiceItemType,
@@ -125,9 +127,23 @@ async function loadData() {
categoryGrid.value = getHomeServiceCategories()
promoCards.value = getHomeServicePromoCards()
services.value = await fetchHomeServiceCatalog()
+ const user = await getCurrentUser()
+ if (user == null || getCurrentUserId() == '') {
+ cases.value = [] as Array
+ return
+ }
cases.value = await fetchConsumerHomeServiceCases()
}
+async function ensureLogin(redirectUrl: string): Promise {
+ const user = await getCurrentUser()
+ if (user == null || getCurrentUserId() == '') {
+ goToLogin(redirectUrl)
+ return false
+ }
+ return true
+}
+
function switchCategory(categoryId: string) {
selectedCategory.value = categoryId
}
@@ -141,11 +157,21 @@ function goDetail(serviceId: string) {
}
function goBooking(serviceId: string) {
- uni.navigateTo({ url: '/pages/mall/consumer/home-service/service-detail?id=' + serviceId + '&mode=booking' })
+ ensureLogin('/pages/mall/consumer/home-service/service-detail?id=' + serviceId + '&mode=booking').then((ok) => {
+ if (!ok) {
+ return
+ }
+ uni.navigateTo({ url: '/pages/mall/consumer/home-service/service-detail?id=' + serviceId + '&mode=booking' })
+ })
}
function goOrderDetail(caseId: string) {
- uni.navigateTo({ url: '/pages/mall/consumer/home-service/order-detail?id=' + caseId })
+ ensureLogin('/pages/mall/consumer/home-service/order-detail?id=' + caseId).then((ok) => {
+ if (!ok) {
+ return
+ }
+ uni.navigateTo({ url: '/pages/mall/consumer/home-service/order-detail?id=' + caseId })
+ })
}
function goDetailByName(serviceName: string) {
diff --git a/pages/mall/consumer/home-service/order-detail.uvue b/pages/mall/consumer/home-service/order-detail.uvue
index 5297edb4..ca58157b 100644
--- a/pages/mall/consumer/home-service/order-detail.uvue
+++ b/pages/mall/consumer/home-service/order-detail.uvue
@@ -59,7 +59,17 @@
-
+
+
@@ -82,14 +92,29 @@ import ServiceStatusTag from '@/components/homeService/ServiceStatusTag.uvue'
import ServiceTimeline from '@/components/homeService/ServiceTimeline.uvue'
import { fetchConsumerHomeServiceCaseDetail } from '@/services/homeServiceService.uts'
import { HomeServiceCaseType } from '@/types/home-service.uts'
+import { getCurrentUser, getCurrentUserId } from '@/utils/store.uts'
+import { goToLogin } from '@/utils/utils.uts'
const caseId = ref('')
const detail = ref(null)
+async function ensureLogin(): Promise {
+ const user = await getCurrentUser()
+ if (user == null || getCurrentUserId() == '') {
+ goToLogin('/pages/mall/consumer/home-service/order-detail?id=' + caseId.value)
+ return false
+ }
+ return true
+}
+
async function loadData() {
if (caseId.value == '') {
return
}
+ if (!(await ensureLogin())) {
+ detail.value = null
+ return
+ }
detail.value = await fetchConsumerHomeServiceCaseDetail(caseId.value)
}
@@ -97,7 +122,12 @@ function goFeedback() {
if (caseId.value == '') {
return
}
- uni.navigateTo({ url: '/pages/mall/consumer/home-service/feedback?id=' + caseId.value })
+ ensureLogin().then((ok) => {
+ if (!ok) {
+ return
+ }
+ uni.navigateTo({ url: '/pages/mall/consumer/home-service/feedback?id=' + caseId.value })
+ })
}
function bookAgain() {
diff --git a/pages/mall/consumer/home-service/service-detail.uvue b/pages/mall/consumer/home-service/service-detail.uvue
index 4b56902b..58044650 100644
--- a/pages/mall/consumer/home-service/service-detail.uvue
+++ b/pages/mall/consumer/home-service/service-detail.uvue
@@ -28,12 +28,20 @@
-
- 上门地址
-
- {{ addressLineText }}
- 点击更换
+
+
+ 服务地址
+
+
+ {{ selectedAddress.contactName }}
+ {{ getSelectedAddressPhone() }}
+
+ {{ selectedAddress.fullAddress }}
+
+
+ 请选择服务地址
+ >
@@ -271,11 +279,13 @@
\ No newline at end of file
+
diff --git a/pages/mall/consumer/payment-success.uvue b/pages/mall/consumer/payment-success.uvue
index be2ac192..6fc97d7b 100644
--- a/pages/mall/consumer/payment-success.uvue
+++ b/pages/mall/consumer/payment-success.uvue
@@ -28,6 +28,27 @@ const orderId = ref('')
const orderNo = ref('')
const amount = ref(0)
+const getOptionString = (options: UTSJSONObject, key: string): string => {
+ try {
+ const value = options.getString(key)
+ if (value != null) {
+ return value
+ }
+ } catch (e) {
+ }
+
+ try {
+ const normalized = JSON.parse(JSON.stringify(options)) as UTSJSONObject
+ const value = normalized.getString(key)
+ if (value != null) {
+ return value
+ }
+ } catch (e) {
+ }
+
+ return ''
+}
+
// 定义 loadOrderInfo 函数(必须在 onMounted 之前)
const loadOrderInfo = async () => {
try {
@@ -60,12 +81,12 @@ onLoad((options) => {
if (options == null) return
const optionsObj = options as UTSJSONObject
- const orderIdValue = optionsObj.getString('orderId') ?? ''
+ const orderIdValue = getOptionString(optionsObj, 'orderId')
if (orderIdValue != '') {
orderId.value = orderIdValue
orderNo.value = orderIdValue
- const amountValue = optionsObj.getString('amount') ?? ''
+ const amountValue = getOptionString(optionsObj, 'amount')
if (amountValue != '') {
const amountStr = amountValue
console.log('[payment-success] amountStr:', amountStr)
diff --git a/pages/mall/consumer/payment.uvue b/pages/mall/consumer/payment.uvue
index 3def38e1..db6b3de0 100644
--- a/pages/mall/consumer/payment.uvue
+++ b/pages/mall/consumer/payment.uvue
@@ -1,116 +1,277 @@
-
-
-
+
+
+
-
-
-
- 需支付:
- ¥{{ amount.toFixed(2) }}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ reason.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.key-text-popup {
+ font-size: 22px;
+ font-weight: 500;
+}
+
+@keyframes slideUp {
+ from {
+ transform: translateY(100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
diff --git a/pages/mall/consumer/search.uvue b/pages/mall/consumer/search.uvue
index a1b5d223..661049dc 100644
--- a/pages/mall/consumer/search.uvue
+++ b/pages/mall/consumer/search.uvue
@@ -1,8 +1,8 @@
-
+
- ‹
+ <
@@ -12,7 +12,7 @@
:value="searchKeyword"
@input="onInput"
@confirm="onSearch"
- :placeholder="(placeholderKeyword != null && placeholderKeyword !== '' && searchKeyword === '') ? placeholderKeyword : '请输入商品名称、店铺'"
+ :placeholder="(placeholderKeyword != null && placeholderKeyword !== '' && searchKeyword === '') ? placeholderKeyword : '请输入商品名称、店铺'"
placeholder-class="jd-placeholder"
:focus="autoFocus"
confirm-type="search"
@@ -23,7 +23,7 @@
- 📷
+ 拍
@@ -32,23 +32,25 @@
-
+
- ⚠️
- 加载服务器超时
+ !
+ 加载服务超时
请点击屏幕重试
-
+
-
+ v-else
+ class="main-content"
+ direction="vertical"
+ :show-scrollbar="false"
+ :lower-threshold="120"
+ @scrolltolower="handleMainScrollToLower"
+ >
+
@@ -97,51 +99,21 @@
>
{{ index + 1 }}
{{ item.keyword }}
- 🔥
+ 热
-
+
-
-
-
-
-
-
- {{ tag }}
-
- {{ item.name }}
- {{ item.highlight }}
-
- {{ tag }}
-
-
-
- ¥{{ item.price }}
- ¥{{ item.marketPrice }}
-
-
- +
-
-
- {{ item.salesText }}
-
-
+
+
-
@@ -152,7 +124,7 @@
class="suggestion-item"
@click="selectSuggestion(suggestion)"
>
- 🔍
+ 搜
{{ suggestion }}
@@ -176,7 +148,7 @@
{{ shop.name }}
- 共{{ shop.productCount }}件商品
+ 共 {{ shop.productCount }} 件商品
@@ -195,13 +167,13 @@
class="filter-tab"
:class="{ active: activeSort === 'sales' }"
@click="switchSort('sales')"
- >销量
+ >销量
- 价格 {{ activeSort === 'price' ? (priceSortAsc ? '↑' : '↓') : '' }}
+ 价格 {{ activeSort === 'price' ? (priceSortAsc ? '↑' : '↓') : '' }}
@@ -235,14 +207,14 @@
-
+
- 🤔
+ !
未找到相关商品
换个关键词试试吧
-
+
加载中...
@@ -252,41 +224,14 @@
--- 到底了 ---
-
-
-
-
-
-
- {{ tag }}
-
- {{ item.name }}
- {{ item.highlight }}
-
- {{ tag }}
-
-
-
- ¥{{ item.price }}
- ¥{{ item.marketPrice }}
-
-
- +
-
-
- {{ item.salesText }}
-
-
+
+
@@ -302,6 +247,7 @@ import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import { supabaseService } from '@/utils/supabaseService.uts'
import type { Product } from '@/utils/supabaseService.uts'
import { goToLogin } from '@/utils/utils.uts'
+import GuessYouLike from '@/components/mall/GuessYouLike/GuessYouLike.uvue'
// 状态定义
const statusBarHeight = ref(0)
@@ -316,6 +262,7 @@ const loading = ref(false)
const hasMore = ref(true)
const isError = ref(false)
const autoFocus = ref(true)
+const guessLoadMoreKey = ref(0)
const navbarStyle = computed(() => {
const top = navBarTop.value > 0 ? navBarTop.value : statusBarHeight.value
@@ -330,7 +277,6 @@ const handleCameraSearch = () => {
const activeSort = ref('default') // 当前排序方式: default, sales, price
const priceSortAsc = ref(false) // 价格排序是否为升序
-
type HotSearchItemType = {
keyword: string
hot: boolean
@@ -436,21 +382,21 @@ const guessList = ref>([])
const allGuessItems = ref>([])
const DEFAULT_HOT_KEYWORDS: Array = [
- '大疆neo2',
+ '澶х枂neo2',
'iPhone 15 Pro',
'Nike Air Max 270',
- '厨具',
- '老干妈',
- '钢化膜',
- '手机壳',
- '零食坚果',
- '新鲜水果',
- '液态硅胶壳',
- '充电宝',
- '蓝牙耳机'
+ '厨具',
+ '老干妈',
+ '钢化膜',
+ '手机壳',
+ '零食坚果',
+ '新鲜水果',
+ '液态硅胶壳',
+ '充电宝',
+ '蓝牙耳机'
]
-// 推荐商品区(用于 “猜你喜欢/推荐商品”)
+// 推荐商品区
const recommendList = ref>([])
const recommendPool = ref>([])
const recommendPage = ref(0)
@@ -459,6 +405,16 @@ const recommendAppendSize = ref(20)
const loadingRecommend = ref(false)
const searchResults = ref>([])
const searchShopResults = ref>([])
+const searchResultProductIds = computed((): Array => {
+ const ids: Array = []
+ for (let i = 0; i < searchResults.value.length; i++) {
+ const id = searchResults.value[i].id
+ if (id !== '' && ids.indexOf(id) < 0) {
+ ids.push(id)
+ }
+ }
+ return ids
+})
const SEARCH_HISTORY_KEY = 'consumer_search_history'
@@ -504,8 +460,8 @@ const MAX_EXPANDED_COUNT = 24
const clearHistory = () => {
uni.showModal({
- title: '提示',
- content: '确定清空搜索历史吗?',
+ title: '提示',
+ content: '确定清空搜索历史吗?',
success: (res) => {
if (res.confirm) {
searchHistory.value = []
@@ -525,8 +481,6 @@ const deleteHistoryItem = (index: number) => {
const toggleHistoryEdit = () => { isEditMode.value = !isEditMode.value }
const toggleHistoryExpanded = () => { historyExpanded.value = !historyExpanded.value }
-import { computed } from 'vue'
-
const visibleHistory = computed(() => {
if (historyExpanded.value) {
const maxLen = searchHistory.value.length < MAX_EXPANDED_COUNT ? searchHistory.value.length : MAX_EXPANDED_COUNT
@@ -577,7 +531,7 @@ const loadData = async (): Promise => {
const hotResult = await supabaseService.getHotProducts(30)
hotProducts = hotResult as Product[]
} catch (hotError) {
- console.error('获取热销商品失败,使用空列表:', hotError)
+ console.error('获取热销商品失败,使用空列表:', hotError)
hotProducts = []
}
@@ -656,7 +610,7 @@ const loadData = async (): Promise => {
}
const retryLoad = () => {
- uni.showLoading({ title: '重新加载中' })
+ uni.showLoading({ title: '重新加载中...' })
setTimeout(() => {
uni.hideLoading()
loadData()
@@ -774,7 +728,7 @@ const performSearch = async (): Promise => {
image: p.main_image_url ?? '/static/default.jpg',
price: p.base_price ?? 0,
marketPrice: formatMarketPriceText(p),
- specification: p.specification ?? '标准规格',
+ specification: p.specification ?? '标准规格',
tag: tag,
sales: p.sale_count ?? 0,
salesText: formatSalesText(p),
@@ -848,7 +802,7 @@ const initPage = () => {
}
}
} catch (e) {
- console.error('初始化失败', e)
+ console.error('初始化失败:', e)
isError.value = true
}
}
@@ -891,16 +845,16 @@ const onSearch = () => {
}
if (effective === '') return
addToHistory(effective)
- // 如果搜索词来自 placeholder,确保输入框仍为空但执行搜索
+ // 如果搜索词来自 placeholder,保持输入框为空但执行搜索
if (userInput === '') {
- // 保持 searchKeyword 为空 but perform search with effective
+ // 保持 searchKeyword 为空,但使用 effective 搜索
searchKeyword.value = ''
}
- // 将 searchKeyword 临时设置为 effective 以便 performSearch 使用(performSearch 使用 searchKeyword)
+ // 临时将 searchKeyword 设置为 effective,供 performSearch 使用
const prev = searchKeyword.value
searchKeyword.value = effective
performSearch()
- // 恢复输入框为空状态(如果用户未输入)
+ // 如果用户没有手动输入,则恢复为空
if (userInput === '') searchKeyword.value = prev
}
@@ -934,12 +888,12 @@ const switchSort = (type: string) => {
priceSortAsc.value = !priceSortAsc.value
} else {
activeSort.value = 'price'
- priceSortAsc.value = true // 默认升序
+ priceSortAsc.value = true // 默认升序
}
} else {
activeSort.value = type
}
- // 重新执行搜索以获取正确排序的数据
+ // 重新搜索以获取正确排序的数据
performSearch()
}
@@ -984,7 +938,7 @@ const loadMore = async (): Promise => {
image: p.main_image_url ?? '/static/default.jpg',
price: p.base_price ?? 0,
marketPrice: formatMarketPriceText(p),
- specification: p.specification ?? '标准规格',
+ specification: p.specification ?? '标准规格',
tag: tag,
sales: p.sale_count ?? 0,
salesText: formatSalesText(p),
@@ -1004,67 +958,26 @@ const loadMore = async (): Promise => {
}
}
-async function loadRecommendGoods(appendSize: number): Promise {
- if (loadingRecommend.value) return
- loadingRecommend.value = true
- try {
- // 如果后端支持分页接口,可在此调用;当前使用本地 pool
- if (appendSize == null || appendSize <= 0) {
- // 不追加时确保至少保持初始量
- recommendList.value = recommendPool.value.slice(0, recommendInitialSize.value)
- recommendPage.value = 1
- } else {
- const startIndex = recommendList.value.length
- const endIndex = startIndex + appendSize
- const slice = recommendPool.value.slice(startIndex, endIndex)
- // 如果池不够,尝试循环或留空
- if (slice.length === 0) {
- // 将池随机打乱并继续追加(循环播放)
- const arr = recommendPool.value.slice()
- for (let i = arr.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1))
- const t = arr[i]; arr[i] = arr[j]; arr[j] = t
- }
- recommendPool.value = arr
- const more = recommendPool.value.slice(0, appendSize)
- recommendList.value = recommendList.value.concat(more)
- } else {
- recommendList.value = recommendList.value.concat(slice)
- }
- recommendPage.value += 1
- }
- } catch (e) {
- console.error('加载推荐商品失败', e)
- } finally {
- loadingRecommend.value = false
- }
-}
-
-function refreshGuessList(): void {
- loadRecommendGoods(recommendAppendSize.value)
-}
-
function handleReachBottom(): void {
if (showResults.value === true) {
- loadMore()
+ if (hasMore.value) {
+ loadMore()
+ return
+ }
+ guessLoadMoreKey.value = guessLoadMoreKey.value + 1
return
}
- loadRecommendGoods(recommendAppendSize.value)
+ guessLoadMoreKey.value = guessLoadMoreKey.value + 1
+}
+
+function handleMainScrollToLower(): void {
+ handleReachBottom()
}
onReachBottom(() => {
handleReachBottom()
})
-const viewProductDetail = (item: SearchResultType | GuessItemType) => {
- const id = (item as GuessItemType).id
- const price = (item as GuessItemType).price
- const name = (item as GuessItemType).name
- uni.navigateTo({
- url: `/pages/mall/consumer/product-detail?productId=${id}&price=${price}&name=${encodeURIComponent(name)}`
- })
-}
-
const viewShopDetail = (shop: ShopResultType) => {
uni.navigateTo({
url: `/pages/mall/consumer/shop-detail?id=${shop.id}`
@@ -1077,40 +990,40 @@ const addToCart = async (product: SearchResultType | GuessItemType) => {
goToLogin('/pages/mall/consumer/search')
return
}
- uni.showLoading({ title: '检查商品...' })
+ uni.showLoading({ title: '检查商品...' })
try {
- // 统一转换为 UTSJSONObject 访问属性
- const prodObj = JSON.parse(JSON.stringify(product)) as UTSJSONObject
+ // 统一转换为 UTSJSONObject 访问属性
+ const prodObj = JSON.parse(JSON.stringify(product)) as UTSJSONObject
const productId = prodObj.getString('id') ?? ''
const merchantId = prodObj.getString('merchant_id') ?? ''
- // 检查商品是否有SKU
+ // 检查商品是否有 SKU
const skus = await supabaseService.getProductSkus(productId)
uni.hideLoading()
if (skus.length > 0) {
- // 有规格,提示并跳转到商品详情页选择规格
- uni.showToast({ title: '请选择规格', icon: 'none' })
+ // 有规格时跳商品详情选择规格
+ uni.showToast({ title: '请选择规格', icon: 'none' })
setTimeout(() => {
uni.navigateTo({
url: '/pages/mall/consumer/product-detail?id=' + productId
})
}, 500)
} else {
- // 无规格,直接加入购物车
- uni.showLoading({ title: '添加中...' })
+ // 无规格则直接加入购物车
+ uni.showLoading({ title: '添加中...' })
const success = await supabaseService.addToCart(productId, 1, '', merchantId)
uni.hideLoading()
if (success) {
- uni.showToast({ title: '已添加到购物车', icon: 'success' })
+ uni.showToast({ title: '已添加到购物车', icon: 'success' })
} else {
- uni.showToast({ title: '添加失败,请先登录', icon: 'none' })
+ uni.showToast({ title: '添加失败,请先登录', icon: 'none' })
}
}
} catch (e) {
- console.error('添加到购物车异常', e)
+ console.error('添加到购物车异常', e)
uni.hideLoading()
- uni.showToast({ title: '操作异常', icon: 'none' })
+ uni.showToast({ title: '操作异常', icon: 'none' })
}
}
@@ -1119,31 +1032,31 @@ const openCamera = () => {
count: 1,
sourceType: ['camera'],
success: (res) => {
- console.log('拍摄图片路径:', (res.tempFilePaths as string[])[0])
- uni.showToast({ title: '已启用相机', icon: 'none' })
+ console.log('拍摄图片路径:', (res.tempFilePaths as string[])[0])
+ uni.showToast({ title: '已启用相机', icon: 'none' })
},
fail: (err) => {
- console.error('启用相机失败', err)
+ console.error('鍚敤鐩告満澶辫触', err)
}
})
}
const goBack = () => {
- if (showResults.value) {
- // 如果在搜索结果页,先返回到搜索初始页
- showResults.value = false
- searchKeyword.value = ''
- } else {
- // 如果在搜索初始页,则返回上一页
- const pages = getCurrentPages()
- if (pages.length > 1) {
- uni.navigateBack()
- } else {
- // 如果只有一页(由于深链接或重定向),返回首页
- uni.switchTab({
- url: '/pages/main/index'
- })
- }
+ if (showResults.value) {
+ // 如果在搜索结果页,先返回搜索初始页
+ showResults.value = false
+ searchKeyword.value = ''
+ } else {
+ // 如果在搜索初始页,则返回上一页
+ const pages = getCurrentPages()
+ if (pages.length > 1) {
+ uni.navigateBack()
+ } else {
+ // 如果只有一个页面,则回首页
+ uni.switchTab({
+ url: '/pages/main/index'
+ })
+ }
}
}
@@ -1158,7 +1071,7 @@ const goBack = () => {
min-height: 100%;
}
-/* 店铺搜索结果 */
+/* 搴楅摵鎼滅储缁撴灉 */
.shop-results-section {
background-color: #fff;
margin-bottom: 10px;
@@ -1486,7 +1399,7 @@ const goBack = () => {
margin-left: 6rpx;
}
-/* 猜你需要 */
+/* 鐚滀綘闇€瑕?*/
.guess-you-like {
margin-bottom: 24rpx;
background-color: #ffffff;
@@ -1840,7 +1753,7 @@ const goBack = () => {
font-weight: bold;
}
-/* 错误状态 */
+/* 閿欒鐘舵€?*/
.error-state {
flex: 1;
display: flex;
@@ -1872,7 +1785,7 @@ const goBack = () => {
color: #999;
}
-/* 加载更多 */
+/* 鍔犺浇鏇村 */
.loading-more {
padding: 20px 0;
display: flex;
@@ -1932,3 +1845,5 @@ const goBack = () => {
}
+
+
diff --git a/services/homeServiceService.uts b/services/homeServiceService.uts
index 48a7dbb0..234b6af3 100644
--- a/services/homeServiceService.uts
+++ b/services/homeServiceService.uts
@@ -12,39 +12,80 @@ import {
HomeServiceTaskType,
HomeServiceTimelineItemType
} from '@/types/home-service.uts'
+import {
+ confirmServiceOrder,
+ createServiceOrder,
+ getServiceOrderDetail,
+ listConsumerServiceOrders,
+ rejectServiceOrderAcceptance
+} from '@/services/serviceOrderService.uts'
+import supa from '@/components/supadb/aksupainstance.uts'
+import {
+ getServiceOrderStatusText,
+ type ServiceOrderStatus,
+ type ServiceOrderTimelineItemType,
+ type ServiceOrderType
+} from '@/types/service-order.uts'
-const SERVICE_CATALOG: Array = [
- {
- id: 'svc-001',
- name: '基础上门护理',
- category: '日常照护',
- price: 168,
- durationText: '约 2 小时',
- summary: '覆盖生命体征监测、基础照护、风险提醒。',
- tags: ['适老化', '护理员上门', '支持家属陪同'],
- suitableFor: '行动不便、术后恢复、慢病随访老人'
- },
- {
- id: 'svc-002',
- name: '康复训练指导',
- category: '康复支持',
- price: 260,
- durationText: '约 3 小时',
- summary: '提供肢体训练、步态练习和居家康复建议。',
- tags: ['康复师', '步骤清晰', '可连续预约'],
- suitableFor: '卒中恢复、术后康复、失能半失能老人'
- },
- {
- id: 'svc-003',
- name: '慢病健康随访',
- category: '健康管理',
- price: 128,
- durationText: '约 90 分钟',
- summary: '完成血压血糖监测、用药核对与健康宣教。',
- tags: ['随访', '慢病', '可生成记录'],
- suitableFor: '高血压、糖尿病等长期管理老人'
+function plainObject(source: any): any {
+ return JSON.parse(JSON.stringify(source)) as any
+}
+
+function readString(source: any, key: string): string {
+ const value = plainObject(source)[key]
+ if (value == null) {
+ return ''
}
-]
+ return typeof value == 'string' ? value : String(value)
+}
+
+function readNumber(source: any, key: string): number {
+ const value = plainObject(source)[key]
+ if (typeof value == 'number') {
+ return value
+ }
+ const parsed = Number(value)
+ return isNaN(parsed) ? 0 : parsed
+}
+
+function readStringArray(source: any, key: string): Array {
+ const value = plainObject(source)[key]
+ if (Array.isArray(value)) {
+ const result = [] as Array
+ for (let i = 0; i < value.length; i++) {
+ result.push(typeof value[i] == 'string' ? value[i] : String(value[i]))
+ }
+ return result
+ }
+ if (typeof value == 'string' && value != '') {
+ try {
+ const parsed = JSON.parse(value) as any
+ if (Array.isArray(parsed)) {
+ const result = [] as Array
+ for (let i = 0; i < parsed.length; i++) {
+ result.push(typeof parsed[i] == 'string' ? parsed[i] : String(parsed[i]))
+ }
+ return result
+ }
+ } catch (error) {
+ return [] as Array
+ }
+ }
+ return [] as Array
+}
+
+function parseCatalogItem(source: any): HomeServiceCatalogType {
+ return {
+ id: readString(source, 'id'),
+ name: readString(source, 'name'),
+ category: readString(source, 'category'),
+ price: readNumber(source, 'price'),
+ durationText: readString(source, 'duration_text'),
+ summary: readString(source, 'summary'),
+ tags: readStringArray(source, 'tags_json'),
+ suitableFor: readString(source, 'suitable_for')
+ }
+}
function createTimeline(title1: string, title2: string, title3: string): Array {
return [
@@ -243,31 +284,6 @@ const ADMIN_PLANS: Array = [
}
]
-const CONSUMER_ACCEPTANCE: Array = [
- {
- caseId: 'case-001',
- caseNo: 'HS202605130001',
- elderName: '李奶奶',
- serviceName: '基础上门护理',
- acceptanceStatus: 'waiting',
- acceptanceStatusText: '待验收',
- rating: 5,
- feedback: '护理员按时到达,服务过程清晰,老人状态稳定。',
- tags: ['准时上门', '沟通清楚', '动作规范']
- },
- {
- caseId: 'case-002',
- caseNo: 'HS202605130002',
- elderName: '张爷爷',
- serviceName: '慢病健康随访',
- acceptanceStatus: 'waiting',
- acceptanceStatusText: '待验收',
- rating: 5,
- feedback: '已查看记录,等待家属最终确认。',
- tags: ['记录完整', '态度耐心']
- }
-]
-
const ADMIN_RECTIFICATIONS: Array = [
{
caseId: 'case-001',
@@ -355,13 +371,6 @@ function clonePlan(item: HomeServicePlanType): HomeServicePlanType {
}
}
-function cloneAcceptance(item: HomeServiceAcceptanceType): HomeServiceAcceptanceType {
- return {
- ...item,
- tags: item.tags.slice(0)
- }
-}
-
function cloneRectification(item: HomeServiceRectificationType): HomeServiceRectificationType {
return {
...item
@@ -375,68 +384,196 @@ function cloneSettlement(item: HomeServiceSettlementType): HomeServiceSettlement
}
export async function fetchHomeServiceCatalog(): Promise> {
- await delay()
- return SERVICE_CATALOG.map((item) => ({ ...item, tags: item.tags.slice(0) }))
+ const response = await supa
+ .from('hss_service_catalog')
+ .select('id, name, category, price, duration_text, summary, tags_json, suitable_for, sort_no')
+ .eq('status', 1)
+ .is('deleted_at', null)
+ .order('sort_no', { ascending: true })
+ .execute()
+ if (response.error != null || response.data == null || !Array.isArray(response.data)) {
+ return [] as Array
+ }
+ const result = [] as Array
+ for (let i = 0; i < response.data.length; i++) {
+ result.push(parseCatalogItem(response.data[i]))
+ }
+ return result
}
export async function fetchConsumerHomeServiceCases(): Promise> {
- await delay()
- return CASE_STORE.map((item) => cloneCase(item))
+ const orders = await listConsumerServiceOrders()
+ const result = [] as Array
+ for (let i = 0; i < orders.length; i++) {
+ result.push(mapOrderToCase(orders[i]))
+ }
+ return result
}
export async function fetchConsumerHomeServiceCaseDetail(caseId: string): Promise {
- await delay()
- const target = CASE_STORE.find((item) => item.id == caseId)
- return target == null ? null : cloneCase(target)
+ const detail = await getServiceOrderDetail(caseId)
+ if (detail != null) {
+ return mapOrderToCase(detail)
+ }
+ return null
}
-export async function createHomeServiceApplication(draft: HomeServiceApplicationDraftType): Promise {
- await delay()
- const matchedService = SERVICE_CATALOG.find((item) => item.id == draft.serviceId)
- const created: HomeServiceCaseType = {
- id: 'case-' + String(CASE_STORE.length + 1).padStart(3, '0'),
- caseNo: 'HS20260513' + String(CASE_STORE.length + 1).padStart(4, '0'),
- status: 'submitted',
- statusText: '已提交',
- statusTone: 'primary',
- serviceName: draft.serviceName,
- serviceTime: draft.preferredTime,
- applicantName: draft.applicantName,
- elderName: draft.elderName,
- age: draft.age,
- phone: draft.phone,
- address: draft.address,
- summary: draft.demandSummary,
- currentStep: 1,
- totalSteps: 8,
- staffName: '待分配',
- staffPhone: '待分配',
- amount: matchedService != null ? matchedService.price : 0,
- timeline: [
- {
- id: 'new-1',
- title: '已提交申请',
- time: '2026-05-13 10:00',
- description: '系统已接收申请,等待受理。'
- }
- ]
+export async function createHomeServiceApplication(draft: HomeServiceApplicationDraftType): Promise {
+ const catalog = await fetchHomeServiceCatalog()
+ let matchedService: HomeServiceCatalogType | null = null
+ for (let i = 0; i < catalog.length; i++) {
+ if (catalog[i].id == draft.serviceId) {
+ matchedService = catalog[i]
+ break
+ }
+ }
+ if (matchedService != null && draft.serviceAddressSnapshot != null) {
+ const createdOrder = await createServiceOrder({
+ service: matchedService,
+ address: {
+ addressId: draft.serviceAddressSnapshot.addressId,
+ contactName: draft.serviceAddressSnapshot.contactName,
+ contactPhone: draft.serviceAddressSnapshot.contactPhone,
+ province: '',
+ city: '',
+ district: '',
+ detailAddress: draft.serviceAddressSnapshot.addressDetail,
+ fullAddress: draft.serviceAddressSnapshot.fullAddress,
+ latitude: draft.serviceAddressSnapshot.latitude,
+ longitude: draft.serviceAddressSnapshot.longitude,
+ coordinateType: draft.serviceAddressSnapshot.coordinateType,
+ remark: draft.serviceAddressSnapshot.remark
+ },
+ recipientName: draft.elderName,
+ recipientPhone: draft.phone,
+ contactName: draft.applicantName,
+ contactPhone: draft.phone,
+ appointmentTime: draft.preferredTime,
+ remark: draft.demandSummary
+ })
+ if (createdOrder != null) {
+ return mapOrderToCase(createdOrder)
+ }
+ }
+ return null
+}
+
+function getCaseStep(status: ServiceOrderStatus): number {
+ if (status == 'created') return 1
+ if (status == 'assigned') return 3
+ if (status == 'accepted') return 4
+ if (status == 'departed') return 5
+ if (status == 'arrived' || status == 'in_service') return 6
+ if (status == 'pending_acceptance') return 7
+ if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 8
+ if (status == 'rejected' || status == 'cancelled' || status == 'exception') return 7
+ return 2
+}
+
+function getCaseTone(status: ServiceOrderStatus): string {
+ if (status == 'created' || status == 'assigned' || status == 'pending_acceptance') return 'warning'
+ if (status == 'accepted' || status == 'departed' || status == 'arrived' || status == 'in_service') return 'primary'
+ if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 'success'
+ if (status == 'exception' || status == 'cancelled' || status == 'rejected') return 'danger'
+ return 'neutral'
+}
+
+function getTimelineDescription(log: ServiceOrderTimelineItemType): string {
+ if (log.remark != '') {
+ return log.remark
+ }
+ if (log.toStatus == 'created') return '平台已接收预约申请,等待后续处理。'
+ if (log.toStatus == 'assigned') return '系统已完成派单,正在通知服务人员。'
+ if (log.toStatus == 'accepted') return '服务人员已接单,正在准备上门。'
+ if (log.toStatus == 'departed') return '服务人员已出发,正在前往服务地点。'
+ if (log.toStatus == 'arrived') return '服务人员已到达服务地点。'
+ if (log.toStatus == 'in_service') return '服务已开始执行,请留意后续进度。'
+ if (log.toStatus == 'completed') return '服务执行已完成,正在整理结果。'
+ if (log.toStatus == 'pending_acceptance') return '服务记录已提交,等待家属验收。'
+ if (log.toStatus == 'accepted_by_user') return '家属已确认本次服务结果。'
+ if (log.toStatus == 'reviewed') return '家属已完成评价反馈。'
+ if (log.toStatus == 'settled') return '本次服务已完成结算归档。'
+ if (log.toStatus == 'cancelled') return '服务单已取消。'
+ if (log.toStatus == 'rejected') return '服务人员未接受该工单。'
+ return '服务过程状态已更新。'
+}
+
+function formatServiceAppointmentText(value: string): string {
+ if (value == '') {
+ return ''
+ }
+ if (value.indexOf('上午') >= 0 || value.indexOf('下午') >= 0 || value.indexOf('晚上') >= 0) {
+ return value
+ }
+ const parsed = Date.parse(value)
+ if (!isNaN(parsed)) {
+ const date = new Date(parsed)
+ let year = date.getFullYear()
+ const currentYear = new Date().getFullYear()
+ if (year < currentYear - 1) {
+ year = currentYear
+ }
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hour = String(date.getHours()).padStart(2, '0')
+ const minute = String(date.getMinutes()).padStart(2, '0')
+ return year + '-' + month + '-' + day + ' ' + hour + ':' + minute
+ }
+ const monthDayMatch = value.match(/(\d{2})\/(\d{2})/)
+ const timeMatch = value.match(/(\d{2}:\d{2}(\s*-\s*\d{2}:\d{2})?)/)
+ if (monthDayMatch != null) {
+ const month = monthDayMatch[1] ?? ''
+ const day = monthDayMatch[2] ?? ''
+ const timeText = timeMatch != null ? (timeMatch[1] ?? '') : ''
+ if (month != '' && day != '') {
+ return String(new Date().getFullYear()) + '-' + month + '-' + day + (timeText != '' ? ' ' + timeText.replace(/\s+/g, '') : '')
+ }
+ }
+ return value.replace('T', ' ')
+}
+
+function mapLogsToTimeline(logs: Array): Array {
+ const timeline = [] as Array
+ for (let i = 0; i < logs.length; i++) {
+ timeline.push({
+ id: logs[i].id,
+ title: getServiceOrderStatusText(logs[i].toStatus),
+ time: logs[i].createdAt,
+ description: getTimelineDescription(logs[i])
+ })
+ }
+ return timeline
+}
+
+function mapOrderToCase(order: ServiceOrderType): HomeServiceCaseType {
+ return {
+ id: order.id,
+ caseNo: order.orderNo,
+ status: order.status,
+ statusText: getServiceOrderStatusText(order.status),
+ statusTone: getCaseTone(order.status),
+ serviceName: order.serviceName,
+ serviceTime: formatServiceAppointmentText(order.appointmentTime),
+ applicantName: order.contactName,
+ elderName: order.recipientName,
+ age: 0,
+ phone: order.contactPhone,
+ address: order.addressSnapshot.fullAddress,
+ summary: order.remark,
+ currentStep: getCaseStep(order.status),
+ totalSteps: 8,
+ staffName: order.staffName == '' ? '待分配' : order.staffName,
+ staffPhone: order.staffPhone == '' ? '待分配' : order.staffPhone,
+ amount: order.serviceSnapshot.price,
+ checkinTime: order.executionRecord != null ? order.executionRecord.checkinTime : '',
+ checkinAddress: order.executionRecord != null ? order.executionRecord.checkinAddress : '',
+ serviceStartedAt: order.executionRecord != null ? order.executionRecord.serviceStartedAt : order.serviceStartedAt,
+ serviceFinishedAt: order.executionRecord != null ? order.executionRecord.serviceFinishedAt : order.completedAt,
+ executionSummary: order.executionRecord != null ? (order.executionRecord.summary != '' ? order.executionRecord.summary : order.executionRecord.remark) : '',
+ evidenceCount: order.evidenceFiles.length,
+ serviceAddressSnapshot: null,
+ timeline: mapLogsToTimeline(order.logs)
}
- CASE_STORE.unshift(created)
- ADMIN_APPLICATIONS.unshift({
- id: 'admin-app-' + String(ADMIN_APPLICATIONS.length + 1).padStart(3, '0'),
- caseId: created.id,
- caseNo: created.caseNo,
- status: 'submitted',
- statusText: '已提交',
- statusTone: 'primary',
- elderName: created.elderName,
- serviceName: created.serviceName,
- preferredTime: created.serviceTime,
- assessmentResult: '待受理',
- dispatcherName: '待分配',
- staffName: '待分配'
- })
- return cloneCase(created)
}
export async function fetchWorkerTasks(): Promise> {
@@ -738,9 +875,21 @@ export async function submitAdminServicePlan(
}
export async function fetchConsumerAcceptanceDetail(caseId: string): Promise {
- await delay()
- const target = CONSUMER_ACCEPTANCE.find((item) => item.caseId == caseId)
- return target == null ? null : cloneAcceptance(target)
+ const detail = await getServiceOrderDetail(caseId)
+ if (detail != null) {
+ return {
+ caseId: detail.id,
+ caseNo: detail.orderNo,
+ elderName: detail.recipientName,
+ serviceName: detail.serviceName,
+ acceptanceStatus: detail.status,
+ acceptanceStatusText: getServiceOrderStatusText(detail.status),
+ rating: detail.review != null ? detail.review.rating : 5,
+ feedback: detail.review != null ? detail.review.content : '',
+ tags: detail.review != null ? detail.review.tags : [] as Array
+ }
+ }
+ return null
}
export async function submitConsumerAcceptance(
@@ -750,40 +899,18 @@ export async function submitConsumerAcceptance(
feedback: string,
tags: Array
): Promise {
- await delay()
- const target = CONSUMER_ACCEPTANCE.find((item) => item.caseId == caseId)
- if (target == null) {
+ if (approved) {
+ const result = await confirmServiceOrder(caseId, rating, feedback, tags)
+ if (result != null) {
+ return await fetchConsumerAcceptanceDetail(caseId)
+ }
return null
}
-
- target.acceptanceStatus = approved ? 'approved' : 'rejected'
- target.acceptanceStatusText = approved ? '已验收' : '已退回'
- target.rating = rating
- target.feedback = feedback
- target.tags = tags.slice(0)
-
- const relatedCase = CASE_STORE.find((item) => item.id == caseId)
- if (relatedCase != null) {
- relatedCase.status = approved ? 'completed' : 'revision_required'
- relatedCase.statusText = approved ? '已完成' : '待整改'
- relatedCase.statusTone = approved ? 'success' : 'warning'
- relatedCase.currentStep = approved ? 8 : 7
- relatedCase.timeline.unshift({
- id: 'case-accept-' + String(relatedCase.timeline.length + 1),
- title: approved ? '家属已验收' : '家属退回整改',
- time: '2026-05-13 17:10',
- description: feedback
- })
+ const rejected = await rejectServiceOrderAcceptance(caseId, feedback)
+ if (rejected != null) {
+ return await fetchConsumerAcceptanceDetail(caseId)
}
-
- const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == caseId)
- if (relatedAdmin != null) {
- relatedAdmin.status = approved ? 'completed' : 'revision_required'
- relatedAdmin.statusText = approved ? '已完成' : '待整改'
- relatedAdmin.statusTone = approved ? 'success' : 'warning'
- }
-
- return cloneAcceptance(target)
+ return null
}
export async function fetchAdminRectificationDetail(caseId: string): Promise {
diff --git a/services/serviceOrderService.uts b/services/serviceOrderService.uts
index c2193636..f41cb401 100644
--- a/services/serviceOrderService.uts
+++ b/services/serviceOrderService.uts
@@ -1,124 +1,135 @@
import supa from '@/components/supadb/aksupainstance.uts'
import { getCurrentUserId } from '@/utils/store.uts'
-import { getDeliveryProfileByUserId } from '@/api/delivery.uts'
-import { getServiceOrderStatusText, normalizeServiceOrderStatus, type ServiceOrderStatus } from '@/types/service-order.uts'
-import type {
- DeliveryCheckinPayloadType,
- DeliveryDashboardType,
- DeliveryLocationType,
- DeliveryOrderType,
- DeliveryServiceRecordType
-} from '@/types/delivery.uts'
+import { getCurrentUser } from '@/utils/store.uts'
+import type { UserAddress } from '@/utils/supabaseService.uts'
+import type { HomeServiceCatalogType } from '@/types/home-service.uts'
+import {
+ getServiceOrderStatusText,
+ normalizeServiceOrderStatus,
+ type ServiceOrderAddressSnapshotType,
+ type ServiceEvidenceFileType,
+ type ServiceExecutionRecordType,
+ type ServiceOrderStatus,
+ type ServiceOrderTimelineItemType,
+ type ServiceOrderType,
+ type ServiceReviewType
+} from '@/types/service-order.uts'
-function nowIso(): string {
- return new Date().toISOString()
- }
+export type CreateServiceOrderParams = {
+ service: HomeServiceCatalogType
+ address: ServiceOrderAddressSnapshotType
+ recipientName: string
+ recipientPhone: string
+ contactName: string
+ contactPhone: string
+ appointmentTime: string
+ remark: string
+}
+
+function nowText(): string {
+ return new Date().toISOString().replace('T', ' ').substring(0, 19)
+}
function buildId(prefix: string): string {
return prefix + '-' + String(Date.now()) + '-' + String(Math.floor(Math.random() * 100000)).padStart(5, '0')
- }
+}
-async function getCurrentStaffId(): Promise {
- const userId = getCurrentUserId()
- if (userId == '') {
- return ''
- }
- const profile = await getDeliveryProfileByUserId(userId)
- return profile != null ? profile.id : ''
- }
+function buildOrderNo(): string {
+ const date = new Date()
+ const y = String(date.getFullYear())
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ const h = String(date.getHours()).padStart(2, '0')
+ const i = String(date.getMinutes()).padStart(2, '0')
+ const s = String(date.getSeconds()).padStart(2, '0')
+ return 'HS' + y + m + d + h + i + s + String(Math.floor(Math.random() * 900) + 100)
+}
-async function insertStatusLog(orderId: string, fromStatus: string, toStatus: ServiceOrderStatus, remark: string): Promise {
- const userId = getCurrentUserId()
- await supa.from('hss_service_order_status_logs').insert({
- id: buildId('slog'),
- order_id: orderId,
- from_status: fromStatus,
- to_status: toStatus,
- operator_id: userId == '' ? null : userId,
- operator_role: 'delivery',
- remark,
- created_at: nowIso()
- }).execute()
- }
+function isUuidLike(value: string): boolean {
+ return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/.test(value)
+}
-function emptyOrder(): DeliveryOrderType {
- return {
- id: '',
- orderNo: '',
- serviceType: '',
- serviceName: '',
- serviceCategory: '',
- serviceItems: [] as Array,
- elderId: '',
- elderName: '',
- elderNameMasked: '',
- elderGender: '',
- elderAge: 0,
- elderPhone: '',
- elderPhoneMasked: '',
- fullElderName: '',
- fullPhone: '',
- contactRelation: '家属',
- addressSummary: '',
- address: '',
- addressDetail: '',
- fullAddress: '',
- latitude: 0,
- longitude: 0,
- appointmentTime: '',
- appointmentStartTime: '',
- appointmentEndTime: '',
- duration: 90,
- estimatedDuration: 90,
- price: 0,
- staffIncome: 0,
- distance: '',
- actualStartTime: '',
- actualEndTime: '',
- status: 'pending_assignment' as any,
- statusText: '',
- statusTone: 'warning',
- riskTags: [] as Array,
- healthTags: [] as Array,
- careLevel: '',
- needFamilyPresent: false,
- needMaterials: false,
- remark: '',
- merchantId: '',
- merchantName: '',
- deliveryStaffId: '',
- deliveryStaffName: '',
- acceptTime: '',
- departTime: '',
- arriveTime: '',
- checkinTime: '',
- finishTime: '',
- cancelReason: '',
- exceptionType: '',
- exceptionDesc: '',
- evidenceList: [] as Array,
- signatureUrl: '',
- signatureName: '',
- satisfactionStatus: '',
- settlementStatus: '',
- archiveStatus: '',
- createdAt: '',
- updatedAt: '',
- contactName: '',
- contactPhone: '',
- notices: [] as Array,
- timeline: [] as Array,
- statusLog: [] as Array,
- serviceSummary: '',
- progressNote: '',
- distanceKm: '',
- allowCheckinRadiusMeters: 100,
- lastLocation: null,
- trackPoints: [] as Array,
- serviceRecord: null,
- abnormalReport: null
- } as DeliveryOrderType
+function normalizeUuidOrNull(value: string): string | null {
+ if (value == '') {
+ return null
}
+ return isUuidLike(value) ? value : null
+}
+
+function normalizeAppointmentTime(value: string): string | null {
+ const text = value.trim()
+ if (text == '') {
+ return null
+ }
+ if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(\s*-\s*\d{2}:\d{2})?$/.test(text)) {
+ return text
+ }
+ if (/^\d{4}-\d{2}-\d{2}(\s+(上午|下午|晚上))$/.test(text)) {
+ return text
+ }
+ if (/^\d{4}-\d{2}-\d{2}T/.test(text)) {
+ const parsed = Date.parse(text)
+ if (!isNaN(parsed)) {
+ const date = new Date(parsed)
+ const year = String(date.getFullYear())
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hour = String(date.getHours()).padStart(2, '0')
+ const minute = String(date.getMinutes()).padStart(2, '0')
+ return year + '-' + month + '-' + day + ' ' + hour + ':' + minute
+ }
+ return text.replace('T', ' ')
+ }
+ const monthDayMatch = text.match(/(\d{2})\/(\d{2})/)
+ if (monthDayMatch != null) {
+ const month = monthDayMatch[1] ?? ''
+ const day = monthDayMatch[2] ?? ''
+ if (month == '' || day == '') {
+ return text
+ }
+ const year = String(new Date().getFullYear())
+ const rangeMatch = text.match(/(\d{2}:\d{2})(\s*-\s*\d{2}:\d{2})?/)
+ if (rangeMatch != null) {
+ const startTime = rangeMatch[1] ?? ''
+ const endRange = rangeMatch[2] ?? ''
+ if (startTime != '') {
+ return year + '-' + month + '-' + day + ' ' + startTime + endRange.replace(/\s+/g, '')
+ }
+ }
+ const tailText = text.substring(text.indexOf(month + '/' + day) + 5).trim()
+ return year + '-' + month + '-' + day + (tailText != '' ? ' ' + tailText : '')
+ }
+ const explicitParsed = Date.parse(text)
+ if (!isNaN(explicitParsed) && /^\d{4}\//.test(text)) {
+ const date = new Date(explicitParsed)
+ const year = String(date.getFullYear())
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hour = String(date.getHours()).padStart(2, '0')
+ const minute = String(date.getMinutes()).padStart(2, '0')
+ return year + '-' + month + '-' + day + ' ' + hour + ':' + minute
+ }
+ const year = String(new Date().getFullYear())
+ return /^\d{2}-\d{2}\s+\d{2}:\d{2}/.test(text) ? year + '-' + text : text
+}
+
+function safeParseObject(value: string): UTSJSONObject {
+ if (value == '') {
+ return JSON.parse('{}') as UTSJSONObject
+ }
+ return JSON.parse(value) as UTSJSONObject
+}
+
+function safeParseArray(value: string): Array {
+ if (value == '') {
+ return [] as Array
+ }
+ const parsed = JSON.parse(value) as any
+ if (Array.isArray(parsed)) {
+ return parsed as Array
+ }
+ return [] as Array
+}
function safeJsonField(source: any, key: string): string {
const plain = JSON.parse(JSON.stringify(source)) as any
@@ -127,374 +138,387 @@ function safeJsonField(source: any, key: string): string {
return ''
}
return JSON.stringify(value)
- }
+}
-function statusToDeliveryStatus(status: ServiceOrderStatus): string {
- if (status == 'assigned') return 'pending_assignment'
- if (status == 'accepted') return 'accepted'
- if (status == 'departed') return 'departed'
- if (status == 'arrived') return 'arrived'
- if (status == 'in_service') return 'in_service'
- if (status == 'pending_acceptance') return 'pending_acceptance'
- if (status == 'reviewed' || status == 'accepted_by_user' || status == 'settled') return 'completed'
- if (status == 'rejected') return 'rejected'
- if (status == 'cancelled') return 'cancelled'
- if (status == 'exception') return 'abnormal'
- return 'pending_assignment'
- }
+function plainObject(source: any): any {
+ return JSON.parse(JSON.stringify(source)) as any
+}
-function statusTone(status: ServiceOrderStatus): string {
- if (status == 'pending_acceptance' || status == 'assigned') return 'warning'
- if (status == 'accepted' || status == 'departed' || status == 'arrived' || status == 'in_service') return 'primary'
- if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 'success'
- if (status == 'rejected' || status == 'cancelled' || status == 'exception') return 'danger'
- return 'warning'
+function readString(source: any, key: string): string {
+ const value = plainObject(source)[key]
+ if (value == null) {
+ return ''
}
+ return typeof value == 'string' ? value : String(value)
+}
-async function parseDeliveryOrder(orderId: string, item: any): Promise {
- const order = emptyOrder()
- const obj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
- const addressRaw = safeJsonField(item, 'address_snapshot_json')
- const serviceRaw = safeJsonField(item, 'service_snapshot_json')
- const addressObj = JSON.parse(addressRaw == '' ? '{}' : addressRaw) as UTSJSONObject
- const serviceObj = JSON.parse(serviceRaw == '' ? '{}' : serviceRaw) as UTSJSONObject
- const normalizedStatus = normalizeServiceOrderStatus(obj.getString('status') ?? '')
- order.id = obj.getString('id') ?? ''
- order.orderNo = obj.getString('order_no') ?? ''
- order.serviceType = serviceObj.getString('category') ?? '居家服务'
- order.serviceName = obj.getString('service_name') ?? ''
- order.serviceCategory = serviceObj.getString('category') ?? ''
- order.elderName = obj.getString('recipient_name') ?? ''
- order.elderNameMasked = order.elderName
- order.fullElderName = order.elderName
- order.elderPhone = obj.getString('recipient_phone') ?? ''
- order.elderPhoneMasked = order.elderPhone
- order.fullPhone = order.elderPhone
- order.contactName = obj.getString('contact_name') ?? ''
- order.contactPhone = obj.getString('contact_phone') ?? ''
- order.contactRelation = '家属'
- order.address = addressObj.getString('fullAddress') ?? ''
- order.addressDetail = addressObj.getString('detailAddress') ?? ''
- order.fullAddress = order.address
- order.latitude = addressObj.getNumber('latitude') ?? 0
- order.longitude = addressObj.getNumber('longitude') ?? 0
- order.appointmentTime = obj.getString('appointment_time') ?? ''
- order.appointmentStartTime = order.appointmentTime
- order.appointmentEndTime = order.appointmentTime
- order.duration = 90
- order.estimatedDuration = 90
- order.price = serviceObj.getNumber('price') ?? 0
- order.staffIncome = order.price
- order.status = statusToDeliveryStatus(normalizedStatus) as any
- order.statusText = getServiceOrderStatusText(normalizedStatus)
- order.statusTone = statusTone(normalizedStatus)
- order.remark = obj.getString('remark') ?? ''
- order.deliveryStaffId = obj.getString('current_staff_id') ?? ''
- order.acceptTime = obj.getString('accepted_at') ?? ''
- order.departTime = obj.getString('departed_at') ?? ''
- order.arriveTime = obj.getString('arrived_at') ?? ''
- order.actualStartTime = obj.getString('service_started_at') ?? ''
- order.startServiceTime = order.actualStartTime
- order.finishTime = obj.getString('completed_at') ?? ''
- order.createdAt = obj.getString('created_at') ?? ''
- order.updatedAt = obj.getString('updated_at') ?? ''
- order.allowCheckinRadiusMeters = 100
- const logsResponse = await supa.from('hss_service_order_status_logs').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (logsResponse.data != null) {
- const rawLogs = logsResponse.data as any[]
- for (let i = 0; i < rawLogs.length; i++) {
- const logObj = JSON.parse(JSON.stringify(rawLogs[i])) as UTSJSONObject
- order.timeline.push({
- id: logObj.getString('id') ?? '',
- title: getServiceOrderStatusText(normalizeServiceOrderStatus(logObj.getString('to_status') ?? 'created')),
- time: logObj.getString('created_at') ?? '',
- description: logObj.getString('remark') ?? ''
- })
- }
+function readNumber(source: any, key: string): number {
+ const value = plainObject(source)[key]
+ if (typeof value == 'number') {
+ return value
}
- const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (recordResponse.data != null) {
- const raw = recordResponse.data as any[]
- if (raw.length > 0) {
- const recordObj = JSON.parse(JSON.stringify(raw[0])) as UTSJSONObject
- order.checkinTime = recordObj.getString('checkin_time') ?? ''
- order.serviceRecord = {
- id: recordObj.getString('id') ?? '',
- orderId: orderId,
- startTime: recordObj.getString('service_started_at') ?? '',
- endTime: recordObj.getString('service_finished_at') ?? '',
- actualDurationMinutes: recordObj.getNumber('actual_duration_minutes') ?? 0,
- serviceItems: [] as Array,
- serviceContent: [] as Array,
- processNote: recordObj.getString('summary') ?? '',
- elderStatus: '',
- healthMetrics: { bloodPressure: '', heartRate: '', bloodSugar: '', bloodOxygen: '' },
- materialsUsed: '',
- abnormalNote: '',
- photos: [] as Array,
- staffRemark: recordObj.getString('remark') ?? '',
- familyConfirmation: { method: 'none', code: '', signatureName: '', signatureUrl: '', confirmedAt: '' },
- createdAt: recordObj.getString('created_at') ?? '',
- updatedAt: recordObj.getString('updated_at') ?? ''
- } as any
- }
- }
- const evidenceResponse = await supa.from('hss_service_evidence_files').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (evidenceResponse.data != null) {
- const rawEvidence = evidenceResponse.data as any[]
- for (let i = 0; i < rawEvidence.length; i++) {
- const evidenceObj = JSON.parse(JSON.stringify(rawEvidence[i])) as UTSJSONObject
- order.evidenceList.push({
- id: evidenceObj.getString('id') ?? '',
- orderId: orderId,
- phase: evidenceObj.getString('phase') ?? '',
- fileType: evidenceObj.getString('file_type') ?? 'image',
- name: evidenceObj.getString('storage_path') ?? '',
- url: evidenceObj.getString('file_url') ?? '',
- localPath: '',
- status: 'success',
- progress: 100,
- retryable: false,
- createdAt: evidenceObj.getString('created_at') ?? ''
- })
- }
- }
- return order
+ if (typeof value == 'string' && value != '') {
+ const parsed = Number(value)
+ return isNaN(parsed) ? 0 : parsed
}
+ return 0
+}
-export async function getDashboard(): Promise {
- const orders = await getOrdersByTab('all')
- let pending = 0
- let today = 0
- let serving = 0
- let completed = 0
- let nextOrder: DeliveryOrderType | null = null
- for (let i = 0; i < orders.length; i++) {
- const item = orders[i]
- if (item.status == 'pending_assignment') pending++
- if (item.status == 'pending_assignment' || item.status == 'accepted' || item.status == 'departed' || item.status == 'arrived' || item.status == 'in_service') today++
- if (item.status == 'in_service') serving++
- if (item.status == 'completed' || item.status == 'pending_acceptance') completed++
- if (nextOrder == null && item.status != 'completed' && item.status != 'cancelled' && item.status != 'abnormal') {
- nextOrder = item
- }
- }
+function parseTimeline(item: any): ServiceOrderTimelineItemType {
return {
- pendingAssignmentCount: pending,
- pendingAcceptCount: pending,
- todayOrderCount: today,
- pendingDepartCount: 0,
- servingCount: serving,
- completedCount: completed,
- exceptionCount: 0,
- expectedIncome: 0,
- onlineStatus: 'online',
- nextOrder,
- recentOrders: orders.slice(0, 5)
- } as DeliveryDashboardType
+ id: readString(item, 'id'),
+ orderId: readString(item, 'order_id'),
+ fromStatus: readString(item, 'from_status'),
+ toStatus: normalizeServiceOrderStatus(readString(item, 'to_status')),
+ operatorId: readString(item, 'operator_id'),
+ operatorRole: readString(item, 'operator_role'),
+ remark: readString(item, 'remark'),
+ createdAt: readString(item, 'created_at')
+ }
}
-export async function getOrdersByTab(tab: string): Promise> {
- const staffId = await getCurrentStaffId()
- if (staffId == '') {
- return [] as Array
+function parseReview(item: any): ServiceReviewType {
+ return {
+ id: readString(item, 'id'),
+ orderId: readString(item, 'order_id'),
+ userId: readString(item, 'user_id'),
+ rating: readNumber(item, 'rating') == 0 ? 5 : readNumber(item, 'rating'),
+ tags: safeParseArray(safeJsonField(item, 'tags_json')),
+ content: readString(item, 'content'),
+ createdAt: readString(item, 'created_at')
}
- const response = await supa.from('hss_service_orders').select('*').eq('current_staff_id', staffId).order('created_at', { ascending: false }).execute()
- if (response.error != null || response.data == null) {
- return [] as Array
}
- const rawOrders = response.data as any[]
- const result = [] as Array
- for (let i = 0; i < rawOrders.length; i++) {
- const orderObj = JSON.parse(JSON.stringify(rawOrders[i])) as UTSJSONObject
- const normalized = normalizeServiceOrderStatus(orderObj.getString('status') ?? '')
- let matched = true
- if (tab == 'pending') {
- matched = normalized == 'assigned'
- } else if (tab == 'today') {
- matched = normalized == 'assigned' || normalized == 'accepted' || normalized == 'departed' || normalized == 'arrived' || normalized == 'in_service' || normalized == 'pending_acceptance'
- } else if (tab == 'history') {
- matched = normalized == 'pending_acceptance' || normalized == 'accepted_by_user' || normalized == 'reviewed' || normalized == 'settled' || normalized == 'exception' || normalized == 'cancelled'
+
+function parseExecutionRecord(item: any): ServiceExecutionRecordType {
+ return {
+ id: readString(item, 'id'),
+ orderId: readString(item, 'order_id'),
+ assignmentId: readString(item, 'assignment_id'),
+ checkinTime: readString(item, 'checkin_time'),
+ checkinLatitude: readNumber(item, 'checkin_latitude'),
+ checkinLongitude: readNumber(item, 'checkin_longitude'),
+ checkinAddress: readString(item, 'checkin_address'),
+ serviceStartedAt: readString(item, 'service_started_at'),
+ serviceFinishedAt: readString(item, 'service_finished_at'),
+ actualDurationMinutes: readNumber(item, 'actual_duration_minutes'),
+ serviceItemsJson: safeJsonField(item, 'service_items_json'),
+ summary: readString(item, 'summary'),
+ remark: readString(item, 'remark'),
+ trackPointsJson: safeJsonField(item, 'track_points_json'),
+ createdAt: readString(item, 'created_at'),
+ updatedAt: readString(item, 'updated_at')
+ }
+ }
+
+function parseEvidenceFile(item: any): ServiceEvidenceFileType {
+ return {
+ id: readString(item, 'id'),
+ orderId: readString(item, 'order_id'),
+ executionRecordId: readString(item, 'execution_record_id'),
+ phase: readString(item, 'phase'),
+ fileType: readString(item, 'file_type'),
+ storagePath: readString(item, 'storage_path'),
+ fileUrl: readString(item, 'file_url'),
+ latitude: readNumber(item, 'latitude'),
+ longitude: readNumber(item, 'longitude'),
+ capturedAt: readString(item, 'captured_at'),
+ createdAt: readString(item, 'created_at')
+ }
+ }
+
+function parseServiceOrder(item: any, logs: Array, review: ServiceReviewType | null): ServiceOrderType {
+ const addressSnapshot = safeJsonField(item, 'address_snapshot_json')
+ const serviceSnapshot = safeJsonField(item, 'service_snapshot_json')
+ const addressObj = plainObject(safeParseObject(addressSnapshot))
+ const serviceObj = plainObject(safeParseObject(serviceSnapshot))
+ return {
+ id: readString(item, 'id'),
+ orderNo: readString(item, 'order_no'),
+ userId: readString(item, 'user_id'),
+ serviceId: readString(item, 'service_id'),
+ serviceName: readString(item, 'service_name'),
+ serviceSnapshot: {
+ serviceId: readString(serviceObj, 'serviceId') != '' ? readString(serviceObj, 'serviceId') : readString(item, 'service_id'),
+ serviceName: readString(serviceObj, 'serviceName') != '' ? readString(serviceObj, 'serviceName') : readString(item, 'service_name'),
+ category: readString(serviceObj, 'category'),
+ price: readNumber(serviceObj, 'price'),
+ durationText: readString(serviceObj, 'durationText'),
+ summary: readString(serviceObj, 'summary'),
+ tags: safeParseArray(safeJsonField(serviceObj, 'tags')),
+ suitableFor: readString(serviceObj, 'suitableFor')
+ },
+ serviceAddressId: readString(item, 'service_address_id'),
+ addressSnapshot: {
+ addressId: readString(addressObj, 'addressId'),
+ contactName: readString(addressObj, 'contactName'),
+ contactPhone: readString(addressObj, 'contactPhone'),
+ province: readString(addressObj, 'province'),
+ city: readString(addressObj, 'city'),
+ district: readString(addressObj, 'district'),
+ detailAddress: readString(addressObj, 'detailAddress'),
+ fullAddress: readString(addressObj, 'fullAddress'),
+ latitude: readNumber(addressObj, 'latitude'),
+ longitude: readNumber(addressObj, 'longitude'),
+ coordinateType: readString(addressObj, 'coordinateType') == '' ? 'gcj02' : readString(addressObj, 'coordinateType'),
+ remark: readString(addressObj, 'remark')
+ },
+ recipientName: readString(item, 'recipient_name'),
+ recipientPhone: readString(item, 'recipient_phone'),
+ contactName: readString(item, 'contact_name'),
+ contactPhone: readString(item, 'contact_phone'),
+ appointmentTime: readString(item, 'appointment_time'),
+ remark: readString(item, 'remark'),
+ status: normalizeServiceOrderStatus(readString(item, 'status')),
+ currentAssignmentId: readString(item, 'current_assignment_id'),
+ currentStaffId: readString(item, 'current_staff_id'),
+ acceptedAt: readString(item, 'accepted_at'),
+ departedAt: readString(item, 'departed_at'),
+ arrivedAt: readString(item, 'arrived_at'),
+ serviceStartedAt: readString(item, 'service_started_at'),
+ completedAt: readString(item, 'completed_at'),
+ pendingAcceptanceAt: readString(item, 'pending_acceptance_at'),
+ acceptedByUserAt: readString(item, 'accepted_by_user_at'),
+ reviewedAt: readString(item, 'reviewed_at'),
+ createdAt: readString(item, 'created_at'),
+ updatedAt: readString(item, 'updated_at'),
+ staffName: '',
+ staffPhone: '',
+ logs,
+ executionRecord: null,
+ evidenceFiles: [] as Array,
+ review
+ }
+ }
+
+async function insertStatusLog(orderId: string, fromStatus: string, toStatus: ServiceOrderStatus, operatorId: string, operatorRole: string, remark: string): Promise {
+ await supa.from('hss_service_order_status_logs').insert({
+ id: buildId('slog'),
+ order_id: orderId,
+ from_status: fromStatus,
+ to_status: toStatus,
+ operator_id: operatorId == '' ? null : operatorId,
+ operator_role: operatorRole,
+ remark,
+ created_at: new Date().toISOString()
+ }).execute()
+ }
+
+export function buildAddressSnapshot(address: UserAddress, latitude: number, longitude: number): ServiceOrderAddressSnapshotType {
+ return {
+ addressId: address.id,
+ contactName: address.recipient_name,
+ contactPhone: address.phone,
+ province: address.province,
+ city: address.city,
+ district: address.district,
+ detailAddress: address.detail_address,
+ fullAddress: address.province + address.city + address.district + ' ' + address.detail_address,
+ latitude,
+ longitude,
+ coordinateType: 'gcj02',
+ remark: address.label ?? ''
+ }
+ }
+
+export async function createServiceOrder(params: CreateServiceOrderParams): Promise {
+ const userId = getCurrentUserId()
+ if (userId == '') {
+ return null
+ }
+ const orderId = buildId('so')
+ const orderNo = buildOrderNo()
+ const now = new Date().toISOString()
+ const appointmentTime = normalizeAppointmentTime(params.appointmentTime)
+ const response = await supa.from('hss_service_orders').insert({
+ id: orderId,
+ order_no: orderNo,
+ user_id: userId,
+ service_id: params.service.id,
+ service_name: params.service.name,
+ service_snapshot_json: params.service as any,
+ service_address_id: normalizeUuidOrNull(params.address.addressId),
+ address_snapshot_json: params.address as any,
+ recipient_name: params.recipientName,
+ recipient_phone: params.recipientPhone,
+ contact_name: params.contactName,
+ contact_phone: params.contactPhone,
+ appointment_time: appointmentTime,
+ remark: params.remark,
+ status: 'created',
+ created_at: now,
+ updated_at: now
+ }).execute()
+ if (response.error != null) {
+ console.error('createServiceOrder failed', response.error)
+ return null
+ }
+ await insertStatusLog(orderId, '', 'created', userId, 'consumer', '创建服务订单')
+ const staffResponse = await supa
+ .from('ml_delivery_staff')
+ .select('id, station_id, nickname, phone, status, deleted_at')
+ .eq('status', 1)
+ .order('created_at', { ascending: true })
+ .execute()
+ if (staffResponse.data != null) {
+ const rawStaffList = staffResponse.data as any[]
+ if (rawStaffList.length > 0) {
+ const staffObj = plainObject(rawStaffList[0])
+ const assignmentId = buildId('sa')
+ await supa.from('hss_service_assignments').insert({
+ id: assignmentId,
+ order_id: orderId,
+ staff_id: readString(staffObj, 'id'),
+ station_id: readString(staffObj, 'station_id') == '' ? null : readString(staffObj, 'station_id'),
+ status: 'assigned',
+ assigned_at: now,
+ created_at: now,
+ updated_at: now
+ }).execute()
+ await supa.from('hss_service_orders').update({
+ status: 'assigned',
+ current_assignment_id: assignmentId,
+ current_staff_id: readString(staffObj, 'id'),
+ updated_at: now
+ }).eq('id', orderId).execute()
+ await insertStatusLog(orderId, 'created', 'assigned', userId, 'system', '系统已自动派单')
}
- if (tab == 'all' || matched) {
- result.push(await parseDeliveryOrder(orderObj.getString('id') ?? '', rawOrders[i]))
+ }
+ return await getServiceOrderDetail(orderId)
+ }
+
+export async function listConsumerServiceOrders(): Promise> {
+ const userId = getCurrentUserId()
+ if (userId == '') {
+ return [] as Array
+ }
+ const response = await supa.from('hss_service_orders').select('*').eq('user_id', userId).order('created_at', { ascending: false }).execute()
+ if (response.error != null || response.data == null) {
+ return [] as Array
+ }
+ const list = response.data as any[]
+ const result = [] as Array
+ for (let i = 0; i < list.length; i++) {
+ const parsed = await getServiceOrderDetail(JSON.parse(JSON.stringify(list[i]))['id'] as string)
+ if (parsed != null) {
+ result.push(parsed)
}
}
return result
}
-export async function getOrderDetail(orderId: string): Promise {
- const response = await supa.from('hss_service_orders').select('*').eq('id', orderId).single().execute()
- if (response.error != null || response.data == null) {
+export async function getServiceOrderDetail(orderId: string): Promise {
+ const orderResponse = await supa.from('hss_service_orders').select('*').eq('id', orderId).limit(1).execute()
+ if (orderResponse.error != null || orderResponse.data == null) {
return null
}
- return await parseDeliveryOrder(orderId, response.data)
+ const rawOrders = orderResponse.data as any[]
+ if (rawOrders.length == 0) {
+ return null
+ }
+ const logsResponse = await supa.from('hss_service_order_status_logs').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ const reviewResponse = await supa.from('hss_service_reviews').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ const executionResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).limit(1).execute()
+ const evidenceResponse = await supa.from('hss_service_evidence_files').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ const logs = [] as Array
+ if (logsResponse.data != null) {
+ const rawLogs = logsResponse.data as any[]
+ for (let i = 0; i < rawLogs.length; i++) {
+ logs.push(parseTimeline(rawLogs[i]))
+ }
+ }
+ let review: ServiceReviewType | null = null
+ if (reviewResponse.data != null) {
+ const rawReviews = reviewResponse.data as any[]
+ if (rawReviews.length > 0) {
+ review = parseReview(rawReviews[0])
+ }
+ }
+ const parsed = parseServiceOrder(rawOrders[0], logs, review)
+ if (executionResponse.data != null) {
+ const rawExecutionList = executionResponse.data as any[]
+ if (rawExecutionList.length > 0) {
+ parsed.executionRecord = parseExecutionRecord(rawExecutionList[0])
+ }
+ }
+ if (evidenceResponse.data != null) {
+ const rawEvidenceList = evidenceResponse.data as any[]
+ const evidenceFiles = [] as Array
+ for (let i = 0; i < rawEvidenceList.length; i++) {
+ evidenceFiles.push(parseEvidenceFile(rawEvidenceList[i]))
+ }
+ parsed.evidenceFiles = evidenceFiles
+ }
+ if (parsed.currentStaffId != '') {
+ const staffResponse = await supa.from('ml_delivery_staff').select('nickname, phone').eq('id', parsed.currentStaffId).limit(1).execute()
+ if (staffResponse.data != null) {
+ const rawStaffList = staffResponse.data as any[]
+ if (rawStaffList.length > 0) {
+ const staffObj = plainObject(rawStaffList[0])
+ parsed.staffName = readString(staffObj, 'nickname')
+ parsed.staffPhone = readString(staffObj, 'phone')
+ }
+ }
+ }
+ return parsed
}
-async function updateOrderStatus(orderId: string, nextStatus: ServiceOrderStatus, updateData: UTSJSONObject, remark: string): Promise {
- const current = await getOrderDetail(orderId)
+export async function confirmServiceOrder(orderId: string, rating: number, content: string, tags: Array): Promise {
+ const userId = getCurrentUserId()
+ if (userId == '') {
+ return null
+ }
+ const current = await getServiceOrderDetail(orderId)
if (current == null) {
return null
}
- const map = JSON.parse('{}') as UTSJSONObject
- map.set('status', nextStatus)
- map.set('updated_at', nowIso())
- const iterator = updateData.keys()
- while (iterator.hasNext()) {
- const key = iterator.next()
- map.set(key, updateData.get(key))
- }
- const response = await supa.from('hss_service_orders').update(map).eq('id', orderId).execute()
- if (response.error != null) {
- console.error('updateOrderStatus failed', response.error)
+ const acceptedAt = new Date().toISOString()
+ const updateResponse = await supa.from('hss_service_orders').update({
+ status: 'accepted_by_user',
+ accepted_by_user_at: acceptedAt,
+ updated_at: acceptedAt
+ }).eq('id', orderId).execute()
+ if (updateResponse.error != null) {
return null
}
- await insertStatusLog(orderId, normalizeServiceOrderStatus(current.status as string), nextStatus, remark)
- return await getOrderDetail(orderId)
- }
-
-export async function acceptOrder(orderId: string): Promise {
- const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (assignmentResponse.data != null) {
- const raw = assignmentResponse.data as any[]
- if (raw.length > 0) {
- const assignmentId = JSON.parse(JSON.stringify(raw[0]))['id'] as string
- await supa.from('hss_service_assignments').update({ status: 'accepted', accepted_at: nowIso(), updated_at: nowIso() }).eq('id', assignmentId).execute()
- }
- }
- const updateData = JSON.parse('{}') as UTSJSONObject
- updateData.set('accepted_at', nowIso())
- return await updateOrderStatus(orderId, 'accepted', updateData, '服务人员接单')
- }
-
-export async function departOrder(orderId: string, location: DeliveryLocationType | null): Promise {
- const updateData = JSON.parse('{}') as UTSJSONObject
- updateData.set('departed_at', nowIso())
- return await updateOrderStatus(orderId, 'departed', updateData, location == null ? '服务人员出发' : '服务人员出发:' + location.address)
- }
-
-export async function arriveOrder(orderId: string, location: DeliveryLocationType | null): Promise {
- const updateData = JSON.parse('{}') as UTSJSONObject
- updateData.set('arrived_at', nowIso())
- return await updateOrderStatus(orderId, 'arrived', updateData, location == null ? '服务人员到达' : '服务人员到达:' + location.address)
- }
-
-export async function checkinOrder(orderId: string, payload: DeliveryCheckinPayloadType): Promise {
- const current = await getOrderDetail(orderId)
- if (current == null) {
- return null
- }
- let assignmentId = ''
- const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (assignmentResponse.data != null) {
- const rawAssignments = assignmentResponse.data as any[]
- if (rawAssignments.length > 0) {
- assignmentId = JSON.parse(JSON.stringify(rawAssignments[0]))['id'] as string
- }
- }
- let recordId = buildId('ser')
- const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (recordResponse.data != null) {
- const records = recordResponse.data as any[]
- if (records.length > 0) {
- recordId = JSON.parse(JSON.stringify(records[0]))['id'] as string
- await supa.from('hss_service_execution_records').update({
- checkin_time: nowIso(),
- checkin_latitude: payload.location.latitude,
- checkin_longitude: payload.location.longitude,
- checkin_address: payload.location.address,
- remark: payload.note,
- updated_at: nowIso()
- }).eq('id', recordId).execute()
- } else {
- await supa.from('hss_service_execution_records').insert({
- id: recordId,
- order_id: orderId,
- assignment_id: assignmentId,
- checkin_time: nowIso(),
- checkin_latitude: payload.location.latitude,
- checkin_longitude: payload.location.longitude,
- checkin_address: payload.location.address,
- remark: payload.note,
- created_at: nowIso(),
- updated_at: nowIso()
- }).execute()
- }
- }
- for (let i = 0; i < payload.photos.length; i++) {
- await supa.from('hss_service_evidence_files').insert({
- id: buildId('sef'),
- order_id: orderId,
- execution_record_id: recordId,
- phase: 'checkin',
- file_type: 'image',
- storage_path: payload.photos[i],
- file_url: payload.photos[i],
- latitude: payload.location.latitude,
- longitude: payload.location.longitude,
- captured_at: nowIso(),
- created_at: nowIso()
- }).execute()
- }
- return current
- }
-
-export async function startService(orderId: string): Promise {
- let recordId = ''
- const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (recordResponse.data != null) {
- const records = recordResponse.data as any[]
- if (records.length > 0) {
- recordId = JSON.parse(JSON.stringify(records[0]))['id'] as string
- await supa.from('hss_service_execution_records').update({ service_started_at: nowIso(), updated_at: nowIso() }).eq('id', recordId).execute()
- }
- }
- const updateData = JSON.parse('{}') as UTSJSONObject
- updateData.set('service_started_at', nowIso())
- return await updateOrderStatus(orderId, 'in_service', updateData, '开始服务')
- }
-
-export async function saveServiceRecord(orderId: string, record: DeliveryServiceRecordType): Promise {
- let assignmentId = ''
- const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
- if (assignmentResponse.data != null) {
- const assignments = assignmentResponse.data as any[]
- if (assignments.length > 0) {
- assignmentId = JSON.parse(JSON.stringify(assignments[0]))['id'] as string
- }
- }
- await supa.from('hss_service_execution_records').upsert({
- id: record.id,
+ await insertStatusLog(orderId, current.status, 'accepted_by_user', userId, 'consumer', '用户确认验收')
+ await supa.from('hss_service_reviews').insert({
+ id: buildId('srv'),
order_id: orderId,
- assignment_id: assignmentId,
- service_started_at: record.startTime,
- service_finished_at: record.endTime,
- actual_duration_minutes: record.actualDurationMinutes,
- service_items_json: record.serviceItems as any,
- summary: record.processNote,
- remark: record.staffRemark,
- updated_at: nowIso(),
- created_at: record.createdAt
+ user_id: userId,
+ rating,
+ tags_json: tags as any,
+ content,
+ created_at: acceptedAt
}).execute()
- for (let i = 0; i < record.photos.length; i++) {
- await supa.from('hss_service_evidence_files').insert({
- id: buildId('sef'),
- order_id: orderId,
- execution_record_id: record.id,
- phase: 'service',
- file_type: 'image',
- storage_path: record.photos[i],
- file_url: record.photos[i],
- captured_at: nowIso(),
- created_at: nowIso()
- }).execute()
- }
- return await getOrderDetail(orderId)
+ await supa.from('hss_service_orders').update({
+ status: 'reviewed',
+ reviewed_at: acceptedAt,
+ updated_at: acceptedAt
+ }).eq('id', orderId).execute()
+ await insertStatusLog(orderId, 'accepted_by_user', 'reviewed', userId, 'consumer', '用户提交评价')
+ return await getServiceOrderDetail(orderId)
}
-export async function finishOrder(orderId: string): Promise {
- const updateData = JSON.parse('{}') as UTSJSONObject
- updateData.set('completed_at', nowIso())
- updateData.set('pending_acceptance_at', nowIso())
- return await updateOrderStatus(orderId, 'pending_acceptance', updateData, '服务完成,等待用户验收')
+export async function rejectServiceOrderAcceptance(orderId: string, content: string): Promise {
+ const userId = getCurrentUserId()
+ if (userId == '') {
+ return null
+ }
+ const current = await getServiceOrderDetail(orderId)
+ if (current == null) {
+ return null
+ }
+ const updateResponse = await supa.from('hss_service_orders').update({
+ status: 'exception',
+ updated_at: nowIso()
+ }).eq('id', orderId).execute()
+ if (updateResponse.error != null) {
+ return null
+ }
+ await insertStatusLog(orderId, current.status, 'exception', userId, 'consumer', content == '' ? '用户退回整改' : content)
+ return await getServiceOrderDetail(orderId)
+ }
+
+export async function getCurrentConsumerUser() {
+ return await getCurrentUser()
}
\ No newline at end of file
diff --git a/types/home-service.uts b/types/home-service.uts
index 012258cb..da811dba 100644
--- a/types/home-service.uts
+++ b/types/home-service.uts
@@ -16,6 +16,28 @@ export type HomeServiceTimelineItemType = {
description: string
}
+export type HomeServiceSelectedAddressType = {
+ addressId: string
+ userId: string
+ isDefault: boolean
+ contactName: string
+ contactPhone: string
+ phone?: string
+ addressName: string
+ locationName?: string
+ addressDetail: string
+ locationAddress?: string
+ houseNumber: string
+ doorNo?: string
+ fullAddress: string
+ latitude: number
+ longitude: number
+ remark: string
+ coordinateType: string
+ createdAt: number
+ updatedAt: number
+}
+
export type HomeServiceCaseType = {
id: string
caseNo: string
@@ -35,6 +57,13 @@ export type HomeServiceCaseType = {
staffName: string
staffPhone: string
amount: number
+ checkinTime: string
+ checkinAddress: string
+ serviceStartedAt: string
+ serviceFinishedAt: string
+ executionSummary: string
+ evidenceCount: number
+ serviceAddressSnapshot?: HomeServiceSelectedAddressType | null
timeline: Array
}
@@ -67,6 +96,7 @@ export type HomeServiceApplicationDraftType = {
address: string
preferredTime: string
demandSummary: string
+ serviceAddressSnapshot?: HomeServiceSelectedAddressType | null
}
export type HomeServiceAdminApplicationType = {
diff --git a/types/service-order.uts b/types/service-order.uts
index 0d0923d7..9216589e 100644
--- a/types/service-order.uts
+++ b/types/service-order.uts
@@ -33,6 +33,32 @@ export const SERVICE_ORDER_STATUS_LIST: Array = [
'exception'
]
+export type ServiceOrderAddressSnapshotType = {
+ addressId: string
+ contactName: string
+ contactPhone: string
+ province: string
+ city: string
+ district: string
+ detailAddress: string
+ fullAddress: string
+ latitude: number
+ longitude: number
+ coordinateType: string
+ remark: string
+}
+
+export type ServiceOrderServiceSnapshotType = {
+ serviceId: string
+ serviceName: string
+ category: string
+ price: number
+ durationText: string
+ summary: string
+ tags: Array
+ suitableFor: string
+}
+
export type ServiceOrderTimelineItemType = {
id: string
orderId: string
@@ -44,6 +70,85 @@ export type ServiceOrderTimelineItemType = {
createdAt: string
}
+export type ServiceExecutionRecordType = {
+ id: string
+ orderId: string
+ assignmentId: string
+ checkinTime: string
+ checkinLatitude: number
+ checkinLongitude: number
+ checkinAddress: string
+ serviceStartedAt: string
+ serviceFinishedAt: string
+ actualDurationMinutes: number
+ serviceItemsJson: string
+ summary: string
+ remark: string
+ trackPointsJson: string
+ createdAt: string
+ updatedAt: string
+}
+
+export type ServiceEvidenceFileType = {
+ id: string
+ orderId: string
+ executionRecordId: string
+ phase: string
+ fileType: string
+ storagePath: string
+ fileUrl: string
+ latitude: number
+ longitude: number
+ capturedAt: string
+ createdAt: string
+}
+
+export type ServiceReviewType = {
+ id: string
+ orderId: string
+ userId: string
+ rating: number
+ tags: Array
+ content: string
+ createdAt: string
+}
+
+export type ServiceOrderType = {
+ id: string
+ orderNo: string
+ userId: string
+ serviceId: string
+ serviceName: string
+ serviceSnapshot: ServiceOrderServiceSnapshotType
+ serviceAddressId: string
+ addressSnapshot: ServiceOrderAddressSnapshotType
+ recipientName: string
+ recipientPhone: string
+ contactName: string
+ contactPhone: string
+ appointmentTime: string
+ remark: string
+ status: ServiceOrderStatus
+ currentAssignmentId: string
+ currentStaffId: string
+ acceptedAt: string
+ departedAt: string
+ arrivedAt: string
+ serviceStartedAt: string
+ completedAt: string
+ pendingAcceptanceAt: string
+ acceptedByUserAt: string
+ reviewedAt: string
+ createdAt: string
+ updatedAt: string
+ staffName: string
+ staffPhone: string
+ logs: Array
+ executionRecord: ServiceExecutionRecordType | null
+ evidenceFiles: Array
+ review: ServiceReviewType | null
+}
+
export function getServiceOrderStatusText(status: ServiceOrderStatus): string {
if (status == 'created') return '待处理'
if (status == 'paid') return '已支付'
diff --git a/utils/orderStatus.uts b/utils/orderStatus.uts
new file mode 100644
index 00000000..55619afa
--- /dev/null
+++ b/utils/orderStatus.uts
@@ -0,0 +1,172 @@
+export type UnifiedOrderSource = 'goods' | 'service'
+
+export type OrderStatusSource = {
+ source?: UnifiedOrderSource | string
+ order_status: number
+ payment_status: number
+ pay_expire_at: string
+ created_at: string
+ cancel_reason: string
+}
+
+export const ORDER_STATUS_PENDING = 1
+export const ORDER_STATUS_PAID_OR_SHIPPING = 2
+export const ORDER_STATUS_RECEIVING = 3
+export const ORDER_STATUS_COMPLETED = 4
+export const ORDER_STATUS_CANCELLED = 5
+export const ORDER_STATUS_REFUNDING = 6
+export const ORDER_STATUS_REFUNDED = 7
+export const ORDER_STATUS_TIMEOUT_LEGACY = 8
+
+export const PAYMENT_STATUS_UNPAID = 1
+export const PAYMENT_STATUS_PAID = 2
+export const PAYMENT_STATUS_PARTIAL_REFUND = 3
+export const PAYMENT_STATUS_REFUNDED = 4
+export const PAYMENT_STATUS_TIMEOUT = 5
+
+export const ORDER_PAY_TIMEOUT_SECONDS = 10 * 60
+export const ORDER_TIMEOUT_CANCEL_REASON = '支付超时自动取消'
+
+function parseDateMs(value: string): number {
+ if (value == '') {
+ return 0
+ }
+ const date = new Date(value)
+ if (isNaN(date.getTime())) {
+ return 0
+ }
+ return date.getTime()
+}
+
+function containsTimeoutReason(cancelReason: string): boolean {
+ return cancelReason.indexOf('超时') >= 0
+}
+
+function getNormalizedSource(order: OrderStatusSource): UnifiedOrderSource {
+ if (order.source == 'service') {
+ return 'service'
+ }
+ return 'goods'
+}
+
+function isPendingSourceOrder(order: OrderStatusSource): boolean {
+ return order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID
+}
+
+export function getOrderDeadlineMs(order: OrderStatusSource): number {
+ const payExpireAtMs = parseDateMs(order.pay_expire_at)
+ if (payExpireAtMs > 0) {
+ return payExpireAtMs
+ }
+ const createdAtMs = parseDateMs(order.created_at)
+ if (createdAtMs > 0) {
+ return createdAtMs + ORDER_PAY_TIMEOUT_SECONDS * 1000
+ }
+ return 0
+}
+
+export function getRemainingSeconds(order: OrderStatusSource): number {
+ const deadlineMs = getOrderDeadlineMs(order)
+ if (deadlineMs <= 0) {
+ return 0
+ }
+ const diff = Math.floor((deadlineMs - Date.now()) / 1000)
+ return diff > 0 ? diff : 0
+}
+
+export function getUnifiedDisplayState(order: OrderStatusSource): string {
+ const source = getNormalizedSource(order)
+ const deadlineMs = getOrderDeadlineMs(order)
+ const hasExpiredByDeadline = order.order_status == ORDER_STATUS_PENDING && deadlineMs > 0 && deadlineMs <= Date.now()
+
+ if (source == 'service') {
+ if (order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID && !hasExpiredByDeadline && !containsTimeoutReason(order.cancel_reason)) {
+ return 'pending_pay'
+ }
+ if (order.order_status == ORDER_STATUS_TIMEOUT_LEGACY || order.payment_status == PAYMENT_STATUS_TIMEOUT || containsTimeoutReason(order.cancel_reason)) {
+ return 'cancelled'
+ }
+ if (hasExpiredByDeadline) {
+ return 'expired'
+ }
+ if (order.order_status == 2 || order.order_status == 3 || order.order_status == 4) {
+ return 'processing'
+ }
+ if (order.order_status == ORDER_STATUS_COMPLETED) {
+ return 'completed'
+ }
+ if (order.order_status == ORDER_STATUS_REFUNDING || order.order_status == ORDER_STATUS_REFUNDED) {
+ return 'refund'
+ }
+ return 'unknown'
+ }
+
+ if (order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID && !hasExpiredByDeadline && !containsTimeoutReason(order.cancel_reason)) {
+ return 'pending_pay'
+ }
+ if (order.order_status == ORDER_STATUS_CANCELLED || order.order_status == ORDER_STATUS_TIMEOUT_LEGACY || order.payment_status == PAYMENT_STATUS_TIMEOUT || containsTimeoutReason(order.cancel_reason)) {
+ return 'cancelled'
+ }
+ if (hasExpiredByDeadline) {
+ return 'expired'
+ }
+ if (order.order_status == ORDER_STATUS_PAID_OR_SHIPPING || order.order_status == ORDER_STATUS_RECEIVING) {
+ return 'processing'
+ }
+ if (order.order_status == ORDER_STATUS_COMPLETED) {
+ return 'completed'
+ }
+ if (order.order_status == ORDER_STATUS_REFUNDING || order.order_status == ORDER_STATUS_REFUNDED) {
+ return 'refund'
+ }
+ return 'unknown'
+}
+
+export function isOrderPayExpired(order: OrderStatusSource): boolean {
+ const displayState = getUnifiedDisplayState(order)
+ return displayState == 'cancelled' || displayState == 'expired'
+}
+
+export function isPendingPayOrder(order: OrderStatusSource): boolean {
+ return getUnifiedDisplayState(order) == 'pending_pay' && isPendingSourceOrder(order)
+}
+
+export function getOrderDisplayStatus(order: OrderStatusSource): string {
+ const displayState = getUnifiedDisplayState(order)
+ if (displayState == 'pending_pay') {
+ return 'pending'
+ }
+ if (displayState == 'cancelled' || displayState == 'expired') {
+ return 'cancelled'
+ }
+ if (displayState == 'processing') {
+ return 'shipping'
+ }
+ if (displayState == 'completed') {
+ return 'completed'
+ }
+ if (displayState == 'refund') {
+ return 'refund'
+ }
+ return 'unknown'
+}
+
+function pad2(value: number): string {
+ return value < 10 ? '0' + value : '' + value
+}
+
+export function formatCountdownHM(seconds: number): string {
+ const safeSeconds = seconds > 0 ? seconds : 0
+ const totalMinutes = Math.floor(safeSeconds / 60)
+ const hours = Math.floor(totalMinutes / 60)
+ const minutes = totalMinutes % 60
+ return pad2(hours) + '时' + pad2(minutes) + '分钟'
+}
+
+export function formatCountdownHMS(seconds: number): string {
+ const safeSeconds = seconds > 0 ? seconds : 0
+ const hours = Math.floor(safeSeconds / 3600)
+ const minutes = Math.floor((safeSeconds % 3600) / 60)
+ const remainSeconds = safeSeconds % 60
+ return pad2(hours) + ':' + pad2(minutes) + ':' + pad2(remainSeconds)
+}
\ No newline at end of file
diff --git a/utils/supabaseService.uts b/utils/supabaseService.uts
index b5a87bcf..f0527a38 100644
--- a/utils/supabaseService.uts
+++ b/utils/supabaseService.uts
@@ -1,6 +1,7 @@
import supa from '@/components/supadb/aksupainstance.uts'
import type { AkReqResponse } from '@/uni_modules/ak-req/index.uts'
import { APP_ROLE, CURRENT_CLIENT, SUPA_KEY, SUPA_URL } from '@/ak/config.uts'
+import { ORDER_PAY_TIMEOUT_SECONDS, ORDER_STATUS_CANCELLED, ORDER_STATUS_PAID_OR_SHIPPING, ORDER_STATUS_PENDING, ORDER_STATUS_TIMEOUT_LEGACY, ORDER_TIMEOUT_CANCEL_REASON, PAYMENT_STATUS_PAID, PAYMENT_STATUS_TIMEOUT, PAYMENT_STATUS_UNPAID, type UnifiedOrderSource } from '@/utils/orderStatus.uts'
// 导出 supa 实例,供 services 层统一使用
export { supa }
@@ -377,6 +378,9 @@ export type UserAddress = {
postal_code?: string
is_default: boolean
label?: string
+ latitude?: number
+ longitude?: number
+ coordinate_type?: string
created_at?: string
updated_at?: string
}
@@ -472,6 +476,9 @@ export type AddAddressParams = {
postal_code?: string
is_default?: boolean
label?: string
+ latitude?: number
+ longitude?: number
+ coordinate_type?: string
}
export type UpdateAddressParams = {
@@ -484,6 +491,9 @@ export type UpdateAddressParams = {
postal_code?: string
is_default?: boolean
label?: string
+ latitude?: number
+ longitude?: number
+ coordinate_type?: string
}
export type CreateOrderParams = {
@@ -714,6 +724,43 @@ export type ConfirmReceiptResponse = {
error?: string
}
+export type GetOrdersByCursorParams = {
+ cursor: string
+ limit: number
+ status: number
+ keyword: string
+}
+
+export type GetOrdersByCursorResult = {
+ list: UTSJSONObject[]
+ nextCursor: string
+ hasMore: boolean
+}
+
+export type GetUnifiedOrdersByCursorParams = {
+ cursor: string
+ limit: number
+ bizType: string
+ statusTab: string
+ keyword: string
+}
+
+export type GetUnifiedOrdersByCursorResult = {
+ list: UTSJSONObject[]
+ nextCursor: string
+ hasMore: boolean
+}
+
+export type OrderCountsResult = {
+ all: number
+ pending: number
+ shipping: number
+ delivering: number
+ completed: number
+ aftersale: number
+ cancelled: number
+}
+
class SupabaseService {
// 获取当前用户ID
public getCurrentUserId(): string | null {
@@ -2596,6 +2643,102 @@ class SupabaseService {
}
}
+ async getRecommendProducts(params: UTSJSONObject): Promise> {
+ try {
+ const categoryId = params.getString('categoryId') ?? ''
+ const limitRaw = params.getNumber('limit')
+ const offsetRaw = params.getNumber('offset')
+ const limit = limitRaw != null && limitRaw > 0 ? limitRaw : 8
+ const offset = offsetRaw != null && offsetRaw >= 0 ? offsetRaw : 0
+ const excludeProductIdsRaw = params.get('excludeProductIds')
+ const excludeProductIds: Array = []
+
+ if (excludeProductIdsRaw != null) {
+ const normalized = JSON.parse(JSON.stringify(excludeProductIdsRaw))
+ if (Array.isArray(normalized)) {
+ for (let i = 0; i < normalized.length; i++) {
+ const item = normalized[i]
+ if (item != null) {
+ const itemText = '' + item
+ if (itemText !== '') {
+ excludeProductIds.push(itemText)
+ }
+ }
+ }
+ }
+ }
+
+ const collected: Array = []
+ const collectedIds: Array = []
+ const targetCount = offset + limit
+ const fetchLimit = limit > 20 ? limit : 20
+
+ const appendRows = (rows: any[]): void => {
+ for (let i = 0; i < rows.length; i++) {
+ const product = parseProductFromRaw(rows[i])
+ const productId = product.id ?? ''
+ if (productId === '') {
+ continue
+ }
+ if (excludeProductIds.indexOf(productId) >= 0) {
+ continue
+ }
+ if (collectedIds.indexOf(productId) >= 0) {
+ continue
+ }
+ collectedIds.push(productId)
+ collected.push(JSON.parse(JSON.stringify(product)) as UTSJSONObject)
+ }
+ }
+
+ const fetchBatch = async (preferCategory: boolean): Promise => {
+ let page = 1
+ while (collected.length < targetCount && page <= 10) {
+ let rows: any[] = []
+ if (preferCategory && categoryId !== '') {
+ const response = await this.getProductsByCategory(categoryId, page, fetchLimit)
+ rows = response.data as any[]
+ } else {
+ const response = await this.getProductsBySales(page, fetchLimit)
+ rows = response.data as any[]
+ }
+
+ if (rows == null) {
+ return true
+ }
+ if (rows.length === 0) {
+ return true
+ }
+
+ appendRows(rows)
+
+ if (rows.length < fetchLimit) {
+ return true
+ }
+ page += 1
+ }
+ return true
+ }
+
+ if (categoryId !== '') {
+ await fetchBatch(true)
+ }
+ if (collected.length < targetCount) {
+ await fetchBatch(false)
+ }
+
+ const result: Array = []
+ const end = collected.length < targetCount ? collected.length : targetCount
+ for (let i = offset; i < end; i++) {
+ result.push(collected[i])
+ }
+ return result
+ } catch (error) {
+ console.error('获取通用推荐商品异常:', error)
+ return []
+ }
+ }
+
// 获取特价商品(这里假设没有specific flag, just use logic or tag if exists, defaulting to hot for now or just skip)
// Modify to use compatible logic if badge column doesn't exist
async getDiscountProducts(limit: number = 10): Promise {
@@ -3648,6 +3791,9 @@ class SupabaseService {
detail_address: itemObj.getString('address_detail') ?? itemObj.getString('detail_address') ?? '',
is_default: itemObj.getBoolean('is_default') ?? false,
label: itemObj.getString('label') ?? '',
+ latitude: itemObj.getNumber('latitude') ?? 0,
+ longitude: itemObj.getNumber('longitude') ?? 0,
+ coordinate_type: itemObj.getString('coordinate_type') ?? 'gcj02',
created_at: itemObj.getString('created_at') ?? '',
updated_at: itemObj.getString('updated_at') ?? ''
}
@@ -3718,6 +3864,9 @@ class SupabaseService {
address_detail: address.detail_address,
postal_code: address.postal_code ?? null,
is_default: address.is_default ?? false,
+ latitude: address.latitude ?? null,
+ longitude: address.longitude ?? null,
+ coordinate_type: address.coordinate_type ?? 'gcj02',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
})
@@ -3760,6 +3909,9 @@ class SupabaseService {
if (address.postal_code != null) updateData['postal_code'] = address.postal_code
if (address.is_default != null) updateData['is_default'] = address.is_default
if (address.label != null) updateData['label'] = address.label
+ if (address.latitude != null) updateData['latitude'] = address.latitude
+ if (address.longitude != null) updateData['longitude'] = address.longitude
+ if (address.coordinate_type != null) updateData['coordinate_type'] = address.coordinate_type
updateData['updated_at'] = new Date().toISOString()
const response = await supa
@@ -3863,60 +4015,17 @@ class SupabaseService {
}
// 取消订单
- async cancelOrder(orderId: string): Promise {
- try {
- const userId = this.getCurrentUserId()
- if (userId == null) {
- return false
- }
-
- const response = await supa
- .from('ml_orders')
- .update({
- order_status: 5,
- updated_at: new Date().toISOString()
- })
- .eq('id', orderId)
- .eq('user_id', userId)
- .execute()
-
- if (response.error != null) {
- console.error('取消订单失败:', response.error)
- return false
- }
-
- return true
- } catch (e) {
- console.error('取消订单异常:', e)
- return false
- }
+ async cancelOrder(orderId: string, reason: string = '用户取消订单'): Promise {
+ return await this.cancelUnifiedOrder(orderId, 'goods', reason)
}
// 删除订单
async deleteOrder(orderId: string): Promise {
- try {
- const userId = this.getCurrentUserId()
- if (userId == null) {
- return false
- }
+ return await this.softDeleteUnifiedOrderForConsumer(orderId, 'goods')
+ }
- const response = await supa
- .from('ml_orders')
- .delete()
- .eq('id', orderId)
- .eq('user_id', userId)
- .execute()
-
- if (response.error != null) {
- console.error('删除订单失败:', response.error)
- return false
- }
-
- return true
- } catch (e) {
- console.error('删除订单异常:', e)
- return false
- }
+ async softDeleteOrderForConsumer(orderId: string): Promise {
+ return await this.softDeleteUnifiedOrderForConsumer(orderId, 'goods')
}
// 确认收货
@@ -4067,12 +4176,14 @@ class SupabaseService {
orderPayload.set('total_amount', orderData.total_amount)
orderPayload.set('paid_amount', 0)
orderPayload.set('shipping_address', shippingAddrStr)
- orderPayload.set('order_status', 1)
- orderPayload.set('payment_status', 1)
+ orderPayload.set('order_status', ORDER_STATUS_PENDING)
+ orderPayload.set('payment_status', PAYMENT_STATUS_UNPAID)
+ orderPayload.set('pay_expire_at', new Date(Date.now() + ORDER_PAY_TIMEOUT_SECONDS * 1000).toISOString())
+ orderPayload.set('consumer_deleted_at', null)
orderPayload.set('shipping_status', 1)
orderPayload.set('created_at', new Date().toISOString())
orderPayload.set('updated_at', new Date().toISOString())
-
+
console.log('[CreateOrder] 插入订单数据:', JSON.stringify(orderPayload))
console.log('[CreateOrder] 期望的订单号:', orderNo)
@@ -4269,6 +4380,18 @@ class SupabaseService {
}
}
+ async ensurePayExpireAt(orderId: string): Promise {
+ return await this.ensureUnifiedPayExpireAt(orderId, 'goods')
+ }
+
+ async markOrderPaymentCancelled(orderId: string): Promise {
+ return await this.markUnifiedOrderPaymentCancelled(orderId, 'goods')
+ }
+
+ async expireOrder(orderId: string): Promise {
+ return await this.expireUnifiedOrder(orderId, 'goods')
+ }
+
// 批量通过店铺创建订单
async createOrdersByShop(params: ShopOrderParams): Promise {
try {
@@ -4381,6 +4504,7 @@ class SupabaseService {
.from('ml_orders')
.select('*, ml_order_items(*), ml_shops(shop_name)')
.eq('user_id', userId)
+ .is('consumer_deleted_at', null)
.order('created_at', { ascending: false })
if (status > 0) {
@@ -4441,123 +4565,1096 @@ class SupabaseService {
return empty
}
}
-
- // 获取订单详情
- async getOrderDetail(orderId: string): Promise {
- try {
- console.log('[getOrderDetail] 开始获取订单详情,orderId:', orderId)
- const userId = this.getCurrentUserId()
- if (userId == null) return null
-
- const response = await supa
- .from('ml_orders')
- .select('*, ml_order_items(*)')
- .eq('id', orderId)
- .eq('user_id', userId!)
- .limit(1)
- .execute()
-
- console.log('[getOrderDetail] response.error:', response.error)
- console.log('[getOrderDetail] response.data:', JSON.stringify(response.data))
-
- if (response.error != null) {
- console.error('[getOrderDetail] 获取订单详情失败:', response.error)
- return null
+
+ private normalizeOrderList(orderList: UTSJSONObject[]): UTSJSONObject[] {
+ for (let i = 0; i < orderList.length; i++) {
+ const order = orderList[i]
+ const orderStr = JSON.stringify(order)
+ const orderObj = JSON.parse(orderStr) as UTSJSONObject
+ const itemsRaw = orderObj.get('ml_order_items')
+ if (itemsRaw != null && Array.isArray(itemsRaw)) {
+ const items = itemsRaw as UTSJSONObject[]
+ for (let j = 0; j < items.length; j++) {
+ const item = items[j]
+ const itemStr = JSON.stringify(item)
+ const itemObj = JSON.parse(itemStr) as UTSJSONObject
+ const imgUrl = itemObj.getString('image_url')
+ if (imgUrl != null) {
+ itemObj['image_url'] = fixImageUrl(imgUrl)
+ }
+ const prodImg = itemObj.getString('product_image')
+ if (prodImg != null) {
+ itemObj['product_image'] = fixImageUrl(prodImg)
+ }
+ items[j] = itemObj
+ }
+ orderObj['ml_order_items'] = items
+ orderList[i] = orderObj
}
-
- const rawData = response.data
- if (rawData == null) {
- console.log('[getOrderDetail] 数据为空')
- return null
- }
-
- const rawList = rawData as any[]
- if (rawList.length == 0) {
- console.log('[getOrderDetail] 未找到订单')
- return null
- }
-
- const orderData = rawList[0]
- console.log('[getOrderDetail] 成功获取订单')
-
- const orderObj = JSON.parse(JSON.stringify(orderData)) as UTSJSONObject
-
- const result = new UTSJSONObject()
- result.set('id', orderObj.get('id') ?? '')
- result.set('order_no', orderObj.get('order_no') ?? '')
- result.set('order_status', orderObj.get('order_status') ?? 1)
- result.set('user_id', orderObj.get('user_id') ?? '')
- result.set('merchant_id', orderObj.get('merchant_id') ?? '')
- result.set('product_amount', orderObj.get('product_amount') ?? 0)
- result.set('shipping_fee', orderObj.get('shipping_fee') ?? 0)
- result.set('total_amount', orderObj.get('total_amount') ?? 0)
- result.set('paid_amount', orderObj.get('paid_amount') ?? 0)
- result.set('discount_amount', orderObj.get('discount_amount') ?? 0)
- result.set('payment_method', orderObj.get('payment_method') ?? '')
- result.set('payment_status', orderObj.get('payment_status') ?? 1)
- result.set('shipping_status', orderObj.get('shipping_status') ?? 1)
- result.set('created_at', orderObj.get('created_at') ?? '')
- result.set('paid_at', orderObj.get('paid_at') ?? '')
- result.set('shipped_at', orderObj.get('shipped_at') ?? '')
- result.set('completed_at', orderObj.get('completed_at') ?? '')
- result.set('shipping_address', orderObj.get('shipping_address'))
- result.set('ml_order_items', orderObj.get('ml_order_items'))
- // 添加物流信息
- result.set('tracking_no', orderObj.get('tracking_no') ?? '')
- result.set('carrier_name', orderObj.get('carrier_name') ?? '')
- result.set('delivered_at', orderObj.get('delivered_at') ?? '')
-
- return result
- } catch (e) {
- console.error('[getOrderDetail] 获取订单详情异常:', e)
- return null
+ }
+ return orderList
}
+
+ private appendUniqueString(list: string[], value: string | null): void {
+ if (value == null || value == '') {
+ return
+ }
+ for (let i = 0; i < list.length; i++) {
+ if (list[i] == value) {
+ return
+ }
+ }
+ list.push(value)
+ }
+
+ private async getKeywordMatchedOrderIds(userId: string, keyword: string): Promise {
+ const matchedIds: string[] = []
+ const keywordPattern = '%' + keyword + '%'
+
+ try {
+ const orderResponse = await supa
+ .from('ml_orders')
+ .select('id')
+ .eq('user_id', userId)
+ .ilike('order_no', keywordPattern)
+ .limit(200)
+ .execute()
+
+ if (orderResponse.error == null && orderResponse.data != null && Array.isArray(orderResponse.data)) {
+ const orderRows = orderResponse.data as UTSJSONObject[]
+ for (let i = 0; i < orderRows.length; i++) {
+ const rowStr = JSON.stringify(orderRows[i])
+ const rowObj = JSON.parse(rowStr) as UTSJSONObject
+ this.appendUniqueString(matchedIds, rowObj.getString('id'))
+ }
+ }
+ } catch (error) {
+ console.error('[getKeywordMatchedOrderIds] 订单号搜索异常:', error)
+ }
+
+ try {
+ const itemResponse = await supa
+ .from('ml_order_items')
+ .select('order_id')
+ .ilike('product_name', keywordPattern)
+ .limit(200)
+ .execute()
+
+ if (itemResponse.error == null && itemResponse.data != null && Array.isArray(itemResponse.data)) {
+ const itemRows = itemResponse.data as UTSJSONObject[]
+ for (let i = 0; i < itemRows.length; i++) {
+ const rowStr = JSON.stringify(itemRows[i])
+ const rowObj = JSON.parse(rowStr) as UTSJSONObject
+ this.appendUniqueString(matchedIds, rowObj.getString('order_id'))
+ }
+ }
+ } catch (error) {
+ console.error('[getKeywordMatchedOrderIds] 商品名搜索异常:', error)
+ }
+
+ return matchedIds
+ }
+
+ private normalizeServiceStatus(status: string): string {
+ if (status == 'created' || status == 'submitted') return 'created'
+ if (status == 'paid') return 'paid'
+ if (status == 'assigned' || status == 'pending_dispatch' || status == 'pending_assignment') return 'assigned'
+ if (status == 'accepted' || status == 'pending_accept') return 'accepted'
+ if (status == 'rejected') return 'rejected'
+ if (status == 'departed' || status == 'waiting_departure' || status == 'on_the_way') return 'departed'
+ if (status == 'arrived' || status == 'checked_in') return 'arrived'
+ if (status == 'in_service' || status == 'serving') return 'in_service'
+ if (status == 'completed') return 'completed'
+ if (status == 'pending_acceptance' || status == 'pending_confirm' || status == 'pending_submit') return 'pending_acceptance'
+ if (status == 'accepted_by_user') return 'accepted_by_user'
+ if (status == 'reviewed') return 'reviewed'
+ if (status == 'settled') return 'settled'
+ if (status == 'cancelled') return 'cancelled'
+ return 'exception'
+ }
+
+ private getUnifiedServiceStatusNumber(status: string): number {
+ const normalizedStatus = this.normalizeServiceStatus(status)
+ if (normalizedStatus == 'created') return 1
+ if (normalizedStatus == 'paid' || normalizedStatus == 'assigned') return 2
+ if (normalizedStatus == 'accepted' || normalizedStatus == 'departed') return 3
+ if (normalizedStatus == 'arrived' || normalizedStatus == 'in_service') return 4
+ if (normalizedStatus == 'completed' || normalizedStatus == 'pending_acceptance' || normalizedStatus == 'accepted_by_user' || normalizedStatus == 'reviewed' || normalizedStatus == 'settled') return 5
+ if (normalizedStatus == 'cancelled' || normalizedStatus == 'rejected' || normalizedStatus == 'exception') return 8
+ return 3
+ }
+
+ private matchesServiceStatusTab(status: string, statusTab: string): boolean {
+ if (statusTab == 'all') return true
+ const normalizedStatus = this.normalizeServiceStatus(status)
+ if (statusTab == 'pending') return normalizedStatus == 'created'
+ if (statusTab == 'accepted') return normalizedStatus == 'paid' || normalizedStatus == 'assigned'
+ if (statusTab == 'scheduled') return normalizedStatus == 'accepted' || normalizedStatus == 'departed'
+ if (statusTab == 'inservice') return normalizedStatus == 'arrived' || normalizedStatus == 'in_service'
+ if (statusTab == 'completed') return normalizedStatus == 'completed' || normalizedStatus == 'pending_acceptance' || normalizedStatus == 'accepted_by_user' || normalizedStatus == 'reviewed' || normalizedStatus == 'settled'
+ if (statusTab == 'aftersale') return false
+ if (statusTab == 'inprogress') return normalizedStatus == 'paid' || normalizedStatus == 'assigned' || normalizedStatus == 'accepted' || normalizedStatus == 'departed' || normalizedStatus == 'arrived' || normalizedStatus == 'in_service'
+ return true
+ }
+
+ private formatServiceAppointmentText(value: string): string {
+ if (value == '') {
+ return ''
+ }
+ if (value.indexOf('上午') >= 0 || value.indexOf('下午') >= 0 || value.indexOf('晚上') >= 0) {
+ return value
+ }
+ const parsed = Date.parse(value)
+ if (!isNaN(parsed)) {
+ const date = new Date(parsed)
+ let year = date.getFullYear()
+ const currentYear = new Date().getFullYear()
+ if (year < currentYear - 1) {
+ year = currentYear
+ }
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hour = String(date.getHours()).padStart(2, '0')
+ const minute = String(date.getMinutes()).padStart(2, '0')
+ return year + '-' + month + '-' + day + ' ' + hour + ':' + minute
+ }
+ const monthDayMatch = value.match(/(\d{2})\/(\d{2})/)
+ const timeMatch = value.match(/(\d{2}:\d{2}(\s*-\s*\d{2}:\d{2})?)/)
+ if (monthDayMatch != null) {
+ const month = monthDayMatch[1] ?? ''
+ const day = monthDayMatch[2] ?? ''
+ const timeText = timeMatch != null ? (timeMatch[1] ?? '') : ''
+ if (month != '' && day != '') {
+ return String(new Date().getFullYear()) + '-' + month + '-' + day + (timeText != '' ? ' ' + timeText.replace(/\s+/g, '') : '')
+ }
+ }
+ return value.replace('T', ' ')
+ }
+
+ private buildUnifiedServiceOrder(rawOrder: any): UTSJSONObject {
+ const orderObj = JSON.parse(JSON.stringify(rawOrder)) as UTSJSONObject
+ const addressSnapshotRaw = orderObj.get('address_snapshot_json')
+ const serviceSnapshotRaw = orderObj.get('service_snapshot_json')
+ let addressObj: UTSJSONObject | null = null
+ let serviceObj: UTSJSONObject | null = null
+
+ try {
+ if (addressSnapshotRaw != null) {
+ const addressText = JSON.stringify(addressSnapshotRaw)
+ if (addressText.startsWith('"')) {
+ addressObj = JSON.parse(orderObj.getString('address_snapshot_json') ?? '{}') as UTSJSONObject
+ } else {
+ addressObj = JSON.parse(addressText) as UTSJSONObject
+ }
+ }
+ } catch (e) {
+ addressObj = null
+ }
+
+ try {
+ if (serviceSnapshotRaw != null) {
+ const serviceText = JSON.stringify(serviceSnapshotRaw)
+ if (serviceText.startsWith('"')) {
+ serviceObj = JSON.parse(orderObj.getString('service_snapshot_json') ?? '{}') as UTSJSONObject
+ } else {
+ serviceObj = JSON.parse(serviceText) as UTSJSONObject
+ }
+ }
+ } catch (e) {
+ serviceObj = null
+ }
+
+ const normalizedStatus = this.getUnifiedServiceStatusNumber(orderObj.getString('status') ?? '')
+ const fullAddress = addressObj != null ? (addressObj.getString('fullAddress') ?? '') : ''
+ const providerName = orderObj.getString('staff_name') ?? ''
+ const serviceName = orderObj.getString('service_name') ?? (serviceObj != null ? (serviceObj.getString('serviceName') ?? '') : '')
+ const servicePrice = serviceObj != null ? (serviceObj.getNumber('price') ?? 0) : 0
+
+ const serviceInfo = new UTSJSONObject()
+ serviceInfo.set('service_name', serviceName)
+ serviceInfo.set('service_image', '/static/images/default.png')
+ serviceInfo.set('appointment_time', this.formatServiceAppointmentText(orderObj.getString('appointment_time') ?? ''))
+ serviceInfo.set('address', fullAddress)
+ serviceInfo.set('contact_name', orderObj.getString('contact_name') ?? '')
+ serviceInfo.set('contact_phone', orderObj.getString('contact_phone') ?? '')
+ serviceInfo.set('provider_name', providerName)
+
+ const unifiedOrder = new UTSJSONObject()
+ const rawPaymentStatus = orderObj.getNumber('payment_status')
+ let paymentStatus = rawPaymentStatus ?? 0
+ if (paymentStatus <= 0) {
+ if (normalizedStatus == ORDER_STATUS_PENDING) {
+ paymentStatus = PAYMENT_STATUS_UNPAID
+ } else if (normalizedStatus == ORDER_STATUS_CANCELLED || normalizedStatus == ORDER_STATUS_TIMEOUT_LEGACY) {
+ paymentStatus = PAYMENT_STATUS_TIMEOUT
+ } else {
+ paymentStatus = 2
+ }
+ }
+ unifiedOrder.set('id', orderObj.getString('id') ?? '')
+ unifiedOrder.set('order_no', orderObj.getString('order_no') ?? '')
+ unifiedOrder.set('biz_type', 'service')
+ unifiedOrder.set('source', 'service')
+ unifiedOrder.set('status', normalizedStatus)
+ unifiedOrder.set('order_status', normalizedStatus)
+ unifiedOrder.set('payment_status', paymentStatus)
+ unifiedOrder.set('pay_expire_at', orderObj.getString('pay_expire_at') ?? '')
+ unifiedOrder.set('cancel_reason', orderObj.getString('cancel_reason') ?? '')
+ unifiedOrder.set('created_at', orderObj.getString('created_at') ?? '')
+ unifiedOrder.set('create_time', orderObj.getString('created_at') ?? '')
+ unifiedOrder.set('product_amount', servicePrice)
+ unifiedOrder.set('shipping_fee', 0)
+ unifiedOrder.set('total_amount', servicePrice)
+ unifiedOrder.set('paid_amount', servicePrice)
+ unifiedOrder.set('merchant_id', orderObj.getString('current_staff_id') ?? '')
+ unifiedOrder.set('shop_name', providerName != '' ? providerName : '康养上门服务')
+ unifiedOrder.set('service_info', serviceInfo)
+ unifiedOrder.set('ml_order_items', [] as UTSJSONObject[])
+ return unifiedOrder
+ }
+
+ private async getServiceOrdersByCursor(params: GetUnifiedOrdersByCursorParams): Promise {
+ const emptyResult: GetUnifiedOrdersByCursorResult = {
+ list: [] as UTSJSONObject[],
+ nextCursor: '',
+ hasMore: false
+ }
+
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return emptyResult
+ }
+
+ let query = supa
+ .from('hss_service_orders')
+ .select('*')
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .order('created_at', { ascending: false })
+ .limit(200)
+
+ const cursor = params.cursor.trim()
+ if (cursor != '') {
+ query = query.lt('created_at', cursor)
+ }
+
+ const response = await query.execute()
+ if (response.error != null || response.data == null || !Array.isArray(response.data)) {
+ return emptyResult
+ }
+
+ const keyword = params.keyword.trim()
+ const filtered = [] as UTSJSONObject[]
+ const rawList = response.data as any[]
+ for (let i = 0; i < rawList.length; i++) {
+ const rawItem = rawList[i]
+ const rawObj = JSON.parse(JSON.stringify(rawItem)) as UTSJSONObject
+ const orderNo = rawObj.getString('order_no') ?? ''
+ const serviceName = rawObj.getString('service_name') ?? ''
+ const contactName = rawObj.getString('contact_name') ?? ''
+ const appointmentTime = rawObj.getString('appointment_time') ?? ''
+ const addressSnapshot = rawObj.getString('address_snapshot_json') ?? ''
+ const matchedKeyword = keyword == '' || orderNo.indexOf(keyword) >= 0 || serviceName.indexOf(keyword) >= 0 || contactName.indexOf(keyword) >= 0 || appointmentTime.indexOf(keyword) >= 0 || addressSnapshot.indexOf(keyword) >= 0
+ if (!matchedKeyword) {
+ continue
+ }
+ if (!this.matchesServiceStatusTab(rawObj.getString('status') ?? '', params.statusTab)) {
+ continue
+ }
+ filtered.push(this.buildUnifiedServiceOrder(rawItem))
+ }
+
+ const limit = params.limit <= 0 ? 10 : params.limit
+ let hasMore = false
+ let list = filtered
+ if (filtered.length > limit) {
+ hasMore = true
+ list = filtered.slice(0, limit)
+ }
+
+ let nextCursor = ''
+ if (list.length > 0) {
+ const lastOrder = list[list.length - 1]
+ nextCursor = lastOrder.getString('created_at') ?? ''
+ }
+
+ return {
+ list,
+ nextCursor,
+ hasMore
+ }
+ } catch (error) {
+ console.error('[getServiceOrdersByCursor] 查询异常:', error)
+ return emptyResult
+ }
+ }
+
+ async getUnifiedOrdersByCursor(params: GetUnifiedOrdersByCursorParams): Promise {
+ const emptyResult: GetUnifiedOrdersByCursorResult = {
+ list: [] as UTSJSONObject[],
+ nextCursor: '',
+ hasMore: false
+ }
+
+ try {
+ if (params.bizType == 'service') {
+ return await this.getServiceOrdersByCursor(params)
+ }
+
+ if (params.bizType == 'goods') {
+ const goodsResult = await this.getOrdersByCursor({
+ cursor: params.cursor,
+ limit: params.limit,
+ status: params.statusTab == 'pending' ? 1 : params.statusTab == 'shipping' ? 2 : params.statusTab == 'delivering' ? 3 : params.statusTab == 'completed' ? 4 : params.statusTab == 'aftersale' ? 6 : 0,
+ keyword: params.keyword
+ })
+ return {
+ list: goodsResult.list,
+ nextCursor: goodsResult.nextCursor,
+ hasMore: goodsResult.hasMore
+ }
+ }
+
+ const goodsStatus = params.statusTab == 'pending' ? 1 : params.statusTab == 'completed' ? 4 : params.statusTab == 'aftersale' ? 6 : 0
+ const goodsResult = await this.getOrdersByCursor({
+ cursor: '',
+ limit: 200,
+ status: goodsStatus,
+ keyword: params.keyword
+ })
+ const serviceResult = await this.getServiceOrdersByCursor({
+ cursor: '',
+ limit: 200,
+ bizType: 'service',
+ statusTab: params.statusTab,
+ keyword: params.keyword
+ })
+
+ let mergedList = [] as UTSJSONObject[]
+ for (let i = 0; i < goodsResult.list.length; i++) {
+ const goodsItem = goodsResult.list[i]
+ const status = goodsItem.getNumber('order_status') ?? 0
+ const includeGoods = params.statusTab == 'all' || (params.statusTab == 'pending' && status == 1) || (params.statusTab == 'completed' && status == 4) || (params.statusTab == 'aftersale' && (status == 6 || status == 7)) || (params.statusTab == 'inprogress' && (status == 2 || status == 3))
+ if (includeGoods) {
+ mergedList.push(goodsItem)
+ }
+ }
+ for (let i = 0; i < serviceResult.list.length; i++) {
+ mergedList.push(serviceResult.list[i])
+ }
+
+ mergedList.sort((left: UTSJSONObject, right: UTSJSONObject): number => {
+ const leftTime = left.getString('created_at') ?? ''
+ const rightTime = right.getString('created_at') ?? ''
+ if (leftTime == rightTime) return 0
+ return leftTime > rightTime ? -1 : 1
+ })
+
+ const cursor = params.cursor.trim()
+ if (cursor != '') {
+ const cursorFiltered = [] as UTSJSONObject[]
+ for (let i = 0; i < mergedList.length; i++) {
+ const createdAt = mergedList[i].getString('created_at') ?? ''
+ if (createdAt < cursor) {
+ cursorFiltered.push(mergedList[i])
+ }
+ }
+ mergedList = cursorFiltered
+ }
+
+ const limit = params.limit <= 0 ? 10 : params.limit
+ let hasMore = false
+ let list = mergedList
+ if (mergedList.length > limit) {
+ hasMore = true
+ list = mergedList.slice(0, limit)
+ }
+
+ let nextCursor = ''
+ if (list.length > 0) {
+ nextCursor = list[list.length - 1].getString('created_at') ?? ''
+ }
+
+ return {
+ list,
+ nextCursor,
+ hasMore
+ }
+ } catch (error) {
+ console.error('[getUnifiedOrdersByCursor] 查询异常:', error)
+ return emptyResult
+ }
+ }
+
+ async getServiceOrderStatusCounts(): Promise {
+ const counts = new UTSJSONObject()
+ counts.set('all', 0)
+ counts.set('pending', 0)
+ counts.set('accepted', 0)
+ counts.set('scheduled', 0)
+ counts.set('inservice', 0)
+ counts.set('completed', 0)
+ counts.set('aftersale', 0)
+
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return counts
+ }
+
+ const response = await supa
+ .from('hss_service_orders')
+ .select('status')
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .limit(500)
+ .execute()
+
+ if (response.error != null || response.data == null || !Array.isArray(response.data)) {
+ return counts
+ }
+
+ const rawList = response.data as any[]
+ counts.set('all', rawList.length)
+ for (let i = 0; i < rawList.length; i++) {
+ const rawObj = JSON.parse(JSON.stringify(rawList[i])) as UTSJSONObject
+ const normalizedStatus = this.normalizeServiceStatus(rawObj.getString('status') ?? '')
+ if (normalizedStatus == 'created') {
+ counts.set('pending', (counts.getNumber('pending') ?? 0) + 1)
+ } else if (normalizedStatus == 'paid' || normalizedStatus == 'assigned') {
+ counts.set('accepted', (counts.getNumber('accepted') ?? 0) + 1)
+ } else if (normalizedStatus == 'accepted' || normalizedStatus == 'departed') {
+ counts.set('scheduled', (counts.getNumber('scheduled') ?? 0) + 1)
+ } else if (normalizedStatus == 'arrived' || normalizedStatus == 'in_service') {
+ counts.set('inservice', (counts.getNumber('inservice') ?? 0) + 1)
+ } else if (normalizedStatus == 'completed' || normalizedStatus == 'pending_acceptance' || normalizedStatus == 'accepted_by_user' || normalizedStatus == 'reviewed' || normalizedStatus == 'settled') {
+ counts.set('completed', (counts.getNumber('completed') ?? 0) + 1)
+ }
+ }
+ return counts
+ } catch (error) {
+ console.error('[getServiceOrderStatusCounts] 统计异常:', error)
+ return counts
+ }
+ }
+
+ async getOrdersByCursor(params: GetOrdersByCursorParams): Promise {
+ const emptyResult: GetOrdersByCursorResult = {
+ list: [] as UTSJSONObject[],
+ nextCursor: '',
+ hasMore: false
+ }
+
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return emptyResult
+ }
+
+ const limit = params.limit <= 0 ? 10 : params.limit
+ const queryLimit = limit + 1
+
+ let query = supa
+ .from('ml_orders')
+ .select('*, ml_order_items(*), ml_shops(shop_name)')
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .order('created_at', { ascending: false })
+ .limit(queryLimit)
+
+ if (params.status > 0) {
+ if (params.status == 6) {
+ query = query.in('order_status', [6, 7])
+ } else {
+ query = query.eq('order_status', params.status)
+ }
+ }
+
+ const keyword = params.keyword.trim()
+ if (keyword != '') {
+ const matchedOrderIds = await this.getKeywordMatchedOrderIds(userId, keyword)
+ if (matchedOrderIds.length == 0) {
+ return emptyResult
+ }
+ query = query.in('id', matchedOrderIds)
+ }
+
+ const cursor = params.cursor.trim()
+ if (cursor != '') {
+ query = query.lt('created_at', cursor)
+ }
+
+ const response = await query.execute()
+ if (response.error != null) {
+ console.error('[getOrdersByCursor] 查询失败:', response.error)
+ throw response.error
+ }
+
+ const rawData = response.data
+ if (rawData == null || !Array.isArray(rawData)) {
+ return emptyResult
+ }
+
+ const rawList = rawData as UTSJSONObject[]
+ let hasMore = false
+ let list = rawList
+ if (rawList.length > limit) {
+ hasMore = true
+ list = rawList.slice(0, limit)
+ }
+
+ const normalizedList = this.normalizeOrderList(list)
+
+ let nextCursor = ''
+ if (normalizedList.length > 0) {
+ const lastOrder = normalizedList[normalizedList.length - 1]
+ const lastOrderStr = JSON.stringify(lastOrder)
+ const lastOrderObj = JSON.parse(lastOrderStr) as UTSJSONObject
+ nextCursor = lastOrderObj.getString('created_at') ?? ''
+ }
+
+ return {
+ list: normalizedList,
+ nextCursor: nextCursor,
+ hasMore: hasMore
+ }
+ } catch (error) {
+ console.error('[getOrdersByCursor] 查询异常:', error)
+ return emptyResult
+ }
+ }
+
+ async getOrderCounts(): Promise {
+ const emptyCounts: OrderCountsResult = {
+ all: 0,
+ pending: 0,
+ shipping: 0,
+ delivering: 0,
+ completed: 0,
+ aftersale: 0,
+ cancelled: 0
+ }
+
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return emptyCounts
+ }
+
+ const queryCount = async (statusList: number[]): Promise => {
+ let query = supa
+ .from('ml_orders')
+ .select('*', { count: 'exact' })
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .limit(1)
+
+ if (statusList.length == 1) {
+ query = query.eq('order_status', statusList[0])
+ } else if (statusList.length > 1) {
+ query = query.in('order_status', statusList)
+ }
+
+ const response = await query.execute()
+ if (response.error != null) {
+ console.error('[getOrderCounts] 统计失败:', response.error)
+ return 0
+ }
+
+ return response.total ?? 0
+ }
+
+ const all = await queryCount([])
+ const pending = await queryCount([1])
+ const shipping = await queryCount([2])
+ const delivering = await queryCount([3])
+ const completed = await queryCount([4])
+ const aftersale = await queryCount([6, 7])
+ const cancelled = await queryCount([5])
+
+ return {
+ all,
+ pending,
+ shipping,
+ delivering,
+ completed,
+ aftersale,
+ cancelled
+ }
+ } catch (error) {
+ console.error('[getOrderCounts] 统计异常:', error)
+ return emptyCounts
+ }
+ }
+
+ isUuidOrderId(orderId: string): boolean {
+ if (orderId == null || orderId == '') {
+ return false
+ }
+ const normalized = orderId.toLowerCase()
+ const parts = normalized.split('-')
+ if (parts.length != 5) {
+ return false
+ }
+ const expectedLengths = [8, 4, 4, 4, 12]
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i]
+ if (part.length != expectedLengths[i]) {
+ return false
+ }
+ for (let j = 0; j < part.length; j++) {
+ const ch = part.charAt(j)
+ const isDigit = ch >= '0' && ch <= '9'
+ const isHexLower = ch >= 'a' && ch <= 'f'
+ if (!isDigit && !isHexLower) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+
+ private isServiceOrderSource(orderId: string, source: string): boolean {
+ return source == 'service' || orderId.startsWith('so-')
+ }
+
+ private getUnifiedOrderTableName(orderId: string, source: string): string {
+ return this.isServiceOrderSource(orderId, source) ? 'hss_service_orders' : 'ml_orders'
+ }
+
+ private logUnifiedOrderRoute(action: string, orderId: string, source: string, tableName: string): void {
+ console.log('[' + action + '] source =', source)
+ console.log('[' + action + '] orderId =', orderId)
+ console.log('[' + action + '] tableName =', tableName)
+ }
+
+ private logPostgrestFailure(action: string, response: any): void {
+ console.error('[' + action + '] 请求失败:', response?.error)
+ console.error('[' + action + '] response.data:', JSON.stringify(response?.data))
+ console.error('[' + action + '] response.status:', response?.status)
+ }
+
+ private buildUnifiedGoodsOrderResult(rawOrder: any): UTSJSONObject {
+ const orderObj = JSON.parse(JSON.stringify(rawOrder)) as UTSJSONObject
+ const result = new UTSJSONObject()
+ result.set('id', orderObj.get('id') ?? '')
+ result.set('order_no', orderObj.get('order_no') ?? '')
+ result.set('biz_type', 'goods')
+ result.set('source', 'goods')
+ result.set('status', orderObj.get('order_status') ?? 1)
+ result.set('order_status', orderObj.get('order_status') ?? 1)
+ result.set('user_id', orderObj.get('user_id') ?? '')
+ result.set('merchant_id', orderObj.get('merchant_id') ?? '')
+ result.set('product_amount', orderObj.get('product_amount') ?? 0)
+ result.set('shipping_fee', orderObj.get('shipping_fee') ?? 0)
+ result.set('total_amount', orderObj.get('total_amount') ?? 0)
+ result.set('paid_amount', orderObj.get('paid_amount') ?? 0)
+ result.set('discount_amount', orderObj.get('discount_amount') ?? 0)
+ result.set('payment_method', orderObj.get('payment_method') ?? '')
+ result.set('payment_status', orderObj.get('payment_status') ?? 1)
+ result.set('cancel_reason', orderObj.get('cancel_reason') ?? '')
+ result.set('pay_expire_at', orderObj.get('pay_expire_at') ?? '')
+ result.set('consumer_deleted_at', orderObj.get('consumer_deleted_at') ?? '')
+ result.set('shipping_status', orderObj.get('shipping_status') ?? 1)
+ result.set('created_at', orderObj.get('created_at') ?? '')
+ result.set('paid_at', orderObj.get('paid_at') ?? '')
+ result.set('shipped_at', orderObj.get('shipped_at') ?? '')
+ result.set('completed_at', orderObj.get('completed_at') ?? '')
+ result.set('shipping_address', orderObj.get('shipping_address'))
+ result.set('ml_order_items', orderObj.get('ml_order_items'))
+ result.set('items', orderObj.get('ml_order_items'))
+ result.set('ml_shops', orderObj.get('ml_shops'))
+ result.set('tracking_no', orderObj.get('tracking_no') ?? '')
+ result.set('carrier_name', orderObj.get('carrier_name') ?? '')
+ result.set('delivered_at', orderObj.get('delivered_at') ?? '')
+ return result
+ }
+
+ private buildUnifiedServiceOrderDetailResult(rawOrder: any): UTSJSONObject {
+ const orderObj = JSON.parse(JSON.stringify(rawOrder)) as UTSJSONObject
+ const unifiedOrder = this.buildUnifiedServiceOrder(rawOrder)
+ const appointmentTime = this.formatServiceAppointmentText(orderObj.getString('appointment_time') ?? '')
+ const fullAddress = unifiedOrder.get('service_info')
+ let serviceInfo = new UTSJSONObject()
+ if (fullAddress != null) {
+ serviceInfo = JSON.parse(JSON.stringify(fullAddress)) as UTSJSONObject
+ }
+
+ const contactName = orderObj.getString('contact_name') ?? ''
+ const contactPhone = orderObj.getString('contact_phone') ?? ''
+ const addressText = serviceInfo.getString('address') ?? ''
+
+ const shippingAddress = new UTSJSONObject()
+ shippingAddress.set('name', contactName)
+ shippingAddress.set('phone', contactPhone)
+ shippingAddress.set('address', addressText)
+ shippingAddress.set('detail', addressText)
+ shippingAddress.set('province', '')
+ shippingAddress.set('city', '')
+ shippingAddress.set('district', '')
+
+ const item = new UTSJSONObject()
+ item.set('id', orderObj.getString('id') ?? '')
+ item.set('product_id', orderObj.getString('service_id') ?? '')
+ item.set('product_name', serviceInfo.getString('service_name') ?? (orderObj.getString('service_name') ?? '服务订单'))
+ item.set('sku_name', appointmentTime)
+ item.set('spec', appointmentTime)
+ item.set('product_image', serviceInfo.getString('service_image') ?? '/static/images/default.png')
+ item.set('image', serviceInfo.getString('service_image') ?? '/static/images/default.png')
+ item.set('price', unifiedOrder.getNumber('total_amount') ?? 0)
+ item.set('quantity', 1)
+
+ const items = [] as UTSJSONObject[]
+ items.push(item)
+
+ unifiedOrder.set('status', unifiedOrder.getNumber('order_status') ?? 1)
+ unifiedOrder.set('shipping_address', shippingAddress)
+ unifiedOrder.set('items', items)
+ unifiedOrder.set('service_info', serviceInfo)
+ unifiedOrder.set('contact_name', contactName)
+ unifiedOrder.set('contact_phone', contactPhone)
+ unifiedOrder.set('appointment_time', appointmentTime)
+ unifiedOrder.set('address', addressText)
+ unifiedOrder.set('payment_method', '')
+ unifiedOrder.set('discount_amount', 0)
+ unifiedOrder.set('paid_at', '')
+ unifiedOrder.set('shipped_at', '')
+ unifiedOrder.set('completed_at', orderObj.getString('completed_at') ?? '')
+ return unifiedOrder
+ }
+
+ async getUnifiedOrderDetail(orderId: string, source: string): Promise {
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return null
+ }
+
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ this.logUnifiedOrderRoute('getUnifiedOrderDetail', orderId, source, tableName)
+
+ if (tableName == 'hss_service_orders') {
+ const response = await supa
+ .from('hss_service_orders')
+ .select('*')
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .limit(1)
+ .execute()
+
+ console.log('[getUnifiedOrderDetail] response.data =', JSON.stringify(response.data))
+ if (response.error != null) {
+ this.logPostgrestFailure('getUnifiedOrderDetail', response)
+ return null
+ }
+ const rawList = response.data as any[]
+ if (rawList == null || rawList.length == 0) {
+ console.log('[getUnifiedOrderDetail] 未找到订单')
+ return null
+ }
+ return this.buildUnifiedServiceOrderDetailResult(rawList[0])
+ }
+
+ const response = await supa
+ .from('ml_orders')
+ .select('*, ml_order_items(*), ml_shops(shop_name)')
+ .eq('user_id', userId)
+ .is('consumer_deleted_at', null)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .limit(1)
+ .execute()
+
+ console.log('[getUnifiedOrderDetail] response.data =', JSON.stringify(response.data))
+ if (response.error != null) {
+ this.logPostgrestFailure('getUnifiedOrderDetail', response)
+ return null
+ }
+ const rawList = response.data as any[]
+ if (rawList == null || rawList.length == 0) {
+ console.log('[getUnifiedOrderDetail] 未找到订单')
+ return null
+ }
+ return this.buildUnifiedGoodsOrderResult(rawList[0])
+ } catch (e) {
+ console.error('[getUnifiedOrderDetail] 获取订单详情异常:', e)
+ return null
+ }
+ }
+
+ async ensureUnifiedPayExpireAt(orderId: string, source: string): Promise {
+ try {
+ const latestOrder = await this.getUnifiedOrderDetail(orderId, source)
+ if (latestOrder == null) {
+ return ''
+ }
+
+ const currentExpireAt = latestOrder.getString('pay_expire_at') ?? ''
+ if (currentExpireAt != '') {
+ return currentExpireAt
+ }
+
+ const createdAt = latestOrder.getString('created_at') ?? ''
+ const createdDate = new Date(createdAt)
+ if (createdAt == '' || isNaN(createdDate.getTime())) {
+ return ''
+ }
+
+ const expireAtIso = new Date(createdDate.getTime() + ORDER_PAY_TIMEOUT_SECONDS * 1000).toISOString()
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return ''
+ }
+
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ this.logUnifiedOrderRoute('ensureUnifiedPayExpireAt', orderId, source, tableName)
+ const response = await supa
+ .from(tableName)
+ .update({
+ pay_expire_at: expireAtIso,
+ updated_at: new Date().toISOString()
+ })
+ .eq('user_id', userId)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .is('pay_expire_at', null)
+ .execute()
+
+ if (response.error != null) {
+ this.logPostgrestFailure('ensureUnifiedPayExpireAt', response)
+ return ''
+ }
+ return expireAtIso
+ } catch (e) {
+ console.error('[ensureUnifiedPayExpireAt] 异常:', e)
+ return ''
+ }
+ }
+
+ async cancelUnifiedOrder(orderId: string, source: string, reason: string = '用户取消订单'): Promise {
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return false
+ }
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ const isService = tableName == 'hss_service_orders'
+ this.logUnifiedOrderRoute('cancelUnifiedOrder', orderId, source, tableName)
+ const nowIso = new Date().toISOString()
+
+ const updatePayload = new UTSJSONObject()
+ updatePayload.set('payment_status', PAYMENT_STATUS_TIMEOUT)
+ updatePayload.set('cancel_reason', reason)
+ updatePayload.set('cancelled_at', nowIso)
+ updatePayload.set('updated_at', nowIso)
+ if (isService) {
+ updatePayload.set('status', 'cancelled')
+ } else {
+ updatePayload.set('order_status', ORDER_STATUS_CANCELLED)
+ }
+
+ const response = await supa
+ .from(tableName)
+ .update(updatePayload)
+ .eq('user_id', userId)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .execute()
+
+ if (response.error != null) {
+ this.logPostgrestFailure('cancelUnifiedOrder', response)
+ return false
+ }
+ return response.data != null && (!Array.isArray(response.data) || response.data.length > 0)
+ } catch (e) {
+ console.error('[cancelUnifiedOrder] 异常:', e)
+ return false
+ }
+ }
+
+ async expireUnifiedOrder(orderId: string, source: string): Promise {
+ try {
+ const latestOrder = await this.getUnifiedOrderDetail(orderId, source)
+ if (latestOrder == null) {
+ return false
+ }
+ const latestStatus = latestOrder.getNumber('order_status') ?? ORDER_STATUS_PENDING
+ const latestPaymentStatus = latestOrder.getNumber('payment_status') ?? PAYMENT_STATUS_UNPAID
+ const latestCancelReason = latestOrder.getString('cancel_reason') ?? ''
+ const latestExpireAt = latestOrder.getString('pay_expire_at') ?? ''
+ if (latestStatus == ORDER_STATUS_CANCELLED || latestStatus == ORDER_STATUS_TIMEOUT_LEGACY || latestPaymentStatus == PAYMENT_STATUS_TIMEOUT || latestCancelReason.indexOf('超时') >= 0) {
+ return true
+ }
+
+ let effectiveExpireAt = latestExpireAt
+ if (effectiveExpireAt == '') {
+ effectiveExpireAt = await this.ensureUnifiedPayExpireAt(orderId, source)
+ }
+ const expireMs = effectiveExpireAt != '' ? new Date(effectiveExpireAt).getTime() : 0
+ if (expireMs <= 0 || expireMs > Date.now()) {
+ return false
+ }
+
+ return await this.cancelUnifiedOrder(orderId, source, ORDER_TIMEOUT_CANCEL_REASON)
+ } catch (e) {
+ console.error('[expireUnifiedOrder] 异常:', e)
+ return false
+ }
+ }
+
+ async softDeleteUnifiedOrderForConsumer(orderId: string, source: string): Promise {
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return false
+ }
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ this.logUnifiedOrderRoute('softDeleteUnifiedOrderForConsumer', orderId, source, tableName)
+ const response = await supa
+ .from(tableName)
+ .update({
+ consumer_deleted_at: new Date().toISOString(),
+ updated_at: new Date().toISOString()
+ })
+ .eq('user_id', userId)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .execute()
+
+ if (response.error != null) {
+ this.logPostgrestFailure('softDeleteUnifiedOrderForConsumer', response)
+ return false
+ }
+ return response.data != null && (!Array.isArray(response.data) || response.data.length > 0)
+ } catch (e) {
+ console.error('[softDeleteUnifiedOrderForConsumer] 异常:', e)
+ return false
+ }
+ }
+
+ async markUnifiedOrderPaymentCancelled(orderId: string, source: string): Promise {
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ return false
+ }
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ const isService = tableName == 'hss_service_orders'
+ this.logUnifiedOrderRoute('markUnifiedOrderPaymentCancelled', orderId, source, tableName)
+
+ const updatePayload = new UTSJSONObject()
+ updatePayload.set('payment_status', PAYMENT_STATUS_UNPAID)
+ updatePayload.set('updated_at', new Date().toISOString())
+ if (isService) {
+ updatePayload.set('status', 'created')
+ } else {
+ updatePayload.set('order_status', ORDER_STATUS_PENDING)
+ }
+
+ const response = await supa
+ .from(tableName)
+ .update(updatePayload)
+ .eq('user_id', userId)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+ .execute()
+
+ if (response.error != null) {
+ this.logPostgrestFailure('markUnifiedOrderPaymentCancelled', response)
+ return false
+ }
+ return true
+ } catch (e) {
+ console.error('[markUnifiedOrderPaymentCancelled] 异常:', e)
+ return false
+ }
+ }
+
+ async payUnifiedOrder(orderId: string, source: string, paymentMethod: string, amount: number): Promise {
+ try {
+ const userId = this.getCurrentUserId()
+ if (userId == null || userId == '') {
+ console.error('[payUnifiedOrder] 用户未登录')
+ return false
+ }
+
+ const latestOrder = await this.getUnifiedOrderDetail(orderId, source)
+ if (latestOrder == null) {
+ console.error('[payUnifiedOrder] 订单不存在,无法支付')
+ return false
+ }
+
+ const latestStatus = latestOrder.getNumber('order_status') ?? ORDER_STATUS_PENDING
+ const latestPaymentStatus = latestOrder.getNumber('payment_status') ?? PAYMENT_STATUS_UNPAID
+ const latestCancelReason = latestOrder.getString('cancel_reason') ?? ''
+ const latestPayExpireAt = latestOrder.getString('pay_expire_at') ?? ''
+ const expireMs = latestPayExpireAt != '' ? new Date(latestPayExpireAt).getTime() : 0
+ const isExpired = latestStatus == ORDER_STATUS_CANCELLED || latestStatus == ORDER_STATUS_TIMEOUT_LEGACY || latestPaymentStatus == PAYMENT_STATUS_TIMEOUT || latestCancelReason.indexOf('超时') >= 0 || (expireMs > 0 && expireMs <= Date.now())
+
+ if (isExpired) {
+ await this.expireUnifiedOrder(orderId, source)
+ console.error('[payUnifiedOrder] 订单已超时,拒绝支付')
+ return false
+ }
+ if (latestStatus != ORDER_STATUS_PENDING || latestPaymentStatus != PAYMENT_STATUS_UNPAID) {
+ console.error('[payUnifiedOrder] 订单状态已变更,拒绝支付:', latestStatus, latestPaymentStatus)
+ return false
+ }
+
+ const tableName = this.getUnifiedOrderTableName(orderId, source)
+ const isService = tableName == 'hss_service_orders'
+ this.logUnifiedOrderRoute('payUnifiedOrder', orderId, source, tableName)
+ const nowIso = new Date().toISOString()
+
+ const updatePayload = new UTSJSONObject()
+ updatePayload.set('payment_status', PAYMENT_STATUS_PAID)
+ updatePayload.set('updated_at', nowIso)
+ if (isService) {
+ updatePayload.set('status', 'paid')
+ } else {
+ updatePayload.set('order_status', ORDER_STATUS_PAID_OR_SHIPPING)
+ updatePayload.set('payment_method', paymentMethod)
+ updatePayload.set('paid_at', nowIso)
+ updatePayload.set('paid_amount', amount)
+ }
+
+ let query = supa
+ .from(tableName)
+ .update(updatePayload)
+ .eq('user_id', userId)
+ .eq('payment_status', PAYMENT_STATUS_UNPAID)
+ .or(`id.eq.${orderId},order_no.eq.${orderId}`)
+
+ if (isService) {
+ query = query.eq('status', 'created')
+ } else {
+ query = query.eq('order_status', ORDER_STATUS_PENDING)
+ }
+ if (latestPayExpireAt != '') {
+ query = query.gt('pay_expire_at', nowIso)
+ }
+
+ const response = await query.execute()
+ if (response.error != null) {
+ this.logPostgrestFailure('payUnifiedOrder', response)
+ return false
+ }
+ if (response.data == null || (Array.isArray(response.data) && response.data.length === 0)) {
+ console.error('[payUnifiedOrder] 没有订单被更新,支付前状态校验未通过')
+ return false
+ }
+ return true
+ } catch (e) {
+ console.error('[payUnifiedOrder] 支付异常:', e)
+ return false
+ }
+ }
+
+ // 获取订单详情
+ async getOrderDetail(orderId: string): Promise {
+ return await this.getUnifiedOrderDetail(orderId, 'goods')
}
// 支付订单
async payOrder(orderId: string, paymentMethod: string, amount: number): Promise {
- try {
- const userId = this.getCurrentUserId()
- if (userId == null) {
- console.error('[payOrder] 用户未登录')
- return false
- }
-
- console.log('[payOrder] 开始更新订单状态, orderId:', orderId, 'userId:', userId)
-
- const updatePayload = new UTSJSONObject()
- updatePayload.set('order_status', 2)
- updatePayload.set('payment_status', 1)
- updatePayload.set('payment_method', paymentMethod)
- updatePayload.set('payment_time', new Date().toISOString())
- updatePayload.set('paid_amount', amount)
- updatePayload.set('updated_at', new Date().toISOString())
-
- console.log('[payOrder] 更新数据:', JSON.stringify(updatePayload))
-
- const response = await supa
- .from('ml_orders')
- .update(updatePayload)
- .eq('id', orderId)
- .eq('user_id', userId)
- .execute()
-
- if (response.error != null) {
- console.error('[payOrder] 更新订单失败:', response.error)
- return false
- }
-
- console.log('[payOrder] 订单状态更新成功')
-
- if (paymentMethod === 'balance') {
- console.log('[payOrder] 余额支付,暂不扣减余额')
- }
-
- return true
- } catch (e) {
- console.error('[payOrder] 支付异常:', e)
- return false
- }
+ return await this.payUnifiedOrder(orderId, 'goods', paymentMethod, amount)
}
// 根据ID获取订单信息
@@ -4571,52 +5668,18 @@ class SupabaseService {
console.log('[getOrderById] 查询订单, orderId:', orderId)
- const response = await supa
+ const query = supa
.from('ml_orders')
- .select('*')
- .eq('id', orderId)
- .eq('user_id', userId)
- .execute()
-
- if (response.error != null) {
- console.error('[getOrderById] 查询订单失败:', response.error)
- return null
- }
-
- const data = response.data as any[]
- if (data == null || data.length === 0) {
- console.log('[getOrderById] 未找到订单')
- return null
- }
-
- const orderRaw = data[0]
- let orderObj: UTSJSONObject
- if (orderRaw instanceof UTSJSONObject) {
- orderObj = orderRaw as UTSJSONObject
- } else {
- orderObj = JSON.parse(JSON.stringify(orderRaw)) as UTSJSONObject
- }
-
- console.log('[getOrderById] 订单数据:', JSON.stringify(orderObj))
- return orderObj
- } catch (e) {
- console.error('[getOrderById] 查询异常:', e)
- return null
- }
- }
-
- // 提交售后申请
- async createRefund(data: any): Promise {
- try {
- console.log('[createRefund] 开始处理退款申请')
- const userId = this.getCurrentUserId()
- if (userId == null) {
- console.log('[createRefund] 用户未登录')
- return { success: false, message: '请先登录' }
- }
-
- const d = JSON.parse(JSON.stringify(data)) as UTSJSONObject
- const orderId = d.getString('order_id') ?? ''
+ try {
+ const result = await this.getUnifiedOrderDetail(orderId, 'goods')
+ if (result == null) {
+ return null
+ }
+ return result
+ } catch (e) {
+ console.error('[getOrderById] 查询订单异常:', e)
+ return null
+ }
const refundType = d.getNumber('refund_type')
const refundReason = d.getString('refund_reason')
const refundAmount = d.getNumber('refund_amount')
@@ -4888,27 +5951,35 @@ class SupabaseService {
// 如果是数组,取第一项
if (Array.isArray(data)) {
const arr = data as any[]
- if (arr.length > 0) {
- data = arr[0]
- }
+ if (arr.length == 0) {
+ data = null
+ } else {
+ data = arr[0]
+ }
}
- let val:number = 0
- if (data instanceof UTSJSONObject) {
+ if (data == null) {
+ console.log('[Supabase] Wallet table returned empty array')
+ } else {
+ let val:number = 0
+ if (data instanceof UTSJSONObject) {
val = data.getNumber('balance') ?? 0
// 尝试字符串转换,防止精度丢失导致转为string
if (val === 0 && data.getString('balance') != null) {
val = parseFloat(data.getString('balance')!)
}
return val
- } else {
+ } else if (data != null) {
// 对于 Map 或 loose object
- const jsonObj = JSON.parse(JSON.stringify(data)) as UTSJSONObject
- val = jsonObj.getNumber('balance') ?? 0
- if (val === 0 && jsonObj.getString('balance') != null) {
- val = parseFloat(jsonObj.getString('balance')!)
+ const plainObj = JSON.parse(JSON.stringify(data))
+ const rawBalance = plainObj['balance']
+ if (typeof rawBalance === 'number') {
+ val = rawBalance as number
+ } else if (typeof rawBalance === 'string' && rawBalance != '') {
+ val = parseFloat(rawBalance as string)
}
return val
+ }
}
}
@@ -4935,7 +6006,6 @@ class SupabaseService {
async getUserPoints(): Promise {
try {
const userId = this.getCurrentUserId()
- console.log('[Supabase] getUserPoints userId:', userId)
if (userId == null) return 0
// 查 ml_user_points
@@ -4948,8 +6018,6 @@ class SupabaseService {
if (res.error != null) {
console.error('[Supabase] getUserPoints error:', res.error)
- } else {
- console.log('[Supabase] getUserPoints data:', res.data)
}
if (res.error == null && res.data != null) {
@@ -4957,18 +6025,25 @@ class SupabaseService {
// 如果是数组,取第一项
if (Array.isArray(data)) {
const arr = data as any[]
- if (arr.length > 0) {
- data = arr[0]
- }
+ if (arr.length == 0) {
+ data = null
+ } else {
+ data = arr[0]
+ }
}
if (data instanceof UTSJSONObject) {
return data.getNumber('points') ?? 0
- } else {
+ } else if (data != null) {
// 尝试转为 UTSJSONObject
- const jsonObj = JSON.parse(JSON.stringify(data)) as UTSJSONObject
- const val = jsonObj.getNumber('points')
- if (val != null) return val
+ const plainObj = JSON.parse(JSON.stringify(data))
+ const rawPoints = plainObj['points']
+ if (typeof rawPoints === 'number') {
+ return rawPoints as number
+ }
+ if (typeof rawPoints === 'string' && rawPoints != '') {
+ return parseFloat(rawPoints as string)
+ }
return 0
}
@@ -4980,14 +6055,20 @@ class SupabaseService {
if (profile instanceof UTSJSONObject) {
return profile.getNumber('points') ?? 0
} else {
- const pObj = JSON.parse(JSON.stringify(profile)) as UTSJSONObject
- return pObj.getNumber('points') ?? 0
+ const plainProfile = JSON.parse(JSON.stringify(profile))
+ const rawPoints = plainProfile['points']
+ if (typeof rawPoints === 'number') {
+ return rawPoints as number
+ }
+ if (typeof rawPoints === 'string' && rawPoints != '') {
+ return parseFloat(rawPoints as string)
+ }
+ return 0
}
}
return 0
- } catch (e) {
- console.error('[Supabase] getUserPoints exception:', e)
+ } catch (e) {
return 0
}
}
diff --git a/报错信息.txt b/报错信息.txt
index b449fc3a..907bce32 100644
--- a/报错信息.txt
+++ b/报错信息.txt
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
uni.api.esm.js:1042 POST http://192.168.1.62:18000/auth/v1/token?grant_type=password 401 (Unauthorized)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ uni.api.esm.js:1042
invokeApi @ uni.api.esm.js:330
@@ -12,239 +13,579 @@ s @ regeneratorRuntime.js?forceSync=true:1
_ @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+=======
+mp.esm.js:529 [getOrderDetail] 开始获取订单详情,orderId: so-1779679148063-14086
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] response.error: null
+mp.esm.js:529 [getOrderDetail] response.data: []
+mp.esm.js:529 [getOrderDetail] 未找到订单
+mp.esm.js:529 [confirmPayment] 开始支付, orderId: so-1779679148063-14086 method: wechat
+mp.esm.js:529 [getOrderDetail] 开始获取订单详情,orderId: so-1779679148063-14086
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] response.error: null
+mp.esm.js:529 [getOrderDetail] response.data: []
+mp.esm.js:529 [getOrderDetail] 未找到订单
+mp.esm.js:529 [payOrder] 订单不存在,无法支付(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee73$ @ supabaseService.uts:5491
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+request @ ak-req.uts:148
+_callee9$ @ aksupa.uts:887
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+signIn @ aksupa.uts:876
+_callee3$ @ delivery.uts:1506
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+loginDelivery @ delivery.uts:1500
+_callee$ @ deliveryService.uts:42
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+loginDelivery @ deliveryService.uts:41
+_callee5$ @ login.uvue:495
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+handleLogin @ login.uvue:477
+=======
+payOrder @ supabaseService.uts:5481
+_callee6$ @ payment.uvue:1291
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+confirmPayment @ payment.uvue:1232
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+<<<<<<< HEAD
+Show 19 more frames
+mp.esm.js:529 [ak-req] ★ 401 Unauthorized(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee2$ @ ak-req.uts:336
+=======
+mp.esm.js:529 [confirmPayment] 支付结果: false
+mp.esm.js:529 [confirmPayment] payOrder 返回 false(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee6$ @ payment.uvue:1295
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+request @ ak-req.uts:148
+_callee9$ @ aksupa.uts:887
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+signIn @ aksupa.uts:876
+_callee3$ @ delivery.uts:1506
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+loginDelivery @ delivery.uts:1500
+_callee$ @ deliveryService.uts:42
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+loginDelivery @ deliveryService.uts:41
+_callee5$ @ login.uvue:495
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+handleLogin @ login.uvue:477
+=======
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+confirmPayment @ payment.uvue:1232
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+<<<<<<< HEAD
+Show 14 more frames
+mp.esm.js:529 [ak-req] url: http://192.168.1.62:18000/auth/v1/token?grant_type=password(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee2$ @ ak-req.uts:337
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+=======
+mp.esm.js:529 [getOrderDetail] 开始获取订单详情,orderId: so-1779679148063-14086
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] response.error: null
+mp.esm.js:529 [getOrderDetail] response.data: []
+mp.esm.js:529 [getOrderDetail] 未找到订单
+mp.esm.js:529 [getOrderDetail] 开始获取订单详情,orderId: so-1779679148063-14086
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] response.error: null
+mp.esm.js:529 [getOrderDetail] response.data: []
+mp.esm.js:529 [getOrderDetail] 未找到订单
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: return=representation
+uni.api.esm.js:1042 PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ uni.api.esm.js:1042
+invokeApi @ uni.api.esm.js:330
+promiseApi @ uni.api.esm.js:889
+(anonymous) @ ak-req.uts:214
+doOnce @ ak-req.uts:213
+_loop$ @ ak-req.uts:312
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+s @ regeneratorRuntime.js?forceSync=true:1
+_ @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+request @ ak-req.uts:148
+<<<<<<< HEAD
+_callee9$ @ aksupa.uts:887
+=======
+_callee19$ @ aksupa.uts:1288
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+signIn @ aksupa.uts:876
+_callee3$ @ delivery.uts:1506
+=======
+requestWithAutoRefresh @ aksupa.uts:1287
+_callee14$ @ aksupa.uts:1141
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+loginDelivery @ delivery.uts:1500
+_callee$ @ deliveryService.uts:42
+=======
+update @ aksupa.uts:1117
+_callee$ @ aksupa.uts:477
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+loginDelivery @ deliveryService.uts:41
+_callee5$ @ login.uvue:495
+=======
+execute @ aksupa.uts:369
+_callee61$ @ supabaseService.uts:4508
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+handleLogin @ login.uvue:477
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+Show 14 more frames
+mp.esm.js:529 [ak-req] auth-mode: apikey-only(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee2$ @ ak-req.uts:338
+=======
+markOrderPaymentCancelled @ supabaseService.uts:4491
+_callee6$ @ payment.uvue:1271
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+request @ ak-req.uts:148
+_callee9$ @ aksupa.uts:887
+=======
+confirmPayment @ payment.uvue:1232
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+Show 21 more frames
+mp.esm.js:529 [markOrderPaymentCancelled] 保留待付款订单失败: UniError: 请求失败: 400
+ at _construct (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/construct.js?forceSync=true:1:1227)
+ at new r (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/wrapNativeSuper.js?forceSync=true:1:1357)
+ at UniError2. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/createSuper.js?forceSync=true:1:1176)
+ at new UniError2 (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:890:22)
+ at Object.toUniError (weapp:///http://127.0.0.1:60394/appservice/utils/utils.js?t=wechat&s=1779670571338&v=44243ae0f2468b2c481e39a715a29204:77:18)
+ at AkSupa._callee19$ (weapp:///http://127.0.0.1:60394/appservice/components/supadb/aksupa.js?t=wechat&s=1779670571338&v=f87225c7384edc68270412f64fbd4e8a:1959:41)
+ at s (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1588)
+ at Generator. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:2925)
+ at Generator.next (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1951)
+ at fulfilled (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:10009:24)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee61$ @ supabaseService.uts:4511
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+markOrderPaymentCancelled @ supabaseService.uts:4491
+_callee6$ @ payment.uvue:1271
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+fulfilled @ uni.mp.esm.js:1134
+Promise.then (async)
+step @ uni.mp.esm.js:1134
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+confirmPayment @ payment.uvue:1232
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+mp.esm.js:529 收到订单更新事件: UTSJSONObject {orderId: "so-1779679148063-14086", status: 1, paymentStatus: 1, cancelReason: "", payExpireAt: "", …}
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_user_profiles filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_user_coupons filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&status=eq.1&expire_at=gt.2026-05-25T03%3A20%3A04.256Z
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_user_profiles?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)%2C%20ml_shops(shop_name)&order=created_at.desc&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: (none)
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_user_coupons?select=id&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&status=eq.1&expire_at=gt.2026-05-25T03%3A20%3A04.256Z
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_user_balance filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_user_balance?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.1
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.1
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_user_points filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_user_points?select=points&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact,return=representation,single-object
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.2
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.2
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.3
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.3
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.4
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.4
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=in.(6,7)
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=in.(6,7)
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrders] response.error: null
+mp.esm.js:529 [getOrders] 订单数量: 104
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.5
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_status=eq.5
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_orders?select=status&limit=500&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] 开始获取订单详情,orderId: so-1779679148063-14086
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_orders?select=*%2C%20ml_order_items(*)&limit=1&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&consumer_deleted_at=is.null&order_no=eq.so-1779679148063-14086
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: count=exact
+mp.esm.js:529 [getOrderDetail] response.error: null
+mp.esm.js:529 [getOrderDetail] response.data: []
+mp.esm.js:529 [getOrderDetail] 未找到订单
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: return=representation
+uni.api.esm.js:1042 PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ uni.api.esm.js:1042
+invokeApi @ uni.api.esm.js:330
+promiseApi @ uni.api.esm.js:889
+(anonymous) @ ak-req.uts:214
+doOnce @ ak-req.uts:213
+_loop$ @ ak-req.uts:312
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+s @ regeneratorRuntime.js?forceSync=true:1
+_ @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
request @ ak-req.uts:148
-_callee9$ @ aksupa.uts:887
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-signIn @ aksupa.uts:876
-_callee3$ @ delivery.uts:1506
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ delivery.uts:1500
-_callee$ @ deliveryService.uts:42
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ deliveryService.uts:41
-_callee5$ @ login.uvue:495
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-handleLogin @ login.uvue:477
-callWithErrorHandling @ vue.runtime.esm.js:1356
-callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
-invoke @ vue.runtime.esm.js:6223
-setTimeout (async)
-invoker @ vue.runtime.esm.js:6232
-Show 19 more frames
-mp.esm.js:529 [ak-req] ★ 401 Unauthorized(env: Windows,mp,1.06.2504030; lib: 3.15.2)
-(anonymous) @ mp.esm.js:529
-__f__ @ uni.api.esm.js:590
-_callee2$ @ ak-req.uts:336
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-request @ ak-req.uts:148
-_callee9$ @ aksupa.uts:887
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-signIn @ aksupa.uts:876
-_callee3$ @ delivery.uts:1506
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ delivery.uts:1500
-_callee$ @ deliveryService.uts:42
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ deliveryService.uts:41
-_callee5$ @ login.uvue:495
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-handleLogin @ login.uvue:477
-callWithErrorHandling @ vue.runtime.esm.js:1356
-callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
-invoke @ vue.runtime.esm.js:6223
-setTimeout (async)
-invoker @ vue.runtime.esm.js:6232
-Show 14 more frames
-mp.esm.js:529 [ak-req] url: http://192.168.1.62:18000/auth/v1/token?grant_type=password(env: Windows,mp,1.06.2504030; lib: 3.15.2)
-(anonymous) @ mp.esm.js:529
-__f__ @ uni.api.esm.js:590
-_callee2$ @ ak-req.uts:337
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-request @ ak-req.uts:148
-_callee9$ @ aksupa.uts:887
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-signIn @ aksupa.uts:876
-_callee3$ @ delivery.uts:1506
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ delivery.uts:1500
-_callee$ @ deliveryService.uts:42
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-loginDelivery @ deliveryService.uts:41
-_callee5$ @ login.uvue:495
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-handleLogin @ login.uvue:477
-callWithErrorHandling @ vue.runtime.esm.js:1356
-callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
-invoke @ vue.runtime.esm.js:6223
-setTimeout (async)
-invoker @ vue.runtime.esm.js:6232
-Show 14 more frames
-mp.esm.js:529 [ak-req] auth-mode: apikey-only(env: Windows,mp,1.06.2504030; lib: 3.15.2)
-(anonymous) @ mp.esm.js:529
-__f__ @ uni.api.esm.js:590
-_callee2$ @ ak-req.uts:338
-s @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-(anonymous) @ regeneratorRuntime.js?forceSync=true:1
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-fulfilled @ uni.mp.esm.js:1134
-Promise.then (async)
-step @ uni.mp.esm.js:1134
-(anonymous) @ uni.mp.esm.js:1134
-__awaiter @ uni.mp.esm.js:1134
-request @ ak-req.uts:148
-_callee9$ @ aksupa.uts:887
+_callee19$ @ aksupa.uts:1288
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
signIn @ aksupa.uts:876
_callee3$ @ delivery.uts:1506
+=======
+requestWithAutoRefresh @ aksupa.uts:1287
+_callee14$ @ aksupa.uts:1141
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
loginDelivery @ delivery.uts:1500
_callee$ @ deliveryService.uts:42
+=======
+update @ aksupa.uts:1117
+_callee$ @ aksupa.uts:477
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
loginDelivery @ deliveryService.uts:41
_callee5$ @ login.uvue:495
+=======
+execute @ aksupa.uts:369
+_callee52$ @ supabaseService.uts:4038
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
handleLogin @ login.uvue:477
+=======
+cancelOrder @ supabaseService.uts:4018
+_callee5$ @ payment.uvue:1153
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+confirmCancelOrder @ payment.uvue:1141
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
setTimeout (async)
invoker @ vue.runtime.esm.js:6232
+<<<<<<< HEAD
Show 14 more frames
mp.esm.js:529 [ak-req] 发送 apikey: eyJhbG...7890(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
_callee2$ @ ak-req.uts:339
+=======
+Show 26 more frames
+mp.esm.js:529 取消订单失败: UniError: 请求失败: 400
+ at _construct (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/construct.js?forceSync=true:1:1227)
+ at new r (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/wrapNativeSuper.js?forceSync=true:1:1357)
+ at UniError2. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/createSuper.js?forceSync=true:1:1176)
+ at new UniError2 (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:890:22)
+ at Object.toUniError (weapp:///http://127.0.0.1:60394/appservice/utils/utils.js?t=wechat&s=1779670571338&v=44243ae0f2468b2c481e39a715a29204:77:18)
+ at AkSupa._callee19$ (weapp:///http://127.0.0.1:60394/appservice/components/supadb/aksupa.js?t=wechat&s=1779670571338&v=f87225c7384edc68270412f64fbd4e8a:1959:41)
+ at s (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1588)
+ at Generator. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:2925)
+ at Generator.next (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1951)
+ at fulfilled (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:10009:24)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee52$ @ supabaseService.uts:4041
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
+<<<<<<< HEAD
+=======
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+cancelOrder @ supabaseService.uts:4018
+_callee5$ @ payment.uvue:1153
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+confirmCancelOrder @ payment.uvue:1141
+callWithErrorHandling @ vue.runtime.esm.js:1356
+callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
+invoke @ vue.runtime.esm.js:6223
+setTimeout (async)
+invoker @ vue.runtime.esm.js:6232
+[自动热重载] 已开启代码文件保存后自动热重载
+mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_orders filter: id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c
+mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...YFxs | auth-mode: pre-set | prefer: return=representation
+uni.api.esm.js:1042 PATCH http://119.146.131.237:9126/rest/v1/ml_orders?id=eq.so-1779679148063-14086&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ uni.api.esm.js:1042
+invokeApi @ uni.api.esm.js:330
+promiseApi @ uni.api.esm.js:889
+(anonymous) @ ak-req.uts:214
+doOnce @ ak-req.uts:213
+_loop$ @ ak-req.uts:312
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+s @ regeneratorRuntime.js?forceSync=true:1
+_ @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
request @ ak-req.uts:148
+<<<<<<< HEAD
_callee9$ @ aksupa.uts:887
+=======
+_callee19$ @ aksupa.uts:1288
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
signIn @ aksupa.uts:876
_callee3$ @ delivery.uts:1506
+=======
+requestWithAutoRefresh @ aksupa.uts:1287
+_callee14$ @ aksupa.uts:1141
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
loginDelivery @ delivery.uts:1500
_callee$ @ deliveryService.uts:42
+=======
+update @ aksupa.uts:1117
+_callee$ @ aksupa.uts:477
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
loginDelivery @ deliveryService.uts:41
_callee5$ @ login.uvue:495
+=======
+execute @ aksupa.uts:369
+_callee52$ @ supabaseService.uts:4038
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
handleLogin @ login.uvue:477
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
@@ -256,12 +597,40 @@ mp.esm.js:529 [ak-req] 发送 Authorization: (MISSING!)(env: Windows,mp,1.06.250
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
_callee2$ @ ak-req.uts:340
+=======
+cancelOrder @ supabaseService.uts:4018
+_callee7$ @ orders.uvue:1422
+s @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ regeneratorRuntime.js?forceSync=true:1
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+doCancelOrder @ orders.uvue:1417
+success @ orders.uvue:1443
+(anonymous) @ uni.api.esm.js:946
+Show 25 more frames
+mp.esm.js:529 取消订单失败: UniError: 请求失败: 400
+ at _construct (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/construct.js?forceSync=true:1:1227)
+ at new r (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/wrapNativeSuper.js?forceSync=true:1:1357)
+ at UniError2. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/createSuper.js?forceSync=true:1:1176)
+ at new UniError2 (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:890:22)
+ at Object.toUniError (weapp:///http://127.0.0.1:60394/appservice/utils/utils.js?t=wechat&s=1779670571338&v=44243ae0f2468b2c481e39a715a29204:77:18)
+ at AkSupa._callee19$ (weapp:///http://127.0.0.1:60394/appservice/components/supadb/aksupa.js?t=wechat&s=1779670571338&v=f87225c7384edc68270412f64fbd4e8a:1959:41)
+ at s (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1588)
+ at Generator. (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:2925)
+ at Generator.next (weapp:///http://127.0.0.1:60394/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1951)
+ at fulfilled (weapp:///http://127.0.0.1:60394/appservice/common/vendor.js?t=wechat&s=1779670571338&v=2c3f4623fbd33b44aa0c0065a9eb296b:10009:24)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
+(anonymous) @ mp.esm.js:529
+__f__ @ uni.api.esm.js:590
+_callee52$ @ supabaseService.uts:4041
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
+<<<<<<< HEAD
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
@@ -269,11 +638,18 @@ step @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
request @ ak-req.uts:148
_callee9$ @ aksupa.uts:887
+=======
+(anonymous) @ uni.mp.esm.js:1134
+__awaiter @ uni.mp.esm.js:1134
+cancelOrder @ supabaseService.uts:4018
+_callee7$ @ orders.uvue:1422
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
+<<<<<<< HEAD
signIn @ aksupa.uts:876
_callee3$ @ delivery.uts:1506
s @ regeneratorRuntime.js?forceSync=true:1
@@ -459,4 +835,9 @@ Error: timeout
at WAServiceMainContext.js?t=wechat&v=3.15.2:1(env: Windows,mp,1.06.2504030; lib: 3.15.2)
15:42:21.953 [plugin:uts] Invalid end tag.
-15:42:22.203 at pages/mall/delivery/home/index.uvue:454:1
\ No newline at end of file
+15:42:22.203 at pages/mall/delivery/home/index.uvue:454:1
+=======
+doCancelOrder @ orders.uvue:1417
+success @ orders.uvue:1443
+(anonymous) @ uni.api.esm.js:946
+>>>>>>> d9103c9bf (完善下单逻辑及其ui展示,修复支付倒计时显示错误bug)