257 Commits
Author SHA1 Message Date
YunaiV 46ba5e6aef V2.4.0 发布~ 2024-12-31 12:45:10 +08:00
芋道源码 75d0b004dc !131 【缺陷修复】购物车提交的校验、bgStyle 样式
Merge pull request !131 from puhui999/dev
2024-12-19 12:39:47 +00:00
puhui999 52cfca1ca1 【缺陷修复】s-layout bgStyle 样式 2024-12-19 16:23:35 +08:00
puhui999 34be99fccb 【缺陷修复】购物车提交时的配送方式校验 2024-12-19 15:45:32 +08:00
YunaiV 164589f634 【代码评审】购物车提交的校验、bgStyle 样式 2024-12-11 13:08:55 +08:00
芋道源码 39869cd92d !129 【缺陷修复】购物车提交前校验
Merge pull request !129 from puhui999/dev
2024-12-11 05:01:19 +00:00
puhui999 a863300ae3 【缺陷修复】s-layout bgStyle 样式对齐 2024-12-09 15:05:44 +08:00
puhui999 8ac9f2714f 【缺陷修复】购物车同时结算两个商品,一个仅支持门店自提,一个仅支持快递发货,就会无法结算 2024-12-09 11:57:00 +08:00
芋道源码 dcd63dd54e !128 【缺陷修复】
Merge pull request !128 from puhui999/dev
2024-12-07 02:16:23 +00:00
puhui999 25fc972833 【缺陷修复】自己查看自己的分享时移除 shareId 2024-12-06 15:10:14 +08:00
puhui999 cde0de43ff 【缺陷修复】分销订单查询状态携带 2024-12-06 15:05:14 +08:00
puhui999 bfde5df0cd 【缺陷修复】异步绑定推广员失败的问题 2024-12-06 14:37:41 +08:00
puhui999 b92bf484df 【缺陷修复】商城装修: 顶部导航栏-文字链接支持 2024-12-06 11:26:53 +08:00
puhui999 1b02d0cefa Merge remote-tracking branch 'yudao/develop' into dev 2024-12-05 17:50:53 +08:00
puhui999 b24af1f71f 【缺陷修复】商城装修: TitleBar 样式同步 2024-12-05 15:38:40 +08:00
YunaiV 951a52d37b 【代码评审】积分界面 2024-12-01 18:19:12 +08:00
芋道源码 9616e660a6 !126 分享相关完善
Merge pull request !126 from puhui999/master
2024-12-01 08:58:39 +00:00
YunaiV da81f6df7d 【功能完善】商城:优化分销的界面 2024-12-01 16:57:54 +08:00
puhui999 c48951df8c 【功能修复】收款码上传问题 2024-11-29 15:52:41 +08:00
puhui999 1407350bf7 【功能完善】商品评论上传图片 2024-11-29 15:43:05 +08:00
puhui999 4c4134389e 【功能完善】小程序支持转发到朋友圈 2024-11-29 12:44:42 +08:00
puhui999 f98b1a2bea 【功能完善】小程序 forward 转发 2024-11-29 12:21:31 +08:00
puhui999 1ff8890d58 【功能完善】分享赚分享 2024-11-29 11:10:37 +08:00
puhui999 7be216ea4b 【功能完善】秒杀活动分享 2024-11-29 10:45:58 +08:00
puhui999 530410fcf0 【功能完善】积分商城分享 2024-11-29 10:29:31 +08:00
YunaiV 29a5024468 Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp
# Conflicts:
#	pages/activity/point/list.vue
2024-11-25 20:37:32 +08:00
YunaiV cb8431fbd4 Merge remote-tracking branch 'origin/develop' into develop 2024-11-20 09:18:57 +08:00
YunaiV 6900eb1909 【功能完善】商城:售后退货,已经完成 2024-11-20 09:18:50 +08:00
芋道源码 7793a69b9d !125 【功能完善】商城客服
Merge pull request !125 from puhui999/master
2024-11-19 01:12:35 +00:00
puhui999 7017561242 Merge remote-tracking branch 'yudao/develop' 2024-11-18 17:32:38 +08:00
puhui999 0cdaaac0ed 【功能完善】商城:客服消息查询 pageSize 改 limit 2024-11-18 17:19:24 +08:00
puhui999 18aa26dcae 【功能完善】商城:客服消息样式调整 2024-11-18 16:54:55 +08:00
YunaiV 87c6e967f2 【功能完善】商城:客服消息改为游标查询,消息 JSON 化 2024-11-10 18:53:26 +08:00
YunaiV 01c2dfa30d Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp into develop 2024-11-10 18:50:46 +08:00
YunaiV 1978484071 Merge branch 'master' of https://gitee.com/yudaocode/yudao-mall-uniapp 2024-11-10 18:50:11 +08:00
芋道源码 68680d0d0e !124 【功能完善】商城:客服消息改为游标查询,消息 JSON 化
Merge pull request !124 from puhui999/master
2024-11-10 10:49:46 +00:00
puhui999 92e89bae34 Merge remote-tracking branch 'refs/remotes/yudao/develop' 2024-11-10 18:11:58 +08:00
puhui999 94e0dd4f5d 【功能完善】商城:客服消息改为游标查询,消息 JSON 化 2024-11-10 17:56:58 +08:00
芋道源码 9a225c6ba5 !123 【功能修复】修复分佣商品界面预计佣金一直显示“计算中”的问题;
Merge pull request !123 from 卢越/master
2024-11-09 12:55:34 +00:00
卢越 6c80e36508 【功能修复】修复分佣商品界面预计佣金一直显示“计算中”的问题; 2024-11-07 17:00:56 +08:00
芋道源码 cf88789eef !119 修复在first_two模式的分类页面里商品名字无法显示的问题
Merge pull request !119 from 杨宇庆/N/A
2024-11-01 12:47:56 +00:00
芋道源码 98b3f1fc67 !118 pageNo: state.pagination.pageSize,改为 pageNo: state.pagination.pageNo,
Merge pull request !118 from yuannuoxin/N/A
2024-11-01 12:46:26 +00:00
芋道源码 a09e171bf7 !117 【轻量级 PR】:【修复】 积分商城:商品列表加载更多异常
Merge pull request !117 from Super Junior/fix_loadmore
2024-11-01 12:44:57 +00:00
杨宇庆 5ee9c18f4c fix: 修复在first_two模式的分类页面里商品名字无法显示的问题
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-11-01 07:11:58 +00:00
yuannuoxin b8132062ea pageNo: state.pagination.pageSize,改为 pageNo: state.pagination.pageNo,
Signed-off-by: yuannuoxin <3339694043@qq.com>
2024-10-30 05:06:09 +00:00
= 35c143039c Fix: 积分商城:商品列表加载更多异常 2024-10-28 20:25:50 +08:00
芋道源码 f94df74591 !116 【功能修复】修复真机环境下的图片上传问题
Merge pull request !116 from 卢越/master
2024-10-28 11:14:29 +00:00
卢越 e9b9ac1f7a 【功能修复】修复真机环境下的图片上传问题 2024-10-28 17:04:57 +08:00
YunaiV d916fe5c80 README 更新:单体、微服务的地址 2024-10-28 09:59:47 +08:00
YunaiV 315b5b852d README 更新:单体、微服务的地址 2024-10-28 09:58:22 +08:00
YunaiV 0213a4b88a 【功能修复】领取优惠劵在 validityType = 1 时(固定日期),展示不正确的问题 2024-10-27 20:23:29 +08:00
YunaiV 4bdc82786e 【功能修复】s-title-block 的 fontWeight 多加了 px 像素 2024-10-27 18:22:47 +08:00
YunaiV f36b183407 【功能优化】签到的样式 2024-10-27 18:17:36 +08:00
芋道源码 113aead37b !113 【优化】当原价高于售价时,才显示划线价格
Merge pull request !113 from 杨宇庆/N/A
2024-10-27 08:34:33 +00:00
芋道源码 90b6ae7d40 !114 【修复】当无优惠价格时,商品无法显示的问题
Merge pull request !114 from 杨宇庆/N/A
2024-10-27 08:34:04 +00:00
杨宇庆 959fe83d72 【修复】当无优惠价格时,商品无法显示的问题
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-10-16 16:51:52 +00:00
杨宇庆 f784d411b6 【优化】当原价高于售价时,才显示划线价格
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-10-16 16:39:30 +00:00
YunaiV c8f9043869 【功能修复】积分商城,商品不支持下拉展示更多 2024-10-13 21:41:25 +08:00
YunaiV 789ec69933 【功能优化】订单确认页,增加按照顺序自动选择物流方式,替代原有只选择“快递配送”的方式 2024-10-13 21:30:14 +08:00
YunaiV 4dd6e82309 Merge branch 'master' of https://gitee.com/yudaocode/yudao-mall-uniapp into develop 2024-10-13 21:08:13 +08:00
芋道源码 3051aed6b2 !112 【优化】1.分销中心用户卡片样式没有居中问题 2.推广排行榜周排行、月排行切换样式留白问题
Merge pull request !112 from FN/FIX-FN-241013
2024-10-13 13:07:55 +00:00
FN 8e50c5da49 【优化】1.分销中心用户卡片样式没有居中问题 2.推广排行榜周排行、月排行切换样式留白问题 2024-10-13 14:55:58 +08:00
YunaiV fd10210972 【新增功能】添加微信转账到零钱提现功能 2024-10-13 12:42:08 +08:00
芋道源码 c399bb2373 !109 【新增功能】添加微信转账到零钱提现功能
Merge pull request !109 from 痴货/jh-wxzz
2024-10-13 04:40:20 +00:00
YunaiV 9451092335 【修复】部分界面的 pageSize = 1,导致无法上啦刷新 2024-10-12 20:23:26 +08:00
YunaiV fd33176222 Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp 2024-10-12 20:22:46 +08:00
芋道源码 290ee5db04 !110 【修改BUG】修改分销记录只显示1条记录的问题
Merge pull request !110 from 痴货/jh-bug
2024-10-12 12:16:13 +00:00
痴货 07b630601a 【功能完善】积分商城:修改每页显示商品数量 2024-10-08 21:37:08 +08:00
痴货 b59c41c5f8 【功能优化】增加转账回调接口 2024-10-08 21:30:46 +08:00
痴货 a4ec421412 微信支付未完成 2024-10-08 21:30:37 +08:00
YunaiV 0961aa610b 【版本发布】2.3.0 发布~ 2024-10-07 15:28:12 +08:00
YunaiV 545d4259c4 Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp 2024-10-07 15:23:47 +08:00
YunaiV 1ffa754dbe 【功能完善】纯积分兑换时,直接跳转到订单详情 2024-10-04 19:17:40 +08:00
芋道源码 96d86f237d !108 【功能完善】商城: 积分商城
Merge pull request !108 from puhui999/master
2024-10-04 10:38:03 +00:00
puhui999 8e64ecebd1 Merge remote-tracking branch 'yudao/develop' 2024-10-04 14:00:11 +08:00
puhui999 98f912f113 【功能完善】积分商城:完善订单相关信息展示 2024-10-04 13:58:41 +08:00
puhui999 732aa7686c 【功能完善】积分商城:完善订单相关信息展示 2024-10-04 13:30:08 +08:00
puhui999 b2e40e7011 【功能完善】积分商城:完善订单相关信息展示 2024-10-04 12:36:18 +08:00
puhui999 d7a18f9c01 【功能完善】积分商城:完善积分+价格显示样式 2024-10-02 15:40:09 +08:00
puhui999 c5307513cc 【功能完善】积分商城:完善积分+价格显示样式 2024-10-02 15:02:22 +08:00
芋道源码 a5d073e798 !107 【功能完善】商城: 完善积分商城
Merge pull request !107 from puhui999/master
2024-10-02 07:00:43 +00:00
puhui999 9639c2f270 【功能完善】积分商城:完善积分+价格显示样式 2024-10-02 14:56:21 +08:00
puhui999 cf6b26e565 Merge remote-tracking branch 'yudao/develop'
# Conflicts:
#	pages.json
#	pages/activity/point/list.vue
2024-10-02 14:55:53 +08:00
puhui999 9171275d3d 【功能完善】积分商城:完善积分+价格显示样式 2024-10-02 14:53:46 +08:00
puhui999 2a2ce2eff6 【功能完善】积分商城:完善活动详情页样式 2024-10-02 14:30:18 +08:00
YunaiV b6635a5d5c 【功能优化】WebSocket 使用 refreshToken 认证,解决无法刷新访问令牌的问题 2024-10-02 14:19:23 +08:00
puhui999 da4cd2238e 【功能完善】积分商城:完善活动列表布局切换 2024-10-02 12:15:53 +08:00
puhui999 fb3120d818 【功能完善】积分商城:完善活动列表 2024-10-02 11:46:50 +08:00
YunaiV 5f496038c5 【代码评审】商城首页的 activity name 的展示 2024-10-01 19:00:19 +08:00
芋道源码 146f9ddc88 !104 【功能优化】秒杀详情页过期活动处理
Merge pull request !104 from 卢越/master
2024-10-01 10:57:24 +00:00
芋道源码 6abad0df4d !106 【功能优化】确认收货添加提示确认,提交售后信息后直接返回到订单详情
Merge pull request !106 from Lcp/pr
2024-10-01 09:33:30 +00:00
YunaiV 47ac7d1fbc 【功能】下单页的优惠明细弹窗,不展示积分、优惠劵、会员折扣,因为它们已经单独展示了 2024-10-01 10:15:08 +08:00
YunaiV 25e4686e30 Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp 2024-09-30 09:05:31 +08:00
Lcp b476305e30 【功能优化】确认收货添加提示确认 2024-09-29 16:44:01 +08:00
Lcp 518c898882 【功能优化】提交售后信息后直接返回到订单详情 2024-09-29 16:43:55 +08:00
YunaiV 09dc98db16 【代码评审】积分商城部分 2024-09-29 09:43:29 +08:00
芋道源码 ea98ba45ca !105 【功能完善】积分商城
Merge pull request !105 from puhui999/master
2024-09-29 01:23:47 +00:00
YunaiV 2e572639b0 【功能优化】修改 h5 的 base 是 /,解决 https://gitee.com/yudaocode/yudao-mall-uniapp/issues/IATB5V 静态资源访问的问题 2024-09-29 09:22:29 +08:00
YunaiV 4a0ea9b43c 【功能优化】文件上传,默认使用 server 模式,兼容更多存储器的类型 2024-09-28 20:25:32 +08:00
puhui999 6d762c5367 【功能完善】积分商城:完善活动详情页面 2024-09-28 16:33:26 +08:00
puhui999 2349faa1bc 【功能完善】积分商城:完善活动详情页面 2024-09-28 16:24:57 +08:00
puhui999 4cdb595a2a 【代码优化】商城装修:修复布局为单列小图时价格显示方式不正确的问题 2024-09-28 15:39:03 +08:00
puhui999 5552236952 【功能完善】积分商城: 首页活动装修显示 2024-09-28 14:43:04 +08:00
puhui999 d2f516e1ae 【功能完善】积分商城 2024-09-28 11:37:22 +08:00
卢越 cb39246850 【功能优化】拼团、秒杀组件的SPU名称使用活动的 2024-09-27 19:58:00 +08:00
卢越 18484f7ee8 【功能优化】秒杀详情页过期活动处理 2024-09-27 16:46:09 +08:00
芋道源码 2ca7f48131 !103 【功能新增】现已支持前端文件直传到OSS服务
Merge pull request !103 from 卢越/master
2024-09-26 01:24:22 +00:00
卢越 f491e87d6a 【功能优化】简化代码 2024-09-26 09:08:57 +08:00
卢越 6f8e82e6cf 【功能优化】简化代码 2024-09-25 23:16:50 +08:00
卢越 6c95891476 【功能优化】简化代码 2024-09-25 08:25:20 +08:00
卢越 76e7ed8f25 【功能优化】文件直传优化 2024-09-24 19:14:52 +08:00
卢越 f6b2eb6925 默认使用服务端上传文件 2024-09-24 17:17:18 +08:00
卢越 c23e7ac3d0 【功能新增】现已支持前端文件直传到OSS服务器 2024-09-24 17:16:38 +08:00
YunaiV ca1926a732 【功能优化】支付:查询支付订单时,增加 sync 主动轮询,解决支付宝、微信存在延迟的问题 2024-09-24 13:00:46 +08:00
YunaiV a1995c88e8 【功能优化】支付:查询支付订单时,增加 sync 主动轮询,解决支付宝、微信存在延迟的问题 2024-09-24 09:04:57 +08:00
YunaiV 27260f4a75 【功能完善】商城:支付宝 wap 的接入 2024-09-23 09:55:07 +08:00
YunaiV 0c5ef4a085 Merge branch 'master' of https://gitee.com/yudaocode/yudao-mall-uniapp into develop 2024-09-23 09:27:29 +08:00
YunaiV 3dad154ca0 【功能优化】商城:调整满减送的数据返回 2024-09-21 14:30:27 +08:00
芋道源码 e727974806 !102 【优化】优化满减提示显示
Merge pull request !102 from 痴货/develop-mall
2024-09-21 03:19:24 +00:00
痴货 ab709267be 【优化】优化满减显示 2024-09-18 18:11:54 +08:00
岳琳红 51cd8908ec 修改部分页面 2024-09-18 17:04:23 +08:00
芋道源码 56182dfbe9 !99 删除微信支付的临时测试代码
Merge pull request !99 from 杨宇庆/N/A
2024-09-17 04:11:30 +00:00
YunaiV 6b6fa0c257 【功能完善】订单确认价格时,展示具体的优惠信息 2024-09-16 19:38:48 +08:00
YunaiV ff6769001d 【功能完善】商品列表,增加满减送提示(优化) 2024-09-16 19:30:56 +08:00
YunaiV 9df0194aeb 【功能完善】商品详情,增加满减送提示 2024-09-16 18:15:13 +08:00
YunaiV b741a1d148 【功能完善】商品列表,展示满减送、限时折扣的信息 2024-09-16 09:16:16 +08:00
YunaiV f03fcf1373 Merge branch 'master' of https://gitee.com/yudaocode/yudao-mall-uniapp into develop
# Conflicts:
#	pages/goods/index.vue
#	pages/order/addressSelection.vue
#	pages/order/confirm.vue
#	sheep/components/s-goods-column/s-goods-column.vue
2024-09-16 08:24:54 +08:00
YunaiV 5a66e71809 【代码优化】差满减活动时间外,from 岳琳红 https://gitee.com/yudaocode/yudao-mall-uniapp/commit/26a18e7e9a78af4ec49f0d07785a0aead22b00cc 2024-09-16 08:18:32 +08:00
YunaiV e93322959e 【代码优化】差满减活动时间外,from 岳琳红 https://gitee.com/yudaocode/yudao-mall-uniapp/commit/26a18e7e9a78af4ec49f0d07785a0aead22b00cc 2024-09-16 08:15:54 +08:00
芋道源码 4a8a92f323 !98 【功能修复】修复拼团的单买价格
Merge pull request !98 from 卢越/master
2024-09-15 13:15:07 +00:00
芋道源码 ba56d9843f !101 批量优化或修改BUG
Merge pull request !101 from 痴货/develop-mall
2024-09-15 13:13:26 +00:00
YunaiV eb3d0ff6b0 【代码优化】商城:优化 /promotion/activity/list-by-spu-id 的逻辑 2024-09-15 17:59:42 +08:00
痴货 aa571f25f9 【功能】批量修改bug和功能 2024-09-15 17:32:29 +08:00
YunaiV 49e8b9f5c3 【功能完善】下单界面,接入门店自提的开关 2024-09-13 19:21:22 +08:00
杨宇庆 6af76a20f3 删除微信支付的临时测试代码
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-09-11 07:32:21 +00:00
芋道源码 55131bf0c3 !97 【功能优化】支付方式默认选中首个可用方式
Merge pull request !97 from Lcp/pr
2024-09-09 14:58:32 +00:00
卢越 f3c7387650 Merge remote-tracking branch 'origin/master' 2024-09-09 22:46:34 +08:00
卢越 b2d030f96b 【功能修复】修复拼团的单买价格 2024-09-09 22:46:24 +08:00
Lcp 97711bad3e 【功能优化】支付方式默认选中首个可用方式 2024-09-09 15:16:29 +08:00
YunaiV 29584a108b 【功能修复】秒杀装修重构 2024-09-09 09:22:06 +08:00
芋道源码 1432ec5996 !96 【功能修复】优惠券相关问题修正
Merge pull request !96 from 卢越/master
2024-09-09 01:21:15 +00:00
卢越 9521d4b557 【功能修复】秒杀装修重构 2024-09-08 11:39:28 +08:00
卢越 27af994667 【功能修复】修正订单优惠券选择列表中,可用和不可用优惠券的颜色 2024-09-08 10:27:46 +08:00
卢越 dbc192faa7 【功能修复】修正“暂无可用优惠券”字体颜色 2024-09-08 10:06:15 +08:00
卢越 5f8ed35521 【功能修复】订单确认页的可用优惠券数量统计 2024-09-08 09:56:21 +08:00
卢越 b42c151585 【功能修复】订单确认页的可用优惠券数量统计 2024-09-08 09:55:34 +08:00
YunaiV 3e2749672e CDN 使用 yudao 提供,解决不定期的 https 问题! 2024-09-07 16:15:43 +08:00
芋道源码 07098527ff !90 修复在线客服页面的一些问题
Merge pull request !90 from 英狐/master
2024-09-07 06:29:26 +00:00
YunaiV cb29c2c0aa 【BUG修复】授权登录成功后,每次都要填昵称和重新上传头像。
如果用户已经有头像和昵称,则不要弹出上传头像和填写昵称的信息弹框。
2024-09-07 14:28:02 +08:00
芋道源码 4be2b441f5 !93 【BUG修复】授权登录成功后,每次都要填昵称和重新上传头像。
Merge pull request !93 from heyho/N/A
2024-09-07 06:26:11 +00:00
芋道源码 2cd4095035 !95 【优化】分销中心 => 我的团队 页面样式,布局优化、美化
Merge pull request !95 from heyho/master
2024-09-07 06:23:40 +00:00
YunaiV 80b2bb6f5e 【功能修复】商城:选择拼团 SKU 时,存在遮罩的问题 https://gitee.com/yudaocode/yudao-mall-uniapp/issues/IAMK4A 2024-09-07 14:22:44 +08:00
YunaiV 9b6d1a9a97 【功能优化】商城:价格计算时,返回可用 + 不可用的优惠劵 2024-09-07 12:06:10 +08:00
YunaiV 735dc8c373 【代码优化】商城:拼团装修重构 2024-09-06 21:02:59 +08:00
芋道源码 fdca229a77 !91 【综合修复】拼团装修重构、处理无法发布到微信小程序的问题
Merge pull request !91 from 卢越/master
2024-09-06 12:56:49 +00:00
heyho fd8f9a6857 分销中心 => 我的团队 页面样式,布局优化、美化
原版本头部结构错乱,显示有误。

Signed-off-by: heyho <heywsk@qq.com>
2024-09-06 08:32:42 +00:00
heyho 9f5f4816d3 【BUG修复】授权登录成功后,每次都要填昵称和重新上传头像。
如果用户已经有头像和昵称,则不要弹出上传头像和填写昵称的信息弹框。

Signed-off-by: heyho <heywsk@qq.com>
2024-09-06 01:25:25 +00:00
卢越 089e0197cd 【代码优化】拼团重构 2024-09-05 22:34:59 +08:00
YunaiV 81cd02bbee 【修复】小程序端银行卡提现,银行名称改为下拉选择 2024-09-05 20:08:48 +08:00
芋道源码 1cfd974fed !92 【修复】小程序端银行卡提现,银行名称改为下拉选择。【新增接口】新增系统字典查询接口
Merge pull request !92 from heyho/master
2024-09-05 11:23:14 +00:00
卢越 82a82a4bed 【代码优化】接口地址修正 2024-09-05 17:11:39 +08:00
卢越 75e7e58b8d 【代码优化】代码格式化 2024-09-05 12:40:53 +08:00
卢越 b6dd2ae1eb 【代码优化】拼团活动逻辑优化 2024-09-05 11:32:30 +08:00
卢越 85441704cb 【代码优化】接口地址修正 2024-09-04 23:01:16 +08:00
heyho 089191e95d 【修复】小程序端银行卡提现,银行名称改为下拉选择。
由于后端接收的是银行名称参数会由配置字典进行解析,所以小程序端银行名称是不能让用户输入框手动输入的,需要查询后端字典配置进行显示和选择。提交的值是银行字典对应的value。

Signed-off-by: heyho <heywsk@qq.com>
2024-09-04 09:31:55 +00:00
heyho 9f33e44db0 【修复】小程序端银行卡提现,银行名称改为下拉选择。
由于后端接收的是银行名称参数会由配置字典进行解析,所以小程序端银行名称是不能让用户输入框手动输入的,需要查询后端字典配置进行显示和选择。提交的值是银行字典对应的value。

Signed-off-by: heyho <heywsk@qq.com>
2024-09-04 09:22:18 +00:00
heyho ed3d370de2 【新增接口】新增系统字典查询接口
通过字典类型查询字典数据

Signed-off-by: heyho <heywsk@qq.com>
2024-09-04 09:14:33 +00:00
卢越 36289d526c 【综合修复】改造拼团装修(仍需优化);处理无法发布到微信小程序的问题; 2024-09-03 15:36:18 +08:00
YunaiV 7268659193 【功能修复】拼团的单买价格展示不正确 2024-09-03 10:18:30 +08:00
PonyHu b1d6e1d687 【功能修复】在线客服消息列表窗体顶部导航的高度计算不准确,导致其被页面顶部标题栏遮住了一小部分;
【功能修复】在线客服不能收到服务端发来的最新消息(被判断为重置消息列表了);
【功能优化】在线客服不需要在滚动到底部时重置消息列表,因为没有必要;
2024-09-01 17:37:26 +08:00
YunaiV 89e86b84fe 增加优惠劵描述 2024-08-31 09:06:47 +08:00
芋道源码 77574e099a !89 【功能修复】修复商城部分BUG
Merge pull request !89 from 卢越/master
2024-08-26 10:40:33 +00:00
卢越 5e6adced12 【功能修复】修复商品秒杀不选择规格提示库存不足的问题 2024-08-26 17:44:37 +08:00
卢越 4fef5c5f0e 清理TODO 2024-08-26 17:00:29 +08:00
卢越 1bff364d38 【功能修复】修复商城商品评论无法上传图片的问题 2024-08-26 16:56:48 +08:00
芋道源码 abf1235916 !88 【功能修复】修复客服聊天列表页点击商品卡片无法显示商品详情的问题
Merge pull request !88 from 卢越/master
2024-08-25 13:55:10 +00:00
卢越 2c4ad9f78a 【功能修复】修复客服聊天列表页点击商品卡片无法显示商品详情的问题 2024-08-25 19:42:04 +08:00
YunaiV 75c5203c90 【代码评审】商城的 todo 进一步说明 2024-08-25 14:33:27 +08:00
芋道源码 a514de68b3 !87 【功能修复】修复顶部导航栏装修无效的问题
Merge pull request !87 from 卢越/master
2024-08-25 06:29:58 +00:00
卢越 522591c486 【功能修复】修复顶部导航栏装修无效的问题 2024-08-25 14:06:57 +08:00
芋道源码 263eb74695 !86 【功能修复】修复订单结算页没有优惠券但是提示优惠券不存在的问题#IAK656:issue
Merge pull request !86 from 卢越/master
2024-08-23 12:04:02 +00:00
卢越 53920d2efa 【功能修复】优惠券匹配问题 2024-08-22 22:01:14 +08:00
卢越 a0f85bf157 【功能修复】修复没有优惠券时点击确定提示优惠券找不到的问题;现在订单结算页面切换地址或者配送方式会重新计算价格 2024-08-22 21:59:07 +08:00
芋道源码 adc521022f !85 【代码优化】格式化配置文件
Merge pull request !85 from 卢越/master
2024-08-21 13:58:33 +00:00
卢越 356193aab5 【代码优化】格式化配置文件 2024-08-21 09:10:21 +08:00
芋道源码 07d167c624 !84 【功能优化】商品结算页现在可展示优惠券是否可用的原因
Merge pull request !84 from 卢越/master
2024-08-20 12:57:31 +00:00
卢越 01ee048a22 Merge remote-tracking branch 'origin/master' 2024-08-20 16:39:32 +08:00
卢越 512d87c6f2 【功能优化】商品结算页现在可展示优惠券是否可用以及是否可用的原因 2024-08-20 16:38:50 +08:00
芋道源码 af0643ce93 !83 【逻辑优化】现在活动库存可以真实显示
Merge pull request !83 from 卢越/master
2024-08-19 13:55:24 +00:00
卢越 9d333085cb 【逻辑优化】拼团库存使用总库存 2024-08-19 17:40:17 +08:00
卢越 d37b848307 【逻辑优化】更新活动的库存(拼团库存可能有逻辑问题) 2024-08-19 11:52:05 +08:00
芋道源码 28faf73c62 !82 【功能修复】修复拼团详情页的价格
Merge pull request !82 from 卢越/master
2024-08-18 05:32:29 +00:00
卢越 f10c9ec16c 【界面优化】优化装修秒杀列表的样式,补全关键信息 2024-08-18 13:03:52 +08:00
卢越 ef3a82ee14 【界面优化】优化装修拼团单列商品列表样式 2024-08-18 12:30:04 +08:00
卢越 47f4295316 【功能修复】修复拼团详情页的价格展示 2024-08-18 09:10:40 +08:00
卢越 8a6ec1cb02 【功能修复】修复拼团详情页的价格展示 2024-08-18 08:50:54 +08:00
芋道源码 edf158ec2c !81 【功能修复】修复商品拼团和秒杀价格显示
Merge pull request !81 from 卢越/master
2024-08-16 12:22:26 +00:00
卢越 f4adfde90b 【功能修复】修复商品拼团和秒杀价格的显示 2024-08-16 17:29:09 +08:00
芋道源码 283449fc3d !80 【功能修复】修复装修优惠券背景图片问题
Merge pull request !80 from 卢越/master
2024-08-15 13:43:47 +00:00
卢越 e66e1c6eac 【功能修复】修复优惠券排版问题和页面交互问题 2024-08-15 15:46:22 +08:00
卢越 ea8ea9e744 【功能优化】装修页面优惠券,如果每行两个优惠券,则让优惠券居中,而不是分散在两边 2024-08-15 14:51:15 +08:00
卢越 c8a95ef3ec 【功能修复】装修页面修复多个优惠券,第一个显示不全的问题 2024-08-15 14:27:27 +08:00
卢越 bf75dc2768 【功能修复】修复装修优惠券整体的背景图片问题;如果2列或者3排优惠券现在居中显示并且有间隔 2024-08-15 14:18:44 +08:00
YunaiV 0575bd56b0 Merge remote-tracking branch 'origin/master' 2024-08-15 09:24:49 +08:00
YunaiV 8154108fe9 【功能修复】拼团:未开启拼团的 SKU 需要禁用选择 2024-08-15 09:24:39 +08:00
芋道源码 7f1504214c !77 删除无用代码
Merge pull request !77 from 卢越/master
2024-08-14 16:29:51 +00:00
芋道源码 c6b55dc90b !78 购物车商品数量减为0时,删除错误
Merge pull request !78 from 邓飞鹏/master
2024-08-14 16:27:04 +00:00
芋道源码 ed7258b577 !79 完善拼团的剩余 TODO
Merge pull request !79 from puhui999/master
2024-08-14 16:12:22 +00:00
puhui999 6c0ba7d585 Merge remote-tracking branch 'refs/remotes/yudao/master' 2024-08-14 11:49:22 +08:00
puhui999 154a706af4 【代码优化】拼团:其它用户,打开团长的界面的相关操作实现 2024-08-14 11:46:59 +08:00
puhui999 fc396021c6 【代码优化】拼团:完善邀请拼团海报 2024-08-14 11:09:25 +08:00
puhui999 532503b65b 【代码优化】h5-router-mode: hash 改为 history 解决分享链接跳转失败 2024-08-14 11:08:55 +08:00
D b22cdae4fe 购物车商品数量减为0时,删除错误 2024-08-13 21:57:04 +08:00
卢越 42bcab7f28 删除无用输出 2024-08-13 12:29:02 +08:00
芋道源码 4db992d3fa !76 【功能修复】修复部分装修背景图片问题
Merge pull request !76 from 卢越/master
2024-08-12 16:34:44 +00:00
卢越 e9a91b84fc 【代码优化】优化参数命名 2024-08-12 14:13:11 +08:00
卢越 e13eedc2c2 【功能修复】修复装修菜单导航背景图片问题 2024-08-12 13:57:25 +08:00
卢越 be5db900a0 【功能修复】修复标题栏装修背景图片问题 2024-08-12 13:06:35 +08:00
卢越 34274cbaa8 【功能修复】修复装修宫格导航背景图片问题 2024-08-12 11:03:39 +08:00
卢越 438897b9d3 【功能修复】修复用户卡券装修背景图片问题 2024-08-12 10:15:51 +08:00
卢越 58af434a90 【功能修复】修复用户资产装修背景图片问题 2024-08-12 10:05:39 +08:00
芋道源码 83daf7f7c2 !75 【功能修复】商城模板装修
Merge pull request !75 from 卢越/master
2024-08-12 01:28:07 +00:00
卢越 35c75338ee 【功能修复】修复用户订单卡片装修的背景图片问题 2024-08-11 14:59:08 +08:00
卢越 19d3cb33e2 【功能修复】修复装修用户卡片使用图片无效的问题 2024-08-11 14:03:20 +08:00
YunaiV 5feab41b3d 【功能修复】拼团:未开启拼团的 SKU 需要禁用选择 2024-08-10 11:36:02 +08:00
YunaiV 51ba185e81 【代码优化】进入分类页的时候,自动勾选左侧一级分类(比如从首页进入) 2024-08-10 10:25:51 +08:00
芋道源码 92b9820f20 !74 进入分类页的时候自动勾选左侧一级分类(比如从首页进入)
Merge pull request !74 from 卢越/master
2024-08-10 02:23:32 +00:00
卢越 cc9f31f8c3 进入分类页的时候自动勾选左侧一级分类 2024-08-09 22:06:35 +08:00
芋道源码 374ccf43fd !71 修复一些 issues 提到的问题
Merge pull request !71 from puhui999/master
2024-08-09 13:38:49 +00:00
卢越 fd83bcf02a 进入分类页的时候自动勾选左侧一级分类 2024-08-09 21:26:29 +08:00
卢越 b093f99ab5 如果从主页点击某个分类进入分类页面,则自动选中左侧的一级分类 2024-08-09 20:16:56 +08:00
puhui999 e53cb63ce4 Merge remote-tracking branch 'refs/remotes/yudao/master' 2024-08-09 10:48:51 +08:00
puhui999 a9a522880d 【代码优化】修复秒杀活动详情页报错 2024-08-09 10:46:12 +08:00
puhui999 e7ba7a82e3 【代码优化】修复我的页面地址列表,点击地址卡片会返回上一页的 bug 2024-08-09 10:10:01 +08:00
芋道源码 7a3f98e5fc !69 修复优惠券(类别型)功能
Merge pull request !69 from 卢越/master
2024-08-08 05:06:40 +00:00
YunaiV a9739208f8 Merge remote-tracking branch 'origin/master' 2024-08-08 13:05:00 +08:00
YunaiV bb1da04177 【代码优化】实现动态 terminal 终端类型的计算 2024-08-08 13:04:34 +08:00
YunaiV 15ce250c1b Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp 2024-08-08 12:58:51 +08:00
芋道源码 f7ec1badf7 !66 feat(request): 实现动态Terminal功能。
Merge pull request !66 from Ordinary/master
2024-08-08 04:58:39 +00:00
芋道源码 2b5a06174f !70 【代码优化】门店自提:h5 位置信息相关逻辑
Merge pull request !70 from puhui999/master
2024-08-08 04:56:46 +00:00
puhui999 1fc3537bf4 【代码优化】门店自提:h5 位置信息相关逻辑 2024-08-08 12:26:09 +08:00
卢越 e126981628 修复切换登录方式的时候,偶现登录面板无法弹出的问题 2024-08-08 09:56:21 +08:00
卢越 2eb1a1bf41 商品详情和购物车跳转到结算页面时传入商品分类编号,以便查询可用优惠券 2024-08-08 09:45:12 +08:00
YunaiV 3099c8c491 实现抖音小程序的兼容 2024-08-07 23:51:29 +08:00
YunaiV 1aa944e18c Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp
# Conflicts:
#	pages/goods/index.vue
#	pages/order/confirm.vue
#	pages/order/detail.vue
2024-08-07 23:37:38 +08:00
芋道源码 e635b91bb5 !67 修复抖音小程序无法启动问题
Merge pull request !67 from undefined/master
2024-08-07 15:07:19 +00:00
YunaiV c7ea2f97ac 【代码评审】门店自提:相关代码的优化、以及 todo 评审 2024-08-07 22:09:15 +08:00
芋道源码 965b97dae7 !68 【新增】自提门店选择、积分选择
Merge pull request !68 from puhui999/master
2024-08-07 13:36:47 +00:00
puhui999 c78a849c96 【新增】订单详情:积分抵扣展示 2024-08-07 17:37:03 +08:00
puhui999 a2dec947a2 【新增】订单:积分选择 2024-08-07 17:36:29 +08:00
puhui999 aaad12c50e Merge remote-tracking branch 'refs/remotes/yudao/master'
# Conflicts:
#	pages/order/confirm.vue
2024-08-06 18:16:10 +08:00
puhui999 44052b22c3 【新增】订单详情:核销相关逻辑及组件 2024-08-06 18:11:49 +08:00
puhui999 196a1b2235 【优化】订单发货方式切换逻辑 2024-08-06 16:09:53 +08:00
puhui999 1bc75b2ff5 【新增】自提门店选择 2024-08-05 18:01:47 +08:00
puhui999 b98f848be1 【新增】自提门店选择 2024-08-05 18:00:11 +08:00
undefined ce81110bb3 实现抖音小程序显示(仅限显示) 2024-08-04 17:11:12 +08:00
Ordinary c9434b9ec9 feat(request): 实现动态Terminal功能。 2024-08-03 10:33:09 +08:00
134 changed files with 10505 additions and 5545 deletions

No files matched your search

+13 -9
View File
@@ -1,27 +1,31 @@
# 版本号
SHOPRO_VERSION = v1.8.3
SHOPRO_VERSION=v2.4.0
# 后端接口 - 正式环境(通过 process.env.NODE_ENV 非 development)
SHOPRO_BASE_URL = http://api-dashboard.yudao.iocoder.cn
SHOPRO_BASE_URL=http://api-dashboard.yudao.iocoder.cn
# 后端接口 - 测试环境(通过 process.env.NODE_ENV = development)
SHOPRO_DEV_BASE_URL = http://127.0.0.1:48080
SHOPRO_DEV_BASE_URL=http://127.0.0.1:48080
### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
SHOPRO_UPLOAD_TYPE=server
# 后端接口前缀(一般不建议调整)
SHOPRO_API_PATH = /app-api
SHOPRO_API_PATH=/app-api
# 后端 websocket 接口前缀
SHOPRO_WEBSOCKET_PATH = /infra/ws
SHOPRO_WEBSOCKET_PATH=/infra/ws
# 开发环境运行端口
SHOPRO_DEV_PORT = 3000
SHOPRO_DEV_PORT=3000
# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀
SHOPRO_STATIC_URL = https://file.sheepjs.com
SHOPRO_STATIC_URL=http://test.yudao.iocoder.cn
### SHOPRO_STATIC_URL = https://file.sheepjs.com
# 是否开启直播 1 开启直播 | 0 关闭直播 (小程序官方后台未审核开通直播权限时请勿开启)
SHOPRO_MPLIVE_ON = 0
SHOPRO_MPLIVE_ON=0
# 租户ID 默认 1
SHOPRO_TENANT_ID = 1
SHOPRO_TENANT_ID=1
+2 -2
View File
@@ -30,11 +30,11 @@
支持 Spring Boot、Spring Cloud 两种架构:
① Spring Boot 单体架构:<https://github.com/YunaiV/ruoyi-vue-pro>
① Spring Boot 单体架构:<https://doc.iocoder.cn>
![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
② Spring Cloud 微服务架构:<https://github.com/YunaiV/yudao-cloud>
② Spring Cloud 微服务架构:<https://cloud.iocoder.cn>
![架构图](/.image/common/yudao-cloud-architecture.png)
+5 -4
View File
@@ -3,7 +3,7 @@
"appid": "__UNI__460BC4C",
"description": "基于 uni-app + Vue3 技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。",
"versionName": "2.1.0",
"versionCode": 183,
"versionCode": "183",
"transformPx": false,
"app-plus": {
"usingComponents": true,
@@ -188,7 +188,8 @@
"setting": {
"urlCheck": false,
"minified": true,
"postcss": true
"postcss": false,
"es6": false
},
"optimization": {
"subPackages": true
@@ -216,8 +217,8 @@
"h5": {
"template": "index.html",
"router": {
"mode": "hash",
"base": "./"
"mode": "history",
"base": "/"
},
"sdkConfigs": {
"maps": {}
+2 -1
View File
@@ -2,7 +2,7 @@
"id": "shopro",
"name": "shopro",
"displayName": "芋道商城",
"version": "2.2.0",
"version": "2.4.0",
"description": "芋道商城,一套代码,同时发行到iOS、Android、H5、微信小程序多个平台,请使用手机扫码快速体验强大功能",
"scripts": {
"prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
@@ -90,6 +90,7 @@
"dependencies": {
"dayjs": "^1.11.7",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"luch-request": "^3.0.8",
"pinia": "^2.0.33",
"pinia-plugin-persist-uni": "^1.2.0",
+35 -1
View File
@@ -120,6 +120,17 @@
"group": "商品"
}
},
{
"path": "point",
"style": {
"navigationBarTitleText": "积分商品"
},
"meta": {
"sync": true,
"title": "积分商品",
"group": "商品"
}
},
{
"path": "list",
"style": {
@@ -307,6 +318,18 @@
"title": "编辑地址"
}
},
{
"path": "goods_details_store/index",
"style": {
"navigationBarTitleText": "自提门店"
},
"meta": {
"auth": true,
"sync": true,
"title": "地址管理",
"group": "用户中心"
}
},
{
"path": "wallet/money",
"style": {
@@ -629,7 +652,18 @@
"title": "秒杀活动",
"group": "营销活动"
}
}
},
{
"path": "point/list",
"style": {
"navigationBarTitleText": "积分商城"
},
"meta": {
"sync": true,
"title": "积分商城",
"group": "营销活动"
}
}
]
}
],
+66 -36
View File
@@ -1,6 +1,11 @@
<!-- 拼团订单的详情 -->
<template>
<s-layout title="拼团详情" class="detail-wrap" :navbar="state.data && !state.loading ? 'inner': 'normal'" :onShareAppMessage="shareInfo">
<s-layout
title="拼团详情"
class="detail-wrap"
:navbar="state.data && !state.loading ? 'inner' : 'normal'"
:onShareAppMessage="shareInfo"
>
<view v-if="state.loading"></view>
<view v-if="state.data && !state.loading">
<!-- 团长信息 + 活动信息 -->
@@ -22,7 +27,7 @@
priceColor="#E1212B"
@tap="
sheep.$router.go('/pages/goods/groupon', {
id: state.data.headRecord.activityId
id: state.data.headRecord.activityId,
})
"
:style="[{ top: Number(statusBarHeight + 108) + 'rpx' }]"
@@ -71,7 +76,9 @@
</view>
<view class="countdown-title ss-flex" v-else>
还差
<view class="num">{{ state.data.headRecord.userSize - state.data.headRecord.userCount }}人</view>
<view class="num"
>{{ state.data.headRecord.userSize - state.data.headRecord.userCount }}人</view
>
拼团成功
<view class="ss-flex countdown-time">
<view class="countdown-h ss-flex ss-row-center">{{ endTime.h }}</view>
@@ -109,7 +116,11 @@
</view>
</view>
<!-- 还有几个坑位 -->
<view class="default-avatar ss-m-r-24 ss-m-b-20" v-for="item in state.remainNumber" :key="item">
<view
class="default-avatar ss-m-r-24 ss-m-b-20"
v-for="item in state.remainNumber"
:key="item"
>
<image
:src="sheep.$url.static('/static/img/shop/avatar/unknown.png')"
class="avatar-img"
@@ -154,11 +165,7 @@
</view>
<view v-else class="ss-flex ss-row-center">
<view v-if="state.data.orderId">
<button
class="ss-reset-button join-btn"
:disabled="endTime.ms <= 0"
@tap="onShare"
>
<button class="ss-reset-button join-btn" :disabled="endTime.ms <= 0" @tap="onShare">
邀请好友来拼团
</button>
</view>
@@ -174,11 +181,11 @@
</view>
</view>
<!-- TODO 芋艿:这里暂时没接入 -->
<view v-if="state.data.goods">
<view v-if="!isEmpty(state.goodsInfo)">
<!-- 规格与数量弹框 -->
<s-select-groupon-sku
:show="state.showSelectSku"
:goodsInfo="state.data.goods"
:goodsInfo="state.goodsInfo"
:grouponAction="state.grouponAction"
:grouponNum="state.grouponNum"
@buy="onBuy"
@@ -186,6 +193,7 @@
@close="state.showSelectSku = false"
/>
</view>
</view>
<s-empty v-if="!state.data && !state.loading" icon="/static/goods-empty.png" />
@@ -196,25 +204,28 @@
import { computed, reactive } from 'vue';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { useDurationTime } from '@/sheep/hooks/useGoods';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import { showShareModal } from '@/sheep/hooks/useModal';
import { isEmpty } from 'lodash';
import CombinationApi from "@/sheep/api/promotion/combination";
import { isEmpty } from 'lodash-es';
import CombinationApi from '@/sheep/api/promotion/combination';
import SpuApi from '@/sheep/api/product/spu';
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const state = reactive({
data: {}, // 拼团详情
loading: true,
grouponAction: 'create',
showSelectSku: false,
grouponNum: 0,
number: 0,
activity: {},
goodsId: 0, // 商品ID
goodsInfo: {}, // 商品信息
showSelectSku: false, // 显示规格弹框
selectedSkuPrice: {}, // 选中的规格价格
activity: {}, // 团购活动
grouponId: 0, // 团购ID
grouponNum: 0, // 团购人数
grouponAction: 'create', // 团购操作
combinationHeadId: null, // 拼团团长编号
loading: true,
});
// todo 芋艿:分享要再接下
const shareInfo = computed(() => {
if (isEmpty(state.data)) return {};
return sheep.$platform.share.getShareInfo(
@@ -224,15 +235,14 @@
desc: state.data.goods?.subtitle,
params: {
page: '5',
query: state.data.id,
query: state.data.headRecord.id,
},
},
{
type: 'groupon', // 邀请拼团海报
title: state.data.headRecord.spuName, // 商品标题
image: sheep.$url.cdn(state.data.headRecord.picUrl), // 商品主图
price: state.data.goods?.price, // 商品价格
original_price: state.data.goods?.original_price, // 商品原价
price: fen2yuan(state.data.headRecord.combinationPrice), // 商品价格
},
);
});
@@ -244,33 +254,33 @@
});
}
// 去开团 TODO 芋艿:这里没接入
// 去开团
function onCreateGroupon() {
state.grouponAction = 'create';
state.grouponId = 0;
state.showSelectSku = true;
}
// 规格变更 TODO 芋艿:这里没接入
// 规格变更
function onSkuChange(e) {
state.selectedSkuPrice = e;
}
// 立即参团 TODO 芋艿:这里没接入
// 立即参团
function onJoinGroupon() {
state.grouponAction = 'join';
state.grouponId = state.data.activityId;
state.combinationHeadId = state.data.id;
state.grouponNum = state.data.num;
state.grouponId = state.data.headRecord.activityId;
state.combinationHeadId = state.data.headRecord.id;
state.grouponNum = state.data.headRecord.userSize;
state.showSelectSku = true;
}
// 立即购买 TODO 芋艿:这里没接入
// 立即购买
function onBuy(sku) {
sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({
order_type: 'goods',
combinationActivityId: state.data.activity.id,
combinationActivityId: state.activity.id,
combinationHeadId: state.combinationHeadId,
items: [
{
@@ -295,8 +305,29 @@
state.remainNumber = remainNumber > 0 ? remainNumber : 0;
// 获取活动信息
const { data: activity } = await CombinationApi.getCombinationActivity(data.headRecord.activityId);
const { data: activity } = await CombinationApi.getCombinationActivity(
data.headRecord.activityId,
);
state.activity = activity;
state.grouponNum = activity.userSize;
// 加载商品信息
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId);
state.goodsId = spu.id;
// 默认显示最低价
activity.products.forEach((product) => {
spu.price = Math.min(spu.price, product.combinationPrice); // 设置 SPU 的最低价格
});
state.goodsInfo = spu;
// 价格、库存使用活动的
spu.skus.forEach((sku) => {
const product = activity.products.find((product) => product.skuId === sku.id);
if (product) {
sku.price = product.combinationPrice;
} else {
// 找不到可能是没配置,则不能发起秒杀
sku.stock = 0;
}
});
} else {
state.data = null;
}
@@ -316,8 +347,7 @@
.recharge-box {
position: relative;
margin-bottom: 120rpx;
background: v-bind(headerBg) center/750rpx 100%
no-repeat,
background: v-bind(headerBg) center/750rpx 100% no-repeat,
linear-gradient(115deg, #f44739 0%, #ff6600 100%);
border-radius: 0 0 5% 5%;
height: 100rpx;
+1 -1
View File
@@ -1,6 +1,6 @@
<!-- 拼团活动列表 -->
<template>
<s-layout navbar="inner" :bgStyle="{ color: '#FE832A' }">
<s-layout :bgStyle="{ color: '#FE832A' }" navbar="inner">
<view class="page-bg" :style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]" />
<view class="list-content">
<!-- 参团会员统计 -->
+6 -6
View File
@@ -24,9 +24,9 @@
</view>
<view class="border-bottom">
<s-goods-item
:img="record.picUrl"
:title="record.spuName"
:price="record.combinationPrice"
:img="record.picUrl"
:title="record.spuName"
:price="record.combinationPrice"
>
<template #groupon>
<view class="ss-flex">
@@ -67,8 +67,8 @@
import { reactive } from 'vue';
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import _ from 'lodash';
import {formatOrderColor} from "@/sheep/hooks/useGoods";
import _ from 'lodash-es';
import { formatOrderColor } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util';
import CombinationApi from '@/sheep/api/promotion/combination';
@@ -121,7 +121,7 @@
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list)
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
+12 -4
View File
@@ -7,7 +7,7 @@
<view class="type-text ss-flex ss-row-center">满减:</view>
<view class="ss-flex-1">
<view class="tip-content" v-for="item in state.activityInfo.rules" :key="item">
{{ formatRewardActivityRule(state.activityInfo, item) }}
{{ item.description }}
</view>
</view>
<image class="activity-left-image" src="/static/activity-left.png" />
@@ -63,10 +63,11 @@
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import _ from 'lodash';
import _ from 'lodash-es';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
import SpuApi from '@/sheep/api/product/spu';
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
import OrderApi from '@/sheep/api/trade/order';
const state = reactive({
activityId: 0, // 获得编号
@@ -118,11 +119,18 @@
const { code, data } = await SpuApi.getSpuPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
...params
...params,
});
if (code !== 0) {
return;
}
// 拼接结算信息(营销)
await OrderApi.getSettlementProduct(data.list.map((item) => item.id).join(',')).then((res) => {
if (res.code !== 0) {
return;
}
appendSettlementProduct(data.list, res.data);
});
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
+76
View File
@@ -0,0 +1,76 @@
<!-- 积分商城:商品列表 -->
<template>
<s-layout title="积分商城" navbar="normal" :leftWidth="0" :rightWidth="0">
<scroll-view
class="scroll-box"
:style="{ height: pageHeight + 'rpx' }"
scroll-y="true"
:scroll-with-animation="false"
:enable-back-to-top="true"
>
<s-point-card ref="sPointCardRef" class="ss-p-x-20 ss-m-t-20" />
<s-empty
v-if="activityTotal === 0"
icon="/static/goods-empty.png"
text="暂无积分商品"
></s-empty>
<uni-load-more
v-if="activityTotal > 0"
:status="loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
@tap="loadMore"
/>
</scroll-view>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue';
import PointApi from '@/sheep/api/promotion/point';
import SLayout from '@/sheep/components/s-layout/s-layout.vue';
// 计算页面高度
const { safeAreaInsets, safeArea } = sheep.$platform.device;
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const pageHeight =
(safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sheep.$platform.navbar - 350;
const sPointCardRef = ref();
// 查询活动列表
const activityPageParams = reactive({
pageNo: 1, // 页码
pageSize: 5, // 每页数量
});
const activityTotal = ref(0); // 活动总数
const activityCount = ref(0); // 已加载活动数量
const loadStatus = ref(''); // 页面加载状态
async function getActivityList() {
loadStatus.value = 'loading';
const { data } = await PointApi.getPointActivityPage(activityPageParams);
await sPointCardRef.value.concatActivity(data.list);
activityCount.value = sPointCardRef.value.getActivityCount();
activityTotal.value = data.total;
loadStatus.value = activityCount.value < activityTotal.value ? 'more' : 'noMore';
}
// 加载更多
function loadMore() {
if (loadStatus.value !== 'noMore') {
activityPageParams.pageNo += 1;
getActivityList();
}
}
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
onLoad(() => {
getActivityList();
});
</script>
+138 -62
View File
@@ -1,15 +1,22 @@
<!-- 秒杀活动列表 -->
<template>
<s-layout navbar="inner" :bgStyle="{ color: 'rgb(245,28,19)' }">
<s-layout :bgStyle="{ color: 'rgb(245,28,19)' }" navbar="inner">
<!--顶部背景图-->
<view
class="page-bg"
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
class="page-bg"
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
></view>
<!-- 时间段轮播图 -->
<view class="header" v-if="activeTimeConfig?.sliderPicUrls?.length > 0">
<swiper indicator-dots="true" autoplay="true" :circular="true" interval="3000" duration="1500"
indicator-color="rgba(255,255,255,0.6)" indicator-active-color="#fff">
<swiper
indicator-dots="true"
autoplay="true"
:circular="true"
interval="3000"
duration="1500"
indicator-color="rgba(255,255,255,0.6)"
indicator-active-color="#fff"
>
<block v-for="(picUrl, index) in activeTimeConfig.sliderPicUrls" :key="index">
<swiper-item class="borRadius14">
<image :src="picUrl" class="slide-image borRadius14" lazy-load />
@@ -22,17 +29,28 @@
<!-- 左侧图标 -->
<view class="time-icon">
<!-- TODO 芋艿:图片统一维护 -->
<image class="ss-w-100 ss-h-100" src="http://mall.yudao.iocoder.cn/static/images/priceTag.png" />
<image
class="ss-w-100 ss-h-100"
src="http://mall.yudao.iocoder.cn/static/images/priceTag.png"
/>
</view>
<scroll-view class="time-list" :scroll-into-view="activeTimeElId" scroll-x scroll-with-animation>
<view v-for="(config, index) in timeConfigList" :key="index"
:class="['item', { active: activeTimeIndex === index}]"
:id="`timeItem${index}`"
@tap="handleChangeTimeConfig(index)">
<scroll-view
class="time-list"
:scroll-into-view="activeTimeElId"
scroll-x
scroll-with-animation
>
<view
v-for="(config, index) in timeConfigList"
:key="index"
:class="['item', { active: activeTimeIndex === index }]"
:id="`timeItem${index}`"
@tap="handleChangeTimeConfig(index, config.id)"
>
<!-- 活动起始时间 -->
<view class="time">{{ config.startTime }}</view>
<!-- 活动状态 -->
<view class="status">{{ config.status }}</view>
<view class="status">{{ config?.status }}</view>
</view>
</scroll-view>
</view>
@@ -42,7 +60,10 @@
<!-- 活动倒计时 -->
<view class="content-header ss-flex-col ss-col-center ss-row-center">
<view class="content-header-box ss-flex ss-row-center">
<view class="countdown-box ss-flex" v-if="activeTimeConfig?.status === TimeStatusEnum.STARTED">
<view
class="countdown-box ss-flex"
v-if="activeTimeConfig?.status === TimeStatusEnum.STARTED"
>
<view class="countdown-title ss-m-r-12">距结束</view>
<view class="ss-flex countdown-time">
<view class="ss-flex countdown-h">{{ countDown.h }}</view>
@@ -70,19 +91,44 @@
:data="{ ...activity, price: activity.seckillPrice }"
:goodsFields="goodsFields"
:seckillTag="true"
@click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })"
>
<!-- 抢购进度 -->
<template #activity>
<view class="limit">限量 <text class="ss-m-l-5">{{ activity.stock}} {{activity.unitName}}</text></view>
<view class="limit">
限量
<text class="ss-m-l-5">{{ activity.stock }} {{ activity.unitName }}</text>
</view>
<su-progress :percentage="activity.percent" strokeWidth="10" textInside isAnimate />
</template>
<!-- 抢购按钮 -->
<template #cart>
<button :class="['ss-reset-button cart-btn', { disabled: activeTimeConfig.status === TimeStatusEnum.END }]">
<span v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START">未开始</span>
<span v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED">马上抢</span>
<span v-else>已结束</span>
<button
:class="[
'ss-reset-button cart-btn',
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
]"
v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START"
>
<span>未开始</span>
</button>
<button
:class="[
'ss-reset-button cart-btn',
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
]"
@click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })"
v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED"
>
<span>马上抢</span>
</button>
<button
:class="[
'ss-reset-button cart-btn',
{ disabled: activeTimeConfig?.status === TimeStatusEnum.END },
]"
v-else
>
<span>已结束</span>
</button>
</template>
</s-goods-column>
@@ -100,40 +146,51 @@
</s-layout>
</template>
<script setup>
import {reactive, computed, ref, nextTick} from 'vue';
import { computed, nextTick, reactive, ref } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { useDurationTime } from '@/sheep/hooks/useGoods';
import SeckillApi from "@/sheep/api/promotion/seckill";
import dayjs from "dayjs";
import {TimeStatusEnum} from "@/sheep/util/const";
import SeckillApi from '@/sheep/api/promotion/seckill';
import dayjs from 'dayjs';
import { TimeStatusEnum } from '@/sheep/util/const';
// 计算页面高度
const { safeAreaInsets, safeArea } = sheep.$platform.device;
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const pageHeight = (safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sheep.$platform.navbar - 350;
const pageHeight =
(safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sheep.$platform.navbar - 350;
const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-header.png');
// 商品控件显示的字段(不显示库存、销量。改为显示自定义的进度条)
const goodsFields = {
name: { show: true },
introduction: { show: true },
price: { show: true },
marketPrice: { show: true },
name: {
show: true,
},
introduction: {
show: true,
},
price: {
show: true,
},
marketPrice: {
show: true,
},
};
//#region 时间段
// 时间段列表
const timeConfigList = ref([])
const timeConfigList = ref([]);
// 查询时间段
const getSeckillConfigList = async () => {
const { data } = await SeckillApi.getSeckillConfigList()
const { data } = await SeckillApi.getSeckillConfigList();
const now = dayjs();
const today = now.format('YYYY-MM-DD')
const today = now.format('YYYY-MM-DD');
const select = ref([]);
// 判断时间段的状态
data.forEach((config, index) => {
const startTime = dayjs(`${today} ${config.startTime}`)
const endTime = dayjs(`${today} ${config.endTime}`)
const startTime = dayjs(`${today} ${config.startTime}`);
const endTime = dayjs(`${today} ${config.endTime}`);
select.value[index] = config.id;
if (now.isBefore(startTime)) {
config.status = TimeStatusEnum.WAIT_START;
} else if (now.isAfter(endTime)) {
@@ -142,35 +199,36 @@
config.status = TimeStatusEnum.STARTED;
activeTimeIndex.value = index;
}
})
timeConfigList.value = data
});
timeConfigList.value = data;
// 默认选中进行中的活动
handleChangeTimeConfig(activeTimeIndex.value);
handleChangeTimeConfig(activeTimeIndex.value, select.value[activeTimeIndex.value]);
// 滚动到进行中的时间段
scrollToTimeConfig(activeTimeIndex.value)
}
scrollToTimeConfig(activeTimeIndex.value);
};
// 滚动到指定时间段
const activeTimeElId = ref('') // 当前选中的时间段的元素ID
const activeTimeElId = ref(''); // 当前选中的时间段的元素ID
const scrollToTimeConfig = (index) => {
nextTick(() => activeTimeElId.value = `timeItem${index}`)
}
nextTick(() => (activeTimeElId.value = `timeItem${index}`));
};
// 切换时间段
const activeTimeIndex = ref(0) // 当前选中的时间段的索引
const activeTimeConfig = computed(() => timeConfigList.value[activeTimeIndex.value]) // 当前选中的时间段
const handleChangeTimeConfig = (index) => {
activeTimeIndex.value = index
const activeTimeIndex = ref(0); // 当前选中的时间段的索引
const activeTimeConfig = computed(() => timeConfigList.value[activeTimeIndex.value]); // 当前选中的时间段
const handleChangeTimeConfig = (index, id) => {
activeTimeIndex.value = index;
// 查询活动列表
activityPageParams.pageNo = 1
activityList.value = []
activityPageParams.pageNo = 1;
activityPageParams.configId = id;
activityList.value = [];
getActivityList();
}
};
// 倒计时
const countDown = computed(() => {
const endTime = activeTimeConfig.value?.endTime
const endTime = activeTimeConfig.value?.endTime;
if (endTime) {
return useDurationTime(`${dayjs().format('YYYY-MM-DD')} ${endTime}`);
}
@@ -182,20 +240,22 @@
// 查询活动列表
const activityPageParams = reactive({
id: 0, // 时间段 ID
configId: 0, // 时间段 ID
pageNo: 1, // 页码
pageSize: 5, // 每页数量
})
const activityTotal = ref(0) // 活动总数
const activityList = ref([]) // 活动列表
const loadStatus = ref('') // 页面加载状态
});
const activityTotal = ref(0); // 活动总数
const activityList = ref([]); // 活动列表
const loadStatus = ref(''); // 页面加载状态
async function getActivityList() {
loadStatus.value = 'loading';
const { data } = await SeckillApi.getSeckillActivityPage(activityPageParams)
data.list.forEach(activity => {
const { data } = await SeckillApi.getSeckillActivityPage(activityPageParams);
data.list.forEach((activity) => {
// 计算抢购进度
activity.percent = parseInt(100 * (activity.totalStock - activity.stock) / activity.totalStock);
})
activity.percent = parseInt(
(100 * (activity.totalStock - activity.stock)) / activity.totalStock,
);
});
activityList.value = activityList.value.concat(...data.list);
activityTotal.value = data.total;
@@ -205,7 +265,7 @@
// 加载更多
function loadMore() {
if (loadStatus.value !== 'noMore') {
activityPageParams.pageNo += 1
activityPageParams.pageNo += 1;
getActivityList();
}
}
@@ -216,7 +276,7 @@
// 页面初始化
onLoad(async () => {
await getSeckillConfigList()
await getSeckillConfigList();
});
</script>
<style lang="scss" scoped>
@@ -235,7 +295,8 @@
margin: -276rpx auto 0 auto;
border-radius: 14rpx;
overflow: hidden;
swiper{
swiper {
height: 330rpx !important;
border-radius: 14rpx;
overflow: hidden;
@@ -246,7 +307,8 @@
height: 100%;
border-radius: 14rpx;
overflow: hidden;
img{
img {
border-radius: 14rpx;
}
}
@@ -257,10 +319,12 @@
width: 75rpx;
height: 70rpx;
}
// 时间段列表
.time-list {
width: 596rpx;
white-space: nowrap;
// 时间段
.item {
display: inline-block;
@@ -270,17 +334,20 @@
box-sizing: border-box;
margin-right: 30rpx;
width: 130rpx;
// 开始时间
.time {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
// 选中的时间段
&.active {
.time {
color: var(--ui-BG-Main);
}
// 状态
.status {
height: 30rpx;
@@ -301,6 +368,7 @@
margin: 0 20rpx 0 20rpx;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
.content-header {
width: 100%;
border-radius: 20rpx 20rpx 0 0;
@@ -312,6 +380,7 @@
height: 64rpx;
background: rgba($color: #fff, $alpha: 0.66);
border-radius: 32px;
// 场次倒计时内容
.countdown-title {
font-size: 28rpx;
@@ -319,10 +388,12 @@
color: #333333;
line-height: 28rpx;
}
// 场次倒计时
.countdown-time {
font-size: 28rpx;
color: rgba(#ed3c30, 0.23);
// 场次倒计时:小时部分
.countdown-h {
font-size: 24rpx;
@@ -334,6 +405,7 @@
background: rgba(#ed3c30, 0.23);
border-radius: 6rpx;
}
// 场次倒计时:分钟、秒
.countdown-num {
font-size: 24rpx;
@@ -348,12 +420,15 @@
}
}
}
// 活动列表
.scroll-box {
height: 900rpx;
// 活动
.goods-box {
position: relative;
// 抢购按钮
.cart-btn {
position: absolute;
@@ -373,6 +448,7 @@
color: #fff;
}
}
// 秒杀限量商品数
.limit {
font-size: 22rpx;
+16 -14
View File
@@ -25,7 +25,7 @@
<view class="item" v-for="(item, index) in state.signConfigList" :key="index">
<view
:class="
(index === state.signConfigList.length ? 'reward' : '') +
(index === state.signConfigList.length ? 'reward' : '') +
' ' +
(state.signInfo.continuousDay >= item.day ? 'rewardTxt' : '')
"
@@ -65,10 +65,11 @@
<!-- 签到说明 TODO @科举:这里改成【已累计签到】 -->
<view class="bg-white ss-m-t-16 ss-p-t-30 ss-p-b-60 ss-p-x-40">
<view class="activity-title ss-m-b-30">签到说明</view>
<view class="activity-des">1、已累计签到{{state.signInfo.totalDay}}天</view>
<view class="activity-des">1.已累计签到{{ state.signInfo.totalDay }}天</view>
<view class="activity-des">
2、据说连续签到第 {{ state.maxDay }} 天可获得超额积分,一定要坚持签到哦~~~
2.据说连续签到第 {{ state.maxDay }} 天可获得超额积分,要坚持签到哦~~
</view>
<view class="activity-des"> 3.积分可以在购物时抵现金结算的哦 ~~</view>
</view>
</view>
@@ -110,7 +111,7 @@
signInfo: {}, // 签到信息
signConfigList: [], // 签到配置列表
maxDay: 0, // 最大的签到天数
maxDay: 0, // 最大的签到天数
showModel: false, // 签到弹框
signResult: {}, // 签到结果
@@ -347,8 +348,7 @@
.title {
font-size: 34rpx;
font-weight: bold;
// color: var(--ui-BG-Main);
color: #ff6000;
color: var(--ui-BG-Main-TC);
}
.subtitle {
@@ -410,12 +410,10 @@
background-color: #f4b409;
border-radius: 16rpx;
font-size: 20rpx;
color: #a57d3f;
color: var(--ui-BG-Main-TC);
line-height: 32rpx;
}
.venusSelect {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzkwRkI4NEFEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzkwRkI4NDlEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIv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');
text-align: center;
padding: 2rpx;
}
.venus {
@@ -424,7 +422,11 @@
background-size: 100% 100%;
width: 56rpx;
height: 56rpx;
margin: 10rpx 0;
margin: 10rpx auto;
}
.venusSelect {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzkwRkI4NEFEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzkwRkI4NDlEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIv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');
}
.num {
@@ -435,7 +437,7 @@
.item {
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #eee;
flex-direction: column;
height: 130rpx;
}
@@ -446,6 +448,6 @@
}
.on {
background-color: #999 !important;
color: #f4b409;
}
</style>
+74 -29
View File
@@ -1,20 +1,35 @@
<template>
<!-- 聊天虚拟列表 -->
<z-paging ref="pagingRef" v-model="messageList" use-chat-record-mode use-virtual-list
cell-height-mode="dynamic" default-page-size="20" :auto-clean-list-when-reload="false"
safe-area-inset-bottom bottom-bg-color="#f8f8f8" :back-to-top-style="backToTopStyle"
:auto-show-back-to-top="showNewMessageTip" @backToTopClick="onBackToTopClick"
@scrolltoupper="onScrollToUpper" @query="queryList">
<z-paging
ref="pagingRef"
v-model="messageList"
use-chat-record-mode
use-virtual-list
cell-height-mode="dynamic"
default-page-size="20"
:auto-clean-list-when-reload="false"
safe-area-inset-bottom
bottom-bg-color="#f8f8f8"
:back-to-top-style="backToTopStyle"
:auto-show-back-to-top="showNewMessageTip"
@backToTopClick="onBackToTopClick"
@scrolltoupper="onScrollToUpper"
@query="queryList"
>
<template #top>
<!-- 撑一下顶部导航 -->
<view style="height: 45px"></view>
<view :style="{ height: sys_navBar + 'px' }"></view>
</template>
<!-- style="transform: scaleY(-1)"必须写,否则会导致列表倒置!!! -->
<!-- 注意不要直接在chat-item组件标签上设置style,因为在微信小程序中是无效的,请包一层view -->
<template #cell="{item,index}">
<template #cell="{ item, index }">
<view style="transform: scaleY(-1)">
<!-- 消息渲染 -->
<MessageListItem :message="item" :message-index="index" :message-list="messageList"></MessageListItem>
<MessageListItem
:message="item"
:message-index="index"
:message-list="messageList"
></MessageListItem>
</view>
</template>
<!-- 底部聊天输入框 -->
@@ -33,48 +48,80 @@
import { reactive, ref } from 'vue';
import KeFuApi from '@/sheep/api/promotion/kefu';
import { isEmpty } from '@/sheep/helper/utils';
import sheep from '@/sheep';
import { formatDate } from '@/sheep/util';
const sys_navBar = sheep.$platform.navbar;
const messageList = ref([]); // 消息列表
const showNewMessageTip = ref(false); // 显示有新消息提示
const refreshMessage = ref(false); // 更新消息列表
const backToTopStyle = reactive({
'width': '100px',
width: '100px',
'background-color': '#fff',
'border-radius': '30px',
'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
'display': 'flex',
'justifyContent': 'center',
'alignItems': 'center',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}); // 返回顶部样式
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
no: 1, // 查询次数,只用于触底计算
limit: 20,
createTime: undefined,
});
const pagingRef = ref(null); // 虚拟列表
const queryList = async (pageNo, pageSize) => {
const queryList = async (no, limit) => {
// 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
// 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
queryParams.pageNo = pageNo;
queryParams.pageSize = pageSize;
queryParams.no = no;
queryParams.limit = limit;
await getMessageList();
};
// 获得消息分页列表
const getMessageList = async () => {
const { data } = await KeFuApi.getKefuMessagePage(queryParams);
if (isEmpty(data.list)) {
const { data } = await KeFuApi.getKefuMessageList(queryParams);
if (isEmpty(data)) {
pagingRef.value.completeByNoMore([], true);
return;
}
pagingRef.value.completeByTotal(data.list, data.total);
if (queryParams.no > 1 && refreshMessage.value) {
const newMessageList = [];
for (const message of data) {
if (messageList.value.some((val) => val.id === message.id)) {
continue;
}
newMessageList.push(message);
}
// 新消息追加到开头
messageList.value = [...newMessageList, ...messageList.value];
pagingRef.value.updateCache(); // 更新缓存
refreshMessage.value = false; // 更新好后重置状态
return;
}
if (data.slice(-1).length > 0) {
// 设置最后一次历史查询的最后一条消息的 createTime
queryParams.createTime = formatDate(data.slice(-1)[0].createTime);
}
pagingRef.value.completeByNoMore(data, false);
};
/** 刷新消息列表 */
const refreshMessageList = (message = undefined) => {
if (queryParams.pageNo != 1 && message !== undefined) {
showNewMessageTip.value = true;
const refreshMessageList = async (message = undefined) => {
if (typeof message !== 'undefined') {
// 追加数据
pagingRef.value.addChatRecordData([message], false);
return;
} else {
queryParams.createTime = undefined;
refreshMessage.value = true;
await getMessageList();
}
// 若已是第一页则不做处理
if (queryParams.no > 1) {
showNewMessageTip.value = true;
} else {
onScrollToUpper();
}
pagingRef.value.reload();
};
/** 滚动到最新消息 */
const onBackToTopClick = (event) => {
event(false); // 禁用默认操作
@@ -83,12 +130,10 @@
/** 监听滚动到底部事件(因为 scroll 翻转了顶就是底) */
const onScrollToUpper = () => {
// 若已是第一页则不做处理
if (queryParams.pageNo === 1) {
if (queryParams.no === 1) {
return;
}
showNewMessageTip.value = false;
// 到底重置消息列表
refreshMessageList();
};
defineExpose({ getMessageList, refreshMessageList });
</script>
+54 -64
View File
@@ -4,12 +4,20 @@
<view class="message-item ss-flex-col scroll-item">
<view class="ss-flex ss-row-center ss-col-center">
<!-- 日期 -->
<view v-if="message.contentType !== KeFuMessageContentTypeEnum.SYSTEM && showTime(message, messageIndex)"
class="date-message">
<view
v-if="
message.contentType !== KeFuMessageContentTypeEnum.SYSTEM &&
showTime(message, messageIndex)
"
class="date-message"
>
{{ formatDate(message.createTime) }}
</view>
<!-- 系统消息 -->
<view v-if="message.contentType === KeFuMessageContentTypeEnum.SYSTEM" class="system-message">
<view
v-if="message.contentType === KeFuMessageContentTypeEnum.SYSTEM"
class="system-message"
>
{{ message.content }}
</view>
</view>
@@ -18,38 +26,41 @@
v-if="message.contentType !== KeFuMessageContentTypeEnum.SYSTEM"
class="ss-flex ss-col-top"
:class="[
message.senderType === UserTypeEnum.ADMIN
? `ss-row-left`
: message.senderType === UserTypeEnum.MEMBER
? `ss-row-right`
: '',
]"
message.senderType === UserTypeEnum.ADMIN
? `ss-row-left`
: message.senderType === UserTypeEnum.MEMBER
? `ss-row-right`
: '',
]"
>
<!-- 客服头像 -->
<image
v-show="message.senderType === UserTypeEnum.ADMIN"
class="chat-avatar ss-m-r-24"
:src="
sheep.$url.cdn(message.senderAvatar) ||
sheep.$url.static('/static/img/shop/chat/default.png')
"
sheep.$url.cdn(message.senderAvatar) ||
sheep.$url.static('/static/img/shop/chat/default.png')
"
mode="aspectFill"
></image>
<!-- 内容 -->
<template v-if="message.contentType === KeFuMessageContentTypeEnum.TEXT">
<view class="message-box" :class="{'admin': message.senderType === UserTypeEnum.ADMIN}">
<mp-html :content="replaceEmoji(message.content)" />
<view class="message-box" :class="{ admin: message.senderType === UserTypeEnum.ADMIN }">
<mp-html :content="replaceEmoji(getMessageContent(message).text || message.content)" />
</view>
</template>
<template v-if="message.contentType === KeFuMessageContentTypeEnum.IMAGE">
<view class="message-box" :class="{'admin': message.senderType === UserTypeEnum.ADMIN}"
:style="{ width: '200rpx' }">
<view
class="message-box"
:class="{ admin: message.senderType === UserTypeEnum.ADMIN }"
:style="{ width: '200rpx' }"
>
<su-image
class="message-img"
isPreview
:previewList="[sheep.$url.cdn(message.content)]"
:previewList="[sheep.$url.cdn(getMessageContent(message).picUrl || message.content)]"
:current="0"
:src="sheep.$url.cdn(message.content)"
:src="sheep.$url.cdn(getMessageContent(message).picUrl || message.content)"
:height="200"
:width="200"
mode="aspectFill"
@@ -57,31 +68,27 @@
</view>
</template>
<template v-if="message.contentType === KeFuMessageContentTypeEnum.PRODUCT">
<div class="ss-m-b-10">
<GoodsItem
:goodsData="getMessageContent(message)"
@tap="
sheep.$router.go('/pages/goods/index', {
id: getMessageContent(message).id,
})
"
@tap="sheep.$router.go('/pages/goods/index', { id: getMessageContent(message).spuId })"
/>
</div>
</template>
<template v-if="message.contentType === KeFuMessageContentTypeEnum.ORDER">
<OrderItem
:orderData="getMessageContent(message)"
@tap="
sheep.$router.go('/pages/order/detail', {
id: getMessageContent(message).id,
})
"
@tap="sheep.$router.go('/pages/order/detail', { id: getMessageContent(message).id })"
/>
</template>
<!-- user头像 -->
<image
v-if="message.senderType === UserTypeEnum.MEMBER"
class="chat-avatar ss-m-l-24"
:src="sheep.$url.cdn(message.senderAvatar) ||
sheep.$url.static('/static/img/shop/chat/default.png')"
:src="
sheep.$url.cdn(userInfo.avatar) ||
sheep.$url.static('/static/img/shop/chat/default.png')
"
mode="aspectFill"
>
</image>
@@ -96,7 +103,7 @@
import { KeFuMessageContentTypeEnum, UserTypeEnum } from '@/pages/chat/util/constants';
import { emojiList } from '@/pages/chat/util/emoji';
import sheep from '@/sheep';
import { formatDate } from '@/sheep/util';
import { formatDate, jsonParse } from '@/sheep/util';
import GoodsItem from '@/pages/chat/components/goods.vue';
import OrderItem from '@/pages/chat/components/order.vue';
@@ -104,7 +111,7 @@
// 消息
message: {
type: Object,
default: ()=>({}),
default: () => ({}),
},
// 消息索引
messageIndex: {
@@ -112,12 +119,14 @@
default: 0,
},
// 消息列表
messageList:{
messageList: {
type: Array,
default: () => [],
}
},
});
const getMessageContent = computed(() => (item) => JSON.parse(item.content)); // 解析消息内容
const getMessageContent = computed(() => (item) => jsonParse(item.content)); // 解析消息内容
const userInfo = computed(() => sheep.$store('user').userInfo);
//======================= 工具 =======================
@@ -140,7 +149,7 @@
let emojiFile = selEmojiFile(item);
newData = newData.replace(
item,
`<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;" src="${sheep.$url.cdn(
`<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;vertical-align: middle;" src="${sheep.$url.cdn(
'/static/img/chat/emoji/' + emojiFile,
)}"/>`,
);
@@ -162,7 +171,7 @@
<style scoped lang="scss">
.message-item {
margin-bottom: 33rpx;
margin-bottom: 10rpx;
}
.date-message,
@@ -226,18 +235,23 @@
.message-box {
max-width: 50%;
font-size: 16px;
line-height: 20px;
white-space: normal;
word-break: break-all;
word-wrap: break-word;
padding: 20rpx;
border-radius: 10rpx;
color: #fff;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
margin-top: 3px;
margin-bottom: 9px;
border-top-left-radius: 10px;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
&.admin {
background: #fff;
color: #333;
margin-top: 3px;
margin-bottom: 9px;
border-radius: 0 10px 10px 10px;
}
:deep() {
@@ -265,30 +279,6 @@
border-radius: 6rpx;
}
.template-wrap {
// width: 100%;
padding: 20rpx 24rpx;
background: #fff;
border-radius: 10rpx;
.title {
font-size: 26rpx;
font-weight: 500;
color: #333;
margin-bottom: 29rpx;
}
.item {
font-size: 24rpx;
color: var(--ui-BG-Main);
margin-bottom: 16rpx;
&:last-of-type {
margin-bottom: 0;
}
}
}
.error-img {
width: 400rpx;
height: 400rpx;
+1 -1
View File
@@ -32,7 +32,7 @@
<script setup>
import { reactive, watch } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import GoodsItem from './goods.vue';
import OrderItem from './order.vue';
import OrderApi from '@/sheep/api/trade/order';
+33 -13
View File
@@ -1,17 +1,35 @@
<template>
<s-layout class="chat-wrap" :title="!isReconnecting ? '连接客服成功' : '会话重连中'" navbar="inner">
<s-layout
class="chat-wrap"
:title="!isReconnecting ? '连接客服成功' : '会话重连中'"
navbar="inner"
>
<!-- 覆盖头部导航栏背景颜色 -->
<div class="page-bg" :style="{ height: sys_navBar + 'px' }"></div>
<!-- 聊天区域 -->
<MessageList ref="messageListRef">
<template #bottom>
<message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
<message-input
v-model="chat.msg"
@on-tools="onTools"
@send-message="onSendMessage"
></message-input>
</template>
</MessageList>
<!-- 聊天工具 -->
<tools-popup :show-tools="chat.showTools" :tools-mode="chat.toolsMode" @close="handleToolsClose"
@on-emoji="onEmoji" @image-select="onSelect" @on-show-select="onShowSelect">
<message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
<tools-popup
:show-tools="chat.showTools"
:tools-mode="chat.toolsMode"
@close="handleToolsClose"
@on-emoji="onEmoji"
@image-select="onSelect"
@on-show-select="onShowSelect"
>
<message-input
v-model="chat.msg"
@on-tools="onTools"
@send-message="onSendMessage"
></message-input>
</tools-popup>
<!-- 商品订单选择 -->
<SelectPopup
@@ -30,10 +48,14 @@
import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
import MessageInput from '@/pages/chat/components/messageInput.vue';
import SelectPopup from '@/pages/chat/components/select-popup.vue';
import { KeFuMessageContentTypeEnum, WebSocketMessageTypeConstants } from '@/pages/chat/util/constants';
import {
KeFuMessageContentTypeEnum,
WebSocketMessageTypeConstants,
} from '@/pages/chat/util/constants';
import FileApi from '@/sheep/api/infra/file';
import KeFuApi from '@/sheep/api/promotion/kefu';
import { useWebSocket } from '@/sheep/hooks/useWebSocket';
import { jsonParse } from '@/sheep/util';
const sys_navBar = sheep.$platform.navbar;
@@ -52,7 +74,7 @@
try {
const data = {
contentType: KeFuMessageContentTypeEnum.TEXT,
content: chat.msg,
content: JSON.stringify({ text: chat.msg }),
};
await KeFuApi.sendKefuMessage(data);
await messageListRef.value.refreshMessageList();
@@ -104,7 +126,7 @@
const res = await FileApi.uploadFile(data.tempFiles[0].path);
msg = {
contentType: KeFuMessageContentTypeEnum.IMAGE,
content: res.data,
content: JSON.stringify({ picUrl: res.data }),
};
break;
case 'goods':
@@ -134,19 +156,18 @@
//======================= 聊天工具相关 end =======================
const { options } = useWebSocket({
// 连接成功
onConnected: async () => {
},
onConnected: async () => {},
// 收到消息
onMessage: async (data) => {
const type = data.type;
if (!type) {
console.error('未知的消息类型:' + data.value);
console.error('未知的消息类型:' + data);
return;
}
// 2.2 消息类型:KEFU_MESSAGE_TYPE
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
// 刷新消息列表
await messageListRef.value.refreshMessageList(JSON.parse(data.content));
await messageListRef.value.refreshMessageList(jsonParse(data.content));
return;
}
// 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
@@ -160,7 +181,6 @@
<style scoped lang="scss">
.chat-wrap {
.page-bg {
width: 100%;
position: absolute;
+241 -218
View File
@@ -1,249 +1,272 @@
<!-- 佣金排行榜 -->
<template>
<s-layout title="佣金排行榜">
<view class="CommissionRank">
<view class="header">
<view class="rank" v-if="state.position">
您目前的排名
<text class="num">{{ state.position }}</text> 名
</view>
<view class="rank" v-else>您目前暂无排名</view>
</view>
<view class="wrapper">
<view class="nav acea-row row-around" style="justify-content: space-around; display: flex">
<view class="item" :class="state.currentTab === index ? 'font-color' : ''"
v-for="(item, index) in tabMaps" :key="index" @click="switchTap(index)">
{{ item }}
</view>
</view>
<view class="list">
<view class="item acea-row row-between-wrapper" v-for="(item, index) in state.rankList"
:key="index">
<view class="num" v-if="index <= 2">
<image :src="'/static/images/medal0' + (index + 1) + '.png'" />
</view>
<view class="num" v-else>
{{ index + 1 }}
</view>
<view class="picTxt acea-row row-between-wrapper">
<view class="pictrue">
<image :src="item.avatar" />
</view>
<view class="text line1">{{ item.nickname }}</view>
</view>
<view class="people font-color">¥{{ fen2yuan(item.brokeragePrice) }}</view>
</view>
</view>
<view class="noCommodity"
v-if="state.rankList.length === 0 && (state.page !== 1 || state.active === 0)">
<emptyPage title="暂无排行~" />
</view>
</view>
</view>
</s-layout>
<s-layout title="佣金排行榜">
<view class="commission-rank">
<view class="header">
<view class="rank" v-if="state.position">
您目前的排名
<text class="num">{{ state.position }}</text> 名
</view>
<view class="rank" v-else>您目前暂无排名</view>
</view>
<view class="wrapper">
<view class="nav acea-row row-around" style="justify-content: space-around; display: flex">
<view
class="item"
:class="state.currentTab === index ? 'font-color' : ''"
v-for="(item, index) in tabMaps"
:key="index"
@click="switchTap(index)"
>
{{ item }}
</view>
</view>
<view class="list">
<view class="item" v-for="(item, index) in state.pagination.list" :key="index">
<view class="num" v-if="index <= 2">
<image :src="'/static/images/medal0' + (index + 1) + '.png'" />
</view>
<view class="num" v-else>
{{ index + 1 }}
</view>
<view class="pictrue">
<image :src="item.avatar" />
</view>
<view class="text">{{ item.nickname }}</view>
<view class="people">¥{{ fen2yuan(item.brokeragePrice) }}</view>
</view>
</view>
<view class="noCommodity" v-if="state.pagination.list.length === 0">
<emptyPage title="暂无排行~" />
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
@tap="loadMore"
/>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import {
onLoad
} from '@dcloudio/uni-app';
import {
reactive
} from 'vue';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import {
fen2yuan
} from '@/sheep/hooks/useGoods';
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util';
const tabMaps = ['周排行', '月排行'];
const tabMaps = ['周排行', '月排行'];
const state = reactive({
currentTab: 0,
position: 0, // 排名
rankList: [],
});
const state = reactive({
currentTab: 0,
position: 0, // 排名
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 10,
},
loadStatus: '',
});
async function switchTap(index) {
state.currentTab = index;
state.rankList = [];
calculateTimes();
getBrokerageRankList();
getBrokerageRankNumber();
}
async function switchTap(index) {
state.currentTab = index;
resetPagination(state.pagination);
calculateTimes();
getBrokerageRankList();
}
async function getBrokerageRankList() {
const {
code,
data
} = await BrokerageApi.getBrokerageUserChildSummaryPageByPrice({
pageNo: 1,
pageSize: 10,
'times[0]': state.times[0],
'times[1]': state.times[1],
});
if (code !== 0) {
return;
}
state.rankList = data.list;
}
async function getBrokerageRankList() {
const { code, data } = await BrokerageApi.getBrokerageUserChildSummaryPageByPrice({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
'times[0]': state.times[0],
'times[1]': state.times[1],
});
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
if (state.pagination.pageNo === 1) {
getBrokerageRankNumber();
}
}
async function getBrokerageRankNumber() {
const {
code,
data
} = await BrokerageApi.getRankByPrice({
times: state.times,
});
if (code !== 0) {
return;
}
state.position = data;
}
async function getBrokerageRankNumber() {
const { code, data } = await BrokerageApi.getRankByPrice({
times: state.times,
});
if (code !== 0) {
return;
}
state.position = data;
}
function formatDate(date) {
return sheep.$helper.timeFormat(date, 'yyyy-mm-dd hh:MM:ss');
}
function formatDate(date) {
return sheep.$helper.timeFormat(date, 'yyyy-mm-dd hh:MM:ss');
}
function calculateTimes() {
let times;
if (state.currentTab === 0) {
times = getWeekTimes();
} else {
times = getMonthTimes();
}
state.times = [formatDate(times[0]), formatDate(times[1])];
}
function calculateTimes() {
let times;
if (state.currentTab === 0) {
times = getWeekTimes();
} else {
times = getMonthTimes();
}
state.times = [formatDate(times[0]), formatDate(times[1])];
}
onLoad(function() {
calculateTimes();
getBrokerageRankList();
getBrokerageRankNumber();
});
onLoad(function () {
calculateTimes();
getBrokerageRankList();
});
// TODO 芋艿:此处可考虑抽离
/**
* 获得当前周的开始和结束时间
*/
function getWeekTimes() {
const today = new Date();
const dayOfWeek = today.getDay();
return [
new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0),
new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + (6 - dayOfWeek),
23,
59,
59,
),
];
}
// TODO 芋艿:此处可考虑抽离
/**
* 获得当前周的开始和结束时间
*/
function getWeekTimes() {
const today = new Date();
const dayOfWeek = today.getDay();
return [
new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0),
new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + (6 - dayOfWeek),
23,
59,
59,
),
];
}
/**
* 获得当前月的开始和结束时间
*/
function getMonthTimes() {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth();
const startDate = new Date(year, month, 1, 0, 0, 0);
const nextMonth = new Date(year, month + 1, 1);
const endDate = new Date(nextMonth.getTime() - 1);
return [startDate, endDate];
}
/**
* 获得当前月的开始和结束时间
*/
function getMonthTimes() {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth();
const startDate = new Date(year, month, 1, 0, 0, 0);
const nextMonth = new Date(year, month + 1, 1);
const endDate = new Date(nextMonth.getTime() - 1);
return [startDate, endDate];
}
// TODO 芋艿:css 需要优化下;并且展示样式有问题
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getBrokerageRankList();
}
// 上拉加载更多
onReachBottom(() => loadMore());
</script>
<style lang="scss" scoped>
.CommissionRank .header {
background: url('data:image/jpeg;base64,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 truncated
width: 100%;
height: 344rpx;
background-size: 100% 100%;
}
.commission-rank {
.header {
background: url('data:image/jpeg;base64,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 truncated
no-repeat;
width: 100%;
height: 344rpx;
background-size: 100% 100%;
.CommissionRank .header .rank {
font-size: 33rpx;
color: #fff;
position: absolute;
top: 160rpx;
left: 48rpx;
}
.rank {
font-size: 33rpx;
color: #fff;
position: absolute;
top: 160rpx;
left: 48rpx;
.CommissionRank .header .rank .num {
font-size: 51rpx;
font-weight: bold;
margin: 0 10rpx;
}
.num {
font-size: 51rpx;
font-weight: bold;
margin: 0 10rpx;
}
}
}
.CommissionRank .wrapper {
width: 710rpx;
background-color: #fff;
border-radius: 14rpx;
margin: -76rpx auto 0 auto;
}
.wrapper {
width: 710rpx;
background-color: #fff;
border-radius: 14rpx;
margin: -76rpx auto 0 auto;
.CommissionRank .wrapper .nav {
height: 99rpx;
border-bottom: 2.5rpx solid #f3f3f3;
font-size: 30rpx;
font-weight: bold;
color: #999;
line-height: 99rpx;
}
.nav {
height: 99rpx;
border-bottom: 2.5rpx solid #f3f3f3;
font-size: 30rpx;
font-weight: bold;
color: #999;
line-height: 99rpx;
.CommissionRank .wrapper .nav .item.font-color {
border-bottom: 4rpx solid #e93323;
margin-top: -2rpx;
color: #e93323;
}
.item.font-color {
border-bottom: 4rpx solid #e93323;
margin-top: -2rpx;
color: #e93323;
}
}
.CommissionRank .wrapper .list {
padding: 0 30rpx;
}
.list {
padding: 0 20rpx;
.CommissionRank .wrapper .list .item {
border-bottom: 1px solid #f3f3f3;
height: 101rpx;
font-size: 28rpx;
}
.item {
display: flex;
justify-content: space-around;
align-items: center;
border-bottom: 1px solid #f3f3f3;
height: 101rpx;
font-size: 28rpx;
.CommissionRank .wrapper .list .item .num {
color: #666;
width: 70rpx;
}
.num {
color: #666;
flex: 0.08;
display: flex;
justify-content: flex-start;
align-items: center;
.CommissionRank .wrapper .list .item .num image {
width: 34rpx;
height: 40rpx;
display: block;
}
image {
width: 50rpx;
height: 50rpx;
}
}
.CommissionRank .wrapper .list .item .picTxt {
width: 350rpx;
}
.pictrue {
flex: 0.15;
.CommissionRank .wrapper .list .item .picTxt .pictrue {
width: 68rpx;
height: 68rpx;
}
image {
width: 68rpx;
height: 68rpx;
display: block;
border-radius: 50%;
margin: 0 auto;
}
}
.CommissionRank .wrapper .list .item .picTxt .pictrue image {
width: 100%;
height: 100%;
display: block;
border-radius: 50%;
}
.text {
color: #333;
flex: 0.5;
}
.CommissionRank .wrapper .list .item .picTxt .text {
width: 262rpx;
color: #333;
}
.CommissionRank .wrapper .list .item .people {
width: 175rpx;
text-align: right;
}
</style>
.people {
text-align: right;
flex: 0.27;
}
}
}
}
}
</style>
@@ -45,7 +45,8 @@
type: Boolean,
default: false,
},
methods: { // 开启的提现方式
methods: {
// 开启的提现方式
type: Array,
default: [],
},
@@ -57,7 +58,7 @@
const typeList = [
{
// icon: '/static/img/shop/pay/wechat.png', // TODO 芋艿:后续给个 icon
icon: '/static/img/shop/pay/wechat.png',
title: '钱包余额',
value: '1',
},
@@ -68,14 +69,19 @@
},
{
icon: '/static/img/shop/pay/wechat.png',
title: '微信零钱',
title: '微信账户', // 微信手动转账
value: '3',
},
{
icon: '/static/img/shop/pay/alipay.png',
title: '支付宝账户',
value: '4',
}
},
{
icon: '/static/img/shop/pay/wechat.png',
title: '微信零钱', // 微信 API 转账
value: '5',
},
];
function onChange(e) {
@@ -89,7 +95,7 @@
}
// 赋值
emits('update:modelValue', {
type: state.currentValue
type: state.currentValue,
});
// 关闭弹窗
emits('close');
@@ -31,9 +31,10 @@
<style lang="scss" scoped>
// 用户资料卡片
.user-card {
width: 690rpx;
width: 700rpx;
height: 192rpx;
margin: -88rpx 20rpx 0 20rpx;
margin: 0 auto;
margin-top: -88rpx;
padding-top: 88rpx;
background: v-bind(headerBg) no-repeat;
background-size: 100% 100%;
@@ -110,4 +111,4 @@
}
}
}
</style>
</style>
+134 -118
View File
@@ -1,74 +1,90 @@
<!-- 分销首页:明细列表 -->
<template>
<view class="distribution-log-wrap">
<view class="header-box">
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title2.png')" />
<view class="ss-flex header-title">
<view class="title">实时动态</view>
<text class="cicon-forward" />
</view>
</view>
<scroll-view scroll-y="true" @scrolltolower="loadmore" class="scroll-box log-scroll"
scroll-with-animation="true">
<view v-if="state.pagination.list">
<view class="log-item-box ss-flex ss-row-between" v-for="item in state.pagination.list" :key="item.id">
<view class="log-item-wrap">
<view class="log-item ss-flex ss-ellipsis-1 ss-col-center">
<view class="ss-flex ss-col-center">
<image class="log-img" :src="sheep.$url.static('/static/img/shop/avatar/notice.png')" mode="aspectFill" />
</view>
<view class="log-text ss-ellipsis-1">
<view class="distribution-log-wrap">
<view class="header-box">
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title2.png')" />
<view class="ss-flex header-title">
<view class="title">实时动态</view>
<text class="cicon-forward" />
</view>
</view>
<scroll-view
scroll-y="true"
@scrolltolower="loadmore"
class="scroll-box log-scroll"
scroll-with-animation="true"
>
<view v-if="state.pagination.list">
<view
class="log-item-box ss-flex ss-row-between"
v-for="item in state.pagination.list"
:key="item.id"
>
<view class="log-item-wrap">
<view class="log-item ss-flex ss-ellipsis-1 ss-col-center">
<view class="ss-flex ss-col-center">
<image
class="log-img"
:src="sheep.$url.static('/static/img/shop/avatar/notice.png')"
mode="aspectFill"
/>
</view>
<view class="log-text ss-ellipsis-1">
{{ item.title }} {{ fen2yuan(item.price) }} 元
</view>
</view>
</view>
<text class="log-time">{{ dayjs(item.createTime).fromNow() }}</text>
</view>
</view>
</view>
</view>
<text class="log-time">{{ dayjs(item.createTime).fromNow() }}</text>
</view>
</view>
<!-- 加载更多 -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" color="#333333"
@tap="loadmore" />
</scroll-view>
</view>
<!-- 加载更多 -->
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
color="#333333"
@tap="loadmore"
/>
</scroll-view>
</view>
</template>
<script setup>
import sheep from '@/sheep';
import { reactive } from 'vue';
import _ from 'lodash';
import dayjs from 'dayjs';
import sheep from '@/sheep';
import { reactive } from 'vue';
import _ from 'lodash-es';
import dayjs from 'dayjs';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '../../../sheep/hooks/useGoods';
const state = reactive({
loadStatus: '',
pagination: {
const state = reactive({
loadStatus: '',
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
},
});
pageSize: 8,
},
});
async function getLog() {
async function getLog() {
state.loadStatus = 'loading';
const { code, data } = await BrokerageApi.getBrokerageRecordPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize
});
pageSize: state.pagination.pageSize,
});
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
}
getLog();
getLog();
// 加载更多
function loadmore() {
// 加载更多
function loadmore() {
if (state.loadStatus === 'noMore') {
return;
}
@@ -78,88 +94,88 @@
</script>
<style lang="scss" scoped>
.distribution-log-wrap {
width: 690rpx;
margin: 0 auto;
margin-bottom: 20rpx;
border-radius: 12rpx;
z-index: 3;
position: relative;
.distribution-log-wrap {
width: 690rpx;
margin: 0 auto;
margin-bottom: 20rpx;
border-radius: 12rpx;
z-index: 3;
position: relative;
.header-box {
width: 690rpx;
height: 76rpx;
position: relative;
.header-box {
width: 690rpx;
height: 76rpx;
position: relative;
.header-bg {
width: 690rpx;
height: 76rpx;
}
.header-bg {
width: 690rpx;
height: 76rpx;
}
.header-title {
position: absolute;
left: 20rpx;
top: 24rpx;
}
.header-title {
position: absolute;
left: 20rpx;
top: 24rpx;
}
.title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: 30rpx;
}
.title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: 30rpx;
}
.cicon-forward {
font-size: 30rpx;
font-weight: 400;
color: #ffffff;
line-height: 30rpx;
}
}
.cicon-forward {
font-size: 30rpx;
font-weight: 400;
color: #ffffff;
line-height: 30rpx;
}
}
.log-scroll {
height: 600rpx;
background: #fdfae9;
padding: 10rpx 20rpx 0;
box-sizing: border-box;
border-radius: 0 0 12rpx 12rpx;
.log-scroll {
height: 600rpx;
background: #fdfae9;
padding: 10rpx 20rpx 0;
box-sizing: border-box;
border-radius: 0 0 12rpx 12rpx;
.log-item-box {
margin-bottom: 20rpx;
.log-item-box {
margin-bottom: 20rpx;
.log-time {
// margin-left: 30rpx;
text-align: right;
font-size: 24rpx;
font-family: OPPOSANS;
font-weight: 400;
color: #c4c4c4;
}
}
.log-time {
// margin-left: 30rpx;
text-align: right;
font-size: 24rpx;
font-family: OPPOSANS;
font-weight: 400;
color: #c4c4c4;
}
}
.loadmore-wrap {
// line-height: 80rpx;
}
.loadmore-wrap {
// line-height: 80rpx;
}
.log-item {
// background: rgba(#ffffff, 0.2);
border-radius: 24rpx;
padding: 6rpx 20rpx 6rpx 12rpx;
.log-item {
// background: rgba(#ffffff, 0.2);
border-radius: 24rpx;
padding: 6rpx 20rpx 6rpx 12rpx;
.log-img {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.log-img {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.log-text {
max-width: 480rpx;
font-size: 24rpx;
font-weight: 500;
color: #333333;
}
}
}
}
</style>
.log-text {
max-width: 480rpx;
font-size: 24rpx;
font-weight: 500;
color: #333333;
}
}
}
}
</style>
+125 -118
View File
@@ -1,138 +1,145 @@
<!-- 分销:商菜单栏 -->
<template>
<view class="menu-box ss-flex-col">
<view class="header-box">
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title1.png')" />
<view class="ss-flex header-title">
<view class="title">功能专区</view>
<text class="cicon-forward"></text>
</view>
</view>
<view class="menu-list ss-flex ss-flex-wrap">
<view v-for="(item, index) in state.menuList" :key="index" class="item-box ss-flex-col ss-col-center"
@tap="sheep.$router.go(item.path)">
<image class="menu-icon ss-m-b-10" :src="sheep.$url.static(item.img)" mode="aspectFill"></image>
<view>{{ item.title }}</view>
</view>
</view>
</view>
<view class="menu-box ss-flex-col">
<view class="header-box">
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title1.png')" />
<view class="ss-flex header-title">
<view class="title">功能专区</view>
<text class="cicon-forward"></text>
</view>
</view>
<view class="menu-list ss-flex ss-flex-wrap">
<view
v-for="(item, index) in state.menuList"
:key="index"
class="item-box ss-flex-col ss-col-center"
@tap="sheep.$router.go(item.path)"
>
<image
class="menu-icon ss-m-b-10"
:src="sheep.$url.static(item.img)"
mode="aspectFill"
></image>
<view>{{ item.title }}</view>
</view>
</view>
</view>
</template>
<script setup>
import sheep from '@/sheep';
import { reactive } from 'vue';
import sheep from '@/sheep';
import { reactive } from 'vue';
const state = reactive({
menuList: [{
img: '/static/img/shop/commission/commission_icon1.png',
title: '我的团队',
path: '/pages/commission/team',
},
{
img: '/static/img/shop/commission/commission_icon2.png',
title: '佣金明细',
path: '/pages/commission/wallet',
},
{
img: '/static/img/shop/commission/commission_icon3.png',
title: '分销订单',
path: '/pages/commission/order',
},
{
img: '/static/img/shop/commission/commission_icon4.png',
title: '推广商品',
path: '/pages/commission/goods',
},
// {
// img: '/static/img/shop/commission/commission_icon5.png',
// title: '我的资料',
// path: '/pages/commission/apply',
// isAgentFrom: true,
// },
// todo @芋艿:邀请海报需要登录后的个人数据
{
img: '/static/img/shop/commission/commission_icon7.png',
title: '邀请海报',
path: 'action:showShareModal',
},
// TODO @芋艿:缺少 icon
const state = reactive({
menuList: [
{
// img: '/static/img/shop/commission/commission_icon7.png',
title: '推广排行',
path: '/pages/commission/promoter',
},
img: '/static/img/shop/commission/commission_icon1.png',
title: '我的团队',
path: '/pages/commission/team',
},
{
// img: '/static/img/shop/commission/commission_icon7.png',
title: '佣金排行',
path: '/pages/commission/commission-ranking',
}
],
});
img: '/static/img/shop/commission/commission_icon2.png',
title: '佣金明细',
path: '/pages/commission/wallet',
},
{
img: '/static/img/shop/commission/commission_icon3.png',
title: '分销订单',
path: '/pages/commission/order',
},
{
img: '/static/img/shop/commission/commission_icon4.png',
title: '推广商品',
path: '/pages/commission/goods',
},
// {
// img: '/static/img/shop/commission/commission_icon5.png',
// title: '我的资料',
// path: '/pages/commission/apply',
// isAgentFrom: true,
// },
{
img: '/static/img/shop/commission/commission_icon7.png',
title: '邀请海报',
path: 'action:showShareModal',
},
{
img: '/static/img/shop/commission/commission_icon8.png',
title: '推广排行',
path: '/pages/commission/promoter',
},
{
img: '/static/img/shop/commission/commission_icon9.png',
title: '佣金排行',
path: '/pages/commission/commission-ranking',
},
],
});
</script>
<style lang="scss" scoped>
.menu-box {
margin: 0 auto;
width: 690rpx;
margin-bottom: 20rpx;
margin-top: 20rpx;
border-radius: 12rpx;
z-index: 3;
position: relative;
}
.menu-box {
margin: 0 auto;
width: 690rpx;
margin-bottom: 20rpx;
margin-top: 20rpx;
border-radius: 12rpx;
z-index: 3;
position: relative;
}
.header-box {
width: 690rpx;
height: 76rpx;
position: relative;
.header-box {
width: 690rpx;
height: 76rpx;
position: relative;
.header-bg {
width: 690rpx;
height: 76rpx;
}
.header-bg {
width: 690rpx;
height: 76rpx;
}
.header-title {
position: absolute;
left: 20rpx;
top: 24rpx;
}
.header-title {
position: absolute;
left: 20rpx;
top: 24rpx;
}
.title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: 30rpx;
}
.title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: 30rpx;
}
.cicon-forward {
font-size: 30rpx;
font-weight: 400;
color: #ffffff;
line-height: 30rpx;
}
}
.cicon-forward {
font-size: 30rpx;
font-weight: 400;
color: #ffffff;
line-height: 30rpx;
}
}
.menu-list {
padding: 50rpx 0 10rpx 0;
background: #fdfae9;
border-radius: 0 0 12rpx 12rpx;
}
.menu-list {
padding: 50rpx 0 10rpx 0;
background: #fdfae9;
border-radius: 0 0 12rpx 12rpx;
}
.item-box {
width: 25%;
margin-bottom: 40rpx;
}
.item-box {
width: 25%;
margin-bottom: 40rpx;
}
.menu-icon {
width: 68rpx;
height: 68rpx;
background: #ffffff;
border-radius: 50%;
}
.menu-icon {
width: 68rpx;
height: 68rpx;
background: #ffffff;
border-radius: 50%;
}
.menu-title {
font-size: 26rpx;
font-weight: 500;
color: #ffffff;
}
</style>
.menu-title {
font-size: 26rpx;
font-weight: 500;
color: #ffffff;
}
</style>
+36 -21
View File
@@ -14,12 +14,18 @@
>
<template #rightBottom>
<view class="ss-flex ss-row-between">
<view class="commission-num" v-if="item.brokerageMinPrice === undefined">预计佣金:计算中</view>
<view class="commission-num" v-else-if="item.brokerageMinPrice === item.brokerageMaxPrice">
<view class="commission-num" v-if="item.brokerageMinPrice === undefined"
>预计佣金:计算中</view
>
<view
class="commission-num"
v-else-if="item.brokerageMinPrice === item.brokerageMaxPrice"
>
预计佣金:{{ fen2yuan(item.brokerageMinPrice) }}
</view>
<view class="commission-num" v-else>
预计佣金:{{ fen2yuan(item.brokerageMinPrice) }} ~ {{ fen2yuan(item.brokerageMaxPrice) }}
预计佣金:{{ fen2yuan(item.brokerageMinPrice) }} ~
{{ fen2yuan(item.brokerageMaxPrice) }}
</view>
<button
class="ss-reset-button share-btn ui-BG-Main-Gradient"
@@ -53,30 +59,29 @@
import $share from '@/sheep/platform/share';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import { showShareModal } from '@/sheep/hooks/useModal';
import SpuApi from '@/sheep/api/product/spu';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '../../sheep/hooks/useGoods';
import { fen2yuan } from '@/sheep/hooks/useGoods';
const state = reactive({
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
pageSize: 8,
},
loadStatus: '',
shareInfo: {},
});
// TODO 芋艿:分享的接入
function onShareGoods(goodsInfo) {
state.shareInfo = $share.getShareInfo(
{
title: goodsInfo.title,
image: sheep.$url.cdn(goodsInfo.image),
desc: goodsInfo.subtitle,
title: goodsInfo.name,
image: sheep.$url.cdn(goodsInfo.picUrl),
desc: goodsInfo.introduction,
params: {
page: '2',
query: goodsInfo.id,
@@ -84,10 +89,10 @@
},
{
type: 'goods', // 商品海报
title: goodsInfo.title, // 商品标题
image: sheep.$url.cdn(goodsInfo.image), // 商品主图
price: goodsInfo.price[0], // 商品价格
original_price: goodsInfo.original_price, // 商品原价
title: goodsInfo.name, // 商品名称
image: sheep.$url.cdn(goodsInfo.picUrl), // 商品主图
price: fen2yuan(goodsInfo.price), // 商品价格
original_price: fen2yuan(goodsInfo.marketPrice), // 商品原价
},
);
showShareModal();
@@ -99,19 +104,29 @@
pageSize: state.pagination.pageSize,
pageNo: state.pagination.pageNo,
});
if (code !== 0) {
state.loadStatus = 'error'; // 处理错误状态
return;
}
// 使用 Promise.all 来等待所有佣金请求完成
await Promise.all(
data.list.map(async (item) => {
try {
const res = await BrokerageApi.getProductBrokeragePrice(item.id);
item.brokerageMinPrice = res.data.brokerageMinPrice;
item.brokerageMaxPrice = res.data.brokerageMaxPrice;
} catch (error) {
console.error(`获取商品【${item.name}】的佣金时出错:`, error);
}
}),
);
// 在所有请求完成后合并列表和更新状态
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
// 补充分佣金额
data.list.forEach((item) => {
BrokerageApi.getProductBrokeragePrice(item.id).then((res) => {
item.brokerageMinPrice = res.data.brokerageMinPrice;
item.brokerageMaxPrice = res.data.brokerageMaxPrice;
});
});
}
onLoad(() => {
+16 -15
View File
@@ -41,10 +41,7 @@
<view class="no-box ss-flex ss-col-center ss-row-between">
<text class="order-code">订单编号:{{ item.bizId }}</text>
<text class="order-state">
{{
item.status === 0 ? '待结算'
: item.status === 1 ? '已结算' : '已取消'
}}
{{ item.status === 0 ? '待结算' : item.status === 1 ? '已结算' : '已取消' }}
( 佣金 {{ fen2yuan(item.price) }} 元 )
</text>
</view>
@@ -75,10 +72,9 @@
<script setup>
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { onLoad, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import { onPageScroll } from '@dcloudio/uni-app';
import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '../../sheep/hooks/useGoods';
@@ -100,22 +96,22 @@
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
pageSize: 8,
},
});
const tabMaps = [
{
name: '全部',
value: 'all',
value: -1,
},
{
name: '待结算',
value: '0', // 待结算
value: 0, // 待结算
},
{
name: '已结算',
value: '1', // 已结算
value: 1, // 已结算
},
];
@@ -129,12 +125,17 @@
// 获取订单列表
async function getOrderList() {
state.loadStatus = 'loading';
let { code, data } = await BrokerageApi.getBrokerageRecordPage({
const tab = tabMaps[state.currentTab];
const queryParams = {
pageSize: state.pagination.pageSize,
pageNo: state.pagination.pageSize,
pageNo: state.pagination.pageNo,
bizType: 1, // 获得推广佣金
status: state.currentTab > 0 ? state.currentTab : undefined,
});
status: tab.value,
}
if (tab.value < 0) {
delete queryParams.status;
}
const { code, data } = await BrokerageApi.getBrokerageRecordPage(queryParams);
if (code !== 0) {
return;
}
+269 -244
View File
@@ -1,297 +1,322 @@
<template>
<s-layout title="推广人排行榜">
<view class="PromoterRank" style='backgroundColor:red'>
<view class="redBg bg-color">
<view class="header">
<view class="nav acea-row row-center-wrapper" style='display:flex'>
<view class="item" :class="state.currentTab === index ? 'font-color' : ''"
v-for="(item, index) in tabMaps" :key="index" @click="switchTap(index)">
{{ item }}
</view>
</view>
<!-- top3 排名 -->
<view class="rank acea-row row-bottom row-around">
<view class="item" v-show="state.two.id">
<view class="pictrue">
<image :src="state.two.avatar"></image>
</view>
<view class="name line1">{{state.two.nickname}}</view>
<view class="num">{{ state.two.brokerageUserCount }}人</view>
</view>
<view class="item" v-show="state.one.id">
<view class="pictrue">
<image :src="state.one.avatar"></image>
</view>
<view class="name line1">{{state.one.nickname}}</view>
<view class="num">{{ state.one.brokerageUserCount }}人</view>
</view>
<view class="item" v-show="state.three.id">
<view class="pictrue">
<image :src="state.three.avatar"></image>
</view>
<view class="name line1">{{state.three.nickname}}</view>
<view class="num">{{ state.three.brokerageUserCount }}人</view>
</view>
</view>
</view>
</view>
<!-- 其它排名 -->
<view class="list" v-if="state.rankList.length">
<view class="item acea-row row-between-wrapper" v-for="(item,index) in state.rankList" :key="index">
<view class="num">{{ index + 4 }}</view>
<view class="picTxt acea-row row-between-wrapper">
<view class="pictrue">
<image :src="item.avatar"></image>
</view>
<view class="text line1">{{item.nickname}}</view>
</view>
<view class="people font-color">{{ item.brokerageUserCount }}人</view>
</view>
</view>
</view>
</s-layout>
<s-layout title="推广人排行榜">
<view class="promoter-rank">
<view class="nav">
<view
class="item"
:class="state.currentTab === index ? 'active' : ''"
v-for="(item, index) in tabMaps"
:key="index"
@click="switchTap(index)"
>
{{ item }}
</view>
</view>
<view class="top-list">
<template v-for="(item, index) in state.pagination.list" :key="index">
<view v-if="index < 3" class="item">
<view class="pictrue">
<view class="num">
<text>{{ index + 1 }}</text>
</view>
<image class="image" :src="item.avatar"></image>
</view>
<view class="people">{{ item.brokerageUserCount }}人</view>
<view class="nickname">{{ item.nickname }}</view>
</view>
</template>
</view>
</view>
<view class="other-list">
<template v-for="(item, index) in state.pagination.list" :key="index">
<view class="item" v-if="index > 2">
<view class="num">{{ index + 1 }}</view>
<view class="pictrue">
<image class="image" :src="item.avatar"></image>
</view>
<view class="nickname">{{ item.nickname }}</view>
<view class="people">{{ item.brokerageUserCount }}人</view>
</view>
</template>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
@tap="loadMore"
/>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util';
import BrokerageApi from '@/sheep/api/trade/brokerage';
const tabMaps = ['周排行', '月排行'];
const state = reactive({
currentTab: 0,
rankList: [],
times: [],
one: {}, // 排名第一
two: {}, // 排名第二
three: {}, // 排名第三
});
function switchTap(index) {
if (state.currentTab === index) {
return;
}
state.currentTab = index;
calculateTimes();
getRankList();
}
async function getRankList() {
// 重置数据
state.one = {};
state.two = {};
state.three = {};
state.rankList = [];
// 查询
const { code, data } = await BrokerageApi.getBrokerageUserRankPageByUserCount({
times: [],
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 10,
},
loadStatus: '',
});
function switchTap(index) {
if (state.currentTab === index) {
return;
}
state.currentTab = index;
resetPagination(state.pagination);
calculateTimes();
getRankList();
}
async function getRankList() {
state.loadStatus = 'loading';
// 查询
const { code, data } = await BrokerageApi.getBrokerageUserRankPageByUserCount({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
'times[0]': state.times[0],
'times[1]': state.times[1],
});
if (code !== 0) {
return;
}
state.rankList = data.list;
state.one = state.rankList.shift() || {};
state.two = state.rankList.shift() || {};
state.trhee = state.rankList.shift() || {};
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
function calculateTimes() {
let times;
if (state.currentTab === 0) {
times = getWeekTimes();
} else {
times = getMonthTimes();
}
state.times = [formatDate(times[0]), formatDate(times[1])]
}
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getList();
}
// 上拉加载更多
onReachBottom(() => loadMore());
function calculateTimes() {
let times;
if (state.currentTab === 0) {
times = getWeekTimes();
} else {
times = getMonthTimes();
}
state.times = [formatDate(times[0]), formatDate(times[1])];
}
onLoad(function () {
calculateTimes();
getRankList();
});
function formatDate(date) {
return sheep.$helper.timeFormat(date, 'yyyy-mm-dd hh:MM:ss');
}
function formatDate(date) {
return sheep.$helper.timeFormat(date, 'yyyy-mm-dd hh:MM:ss');
}
// TODO 芋艿:此处可考虑抽离
// 此处可考虑抽离
/**
* 获得当前周的开始和结束时间
*/
function getWeekTimes() {
const today = new Date();
const dayOfWeek = today.getDay();
return [
new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0),
new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - dayOfWeek), 23, 59, 59)
]
}
/**
* 获得当前周的开始和结束时间
*/
function getWeekTimes() {
const today = new Date();
const dayOfWeek = today.getDay();
return [
new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0),
new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + (6 - dayOfWeek),
23,
59,
59,
),
];
}
/**
* 获得当前月的开始和结束时间
*/
function getMonthTimes() {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth();
const startDate = new Date(year, month, 1, 0, 0, 0);
const nextMonth = new Date(year, month + 1, 1);
const endDate = new Date(nextMonth.getTime() - 1);
return [startDate, endDate]
}
// TODO 芋艿:css 需要优化下;并且展示样式有问题
/**
* 获得当前月的开始和结束时间
*/
function getMonthTimes() {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth();
const startDate = new Date(year, month, 1, 0, 0, 0);
const nextMonth = new Date(year, month + 1, 1);
const endDate = new Date(nextMonth.getTime() - 1);
return [startDate, endDate];
}
</script>
<style lang='scss' scoped>
.PromoterRank .redBg {
padding: 45rpx 0 30rpx 0;
}
<style lang="scss" scoped>
.promoter-rank {
padding: 45rpx 0 30rpx 0;
background: url('data:image/jpeg;base64,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 truncated
no-repeat;
width: 100%;
background-size: 100% 100%;
.PromoterRank .header {
background: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwkHBgoJCAkLCwoMDxkQDw4ODx4WFxIZJCAmJSMgIyIoLTkwKCo2KyIjMkQyNjs9QEBAJjBGS0U+Sjk/QD3/2wBDAQsLCw8NDx0QEB09KSMpPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT3/wgARCAHMAu4DAREAAhEBAxEB/8QAGQABAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAGQEBAQEBAQEAAAAAAAAAAAAAAAECAwUH/9oADAMBAAIQAxAAAAD5nh/QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANSaSyAS2GWpQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpNzOpAAAAJbzus2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWOkxqQAAAAACW8rqWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUnXOSAAaSgEMqAAOetYugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqTrnAAqbTUgAAGbcLFAHPWsXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsnbOAB0Z1IAAAABm3m0AOWt5tAAAAAAAAAAAAAAAAAAAAAqCgoASgAEUQhFAAAAA7ZxZAOrNkAAAAAAlvJoCW8dbAAAAAAAAAAAAAAAAAqUFSgpCikAVAKgoKCgoCFzbIi5MrhRuZ6ZyB0Z1IAANzNIYugABm3m0BjWud0AAAAAAAAAAAAAABtmgAAAApAUAqAUFQCgoKChKotEhIksEm85AA9GefoxzqARfPvp59dAAOd1lQOG9lAAAAAAAAAAAAAAHRkAAAAACghQCoBQVC1BQUCqCwsoUUshJCJJ6+fDrnIAAHO68fTtFAhyuwOetYugAAAAAAAAAAAAAB0ZAAAAAAAoIUAqAUFCUFBQUVYJVUSxQC2WN5xvOemMazkADhrfl32AHO6yozbz1vTOipSgIWBRAQgIsIsAAAAB0ZAAAAAAAAFICgFQUFCUFBQUoFWFVEWrIpChZOmMdMY6YwB4enfF0BNWW6spKLIEBFgIogWAEAABlYZUAAdGQAAAAAAAAKCFAKgoKEFBQUoKCgqCpQAKQpHTOOmMcmuW9WroIoiwEUSBAsICKIAsBAAACGVyoHRkAAAAAAAAACghQCoWoKEoKClAKClSoKAABQQqFCAQECwEUSBFEIFgIFEBAAAAZXK7ZAAAAAAAAAAAFIUBKpKCgqClBQUFSgqAUAACgAABCIIFgUQLIEIFgIFgBAAIAGaAAAoAAAAAAAAKAACpQUAqUFBQVKUBBVJQAQtpAAABAQEIFgUSBCBYCAkoEAApGaAAAAFAAAAAAAAKQoBQlUlCUFLSKDQSgFCAUEtAAoQAAACAhFhCQWECwECwgAIIVkoAAAAKAAAAAAAACkKAUFKhKClAKUqCgFQVZQAAAAqAAAAACLCEISXJFhFEBIUBlQAQUAAAoAAAAAAAABQAUossEpQUFKCgqAKSqqAQoIFIKCoAAAIohCElyRchYQEBAZUACgBKAAUAAAAAAAAFIUApQlBUFBQUoKEC0UAIUAhQACCgqAsBCEMrmXJFhCKIARRAACoBQEoAKAAAAAAAAAUAFBQlCUoKAUFAKKAFAQAAoAAIABCGJcrlckWAigCAAAAAFQUJQAUAAAAAAAAAAtIoBSoKgoKAUFBQKAAAoQAAoAJFJlcxhcLFyFEAAAAAAAAAKAgAAAAAAAAAoBCgHSZoANzNABUKBSVlQKQxdAAAAEAKAIQysIsAAAAAAAAAAAAAAAAAAAAAAAAABZO2ckAG01JUAAi5twoAHLW82gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACydZmyAAC0kWyAABLed1m0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpM7zkAAAAADNvLWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZNpvMIAAAM287qWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACydJnUgEXnrWbQAAAAAAAAAAAAAAAAAAB/8QALBAAAgECAwcEAwEBAQAAAAAAAAECERIDEyAUMVBRYWKhITBBYAQQQFJCsP/aAAgBAQABPwD/AMzJRbFhsWEjLiWR5GXEykZT+GWtfVFhtigl7TimPD5fUIxciMFH3nFSJQcfpsIVEqaUmxQZl9TL6mX1LGUa1ShT1X0uEK6VFsUEvYcExxa0zh8r6TCNz0xhz9xw5aZxpwqjLWWMsZYzL6mX1MvqZXUyupldTK7jJ7jJ6mS+aMmXQyZGVPkZcv8ALLXyftJVIq1U0RjT3pRroaqhqjpwKjLWWFiLUUX89kf8oyYP4Nnj1Nm5SNnlzQ8Ga/5Gmt6a/WHH50QjTWsKb3RZs+J/keFNb4vXKNdE41VeAWrgrhF74oy4mVyZlMjB19Vqw/xm/Wfp0IwjDcqaJQjPeqmJ+M16w9emqapoao2vpVEWlGYOEoKu9+xjYKxFVekhpptPQ1VU0Yi+fao+RR8ij5FHx9CdBY0181F+RzQsWEvnV+RhXK9b1v0zVH+5qsWKEnuTFgz5UFgP5ZkL5bMqJlx5Fq5Ip7FEWosRZ1LGWvjik47nQjjyW+jI48XvqhNPc6/vEhZiNaJQchYXNmWi1L+exFhRriS9lEcaa6i/IXyj8hqbTiKDFBf3uKY4DTXD1xBwRY1w1fXl/PQoUKfQFwCiLS0tZR8fXAaItRYiws6lrLShTiyfsV/sqVKlSpUqVKlSpXiNdFf7KjY2VKlSpUqV4WuEtjY2NlSpX6u2NjY2V/lqVLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi8vLy8v6F/Qv6C0qNdzLOpYWlpQoUKFDcZnQzOhmdDN6Gb0Np7TauzybV2eTauzybV2eTauzybV2eTauzybV2eTauzybV2eTauzybV2eTauzybV2eTae3ybT2mf2mb0LypXikJWi0IWJzFJPU5JDxOWqc6+i3fS4ytE09dz5l8i589bdESnX6am0Rmn7zmkOTl9QU2hYie8XsvESHNv6qpyIuuhug5sq3wj//EACMRAAMAAgIBAwUAAAAAAAAAAAABERIgAmAwA1FwEFCAkLD/2gAIAQIBAT8A/mZ0pSlKXqt8l+NaUv47VGSKt1oujQhN3yLdLBctl06IxIxvwJzwrpz4Ifp+w+D24vVdUiH6aMGR/VaJMhOrPgmP0/YSaIzFfuapSlKUyMjIyMjIyMjIyKUpS/GU6vOwTxTq02n2n//EACQRAAMAAgICAgIDAQAAAAAAAAABERIgAjBQYAMQQFExQZCw/9oACAEDAQE/AP8AmZ5GRWVlZkUvqlL1UT9Qpe5OCfpre9RTIpVun6W3rS9FE9U/SW9W+xPVPxdKUpSmRkUpSlKZFRUVfgN9yeq8HSlL+RWVmTMjJFX29G98kZoyW6ei8BfC16PZ8/0Nt6JtCLine truncated
width: 100%;
height: 460rpx;
background-size: 100% 100%;
}
.nav {
display: flex;
width: 450rpx;
height: 66rpx;
border: 1px solid #fff;
border-radius: 33rpx;
font-size: 30rpx;
color: #fff;
margin: 0 auto;
overflow: hidden;
.PromoterRank .header .nav {
width: 450rpx;
height: 66rpx;
border: 1px solid #fff;
border-radius: 33rpx;
font-size: 30rpx;
color: #fff;
margin: 0 auto;
}
.item {
width: 50%;
height: 100%;
text-align: center;
line-height: 60rpx;
.PromoterRank .header .nav .item {
width: 223rpx;
height: 100%;
text-align: center;
line-height: 60rpx;
}
&.active {
color: #333;
background-color: white;
}
}
}
.PromoterRank .header .nav .item.font-color:nth-of-type(1) {
background-color: #fff;
border-radius: 33rpx 0 0 33rpx;
color: red
}
.top-list {
display: flex;
justify-content: center;
align-items: center;
padding-top: 50rpx;
.PromoterRank .header .nav .item.font-color:nth-of-type(2) {
background-color: #fff;
border-radius: 0 33rpx 33rpx 0;
color: red
}
.item {
margin: 0 20rpx;
.PromoterRank .header .rank {
padding: 0 20rpx;
margin-top: 30rpx;
}
.pictrue {
position: relative;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIgAAACxCAYAAAAfz/U3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODVBODg2MzdEOEU1MTFFOTg4RTM5OTQyQjhDODMzMTciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODVBODg2MzZEOEU1MTFFOTg4RTM5OTQyQjhDODMzMTciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuM0xM0AABKbSURBVHja7F0LdB1VFb0vSRuSJmk+TWM/KJC2gAgUBWVVQCyKrQsVqC2ggFCgtFC0pbJc8il/qC6WRfn/EVCgUAu4lLYsiiIgVflJxX7oR2kS0jRJbdqkaZPWs+fdV+bd3pk3783Mm3kzZ6911ry8+eae/c4599xz7yT27Nkj8oG1a9eKEKKOZCbJZJJRJKXy+z6SDSR/JvkFyQdBP2hjY2Mg9y0S8cUFJO+RXEdymIkcQIkkTOqY+XFtpLgS5OckD5KMcHAsyDKL5HmSAUyQ6GMOyRU5nPdtkseZINHGF0huc3H+GdLtMEEiihs8ukYpEyR6+BzJNz24znCSbzFBoofxHl7r60yQ6OEwD691FBMkehjp4bUamCDRQ7uH19ri87MmwtJoJQHeG8HeeSRfJfkUST/JJpIlJE+SNHt8vw0eXmu1D+1xKMlZJONIhpH0kDSRPEeykGRrEEpKBDQWM43kFpEcC9EBRLmW5F4PH+FUkkUeXetGkrkeXWsgyfUimcCzytSi8a5obGxcFAeC3E7yI4en3Ucy3aNHKCNZJ62VF13mf3n0XK+QnOjw2OuIJNdHOQaZlQU5gItJrvHo3jDZd3pwnWc8JMeiLMhhEIR+aOdG1YIMpc16kvIsT+2Tv9hVHjwG7o0h/KNzPL+T5Eskazx4FoztPJ9jsD2GLElH1CzI+TmQIxVIX+rRM3SL5HjKdhdxzBqPnmVOjuchbsubFcknQdxkH0+UwZwXQBxyPMmKLM75D8nJJK969Ayo/jnOxflfiyJBDnFx7iibHk8ueEc2shOSLJbKfMnD+3/GZduPiiJBqlycWyq8z9m0kvzSwXGzSTZ6fO9BHvTIIkeQj1yc20LS5cMzvSGDYCv8V4rXcJsE/DiKBPnQxblQkh/pbRQj21VTr5KBrddY7fK6G6JIkN+7OPd5H5+rNUOX0g/AGv7WxflPRpEgC0j+l8N5O0Qyo+oX7MY4mny87105nrfG5x9MYASBIqbmcN5U4e/oqd3IabmP932X5Ooczju3sbFxTxQJAvyO5Mosjv9pHsxpjc2+T/t875uF8wFJuKXTiBxv5lNhQdSD3EpyeoZIfKVIpqLn5eF57AhSkYf7zxDJSnm7wBNjP+OJHM/lW1lBFQxhkOpnNvvvcBnUOgUSTnZzGo8U3iborPCwSNbBWLpZIsc/glBUkBVlbTb7TsnTM2DgzS6FX03y+Tw8BxKBE232dwSlpCAJYudiviKSFWd+Y6KDY/IxxeFkm3gHxVOb40gQJL922/QeLvT5/lDIdxwcd04e3MzMDD+knjgSpDWDm0Hw5ueYww8dBqHVPpMVtSl2I90IXnvjSBB029632Y/SwJt8ujeKl6ZlcfycDL0dN8CAoV0uZlWAOgqUIEj2rHNgeg/34d73k1RmcXw9ya98eA4UQo3LcMwHcSUIkKk6Cz0MJMq8HOr/nsPYQ8XZJN/18DnGCmflBuvjTBAnvw5MmVzg0f1AjMdcnP+USM7jcQvkXv5AUpzhuM44uxgA4xFOJgSdRvIbl/fCfNqnHSjFDjgXVe1HuLjGKEkOJ934lSKPtR9hJEizbASnrmGR7FVkC8yteU14s64HurzIal6Sw7nIqbxNcrDD45cHrJ9QzM39WxbHniob+CyHxx8oY5h7hLcjs5gBh+H65xxakxHy+BeyDI7fDlo5iRAsgzmJ5NkcLrlMJMcwMLqJcsadMqjFzPuDRDLBheUtq3z+11Cv8qJIrl+G+pGN8llgafaXzzA5h24ykoiYD/RvI2gJaBnMvBHEhjgNsjdTmeMluqViUIw0WOY4Bgf4L2HcZBdJrXC3KuJbRIqjg7YggbsYaoRWGazmCriO0SKZkRwdMDmEJEaDcL9k5lshcP+hWR9kuWCoeJ0J8gleYj6kAYNzS5kgn+A14W+BcKFhGbnej8PwICVheAhqjG4KVlGvepkv/2RJiRg4cKAYMGCA8RlSXFxsSFFRkSHGr0Vud+/evXcL6e/vN2TXrl2G7Ny509imjvMBC8PC1JIQ/Wpe8IIgUPp+++0nysrKRGlpqUGMlOIdm1WFMFYAaUAWSE9Pj9ixY4fxnQfuZQkTRO9mkFnNqpIskUgYhCgvLzcEhMgXQEYQETJ4cLLzBLJ0d3cbAsLkkEZYTha1mQmyr5vZQW4GYyWzHfVtiQwVFRVi0KBBWVsIPwGCQqqrqw0XtH37drFt2zaDMA7xQJiCocATZSkQOTBq+wjJMXaxRFVVlaisrDQ+FxL6+vpEV1eX2Lp1q/HZBlgBaSb9YFYwQZLEGCKSq/xNs7JocCH4RcJaRAGwKlu2bDFckBWfRLKo6VoiyubYEoTI8X2RfJtTvZUbqampMQgSRYAgnZ2ddu4HNbuXE0meiBVBiBj7y1/IBN1+BH21tbWRJYaOKB0dHUZPyAJY5WgaEeWjyBOEyIGyPczWr1X3IU9RV1cXGVeSi+tpb283ciwadEqSPBtJghAxMIUB65RO1XVV4UoQZ+BznAF9ID6B67HQzcMyiO2JDEGIHCjcQaZ0rC4Ara+vz2v+ohCAfEpbW5tVIIvR79OJJOsLniBEDkwrfFJ1KbAUiDNgNRjWgDVBfKLRE+pOziKSLC1YghA5sKwByv0GqLFGQ0ODkQpnZEZvb69obW3VxSb4YgaR5KGCIggRA4EE3ohwlboPAejQoUNDlf0sBCAru2nTJiOQ1QAL0Vzjx8pDnhNEkgMFujPUfXApCEYZuQPBK1yOBrDUl3pNEk8JQuTAvBGky89R4w1YDYydMNwDYzuwJhrdoXD6fCJJf+gIIi3Ho0JZaB6uZNiwYbFJeuUL6N20tLToalJ+LUniiWKLPCTHXSo5MBw+YsQIJocPQJuibdHGCn4AXUidhIMgIvl6sbSYA6Ot+Ac4v+Ef0LZoY83I9gypk+BdDDEVD3O3znKgO8vwH+j+NjU16arZLiFXc09gBCFyYGUczCorNsccbDnyD2ReQRIlJkGeZAKRZFneCSLT55jEXGvurSAgxWgsI5jAtbm5We3doE98DJFkXS7XLMqRHJjNtlAo6XN0ZZkcwQau0IEC6Gih1FneglSsjJP2/nokwTjPETygA+hCAQZJ78gLQWQ9R9qqf0ifc4Y0PIAuNDU1U6Xu/ItBZCXYu2bXgp7KyJEjeWwlZECwunHjRnWAD0VHR2ZTmZatVu9Xg1KMyjI5wgfoBLpRCrBqpA69dzGywHiCGnfwkH14Ad1o4pEJUpfeuRg5NQEr3QwxR8zIdzDCD+RHlMo0TKU41MmUCqcW5AYzOWC2UCbIKAxAV4qrGSJ16t6CkPXASsd4EXGxOUrWmC5GiIEaEtSSmIC8/FFkRd53a0Hmm8mBXgt3aQuz66uMjRVL3ebuYsh6YFXhk8zfYd5K3KcmFCKgM+hOwUlSxzlbkLS3LSCNHtdJTVEAdKcZCrkpJ4LI6Qrj1G4to7Ch0eE4qeusLchPzH9gIjVXhhU+Uovt2Ok6I0GIURiIG68GOYzoBKwKxkudO7Ygs1TWsfWIlhXR6HOWI4IQk+Ckppi/4+mR0YNGp5Ol7jNaELxZaS+9UBDLPZdo9miUYucyqfuMBLnA/AfWBGNEExrdXmhLEDIxeP3E3vefILmCBeMY0QR0qyQ9D5ccsLQgU9RgptBWE2Q4B3SrSZxNsSLine truncated
no-repeat;
background-size: 100% 100%;
width: 136rpx;
height: 177rpx;
position: relative;
margin: 0 auto;
text-align: center;
.PromoterRank .header .rank .item .pictrue {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIgAAACxCAYAAAAfz/U3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODVBODg2MzdEOEU1MTFFOTg4RTM5OTQyQjhDODMzMTciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODVBODg2MzZEOEU1MTFFOTg4RTM5OTQyQjhDODMzMTciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuM0xM0AABKbSURBVHja7F0LdB1VFb0vSRuSJmk+TWM/KJC2gAgUBWVVQCyKrQsVqC2ggFCgtFC0pbJc8il/qC6WRfn/EVCgUAu4lLYsiiIgVflJxX7oR2kS0jRJbdqkaZPWs+fdV+bd3pk3783Mm3kzZ6911ry8+eae/c4599xz7yT27Nkj8oG1a9eKEKKOZCbJZJJRJKXy+z6SDSR/JvkFyQdBP2hjY2Mg9y0S8cUFJO+RXEdymIkcQIkkTOqY+XFtpLgS5OckD5KMcHAsyDKL5HmSAUyQ6GMOyRU5nPdtkseZINHGF0huc3H+GdLtMEEiihs8ukYpEyR6+BzJNz24znCSbzFBoofxHl7r60yQ6OEwD691FBMkehjp4bUamCDRQ7uH19ri87MmwtJoJQHeG8HeeSRfJfkUST/JJpIlJE+SNHt8vw0eXmu1D+1xKMlZJONIhpH0kDSRPEeykGRrEEpKBDQWM43kFpEcC9EBRLmW5F4PH+FUkkUeXetGkrkeXWsgyfUimcCzytSi8a5obGxcFAeC3E7yI4en3Ucy3aNHKCNZJ62VF13mf3n0XK+QnOjw2OuIJNdHOQaZlQU5gItJrvHo3jDZd3pwnWc8JMeiLMhhEIR+aOdG1YIMpc16kvIsT+2Tv9hVHjwG7o0h/KNzPL+T5Eskazx4FoztPJ9jsD2GLElH1CzI+TmQIxVIX+rRM3SL5HjKdhdxzBqPnmVOjuchbsubFcknQdxkH0+UwZwXQBxyPMmKLM75D8nJJK969Ayo/jnOxflfiyJBDnFx7iibHk8ueEc2shOSLJbKfMnD+3/GZduPiiJBqlycWyq8z9m0kvzSwXGzSTZ6fO9BHvTIIkeQj1yc20LS5cMzvSGDYCv8V4rXcJsE/DiKBPnQxblQkh/pbRQj21VTr5KBrddY7fK6G6JIkN+7OPd5H5+rNUOX0g/AGv7WxflPRpEgC0j+l8N5O0Qyo+oX7MY4mny87105nrfG5x9MYASBIqbmcN5U4e/oqd3IabmP932X5Ooczju3sbFxTxQJAvyO5Mosjv9pHsxpjc2+T/t875uF8wFJuKXTiBxv5lNhQdSD3EpyeoZIfKVIpqLn5eF57AhSkYf7zxDJSnm7wBNjP+OJHM/lW1lBFQxhkOpnNvvvcBnUOgUSTnZzGo8U3iborPCwSNbBWLpZIsc/glBUkBVlbTb7TsnTM2DgzS6FX03y+Tw8BxKBE232dwSlpCAJYudiviKSFWd+Y6KDY/IxxeFkm3gHxVOb40gQJL922/QeLvT5/lDIdxwcd04e3MzMDD+knjgSpDWDm0Hw5ueYww8dBqHVPpMVtSl2I90IXnvjSBB029632Y/SwJt8ujeKl6ZlcfycDL0dN8CAoV0uZlWAOgqUIEj2rHNgeg/34d73k1RmcXw9ya98eA4UQo3LcMwHcSUIkKk6Cz0MJMq8HOr/nsPYQ8XZJN/18DnGCmflBuvjTBAnvw5MmVzg0f1AjMdcnP+USM7jcQvkXv5AUpzhuM44uxgA4xFOJgSdRvIbl/fCfNqnHSjFDjgXVe1HuLjGKEkOJ934lSKPtR9hJEizbASnrmGR7FVkC8yteU14s64HurzIal6Sw7nIqbxNcrDD45cHrJ9QzM39WxbHniob+CyHxx8oY5h7hLcjs5gBh+H65xxakxHy+BeyDI7fDlo5iRAsgzmJ5NkcLrlMJMcwMLqJcsadMqjFzPuDRDLBheUtq3z+11Cv8qJIrl+G+pGN8llgafaXzzA5h24ykoiYD/RvI2gJaBnMvBHEhjgNsjdTmeMluqViUIw0WOY4Bgf4L2HcZBdJrXC3KuJbRIqjg7YggbsYaoRWGazmCriO0SKZkRwdMDmEJEaDcL9k5lshcP+hWR9kuWCoeJ0J8gleYj6kAYNzS5kgn+A14W+BcKFhGbnej8PwICVheAhqjG4KVlGvepkv/2RJiRg4cKAYMGCA8RlSXFxsSFFRkSHGr0Vud+/evXcL6e/vN2TXrl2G7Ny509imjvMBC8PC1JIQ/Wpe8IIgUPp+++0nysrKRGlpqUGMlOIdm1WFMFYAaUAWSE9Pj9ixY4fxnQfuZQkTRO9mkFnNqpIskUgYhCgvLzcEhMgXQEYQETJ4cLLzBLJ0d3cbAsLkkEZYTha1mQmyr5vZQW4GYyWzHfVtiQwVFRVi0KBBWVsIPwGCQqqrqw0XtH37drFt2zaDMA7xQJiCocATZSkQOTBq+wjJMXaxRFVVlaisrDQ+FxL6+vpEV1eX2Lp1q/HZBlgBaSb9YFYwQZLEGCKSq/xNs7JocCH4RcJaRAGwKlu2bDFckBWfRLKo6VoiyubYEoTI8X2RfJtTvZUbqampMQgSRYAgnZ2ddu4HNbuXE0meiBVBiBj7y1/IBN1+BH21tbWRJYaOKB0dHUZPyAJY5WgaEeWjyBOEyIGyPczWr1X3IU9RV1cXGVeSi+tpb283ciwadEqSPBtJghAxMIUB65RO1XVV4UoQZ+BznAF9ID6B67HQzcMyiO2JDEGIHCjcQaZ0rC4Ara+vz2v+ohCAfEpbW5tVIIvR79OJJOsLniBEDkwrfFJ1KbAUiDNgNRjWgDVBfKLRE+pOziKSLC1YghA5sKwByv0GqLFGQ0ODkQpnZEZvb69obW3VxSb4YgaR5KGCIggRA4EE3ohwlboPAejQoUNDlf0sBCAru2nTJiOQ1QAL0Vzjx8pDnhNEkgMFujPUfXApCEYZuQPBK1yOBrDUl3pNEk8JQuTAvBGky89R4w1YDYydMNwDYzuwJhrdoXD6fCJJf+gIIi3Ho0JZaB6uZNiwYbFJeuUL6N20tLToalJ+LUniiWKLPCTHXSo5MBw+YsQIJocPQJuibdHGCn4AXUidhIMgIvl6sbSYA6Ot+Ac4v+Ef0LZoY83I9gypk+BdDDEVD3O3znKgO8vwH+j+NjU16arZLiFXc09gBCFyYGUczCorNsccbDnyD2ReQRIlJkGeZAKRZFneCSLT55jEXGvurSAgxWgsI5jAtbm5We3doE98DJFkXS7XLMqRHJjNtlAo6XN0ZZkcwQau0IEC6Gih1FneglSsjJP2/nokwTjPETygA+hCAQZJ78gLQWQ9R9qqf0ifc4Y0PIAuNDU1U6Xu/ItBZCXYu2bXgp7KyJEjeWwlZECwunHjRnWAD0VHR2ZTmZatVu9Xg1KMyjI5wgfoBLpRCrBqpA69dzGywHiCGnfwkH14Ad1o4pEJUpfeuRg5NQEr3QwxR8zIdzDCD+RHlMo0TKU41MmUCqcW5AYzOWC2UCbIKAxAV4qrGSJ16t6CkPXASsd4EXGxOUrWmC5GiIEaEtSSmIC8/FFkRd53a0Hmm8mBXgt3aQuz66uMjRVL3ebuYsh6YFXhk8zfYd5K3KcmFCKgM+hOwUlSxzlbkLS3LSCNHtdJTVEAdKcZCrkpJ4LI6Qrj1G4to7Ch0eE4qeusLchPzH9gIjVXhhU+Uovt2Ok6I0GIURiIG68GOYzoBKwKxkudO7Ygs1TWsfWIlhXR6HOWI4IQk+Ckppi/4+mR0YNGp5Ol7jNaELxZaS+9UBDLPZdo9miUYucyqfuMBLnA/AfWBGNEExrdXmhLEDIxeP3E3vefILmCBeMY0QR0qyQ9D5ccsLQgU9RgptBWE2Q4B3SrSZxNsSPIGeY/uMLine truncated
background-size: 100% 100%;
width: 136rpx;
height: 177rpx;
position: relative;
margin: 0 auto;
}
.image {
display: inline-block;
margin-top: 47rpx;
width: 130rpx;
height: 130rpx;
border-radius: 50%;
}
.PromoterRank .header .rank .item .pictrue image {
position: absolute;
width: 130rpx;
height: 130rpx;
display: block;
bottom: 2rpx;
border-radius: 50%;
left: 50%;
margin-left: -65rpx;
}
.num {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
.PromoterRank .header .rank .item:nth-of-type(2) .pictrue {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJwAAADNCAYAAABNahkfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUU1MTlBRjBEOEU1MTFFOUEwRjRGRTQyMUZGNDk5NEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUU1MTlBRUZEOEU1MTFFOUEwRjRGRTQyMUZGNDk5NEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIv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 truncated
width: 156rpx;
height: 205rpx;
}
text {
display: inline-block;
width: 30rpx;
height: 30rpx;
line-height: 30rpx;
color: white;
border-radius: 50%;
overflow: hidden;
font-size: 22rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
}
}
}
.PromoterRank .header .rank .item:nth-of-type(2) .pictrue image {
width: 150rpx;
height: 150rpx;
margin-left: -75rpx;
}
.nickname {
color: white;
font-weight: 20rpx;
text-align: center;
max-width: 200rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.PromoterRank .header .rank .item:nth-of-type(3) .pictrue {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIgAAACxCAYAAAAfz/U3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTExRTlGRDlEOEU2MTFFOTg0MDg4RjQ5MDM3QjNGRTIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTExRTlGRDhEOEU2MTFFOTg0MDg4RjQ5MDM3QjNGRTIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIv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 truncated
}
.people {
text-align: center;
color: white;
font-weight: 20rpx;
}
.PromoterRank .header .rank .item:nth-of-type(3) .pictrue image {
margin-left: -64rpx;
}
&:first-second {
.pictrue {
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJwAAADNCAYAAABNahkfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUU1MTlBRjBEOEU1MTFFOUEwRjRGRTQyMUZGNDk5NEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUU1MTlBRUZEOEU1MTFFOUEwRjRGRTQyMUZGNDk5NEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIv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 truncated
no-repeat;
}
}
.PromoterRank .header .rank .item .name {
font-size: 30rpx;
color: #fff;
margin-top: 22rpx;
text-align: center;
width: 170rpx;
}
&:first-third {
.pictrue {
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIgAAACxCAYAAAAfz/U3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNGUyNzRkOS00YmQ4LTUzNGItYWZkZS02ZTlmMTgxYmZlYWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTExRTlGRDlEOEU2MTFFOTg0MDg4RjQ5MDM3QjNGRTIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTExRTlGRDhEOEU2MTFFOTg0MDg4RjQ5MDM3QjNGRTIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTAxZGU2OWItNWM2My0xOTQ1LTllZmMtMGIzNjcwYTFmNWUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE0ZTI3NGQ5LTRiZDgtNTM0Yi1hZmRlLTZlOWYxODFiZmVhZSIv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 truncated
no-repeat;
}
}
}
}
}
.PromoterRank .header .rank .item .num {
font-size: 30rpx;
color: #fff;
text-align: center;
}
.other-list {
margin-top: 20rpx;
padding: 0 16rpx;
.PromoterRank .list {
width: 710rpx;
background-color: #fff;
border-radius: 20rpx;
margin: -60rpx auto 0 auto;
padding: 0 30rpx;
}
.item {
border-radius: 14rpx;
margin-bottom: 12rpx;
padding: 15rpx 20rpx;
display: flex;
justify-content: space-around;
align-items: center;
background: white;
.PromoterRank .list .item {
border-bottom: 1px solid #f3f3f3;
height: 101rpx;
font-size: 28rpx;
}
.num {
flex: 0.1;
text-align: center;
}
.PromoterRank .list .item .num {
color: #666;
width: 70rpx;
}
.pictrue {
flex: 0.2;
display: flex;
align-items: center;
.PromoterRank .list .item .picTxt {
width: 350rpx;
}
.image {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
}
}
.PromoterRank .list .item .picTxt .pictrue {
width: 68rpx;
height: 68rpx;
}
.nickname {
flex: 0.5;
}
.PromoterRank .list .item .picTxt .pictrue image {
width: 100%;
height: 100%;
display: block;
border-radius: 50%;
}
.PromoterRank .list .item .picTxt .text {
width: 262rpx;
color: #333;
}
.PromoterRank .list .item .people {
width: 175rpx;
text-align: right;
}
</style>
.people {
flex: 0.2;
text-align: center;
}
}
}
</style>
+28 -7
View File
@@ -1,8 +1,29 @@
<!-- 页面 TODO 芋艿:该页面的实现代码需要优化,包括 js 和 css,以及相关的样式设计 -->
<template>
<s-layout title="我的团队" :class="state.scrollTop ? 'team-wrap' : ''" navbar="inner">
<view
class="header-box"
:style="[
{
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
paddingTop: Number(statusBarHeight + 108) + 'rpx',
},
]"
>
<!-- 推广数据总览 -->
<view class="team-data-box ss-flex ss-col-center ss-row-between" style="width: 100%">
<view class="data-card" style="width: 100%">
<view class="total-item" style="width: 100%">
<view class="item-title" style="text-align: center">推广人数</view>
<view class="total-num" style="text-align: center">
{{ state.summary.firstBrokerageUserCount + state.summary.secondBrokerageUserCount || 0 }}
</view>
</view>
</view>
</view>
</view>
<view class="promoter-list">
<view
<!--<view
class="promoterHeader bg-color"
style="backgroundcolor: #e93323 !important; height: 218rpx; color: #fff"
>
@@ -21,9 +42,9 @@
</view>
<view class="iconfont icon-tuandui" />
</view>
</view>
<view style="padding: 0 30rpx">
<view class="nav acea-row row-around l1">
</view>-->
<view style="padding: 0 20rpx">
<view class="nav acea-row row-around l1" style="margin-top:20rpx;">
<view :class="state.level == 1 ? 'item on' : 'item'" @click="setType(1)">
一级({{ state.summary.firstBrokerageUserCount || 0 }})
</view>
@@ -152,7 +173,7 @@
</view>
</block>
<block v-if="state.pagination.list.length === 0">
<view style="text-align: center">暂无推广人数</view>
<view style="text-align: center;margin-top:30rpx;">暂无推广人数</view>
</block>
</view>
</view>
@@ -235,7 +256,7 @@
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive, ref } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import { onPageScroll } from '@dcloudio/uni-app';
import BrokerageApi from '@/sheep/api/trade/brokerage';
@@ -456,7 +477,7 @@
.promoter-list .nav .item.on {
border-bottom: 5rpx solid;
// $theme-color
color: red;
color: var(--ui-BG-Main);
// $theme-color
}
+412 -364
View File
@@ -1,196 +1,243 @@
<!-- 分销 - 佣金明细 -->
<template>
<s-layout class="wallet-wrap" title="佣金">
<!-- 钱包卡片 -->
<view class="header-box ss-flex ss-row-center ss-col-center">
<view class="card-box ui-BG-Main ui-Shadow-Main">
<view class="card-head ss-flex ss-col-center">
<view class="card-title ss-m-r-10">当前佣金(元)</view>
<view @tap="state.showMoney = !state.showMoney" class="ss-eye-icon"
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'" />
</view>
<view class="ss-flex ss-row-between ss-col-center ss-m-t-30">
<view class="money-num">{{ state.showMoney ? fen2yuan(state.summary.withdrawPrice || 0) : '*****' }}</view>
<view class="ss-flex">
<view class="ss-m-r-20">
<button class="ss-reset-button withdraw-btn" @tap="sheep.$router.go('/pages/commission/withdraw')">
提现
</button>
</view>
<button class="ss-reset-button balance-btn ss-m-l-20" @tap="state.showModal = true">
转余额
</button>
</view>
</view>
<s-layout class="wallet-wrap" title="佣金">
<!-- 钱包卡片 -->
<view class="header-box ss-flex ss-row-center ss-col-center">
<view class="card-box ui-BG-Main ui-Shadow-Main">
<view class="card-head ss-flex ss-col-center">
<view class="card-title ss-m-r-10">当前佣金(元)</view>
<view
@tap="state.showMoney = !state.showMoney"
class="ss-eye-icon"
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
/>
</view>
<view class="ss-flex ss-row-between ss-col-center ss-m-t-30">
<view class="money-num">{{
state.showMoney ? fen2yuan(state.summary.withdrawPrice || 0) : '*****'
}}</view>
<view class="ss-flex">
<view class="ss-m-r-20">
<button
class="ss-reset-button withdraw-btn"
@tap="sheep.$router.go('/pages/commission/withdraw')"
>
提现
</button>
</view>
<button class="ss-reset-button balance-btn ss-m-l-20" @tap="state.showModal = true">
转余额
</button>
</view>
</view>
<view class="ss-flex">
<view class="loading-money">
<view class="loading-money-title">冻结佣金</view>
<view class="loading-money-num">
<view class="ss-flex">
<view class="loading-money">
<view class="loading-money-title">冻结佣金</view>
<view class="loading-money-num">
{{ state.showMoney ? fen2yuan(state.summary.frozenPrice || 0) : '*****' }}
</view>
</view>
<view class="loading-money ss-m-l-100">
<view class="loading-money-title">可提现佣金</view>
<view class="loading-money-num">
</view>
<view class="loading-money ss-m-l-100">
<view class="loading-money-title">可提现佣金</view>
<view class="loading-money-num">
{{ state.showMoney ? fen2yuan(state.summary.brokeragePrice || 0) : '*****' }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<su-sticky>
<!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.date" type="daterange" @change="onChangeTime" :end="state.today">
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon" />
</button>
</uni-datetime-picker>
<su-sticky>
<!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker
v-model="state.date"
type="daterange"
@change="onChangeTime"
:end="state.today"
>
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon" />
</button>
</uni-datetime-picker>
<view class="total-box">
<view class="total-box">
<!-- TODO 芋艿:这里暂时不考虑做 -->
<!-- <view class="ss-m-b-10">总收入¥{{ state.pagination.income.toFixed(2) }}</view> -->
<!-- <view>总支出¥{{ (-state.pagination.expense).toFixed(2) }}</view> -->
</view>
</view>
<su-tabs :list="tabMaps" @change="onChangeTab" :scrollable="false" :current="state.currentTab" />
</su-sticky>
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据"></s-empty>
<!-- <view class="ss-m-b-10">总收入¥{{ state.pagination.income.toFixed(2) }}</view> -->
<!-- <view>总支出¥{{ (-state.pagination.expense).toFixed(2) }}</view> -->
</view>
</view>
<su-tabs
:list="tabMaps"
@change="onChangeTab"
:scrollable="false"
:current="state.currentTab"
/>
</su-sticky>
<s-empty
v-if="state.pagination.total === 0"
icon="/static/data-empty.png"
text="暂无数据"
></s-empty>
<!-- 转余额弹框 -->
<su-popup :show="state.showModal" type="bottom" round="20" @close="state.showModal = false" showClose>
<view class="ss-p-x-20 ss-p-y-30">
<view class="model-title ss-m-b-30 ss-m-l-20">转余额</view>
<view class="model-subtitle ss-m-b-100 ss-m-l-20">将您的佣金转到余额中继续消费</view>
<view class="input-box ss-flex ss-col-center border-bottom ss-m-b-70 ss-m-x-20">
<view class="unit">¥</view>
<uni-easyinput :inputBorder="false" class="ss-flex-1 ss-p-l-10" v-model="state.price" type="number"
placeholder="请输入金额" />
</view>
<button class="ss-reset-button model-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onConfirm">
确定
</button>
</view>
</su-popup>
<su-popup
:show="state.showModal"
type="bottom"
round="20"
@close="state.showModal = false"
showClose
>
<view class="ss-p-x-20 ss-p-y-30">
<view class="model-title ss-m-b-30 ss-m-l-20">转余额</view>
<view class="model-subtitle ss-m-b-100 ss-m-l-20">将您的佣金转到余额中继续消费</view>
<view class="input-box ss-flex ss-col-center border-bottom ss-m-b-70 ss-m-x-20">
<view class="unit">¥</view>
<uni-easyinput
:inputBorder="false"
class="ss-flex-1 ss-p-l-10"
v-model="state.price"
type="number"
placeholder="请输入金额"
/>
</view>
<button
class="ss-reset-button model-btn ui-BG-Main-Gradient ui-Shadow-Main"
@tap="onConfirm"
>
确定
</button>
</view>
</su-popup>
<!-- 钱包记录 -->
<view v-if="state.pagination.total > 0">
<view class="wallet-list ss-flex border-bottom" v-for="item in state.pagination.list" :key="item.id">
<view class="list-content">
<view class="title-box ss-flex ss-row-between ss-m-b-20">
<text class="title ss-line-1">{{ item.title }}</text>
<view class="money">
<text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text>
<text v-else class="minus">{{ fen2yuan(item.price) }}</text>
</view>
</view>
<text class="time">{{ sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</text>
</view>
</view>
</view>
<!-- 钱包记录 -->
<view v-if="state.pagination.total > 0">
<view
class="wallet-list ss-flex border-bottom"
v-for="item in state.pagination.list"
:key="item.id"
>
<view class="list-content">
<view class="title-box ss-flex ss-row-between ss-m-b-20">
<text class="title ss-line-1">{{ item.title }}</text>
<view class="money">
<text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text>
<text v-else class="minus">{{ fen2yuan(item.price) }}</text>
</view>
</view>
<text class="time">{{
sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss')
}}</text>
</view>
</view>
</view>
<!-- <u-gap></u-gap> -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
<!-- <u-gap></u-gap> -->
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}" />
</s-layout>
}"
/>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import dayjs from 'dayjs';
import _ from 'lodash';
import { computed, reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import dayjs from 'dayjs';
import _ from 'lodash-es';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util';
const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png');
const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png');
const state = reactive({
const state = reactive({
showMoney: false,
summary: {}, // 分销信息
today: '',
date: [],
currentTab: 0,
pagination: {
currentTab: 0,
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
pageSize: 8,
},
loadStatus: '',
loadStatus: '',
price: undefined,
showModal: false,
});
showModal: false,
});
const tabMaps = [{
name: '分佣',
value: '1', // BrokerageRecordBizTypeEnum.ORDER
},
{
name: '提现',
value: '2', // BrokerageRecordBizTypeEnum.WITHDRAW
}
];
const tabMaps = [
{
name: '分佣',
value: '1', // BrokerageRecordBizTypeEnum.ORDER
},
{
name: '提现',
value: '2', // BrokerageRecordBizTypeEnum.WITHDRAW
},
];
const dateFilterText = computed(() => {
if (state.date[0] === state.date[1]) {
return state.date[0];
} else {
return state.date.join('~');
}
});
const dateFilterText = computed(() => {
if (state.date[0] === state.date[1]) {
return state.date[0];
} else {
return state.date.join('~');
}
});
async function getLogList() {
state.loadStatus = 'loading';
let { code, data } = await BrokerageApi.getBrokerageRecordPage({
pageSize: state.pagination.pageSize,
pageNo: state.pagination.pageNo,
async function getLogList() {
state.loadStatus = 'loading';
let { code, data } = await BrokerageApi.getBrokerageRecordPage({
pageSize: state.pagination.pageSize,
pageNo: state.pagination.pageNo,
bizType: tabMaps[state.currentTab].value,
'createTime[0]': state.date[0] + ' 00:00:00',
'createTime[1]': state.date[1] + ' 23:59:59',
});
if (code !== 0) {
});
if (code !== 0) {
return;
}
}
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
}
function onChangeTab(e) {
resetPagination(state.pagination);
state.currentTab = e.index;
getLogList();
}
function onChangeTime(e) {
state.date[0] = e[0];
state.date[1] = e[e.length - 1];
function onChangeTab(e) {
resetPagination(state.pagination);
getLogList();
}
state.currentTab = e.index;
getLogList();
}
// 确认操作(转账到余额)
async function onConfirm() {
if (state.price <= 0) {
sheep.$helper.toast('请输入正确的金额');
return;
}
uni.showModal({
title: '提示',
content: '确认把您的佣金转入到余额钱包中?',
success: async function(res) {
if (!res.confirm) {
function onChangeTime(e) {
state.date[0] = e[0];
state.date[1] = e[e.length - 1];
resetPagination(state.pagination);
getLogList();
}
// 确认操作(转账到余额)
async function onConfirm() {
if (state.price <= 0) {
sheep.$helper.toast('请输入正确的金额');
return;
}
uni.showModal({
title: '提示',
content: '确认把您的佣金转入到余额钱包中?',
success: async function (res) {
if (!res.confirm) {
return;
}
}
const { code } = await BrokerageApi.createBrokerageWithdraw({
type: 1, // 钱包
price: state.price * 100,
@@ -199,272 +246,273 @@
state.showModal = false;
await getAgentInfo();
onChangeTab({
index: 1
index: 1,
});
}
}
});
}
},
});
}
async function getAgentInfo() {
const { code, data } = await BrokerageApi.getBrokerageUserSummary();
async function getAgentInfo() {
const { code, data } = await BrokerageApi.getBrokerageUserSummary();
if (code !== 0) {
return;
}
state.summary = data;
}
}
onLoad(async (options) => {
state.today = dayjs().format('YYYY-MM-DD');
state.date = [state.today, state.today];
if (options.type === 2) { // 切换到“提现” tab 下
onLoad(async (options) => {
state.today = dayjs().format('YYYY-MM-DD');
state.date = [state.today, state.today];
if (options.type === 2) {
// 切换到“提现” tab 下
state.currentTab = 1;
}
getLogList();
getAgentInfo();
});
getLogList();
getAgentInfo();
});
onReachBottom(() => {
if (state.loadStatus === 'noMore') {
return;
}
onReachBottom(() => {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getLogList();
});
});
</script>
<style lang="scss" scoped>
// 钱包
.header-box {
background-color: $white;
padding: 30rpx;
// 钱包
.header-box {
background-color: $white;
padding: 30rpx;
.card-box {
width: 100%;
min-height: 300rpx;
padding: 40rpx;
background-size: 100% 100%;
border-radius: 30rpx;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
.card-box {
width: 100%;
min-height: 300rpx;
padding: 40rpx;
background-size: 100% 100%;
border-radius: 30rpx;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
&::after {
content: '';
display: block;
width: 100%;
height: 100%;
z-index: 2;
position: absolute;
top: 0;
left: 0;
background: v-bind(headerBg) no-repeat;
pointer-events: none;
}
&::after {
content: '';
display: block;
width: 100%;
height: 100%;
z-index: 2;
position: absolute;
top: 0;
left: 0;
background: v-bind(headerBg) no-repeat;
pointer-events: none;
}
.card-head {
color: $white;
font-size: 24rpx;
}
.card-head {
color: $white;
font-size: 24rpx;
}
.ss-eye-icon {
font-size: 40rpx;
color: $white;
}
.ss-eye-icon {
font-size: 40rpx;
color: $white;
}
.money-num {
font-size: 40rpx;
line-height: normal;
font-weight: 500;
color: $white;
font-family: OPPOSANS;
}
.money-num {
font-size: 40rpx;
line-height: normal;
font-weight: 500;
color: $white;
font-family: OPPOSANS;
}
.reduce-num {
font-size: 26rpx;
font-weight: 400;
color: $white;
}
.reduce-num {
font-size: 26rpx;
font-weight: 400;
color: $white;
}
.withdraw-btn {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 24rpx;
font-weight: 500;
background-color: $white;
color: var(--ui-BG-Main);
}
.withdraw-btn {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 24rpx;
font-weight: 500;
background-color: $white;
color: var(--ui-BG-Main);
}
.balance-btn {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 24rpx;
font-weight: 500;
color: $white;
border: 1px solid $white;
}
}
}
.balance-btn {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 24rpx;
font-weight: 500;
color: $white;
border: 1px solid $white;
}
}
}
.loading-money {
margin-top: 56rpx;
.loading-money {
margin-top: 56rpx;
.loading-money-title {
font-size: 24rpx;
font-weight: 400;
color: #ffffff;
line-height: normal;
margin-bottom: 30rpx;
}
.loading-money-title {
font-size: 24rpx;
font-weight: 400;
color: #ffffff;
line-height: normal;
margin-bottom: 30rpx;
}
.loading-money-num {
font-size: 30rpx;
font-family: OPPOSANS;
font-weight: 500;
color: #fefefe;
}
}
.loading-money-num {
font-size: 30rpx;
font-family: OPPOSANS;
font-weight: 500;
color: #fefefe;
}
}
// 筛选
// 筛选
.filter-box {
height: 120rpx;
padding: 0 30rpx;
background-color: $bg-page;
.filter-box {
height: 120rpx;
padding: 0 30rpx;
background-color: $bg-page;
.total-box {
font-size: 24rpx;
font-weight: 500;
color: $dark-9;
}
.total-box {
font-size: 24rpx;
font-weight: 500;
color: $dark-9;
}
.date-btn {
background-color: $white;
line-height: 54rpx;
border-radius: 27rpx;
padding: 0 20rpx;
font-size: 24rpx;
font-weight: 500;
color: $dark-6;
.date-btn {
background-color: $white;
line-height: 54rpx;
border-radius: 27rpx;
padding: 0 20rpx;
font-size: 24rpx;
font-weight: 500;
color: $dark-6;
.ss-seldate-icon {
font-size: 50rpx;
color: $dark-9;
}
}
}
.ss-seldate-icon {
font-size: 50rpx;
color: $dark-9;
}
}
}
// tab
.wallet-tab-card {
.tab-item {
height: 80rpx;
position: relative;
// tab
.wallet-tab-card {
.tab-item {
height: 80rpx;
position: relative;
.tab-title {
font-size: 30rpx;
}
.tab-title {
font-size: 30rpx;
}
.cur-tab-title {
font-weight: $font-weight-bold;
}
.cur-tab-title {
font-weight: $font-weight-bold;
}
.tab-line {
width: 60rpx;
height: 6rpx;
border-radius: 6rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 2rpx;
background-color: var(--ui-BG-Main);
}
}
}
.tab-line {
width: 60rpx;
height: 6rpx;
border-radius: 6rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 2rpx;
background-color: var(--ui-BG-Main);
}
}
}
// 钱包记录
.wallet-list {
padding: 30rpx;
background-color: #ffff;
// 钱包记录
.wallet-list {
padding: 30rpx;
background-color: #ffff;
.head-img {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: $gray-c;
}
.head-img {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: $gray-c;
}
.list-content {
justify-content: space-between;
align-items: flex-start;
flex: 1;
.list-content {
justify-content: space-between;
align-items: flex-start;
flex: 1;
.title {
font-size: 28rpx;
color: $dark-3;
width: 400rpx;
}
.title {
font-size: 28rpx;
color: $dark-3;
width: 400rpx;
}
.time {
color: $gray-c;
font-size: 22rpx;
}
}
.time {
color: $gray-c;
font-size: 22rpx;
}
}
.money {
font-size: 28rpx;
font-weight: bold;
font-family: OPPOSANS;
.money {
font-size: 28rpx;
font-weight: bold;
font-family: OPPOSANS;
.add {
color: var(--ui-BG-Main);
}
.add {
color: var(--ui-BG-Main);
}
.minus {
color: $dark-3;
}
}
}
.minus {
color: $dark-3;
}
}
}
.model-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.model-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.model-subtitle {
font-size: 26rpx;
color: #c2c7cf;
}
.model-subtitle {
font-size: 26rpx;
color: #c2c7cf;
}
.model-btn {
width: 100%;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: normal;
}
.model-btn {
width: 100%;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
line-height: normal;
}
.input-box {
height: 100rpx;
.input-box {
height: 100rpx;
.unit {
font-size: 48rpx;
color: #333;
font-weight: 500;
line-height: normal;
}
.unit {
font-size: 48rpx;
color: #333;
font-weight: 500;
line-height: normal;
}
.uni-easyinput__placeholder-class {
font-size: 30rpx;
height: 40rpx;
line-height: normal;
}
}
</style>
.uni-easyinput__placeholder-class {
font-size: 30rpx;
height: 40rpx;
line-height: normal;
}
}
</style>
+54 -15
View File
@@ -15,7 +15,10 @@
<view class="num-title">可提现金额(元)</view>
<view class="wallet-num">{{ fen2yuan(state.brokerageInfo.brokeragePrice) }}</view>
</view>
<button class="ss-reset-button log-btn" @tap="sheep.$router.go('/pages/commission/wallet', { type: 2 })">
<button
class="ss-reset-button log-btn"
@tap="sheep.$router.go('/pages/commission/wallet', { type: 2 })"
>
提现记录
</button>
</view>
@@ -27,8 +30,9 @@
<view v-if="!state.accountInfo.type" class="empty-text">请选择提现方式</view>
<view v-if="state.accountInfo.type === '1'" class="empty-text">钱包余额</view>
<view v-if="state.accountInfo.type === '2'" class="empty-text">银行卡转账</view>
<view v-if="state.accountInfo.type === '3'" class="empty-text">微信零钱</view>
<view v-if="state.accountInfo.type === '3'" class="empty-text">微信账户</view>
<view v-if="state.accountInfo.type === '4'" class="empty-text">支付宝账户</view>
<view v-if="state.accountInfo.type === '5'" class="empty-text">微信零钱</view>
<text class="cicon-forward" />
</view>
</view>
@@ -45,7 +49,7 @@
/>
</view>
<!-- 提现账号 -->
<view class="card-title" v-show="['2', '3', '4'].includes(state.accountInfo.type)">
<view class="card-title" v-show="['2', '3', '4', '5'].includes(state.accountInfo.type)">
提现账号
</view>
<view
@@ -74,6 +78,7 @@
limit="1"
mode="grid"
:imageStyles="{ width: '168rpx', height: '168rpx' }"
@success="(payload) => state.accountInfo.accountQrCodeUrl = payload.tempFilePaths[0]"
/>
</view>
</view>
@@ -98,12 +103,23 @@
v-show="state.accountInfo.type === '2'"
>
<view class="unit" />
<uni-easyinput
:inputBorder="false"
class="ss-flex-1 ss-p-l-10"
v-model="state.accountInfo.bankName"
placeholder="请输入提现银行"
/>
<!--银行改为下拉选择-->
<picker
@change="bankChange"
:value="state.bankListSelectedIndex"
:range="state.bankList"
range-key="label"
style="width: 100%"
>
<uni-easyinput
:inputBorder="false"
:value="state.accountInfo.bankName"
placeholder="请选择银行"
suffixIcon="right"
disabled
:styles="{ disableColor: '#fff', borderColor: '#fff', color: '#333!important' }"
/>
</picker>
</view>
<!-- 开户地址 -->
<view class="card-title" v-show="state.accountInfo.type === '2'">开户地址</view>
@@ -146,12 +162,14 @@
</template>
<script setup>
import { computed, reactive, onBeforeMount } from 'vue';
import { computed, onBeforeMount, reactive } from 'vue';
import sheep from '@/sheep';
import accountTypeSelect from './components/account-type-select.vue';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import TradeConfigApi from '@/sheep/api/trade/config';
import BrokerageApi from '@/sheep/api/trade/brokerage';
import DictApi from '@/sheep/api/system/dict';
import SLayout from '@/sheep/components/s-layout/s-layout.vue';
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
@@ -176,6 +194,8 @@
frozenDays: 0, // 冻结天数
minPrice: 0, // 最低提现金额
withdrawTypes: [], // 提现方式
bankList: [], // 银行字典数据
bankListSelectedIndex: '', // 选中银行 bankList 的 index
});
// 打开提现方式的弹窗
@@ -186,7 +206,7 @@
// 提交提现
const onConfirm = async () => {
// 参数校验
debugger;
//debugger;
if (
!state.accountInfo.price ||
state.accountInfo.price > state.brokerageInfo.price ||
@@ -215,12 +235,12 @@
confirmText: '查看记录',
success: (res) => {
if (res.confirm) {
sheep.$router.go('/pages/commission/wallet', { type: 2 })
sheep.$router.go('/pages/commission/wallet', { type: 2 });
return;
}
getBrokerageUser();
state.accountInfo = {};
}
},
});
};
@@ -245,10 +265,29 @@
}
}
// 获取提现银行配置字典
async function getDictDataListByType() {
let { code, data } = await DictApi.getDictDataListByType('brokerage_bank_name');
if (code !== 0) {
return;
}
if (data && data.length > 0) {
state.bankList = data;
}
}
// 银行选择
function bankChange(e) {
const value = e.detail.value;
state.bankListSelectedIndex = value;
state.accountInfo.bankName = state.bankList[value].label;
}
onBeforeMount(() => {
getWithdrawRules();
getBrokerageUser()
})
getBrokerageUser();
getDictDataListByType(); //获取银行字典数据
});
</script>
<style lang="scss" scoped>
+24 -12
View File
@@ -16,13 +16,16 @@
<view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.coupon.name }}</view>
<view class="subtitle ss-m-b-50">
满 {{ fen2yuan(state.coupon.usePrice) }} 元,
{{ state.coupon.discountType === 1
? '减 ' + fen2yuan(state.coupon.discountPrice) + ' 元'
: '打 ' + state.coupon.discountPercent / 10.0 + ' 折' }}
{{
state.coupon.discountType === 1
? '减 ' + fen2yuan(state.coupon.discountPrice) + ' 元'
: '打 ' + state.coupon.discountPercent / 10.0 + ' 折'
}}
</view>
<button
class="ss-reset-button ss-m-b-30"
:class="state.coupon.canTake || state.coupon.status === 1
:class="
state.coupon.canTake || state.coupon.status === 1
? 'use-btn' // 优惠劵模版(可领取)、优惠劵(可使用)
: 'disable-btn'
"
@@ -31,7 +34,13 @@
>
<text v-if="state.id > 0">{{ state.coupon.canTake ? '立即领取' : '已领取' }}</text>
<text v-else>
{{ state.coupon.status === 1 ? '立即使用' : state.coupon.status === 2 ? '已使用' : '已过期' }}
{{
state.coupon.status === 1
? '可使用'
: state.coupon.status === 2
? '已使用'
: '已过期'
}}
</text>
</button>
<view class="time ss-m-y-30" v-if="state.coupon.validityType === 2">
@@ -48,7 +57,6 @@
<view>优惠券类型</view>
<view>{{ state.coupon.discountType === 1 ? '满减券' : '折扣券' }}</view>
</view>
<!-- TODO 芋艿:可优化,增加优惠劵的描述 -->
<uni-collapse>
<uni-collapse-item title="优惠券说明" v-if="state.coupon.description">
<view class="content ss-p-b-20">
@@ -140,7 +148,7 @@
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import CouponApi from '@/sheep/api/promotion/coupon';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import SpuApi from '@/sheep/api/product/spu';
@@ -156,7 +164,7 @@
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
pageSize: 8,
},
categoryId: 0, // 选中的商品分类编号
tabMaps: [], // 指定分类时,每个分类构成一个 tab
@@ -176,7 +184,7 @@
const { code, data } = await SpuApi.getSpuPage({
categoryId: state.categoryId,
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize
pageSize: state.pagination.pageSize,
});
if (code !== 0) {
return;
@@ -197,7 +205,9 @@
// 获得分类列表
async function getCategoryList() {
const { data, code } = await CategoryApi.getCategoryListByIds(state.coupon.productScopeValues.join(','));
const { data, code } = await CategoryApi.getCategoryListByIds(
state.coupon.productScopeValues.join(','),
);
if (code !== 0) {
return;
}
@@ -225,8 +235,10 @@
// 加载优惠劵信息
async function getCouponContent() {
const { code, data } = state.id > 0 ? await CouponApi.getCouponTemplate(state.id)
: await CouponApi.getCoupon(state.couponId);
const { code, data } =
state.id > 0
? await CouponApi.getCouponTemplate(state.id)
: await CouponApi.getCoupon(state.couponId);
if (code !== 0) {
return;
}
+17 -13
View File
@@ -1,6 +1,6 @@
<!-- 优惠券中心 -->
<template>
<s-layout title="优惠券" :bgStyle="{ color: '#f2f2f2' }">
<s-layout :bgStyle="{ color: '#f2f2f2' }" title="优惠券">
<su-sticky bgColor="#fff">
<su-tabs
:list="tabMaps"
@@ -45,7 +45,7 @@
<template #default>
<button
class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
:class=" item.status !== 1 ? 'disabled-btn': ''"
:class="item.status !== 1 ? 'disabled-btn' : ''"
:disabled="item.status !== 1"
@click.stop="sheep.$router.go('/pages/coupon/detail', { couponId: item.id })"
>
@@ -56,9 +56,14 @@
</view>
</template>
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}" @tap="loadMore" />
}"
@tap="loadMore"
/>
</s-layout>
</template>
@@ -66,7 +71,7 @@
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util';
import CouponApi from '@/sheep/api/promotion/coupon';
@@ -78,7 +83,7 @@
list: [],
total: 0,
pageNo: 1,
pageSize: 5
pageSize: 5,
},
loadStatus: '',
});
@@ -102,13 +107,12 @@
},
];
// TODO yunai:
function onTabsChange(e) {
state.currentTab = e.index;
state.type = e.value;
resetPagination(state.pagination)
resetPagination(state.pagination);
if (state.currentTab === 0) {
getData();
getData();
} else {
getCoupon();
}
@@ -135,7 +139,7 @@
const { data, code } = await CouponApi.getCouponPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
status: state.type
status: state.type,
});
if (code !== 0) {
return;
@@ -177,13 +181,13 @@
// 领劵中心
if (Option.type === 'all' || !Option.type) {
getData();
// 我的优惠劵
// 我的优惠劵
} else {
Option.type === 'geted'
? (state.currentTab = 1)
: Option.type === 'used'
? (state.currentTab = 2)
: (state.currentTab = 3);
? (state.currentTab = 2)
: (state.currentTab = 3);
state.type = state.currentTab;
getCoupon();
}
+142 -97
View File
@@ -1,89 +1,130 @@
<!-- 评价 -->
<template>
<s-layout title="评价">
<view>
<view v-for="(item, index) in state.orderInfo.items" :key="item.id">
<view>
<view class="commont-from-wrap">
<!-- 评价商品 -->
<s-goods-item
<s-layout title="评价">
<view>
<view v-for="(item, index) in state.orderInfo.items" :key="item.id">
<view>
<view class="commont-from-wrap">
<!-- 评价商品 -->
<s-goods-item
:img="item.picUrl"
:title="item.spuName"
:skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.payPrice"
:price="item.payPrice"
:num="item.count"
/>
</view>
</view>
<view class="form-item">
<!-- 评分 -->
<view class="star-box ss-flex ss-col-center">
<view class="star-title ss-m-r-40">商品质量</view>
<uni-rate v-model="state.commentList[index].descriptionScores" />
</view>
<view class="star-box ss-flex ss-col-center">
<view class="star-title ss-m-r-40">服务态度</view>
<uni-rate v-model="state.commentList[index].benefitScores" />
</view>
<!-- 评价 -->
<view class="area-box">
<uni-easyinput :inputBorder="false" type="textarea" maxlength="120" autoHeight
v-model="state.commentList[index].content"
placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~" />
<!-- TODO 芋艿:文件上传 -->
<view class="img-box">
<s-uploader v-model:url="state.commentList[index].images" fileMediatype="image"
limit="9" mode="grid" :imageStyles="{ width: '168rpx', height: '168rpx' }" />
</view>
</view>
</view>
</view>
</view>
</view>
<!-- TODO 芋艿:是否匿名 -->
<su-fixed bottom placeholder>
<view class="foot_box ss-flex ss-row-center ss-col-center">
<button class="ss-reset-button post-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onSubmit">
发布
</button>
</view>
</su-fixed>
</s-layout>
<view class="form-item">
<!-- 评分 -->
<view class="star-box ss-flex ss-col-center">
<view class="star-title ss-m-r-40">商品质量</view>
<uni-rate v-model="state.commentList[index].descriptionScores" />
</view>
<view class="star-box ss-flex ss-col-center">
<view class="star-title ss-m-r-40">服务态度</view>
<uni-rate v-model="state.commentList[index].benefitScores" />
</view>
<!-- 评价 -->
<view class="area-box">
<uni-easyinput
:inputBorder="false"
type="textarea"
maxlength="120"
autoHeight
v-model="state.commentList[index].content"
placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~"
/>
<view class="img-box">
<s-uploader
v-model:url="state.commentList[index].images"
fileMediatype="image"
limit="9"
mode="grid"
:imageStyles="{ width: '168rpx', height: '168rpx' }"
@success="(payload) => uploadSuccess(payload, index)"
/>
</view>
</view>
<view class="checkbox-container">
<checkbox-group @change="(event) => toggleAnonymous(index, event)">
<label>
<checkbox value="anonymousChecked" />
匿名评论
</label>
</checkbox-group>
</view>
</view>
</view>
</view>
</view>
<su-fixed bottom placeholder>
<view class="foot_box ss-flex ss-row-center ss-col-center">
<button class="ss-reset-button post-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onSubmit">
发布
</button>
</view>
</su-fixed>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import OrderApi from '@/sheep/api/trade/order';
const state = reactive({
orderInfo: {},
commentList: [],
id: null
});
const state = reactive({
orderInfo: {},
commentList: [],
id: null,
});
async function onSubmit() {
/**
* 切换是否匿名
*
* @param commentIndex 当前评论下标
* @param event 复选框事件
*/
function toggleAnonymous(commentIndex, event) {
state.commentList[commentIndex].anonymous = event.detail.value[0] === 'anonymousChecked';
}
/**
* 发布评论
*
* @returns {Promise<void>}
*/
async function onSubmit() {
// 顺序提交评论
for (const comment of state.commentList) {
await OrderApi.createOrderItemComment(comment);
}
// 都评论好,返回
sheep.$router.back();
}
}
onLoad(async (options) => {
/**
* 图片添加到表单
*
* @param payload 上传成功后的回调数据
* @param commentIndex 当前评论的下标
*/
function uploadSuccess(payload, commentIndex) {
state.commentList[commentIndex].picUrls = payload.tempFilePaths;
}
onLoad(async (options) => {
if (!options.id) {
sheep.$helper.toast(`缺少订单信息,请检查`);
return
return;
}
state.id = options.id;
state.id = options.id;
const { code, data } = await OrderApi.getOrder(state.id);
const { code, data } = await OrderApi.getOrderDetail(state.id);
if (code !== 0) {
sheep.$helper.toast('无待评价订单');
return
return;
}
// 处理评论
data.items.forEach((item) => {
@@ -93,53 +134,57 @@
descriptionScores: 5,
benefitScores: 5,
content: '',
picUrls: []
picUrls: [],
});
});
state.orderInfo = data;
});
});
</script>
<style lang="scss" scoped>
// 评价商品
.goods-card {
margin: 10rpx 0;
padding: 20rpx;
background: #fff;
}
// 评价商品
.goods-card {
margin: 10rpx 0;
padding: 20rpx;
background: #fff;
}
// 评论,选择图片
.form-item {
background: #fff;
// 评论,选择图片
.form-item {
background: #fff;
.star-box {
height: 100rpx;
padding: 0 25rpx;
}
.star-box {
height: 100rpx;
padding: 0 25rpx;
}
.star-title {
font-weight: 600;
}
}
.star-title {
font-weight: 600;
}
}
.area-box {
width: 690rpx;
min-height: 306rpx;
background: rgba(249, 250, 251, 1);
border-radius: 20rpx;
padding: 28rpx;
margin: auto;
.area-box {
width: 690rpx;
min-height: 306rpx;
background: rgba(249, 250, 251, 1);
border-radius: 20rpx;
padding: 28rpx;
margin: auto;
.img-box {
margin-top: 20rpx;
}
}
.img-box {
margin-top: 20rpx;
}
}
.post-btn {
width: 690rpx;
line-height: 80rpx;
border-radius: 40rpx;
color: rgba(#fff, 0.9);
margin-bottom: 20rpx;
}
</style>
.checkbox-container {
padding: 10rpx;
}
.post-btn {
width: 690rpx;
line-height: 80rpx;
border-radius: 40rpx;
color: rgba(#fff, 0.9);
margin-bottom: 20rpx;
}
</style>
+4 -3
View File
@@ -1,6 +1,6 @@
<!-- 商品评论的分页 -->
<template>
<s-layout title="全部评价">
<s-layout title="全部评论">
<su-tabs
:list="state.type"
:scrollable="false"
@@ -16,6 +16,7 @@
<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
<!-- 下拉 -->
<uni-load-more
icon-type="auto"
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
@@ -30,7 +31,7 @@
import CommentApi from '@/sheep/api/product/comment';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import commentItem from '../components/detail/comment-item.vue';
const state = reactive({
@@ -47,7 +48,7 @@
list: [],
total: 0,
pageNo: 1,
pageSize: 1,
pageSize: 8,
},
});
@@ -2,7 +2,6 @@
<su-fixed bottom placeholder :val="44">
<view>
<view v-for="activity in props.activityList" :key="activity.id">
<!-- TODO 芋艿:拼团 -->
<view
class="activity-box ss-p-x-38 ss-flex ss-row-between ss-col-center"
:class="activity.type === 1 ? 'seckill-box' : 'groupon-box'"
@@ -14,7 +13,6 @@
:src="sheep.$url.static('/static/img/shop/goods/seckill-icon.png')"
class="activity-icon"
/>
<!-- TODO 芋艿:拼团 -->
<image
v-else-if="activity.type === 3"
:src="sheep.$url.static('/static/img/shop/goods/groupon-icon.png')"
@@ -33,7 +31,6 @@
<script setup>
import sheep from '@/sheep';
// TODO 芋艿:这里要迁移下;
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
+55 -37
View File
@@ -7,12 +7,16 @@
<detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 -->
<s-empty
v-else-if="state.goodsInfo === null || state.activity.status !== 0 || state.activity.endTime < new Date().getTime()"
text="活动不存在或已结束"
icon="/static/soldout-empty.png"
showAction
actionText="返回上一页"
@clickAction="sheep.$router.back()"
v-else-if="
state.goodsInfo === null ||
state.activity.status !== 0 ||
state.activity.endTime < new Date().getTime()
"
text="活动不存在或已结束"
icon="/static/soldout-empty.png"
showAction
actionText="返回上一页"
@clickAction="sheep.$router.back()"
/>
<block v-else>
<view class="detail-swiper-selector">
@@ -47,13 +51,10 @@
</view>
</view>
<view class="ss-flex ss-row-between">
<view
class="origin-price ss-flex ss-col-center"
v-if="state.goodsInfo.price"
>
<view class="origin-price ss-flex ss-col-center" v-if="state.goodsInfo.price">
单买价:
<view class="origin-price-text">
{{ fen2yuan(state.goodsInfo.price) }}
{{ fen2yuan(state.goodsInfo.marketPrice) }}
</view>
</view>
</view>
@@ -79,7 +80,7 @@
<!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col">
<!-- 规格 -->
<detail-cell-sku :sku="state.selectedSkuPrice" @tap="state.showSelectSku = true" />
<detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
</view>
<!-- 参团列表 -->
@@ -103,7 +104,6 @@
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
<!-- 商品tabbar -->
<!-- TODO: 已售罄、预热 判断 设计-->
<detail-tabbar v-model="state.goodsInfo">
<view class="buy-box ss-flex ss-col-center ss-p-r-20">
<button
@@ -123,7 +123,14 @@
"
:disabled="state.goodsInfo.stock === 0 || state.activity.status !== 0"
>
<view class="btn-price">{{ fen2yuan(state.activity.price || state.goodsInfo.price) }}</view>
<view class="btn-price">{{
fen2yuan(
state.selectedSku.price * state.selectedSku.count ||
state.activity.price * state.selectedSku.count ||
state.goodsInfo.price * state.selectedSku.count ||
state.goodsInfo.price,
)
}}</view>
<view v-if="state.activity.startTime > new Date().getTime()">未开始</view>
<view v-else-if="state.activity.endTime <= new Date().getTime()">已结束</view>
<view v-else>
@@ -141,7 +148,7 @@
import { reactive, computed } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import { isEmpty } from 'lodash-es';
import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue';
@@ -149,29 +156,27 @@
import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue';
import grouponCardList from './components/groupon/groupon-card-list.vue';
import {useDurationTime, formatGoodsSwiper, fen2yuan} from '@/sheep/hooks/useGoods';
import CombinationApi from "@/sheep/api/promotion/combination";
import SpuApi from "@/sheep/api/product/spu";
import { useDurationTime, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
import CombinationApi from '@/sheep/api/promotion/combination';
import SpuApi from '@/sheep/api/product/spu';
const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-bg.png');
const btnBg = sheep.$url.css('/static/img/shop/goods/groupon-btn.png');
const disabledBtnBg = sheep.$url.css(
'/static/img/shop/goods/activity-btn-disabled.png',
);
const disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
onPageScroll(() => {});
const state = reactive({
skeletonLoading: true, // 骨架屏
goodsId: 0, // 商品ID
goodsInfo: {}, // 商品信息
goodsSwiper: [], // 商品轮播图
showSelectSku: false, // 显示规格弹框
selectedSkuPrice: {}, // 选中的规格价格
activity: {}, // 团购活动
grouponId: 0, // 团购ID
grouponNum: 0, // 团购人数
grouponAction: 'create', // 团购操作
skeletonLoading: true, // 骨架屏
goodsId: 0, // 商品ID
goodsInfo: {}, // 商品信息
goodsSwiper: [], // 商品轮播图
showSelectSku: false, // 显示规格弹框
selectedSku: {}, // 选中的规格属性
activity: {}, // 团购活动
grouponId: 0, // 团购ID
grouponNum: 0, // 团购人数
grouponAction: 'create', // 团购操作
combinationHeadId: null, // 拼团团长编号
});
@@ -182,7 +187,7 @@
// 规格变更
function onSkuChange(e) {
state.selectedSkuPrice = e;
state.selectedSku = e;
}
function onSkuClose() {
@@ -198,6 +203,7 @@
/**
* 去参团
*
* @param record 团长的团购记录
*/
function onJoinGroupon(record) {
@@ -226,7 +232,6 @@
}
// 分享信息
// TODO @芋艿:分享的接入
const shareInfo = computed(() => {
if (isEmpty(state.activity)) return {};
return sheep.$platform.share.getShareInfo(
@@ -261,9 +266,23 @@
// 加载商品信息
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId);
state.goodsId = spu.id;
activity.products.forEach(product => {
spu.price = Math.min(spu.price, product.combinationPrice); // 设置 SPU 的最低价格
// 默认显示最低价
spu.price = activity.products.reduce((min, product) => {
return Math.min(min, product.combinationPrice || Infinity);
}, Infinity);
// 价格、库存使用活动的
spu.skus.forEach((sku) => {
const product = activity.products.find((product) => product.skuId === sku.id);
if (product) {
sku.price = product.combinationPrice;
} else {
// 找不到可能是没配置,则不能发起秒杀
sku.stock = 0;
}
});
// 关闭骨架屏
state.skeletonLoading = false;
if (code === 0) {
@@ -475,8 +494,7 @@
}
.groupon-box {
background: v-bind(grouponBg)
no-repeat;
background: v-bind(grouponBg) no-repeat;
background-size: 100% 100%;
}
+580 -320
View File
@@ -1,177 +1,303 @@
<template>
<view>
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
<!-- 标题栏 -->
<detailNavbar />
<view>
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
<!-- 标题栏 -->
<detailNavbar />
<!-- 骨架屏 -->
<detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 -->
<s-empty v-else-if="state.goodsInfo === null" text="商品不存在或已下架" icon="/static/soldout-empty.png" showAction
actionText="再逛逛" actionUrl="/pages/goods/list" />
<block v-else>
<view class="detail-swiper-selector">
<!-- 商品轮播图 -->
<su-swiper class="ss-m-b-14" isPreview :list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
otStyle="tag" imageMode="widthFix" dotCur="bg-mask-40" :seizeHeight="750" />
<!-- 骨架屏 -->
<detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 -->
<s-empty
v-else-if="state.goodsInfo === null"
text="商品不存在或已下架"
icon="/static/soldout-empty.png"
showAction
actionText="再逛逛"
actionUrl="/pages/goods/list"
/>
<block v-else>
<view class="detail-swiper-selector">
<!-- 商品轮播图 -->
<su-swiper
class="ss-m-b-14"
isPreview
:list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
otStyle="tag"
imageMode="widthFix"
dotCur="bg-mask-40"
:seizeHeight="750"
/>
<!-- 限时折扣/会员价的优惠信息 -->
<view
class="discount"
v-if="
state.settlementSku && state.settlementSku.id && state.settlementSku.promotionPrice
"
>
<image class="disImg" :src="sheep.$url.static('/static/img/shop/goods/dis.png')" />
<view class="discountCont">
<view class="disContT">
<view class="disContT1">
<view class="disContT1P">
¥{{ fen2yuan(state.settlementSku.promotionPrice) }}
</view>
<view class="disContT1End">
直降¥
{{ fen2yuan(state.settlementSku.price - state.settlementSku.promotionPrice) }}
</view>
</view>
<view class="disContT2" v-if="state.settlementSku.promotionType === 4">
限时折扣
</view>
<view class="disContT2" v-else-if="state.settlementSku.promotionType === 6">
会员折扣
</view>
</view>
<view class="disContB">
<view class="disContB1">
价格:¥{{ fen2yuan(state.settlementSku.price) }} 丨 剩余:
{{ state.settlementSku.stock }}
</view>
<view class="disContB2" v-if="state.settlementSku.promotionEndTime > 0">
距结束仅剩
<countDown
:tipText="' '"
:bgColor="bgColor"
:dayText="':'"
:hourText="':'"
:minuteText="':'"
:secondText="' '"
:datatime="state.settlementSku.promotionEndTime / 1000"
:isDay="false"
/>
</view>
</view>
</view>
</view>
<!-- 价格+标题 -->
<view class="title-card detail-card ss-p-y-30 ss-p-x-20">
<!-- 没有限时折扣/会员价的优惠信息时,展示的价格信息 -->
<view
class="ss-flex ss-row-between ss-col-center ss-m-b-26"
v-if="!state.settlementSku.promotionPrice"
>
<view class="price-box ss-flex ss-col-bottom">
<view class="price-text ss-m-r-16">
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
</view>
<view class="origin-price-text" v-if="state.goodsInfo.marketPrice > state.goodsInfo.price">
{{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
</view>
</view>
<view class="sales-text">
{{ formatSales('exact', state.goodsInfo.salesCount) }}
</view>
</view>
<view class="discounts-box ss-flex ss-row-between ss-m-b-28">
<!-- 查看优惠劵的描述 -->
<view
class="tag ss-m-r-10"
v-for="coupon in state.couponInfo.slice(0, 1)"
:key="coupon.id"
@tap="onOpenActivity"
>
[劵]满{{ fen2yuanSimple(coupon.usePrice) }}元{{
coupon.discountType === 1
? '减' + fen2yuanSimple(coupon.discountPrice) + '元'
: '打' + formatDiscountPercent(coupon.discountPercent) + '折'
}}
</view>
<!-- 查看满减送的描述 -->
<div class="tag-content">
<view class="tag-box ss-flex">
<!-- 最多打印 3 条,所以需要扣除优惠劵已打印的 -->
<view
v-for="item in getRewardActivityRuleItemDescriptions(
state.rewardActivity,
).slice(0, 3 - state.couponInfo.slice(0, 1).length)"
:key="item"
class="tag ss-m-r-10"
@tap="onOpenActivity"
>
<text>{{ item }}</text>
</view>
</view>
</div>
<!-- 领取优惠劵的按钮 -->
<view
class="get-coupon-box ss-flex ss-col-center ss-m-l-20"
@tap="onOpenActivity"
v-if="state.couponInfo.length"
>
<view class="discounts-title ss-m-r-8">领券</view>
<text class="cicon-forward"></text>
</view>
</view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name }}</view>
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
</view>
<!-- 价格+标题 -->
<view class="title-card detail-card ss-p-y-40 ss-p-x-20">
<view class="ss-flex ss-row-between ss-col-center ss-m-b-26">
<view class="price-box ss-flex ss-col-bottom">
<view class="price-text ss-m-r-16">
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
</view>
<view class="origin-price-text" v-if="state.goodsInfo.marketPrice > 0">
{{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
</view>
</view>
<view class="sales-text">
{{ formatSales('exact', state.goodsInfo.salesCount) }}
</view>
</view>
<view class="discounts-box ss-flex ss-row-between ss-m-b-28">
<!-- 满减送/限时折扣活动的提示 -->
<div class="tag-content">
<view class="tag-box ss-flex">
<view class="tag ss-m-r-10" v-for="promos in state.activityInfo"
:key="promos.id" @tap="onActivity">
{{ promos.name }}
</view>
</view>
</div>
<!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku
v-model="state.selectedSku.goods_sku_text"
:sku="state.selectedSku"
@tap="state.showSelectSku = true"
/>
</view>
<!-- 优惠劵 -->
<view class="get-coupon-box ss-flex ss-col-center ss-m-l-20" @tap="state.showModel = true"
v-if="state.couponInfo.length">
<view class="discounts-title ss-m-r-8">领券</view>
<text class="cicon-forward"></text>
</view>
</view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name }}</view>
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
</view>
<!-- 规格与数量弹框 -->
<s-select-sku
:goodsInfo="state.goodsInfo"
:show="state.showSelectSku"
@addCart="onAddCart"
@buy="onBuy"
@change="onSkuChange"
@close="state.showSelectSku = false"
/>
</view>
<!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku v-model="state.selectedSku.goods_sku_text" :sku="state.selectedSku"
@tap="state.showSelectSku = true" />
</view>
<!-- 评价 -->
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
<!-- 详情 -->
<detail-content-card
class="detail-content-selector"
:content="state.goodsInfo.description"
/>
<!-- 规格与数量弹框 -->
<s-select-sku :goodsInfo="state.goodsInfo" :show="state.showSelectSku" @addCart="onAddCart"
@buy="onBuy" @change="onSkuChange" @close="state.showSelectSku = false" />
</view>
<!-- 活动跳转:拼团/秒杀/砍价活动 -->
<detail-activity-tip
v-if="state.activityList.length > 0"
:activity-list="state.activityList"
/>
<!-- 评价 -->
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
<!-- 详情 -->
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
<!-- 详情 tabbar -->
<detail-tabbar v-model="state.goodsInfo">
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
<button
class="ss-reset-button add-btn ui-Shadow-Main"
@tap="state.showSelectSku = true"
>
加入购物车
</button>
<button
class="ss-reset-button buy-btn ui-Shadow-Main"
@tap="state.showSelectSku = true"
>
立即购买
</button>
</view>
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-else>
<button class="ss-reset-button disabled-btn" disabled> 已售罄 </button>
</view>
</detail-tabbar>
<!-- 活动跳转:拼团/秒杀/砍价活动 -->
<detail-activity-tip v-if="state.activityList.length > 0" :activity-list="state.activityList" />
<!-- 详情 tabbar -->
<detail-tabbar v-model="state.goodsInfo">
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
<button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
加入购物车
</button>
<button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
立即购买
</button>
</view>
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-else>
<button class="ss-reset-button disabled-btn" disabled> 已售罄 </button>
</view>
</detail-tabbar>
<!-- 优惠劵弹窗 -->
<s-coupon-get v-model="state.couponInfo" :show="state.showModel" @close="state.showModel = false"
@get="onGet" />
<!-- 满减送/限时折扣活动弹窗 -->
<s-activity-pop v-model="state.activityInfo" :show="state.showActivityModel"
@close="state.showActivityModel = false" />
</block>
</s-layout>
</view>
<!-- 满减送/限时折扣活动弹窗 -->
<s-activity-pop
v-model="state"
:show="state.showActivityModel"
@close="state.showActivityModel = false"
@get="onTakeCoupon"
/>
</block>
</s-layout>
</view>
</template>
<script setup>
import { reactive, computed } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon';
import ActivityApi from '@/sheep/api/promotion/activity';
import { reactive, computed, ref, toRaw } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon';
import ActivityApi from '@/sheep/api/promotion/activity';
import FavoriteApi from '@/sheep/api/product/favorite';
import { formatSales, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue';
import detailSkeleton from './components/detail/detail-skeleton.vue';
import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue';
import detailActivityTip from './components/detail/detail-activity-tip.vue';
import { isEmpty } from 'lodash';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
import {
formatSales,
formatGoodsSwiper,
fen2yuan,
fen2yuanSimple,
formatDiscountPercent,
getRewardActivityRuleItemDescriptions,
} from '@/sheep/hooks/useGoods';
import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue';
import detailSkeleton from './components/detail/detail-skeleton.vue';
import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue';
import detailActivityTip from './components/detail/detail-activity-tip.vue';
import { isEmpty } from 'lodash-es';
import SpuApi from '@/sheep/api/product/spu';
onPageScroll(() => {});
onPageScroll(() => {});
import countDown from '@/sheep/components/countDown/index.vue';
import OrderApi from '@/sheep/api/trade/order';
import activity from '@/sheep/api/promotion/activity';
const bgColor = {
bgColor: '#E93323',
Color: '#fff',
width: '44rpx',
timeTxtwidth: '16rpx',
isDay: true,
};
const isLogin = computed(() => sheep.$store('user').isLogin);
const state = reactive({
goodsId: 0,
skeletonLoading: true, // SPU 加载中
goodsInfo: {}, // SPU 信息
showSelectSku: false, // 是否展示 SKU 选择弹窗
selectedSku: {}, // 选中的 SKU
showModel: false, // 是否展示 Coupon 优惠劵的弹窗
couponInfo: [], // 可领取的 Coupon 优惠劵的列表
showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
activityInfo: [], // 【满减送/限时折扣】可参与的 Activity 营销活动的列表
activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
});
goodsId: 0,
skeletonLoading: true, // SPU 加载中
goodsInfo: {}, // SPU 信息
showSelectSku: false, // 是否展示 SKU 选择弹窗
selectedSku: {}, // 选中的 SKU
settlementSku: {}, // 结算的 SKU:由于 selectedSku 不进行默认选中,所以初始使用结算价格最低的 SKU 作为基础展示
showModel: false, // 是否展示 Coupon 优惠劵的弹窗
couponInfo: [], // 可领取的 Coupon 优惠劵的列表
showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
rewardActivity: {}, // 【满减送】活动
activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
});
// 规格变更
function onSkuChange(e) {
state.selectedSku = e;
}
// 规格变更
function onSkuChange(e) {
state.selectedSku = e;
state.settlementSku = e;
}
// 添加购物车
function onAddCart(e) {
// 添加购物车
function onAddCart(e) {
if (!e.id) {
sheep.$helper.toast('请选择商品规格');
return;
}
sheep.$store('cart').add(e);
}
sheep.$store('cart').add(e);
}
// 立即购买
function onBuy(e) {
if (!state.selectedSku.id) {
// 立即购买
function onBuy(e) {
if (!e.id) {
sheep.$helper.toast('请选择商品规格');
return;
}
sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({
items: [{
skuId: e.id,
count: e.goods_num
}],
// TODO 芋艿:后续清理掉这 2 参数
deliveryType: 1,
pointStatus: false,
items: [
{
skuId: e.id,
count: e.goods_num,
categoryId: state.goodsInfo.categoryId,
},
],
}),
});
}
}
// 营销活动
function onActivity() {
state.showActivityModel = true;
}
// 打开营销弹窗
function onOpenActivity() {
state.showActivityModel = true;
}
// 立即领取
async function onGet(id) {
// 立即领取优惠劵
async function onTakeCoupon(id) {
const { code } = await CouponApi.takeCoupon(id);
if (code !== 0) {
return;
@@ -182,27 +308,29 @@
setTimeout(() => {
getCoupon();
}, 1000);
}
}
// TODO 芋艿:待测试
const shareInfo = computed(() => {
if (isEmpty(state.goodsInfo)) return {};
return sheep.$platform.share.getShareInfo({
title: state.goodsInfo.name,
image: sheep.$url.cdn(state.goodsInfo.picUrl),
desc: state.goodsInfo.introduction,
params: {
page: '2',
query: state.goodsInfo.id,
},
}, {
type: 'goods', // 商品海报
title: state.goodsInfo.name, // 商品名称
image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
price: fen2yuan(state.goodsInfo.price), // 商品价格
original_price: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
});
});
const shareInfo = computed(() => {
if (isEmpty(state.goodsInfo)) return {};
return sheep.$platform.share.getShareInfo(
{
title: state.goodsInfo.name,
image: sheep.$url.cdn(state.goodsInfo.picUrl),
desc: state.goodsInfo.introduction,
params: {
page: '2',
query: state.goodsInfo.id,
},
},
{
type: 'goods', // 商品海报
title: state.goodsInfo.name, // 商品名称
image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
price: fen2yuan(state.goodsInfo.price), // 商品价格
original_price: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
},
);
});
async function getCoupon() {
const { code, data } = await CouponApi.getCouponTemplateList(state.goodsId, 2, 10);
@@ -211,24 +339,65 @@
}
}
onLoad((options) => {
// 非法参数
if (!options.id) {
state.goodsInfo = null;
return;
}
state.goodsId = options.id;
// 1. 加载商品信息
SpuApi.getSpuDetail(state.goodsId).then((res) => {
// 未找到商品
if (res.code !== 0 || !res.data) {
state.goodsInfo = null;
return;
}
// 加载到商品
state.skeletonLoading = false;
state.goodsInfo = res.data;
async function getSettlementByIds(ids) {
let { data, code } = await OrderApi.getSettlementProduct(ids);
if (code !== 0 || data.length !== 1) {
return;
}
data = data[0];
// 补充 SKU 的价格信息
state.goodsInfo.skus.forEach((sku) => {
data.skus.forEach((item) => {
if (sku.id === item.id) {
sku.promotionType = item.promotionType;
sku.promotionPrice = item.promotionPrice;
sku.promotionId = item.promotionId;
sku.promotionEndTime = item.promotionEndTime;
}
});
});
// 选择有 promotionPrice 且最小的
state.settlementSku = state.goodsInfo.skus
.filter((sku) => sku.stock > 0 && sku.promotionPrice > 0)
.reduce((prev, curr) => (prev.promotionPrice < curr.promotionPrice ? prev : curr), []);
// 设置满减送活动
if (data.rewardActivity) {
state.rewardActivity = data.rewardActivity;
//获取活动时间
getActivityTime(state.rewardActivity.id);
}
}
//获取活动时间
async function getActivityTime(id) {
const { code, data } = await RewardActivityApi.getRewardActivity(id);
if (code === 0) {
// console.log('获取到的活动 数据', data)
state.rewardActivity.startTime = data.startTime;
state.rewardActivity.endTime = data.endTime;
}
}
onLoad((options) => {
// 非法参数
if (!options.id) {
state.goodsInfo = null;
return;
}
state.goodsId = options.id;
// 1. 加载商品信息
SpuApi.getSpuDetail(state.goodsId).then((res) => {
// 未找到商品
if (res.code !== 0 || !res.data) {
state.goodsInfo = null;
return;
}
// 加载到商品
state.skeletonLoading = false;
state.goodsInfo = res.data;
// 加载是否收藏
if (isLogin.value) {
FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
@@ -238,172 +407,263 @@
state.goodsInfo.favorite = res.data;
});
}
});
});
// 2. 加载优惠劵信息
// 2. 加载优惠劵信息
getCoupon();
// 3. 加载营销活动信息
ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {
if (res.code !== 0) {
return;
}
res.data.forEach(activity => {
if ([1, 2, 3].includes(activity.type)) { // 情况一:拼团/秒杀/砍价
state.activityList.push(activity);
} else if (activity.type === 5) { // 情况二:满减送
state.activityInfo.push(activity);
} else { // 情况三:限时折扣 TODO 芋艿
console.log('待实现!优先级不高');
}
})
});
});
// 3. 加载营销活动信息
ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {
if (res.code !== 0) {
return;
}
state.activityList = res.data;
});
//获取结算信息
getSettlementByIds(state.goodsId);
});
</script>
<style lang="scss" scoped>
.detail-card {
background-color: #ffff;
margin: 14rpx 20rpx;
border-radius: 10rpx;
overflow: hidden;
}
.detail-card {
background-color: #ffff;
margin: 14rpx 20rpx;
border-radius: 10rpx;
overflow: hidden;
}
// 价格标题卡片
.title-card {
.price-box {
.price-text {
font-size: 42rpx;
font-weight: 500;
color: #ff3000;
line-height: 30rpx;
font-family: OPPOSANS;
// 价格标题卡片
.title-card {
.price-box {
.price-text {
font-size: 42rpx;
font-weight: 500;
color: #ff3000;
line-height: 30rpx;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
&::before {
content: '¥';
font-size: 30rpx;
}
}
.origin-price-text {
font-size: 26rpx;
font-weight: 400;
text-decoration: line-through;
color: $gray-c;
font-family: OPPOSANS;
.origin-price-text {
font-size: 26rpx;
font-weight: 400;
text-decoration: line-through;
color: $gray-c;
font-family: OPPOSANS;
&::before {
content: '¥';
}
}
}
&::before {
content: '¥';
}
}
}
.sales-text {
font-size: 26rpx;
font-weight: 500;
color: $gray-c;
}
.sales-text {
font-size: 26rpx;
font-weight: 500;
color: $gray-c;
}
.discounts-box {
.tag-content {
flex: 1;
min-width: 0;
white-space: nowrap;
}
.discounts-box {
.tag-content {
flex: 1;
min-width: 0;
white-space: nowrap;
}
.tag-box {
overflow: hidden;
text-overflow: ellipsis;
}
.tag-box {
overflow: hidden;
text-overflow: ellipsis;
}
.tag {
flex-shrink: 0;
padding: 4rpx 10rpx;
font-size: 24rpx;
font-weight: 500;
border-radius: 4rpx;
color: var(--ui-BG-Main);
background: var(--ui-BG-Main-tag);
}
.tag {
flex-shrink: 0;
padding: 4rpx 10rpx;
font-size: 24rpx;
font-weight: 500;
border-radius: 4rpx;
color: var(--ui-BG-Main);
background: var(--ui-BG-Main-tag);
}
.discounts-title {
font-size: 24rpx;
font-weight: 500;
color: var(--ui-BG-Main);
line-height: normal;
}
.discounts-title {
font-size: 24rpx;
font-weight: 500;
color: var(--ui-BG-Main);
line-height: normal;
}
.cicon-forward {
color: var(--ui-BG-Main);
font-size: 24rpx;
line-height: normal;
margin-top: 4rpx;
}
}
.cicon-forward {
color: var(--ui-BG-Main);
font-size: 24rpx;
line-height: normal;
margin-top: 4rpx;
}
}
.title-text {
font-size: 30rpx;
font-weight: bold;
line-height: 42rpx;
}
.title-text {
font-size: 30rpx;
font-weight: bold;
line-height: 42rpx;
}
.subtitle-text {
font-size: 26rpx;
font-weight: 400;
color: $dark-9;
line-height: 42rpx;
}
}
.subtitle-text {
font-size: 26rpx;
font-weight: 400;
color: $dark-9;
line-height: 42rpx;
}
}
// 购买
.buy-box {
.add-btn {
width: 214rpx;
height: 72rpx;
font-weight: 500;
font-size: 28rpx;
border-radius: 40rpx 0 0 40rpx;
background-color: var(--ui-BG-Main-light);
color: var(--ui-BG-Main);
}
// 购买
.buy-box {
.add-btn {
width: 214rpx;
height: 72rpx;
font-weight: 500;
font-size: 28rpx;
border-radius: 40rpx 0 0 40rpx;
background-color: var(--ui-BG-Main-light);
color: var(--ui-BG-Main);
}
.buy-btn {
width: 214rpx;
height: 72rpx;
font-weight: 500;
font-size: 28rpx;
.buy-btn {
width: 214rpx;
height: 72rpx;
font-weight: 500;
font-size: 28rpx;
border-radius: 0 40rpx 40rpx 0;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
border-radius: 0 40rpx 40rpx 0;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
width: 428rpx;
height: 72rpx;
border-radius: 40rpx;
background: #999999;
color: $white;
}
}
.disabled-btn {
width: 428rpx;
height: 72rpx;
border-radius: 40rpx;
background: #999999;
color: $white;
}
}
.model-box {
height: 60vh;
.model-box {
height: 60vh;
.model-content {
height: 56vh;
}
.model-content {
height: 56vh;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.subtitle {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
}
.subtitle {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
}
// 限时折扣
.discount {
width: 750rpx;
height: 100rpx;
// background-color: red;
overflow: hidden;
position: relative;
}
.disImg {
width: 750rpx;
height: 100rpx;
position: absolute;
top: 0;
z-index: -1;
}
.discountCont {
width: 680rpx;
height: 90rpx;
margin: 10rpx auto 0 auto;
// background-color: gold;
}
.disContT {
width: 680rpx;
height: 50rpx;
display: flex;
justify-content: space-between;
}
.disContT1 {
width: 400rpx;
height: 50rpx;
// background-color: green;
display: flex;
justify-content: flex-start;
align-items: center;
}
.disContT2 {
width: 200rpx;
height: 50rpx;
line-height: 50rpx;
// background-color: gold;
font-size: 30rpx;
text-align: end;
color: white;
font-weight: bolder;
font-style: oblique 20deg;
letter-spacing: 0.1rem;
}
.disContT1P {
color: white;
font-weight: bold;
font-size: 28rpx;
}
.disContT1End {
// width: 180rpx;
padding: 0 10rpx;
height: 30rpx;
line-height: 28rpx;
text-align: center;
font-weight: bold;
background-color: white;
color: #ff3000;
font-size: 23rpx;
border-radius: 20rpx;
margin-left: 10rpx;
}
.disContB {
width: 680rpx;
height: 40rpx;
display: flex;
justify-content: space-between;
font-size: 20rpx;
color: white;
align-items: center;
}
.disContB1 {
width: 300rpx;
height: 40rpx;
line-height: 40rpx;
}
.disContB2 {
width: 300rpx;
height: 40rpx;
line-height: 40rpx;
display: flex;
justify-content: flex-end;
}
</style>
+308 -263
View File
@@ -1,362 +1,407 @@
<template>
<s-layout navbar="normal" :leftWidth="0" :rightWidth="0" tools="search" :defaultSearch="state.keyword"
@search="onSearch">
<!-- 筛选 -->
<su-sticky bgColor="#fff">
<view class="ss-flex">
<view class="ss-flex-1">
<su-tabs :list="state.tabList" :scrollable="false" @change="onTabsChange"
:current="state.currentTab" />
</view>
<view class="list-icon" @tap="state.iconStatus = !state.iconStatus">
<text v-if="state.iconStatus" class="sicon-goods-list" />
<text v-else class="sicon-goods-card" />
</view>
</view>
</su-sticky>
<s-layout
navbar="normal"
:leftWidth="0"
:rightWidth="0"
tools="search"
:defaultSearch="state.keyword"
@search="onSearch"
>
<!-- 筛选 -->
<su-sticky bgColor="#fff">
<view class="ss-flex">
<view class="ss-flex-1">
<su-tabs
:list="state.tabList"
:scrollable="false"
@change="onTabsChange"
:current="state.currentTab"
/>
</view>
<view class="list-icon" @tap="state.iconStatus = !state.iconStatus">
<text v-if="state.iconStatus" class="sicon-goods-list" />
<text v-else class="sicon-goods-card" />
</view>
</view>
</su-sticky>
<!-- 弹窗 -->
<su-popup :show="state.showFilter" type="top" round="10" :space="sys_navBar + 38" backgroundColor="#F6F6F6"
:zIndex="10" @close="state.showFilter = false">
<view class="filter-list-box">
<view class="filter-item" v-for="(item, index) in state.tabList[state.currentTab].list"
:key="item.value" :class="[{ 'filter-item-active': index === state.curFilter }]"
@tap="onFilterItem(index)">
{{ item.label }}
</view>
</view>
</su-popup>
<!-- 弹窗 -->
<su-popup
:show="state.showFilter"
type="top"
round="10"
:space="sys_navBar + 38"
backgroundColor="#F6F6F6"
:zIndex="10"
@close="state.showFilter = false"
>
<view class="filter-list-box">
<view
class="filter-item"
v-for="(item, index) in state.tabList[state.currentTab].list"
:key="item.value"
:class="[{ 'filter-item-active': index === state.curFilter }]"
@tap="onFilterItem(index)"
>
{{ item.label }}
</view>
</view>
</su-popup>
<!-- 情况一:单列布局 -->
<view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20">
<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id">
<s-goods-column
<view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20">
<view
class="ss-p-l-20 ss-p-r-20 ss-m-b-20"
v-for="item in state.pagination.list"
:key="item.id"
>
<s-goods-column
class=""
size="lg"
:data="item"
:topRadius="10"
:bottomRadius="10"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
/>
</view>
</view>
</view>
</view>
<!-- 情况二:双列布局 -->
<view v-if="!state.iconStatus && state.pagination.total > 0"
class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top">
<view class="goods-list-box">
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
<s-goods-column
<view
v-if="!state.iconStatus && state.pagination.total > 0"
class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"
>
<view class="goods-list-box">
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
<s-goods-column
class="goods-md-box"
size="md"
:data="item"
:topRadius="10"
:bottomRadius="10"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'left')"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'left')"
>
<template v-slot:cart>
<button class="ss-reset-button cart-btn" />
</template>
</s-goods-column>
</view>
</view>
<view class="goods-list-box">
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
<s-goods-column
<template v-slot:cart>
<button class="ss-reset-button cart-btn" />
</template>
</s-goods-column>
</view>
</view>
<view class="goods-list-box">
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
<s-goods-column
class="goods-md-box"
size="md"
:topRadius="10"
:bottomRadius="10"
:data="item"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'right')"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'right')"
>
<template v-slot:cart>
<button class="ss-reset-button cart-btn" />
</template>
</s-goods-column>
</view>
</view>
</view>
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
<template v-slot:cart>
<button class="ss-reset-button cart-btn" />
</template>
</s-goods-column>
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}" @tap="loadMore" />
<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" />
</s-layout>
}"
@tap="loadMore"
/>
<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" />
</s-layout>
</template>
<script setup>
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import _ from 'lodash';
import { reactive, ref } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util';
import SpuApi from '@/sheep/api/product/spu';
import OrderApi from '@/sheep/api/trade/order';
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
const sys_navBar = sheep.$platform.navbar;
const emits = defineEmits(['close', 'change']);
const sys_navBar = sheep.$platform.navbar;
const emits = defineEmits(['close', 'change']);
const state = reactive({
pagination: {
list: [],
const state = reactive({
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 6,
},
currentSort: undefined,
currentOrder: undefined,
currentTab: 0, // 当前选中的 tab
curFilter: 0, // 当前选中的 list 筛选项
showFilter: false,
iconStatus: false, // true - 单列布局;false - 双列布局
pageSize: 6,
},
currentSort: undefined,
currentOrder: undefined,
currentTab: 0, // 当前选中的 tab
curFilter: 0, // 当前选中的 list 筛选项
showFilter: false,
iconStatus: false, // true - 单列布局;false - 双列布局
keyword: '',
categoryId: 0,
tabList: [{
name: '综合推荐',
list: [{
label: '综合推荐'
},
{
label: '价格升序',
sort: 'price',
order: true,
},
{
label: '价格降序',
sort: 'price',
order: false,
},
],
},
{
name: '销量',
sort: 'salesCount',
order: false
},
{
name: '新品优先',
value: 'createTime',
order: false
},
],
loadStatus: '',
leftGoodsList: [], // 双列布局 - 左侧商品
rightGoodsList: [], // 双列布局 - 右侧商品
});
tabList: [
{
name: '综合推荐',
list: [
{
label: '综合推荐',
},
{
label: '价格升序',
sort: 'price',
order: true,
},
{
label: '价格降序',
sort: 'price',
order: false,
},
],
},
{
name: '销量',
sort: 'salesCount',
order: false,
},
{
name: '新品优先',
value: 'createTime',
order: false,
},
],
loadStatus: '',
leftGoodsList: [], // 双列布局 - 左侧商品
rightGoodsList: [], // 双列布局 - 右侧商品
});
// 加载瀑布流
let count = 0;
let leftHeight = 0;
let rightHeight = 0;
// 加载瀑布流
let count = 0;
let leftHeight = 0;
let rightHeight = 0;
// 处理双列布局 leftGoodsList + rightGoodsList
function mountMasonry(height = 0, where = 'left') {
if (!state.pagination.list[count]) {
function mountMasonry(height = 0, where = 'left') {
if (!state.pagination.list[count]) {
return;
}
if (where === 'left') {
leftHeight += height;
} else {
rightHeight += height;
}
if (leftHeight <= rightHeight) {
state.leftGoodsList.push(state.pagination.list[count]);
} else {
state.rightGoodsList.push(state.pagination.list[count]);
}
count++;
}
if (where === 'left') {
leftHeight += height;
} else {
rightHeight += height;
}
if (leftHeight <= rightHeight) {
state.leftGoodsList.push(state.pagination.list[count]);
} else {
state.rightGoodsList.push(state.pagination.list[count]);
}
count++;
}
// 清空列表
function emptyList() {
function emptyList() {
resetPagination(state.pagination);
state.leftGoodsList = [];
state.rightGoodsList = [];
count = 0;
leftHeight = 0;
rightHeight = 0;
}
state.rightGoodsList = [];
count = 0;
leftHeight = 0;
rightHeight = 0;
}
// 搜索
function onSearch(e) {
state.keyword = e;
emptyList();
getList(state.currentSort, state.currentOrder);
}
// 搜索
function onSearch(e) {
state.keyword = e;
emptyList();
getList(state.currentSort, state.currentOrder);
}
// 点击
function onTabsChange(e) {
// 点击
function onTabsChange(e) {
// 如果点击的是【综合推荐】,则直接展开或者收起筛选项
if (state.tabList[e.index].list) {
state.currentTab = e.index;
state.showFilter = !state.showFilter;
return;
}
if (state.tabList[e.index].list) {
state.currentTab = e.index;
state.showFilter = !state.showFilter;
return;
}
state.showFilter = false;
// 如果点击的是【销量】或者【新品优先】,则直接切换 tab
if (e.index === state.currentTab) {
return;
}
if (e.index === state.currentTab) {
return;
}
state.currentTab = e.index;
state.currentSort = e.sort;
state.currentOrder = e.order;
emptyList();
getList(e.sort, e.order);
}
emptyList();
getList(e.sort, e.order);
}
// 点击 tab 的 list 筛选项
const onFilterItem = (val) => {
// 点击 tab 的 list 筛选项
const onFilterItem = (val) => {
// 如果点击的是当前的筛选项,则直接收起筛选项,不要加载数据
// 这里选择 tabList[0] 的原因,是目前只有它有 list
if (state.currentSort === state.tabList[0].list[val].sort
&& state.currentOrder === state.tabList[0].list[val].order) {
state.showFilter = false;
return;
}
if (
state.currentSort === state.tabList[0].list[val].sort &&
state.currentOrder === state.tabList[0].list[val].order
) {
state.showFilter = false;
return;
}
state.showFilter = false;
// 设置筛选条件
state.curFilter = val;
state.tabList[0].name = state.tabList[0].list[val].label;
state.currentSort = state.tabList[0].list[val].sort;
state.currentOrder = state.tabList[0].list[val].order;
// 清空 + 加载数据
state.curFilter = val;
state.tabList[0].name = state.tabList[0].list[val].label;
state.currentSort = state.tabList[0].list[val].sort;
state.currentOrder = state.tabList[0].list[val].order;
// 清空 + 加载数据
emptyList();
getList();
}
getList();
};
async function getList() {
state.loadStatus = 'loading';
const { code, data } = await SpuApi.getSpuPage({
async function getList() {
state.loadStatus = 'loading';
const { code, data } = await SpuApi.getSpuPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
sortField: state.currentSort,
sortAsc: state.currentOrder,
categoryId: state.categoryId,
keyword: state.keyword,
});
sortField: state.currentSort,
sortAsc: state.currentOrder,
categoryId: state.categoryId,
keyword: state.keyword,
});
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list)
// 拼接结算信息(营销)
await OrderApi.getSettlementProduct(data.list.map((item) => item.id).join(',')).then((res) => {
if (res.code !== 0) {
return;
}
appendSettlementProduct(data.list, res.data);
});
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
mountMasonry();
}
}
// 加载更多
function loadMore() {
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getList(state.currentSort, state.currentOrder);
}
}
onLoad((options) => {
state.categoryId = options.categoryId;
state.keyword = options.keyword;
getList(state.currentSort, state.currentOrder);
});
onLoad((options) => {
state.categoryId = options.categoryId;
state.keyword = options.keyword;
getList(state.currentSort, state.currentOrder);
});
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
</script>
<style lang="scss" scoped>
.goods-list-box {
width: 50%;
box-sizing: border-box;
.goods-list-box {
width: 50%;
box-sizing: border-box;
.left-list {
margin-right: 10rpx;
margin-bottom: 20rpx;
}
.left-list {
margin-right: 10rpx;
margin-bottom: 20rpx;
}
.right-list {
margin-left: 10rpx;
margin-bottom: 20rpx;
}
}
.right-list {
margin-left: 10rpx;
margin-bottom: 20rpx;
}
}
.goods-box {
&:nth-last-of-type(1) {
margin-bottom: 0 !important;
}
.goods-box {
&:nth-last-of-type(1) {
margin-bottom: 0 !important;
}
&:nth-child(2n) {
margin-right: 0;
}
}
&:nth-child(2n) {
margin-right: 0;
}
}
.list-icon {
width: 80rpx;
.list-icon {
width: 80rpx;
.sicon-goods-card {
font-size: 40rpx;
}
.sicon-goods-card {
font-size: 40rpx;
}
.sicon-goods-list {
font-size: 40rpx;
}
}
.sicon-goods-list {
font-size: 40rpx;
}
}
.goods-card {
margin-left: 20rpx;
}
.goods-card {
margin-left: 20rpx;
}
.list-filter-tabs {
background-color: #fff;
}
.list-filter-tabs {
background-color: #fff;
}
.filter-list-box {
padding: 28rpx 52rpx;
.filter-list-box {
padding: 28rpx 52rpx;
.filter-item {
font-size: 28rpx;
font-weight: 500;
color: #333333;
line-height: normal;
margin-bottom: 24rpx;
.filter-item {
font-size: 28rpx;
font-weight: 500;
color: #333333;
line-height: normal;
margin-bottom: 24rpx;
&:nth-last-child(1) {
margin-bottom: 0;
}
}
&:nth-last-child(1) {
margin-bottom: 0;
}
}
.filter-item-active {
color: var(--ui-BG-Main);
}
}
.filter-item-active {
color: var(--ui-BG-Main);
}
}
.tab-item {
height: 50px;
position: relative;
z-index: 11;
.tab-item {
height: 50px;
position: relative;
z-index: 11;
.tab-title {
font-size: 30rpx;
}
.tab-title {
font-size: 30rpx;
}
.cur-tab-title {
font-weight: $font-weight-bold;
}
.cur-tab-title {
font-weight: $font-weight-bold;
}
.tab-line {
width: 60rpx;
height: 6rpx;
border-radius: 6rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 10rpx;
background-color: var(--ui-BG-Main);
z-index: 12;
}
}
</style>
.tab-line {
width: 60rpx;
height: 6rpx;
border-radius: 6rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 10rpx;
background-color: var(--ui-BG-Main);
z-index: 12;
}
}
</style>
+482
View File
@@ -0,0 +1,482 @@
<!-- 秒杀商品详情 -->
<template>
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
<!-- 标题栏 -->
<detailNavbar />
<!-- 骨架屏 -->
<detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 -->
<s-empty
v-else-if="
state.goodsInfo === null ||
state.goodsInfo.activity_type !== PromotionActivityTypeEnum.POINT.type
"
text="活动不存在或已结束"
icon="/static/soldout-empty.png"
showAction
actionText="再逛逛"
actionUrl="/pages/goods/list"
/>
<block v-else>
<view class="detail-swiper-selector">
<!-- 商品图轮播 -->
<su-swiper
class="ss-m-b-14"
isPreview
:list="state.goodsSwiper"
dotStyle="tag"
imageMode="widthFix"
dotCur="bg-mask-40"
:seizeHeight="750"
/>
<!-- 价格+标题 -->
<view class="title-card detail-card ss-p-y-40 ss-p-x-20">
<view class="ss-flex ss-row-between ss-col-center ss-m-b-18">
<view class="price-box ss-flex ss-col-bottom">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="point-text ss-m-r-16">
{{ getShowPrice.point }}
{{
!getShowPrice.price || getShowPrice.price === 0 ? '' : `+¥${getShowPrice.price}`
}}
</text>
</view>
<view class="sales-text">
{{ formatExchange(state.goodsInfo.sales_show_type, state.goodsInfo.sales) }}
</view>
</view>
<view class="origin-price-text ss-m-b-60" v-if="state.goodsInfo.marketPrice">
原价:¥{{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
</view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name || '' }}</view>
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
</view>
<!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
</view>
<!-- 规格与数量弹框 -->
<s-select-seckill-sku
v-model="state.goodsInfo"
:show="state.showSelectSku"
:single-limit-count="activity.singleLimitCount"
@buy="onBuy"
@change="onSkuChange"
@close="state.showSelectSku = false"
/>
</view>
<!-- 评价 -->
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsInfo.id" />
<!-- 详情 -->
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
<!-- 详情tabbar -->
<detail-tabbar v-model="state.goodsInfo">
<view class="buy-box ss-flex ss-col-center ss-p-r-20">
<button
class="ss-reset-button origin-price-btn ss-flex-col"
v-if="state.goodsInfo.marketPrice"
@tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })"
>
<view>
<view class="btn-price">{{ fen2yuan(state.goodsInfo.marketPrice) }}</view>
<view>原价购买</view>
</view>
</button>
<button
class="ss-reset-button btn-box ss-flex-col"
@tap="state.showSelectSku = true"
:class="state.goodsInfo.stock != 0 ? 'check-btn-box' : 'disabled-btn-box'"
:disabled="state.goodsInfo.stock === 0"
>
<view class="price-box ss-flex">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
style="width: 36rpx; height: 36rpx; margin: 0 4rpx"
></image>
<text class="point-text ss-m-r-16">
{{ getShowPrice.point }}
{{
!getShowPrice.price || getShowPrice.price === 0 ? '' : `+¥${getShowPrice.price}`
}}
</text>
</view>
<view v-if="state.goodsInfo.stock === 0">已售罄</view>
<view v-else>立即兑换</view>
</button>
</view>
</detail-tabbar>
</block>
</s-layout>
</template>
<script setup>
import { computed, reactive, ref, unref } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { isEmpty } from 'lodash-es';
import { fen2yuan, formatExchange, formatGoodsSwiper } from '@/sheep/hooks/useGoods';
import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue';
import detailSkeleton from './components/detail/detail-skeleton.vue';
import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue';
import SpuApi from '@/sheep/api/product/spu';
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
import PointApi from '@/sheep/api/promotion/point';
const headerBg = sheep.$url.css('/static/img/shop/goods/score-bg.png');
const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png');
const disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
onPageScroll(() => {});
const state = reactive({
skeletonLoading: true,
goodsInfo: {},
showSelectSku: false,
goodsSwiper: [],
selectedSku: {},
showModel: false,
total: 0,
price: '',
});
// 规格变更
function onSkuChange(e) {
state.selectedSku = e;
}
// 立即购买
function onBuy(sku) {
sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({
order_type: 'goods',
buy_type: 'point',
pointActivityId: activity.value.id,
items: [
{
skuId: sku.id,
count: sku.count,
},
],
}),
});
}
// 分享信息
const shareInfo = computed(() => {
if (isEmpty(unref(activity))) return {};
return sheep.$platform.share.getShareInfo(
{
title: activity.value.name,
image: sheep.$url.cdn(state.goodsInfo.picUrl),
params: {
// TODO @puhui:page 相关,要不搞个枚举,然后都弄过去;
page: '6',
query: activity.value.id,
},
},
{
type: 'goods', // 商品海报
title: activity.value.name, // 商品标题
image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
price: (getShowPrice.value.price || 0) + ` + ${getShowPrice.value.point} 积分`, // 积分价格
marketPrice: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
},
);
});
const activity = ref();
const getShowPrice = computed(() => {
if (!isEmpty(state.selectedSku)) {
const sku = state.selectedSku;
return {
point: sku.point,
price: !sku.pointPrice ? '' : fen2yuan(sku.pointPrice),
};
}
return {
point: activity.value.point,
price: !activity.value.price ? '' : fen2yuan(activity.value.price),
};
});
const getShowPriceText = computed(() => {
let priceText = `¥${fen2yuan(state.goodsInfo.price)}`;
if (!isEmpty(state.selectedSku)) {
const sku = state.selectedSku;
priceText = `${sku.point}${!sku.pointPrice ? '' : `+¥${fen2yuan(sku.pointPrice)}`}`;
}
return priceText;
});
// 查询活动
const getActivity = async (id) => {
const { data } = await PointApi.getPointActivity(id);
activity.value = data;
// 查询商品
await getSpu(data.spuId);
};
// 查询商品
const getSpu = async (id) => {
const { data } = await SpuApi.getSpuDetail(id);
data.activity_type = PromotionActivityTypeEnum.POINT.type;
state.goodsInfo = data;
state.goodsInfo.stock = Math.min(data.stock, activity.value.stock);
// 处理轮播图
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.sliderPicUrls);
// 价格、库存使用活动的
data.skus.forEach((sku) => {
const product = activity.value.products.find((product) => product.skuId === sku.id);
if (product) {
sku.point = product.point;
sku.pointPrice = product.price;
sku.stock = Math.min(sku.stock, product.stock);
// 设置限购数量
sku.limitCount = product.count;
} else {
// 找不到可能是没配置
sku.stock = 0;
}
});
state.skeletonLoading = false;
};
onLoad((options) => {
// 非法参数
if (!options.id) {
state.goodsInfo = null;
return;
}
// 查询活动
getActivity(options.id);
});
</script>
<style lang="scss" scoped>
.disabled-btn-box[disabled] {
background-color: transparent;
}
.detail-card {
background-color: $white;
margin: 14rpx 20rpx;
border-radius: 10rpx;
overflow: hidden;
}
// 价格标题卡片
.title-card {
width: 710rpx;
box-sizing: border-box;
background-size: 100% 100%;
border-radius: 10rpx;
background-image: v-bind(headerBg);
background-repeat: no-repeat;
.price-box {
.point-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.point-text {
font-size: 42rpx;
font-weight: 500;
color: #ff3000;
line-height: 36rpx;
font-family: OPPOSANS;
}
.price-text {
font-size: 42rpx;
font-weight: 500;
color: #ff3000;
line-height: 36rpx;
font-family: OPPOSANS;
}
}
.origin-price-text {
font-size: 26rpx;
font-weight: 400;
text-decoration: line-through;
color: $gray-c;
font-family: OPPOSANS;
}
.sales-text {
font-size: 26rpx;
font-weight: 500;
color: $gray-c;
}
.discounts-box {
.discounts-tag {
padding: 4rpx 10rpx;
font-size: 24rpx;
font-weight: 500;
border-radius: 4rpx;
color: var(--ui-BG-Main);
// background: rgba(#2aae67, 0.05);
background: var(--ui-BG-Main-tag);
}
.discounts-title {
font-size: 24rpx;
font-weight: 500;
color: var(--ui-BG-Main);
line-height: normal;
}
.cicon-forward {
color: var(--ui-BG-Main);
font-size: 24rpx;
line-height: normal;
margin-top: 4rpx;
}
}
.title-text {
font-size: 30rpx;
font-weight: bold;
line-height: 42rpx;
}
.subtitle-text {
font-size: 26rpx;
font-weight: 400;
color: $dark-9;
line-height: 42rpx;
}
}
// 购买
.buy-box {
.check-btn-box {
width: 248rpx;
height: 80rpx;
font-size: 24rpx;
font-weight: 600;
margin-left: -36rpx;
background-image: v-bind(btnBg);
background-repeat: no-repeat;
background-size: 100% 100%;
color: #ffffff;
line-height: normal;
border-radius: 0px 40rpx 40rpx 0px;
}
.disabled-btn-box {
width: 248rpx;
height: 80rpx;
font-size: 24rpx;
font-weight: 600;
margin-left: -36rpx;
background-image: v-bind(disabledBtnBg);
background-repeat: no-repeat;
background-size: 100% 100%;
color: #999999;
line-height: normal;
border-radius: 0px 40rpx 40rpx 0px;
}
.btn-price {
font-family: OPPOSANS;
&::before {
content: '¥';
}
}
.origin-price-btn {
width: 236rpx;
height: 80rpx;
background: rgba(#ff5651, 0.1);
color: #ff6000;
border-radius: 40rpx 0px 0px 40rpx;
line-height: normal;
font-size: 24rpx;
font-weight: 500;
.no-original {
font-size: 28rpx;
}
.btn-title {
font-size: 28rpx;
}
}
}
//秒杀卡片
.seckill-box {
background: v-bind(seckillBg) no-repeat;
background-size: 100% 100%;
}
.groupon-box {
background: v-bind(grouponBg) no-repeat;
background-size: 100% 100%;
}
//活动卡片
.activity-box {
width: 100%;
height: 80rpx;
box-sizing: border-box;
margin-bottom: 10rpx;
.activity-title {
font-size: 26rpx;
font-weight: 500;
color: #ffffff;
line-height: 42rpx;
.activity-icon {
width: 38rpx;
height: 38rpx;
}
}
.activity-go {
width: 70rpx;
height: 32rpx;
background: #ffffff;
border-radius: 16rpx;
font-weight: 500;
color: #ff6000;
font-size: 24rpx;
line-height: normal;
}
}
.model-box {
.title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.subtitle {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
}
</style>
+47 -38
View File
@@ -7,7 +7,9 @@
<detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 -->
<s-empty
v-else-if="state.goodsInfo === null || state.goodsInfo.activity_type !== 'seckill'"
v-else-if="
state.goodsInfo === null || state.goodsInfo.activity_type !== 'seckill' || endTime.ms <= 0
"
text="活动不存在或已结束"
icon="/static/soldout-empty.png"
showAction
@@ -63,16 +65,13 @@
<detail-progress :percent="state.percent" />
</view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo?.name }}</view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name || '' }}</view>
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
</view>
<!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku
:sku="state.selectedSku"
@tap="state.showSelectSku = true"
/>
<detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
</view>
<!-- 规格与数量弹框 -->
<s-select-seckill-sku
@@ -107,7 +106,9 @@
<button v-else class="ss-reset-button origin-price-btn ss-flex-col">
<view
class="no-original"
:class="state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED ? '' : ''"
:class="
state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED ? '' : ''
"
>
秒杀价
</view>
@@ -136,11 +137,11 @@
</template>
<script setup>
import {reactive, computed, ref} from 'vue';
import { computed, reactive, ref, unref } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import {isEmpty, min} from 'lodash';
import {useDurationTime, formatGoodsSwiper, fen2yuan} from '@/sheep/hooks/useGoods';
import { isEmpty, min } from 'lodash-es';
import { fen2yuan, formatGoodsSwiper, useDurationTime } from '@/sheep/hooks/useGoods';
import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue';
@@ -148,15 +149,13 @@
import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue';
import detailProgress from './components/detail/detail-progress.vue';
import SeckillApi from "@/sheep/api/promotion/seckill";
import SpuApi from "@/sheep/api/product/spu";
import {getTimeStatusEnum, TimeStatusEnum} from "@/sheep/util/const";
import SeckillApi from '@/sheep/api/promotion/seckill';
import SpuApi from '@/sheep/api/product/spu';
import { getTimeStatusEnum, TimeStatusEnum } from '@/sheep/util/const';
const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-bg.png');
const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png');
const disabledBtnBg = sheep.$url.css(
'/static/img/shop/goods/activity-btn-disabled.png',
);
const disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
@@ -199,9 +198,9 @@
});
}
// 分享信息 TODO 芋艿:待接入
// 分享信息
const shareInfo = computed(() => {
if (isEmpty(activity)) return {};
if (isEmpty(unref(activity))) return {};
return sheep.$platform.share.getShareInfo(
{
title: activity.value.name,
@@ -215,42 +214,47 @@
type: 'goods', // 商品海报
title: activity.value.name, // 商品标题
image: sheep.$url.cdn(state.goodsInfo.picUrl), // 商品主图
price: state.goodsInfo.price, // 商品价格
marketPrice: state.goodsInfo.marketPrice, // 商品原价
price: fen2yuan(state.goodsInfo.price), // 商品价格
marketPrice: fen2yuan(state.goodsInfo.marketPrice), // 商品原价
},
);
});
const activity = ref()
const timeStatusEnum = ref('')
const activity = ref();
const timeStatusEnum = ref('');
// 查询活动
const getActivity = async (id) => {
const { data } = await SeckillApi.getSeckillActivity(id)
activity.value = data
timeStatusEnum.value = getTimeStatusEnum(activity.startTime, activity.endTime)
const { data } = await SeckillApi.getSeckillActivity(id);
activity.value = data;
timeStatusEnum.value = getTimeStatusEnum(activity.value.startTime, activity.value.endTime);
state.percent = 100 - (data.stock / data.totalStock) * 100;
// 查询商品
await getSpu(data.spuId)
}
await getSpu(data.spuId);
};
// 查询商品
const getSpu = async (id) => {
const { data } = await SpuApi.getSpuDetail(id)
// 模拟
data.activity_type = 'seckill'
state.goodsInfo = data
const { data } = await SpuApi.getSpuDetail(id);
data.activity_type = 'seckill';
state.goodsInfo = data;
// 处理轮播图
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.sliderPicUrls);
// 默认显示最低价
state.goodsInfo.price = min([state.goodsInfo.price, ...activity.value.products.map(spu => spu.seckillPrice)])
state.goodsInfo.price = min([
state.goodsInfo.price,
...activity.value.products.map((spu) => spu.seckillPrice),
]);
// 价格、库存使用活动的
data.skus.forEach(sku => {
const product = activity.value.products.find(product => product.skuId === sku.id);
data.skus.forEach((sku) => {
const product = activity.value.products.find((product) => product.skuId === sku.id);
if (product) {
sku.price = product.seckillPrice;
sku.stock = Math.min(sku.stock, product.stock);
} else { // 找不到可能是没配置,则不能发起秒杀
} else {
// 找不到可能是没配置,则不能发起秒杀
sku.stock = 0;
}
// 设置限购数量
@@ -264,7 +268,7 @@
});
state.skeletonLoading = false;
}
};
onLoad((options) => {
// 非法参数
@@ -274,7 +278,7 @@
}
// 查询活动
getActivity(options.id)
getActivity(options.id);
});
</script>
@@ -282,6 +286,7 @@
.disabled-btn-box[disabled] {
background-color: transparent;
}
.detail-card {
background-color: $white;
margin: 14rpx 20rpx;
@@ -372,6 +377,7 @@
font-size: 26rpx;
font-weight: 500;
color: #ffffff;
.countdown-h {
font-size: 24rpx;
font-family: OPPOSANS;
@@ -382,6 +388,7 @@
background: rgba(#000000, 0.1);
border-radius: 6rpx;
}
.countdown-num {
font-size: 24rpx;
font-family: OPPOSANS;
@@ -465,6 +472,7 @@
line-height: normal;
border-radius: 0px 40rpx 40rpx 0px;
}
.btn-price {
font-family: OPPOSANS;
@@ -482,6 +490,7 @@
line-height: normal;
font-size: 24rpx;
font-weight: 500;
.no-original {
font-size: 28rpx;
}
+255 -175
View File
@@ -1,196 +1,276 @@
<template>
<s-layout title="购物车" tabbar="/pages/index/cart" :bgStyle="{ color: '#fff' }">
<s-empty v-if="state.list.length === 0" text="购物车空空如也,快去逛逛吧~" icon="/static/cart-empty.png" />
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/cart" title="购物车">
<s-empty
v-if="state.list.length === 0"
icon="/static/cart-empty.png"
text="购物车空空如也,快去逛逛吧~"
/>
<!-- 头部 -->
<view class="cart-box ss-flex ss-flex-col ss-row-between" v-if="state.list.length">
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
<view class="header-left ss-flex ss-col-center ss-font-26">
共
<text class="goods-number ui-TC-Main ss-flex">{{ state.list.length }}</text>
件商品
</view>
<view class="header-right">
<button v-if="state.editMode" class="ss-reset-button" @tap="state.editMode = false">
取消
</button>
<button v-else class="ss-reset-button ui-TC-Main" @tap="state.editMode = true">
编辑
</button>
</view>
</view>
<!-- 内容 -->
<view class="cart-content ss-flex-1 ss-p-x-30 ss-m-b-40">
<view class="goods-box ss-r-10 ss-m-b-14" v-for="item in state.list" :key="item.id">
<view class="ss-flex ss-col-center">
<label class="check-box ss-flex ss-col-center ss-p-l-10" @tap="onSelectSingle(item.id)">
<radio :checked="state.selectedIds.includes(item.id)" color="var(--ui-BG-Main)"
style="transform: scale(0.8)" @tap.stop="onSelectSingle(item.id)" />
</label>
<s-goods-item :title="item.spu.name" :img="item.spu.picUrl || item.goods.image"
:price="item.sku.price"
:skuText="item.sku.properties.length>1? item.sku.properties.reduce((items2,items)=>items2.valueName+' '+items.valueName):item.sku.properties[0].valueName"
priceColor="#FF3000" :titleWidth="400">
<template v-if="!state.editMode" v-slot:tool>
<su-number-box :min="0" :max="item.sku.stock" :step="1" v-model="item.count" @change="onNumberChange($event, item)" />
</template>
</s-goods-item>
</view>
</view>
</view>
<!-- 底部 -->
<su-fixed bottom :val="48" placeholder v-if="state.list.length > 0" :isInset="false">
<view class="cart-footer ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom">
<view class="footer-left ss-flex ss-col-center">
<label class="check-box ss-flex ss-col-center ss-p-r-30" @tap="onSelectAll">
<radio :checked="state.isAllSelected" color="var(--ui-BG-Main)"
style="transform: scale(0.8)" @tap.stop="onSelectAll" />
<view class="ss-m-l-8"> 全选 </view>
</label>
<text>合计:</text>
<view class="text-price price-text">
{{ fen2yuan(state.totalPriceSelected) }}
</view>
</view>
<view class="footer-right">
<button v-if="state.editMode" class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
@tap="onDelete">
删除
</button>
<button v-else class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
@tap="onConfirm">
去结算
{{ state.selectedIds?.length ? `(${state.selectedIds.length})` : '' }}
</button>
</view>
</view>
</su-fixed>
</view>
</s-layout>
<!-- 头部 -->
<view v-if="state.list.length" class="cart-box ss-flex ss-flex-col ss-row-between">
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
<view class="header-left ss-flex ss-col-center ss-font-26">
共
<text class="goods-number ui-TC-Main ss-flex">{{ state.list.length }}</text>
件商品
</view>
<view class="header-right">
<button v-if="state.editMode" class="ss-reset-button" @tap="state.editMode = false">
取消
</button>
<button v-else class="ss-reset-button ui-TC-Main" @tap="state.editMode = true">
编辑
</button>
</view>
</view>
<!-- 内容 -->
<view class="cart-content ss-flex-1 ss-p-x-30 ss-m-b-40">
<view v-for="item in state.list" :key="item.id" class="goods-box ss-r-10 ss-m-b-14">
<view class="ss-flex ss-col-center">
<label class="check-box ss-flex ss-col-center ss-p-l-10" @tap="onSelectSingle(item.id)">
<radio
:checked="state.selectedIds.includes(item.id)"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
@tap.stop="onSelectSingle(item.id)"
/>
</label>
<s-goods-item
:img="item.spu.picUrl || item.goods.image"
:price="item.sku.price"
:skuText="
item.sku.properties.length > 1
? item.sku.properties.reduce(
(items2, items) => items2.valueName + ' ' + items.valueName,
)
: item.sku.properties[0].valueName
"
:title="item.spu.name"
:titleWidth="400"
priceColor="#FF3000"
>
<template v-if="!state.editMode" v-slot:tool>
<su-number-box
v-model="item.count"
:max="item.sku.stock"
:min="0"
:step="1"
@change="onNumberChange($event, item)"
/>
</template>
</s-goods-item>
</view>
</view>
</view>
<!-- 底部 -->
<su-fixed v-if="state.list.length > 0" :isInset="false" :val="48" bottom placeholder>
<view class="cart-footer ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom">
<view class="footer-left ss-flex ss-col-center">
<label class="check-box ss-flex ss-col-center ss-p-r-30" @tap="onSelectAll">
<radio
:checked="state.isAllSelected"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
@tap.stop="onSelectAll"
/>
<view class="ss-m-l-8"> 全选</view>
</label>
<text>合计:</text>
<view class="text-price price-text">
{{ fen2yuan(state.totalPriceSelected) }}
</view>
</view>
<view class="footer-right">
<button
v-if="state.editMode"
class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
@tap="onDelete"
>
删除
</button>
<button
v-else
class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
@tap="onConfirm"
>
去结算
{{ state.selectedIds?.length ? `(${state.selectedIds.length})` : '' }}
</button>
</view>
</view>
</su-fixed>
</view>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { computed, reactive } from 'vue';
import { fen2yuan } from '../../sheep/hooks/useGoods';
import sheep from '@/sheep';
import SpuApi from '@/sheep/api/product/spu';
import { computed, reactive } from 'vue';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import { isEmpty } from '@/sheep/helper/utils';
const sys_navBar = sheep.$platform.navbar;
const cart = sheep.$store('cart');
const sys_navBar = sheep.$platform.navbar;
const cart = sheep.$store('cart');
const state = reactive({
editMode: false,
list: computed(() => cart.list),
selectedList: [],
selectedIds: computed(() => cart.selectedIds),
isAllSelected: computed(() => cart.isAllSelected),
totalPriceSelected: computed(() => cart.totalPriceSelected),
});
const state = reactive({
editMode: false,
list: computed(() => cart.list),
selectedList: [],
selectedIds: computed(() => cart.selectedIds),
isAllSelected: computed(() => cart.isAllSelected),
totalPriceSelected: computed(() => cart.totalPriceSelected),
});
// 单选选中
function onSelectSingle(id) {
cart.selectSingle(id);
}
// 单选选中
function onSelectSingle(id) {
cart.selectSingle(id);
}
// 全选
function onSelectAll() {
cart.selectAll(!state.isAllSelected);
}
function onSelectAll() {
cart.selectAll(!state.isAllSelected);
}
// 结算
function onConfirm() {
let items = []
let goods_list = [];
state.selectedList = state.list.filter((item) => state.selectedIds.includes(item.id));
state.selectedList.map((item) => {
// 此处前端做出修改
items.push({
skuId: item.sku.id,
count: item.count,
cartId: item.id,
})
goods_list.push({
// goods_id: item.goods_id,
goods_id: item.spu.id,
// goods_num: item.goods_num,
goods_num: item.count,
// 商品价格id真没有
// goods_sku_price_id: item.goods_sku_price_id,
});
});
// return;
if (goods_list.length === 0) {
sheep.$helper.toast('请选择商品');
return;
}
sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({
// order_type: 'goods',
// goods_list,
items,
// from: 'cart',
deliveryType: 1,
pointStatus: false,
}),
});
}
// 结算
async function onConfirm() {
const items = [];
state.selectedList = state.list.filter((item) => state.selectedIds.includes(item.id));
state.selectedList.map((item) => {
// 此处前端做出修改
items.push({
skuId: item.sku.id,
count: item.count,
cartId: item.id,
categoryId: item.spu.categoryId,
});
});
if (isEmpty(items)) {
sheep.$helper.toast('请先选择商品');
return;
}
await validateDeliveryType(state.selectedList.map((item) => item.spu).map((spu) => spu.id));
sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({
items,
}),
});
}
function onNumberChange(e, cartItem) {
if (e === 0) {
cart.delete(cartItem.id);
return;
}
if (cartItem.goods_num === e) return;
cartItem.goods_num = e;
cart.update({
goods_id: cartItem.id,
goods_num: e,
goods_sku_price_id: cartItem.goods_sku_price_id,
});
}
async function onDelete() {
cart.delete(state.selectedIds);
}
/**
* 校验配送方式冲突
*
* @param {string[]} spuIds - 商品ID数组
* @returns {Promise<void>}
* @throws {Error} 当配送方式冲突或获取商品信息失败时抛出错误
*/
async function validateDeliveryType(spuIds) {
// 获取商品信息
const { data: spuList } = await SpuApi.getSpuListByIds(spuIds.join(','));
if (isEmpty(spuList)) {
sheep.$helper.toast('未找到商品信息');
throw new Error('未找到商品信息');
}
// 获取所有商品的配送方式列表
const deliveryTypesList = spuList.map(item => item.deliveryTypes);
// 检查配送方式冲突
const hasConflict = checkDeliveryConflicts(deliveryTypesList);
if (hasConflict) {
sheep.$helper.toast('选中商品支持的配送方式冲突,不允许提交');
throw new Error('选中商品支持的配送方式冲突,不允许提交');
}
}
/**
* 检查配送方式列表中是否存在冲突
* @description
* 示例场景:
* A 商品支持:[快递, 自提]
* B 商品支持:[快递]
* C 商品支持:[自提]
*
* 对比结果:
* A 和 B:不冲突 (有交集:快递)
* A 和 C:不冲突 (有交集:自提)
* B 和 C:冲突 (无交集)
* @param {Array<Array<number>>} deliveryTypesList - 配送方式列表的数组
* @returns {boolean} 是否存在冲突
*/
function checkDeliveryConflicts(deliveryTypesList) {
for (let i = 0; i < deliveryTypesList.length - 1; i++) {
const currentTypes = deliveryTypesList[i];
for (let j = i + 1; j < deliveryTypesList.length; j++) {
const nextTypes = deliveryTypesList[j];
// 检查是否没有交集(即冲突)
const hasNoIntersection = !currentTypes.some(type =>
nextTypes.includes(type),
);
if (hasNoIntersection) {
return true;
}
}
}
return false;
}
function onNumberChange(e, cartItem) {
if (e === 0) {
cart.delete(cartItem.id);
return;
}
if (cartItem.goods_num === e) return;
cartItem.goods_num = e;
cart.update({
goods_id: cartItem.id,
goods_num: e,
goods_sku_price_id: cartItem.goods_sku_price_id,
});
}
async function onDelete() {
cart.delete(state.selectedIds);
}
</script>
<style lang="scss" scoped>
:deep(.ui-fixed) {
height: 72rpx;
}
:deep(.ui-fixed) {
height: 72rpx;
}
.cart-box {
width: 100%;
.cart-box {
width: 100%;
.cart-header {
height: 70rpx;
background-color: #f6f6f6;
width: 100%;
position: fixed;
left: 0;
top: v-bind('sys_navBar') rpx;
z-index: 1000;
box-sizing: border-box;
}
.cart-header {
height: 70rpx;
background-color: #f6f6f6;
width: 100%;
position: fixed;
left: 0;
top: v-bind('sys_navBar') rpx;
z-index: 1000;
box-sizing: border-box;
}
.cart-footer {
height: 100rpx;
background-color: #fff;
.cart-footer {
height: 100rpx;
background-color: #fff;
.pay-btn {
width: 180rpx;
height: 70rpx;
font-size: 28rpx;
line-height: 28rpx;
font-weight: 500;
border-radius: 40rpx;
}
}
.pay-btn {
width: 180rpx;
height: 70rpx;
font-size: 28rpx;
line-height: 28rpx;
font-weight: 500;
border-radius: 40rpx;
}
}
.cart-content {
margin-top: 70rpx;
.cart-content {
margin-top: 70rpx;
.goods-box {
background-color: #fff;
}
}
}
</style>
.goods-box {
background-color: #fff;
}
}
}
</style>
+8 -7
View File
@@ -1,6 +1,6 @@
<!-- 商品分类列表 -->
<template>
<s-layout title="分类" tabbar="/pages/index/category" :bgStyle="{ color: '#fff' }">
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/category" title="分类">
<view class="s-category">
<view class="three-level-wrap ss-flex ss-col-top" :style="[{ height: pageHeight + 'px' }]">
<!-- 商品分类(左) -->
@@ -63,7 +63,7 @@
import SpuApi from '@/sheep/api/product/spu';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import { handleTree } from '@/sheep/util';
const state = reactive({
@@ -131,12 +131,13 @@
getGoodsList();
}
onLoad(async () => {
onLoad(async (params) => {
await getList();
// 如果是 first 风格,需要加载商品分页
if (state.style === 'first_one' || state.style === 'first_two') {
onMenu(0);
}
// 首页点击分类的处理:查找满足条件的分类
const foundCategory = state.categoryList.find(category => category.id === params.id);
// 如果找到则调用 onMenu 自动勾选相应分类,否则调用 onMenu(0) 勾选第一个分类
onMenu(foundCategory ? state.categoryList.indexOf(foundCategory) : 0);
});
onReachBottom(() => {
+1 -1
View File
@@ -8,7 +8,7 @@
<image class="goods-img" :src="item.picUrl" mode="aspectFit" />
</view>
<view class="goods-content">
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.title }}</view>
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.name }}</view>
<view class="goods-price">¥{{ fen2yuan(item.price) }}</view>
</view>
</view>
+1 -1
View File
@@ -1,6 +1,6 @@
<!-- 搜索界面 -->
<template>
<s-layout class="set-wrap" title="搜索" :bgStyle="{ color: '#FFF' }">
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="搜索">
<view class="ss-p-x-24">
<view class="ss-flex ss-col-center">
<uni-search-bar
+282
View File
@@ -0,0 +1,282 @@
<!-- 下单界面,收货地址 or 自提门店的选择组件 -->
<template>
<view class="allAddress" :style="state.isPickUp ? '' : 'padding-top:10rpx;'">
<view class="nav flex flex-wrap">
<view
class="item font-color"
:class="state.deliveryType === 1 ? 'on' : 'on2'"
@tap="switchDeliveryType(1)"
v-if="state.isPickUp"
/>
<view
class="item font-color"
:class="state.deliveryType === 2 ? 'on' : 'on2'"
@tap="switchDeliveryType(2)"
v-if="state.isPickUp"
/>
</view>
<!-- 情况一:收货地址的选择 -->
<view
class="address flex flex-wrap flex-center ss-row-between"
@tap="onSelectAddress"
v-if="state.deliveryType === 1"
:style="state.isPickUp ? '' : 'border-top-left-radius: 14rpx;border-top-right-radius: 14rpx;'"
>
<view class="addressCon" v-if="state.addressInfo.name">
<view class="name"
>{{ state.addressInfo.name }}
<text class="phone">{{ state.addressInfo.mobile }}</text>
</view>
<view class="flex flex-wrap">
<text class="default font-color" v-if="state.addressInfo.defaultStatus">[默认]</text>
<text class="line2">
{{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}
</text>
</view>
</view>
<view class="addressCon" v-else>
<view class="setaddress">设置收货地址</view>
</view>
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<!-- 情况二:门店的选择 -->
<view
class="address flex flex-wrap flex-center ss-row-between"
v-if="state.deliveryType === 2"
@tap="onSelectAddress"
>
<view class="addressCon" v-if="state.pickUpInfo.name">
<view class="name"
>{{ state.pickUpInfo.name }}
<text class="phone">{{ state.pickUpInfo.phone }}</text>
</view>
<view class="line1">
{{ state.pickUpInfo.areaName }}{{ ', ' + state.pickUpInfo.detailAddress }}
</view>
</view>
<view class="addressCon" v-else>
<view class="setaddress">选择自提门店</view>
</view>
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<view class="line">
<image :src="sheep.$url.static('/static/images/line.png', 'local')" />
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
import sheep from '@/sheep';
import { isEmpty } from 'lodash-es';
const props = defineProps({
modelValue: {
type: Object,
default() {},
},
});
const emits = defineEmits(['update:modelValue']);
// computed 解决父子组件双向数据同步
const state = computed({
get() {
return new Proxy(props.modelValue, {
set(obj, name, val) {
emits('update:modelValue', {
...obj,
[name]: val,
});
return true;
},
});
},
set(val) {
emits('update:modelValue', val);
},
});
// 选择地址
function onSelectAddress() {
let emitName = 'SELECT_ADDRESS';
let addressPage = '/pages/user/address/list?type=select';
if (state.value.deliveryType === 2) {
emitName = 'SELECT_PICK_UP_INFO';
addressPage = '/pages/user/goods_details_store/index';
}
uni.$once(emitName, (e) => {
changeConsignee(e.addressInfo);
});
sheep.$router.go(addressPage);
}
// 更改收货人地址&计算订单信息
async function changeConsignee(addressInfo = {}) {
if (!isEmpty(addressInfo)) {
if (state.value.deliveryType === 1) {
state.value.addressInfo = addressInfo;
}
if (state.value.deliveryType === 2) {
state.value.pickUpInfo = addressInfo;
}
}
}
// 收货方式切换
const switchDeliveryType = (type) => {
state.value.deliveryType = type;
};
</script>
<style scoped lang="scss">
.allAddress .font-color {
color: #e93323 !important;
}
.line2 {
width: 504rpx;
}
.textR {
text-align: right;
}
.line {
width: 100%;
height: 3rpx;
}
.line image {
width: 100%;
height: 100%;
display: block;
}
.address {
padding: 28rpx;
background-color: #fff;
box-sizing: border-box;
}
.address .addressCon {
width: 596rpx;
font-size: 26rpx;
color: #666;
}
.address .addressCon .name {
font-size: 30rpx;
color: #282828;
font-weight: bold;
margin-bottom: 10rpx;
}
.address .addressCon .name .phone {
margin-left: 50rpx;
}
.address .addressCon .default {
margin-right: 12rpx;
}
.address .addressCon .setaddress {
color: #333;
font-size: 28rpx;
}
.address .iconfont {
font-size: 35rpx;
color: #707070;
}
.allAddress {
width: 100%;
background: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: -webkit-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: -moz-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
//padding: 100rpx 30rpx 0 30rpx;
padding-top: 100rpx;
padding-bottom: 10rpx;
}
.allAddress .nav {
width: 690rpx;
margin: 0 auto;
}
.allAddress .nav .item {
width: 334rpx;
}
.allAddress .nav .item.on {
position: relative;
width: 230rpx;
}
.allAddress .nav .item.on::before {
position: absolute;
bottom: 0;
content: '快递配送';
font-size: 28rpx;
display: block;
height: 0;
width: 336rpx;
border-width: 0 20rpx 80rpx 0;
border-style: none solid solid;
border-color: transparent transparent #fff;
z-index: 2;
border-radius: 14rpx 36rpx 0 0;
text-align: center;
line-height: 80rpx;
}
.allAddress .nav .item:nth-of-type(2).on::before {
content: '到店自提';
border-width: 0 0 80rpx 20rpx;
border-radius: 36rpx 14rpx 0 0;
}
.allAddress .nav .item.on2 {
position: relative;
}
.allAddress .nav .item.on2::before {
position: absolute;
bottom: 0;
content: '到店自提';
font-size: 28rpx;
display: block;
height: 0;
width: 401rpx;
border-width: 0 0 60rpx 60rpx;
border-style: none solid solid;
border-color: transparent transparent #f7c1bd;
border-radius: 36rpx 14rpx 0 0;
text-align: center;
line-height: 60rpx;
}
.allAddress .nav .item:nth-of-type(1).on2::before {
content: '快递配送';
border-width: 0 60rpx 60rpx 0;
border-radius: 14rpx 36rpx 0 0;
}
.allAddress .address {
width: 690rpx;
max-height: 180rpx;
margin: 0 auto;
}
.allAddress .line {
width: 100%;
margin: 0 auto;
}
</style>
+5 -9
View File
@@ -98,11 +98,7 @@
</view>
<view class="modal-content content_box">
<radio-group @change="onChange">
<label
class="radio ss-flex ss-col-center"
v-for="item in state.reasonList"
:key="item"
>
<label class="radio ss-flex ss-col-center" v-for="item in state.reasonList" :key="item">
<view class="ss-flex-1 ss-p-20">{{ item }}</view>
<radio
:value="item"
@@ -153,9 +149,9 @@
],
reasonList: [], // 可选的申请原因数组
showModal: false, // 是否显示申请原因弹窗
currentValue: '' // 当前选择的售后原因
currentValue: '', // 当前选择的售后原因
});
const formData = reactive({
let formData = reactive({
way: '',
applyReason: '',
applyDescription: '',
@@ -175,7 +171,7 @@
uni.showToast({
title: '申请成功',
});
sheep.$router.go('/pages/order/aftersale/list');
sheep.$router.redirect('/pages/order/aftersale/list');
}
}
@@ -212,7 +208,7 @@
state.itemId = parseInt(options.itemId);
// 读取订单信息
const { code, data } = await OrderApi.getOrder(state.orderId);
const { code, data } = await OrderApi.getOrderDetail(state.orderId);
if (code !== 0) {
return;
}
+286 -249
View File
@@ -1,156 +1,193 @@
<!-- 售后详情 -->
<template>
<s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'">
<view class="content_box" v-if="!isEmpty(state.info) && state.loading">
<!-- 步骤条 -->
<view class="steps-box ss-flex" :style="[
<s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'">
<view class="content_box" v-if="!isEmpty(state.info) && state.loading">
<!-- 步骤条 -->
<view
class="steps-box ss-flex"
:style="[
{
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
paddingTop: Number(statusBarHeight + 88) + 'rpx',
},
]">
<view class="ss-flex">
<view class="steps-item" v-for="(item, index) in state.list" :key="index">
<view class="ss-flex">
<text class="sicon-circleclose"
v-if="state.list.length - 1 === index && [61, 62, 63].includes(state.info.status)" />
<text class="sicon-circlecheck" v-else
:class="state.active >= index ? 'activity-color' : 'info-color'" />
]"
>
<view class="ss-flex">
<view class="steps-item" v-for="(item, index) in state.list" :key="index">
<view class="ss-flex">
<text
class="sicon-circleclose"
v-if="state.list.length - 1 === index && [61, 62, 63].includes(state.info.status)"
/>
<text
class="sicon-circlecheck"
v-else
:class="state.active >= index ? 'activity-color' : 'info-color'"
/>
<view v-if="state.list.length - 1 !== index" class="line"
:class="state.active >= index ? 'activity-bg' : 'info-bg'" />
</view>
<view class="steps-item-title" :class="state.active >= index ? 'activity-color' : 'info-color'">
{{ item.title }}
</view>
</view>
</view>
</view>
<view
v-if="state.list.length - 1 !== index"
class="line"
:class="state.active >= index ? 'activity-bg' : 'info-bg'"
/>
</view>
<view
class="steps-item-title"
:class="state.active >= index ? 'activity-color' : 'info-color'"
>
{{ item.title }}
</view>
</view>
</view>
</view>
<!-- 服务状态 -->
<view class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
@tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.id })">
<view class="">
<view class="status-text">
<!-- 服务状态 -->
<view
class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
@tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.id })"
>
<view class="">
<view class="status-text">
{{ formatAfterSaleStatusDescription(state.info) }}
</view>
<view class="status-time">
<view class="status-time">
{{ sheep.$helper.timeFormat(state.info.updateTime, 'yyyy-mm-dd hh:MM:ss') }}
</view>
</view>
<text class="ss-iconfont _icon-forward" style="color: #666" />
</view>
</view>
<text class="ss-iconfont _icon-forward" style="color: #666" />
</view>
<!-- 退款金额 -->
<view class="aftersale-money ss-flex ss-col-center ss-row-between">
<view class="aftersale-money--title">退款总额</view>
<view class="aftersale-money--num">¥{{ fen2yuan(state.info.refundPrice) }}</view>
</view>
<!-- 服务商品 -->
<view class="order-shop">
<s-goods-item
:img=" state.info.picUrl"
:title=" state.info.spuName"
:titleWidth="480"
<!-- 退款金额 -->
<view class="aftersale-money ss-flex ss-col-center ss-row-between">
<view class="aftersale-money--title">退款总额</view>
<view class="aftersale-money--num">¥{{ fen2yuan(state.info.refundPrice) }}</view>
</view>
<!-- 服务商品 -->
<view class="order-shop">
<s-goods-item
:img="state.info.picUrl"
:title="state.info.spuName"
:titleWidth="480"
:skuText="state.info.properties.map((property) => property.valueName).join(' ')"
:num=" state.info.count"
:num="state.info.count"
/>
</view>
</view>
<!-- 服务内容 -->
<view class="aftersale-content">
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">服务单号:</view>
<view class="item-content ss-m-r-16">{{ state.info.no }}</view>
<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请时间:</view>
<view class="item-content">
{{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">售后类型:</view>
<view class="item-content">{{ state.info.way === 10 ? '仅退款' : '退款退货' }}</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请原因:</view>
<view class="item-content">{{ state.info.applyReason }}</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">相关描述:</view>
<view class="item-content">{{ state.info.applyDescription }}</view>
</view>
</view>
</view>
<!-- 服务内容 -->
<view class="aftersale-content">
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">服务单号:</view>
<view class="item-content ss-m-r-16">{{ state.info.no }}</view>
<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请时间:</view>
<view class="item-content">
{{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">售后类型:</view>
<view class="item-content">{{ state.info.way === 10 ? '仅退款' : '退款退货' }}</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请原因:</view>
<view class="item-content">{{ state.info.applyReason }}</view>
</view>
<view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">相关描述:</view>
<view class="item-content">{{ state.info.applyDescription }}</view>
</view>
</view>
</view>
<!-- 操作区 -->
<s-empty v-if="isEmpty(state.info) && state.loading" icon="/static/order-empty.png" text="暂无该订单售后详情" />
<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
<view class="foot_box">
<button class="ss-reset-button btn" v-if="state.info.buttons?.includes('cancel')"
@tap="onApply(state.info.id)">
<s-empty
v-if="isEmpty(state.info) && state.loading"
icon="/static/order-empty.png"
text="暂无该订单售后详情"
/>
<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
<view class="foot_box">
<button
class="ss-reset-button btn"
v-if="state.info.buttons?.includes('cancel')"
@tap="onApply(state.info.id)"
>
取消申请
</button>
<button class="ss-reset-button btn" v-if="state.info.buttons?.includes('delivery')"
@tap="sheep.$router.go('/pages/order/aftersale/return-delivery', { id: state.info.id })">
<button
class="ss-reset-button btn"
v-if="state.info.buttons?.includes('delivery')"
@tap="sheep.$router.go('/pages/order/aftersale/return-delivery', { id: state.info.id })"
>
填写退货
</button>
<button class="ss-reset-button contcat-btn btn" @tap="sheep.$router.go('/pages/chat/index')">
<button
class="ss-reset-button contcat-btn btn"
@tap="sheep.$router.go('/pages/chat/index')"
>
联系客服
</button>
</view>
</su-fixed>
</s-layout>
</view>
</su-fixed>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import { isEmpty } from 'lodash';
import { fen2yuan, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import { isEmpty } from 'lodash-es';
import {
fen2yuan,
formatAfterSaleStatusDescription,
handleAfterSaleButtons,
} from '@/sheep/hooks/useGoods';
import AfterSaleApi from '@/sheep/api/trade/afterSale';
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
const state = reactive({
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
const state = reactive({
id: 0, // 售后编号
info: {}, // 收货信息
loading: false,
loading: false,
active: 0, // 在 list 的激活位置
list: [{
title: '提交申请',
}, {
title: '处理中',
}, {
title: '完成'
}], // 时间轴
});
list: [
{
title: '提交申请',
},
{
title: '处理中',
},
{
title: '完成',
},
], // 时间轴
});
function onApply(id) {
uni.showModal({
title: '提示',
content: '确定要取消此申请吗?',
success: async function(res) {
if (!res.confirm) {
return;
}
function onApply(id) {
uni.showModal({
title: '提示',
content: '确定要取消此申请吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
const { code } = await AfterSaleApi.cancelAfterSale(id);
if (code === 0) {
await getDetail(id);
}
},
});
}
},
});
}
// 复制
const onCopy = () => {
sheep.$helper.copyText(state.info.no);
};
sheep.$helper.copyText(state.info.no);
};
async function getDetail(id) {
async function getDetail(id) {
state.loading = true;
const { code, data } = await AfterSaleApi.getAfterSale(id);
if (code !== 0) {
@@ -170,173 +207,173 @@
} else if ([61, 62, 63].includes(state.info.status)) {
state.active = 2;
}
}
}
onLoad((options) => {
onLoad((options) => {
if (!options.id) {
sheep.$helper.toast(`缺少订单信息,请检查`);
return
return;
}
state.id = options.id;
getDetail(options.id);
});
state.id = options.id;
getDetail(options.id);
});
</script>
<style lang="scss" scoped>
// 步骤条
.steps-box {
width: 100%;
height: 190rpx;
background: v-bind(headerBg) no-repeat,
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
background-size: 750rpx 100%;
padding-left: 72rpx;
// 步骤条
.steps-box {
width: 100%;
height: 190rpx;
background: v-bind(headerBg) no-repeat,
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
background-size: 750rpx 100%;
padding-left: 72rpx;
.steps-item {
.sicon-circleclose {
font-size: 24rpx;
color: #fff;
}
.steps-item {
.sicon-circleclose {
font-size: 24rpx;
color: #fff;
}
.sicon-circlecheck {
font-size: 24rpx;
}
.sicon-circlecheck {
font-size: 24rpx;
}
.steps-item-title {
font-size: 24rpx;
font-weight: 400;
margin-top: 16rpx;
margin-left: -36rpx;
width: 100rpx;
text-align: center;
}
}
}
.steps-item-title {
font-size: 24rpx;
font-weight: 400;
margin-top: 16rpx;
margin-left: -36rpx;
width: 100rpx;
text-align: center;
}
}
}
.activity-color {
color: #fff;
}
.activity-color {
color: #fff;
}
.info-color {
color: rgba(#fff, 0.4);
}
.info-color {
color: rgba(#fff, 0.4);
}
.activity-bg {
background: #fff;
}
.activity-bg {
background: #fff;
}
.info-bg {
background: rgba(#fff, 0.4);
}
.info-bg {
background: rgba(#fff, 0.4);
}
.line {
width: 270rpx;
height: 4rpx;
}
.line {
width: 270rpx;
height: 4rpx;
}
// 服务状态
.status-box {
position: relative;
z-index: 3;
background-color: #fff;
border-radius: 20rpx 20rpx 0px 0px;
padding: 20rpx;
margin-top: -20rpx;
// 服务状态
.status-box {
position: relative;
z-index: 3;
background-color: #fff;
border-radius: 20rpx 20rpx 0px 0px;
padding: 20rpx;
margin-top: -20rpx;
.status-text {
font-size: 28rpx;
.status-text {
font-size: 28rpx;
font-weight: 500;
color: rgba(51, 51, 51, 1);
margin-bottom: 20rpx;
}
font-weight: 500;
color: rgba(51, 51, 51, 1);
margin-bottom: 20rpx;
}
.status-time {
font-size: 24rpx;
.status-time {
font-size: 24rpx;
font-weight: 400;
color: rgba(153, 153, 153, 1);
}
}
font-weight: 400;
color: rgba(153, 153, 153, 1);
}
}
// 退款金额
.aftersale-money {
background-color: #fff;
height: 98rpx;
padding: 0 20rpx;
margin: 20rpx;
// 退款金额
.aftersale-money {
background-color: #fff;
height: 98rpx;
padding: 0 20rpx;
margin: 20rpx;
.aftersale-money--title {
font-size: 28rpx;
.aftersale-money--title {
font-size: 28rpx;
font-weight: 500;
color: rgba(51, 51, 51, 1);
}
font-weight: 500;
color: rgba(51, 51, 51, 1);
}
.aftersale-money--num {
font-size: 28rpx;
font-family: OPPOSANS;
font-weight: 500;
color: #ff3000;
}
}
.aftersale-money--num {
font-size: 28rpx;
font-family: OPPOSANS;
font-weight: 500;
color: #ff3000;
}
}
// order-shop
.order-shop {
padding: 20rpx;
background-color: #fff;
margin: 0 20rpx 20rpx 20rpx;
}
// order-shop
.order-shop {
padding: 20rpx;
background-color: #fff;
margin: 0 20rpx 20rpx 20rpx;
}
// 服务内容
.aftersale-content {
background-color: #fff;
padding: 20rpx;
margin: 0 20rpx;
// 服务内容
.aftersale-content {
background-color: #fff;
padding: 20rpx;
margin: 0 20rpx;
.aftersale-item {
height: 60rpx;
.aftersale-item {
height: 60rpx;
.copy-btn {
background: #eeeeee;
color: #333;
border-radius: 20rpx;
width: 75rpx;
height: 40rpx;
font-size: 22rpx;
}
.copy-btn {
background: #eeeeee;
color: #333;
border-radius: 20rpx;
width: 75rpx;
height: 40rpx;
font-size: 22rpx;
}
.item-title {
color: #999;
font-size: 28rpx;
}
.item-title {
color: #999;
font-size: 28rpx;
}
.item-content {
color: #333;
font-size: 28rpx;
}
}
}
.item-content {
color: #333;
font-size: 28rpx;
}
}
}
// 底部功能
.foot_box {
height: 100rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: flex-end;
// 底部功能
.foot_box {
height: 100rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: flex-end;
.btn {
width: 160rpx;
line-height: 60rpx;
background: rgba(238, 238, 238, 1);
border-radius: 30rpx;
padding: 0;
margin-right: 20rpx;
font-size: 26rpx;
.btn {
width: 160rpx;
line-height: 60rpx;
background: rgba(238, 238, 238, 1);
border-radius: 30rpx;
padding: 0;
margin-right: 20rpx;
font-size: 26rpx;
font-weight: 400;
color: rgba(51, 51, 51, 1);
}
}
</style>
font-weight: 400;
color: rgba(51, 51, 51, 1);
}
}
</style>
+160 -137
View File
@@ -1,187 +1,210 @@
<!-- 售后列表 -->
<template>
<s-layout title="售后列表">
<!-- tab -->
<su-sticky bgColor="#fff">
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" />
</su-sticky>
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据" />
<!-- 列表 -->
<view v-if="state.pagination.total > 0">
<view class="list-box ss-m-y-20" v-for="order in state.pagination.list" :key="order.id"
@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })">
<view class="order-head ss-flex ss-col-center ss-row-between">
<text class="no">服务单号:{{ order.no }}</text>
<text class="state">{{ formatAfterSaleStatus(order) }}</text>
</view>
<s-goods-item
<s-layout title="售后列表">
<!-- tab -->
<su-sticky bgColor="#fff">
<su-tabs
:list="tabMaps"
:scrollable="false"
@change="onTabsChange"
:current="state.currentTab"
/>
</su-sticky>
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据" />
<!-- 列表 -->
<view v-if="state.pagination.total > 0">
<view
class="list-box ss-m-y-20"
v-for="order in state.pagination.list"
:key="order.id"
@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })"
>
<view class="order-head ss-flex ss-col-center ss-row-between">
<text class="no">服务单号:{{ order.no }}</text>
<text class="state">{{ formatAfterSaleStatus(order) }}</text>
</view>
<s-goods-item
:img="order.picUrl"
:title="order.spuName"
:skuText="order.properties.map((property) => property.valueName).join(' ')"
:skuText="order.properties.map((property) => property.valueName).join(' ')"
:price="order.refundPrice"
/>
<view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20">
<view class="ss-flex ss-col-center">
<view class="title ss-m-r-20">{{ order.way === 10 ? '仅退款' : '退款退货' }}</view>
<view class="value">{{ formatAfterSaleStatusDescription(order) }}</view>
</view>
<text class="_icon-forward"></text>
</view>
<view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
<!-- TODO 功能缺失:填写退货信息 -->
<view>
<button class="ss-reset-button tool-btn" @tap.stop="onApply(order.id)"
v-if="order?.buttons.includes('cancel')">取消申请</button>
</view>
</view>
</view>
</view>
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
<view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20">
<view class="ss-flex ss-col-center">
<view class="title ss-m-r-20">{{ order.way === 10 ? '仅退款' : '退款退货' }}</view>
<view class="value">{{ formatAfterSaleStatusDescription(order) }}</view>
</view>
<text class="_icon-forward"></text>
</view>
<view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
<view>
<button
class="ss-reset-button tool-btn"
@tap.stop="onApply(order.id)"
v-if="order?.buttons.includes('cancel')"
>
取消申请
</button>
</view>
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}" @tap="loadMore" />
</s-layout>
}"
@tap="loadMore"
/>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash';
import { formatAfterSaleStatus, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods';
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import _ from 'lodash-es';
import {
formatAfterSaleStatus,
formatAfterSaleStatusDescription,
handleAfterSaleButtons,
} from '@/sheep/hooks/useGoods';
import AfterSaleApi from '@/sheep/api/trade/afterSale';
import { resetPagination } from '@/sheep/util';
const state = reactive({
currentTab: 0,
showApply: false,
pagination: {
const state = reactive({
currentTab: 0,
showApply: false,
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 10
},
loadStatus: '',
});
pageSize: 10,
},
loadStatus: '',
});
// TODO 芋艿:优化点,增加筛选
const tabMaps = [{
name: '全部',
value: 'all',
},
// {
// name: '申请中',
// value: 'nooper',
// },
// {
// name: '处理中',
// value: 'ing',
// },
// {
// name: '已完成',
// value: 'completed',
// },
// {
// name: '已拒绝',
// value: 'refuse',
// },
];
const tabMaps = [
{
name: '全部',
value: 'all',
},
// {
// name: '申请中',
// value: 'nooper',
// },
// {
// name: '处理中',
// value: 'ing',
// },
// {
// name: '已完成',
// value: 'completed',
// },
// {
// name: '已拒绝',
// value: 'refuse',
// },
];
// 切换选项卡
function onTabsChange(e) {
// 切换选项卡
function onTabsChange(e) {
resetPagination(state.pagination);
state.currentTab = e.index;
getOrderList();
}
state.currentTab = e.index;
getOrderList();
}
// 获取售后列表
async function getOrderList() {
state.loadStatus = 'loading';
let { data, code } = await AfterSaleApi.getAfterSalePage({
// type: tabMaps[state.currentTab].value,
// 获取售后列表
async function getOrderList() {
state.loadStatus = 'loading';
let { data, code } = await AfterSaleApi.getAfterSalePage({
// type: tabMaps[state.currentTab].value,
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
});
if (code !== 0) {
});
if (code !== 0) {
return;
}
data.list.forEach(order => handleAfterSaleButtons(order));
}
data.list.forEach((order) => handleAfterSaleButtons(order));
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
}
function onApply(orderId) {
uni.showModal({
title: '提示',
content: '确定要取消此申请吗?',
success: async function(res) {
if (!res.confirm) {
function onApply(orderId) {
uni.showModal({
title: '提示',
content: '确定要取消此申请吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
}
const { code } = await AfterSaleApi.cancelAfterSale(orderId);
if (code === 0) {
resetPagination(state.pagination);
await getOrderList();
}
},
});
}
},
});
}
onLoad(async (options) => {
if (options.type) {
state.currentTab = options.type;
}
await getOrderList();
});
onLoad(async (options) => {
if (options.type) {
state.currentTab = options.type;
}
await getOrderList();
});
// 加载更多
function loadMore() {
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return
return;
}
state.pagination.pageNo++;
getOrderList();
}
}
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
</script>
<style lang="scss" scoped>
.list-box {
background-color: #fff;
.list-box {
background-color: #fff;
.order-head {
padding: 0 25rpx;
height: 77rpx;
}
.order-head {
padding: 0 25rpx;
height: 77rpx;
}
.apply-box {
height: 82rpx;
.apply-box {
height: 82rpx;
.title {
font-size: 24rpx;
}
.title {
font-size: 24rpx;
}
.value {
font-size: 22rpx;
color: $dark-6;
}
}
.value {
font-size: 22rpx;
color: $dark-6;
}
}
.tool-btn-box {
height: 100rpx;
.tool-btn-box {
height: 100rpx;
.tool-btn {
width: 160rpx;
height: 60rpx;
background: #f6f6f6;
border-radius: 30rpx;
font-size: 26rpx;
font-weight: 400;
}
}
}
</style>
.tool-btn {
width: 160rpx;
height: 60rpx;
background: #f6f6f6;
border-radius: 30rpx;
font-size: 26rpx;
font-weight: 400;
}
}
}
</style>
+188 -67
View File
@@ -1,13 +1,7 @@
<template>
<s-layout title="确认订单">
<!-- TODO:这个判断先删除 v-if="state.orderInfo.need_address === 1" -->
<view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress">
<s-address-item :item="state.addressInfo" :hasBorderBottom="false">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</s-address-item>
</view>
<!-- 头部地址选择【配送地址】【自提地址】 -->
<AddressSelection v-model="addressState" />
<!-- 商品信息 -->
<view class="order-card-box ss-m-b-14">
@@ -46,26 +40,89 @@
</text>
</view>
</view>
<!-- TODO 芋艿:接入积分 -->
<view
v-if="state.orderPayload.pointActivityId"
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderPayload.order_type === 'score'"
>
<view class="item-title">扣除积分</view>
<view class="item-title">兑换积分</view>
<view class="ss-flex ss-col-center">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="score-img"
/>
<text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text>
<text class="item-value ss-m-r-24">
{{ state.orderInfo.usePoint }}
</text>
</view>
</view>
<view class="order-item ss-flex ss-col-center ss-row-between">
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.type === 0 || state.orderPayload.pointActivityId"
>
<view class="item-title">积分抵扣</view>
<view class="ss-flex ss-col-center">
{{ state.pointStatus || state.orderPayload.pointActivityId ? '剩余积分' : '当前积分' }}
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="score-img"
/>
<text class="item-value ss-m-r-24">
{{
state.pointStatus || state.orderPayload.pointActivityId
? state.orderInfo.totalPoint - state.orderInfo.usePoint
: state.orderInfo.totalPoint || 0
}}
</text>
<checkbox-group @change="changeIntegral" v-if="!state.orderPayload.pointActivityId">
<checkbox
:checked="state.pointStatus"
:disabled="!state.orderInfo.totalPoint || state.orderInfo.totalPoint <= 0"
/>
</checkbox-group>
</view>
</view>
<!-- 快递配置时,信息的展示 -->
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="addressState.deliveryType === 1"
>
<view class="item-title">运费</view>
<view class="ss-flex ss-col-center">
<text class="item-value ss-m-r-24">
<text class="item-value ss-m-r-24" v-if="state.orderInfo.price.deliveryPrice > 0">
+¥{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
</text>
<view class="item-value ss-m-r-24" v-else>免运费</view>
</view>
</view>
<!-- 门店自提时,需要填写姓名和手机号 -->
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="addressState.deliveryType === 2"
>
<view class="item-title">联系人</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="请填写您的联系姓名"
v-model="addressState.receiverName"
:inputBorder="false"
:clearable="false"
/>
</view>
</view>
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="addressState.deliveryType === 2"
>
<view class="item-title">联系电话</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="请填写您的联系电话"
v-model="addressState.receiverMobile"
:inputBorder="false"
:clearable="false"
/>
</view>
</view>
<!-- 优惠劵:只有 type = 0 普通订单(非拼团、秒杀、砍价),才可以使用优惠劵 -->
@@ -80,11 +137,17 @@
</text>
<text
class="item-value"
:class="state.couponInfo.length > 0 ? 'text-red' : 'text-disabled'"
:class="
state.couponInfo.filter((coupon) => coupon.match).length > 0
? 'text-red'
: 'text-disabled'
"
v-else
>
{{
state.couponInfo.length > 0 ? state.couponInfo.length + ' 张可用' : '暂无可用优惠券'
state.couponInfo.filter((coupon) => coupon.match).length > 0
? state.couponInfo.filter((coupon) => coupon.match).length + ' 张可用'
: '暂无可用优惠券'
}}
</text>
<text class="_icon-forward item-icon" />
@@ -95,8 +158,7 @@
v-if="state.orderInfo.price.discountPrice > 0"
>
<view class="item-title">活动优惠</view>
<view class="ss-flex ss-col-center">
<!-- @tap="state.showDiscount = true" TODO 芋艿:后续要把优惠信息打进去 -->
<view class="ss-flex ss-col-center" @tap="state.showDiscount = true">
<text class="item-value text-red">
-¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
</text>
@@ -120,7 +182,7 @@
共{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}件
</view>
<view>合计:</view>
<view class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }} </view>
<view class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}</view>
</view>
</view>
@@ -132,7 +194,7 @@
@close="state.showCoupon = false"
/>
<!-- 满额折扣弹框 TODO 芋艿:后续要把优惠信息打进去 -->
<!-- 满额折扣弹框 TODO @puhui999:【折扣】后续要把优惠信息打进去 -->
<s-discount-list
v-model="state.orderInfo"
:show="state.showDiscount"
@@ -159,14 +221,14 @@
</template>
<script setup>
import { reactive } from 'vue';
import { reactive, ref, watch } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import AddressSelection from '@/pages/order/addressSelection.vue';
import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import OrderApi from '@/sheep/api/trade/order';
import CouponApi from '@/sheep/api/promotion/coupon';
import TradeConfigApi from '@/sheep/api/trade/config';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
import { DeliveryTypeEnum } from '@/sheep/util/const';
const state = reactive({
orderPayload: {},
@@ -174,41 +236,62 @@
items: [], // 商品项列表
price: {}, // 价格信息
},
addressInfo: {}, // 选择的收货地址
showCoupon: false, // 是否展示优惠劵
couponInfo: [], // 优惠劵列表
showDiscount: false, // 是否展示营销活动
// ========== 积分 ==========
pointStatus: false, //是否使用积分
});
// 选择地址
function onSelectAddress() {
uni.$once('SELECT_ADDRESS', (e) => {
changeConsignee(e.addressInfo);
});
sheep.$router.go('/pages/user/address/list');
}
const addressState = ref({
addressInfo: {}, // 选择的收货地址
deliveryType: undefined, // 收货方式:1-快递配送,2-门店自提
isPickUp: true, // 门店自提是否开启
pickUpInfo: {}, // 选择的自提门店信息
receiverName: '', // 收件人名称
receiverMobile: '', // 收件人手机
});
// 更改收货人地址&计算订单信息
async function changeConsignee(addressInfo = {}) {
if (!isEmpty(addressInfo)) {
state.addressInfo = addressInfo;
}
// ========== 积分 ==========
/**
* 使用积分抵扣
*/
const changeIntegral = async () => {
state.pointStatus = !state.pointStatus;
await getOrderInfo();
}
};
// 选择优惠券
async function onSelectCoupon(couponId) {
state.orderPayload.couponId = couponId || 0;
state.orderPayload.couponId = couponId;
await getOrderInfo();
state.showCoupon = false;
}
// 提交订单
function onConfirm() {
if (!state.addressInfo.id) {
if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
sheep.$helper.toast('请选择收货地址');
return;
}
if (addressState.value.deliveryType === 2) {
if (!addressState.value.pickUpInfo.id) {
sheep.$helper.toast('请选择自提门店地址');
return;
}
if (addressState.value.receiverName === '' || addressState.value.receiverMobile === '') {
sheep.$helper.toast('请填写联系人或联系人电话');
return;
}
if (!/^[\u4e00-\u9fa5\w]{2,16}$/.test(addressState.value.receiverName)) {
sheep.$helper.toast('请填写您的真实姓名');
return;
}
if (!/^1(3|4|5|7|8|9|6)\d{9}$/.test(addressState.value.receiverMobile)) {
sheep.$helper.toast('请填写正确的手机号');
return;
}
}
submitOrder();
}
@@ -218,12 +301,16 @@
items: state.orderPayload.items,
couponId: state.orderPayload.couponId,
remark: state.orderPayload.remark,
addressId: state.addressInfo.id,
deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
pointStatus: false, // TODO 芋艿:需要支持【积分选择】
deliveryType: addressState.value.deliveryType,
addressId: addressState.value.addressInfo.id, // 收件地址编号
pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId
seckillActivityId: state.orderPayload.seckillActivityId,
pointActivityId: state.orderPayload.pointActivityId,
});
if (code !== 0) {
return;
@@ -234,9 +321,15 @@
}
// 跳转到支付页面
sheep.$router.redirect('/pages/pay/index', {
id: data.payOrderId,
});
if (data.payOrderId && data.payOrderId > 0) {
sheep.$router.redirect('/pages/pay/index', {
id: data.payOrderId,
});
} else {
sheep.$router.redirect('/pages/order/detail', {
id: data.id,
});
}
}
// 检查库存 & 计算订单价格
@@ -245,44 +338,72 @@
const { data, code } = await OrderApi.settlementOrder({
items: state.orderPayload.items,
couponId: state.orderPayload.couponId,
addressId: state.addressInfo.id,
deliveryType: 1, // TODO 芋艿:需要支持【门店自提】
pointStatus: false, // TODO 芋艿:需要支持【积分选择】
deliveryType: addressState.value.deliveryType,
addressId: addressState.value.addressInfo.id, // 收件地址编号
pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId
seckillActivityId: state.orderPayload.seckillActivityId,
pointActivityId: state.orderPayload.pointActivityId,
});
if (code !== 0) {
return;
return code;
}
state.orderInfo = data;
state.couponInfo = data.coupons || [];
// 设置收货地址
if (state.orderInfo.address) {
state.addressInfo = state.orderInfo.address;
}
}
// 获取可用优惠券
async function getCoupons() {
const { code, data } = await CouponApi.getMatchCouponList(
state.orderInfo.price.payPrice,
state.orderInfo.items.map((item) => item.spuId),
state.orderPayload.items.map((item) => item.skuId),
state.orderPayload.items.map((item) => item.categoryId),
);
if (code === 0) {
state.couponInfo = data;
addressState.value.addressInfo = state.orderInfo.address;
}
return code;
}
onLoad(async (options) => {
// 解析参数
if (!options.data) {
sheep.$helper.toast('参数不正确,请检查!');
return;
}
state.orderPayload = JSON.parse(options.data);
// 获取交易配置
const { data, code } = await TradeConfigApi.getTradeConfig();
if (code === 0) {
addressState.value.isPickUp = data.deliveryPickUpEnabled;
}
// 价格计算
// 情况一:先自动选择“快递物流”
addressState.value.deliveryType = DeliveryTypeEnum.EXPRESS.type;
let orderCode = await getOrderInfo();
if (orderCode === 0) {
return;
}
// 情况二:失败,再自动选择“门店自提”
if (addressState.value.isPickUp) {
addressState.value.deliveryType = DeliveryTypeEnum.PICK_UP.type;
let orderCode = await getOrderInfo();
if (orderCode === 0) {
return;
}
}
// 情况三:都失败,则不选择
addressState.value.deliveryType = undefined;
await getOrderInfo();
await getCoupons();
});
// 使用 watch 监听地址和配送方式的变化
watch(addressState, async (newAddress, oldAddress) => {
// 如果收货地址或配送方式有变化,则重新计算价格
if (
newAddress.addressInfo.id !== oldAddress.addressInfo.id ||
newAddress.deliveryType !== oldAddress.deliveryType
) {
await getOrderInfo();
}
});
</script>
+57 -17
View File
@@ -45,9 +45,9 @@
</image>
<view class="ss-font-30">{{ formatOrderStatus(state.orderInfo) }}</view>
</view>
<view class="ss-font-26 ss-m-x-20 ss-m-b-70">{{
formatOrderStatusDescription(state.orderInfo)
}}</view>
<view class="ss-font-26 ss-m-x-20 ss-m-b-70">
{{ formatOrderStatusDescription(state.orderInfo) }}
</view>
</view>
<!-- 收货地址 -->
@@ -126,6 +126,13 @@
</view>
</view>
<!-- 自提核销 -->
<PickUpVerify
:order-info="state.orderInfo"
:systemStore="systemStore"
ref="pickUpVerifyRef"
></PickUpVerify>
<!-- 订单信息 -->
<view class="notice-box">
<view class="notice-box__content">
@@ -167,11 +174,14 @@
<text class="title">运费</text>
<text class="detail">¥{{ fen2yuan(state.orderInfo.deliveryPrice) }}</text>
</view>
<!-- TODO 芋艿:优惠劵抵扣、积分抵扣 -->
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.couponPrice > 0">
<text class="title">优惠劵金额</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.couponPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.pointPrice > 0">
<text class="title">积分抵扣</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.pointPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.discountPrice > 0">
<text class="title">活动优惠</text>
<text class="detail">¥{{ fen2yuan(state.orderInfo.discountPrice) }}</text>
@@ -250,9 +260,9 @@
<script setup>
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import { isEmpty } from 'lodash';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue';
import { isEmpty } from 'lodash-es';
import {
fen2yuan,
formatOrderStatus,
@@ -260,6 +270,8 @@
handleOrderButtons,
} from '@/sheep/hooks/useGoods';
import OrderApi from '@/sheep/api/trade/order';
import DeliveryApi from '@/sheep/api/trade/delivery';
import PickUpVerify from '@/pages/order/pickUpVerify.vue';
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
@@ -270,6 +282,9 @@
comeinType: '', // 进入订单详情的来源类型
});
// ========== 门店自提(核销) ==========
const systemStore = ref({}); // 门店信息
// 复制
const onCopy = () => {
sheep.$helper.copyText(state.orderInfo.no);
@@ -313,7 +328,7 @@
});
}
// 确认收货 TODO 芋艿:待测试
// 确认收货
async function onConfirm(orderId, ignore = false) {
// 需开启确认收货组件
// todo: 芋艿:待接入微信
@@ -330,11 +345,20 @@
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(orderId);
if (code === 0) {
await getOrderDetail(orderId);
}
uni.showModal({
title: '提示',
content: '确认收货吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(orderId);
if (code === 0) {
await getOrderDetail(orderId);
}
},
});
}
// #ifdef MP-WEIXIN
@@ -366,34 +390,50 @@
},
});
}
// #endif
// 评价
function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', {
id
id,
});
}
const pickUpVerifyRef = ref();
async function getOrderDetail(id) {
// 对详情数据进行适配
let res;
if (state.comeinType === 'wechat') {
// TODO 芋艿:微信场景下
res = await OrderApi.getOrder(id, {
res = await OrderApi.getOrderDetail(id, {
merchant_trade_no: state.merchantTradeNo,
});
} else {
res = await OrderApi.getOrder(id);
res = await OrderApi.getOrderDetail(id);
}
if (res.code === 0) {
state.orderInfo = res.data;
handleOrderButtons(state.orderInfo);
// 配送方式:门店自提
if (res.data.pickUpStoreId) {
const { data } = await DeliveryApi.getDeliveryPickUpStore(res.data.pickUpStoreId);
systemStore.value = data || {};
}
if (state.orderInfo.deliveryType === 2 && state.orderInfo.payStatus) {
pickUpVerifyRef.value && pickUpVerifyRef.value.markCode(res.data.pickUpVerifyCode);
}
} else {
sheep.$router.back();
}
}
onShow(async () => {
//onShow中获取订单列表,保证跳转后页面为最新状态
await getOrderDetail(state.orderInfo.id);
})
onLoad(async (options) => {
let id = 0;
if (options.id) {
@@ -404,7 +444,7 @@
if (state.comeinType === 'wechat') {
state.merchantTradeNo = options.merchant_trade_no;
}
await getOrderDetail(id);
state.orderInfo.id = id
});
</script>
+10 -10
View File
@@ -12,11 +12,11 @@
</swiper>
</uni-swiper-dot>
<view class="log-card-msg">
<!-- TODO 芋艿:优化点:展示状态 -->
<!-- <view class="ss-flex ss-m-b-8">-->
<!-- <view>物流状态:</view>-->
<!-- <view class="warning-color">{{ state.info.status_text }}</view>-->
<!-- </view>-->
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
<!-- <view class="ss-flex ss-m-b-8">-->
<!-- <view>物流状态:</view>-->
<!-- <view class="warning-color">{{ state.info.status_text }}</view>-->
<!-- </view>-->
<view class="ss-m-b-8">快递单号:{{ state.info.logisticsNo }}</view>
<view>快递公司:{{ state.info.logisticsName }}</view>
</view>
@@ -34,10 +34,10 @@
<view v-if="state.tracks.length - 1 !== index" class="line" />
</view>
<view class="log-content-msg">
<!-- TODO 芋艿:优化点:展示状态 -->
<!-- <view class="log-msg-title ss-m-b-20">-->
<!-- {{ item.status_text }}-->
<!-- </view>-->
<!-- TODO 芋艿:【物流】优化点:展示状态 -->
<!-- <view class="log-msg-title ss-m-b-20">-->
<!-- {{ item.status_text }}-->
<!-- </view>-->
<view class="log-msg-desc ss-m-b-16">{{ item.content }}</view>
<view class="log-msg-date ss-m-b-40">
{{ sheep.$helper.timeFormat(item.time, 'yyyy-mm-dd hh:MM:ss') }}
@@ -78,7 +78,7 @@
}
async function getOrderDetail(id) {
const { data } = await OrderApi.getOrder(id)
const { data } = await OrderApi.getOrderDetail(id);
state.info = data;
}
+414 -363
View File
@@ -1,243 +1,293 @@
<!-- 订单列表 -->
<template>
<s-layout title="我的订单">
<su-sticky bgColor="#fff">
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" />
</su-sticky>
<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单" />
<view v-if="state.pagination.total > 0">
<view class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20" v-for="order in state.pagination.list"
:key="order.id" @tap="onOrderDetail(order.id)">
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
<view class="order-no">订单号:{{ order.no }}</view>
<view class="order-state ss-font-26" :class="formatOrderColor(order)">
<s-layout title="我的订单">
<su-sticky bgColor="#fff">
<su-tabs
:list="tabMaps"
:scrollable="false"
@change="onTabsChange"
:current="state.currentTab"
/>
</su-sticky>
<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单" />
<view v-if="state.pagination.total > 0">
<view
class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20"
v-for="order in state.pagination.list"
:key="order.id"
@tap="onOrderDetail(order.id)"
>
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
<view class="order-no">订单号:{{ order.no }}</view>
<view class="order-state ss-font-26" :class="formatOrderColor(order)">
{{ formatOrderStatus(order) }}
</view>
</view>
<view class="border-bottom" v-for="item in order.items" :key="item.id">
<s-goods-item
</view>
<view class="border-bottom" v-for="item in order.items" :key="item.id">
<s-goods-item
:img="item.picUrl"
:title="item.spuName"
:skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.price"
:skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.price"
:num="item.count"
/>
</view>
<view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20">
<view class="ss-flex ss-col-center">
<view class="discounts-title pay-color">共 {{ order.productCount }} 件商品,总金额:</view>
<view class="discounts-money pay-color">
¥{{ fen2yuan(order.payPrice) }}
</view>
</view>
</view>
<view class="order-card-footer ss-flex ss-col-center ss-p-x-20"
:class="order.buttons.length > 3 ? 'ss-row-between' : 'ss-row-right'">
<view class="ss-flex ss-col-center">
<button v-if="order.buttons.includes('combination')" class="tool-btn ss-reset-button"
@tap.stop="onOrderGroupon(order)">
</view>
<view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20">
<view class="ss-flex ss-col-center">
<view class="discounts-title pay-color"
>共 {{ order.productCount }} 件商品,总金额:</view
>
<view class="discounts-money pay-color"> ¥{{ fen2yuan(order.payPrice) }} </view>
</view>
</view>
<view
class="order-card-footer ss-flex ss-col-center ss-p-x-20"
:class="order.buttons.length > 3 ? 'ss-row-between' : 'ss-row-right'"
>
<view class="ss-flex ss-col-center">
<button
v-if="order.buttons.includes('combination')"
class="tool-btn ss-reset-button"
@tap.stop="onOrderGroupon(order)"
>
拼团详情
</button>
<button v-if="order.buttons.length === 0" class="tool-btn ss-reset-button"
@tap.stop="onOrderDetail(order.id)">
</button>
<button
v-if="order.buttons.length === 0"
class="tool-btn ss-reset-button"
@tap.stop="onOrderDetail(order.id)"
>
查看详情
</button>
<button v-if="order.buttons.includes('confirm')" class="tool-btn ss-reset-button"
@tap.stop="onConfirm(order)">
</button>
<button
v-if="order.buttons.includes('confirm')"
class="tool-btn ss-reset-button"
@tap.stop="onConfirm(order)"
>
确认收货
</button>
<button v-if="order.buttons.includes('express')" class="tool-btn ss-reset-button"
@tap.stop="onExpress(order.id)">
查看物流
</button>
<button v-if="order.buttons.includes('cancel')" class="tool-btn ss-reset-button"
@tap.stop="onCancel(order.id)">
取消订单
</button>
<button v-if="order.buttons.includes('comment')" class="tool-btn ss-reset-button"
@tap.stop="onComment(order.id)">
评价
</button>
<button v-if="order.buttons.includes('delete')" class="delete-btn ss-reset-button"
@tap.stop="onDelete(order.id)">
删除订单
</button>
<button v-if="order.buttons.includes('pay')" class="tool-btn ss-reset-button ui-BG-Main-Gradient"
@tap.stop="onPay(order.payOrderId)">
继续支付
</button>
</view>
</view>
</view>
</view>
</button>
<button
v-if="order.buttons.includes('express')"
class="tool-btn ss-reset-button"
@tap.stop="onExpress(order.id)"
>
查看物流
</button>
<button
v-if="order.buttons.includes('cancel')"
class="tool-btn ss-reset-button"
@tap.stop="onCancel(order.id)"
>
取消订单
</button>
<button
v-if="order.buttons.includes('comment')"
class="tool-btn ss-reset-button"
@tap.stop="onComment(order.id)"
>
评价
</button>
<button
v-if="order.buttons.includes('delete')"
class="delete-btn ss-reset-button"
@tap.stop="onDelete(order.id)"
>
删除订单
</button>
<button
v-if="order.buttons.includes('pay')"
class="tool-btn ss-reset-button ui-BG-Main-Gradient"
@tap.stop="onPay(order.payOrderId)"
>
继续支付
</button>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
<!-- 加载更多 -->
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}" @tap="loadMore" />
</s-layout>
}"
@tap="loadMore"
/>
</s-layout>
</template>
<script setup>
import { reactive } from 'vue';
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import { reactive } from 'vue';
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import {
fen2yuan,
formatOrderColor, formatOrderStatus, handleOrderButtons,
formatOrderColor,
formatOrderStatus,
handleOrderButtons,
} from '@/sheep/hooks/useGoods';
import sheep from '@/sheep';
import _ from 'lodash';
import {
isEmpty
} from 'lodash';
import sheep from '@/sheep';
import _ from 'lodash-es';
import { isEmpty } from 'lodash-es';
import OrderApi from '@/sheep/api/trade/order';
import { resetPagination } from '@/sheep/util';
// 数据
const state = reactive({
currentTab: 0, // 选中的 tabMaps 下标
pagination: {
// 数据
const state = reactive({
currentTab: 0, // 选中的 tabMaps 下标
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 5,
},
loadStatus: ''
});
},
loadStatus: '',
});
const tabMaps = [{
name: '全部'
},
{
name: '待付款',
value: 0,
},
{
name: '待发货',
value: 10,
},
{
name: '待收货',
value: 20,
},
{
name: '待评价',
value: 30,
},
];
const tabMaps = [
{
name: '全部',
},
{
name: '待付款',
value: 0,
},
{
name: '待发货',
value: 10,
},
{
name: '待收货',
value: 20,
},
{
name: '待评价',
value: 30,
},
];
// 切换选项卡
function onTabsChange(e) {
if (state.currentTab === e.index) {
// 切换选项卡
function onTabsChange(e) {
if (state.currentTab === e.index) {
return;
}
// 重头加载代码
resetPagination(state.pagination);
state.currentTab = e.index;
getOrderList();
}
resetPagination(state.pagination);
state.currentTab = e.index;
getOrderList();
}
// 订单详情
function onOrderDetail(id) {
sheep.$router.go('/pages/order/detail', {
id,
});
}
// 跳转拼团记录的详情
function onOrderGroupon(order) {
sheep.$router.go('/pages/activity/groupon/detail', {
id: order.combinationRecordId,
});
}
// 继续支付
function onPay(payOrderId) {
sheep.$router.go('/pages/pay/index', {
id: payOrderId,
});
}
// 评价
function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', {
id,
});
}
// 确认收货 TODO 芋艿:待测试
async function onConfirm(order, ignore = false) {
// 需开启确认收货组件
// todo: 芋艿:需要后续接入微信收货组件
// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
let isOpenBusinessView = true;
if (
sheep.$platform.name === 'WechatMiniProgram' &&
!isEmpty(order.wechat_extra_data) &&
isOpenBusinessView &&
!ignore
) {
mpConfirm(order);
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(order.id);
if (code === 0) {
resetPagination(state.pagination);
await getOrderList();
}
}
// #ifdef MP-WEIXIN
// 小程序确认收货组件 TODO 芋艿:后续再接入
function mpConfirm(order) {
if (!wx.openBusinessView) {
sheep.$helper.toast(`请升级微信版本`);
return;
}
wx.openBusinessView({
businessType: 'weappOrderConfirm',
extraData: {
merchant_id: '1481069012',
merchant_trade_no: order.wechat_extra_data.merchant_trade_no,
transaction_id: order.wechat_extra_data.transaction_id,
},
success(response) {
console.log('success:', response);
if (response.errMsg === 'openBusinessView:ok') {
if (response.extraData.status === 'success') {
onConfirm(order, true);
}
}
},
fail(error) {
console.log('error:', error);
},
complete(result) {
console.log('result:', result);
},
});
}
// #endif
// 查看物流
async function onExpress(id) {
sheep.$router.go('/pages/order/express/log', {
// 订单详情
function onOrderDetail(id) {
sheep.$router.go('/pages/order/detail', {
id,
});
}
});
}
// 取消订单
async function onCancel(orderId) {
uni.showModal({
title: '提示',
content: '确定要取消订单吗?',
success: async function(res) {
if (!res.confirm) {
// 跳转拼团记录的详情
function onOrderGroupon(order) {
sheep.$router.go('/pages/activity/groupon/detail', {
id: order.combinationRecordId,
});
}
// 继续支付
function onPay(payOrderId) {
sheep.$router.go('/pages/pay/index', {
id: payOrderId,
});
}
// 评价
function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', {
id,
});
}
// 确认收货 TODO 芋艿:待测试
async function onConfirm(order, ignore = false) {
// 需开启确认收货组件
// todo: 芋艿:需要后续接入微信收货组件
// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
let isOpenBusinessView = true;
if (
sheep.$platform.name === 'WechatMiniProgram' &&
!isEmpty(order.wechat_extra_data) &&
isOpenBusinessView &&
!ignore
) {
mpConfirm(order);
return;
}
uni.showModal({
title: '提示',
content: '确认收货吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(order.id);
if (code === 0) {
resetPagination(state.pagination);
await getOrderList();
}
},
});
}
// #ifdef MP-WEIXIN
// 小程序确认收货组件 TODO 芋艿:后续再接入
function mpConfirm(order) {
if (!wx.openBusinessView) {
sheep.$helper.toast(`请升级微信版本`);
return;
}
wx.openBusinessView({
businessType: 'weappOrderConfirm',
extraData: {
merchant_id: '1481069012',
merchant_trade_no: order.wechat_extra_data.merchant_trade_no,
transaction_id: order.wechat_extra_data.transaction_id,
},
success(response) {
console.log('success:', response);
if (response.errMsg === 'openBusinessView:ok') {
if (response.extraData.status === 'success') {
onConfirm(order, true);
}
}
},
fail(error) {
console.log('error:', error);
},
complete(result) {
console.log('result:', result);
},
});
}
// #endif
// 查看物流
async function onExpress(id) {
sheep.$router.go('/pages/order/express/log', {
id,
});
}
// 取消订单
async function onCancel(orderId) {
uni.showModal({
title: '提示',
content: '确定要取消订单吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
const { code } = await OrderApi.cancelOrder(orderId);
if (code === 0) {
// 修改数据的状态
@@ -246,208 +296,209 @@
orderInfo.status = 40;
handleOrderButtons(orderInfo);
}
},
});
}
},
});
}
// 删除订单
function onDelete(orderId) {
uni.showModal({
title: '提示',
content: '确定要删除订单吗?',
success: async function(res) {
if (res.confirm) {
const { code } = await OrderApi.deleteOrder(orderId);
if (code === 0) {
// 删除订单
function onDelete(orderId) {
uni.showModal({
title: '提示',
content: '确定要删除订单吗?',
success: async function (res) {
if (res.confirm) {
const { code } = await OrderApi.deleteOrder(orderId);
if (code === 0) {
// 删除数据
let index = state.pagination.list.findIndex((order) => order.id === orderId);
state.pagination.list.splice(index, 1);
}
}
},
});
}
let index = state.pagination.list.findIndex((order) => order.id === orderId);
state.pagination.list.splice(index, 1);
}
}
},
});
}
// 获取订单列表
async function getOrderList() {
state.loadStatus = 'loading';
let { code, data } = await OrderApi.getOrderPage({
// 获取订单列表
async function getOrderList() {
state.loadStatus = 'loading';
let { code, data } = await OrderApi.getOrderPage({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
status: tabMaps[state.currentTab].value,
commentStatus: tabMaps[state.currentTab].value === 30 ? false : null
});
commentStatus: tabMaps[state.currentTab].value === 30 ? false : null,
});
if (code !== 0) {
return;
}
data.list.forEach(order => handleOrderButtons(order));
state.pagination.list = _.concat(state.pagination.list, data.list)
data.list.forEach((order) => handleOrderButtons(order));
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
onLoad(async (options) => {
if (options.type) {
state.currentTab = options.type;
}
await getOrderList();
});
onLoad(async (options) => {
if (options.type) {
state.currentTab = options.type;
}
await getOrderList();
});
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return
}
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getOrderList();
}
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
// 上拉加载更多
onReachBottom(() => {
loadMore();
});
// 下拉刷新
onPullDownRefresh(() => {
// 下拉刷新
onPullDownRefresh(() => {
resetPagination(state.pagination);
getOrderList();
setTimeout(function() {
uni.stopPullDownRefresh();
}, 800);
});
getOrderList();
setTimeout(function () {
uni.stopPullDownRefresh();
}, 800);
});
</script>
<style lang="scss" scoped>
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.tool-btn {
width: 160rpx;
height: 60rpx;
background: #f6f6f6;
font-size: 26rpx;
border-radius: 30rpx;
margin-right: 10rpx;
.tool-btn {
width: 160rpx;
height: 60rpx;
background: #f6f6f6;
font-size: 26rpx;
border-radius: 30rpx;
margin-right: 10rpx;
&:last-of-type {
margin-right: 0;
}
}
&:last-of-type {
margin-right: 0;
}
}
.delete-btn {
width: 160rpx;
height: 56rpx;
color: #ff3000;
background: #fee;
border-radius: 28rpx;
font-size: 26rpx;
margin-right: 10rpx;
line-height: normal;
.delete-btn {
width: 160rpx;
height: 56rpx;
color: #ff3000;
background: #fee;
border-radius: 28rpx;
font-size: 26rpx;
margin-right: 10rpx;
line-height: normal;
&:last-of-type {
margin-right: 0;
}
}
&:last-of-type {
margin-right: 0;
}
}
.apply-btn {
width: 140rpx;
height: 50rpx;
border-radius: 25rpx;
font-size: 24rpx;
border: 2rpx solid #dcdcdc;
line-height: normal;
margin-left: 16rpx;
}
.apply-btn {
width: 140rpx;
height: 50rpx;
border-radius: 25rpx;
font-size: 24rpx;
border: 2rpx solid #dcdcdc;
line-height: normal;
margin-left: 16rpx;
}
.swiper-box {
flex: 1;
.swiper-box {
flex: 1;
.swiper-item {
height: 100%;
width: 100%;
}
}
.swiper-item {
height: 100%;
width: 100%;
}
}
.order-list-card-box {
.order-card-header {
height: 80rpx;
.order-list-card-box {
.order-card-header {
height: 80rpx;
.order-no {
font-size: 26rpx;
font-weight: 500;
}
.order-no {
font-size: 26rpx;
font-weight: 500;
}
.order-state {}
}
.order-state {
}
}
.pay-box {
.discounts-title {
font-size: 24rpx;
line-height: normal;
color: #999999;
}
.pay-box {
.discounts-title {
font-size: 24rpx;
line-height: normal;
color: #999999;
}
.discounts-money {
font-size: 24rpx;
line-height: normal;
color: #999;
font-family: OPPOSANS;
}
.discounts-money {
font-size: 24rpx;
line-height: normal;
color: #999;
font-family: OPPOSANS;
}
.pay-color {
color: #333;
}
}
.pay-color {
color: #333;
}
}
.order-card-footer {
height: 100rpx;
.order-card-footer {
height: 100rpx;
.more-item-box {
padding: 20rpx;
.more-item-box {
padding: 20rpx;
.more-item {
height: 60rpx;
.more-item {
height: 60rpx;
.title {
font-size: 26rpx;
}
}
}
.title {
font-size: 26rpx;
}
}
}
.more-btn {
color: $dark-9;
font-size: 24rpx;
}
.more-btn {
color: $dark-9;
font-size: 24rpx;
}
.content {
width: 154rpx;
color: #333333;
font-size: 26rpx;
font-weight: 500;
}
}
}
.content {
width: 154rpx;
color: #333333;
font-size: 26rpx;
font-weight: 500;
}
}
}
:deep(.uni-tooltip-popup) {
background: var(--ui-BG);
}
:deep(.uni-tooltip-popup) {
background: var(--ui-BG);
}
.warning-color {
color: #faad14;
}
.warning-color {
color: #faad14;
}
.danger-color {
color: #ff3000;
}
.danger-color {
color: #ff3000;
}
.success-color {
color: #52c41a;
}
.success-color {
color: #52c41a;
}
.info-color {
color: #999999;
}
</style>
.info-color {
color: #999999;
}
</style>
+260
View File
@@ -0,0 +1,260 @@
<template>
<view class='order-details'>
<!-- 自提商品核销 -->
<view v-if="orderInfo.deliveryType === 2 && orderInfo.payStatus" class="writeOff borRadius14">
<view class="title">核销信息</view>
<view class="grayBg flex-center">
<view class="pictrue">
<image
v-if="!!painterImageUrl"
:src="painterImageUrl"
:style="{width: `${state.qrcodeSize}px`, height: `${state.qrcodeSize}px`}"
:show-menu-by-longpress="true"
/>
</view>
</view>
<view class="gear">
<image :src="sheep.$url.static('/static/images/writeOff.png', 'local')"></image>
</view>
<view class="num">{{ orderInfo.pickUpVerifyCode }}</view>
<view class="rules">
<!-- TODO puhui999: 需要后端放回:使用 receiveTime 即可 -->
<view class="item">
<view class="rulesTitle flex flex-wrap align-center">
核销时间
</view>
<view class="info">
每日:
<text class="time">2020-2-+52</text>
</view>
</view>
<view class="item">
<view class="rulesTitle flex flex-wrap align-center">
<text class="iconfont icon-shuoming1"></text>
使用说明
</view>
<view class="info">可将二维码出示给店员扫描或提供数字核销码</view>
</view>
</view>
</view>
<view v-if="orderInfo.deliveryType === 2" class="map flex flex-wrap align-center ss-row-between borRadius14">
<view>自提地址信息</view>
<view class="place cart-color flex flex-wrap flex-center" @tap="showMaoLocation">
查看位置
</view>
</view>
<!-- 海报画板:默认隐藏只用来生成海报。生成方式为主动调用 -->
<l-painter
v-if="showPainter"
isCanvasToTempFilePath
pathType="url"
@success="setPainterImageUrl"
hidden
ref="painterRef"
/>
</view>
</template>
<script setup>
import sheep from '@/sheep';
import { reactive, ref } from 'vue';
const props = defineProps({
orderInfo: {
type: Object,
default() {},
},
systemStore:{
type: Object,
default() {},
}
});
const state = reactive({
qrcodeSize: 145
})
/**
* 打开地图
*/
const showMaoLocation = () => {
console.log(props.systemStore);
if (!props.systemStore.latitude || !props.systemStore.longitude) {
sheep.$helper.toast('缺少经纬度信息无法查看地图!');
return
}
uni.openLocation({
latitude: props.systemStore.latitude,
longitude: props.systemStore.longitude,
scale: 8,
name: props.systemStore.name,
address: props.systemStore.areaName + props.systemStore.detailAddress,
});
}
/**
* 拨打电话
*/
const makePhone = () => {
uni.makePhoneCall({
phoneNumber: props.systemStore.phone
})
}
const painterRef = ref(); // 海报画板
const painterImageUrl = ref(); // 海报 url
const showPainter = ref(true)
// 渲染海报
const renderPoster = async (poster) => {
await painterRef.value.render(poster);
};
// 获得生成的图片
const setPainterImageUrl = (path) => {
painterImageUrl.value = path;
showPainter.value = false
};
/**
* 生成核销二维码
*/
const markCode = (text) => {
renderPoster({
css: {
width: `${state.qrcodeSize}px`,
height: `${state.qrcodeSize}px`
},
views:[
{
type: 'qrcode',
text: text,
css: {
width: `${state.qrcodeSize}px`,
height: `${state.qrcodeSize}px`
}
}
]
})
}
defineExpose({
markCode
})
</script>
<style scoped lang="scss">
.borRadius14 {
border-radius: 14rpx !important;
}
.cart-color {
color: #E93323 !important;
border: 1px solid #E93323 !important
}
.order-details{
border-radius: 10rpx;
margin: 0 20rpx 20rpx 20rpx;
}
.order-details .writeOff {
background-color: #fff;
margin-top: 15rpx;
padding-bottom: 50rpx;
}
.order-details .writeOff .title {
font-size: 30rpx;
color: #282828;
height: 87rpx;
border-bottom: 1px solid #f0f0f0;
padding: 0 24rpx;
line-height: 87rpx;
}
.order-details .writeOff .grayBg {
background-color: #f2f5f7;
width: 590rpx;
height: 384rpx;
border-radius: 20rpx 20rpx 0 0;
margin: 50rpx auto 0 auto;
padding-top: 55rpx;
}
.order-details .writeOff .grayBg .pictrue {
width: 290rpx;
height: 290rpx;
}
.order-details .writeOff .grayBg .pictrue image {
width: 100%;
height: 100%;
}
.order-details .writeOff .gear {
width: 590rpx;
height: 30rpx;
margin: 0 auto;
}
.order-details .writeOff .gear image {
width: 100%;
height: 100%;
}
.order-details .writeOff .num {
background-color: #f0c34c;
width: 590rpx;
height: 84rpx;
color: #282828;
font-size: 48rpx;
margin: 0 auto;
border-radius: 0 0 20rpx 20rpx;
text-align: center;
padding-top: 4rpx;
}
.order-details .writeOff .rules {
margin: 46rpx 30rpx 0 30rpx;
border-top: 1px solid #f0f0f0;
padding-top: 10rpx;
}
.order-details .writeOff .rules .item {
margin-top: 20rpx;
}
.order-details .writeOff .rules .item .rulesTitle {
font-size: 28rpx;
color: #282828;
}
.order-details .writeOff .rules .item .rulesTitle .iconfont {
font-size: 30rpx;
color: #333;
margin-right: 8rpx;
margin-top: 5rpx;
}
.order-details .writeOff .rules .item .info {
font-size: 28rpx;
color: #999;
margin-top: 7rpx;
}
.order-details .writeOff .rules .item .info .time {
margin-left: 20rpx;
}
.order-details .map {
height: 86rpx;
font-size: 30rpx;
color: #282828;
line-height: 86rpx;
border-bottom: 1px solid #f0f0f0;
margin-top: 15rpx;
background-color: #fff;
padding: 0 24rpx;
}
.order-details .map .place {
font-size: 26rpx;
width: 176rpx;
height: 50rpx;
border-radius: 25rpx;
line-height: 50rpx;
text-align: center;
}
</style>
+31 -14
View File
@@ -81,7 +81,7 @@
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
import { getPayMethods, goPayResult } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
@@ -135,12 +135,22 @@
// 状态转换:payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
if (state.orderInfo.status === 10 || state.orderInfo.status === 20) {
// 支付成功
state.payStatus = 2;
// 跳转回支付成功页
uni.showModal({
title: '提示',
content: '订单已支付',
showCancel: false,
success: function () {
goPayResult(state.orderInfo.id, state.orderType);
},
});
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
if (state.orderInfo.status === 30) {
// 支付关闭
state.payStatus = -1;
return;
}
@@ -155,31 +165,39 @@
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
const { data, code } = await PayOrderApi.getOrder(id, true);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
// 获得支付方式
await setPayMethods();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId);
if (code !== 0) {
return
return;
}
state.payMethods = getPayMethods(data)
state.payMethods = getPayMethods(data);
state.payMethods.find((item) => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/index')) {
if (
sheep.$platform.name === 'WechatOfficialAccount' &&
sheep.$platform.os === 'ios' &&
!sheep.$platform.landingPage.includes('pages/pay/index')
) {
location.reload();
return;
}
@@ -208,7 +226,6 @@
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
+5 -3
View File
@@ -8,7 +8,7 @@
<view class="title">充值金额</view>
<view class="num" :class="item.refundStatus === 10 ? 'danger-color' : 'success-color'">
{{ fen2yuan(item.payPrice) }} 元
<text v-if="item.bonusPrice > 0">(赠送 {{ fen2yuan(item.bonusPrice)}} 元)</text>
<text v-if="item.bonusPrice > 0">(赠送 {{ fen2yuan(item.bonusPrice) }} 元)</text>
</view>
</view>
<view class="status-box item ss-flex ss-col-center ss-row-between">
@@ -30,7 +30,9 @@
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">充值时间</text>
<view class="time"> {{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view>
<view class="time">
{{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view
>
</view>
</view>
</view>
@@ -53,7 +55,7 @@
<script setup>
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash';
import _ from 'lodash-es';
import PayWalletApi from '@/sheep/api/pay/wallet';
import sheep from '@/sheep';
import { fen2yuan } from '../../sheep/hooks/useGoods';
+19 -14
View File
@@ -1,6 +1,6 @@
<!-- 支付结果页面 -->
<template>
<s-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
<s-layout :bgStyle="{ color: '#FFF' }" title="支付结果">
<view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
<!-- 信息展示 -->
<view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'" />
@@ -39,7 +39,6 @@
<button class="check-btn ss-reset-button" v-if="payResult === 'success'" @tap="onOrder">
查看订单
</button>
<!-- TODO 芋艿:拼团接入 -->
<button
class="check-btn ss-reset-button"
v-if="payResult === 'success' && state.tradeOrder.type === 3"
@@ -50,7 +49,10 @@
</view>
<!-- #ifdef MP -->
<view class="subscribe-box ss-flex ss-m-t-44" v-if="showSubscribeBtn && state.orderType === 'goods'">
<view
class="subscribe-box ss-flex ss-m-t-44"
v-if="showSubscribeBtn && state.orderType === 'goods'"
>
<image class="subscribe-img" :src="sheep.$url.static('/static/img/shop/order/cargo.png')" />
<view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view>
<view class="subscribe-start" @tap="subscribeMessage">立即订阅</view>
@@ -61,9 +63,9 @@
</template>
<script setup>
import { onLoad, onHide, onShow } from '@dcloudio/uni-app';
import { reactive, computed, ref } from 'vue';
import { isEmpty } from 'lodash';
import { onHide, onLoad, onShow } from '@dcloudio/uni-app';
import { computed, reactive, ref } from 'vue';
import { isEmpty } from 'lodash-es';
import sheep from '@/sheep';
import PayOrderApi from '@/sheep/api/pay/order';
import { fen2yuan } from '@/sheep/hooks/useGoods';
@@ -114,17 +116,20 @@
uni.showModal({
title: '支付结果',
showCancel: false, // 不要取消按钮
content: "支付成功",
content: '支付成功',
success: () => {
// 订阅只能由用户主动触发,只能包一层 showModal 诱导用户点击
autoSubscribeMessage();
}
})
},
});
// #endif
// 特殊:获得商品订单信息
if (state.orderType === 'goods') {
const { data, code } = await OrderApi.getOrder(state.orderInfo.merchantOrderId);
const { data, code } = await OrderApi.getOrderDetail(
state.orderInfo.merchantOrderId,
true,
);
if (code === 0) {
state.tradeOrder = data;
}
@@ -153,8 +158,8 @@
}
// #ifdef MP
const showSubscribeBtn = ref(false) // 默认隐藏
const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = "subscribe_btn_status"
const showSubscribeBtn = ref(false); // 默认隐藏
const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = 'subscribe_btn_status';
function subscribeMessage() {
if (state.orderType !== 'goods') {
return;
@@ -176,11 +181,11 @@
const subscribeBtnStatus = uni.getStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
if (!subscribeBtnStatus) {
showSubscribeBtn.value = true;
return
return;
}
// 2. 订阅消息
subscribeMessage()
subscribeMessage();
}
// #endif
+2 -2
View File
@@ -1,6 +1,6 @@
<!-- FAQ 常见问题 -->
<template>
<s-layout class="set-wrap" title="常见问题" :bgStyle="{ color: '#FFF' }">
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="常见问题">
<uni-collapse>
<uni-collapse-item v-for="(item, index) in state.list" :key="item">
<template v-slot:title>
@@ -49,7 +49,7 @@
}
}
onLoad(() => {
// TODO 芋艿:目前简单做,使用营销文章,作为 faq
// TODO 芋艿:【文章】目前简单做,使用营销文章,作为 faq
if (true) {
sheep.$router.go('/pages/public/richtext', {
title: '常见问题',
+1 -1
View File
@@ -1,6 +1,6 @@
<!-- 文章展示 -->
<template>
<s-layout class="set-wrap" :title="state.title" :bgStyle="{ color: '#FFF' }">
<s-layout :bgStyle="{ color: '#FFF' }" :title="state.title" class="set-wrap">
<view class="ss-p-30">
<mp-html class="richtext" :content="state.content" />
</view>
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<s-layout class="set-wrap" title="系统设置" :bgStyle="{ color: '#fff' }">
<s-layout :bgStyle="{ color: '#fff' }" class="set-wrap" title="系统设置">
<view class="header-box ss-flex-col ss-row-center ss-col-center">
<image
class="logo-img ss-m-b-46"
+213 -165
View File
@@ -1,78 +1,118 @@
<!-- 收货地址的新增/编辑 -->
<template>
<s-layout :title="state.model.id ? '编辑地址' : '新增地址'">
<uni-forms ref="addressFormRef" v-model="state.model" :rules="rules" validateTrigger="bind"
labelWidth="160" labelAlign="left" border :labelStyle="{ fontWeight: 'bold' }">
<view class="bg-white form-box ss-p-x-30">
<uni-forms-item name="name" label="收货人" class="form-item">
<uni-easyinput v-model="state.model.name" placeholder="请填写收货人姓名" :inputBorder="false"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" />
</uni-forms-item>
<s-layout :title="state.model.id ? '编辑地址' : '新增地址'">
<uni-forms
ref="addressFormRef"
v-model="state.model"
:rules="rules"
validateTrigger="bind"
labelWidth="160"
labelAlign="left"
border
:labelStyle="{ fontWeight: 'bold' }"
>
<view class="bg-white form-box ss-p-x-30">
<uni-forms-item name="name" label="收货人" class="form-item">
<uni-easyinput
v-model="state.model.name"
placeholder="请填写收货人姓名"
:inputBorder="false"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
/>
</uni-forms-item>
<uni-forms-item name="mobile" label="手机号" class="form-item">
<uni-easyinput v-model="state.model.mobile" type="number" placeholder="请输入手机号" :inputBorder="false"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal">
</uni-easyinput>
</uni-forms-item>
<uni-forms-item name="areaName" label="省市区" @tap="state.showRegion = true" class="form-item">
<uni-easyinput v-model="state.model.areaName" disabled :inputBorder="false"
:styles="{ disableColor: '#fff', color: '#333' }"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
placeholder="请选择省市区">
<template v-slot:right>
<uni-icons type="right" />
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item name="detailAddress" label="详细地址" :formItemStyle="{ alignItems: 'flex-start' }"
:labelStyle="{ lineHeight: '5em' }" class="textarea-item">
<uni-easyinput :inputBorder="false" type="textarea" v-model="state.model.detailAddress"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
placeholder="请输入详细地址" clearable />
</uni-forms-item>
</view>
<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box">
<view class="default-box-title"> 设为默认地址 </view>
<su-switch style="transform: scale(0.8)" v-model="state.model.defaultStatus" />
</view>
</uni-forms>
<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
<view class="footer-box ss-flex-col ss-row-between ss-p-20">
<view class="ss-m-b-20">
<uni-forms-item name="mobile" label="手机号" class="form-item">
<uni-easyinput
v-model="state.model.mobile"
type="number"
placeholder="请输入手机号"
:inputBorder="false"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item
name="areaName"
label="省市区"
@tap="state.showRegion = true"
class="form-item"
>
<uni-easyinput
v-model="state.model.areaName"
disabled
:inputBorder="false"
:styles="{ disableColor: '#fff', color: '#333' }"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
placeholder="请选择省市区"
>
<template v-slot:right>
<uni-icons type="right" />
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item
name="detailAddress"
label="详细地址"
:formItemStyle="{ alignItems: 'flex-start' }"
:labelStyle="{ lineHeight: '5em' }"
class="textarea-item"
>
<uni-easyinput
:inputBorder="false"
type="textarea"
v-model="state.model.detailAddress"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
placeholder="请输入详细地址"
clearable
/>
</uni-forms-item>
</view>
<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box">
<view class="default-box-title"> 设为默认地址 </view>
<su-switch style="transform: scale(0.8)" v-model="state.model.defaultStatus" />
</view>
</uni-forms>
<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
<view class="footer-box ss-flex-col ss-row-between ss-p-20">
<view class="ss-m-b-20">
<button class="ss-reset-button save-btn ui-Shadow-Main" @tap="onSave">保存</button>
</view>
<button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
删除
</button>
</view>
</su-fixed>
<button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
删除
</button>
</view>
</su-fixed>
<!-- 省市区弹窗 -->
<su-region-picker :show="state.showRegion" @cancel="state.showRegion = false" @confirm="onRegionConfirm" />
</s-layout>
<!-- 省市区弹窗 -->
<su-region-picker
:show="state.showRegion"
@cancel="state.showRegion = false"
@confirm="onRegionConfirm"
/>
</s-layout>
</template>
<script setup>
import { ref, reactive, unref } from 'vue';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import _ from 'lodash';
import { mobile } from '@/sheep/validate/form';
import { ref, reactive, unref } from 'vue';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import _ from 'lodash-es';
import { mobile } from '@/sheep/validate/form';
import AreaApi from '@/sheep/api/system/area';
import AddressApi from '@/sheep/api/member/address';
const addressFormRef = ref(null);
const state = reactive({
showRegion: false,
model: {
name: '',
mobile: '',
const addressFormRef = ref(null);
const state = reactive({
showRegion: false,
model: {
name: '',
mobile: '',
detailAddress: '',
defaultStatus: false,
defaultStatus: false,
areaName: '',
},
},
rules: {},
});
});
const rules = {
name: {
@@ -85,93 +125,99 @@
},
mobile,
detailAddress: {
rules: [{
required: true,
errorMessage: '请输入详细地址',
}]
rules: [
{
required: true,
errorMessage: '请输入详细地址',
},
],
},
areaName: {
rules: [{
required: true,
errorMessage: '请选择您的位置'
}]
rules: [
{
required: true,
errorMessage: '请选择您的位置',
},
],
},
};
// 确认选择地区
const onRegionConfirm = (e) => {
state.model.areaName = `${e.province_name} ${e.city_name} ${e.district_name}`
const onRegionConfirm = (e) => {
state.model.areaName = `${e.province_name} ${e.city_name} ${e.district_name}`;
state.model.areaId = e.district_id;
state.showRegion = false;
};
state.showRegion = false;
};
// 获得地区数据
const getAreaData = () => {
if (_.isEmpty(uni.getStorageSync('areaData'))) {
const getAreaData = () => {
if (_.isEmpty(uni.getStorageSync('areaData'))) {
AreaApi.getAreaTree().then((res) => {
if (res.code === 0) {
uni.setStorageSync('areaData', res.data);
}
});
}
};
}
};
// 保存收货地址
const onSave = async () => {
const onSave = async () => {
// 参数校验
const validate = await unref(addressFormRef)
.validate()
.catch((error) => {
console.log('error: ', error);
});
if (!validate) {
const validate = await unref(addressFormRef)
.validate()
.catch((error) => {
console.log('error: ', error);
});
if (!validate) {
return;
}
// 提交请求
const formData = {
...state.model
...state.model,
};
const { code } =
state.model.id > 0
? await AddressApi.updateAddress(formData)
: await AddressApi.createAddress(formData);
if (code === 0) {
sheep.$router.back();
}
const {code } = state.model.id > 0 ? await AddressApi.updateAddress(formData)
: await AddressApi.createAddress(formData);
if (code === 0) {
sheep.$router.back();
}
};
};
// 删除收货地址
const onDelete = () => {
uni.showModal({
title: '提示',
content: '确认删除此收货地址吗?',
success: async function(res) {
if (!res.confirm) {
return;
}
const onDelete = () => {
uni.showModal({
title: '提示',
content: '确认删除此收货地址吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
const { code } = await AddressApi.deleteAddress(state.model.id);
if (code === 0) {
sheep.$router.back();
}
},
});
};
},
});
};
onLoad(async (options) => {
onLoad(async (options) => {
// 获得地区数据
getAreaData();
getAreaData();
// 情况一:基于 id 获得收件地址
if (options.id) {
let { code, data} = await AddressApi.getAddress(options.id);
if (options.id) {
let { code, data } = await AddressApi.getAddress(options.id);
if (code !== 0) {
return;
}
state.model = data;
}
// 情况二:微信导入
}
// 情况二:微信导入
if (options.data) {
let data = JSON.parse(options.data);
const areaData = uni.getStorageSync('areaData');
const findAreaByName = (areas, name) => areas.find(item => item.name === name);
const findAreaByName = (areas, name) => areas.find((item) => item.name === name);
let provinceObj = findAreaByName(areaData, data.province_name);
let cityObj = provinceObj ? findAreaByName(provinceObj.children, data.city_name) : undefined;
@@ -181,77 +227,79 @@
state.model = {
...state.model,
areaId,
areaName: [data.province_name, data.city_name, data.district_name].filter(Boolean).join(" "),
areaName: [data.province_name, data.city_name, data.district_name]
.filter(Boolean)
.join(' '),
defaultStatus: false,
detailAddress: data.address,
mobile: data.mobile,
name: data.consignee,
};
}
});
}
});
</script>
<style lang="scss" scoped>
:deep() {
.uni-forms-item__label .label-text {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
}
:deep() {
.uni-forms-item__label .label-text {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
}
.uni-easyinput__content-input {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
padding-left: 0 !important;
}
.uni-easyinput__content-input {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
padding-left: 0 !important;
}
.uni-easyinput__content-textarea {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
margin-top: 8rpx !important;
}
.uni-easyinput__content-textarea {
font-size: 28rpx !important;
color: #333333 !important;
line-height: normal !important;
margin-top: 8rpx !important;
}
.uni-icons {
font-size: 40rpx !important;
}
.uni-icons {
font-size: 40rpx !important;
}
.is-textarea-icon {
margin-top: 22rpx;
}
.is-textarea-icon {
margin-top: 22rpx;
}
.is-disabled {
color: #333333;
}
}
.is-disabled {
color: #333333;
}
}
.default-box {
width: 100%;
box-sizing: border-box;
height: 100rpx;
.default-box {
width: 100%;
box-sizing: border-box;
height: 100rpx;
.default-box-title {
font-size: 28rpx;
color: #333333;
line-height: normal;
}
}
.default-box-title {
font-size: 28rpx;
color: #333333;
line-height: normal;
}
}
.footer-box {
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.footer-box {
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.cancel-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: var(--ui-BG);
}
}
</style>
.cancel-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: var(--ui-BG);
}
}
</style>
+147 -125
View File
@@ -1,143 +1,165 @@
<!-- 收件地址列表 -->
<template>
<s-layout title="收货地址" :bgStyle="{ color: '#FFF' }">
<view v-if="state.list.length">
<s-address-item hasBorderBottom v-for="item in state.list" :key="item.id" :item="item"
@tap="onSelect(item)" />
</view>
<s-layout :bgStyle="{ color: '#FFF' }" title="收货地址">
<view v-if="state.list.length">
<s-address-item
hasBorderBottom
v-for="item in state.list"
:key="item.id"
:item="item"
@tap="onSelect(item)"
/>
</view>
<su-fixed bottom placeholder>
<view class="footer-box ss-flex ss-row-between ss-p-20">
<!-- 微信小程序和微信H5 -->
<button v-if="['WechatMiniProgram', 'WechatOfficialAccount'].includes(sheep.$platform.name)"
@tap="importWechatAddress"
class="border ss-reset-button sync-wxaddress ss-m-20 ss-flex ss-row-center ss-col-center">
<text class="cicon-weixin ss-p-r-10" style="color: #09bb07; font-size: 40rpx"></text>
导入微信地址
</button>
<button class="add-btn ss-reset-button ui-Shadow-Main"
@tap="sheep.$router.go('/pages/user/address/edit')">
新增收货地址
</button>
</view>
</su-fixed>
<s-empty v-if="state.list.length === 0 && !state.loading" text="暂无收货地址" icon="/static/data-empty.png" />
</s-layout>
<su-fixed bottom placeholder>
<view class="footer-box ss-flex ss-row-between ss-p-20">
<!-- 微信小程序和微信H5 -->
<button
v-if="['WechatMiniProgram', 'WechatOfficialAccount'].includes(sheep.$platform.name)"
@tap="importWechatAddress"
class="border ss-reset-button sync-wxaddress ss-m-20 ss-flex ss-row-center ss-col-center"
>
<text class="cicon-weixin ss-p-r-10" style="color: #09bb07; font-size: 40rpx"></text>
导入微信地址
</button>
<button
class="add-btn ss-reset-button ui-Shadow-Main"
@tap="sheep.$router.go('/pages/user/address/edit')"
>
新增收货地址
</button>
</view>
</su-fixed>
<s-empty
v-if="state.list.length === 0 && !state.loading"
text="暂无收货地址"
icon="/static/data-empty.png"
/>
</s-layout>
</template>
<script setup>
import { reactive, onBeforeMount } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import { onBeforeMount, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { isEmpty } from 'lodash-es';
import AreaApi from '@/sheep/api/system/area';
import AddressApi from '@/sheep/api/member/address';
const state = reactive({
list: [], // 地址列表
loading: true,
});
const state = reactive({
list: [], // 地址列表
loading: true,
openType: '', // 页面打开类型
});
// 选择收货地址
const onSelect = (addressInfo) => {
uni.$emit('SELECT_ADDRESS', {
addressInfo,
});
sheep.$router.back();
};
// 选择收货地址
const onSelect = (addressInfo) => {
if (state.openType !== 'select'){ // 不作为选择组件时阻断操作
return
}
uni.$emit('SELECT_ADDRESS', {
addressInfo,
});
sheep.$router.back();
};
// 导入微信地址
// TODO 芋艿:未测试
function importWechatAddress() {
let wechatAddress = {};
// #ifdef MP
uni.chooseAddress({
success: (res) => {
wechatAddress = {
consignee: res.userName,
mobile: res.telNumber,
province_name: res.provinceName,
city_name: res.cityName,
district_name: res.countyName,
address: res.detailInfo,
region: '',
is_default: false,
};
if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress),
});
}
},
fail: (err) => {
console.log('%cuni.chooseAddress,调用失败', 'color:green;background:yellow');
},
});
// #endif
// #ifdef H5
sheep.$platform.useProvider('wechat').jssdk.openAddress({
success: (res) => {
wechatAddress = {
consignee: res.userName,
mobile: res.telNumber,
province_name: res.provinceName,
city_name: res.cityName,
district_name: res.countryName,
address: res.detailInfo,
region: '',
is_default: false,
};
if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress),
});
}
},
});
// #endif
}
// 导入微信地址
function importWechatAddress() {
let wechatAddress = {};
// #ifdef MP
uni.chooseAddress({
success: (res) => {
wechatAddress = {
consignee: res.userName,
mobile: res.telNumber,
province_name: res.provinceName,
city_name: res.cityName,
district_name: res.countyName,
address: res.detailInfo,
region: '',
is_default: false,
};
if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress),
});
}
},
fail: (err) => {
console.log('%cuni.chooseAddress,调用失败', 'color:green;background:yellow');
},
});
// #endif
// #ifdef H5
sheep.$platform.useProvider('wechat').jssdk.openAddress({
success: (res) => {
wechatAddress = {
consignee: res.userName,
mobile: res.telNumber,
province_name: res.provinceName,
city_name: res.cityName,
district_name: res.countryName,
address: res.detailInfo,
region: '',
is_default: false,
};
if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress),
});
}
},
});
// #endif
}
onShow(async () => {
state.list = (await AddressApi.getAddressList()).data;
state.loading = false;
});
onLoad((option) => {
if (option.type) {
state.openType = option.type;
}
});
onBeforeMount(() => {
if (!!uni.getStorageSync('areaData')) {
return;
}
// 提前加载省市区数据
onShow(async () => {
state.list = (await AddressApi.getAddressList()).data;
state.loading = false;
});
onBeforeMount(() => {
if (!!uni.getStorageSync('areaData')) {
return;
}
// 提前加载省市区数据
AreaApi.getAreaTree().then((res) => {
if (res.code === 0) {
uni.setStorageSync('areaData', res.data);
}
});
});
if (res.code === 0) {
uni.setStorageSync('areaData', res.data);
}
});
});
</script>
<style lang="scss" scoped>
.footer-box {
.add-btn {
flex: 1;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 80rpx;
font-size: 30rpx;
font-weight: 500;
line-height: 80rpx;
color: $white;
position: relative;
z-index: 1;
}
.footer-box {
.add-btn {
flex: 1;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 80rpx;
font-size: 30rpx;
font-weight: 500;
line-height: 80rpx;
color: $white;
position: relative;
z-index: 1;
}
.sync-wxaddress {
flex: 1;
line-height: 80rpx;
background: $white;
border-radius: 80rpx;
font-size: 30rpx;
font-weight: 500;
color: $dark-6;
margin-right: 18rpx;
}
}
</style>
.sync-wxaddress {
flex: 1;
line-height: 80rpx;
background: $white;
border-radius: 80rpx;
font-size: 30rpx;
font-weight: 500;
color: $dark-6;
margin-right: 18rpx;
}
}
</style>
+7 -5
View File
@@ -5,7 +5,8 @@
<!-- 头部 -->
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
<view class="header-left ss-flex ss-col-center ss-font-26">
共 <text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text> 件商品
共
<text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text> 件商品
</view>
<view class="header-right">
<button
@@ -77,7 +78,8 @@
<view class="footer-right">
<button
class="ss-reset-button ui-BG-Main-Gradient pay-btn ss-font-28 ui-Shadow-Main"
@tap="onCancel">
@tap="onCancel"
>
取消收藏
</button>
</view>
@@ -100,7 +102,7 @@
import sheep from '@/sheep';
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash';
import _ from 'lodash-es';
import FavoriteApi from '@/sheep/api/product/favorite';
import { resetPagination } from '@/sheep/util';
@@ -129,7 +131,7 @@
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list)
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
@@ -174,7 +176,7 @@
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return
return;
}
state.pagination.pageNo++;
getData();
+11 -9
View File
@@ -1,6 +1,6 @@
<!-- 商品浏览记录 -->
<template>
<s-layout title="我的足迹" :bgStyle="{ color: '#f2f2f2' }">
<s-layout :bgStyle="{ color: '#f2f2f2' }" title="我的足迹">
<view class="cart-box ss-flex ss-flex-col ss-row-between">
<!-- 头部 -->
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
@@ -81,11 +81,13 @@
</view>
<view class="footer-right ss-flex">
<button
:class="['ss-reset-button pay-btn ss-font-28 ',
{
'ui-BG-Main-Gradient': state.selectedSpuIdList.length > 0,
'ui-Shadow-Main': state.selectedSpuIdList.length > 0
}]"
:class="[
'ss-reset-button pay-btn ss-font-28 ',
{
'ui-BG-Main-Gradient': state.selectedSpuIdList.length > 0,
'ui-Shadow-Main': state.selectedSpuIdList.length > 0,
},
]"
@tap="onDelete"
>
删除足迹
@@ -120,9 +122,9 @@
import sheep from '@/sheep';
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash';
import SpuHistoryApi from "@/sheep/api/product/history";
import {cloneDeep} from "@/sheep/helper/utils";
import _ from 'lodash-es';
import SpuHistoryApi from '@/sheep/api/product/history';
import { cloneDeep } from '@/sheep/helper/utils';
const sys_navBar = sheep.$platform.navbar;
const pagination = {
+282
View File
@@ -0,0 +1,282 @@
<template>
<s-layout :bgStyle="{ color: '#FFF' }" title="选择自提门店">
<view class="storeBox" ref="container">
<view
class="storeBox-box"
v-for="(item, index) in state.storeList"
:key="index"
@tap="checked(item)"
>
<view class="store-img">
<image :src="item.logo" class="img" />
</view>
<view class="store-cent-left">
<view class="store-name">{{ item.name }}</view>
<view class="store-address line1">
{{ item.areaName }}{{ ', ' + item.detailAddress }}
</view>
</view>
<view class="row-right ss-flex-col ss-col-center">
<view>
<!-- #ifdef H5 -->
<a class="store-phone" :href="'tel:' + item.phone">
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</a>
<!-- #endif -->
<!-- #ifdef MP -->
<view class="store-phone" @click="call(item.phone)">
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<!-- #endif -->
</view>
<view class="store-distance ss-flex ss-row-center" @tap.stop="showMaoLocation(item)">
<text class="addressTxt" v-if="item.distance">
距离{{ item.distance.toFixed(2) }}千米
</text>
<text class="addressTxt" v-else>查看地图</text>
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
</view>
</view>
</view>
</s-layout>
</template>
<script setup>
import DeliveryApi from '@/sheep/api/trade/delivery';
import { onMounted, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
const LONGITUDE = 'user_longitude';
const LATITUDE = 'user_latitude';
const state = reactive({
loaded: false,
loading: false,
storeList: [],
system_store: {},
locationShow: false,
user_latitude: 0,
user_longitude: 0,
});
const call = (phone) => {
uni.makePhoneCall({
phoneNumber: phone,
});
};
const selfLocation = () => {
// #ifdef H5
const jsWxSdk = sheep.$platform.useProvider('wechat').jsWxSdk;
if (jsWxSdk.isWechat()) {
jsWxSdk.getLocation((res) => {
state.user_latitude = res.latitude;
state.user_longitude = res.longitude;
uni.setStorageSync(LATITUDE, res.latitude);
uni.setStorageSync(LONGITUDE, res.longitude);
getList();
});
} else {
// #endif
uni.getLocation({
type: 'gcj02',
success: (res) => {
try {
state.user_latitude = res.latitude;
state.user_longitude = res.longitude;
uni.setStorageSync(LATITUDE, res.latitude);
uni.setStorageSync(LONGITUDE, res.longitude);
} catch (e) {
console.error(e);
}
getList();
},
complete: () => {
getList();
},
});
// #ifdef H5
}
// #endif
};
const showMaoLocation = (e) => {
// #ifdef H5
const jsWxSdk = sheep.$platform.useProvider('wechat').jsWxSdk;
if (jsWxSdk.isWechat()) {
jsWxSdk.openLocation({
latitude: Number(e.latitude),
longitude: Number(e.longitude),
name: e.name,
address: `${e.areaName}-${e.detailAddress}`,
});
} else {
// #endif
uni.openLocation({
latitude: Number(e.latitude),
longitude: Number(e.longitude),
name: e.name,
address: `${e.areaName}-${e.detailAddress}`,
success: function () {
console.log('success');
},
});
// #ifdef H5
}
// #endif
};
/**
* 选中门店
*/
const checked = (addressInfo) => {
uni.$emit('SELECT_PICK_UP_INFO', {
addressInfo,
});
sheep.$router.back();
};
/**
* 获取门店列表数据
*/
const getList = async () => {
if (state.loading || state.loaded) {
return;
}
state.loading = true;
const { data, code } = await DeliveryApi.getDeliveryPickUpStoreList({
latitude: state.user_latitude,
longitude: state.user_longitude,
});
if (code !== 0) {
return;
}
state.loading = false;
state.storeList = data;
};
onMounted(() => {
if (state.user_latitude && state.user_longitude) {
getList();
} else {
selfLocation();
getList();
}
});
onLoad(() => {
try {
state.user_latitude = uni.getStorageSync(LATITUDE);
state.user_longitude = uni.getStorageSync(LONGITUDE);
} catch (e) {
console.error(e);
}
});
</script>
<style lang="scss" scoped>
.line1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.geoPage {
position: fixed;
width: 100%;
height: 100%;
top: 0;
z-index: 10000;
}
.storeBox {
width: 100%;
background-color: #fff;
padding: 0 30rpx;
}
.storeBox-box {
width: 100%;
height: auto;
display: flex;
align-items: center;
padding: 23rpx 0;
justify-content: space-between;
border-bottom: 1px solid #eee;
}
.store-cent {
display: flex;
align-items: center;
width: 80%;
}
.store-cent-left {
//width: 45%;
flex: 2;
}
.store-img {
flex: 1;
width: 120rpx;
height: 120rpx;
border-radius: 6rpx;
margin-right: 22rpx;
}
.store-img .img {
width: 100%;
height: 100%;
}
.store-name {
color: #282828;
font-size: 30rpx;
margin-bottom: 22rpx;
font-weight: 800;
}
.store-address {
color: #666666;
font-size: 24rpx;
}
.store-phone {
width: 50rpx;
height: 50rpx;
color: #fff;
border-radius: 50%;
display: block;
text-align: center;
line-height: 48rpx;
background-color: #e83323;
margin-bottom: 22rpx;
text-decoration: none;
}
.store-distance {
font-size: 22rpx;
color: #e83323;
}
.iconfont {
font-size: 20rpx;
}
.row-right {
flex: 2;
//display: flex;
//flex-direction: column;
//align-items: flex-end;
//width: 33.5%;
}
</style>
+8 -9
View File
@@ -26,7 +26,8 @@
<button
class="ss-reset-button avatar-action-btn"
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar">
@chooseavatar="onChooseAvatar"
>
修改
</button>
<!-- #endif -->
@@ -154,10 +155,7 @@
</view>
<view class="ss-flex ss-col-center">
<view class="info ss-flex ss-col-center" v-if="state.thirdInfo">
<image
class="avatar ss-m-r-20"
:src="sheep.$url.cdn(state.thirdInfo.avatar)"
/>
<image class="avatar ss-m-r-20" :src="sheep.$url.cdn(state.thirdInfo.avatar)" />
<text class="name">{{ state.thirdInfo.nickname }}</text>
</view>
<view class="bind-box ss-m-l-20">
@@ -185,7 +183,7 @@
<script setup>
import { computed, reactive, onBeforeMount } from 'vue';
import sheep from '@/sheep';
import { clone } from 'lodash';
import { clone } from 'lodash-es';
import { showAuthModal } from '@/sheep/hooks/useModal';
import FileApi from '@/sheep/api/infra/file';
import UserApi from '@/sheep/api/member/user';
@@ -198,14 +196,15 @@
const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal';
const sexRadioMap = [{
const sexRadioMap = [
{
name: '男',
value: '1',
},
{
name: '女',
value: '2',
}
},
];
const userInfo = computed(() => sheep.$store('user').userInfo);
@@ -279,7 +278,7 @@
// 保存信息
async function onSubmit() {
const { code } = await UserApi.updateUser({
const { code } = await UserApi.updateUser({
avatar: state.model.avatar,
nickname: state.model.nickname,
sex: state.model.sex,
+13 -7
View File
@@ -13,7 +13,9 @@
/>
</view>
<view class="ss-flex ss-row-between ss-col-center ss-m-t-64">
<view class="money-num">{{ state.showMoney ? fen2yuan(userWallet.balance) : '*****' }}</view>
<view class="money-num">{{
state.showMoney ? fen2yuan(userWallet.balance) : '*****'
}}</view>
<button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')">
充值
</button>
@@ -24,7 +26,12 @@
<su-sticky>
<!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.data" type="daterange" @change="onChangeTime" :end="state.today">
<uni-datetime-picker
v-model="state.data"
type="daterange"
@change="onChangeTime"
:end="state.today"
>
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text>
@@ -82,7 +89,7 @@
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import dayjs from 'dayjs';
import _ from 'lodash';
import _ from 'lodash-es';
import PayWalletApi from '@/sheep/api/pay/wallet';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util';
@@ -98,7 +105,7 @@
list: [],
total: 0,
pageNo: 1,
pageSize: 8
pageSize: 8,
},
summary: {
totalIncome: 0,
@@ -155,7 +162,7 @@
// 获得钱包统计
async function getSummary() {
const { data, code } = await PayWalletApi.getWalletTransactionSummary({
'createTime': [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
createTime: [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
});
if (code !== 0) {
return;
@@ -226,8 +233,7 @@
position: absolute;
top: 0;
left: 0;
background: v-bind(headerBg)
no-repeat;
background: v-bind(headerBg) no-repeat;
pointer-events: none;
}
+19 -14
View File
@@ -24,19 +24,24 @@
<su-sticky :customNavHeight="sys_navBar">
<!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.date" type="daterange" @change="onChangeTime" :end="state.today">
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text>
</button>
</uni-datetime-picker>
<uni-datetime-picker
v-model="state.date"
type="daterange"
@change="onChangeTime"
:end="state.today"
>
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text>
</button>
</uni-datetime-picker>
<!-- TODO 芋艿:优化 -->
<!-- <view class="total-box">-->
<!-- <view class="ss-m-b-10">总收入¥{{ state.pagination.income }}</view>-->
<!-- <view>总支出¥{{ -state.pagination.expense }}</view>-->
<!-- </view>-->
</view>
<!-- TODO 芋艿:【钱包】可优化 -->
<!-- <view class="total-box">-->
<!-- <view class="ss-m-b-10">总收入¥{{ state.pagination.income }}</view>-->
<!-- <view>总支出¥{{ -state.pagination.expense }}</view>-->
<!-- </view>-->
</view>
<su-tabs
:list="tabMaps"
@change="onChange"
@@ -83,7 +88,7 @@
import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive } from 'vue';
import _ from 'lodash';
import _ from 'lodash-es';
import dayjs from 'dayjs';
import PointApi from '@/sheep/api/member/point';
import { resetPagination } from '@/sheep/util';
@@ -274,4 +279,4 @@
}
}
}
</style>
</style>
+21
View File
@@ -1,4 +1,5 @@
import { baseUrl, apiPath, tenantId } from '@/sheep/config';
import request from '@/sheep/request';
const FileApi = {
// 上传文件
@@ -40,6 +41,26 @@ const FileApi = {
});
});
},
// 获取文件预签名地址
getFilePresignedUrl: (path) => {
return request({
url: '/infra/file/presigned-url',
method: 'GET',
params: {
path,
},
});
},
// 创建文件
createFile: (data) => {
return request({
url: '/infra/file/create', // 请求的 URL
method: 'POST', // 请求方法
data: data, // 要发送的数据
});
},
};
export default FileApi;
-14
View File
@@ -1,14 +0,0 @@
import request from '@/sheep/request';
// TODO 芋艿:暂不支持 socket 聊天
export default {
// 获取聊天token
unifiedToken: () =>
request({
url: 'unifiedToken',
custom: {
showError: false,
showLoading: false,
},
}),
};
+4 -4
View File
@@ -2,11 +2,11 @@ import request from '@/sheep/request';
const PayOrderApi = {
// 获得支付订单
getOrder: (id) => {
getOrder: (id, sync) => {
return request({
url: '/pay/order/get',
method: 'GET',
params: { id }
params: { id, sync },
});
},
// 提交支付订单
@@ -14,9 +14,9 @@ const PayOrderApi = {
return request({
url: '/pay/order/submit',
method: 'POST',
data
data,
});
}
},
};
export default PayOrderApi;
+12
View File
@@ -13,6 +13,18 @@ const SpuApi = {
},
});
},
// 获得商品结算信息
getSettlementProduct: (spuIds) => {
return request({
url: '/trade/order/settlement-product',
method: 'GET',
params: { spuIds },
custom: {
showLoading: false,
showError: false,
},
});
},
// 获得商品 SPU 分页
getSpuPage: (params) => {
return request({
+1 -1
View File
@@ -1,7 +1,7 @@
import request from '@/sheep/request';
const ActivityApi = {
// 获得单个商品,近期参与的每个活动
// 获得单个商品,进行中的拼团、秒杀、砍价活动信息
getActivityListBySpuId: (spuId) => {
return request({
url: '/promotion/activity/list-by-spu-id',
+13 -11
View File
@@ -2,15 +2,6 @@ import request from '@/sheep/request';
// 拼团 API
const CombinationApi = {
// 获得拼团活动列表
getCombinationActivityList: (count) => {
return request({
url: '/promotion/combination-activity/list',
method: 'GET',
params: { count },
});
},
// 获得拼团活动分页
getCombinationActivityPage: (params) => {
return request({
@@ -31,6 +22,17 @@ const CombinationApi = {
});
},
// 获得拼团活动列表,基于活动编号数组
getCombinationActivityListByIds: (ids) => {
return request({
url: '/promotion/combination-activity/list-by-ids',
method: 'GET',
params: {
ids,
},
});
},
// 获得最近 n 条拼团记录(团长发起的)
getHeadCombinationRecordList: (activityId, status, count) => {
return request({
@@ -47,9 +49,9 @@ const CombinationApi = {
// 获得我的拼团记录分页
getCombinationRecordPage: (params) => {
return request({
url: "/promotion/combination-record/page",
url: '/promotion/combination-record/page',
method: 'GET',
params
params,
});
},
-17
View File
@@ -79,23 +79,6 @@ const CouponApi = {
},
});
},
// 获得匹配指定商品的优惠劵列表
getMatchCouponList: (price, spuIds, skuIds, categoryIds) => {
return request({
url: '/promotion/coupon/match-list',
method: 'GET',
params: {
price,
spuIds: spuIds.join(','),
skuIds: skuIds.join(','),
categoryIds: categoryIds.join(','),
},
custom: {
showError: false,
showLoading: false, // 避免影响 settlementOrder 结算的结果
},
});
}
};
export default CouponApi;
+2 -2
View File
@@ -15,9 +15,9 @@ const KeFuApi = {
},
});
},
getKefuMessagePage: (params) => {
getKefuMessageList: (params) => {
return request({
url: '/promotion/kefu-message/page',
url: '/promotion/kefu-message/list',
method: 'GET',
params,
custom: {
+30
View File
@@ -0,0 +1,30 @@
import request from '@/sheep/request';
const PointApi = {
// 获得积分商城活动分页
getPointActivityPage: (params) => {
return request({ url: 'promotion/point-activity/page', method: 'GET', params });
},
// 获得积分商城活动列表,基于活动编号数组
getPointActivityListByIds: (ids) => {
return request({
url: '/promotion/point-activity/list-by-ids',
method: 'GET',
params: {
ids,
},
});
},
// 获得积分商城活动明细
getPointActivity: (id) => {
return request({
url: 'promotion/point-activity/get-detail',
method: 'GET',
params: { id },
});
},
};
export default PointApi;
+15 -4
View File
@@ -1,4 +1,4 @@
import request from "@/sheep/request";
import request from '@/sheep/request';
const SeckillApi = {
// 获得秒杀时间段列表
@@ -16,6 +16,17 @@ const SeckillApi = {
return request({ url: 'promotion/seckill-activity/page', method: 'GET', params });
},
// 获得秒杀活动列表,基于活动编号数组
getSeckillActivityListByIds: (ids) => {
return request({
url: '/promotion/seckill-activity/list-by-ids',
method: 'GET',
params: {
ids,
},
});
},
/**
* 获得秒杀活动明细
* @param {number} id 秒杀活动编号
@@ -25,9 +36,9 @@ const SeckillApi = {
return request({
url: 'promotion/seckill-activity/get-detail',
method: 'GET',
params: { id }
params: { id },
});
}
}
},
};
export default SeckillApi;
+16
View File
@@ -0,0 +1,16 @@
import request from '@/sheep/request';
const DictApi = {
// 根据字典类型查询字典数据信息
getDictDataListByType: (type) => {
return request({
url: `/system/dict-data/type`,
method: 'GET',
params: {
type,
},
});
},
};
export default DictApi;
+3
View File
@@ -6,6 +6,9 @@ const TradeConfigApi = {
return request({
url: `/trade/config/get`,
method: 'GET',
custom: {
showLoading: false,
},
});
},
};
+19 -1
View File
@@ -7,7 +7,25 @@ const DeliveryApi = {
url: `/trade/delivery/express/list`,
method: 'get',
});
}
},
// 获得自提门店列表
getDeliveryPickUpStoreList: (params) => {
return request({
url: `/trade/delivery/pick-up-store/list`,
method: 'GET',
params,
});
},
// 获得自提门店
getDeliveryPickUpStore: (id) => {
return request({
url: `/trade/delivery/pick-up-store/get`,
method: 'GET',
params: {
id,
},
});
},
};
export default DeliveryApi;
+31 -2
View File
@@ -1,4 +1,5 @@
import request from '@/sheep/request';
import { isEmpty } from '@/sheep/helper/utils';
const OrderApi = {
// 计算订单信息
@@ -13,6 +14,15 @@ const OrderApi = {
if (!(data.addressId > 0)) {
delete data2.addressId;
}
if (!(data.pickUpStoreId > 0)) {
delete data2.pickUpStoreId;
}
if (isEmpty(data.receiverName)) {
delete data2.receiverName;
}
if (isEmpty(data.receiverMobile)) {
delete data2.receiverMobile;
}
if (!(data.combinationActivityId > 0)) {
delete data2.combinationActivityId;
}
@@ -22,6 +32,12 @@ const OrderApi = {
if (!(data.seckillActivityId > 0)) {
delete data2.seckillActivityId;
}
if (!(data.pointActivityId > 0)) {
delete data2.pointActivityId;
}
if (!(data.deliveryType > 0)) {
delete data2.deliveryType;
}
// 解决 SpringMVC 接受 List<Item> 参数的问题
delete data2.items;
for (let i = 0; i < data.items.length; i++) {
@@ -43,6 +59,18 @@ const OrderApi = {
},
});
},
// 获得商品结算信息
getSettlementProduct: (spuIds) => {
return request({
url: '/trade/order/settlement-product',
method: 'GET',
params: { spuIds },
custom: {
showLoading: false,
showError: false,
},
});
},
// 创建订单
createOrder: (data) => {
return request({
@@ -51,13 +79,14 @@ const OrderApi = {
data,
});
},
// 获得订单
getOrder: (id) => {
// 获得订单详细:sync 是可选参数
getOrderDetail: (id, sync) => {
return request({
url: `/trade/order/get-detail`,
method: 'GET',
params: {
id,
sync,
},
custom: {
showLoading: false,
+196
View File
@@ -0,0 +1,196 @@
<!-- TODO 霖:是不是怎么复用 s-count-down 组件 -->
<template>
<view class="time" :style="justifyLeft">
<text class="" v-if="tipText">{{ tipText }}</text>
<text
class="styleAll p6"
v-if="isDay === true"
:style="{ background: bgColor.bgColor, color: bgColor.Color }"
>{{ day }}{{ bgColor.isDay ? '天' : '' }}</text
>
<text
class="timeTxt"
v-if="dayText"
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
>{{ dayText }}</text
>
<text
class="styleAll"
:class="isCol ? 'timeCol' : ''"
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
>{{ hour }}</text
>
<text
class="timeTxt"
v-if="hourText"
:class="isCol ? 'whit' : ''"
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
>{{ hourText }}</text
>
<text
class="styleAll"
:class="isCol ? 'timeCol' : ''"
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
>{{ minute }}</text
>
<text
class="timeTxt"
v-if="minuteText"
:class="isCol ? 'whit' : ''"
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
>{{ minuteText }}</text
>
<text
class="styleAll"
:class="isCol ? 'timeCol' : ''"
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
>{{ second }}</text
>
<text class="timeTxt" v-if="secondText">{{ secondText }}</text>
</view>
</template>
<script>
export default {
name: 'countDown',
props: {
justifyLeft: {
type: String,
default: '',
},
//距离开始提示文字
tipText: {
type: String,
default: '倒计时',
},
dayText: {
type: String,
default: '天',
},
hourText: {
type: String,
default: '时',
},
minuteText: {
type: String,
default: '分',
},
secondText: {
type: String,
default: '秒',
},
datatime: {
type: Number,
default: 0,
},
isDay: {
type: Boolean,
default: true,
},
isCol: {
type: Boolean,
default: false,
},
bgColor: {
type: Object,
default: null,
},
},
data: function () {
return {
day: '00',
hour: '00',
minute: '00',
second: '00',
};
},
created: function () {
this.show_time();
},
mounted: function () {},
methods: {
show_time: function () {
let that = this;
function runTime() {
//时间函数
let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
let day = 0,
hour = 0,
minute = 0,
second = 0;
if (intDiff > 0) {
//转换时间
if (that.isDay === true) {
day = Math.floor(intDiff / (60 * 60 * 24));
} else {
day = 0;
}
hour = Math.floor(intDiff / (60 * 60)) - day * 24;
minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
second = Math.floor(intDiff) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
if (hour <= 9) hour = '0' + hour;
if (minute <= 9) minute = '0' + minute;
if (second <= 9) second = '0' + second;
that.day = day;
that.hour = hour;
that.minute = minute;
that.second = second;
} else {
that.day = '00';
that.hour = '00';
that.minute = '00';
that.second = '00';
}
}
runTime();
setInterval(runTime, 1000);
},
},
};
</script>
<style scoped>
.p6 {
padding: 0 8rpx;
}
.styleAll {
/* color: #fff; */
font-size: 24rpx;
height: 36rpx;
line-height: 36rpx;
border-radius: 6rpx;
text-align: center;
/* padding: 0 6rpx; */
}
.timeTxt {
text-align: center;
/* width: 16rpx; */
height: 36rpx;
line-height: 36rpx;
display: inline-block;
}
.whit {
color: #fff !important;
}
.time {
display: flex;
justify-content: center;
}
.red {
color: #fc4141;
margin: 0 4rpx;
}
.timeCol {
/* width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align:center;
border-radius: 6px;
background: #fff;
font-size: 24rpx; */
color: #e93323;
}
</style>
@@ -2,34 +2,82 @@
<template>
<su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose>
<view class="model-box">
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">营销活动</view>
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠</view>
<view v-if="state.rewardActivity && state.rewardActivity.id > 0">
<view class="titleLi">促销</view>
<scroll-view
class="model-content"
scroll-y
:scroll-with-animation="false"
:enable-back-to-top="true"
>
<view
class="actBox"
v-for="(item, index) in getRewardActivityRuleGroupDescriptions(state.rewardActivity)"
:key="index"
>
<view
class="boxCont ss-flex ss-col-top ss-m-b-40"
@tap="onGoodsList(state.rewardActivity)"
>
<view class="model-content-tag ss-flex ss-row-center">{{ item.name }}</view>
<view class="model-content-title">
<view class="contBu">
{{ item.values.join(';') }}
</view>
<view class="ss-m-b-24 cotBu-txt">
{{ sheep.$helper.timeFormat(state.rewardActivity.startTime, 'yyyy.mm.dd') }}
-
{{ sheep.$helper.timeFormat(state.rewardActivity.endTime, 'yyyy.mm.dd') }}
</view>
</view>
<text class="cicon-forward" />
</view>
</view>
</scroll-view>
</view>
<view class="titleLi">可领优惠券</view>
<scroll-view
class="model-content ss-m-t-50"
class="model-content"
scroll-y
:scroll-with-animation="false"
:enable-back-to-top="true"
v-if="state.couponInfo.length"
>
<view v-for="item in state.activityInfo" :key="item.id">
<view class="ss-flex ss-col-top ss-m-b-40" @tap="onGoodsList(item)">
<view class="model-content-tag ss-flex ss-row-center">满减</view>
<view class="ss-m-l-20 model-content-title ss-flex-1">
<view class="ss-m-b-24" v-for="rule in state.activityMap[item.id]?.rules" :key="rule">
{{ formatRewardActivityRule(state.activityMap[item.id], rule) }}
<view class="actBox" v-for="item in state.couponInfo" :key="item.id">
<view class="boxCont ss-flex ss-col-top ss-m-b-40">
<view class="model-content-tag2">
<view class="usePrice"> ¥{{ fen2yuan(item.discountPrice) }} </view>
<view class="impose"> 满¥{{ fen2yuan(item.usePrice) }}可用 </view>
</view>
<view class="model-content-title2">
<view class="contBu">
{{ item.name }}
</view>
<view class="ss-m-b-24 cotBu-txt">
{{
item.validityType == 1
? sheep.$helper.timeFormat(item.validStartTime, 'yyyy-mm-dd') +
'-' +
sheep.$helper.timeFormat(item.validEndTime, 'yyyy-mm-dd')
: '领取后' + item.fixedStartTerm + '-' + item.fixedEndTerm + '天可用'
}}
</view>
</view>
<text class="cicon-forward" />
<view class="coupon" @click.stop="getBuy(item.id)" v-if="item.canTake"> 立即领取 </view>
<view class="coupon2" v-else> 已领取 </view>
</view>
</view>
</scroll-view>
<view class="nullBox" v-else> 暂无可领优惠券 </view>
</view>
</su-popup>
</template>
<script setup>
import sheep from '@/sheep';
import { computed, reactive, watch } from 'vue';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
import { getRewardActivityRuleGroupDescriptions } from '@/sheep/hooks/useGoods';
import { computed, reactive, watch, ref } from 'vue';
import { fen2yuan } from '@/sheep/hooks/useGoods';
const props = defineProps({
modelValue: {
type: Object,
@@ -42,26 +90,14 @@
});
const emits = defineEmits(['close']);
const state = reactive({
activityInfo: computed(() => props.modelValue),
activityMap: {}
rewardActivity: computed(() => props.modelValue.rewardActivity),
couponInfo: computed(() => props.modelValue.couponInfo),
});
watch(
() => props.show,
() => {
// 展示的情况下,加载每个活动的详细信息
if (props.show) {
state.activityInfo?.forEach(activity => {
RewardActivityApi.getRewardActivity(activity.id).then(res => {
if (res.code !== 0) {
return;
}
state.activityMap[activity.id] = res.data;
})
});
}
},
);
// 领取优惠劵
const getBuy = (id) => {
emits('get', id);
};
function onGoodsList(e) {
sheep.$router.go('/pages/activity/index', {
@@ -72,34 +108,149 @@
<style lang="scss" scoped>
.model-box {
height: 60vh;
.title {
justify-content: center;
font-size: 36rpx;
height: 80rpx;
font-weight: bold;
color: #333333;
}
}
.model-content {
height: fit-content;
max-height: 380rpx;
padding: 0 20rpx;
box-sizing: border-box;
margin-top: 20rpx;
.model-content-tag {
background: rgba(#ff6911, 0.1);
font-size: 24rpx;
// background: rgba(#ff6911, 0.1);
font-size: 35rpx;
font-weight: 500;
color: #ff6911;
line-height: 42rpx;
width: 68rpx;
height: 32rpx;
border-radius: 5rpx;
line-height: 150rpx;
width: 200rpx;
height: 150rpx;
text-align: center;
// border-radius: 5rpx;
}
.model-content-title {
width: 450rpx;
height: 150rpx;
font-size: 26rpx;
font-weight: 500;
color: #333333;
overflow: hidden;
}
.cicon-forward {
font-size: 28rpx;
color: #999999;
margin: 0 auto;
}
}
// 新增的
.titleLi {
margin: 10rpx 0 10rpx 20rpx;
font-size: 26rpx;
}
.actBox {
width: 700rpx;
height: 150rpx;
background-color: #fff2f2;
margin: 10rpx auto;
border-radius: 10rpx;
}
.boxCont {
width: 700rpx;
height: 150rpx;
align-items: center;
}
.contBu {
height: 80rpx;
line-height: 80rpx;
overflow: hidden;
font-size: 30rpx;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
.cotBu-txt {
height: 70rpx;
line-height: 70rpx;
font-size: 25rpx;
color: #999999;
}
.model-content-tag2 {
font-size: 35rpx;
font-weight: 500;
color: #ff6911;
width: 200rpx;
height: 150rpx;
text-align: center;
}
.usePrice {
width: 200rpx;
height: 90rpx;
line-height: 100rpx;
// background-color: red;
}
.impose {
width: 200rpx;
height: 50rpx;
// line-height: 75rpx;
font-size: 23rpx;
// background-color: gold;
}
.model-content-title2 {
width: 330rpx;
height: 150rpx;
font-size: 26rpx;
font-weight: 500;
color: #333333;
overflow: hidden;
}
.coupon {
width: 150rpx;
height: 50rpx;
line-height: 50rpx;
background-color: rgb(255, 68, 68);
color: white;
border-radius: 30rpx;
text-align: center;
font-size: 25rpx;
}
.coupon2 {
width: 150rpx;
height: 50rpx;
line-height: 50rpx;
background-color: rgb(203, 192, 191);
color: white;
border-radius: 30rpx;
text-align: center;
font-size: 25rpx;
}
.nullBox {
width: 100%;
height: 300rpx;
font-size: 25rpx;
line-height: 300rpx;
text-align: center;
color: #999999;
}
</style>
@@ -18,9 +18,7 @@
<view class="address-text">
{{ props.item.detailAddress }}
</view>
<view class="person-text">
{{ props.item.name }} {{ props.item.mobile }}
</view>
<view class="person-text"> {{ props.item.name }} {{ props.item.mobile }} </view>
</view>
<view v-else>
<view class="address-text ss-m-b-10">请选择收货地址</view>
@@ -47,7 +45,7 @@
* @slot - 默认插槽
*/
import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import { isEmpty } from 'lodash-es';
const props = defineProps({
item: {
type: Object,
+19 -11
View File
@@ -10,7 +10,11 @@
/>
<!-- 2. 短信登录 smsLogin -->
<sms-login v-if="authType === 'smsLogin'" :agreeStatus="state.protocol" @onConfirm="onConfirm" />
<sms-login
v-if="authType === 'smsLogin'"
:agreeStatus="state.protocol"
@onConfirm="onConfirm"
/>
<!-- 3. 忘记密码 resetPassword-->
<reset-password v-if="authType === 'resetPassword'" />
@@ -32,7 +36,11 @@
<!-- 7.1 微信小程序的快捷登录 -->
<view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
<view class="register-title">还没有账号?</view>
<button class="ss-reset-button login-btn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
<button
class="ss-reset-button login-btn"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
快捷登录
</button>
<view class="circle" />
@@ -81,17 +89,13 @@
/>
<view class="agreement-text ss-flex ss-col-center ss-m-l-8">
我已阅读并遵守
<view class="tcp-text" @tap.stop="onProtocol('用户协议')">
《用户协议》
</view>
<view class="tcp-text" @tap.stop="onProtocol('用户协议')"> 《用户协议》 </view>
<view class="agreement-text">与</view>
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')">
《隐私协议》
</view>
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')"> 《隐私协议》 </view>
</view>
</label>
</view>
<view class="safe-box"/>
<view class="safe-box" />
</view>
</su-popup>
</template>
@@ -107,8 +111,6 @@
import mpAuthorization from './components/mp-authorization.vue';
import { closeAuthModal, showAuthModal } from '@/sheep/hooks/useModal';
const appInfo = computed(() => sheep.$store('app').info);
const modalStore = sheep.$store('modal');
// 授权弹窗类型
const authType = computed(() => modalStore.auth);
@@ -152,7 +154,13 @@
}
const loginRes = await sheep.$platform.useProvider(provider).login();
if (loginRes) {
const userInfo = await sheep.$store('user').getInfo();
closeAuthModal();
// 如果用户已经有头像和昵称,不需要再次授权
if (userInfo.avatar && userInfo.nickname) {
return;
}
// 触发小程序授权信息弹框
// #ifdef MP-WEIXIN
showAuthModal('mpAuthorization');
@@ -9,7 +9,7 @@
<!-- 基础组件:列表导航 -->
<s-menu-list v-if="type === 'MenuList'" :data="data" />
<!-- 基础组件:宫格导航 -->
<s-menu-grid v-if="type === 'MenuGrid'" :data="data" />
<s-menu-grid v-if="type === 'MenuGrid'" :data="data" :styles="styles" />
<!-- 基础组件:弹窗广告 -->
<s-popup-image v-if="type === 'Popover'" :data="data" />
<!-- 基础组件:悬浮按钮 -->
@@ -39,6 +39,8 @@
<s-groupon-block v-if="type === 'PromotionCombination'" :data="data" :styles="styles" />
<!-- 营销组件:秒杀 -->
<s-seckill-block v-if="type === 'PromotionSeckill'" :data="data" :styles="styles" />
<!-- 营销组件:积分商城 -->
<s-point-block v-if="type === 'PromotionPoint'" :data="data" :styles="styles" />
<!-- 营销组件:小程序直播(暂时没有这个功能) -->
<s-live-block v-if="type === 'MpLive'" :data="data" :styles="styles" />
<!-- 营销组件:优惠券 -->
@@ -47,13 +49,13 @@
<s-richtext-block v-if="type === 'PromotionArticle'" :data="data" :styles="styles" />
<!-- 用户组件:用户卡片 -->
<s-user-card v-if="type === 'UserCard'" />
<s-user-card v-if="type === 'UserCard'" :data="data" :styles="styles" />
<!-- 用户组件:用户订单 -->
<s-order-card v-if="type === 'UserOrder'" :data="data" />
<s-order-card v-if="type === 'UserOrder'" :data="data" :styles="styles" />
<!-- 用户组件:用户资产 -->
<s-wallet-card v-if="type === 'UserWallet'" />
<s-wallet-card v-if="type === 'UserWallet'" :data="data" :styles="styles" />
<!-- 用户组件:用户卡券 -->
<s-coupon-card v-if="type === 'UserCoupon'" />
<s-coupon-card v-if="type === 'UserCoupon'" :data="data" :styles="styles" />
</view>
</template>
+162 -138
View File
@@ -1,152 +1,176 @@
<!-- 装修营销组件:优惠券 -->
<template>
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
<view class="coupon-box ss-flex">
<view
class="coupon-item"
:style="[couponBg, { marginLeft: `${data.space}px` }]"
v-for="(item, index) in couponList"
:key="index"
>
<su-coupon
:size="SIZE_LIST[columns - 1]"
:textColor="data.textColor"
background=""
:couponId="item.id"
:title="item.name"
:type="formatCouponDiscountType(item)"
:value="formatCouponDiscountValue(item)"
:sellBy="formatValidityType(item)"
>
<template v-slot:btn>
<!-- 两列时,领取按钮坚排 -->
<button
v-if="columns === 2"
@click.stop="onGetCoupon(item.id)"
class="ss-reset-button card-btn vertical"
:style="[btnStyles]"
>
<view class="btn-text">立即领取</view>
</button>
<button
v-else
class="ss-reset-button card-btn"
:style="[btnStyles]"
@click.stop="onGetCoupon(item.id)"
>
立即领取
</button>
</template>
</su-coupon>
</view>
</view>
</scroll-view>
<scroll-view class="scroll-box" scroll-x scroll-anchoring :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="coupon-box ss-flex" :style="couponList.length === 2 ? couponBoxStyleTwo : couponBoxStyleNormal">
<view class="coupon-item" :style="[couponBg, { marginLeft: `${data.space}px` }]"
v-for="(item, index) in couponList" :key="index">
<su-coupon :size="SIZE_LIST[columns - 1]" :textColor="data.textColor" background="" :couponId="item.id"
:title="item.name" :type="formatCouponDiscountType(item)" :value="formatCouponDiscountValue(item)"
:sellBy="formatValidityType(item)">
<template v-slot:btn>
<!-- 两列时,领取按钮坚排 -->
<button v-if="columns === 2" @click.stop="onGetCoupon(item.id)"
class="ss-reset-button card-btn vertical" :style="[btnStyles]">
<view class="btn-text">立即领取</view>
</button>
<button v-else class="ss-reset-button card-btn" :style="[btnStyles]"
@click.stop="onGetCoupon(item.id)">
立即领取
</button>
</template>
</su-coupon>
</view>
</view>
</scroll-view>
</template>
<script setup>
import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon';
import { ref, onMounted } from 'vue';
import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from "@/sheep/util/const";
import { floatToFixed2, formatDate } from "@/sheep/util";
import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon';
import {
ref,
onMounted,
computed
} from 'vue';
import {
CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum
} from "@/sheep/util/const";
import {
floatToFixed2,
formatDate
} from "@/sheep/util";
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
styles: {
type: Object,
default: () => ({}),
},
});
const { columns, button } = props.data;
const SIZE_LIST = ['lg', 'md', 'xs']
const couponBg = {
background: `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`,
};
const btnStyles = {
background: button.bgColor,
color: button.color,
};
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
styles: {
type: Object,
default: () => ({}),
},
});
const {
columns,
button
} = props.data;
const SIZE_LIST = ['lg', 'md', 'xs']
const couponBg = {
background: `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`,
};
const btnStyles = {
background: button.bgColor,
color: button.color,
};
// 两列优惠券时的排版方式
const couponBoxStyleNormal = {
'display': 'flex',
'justify-content': 'space-between'
};
// 非两列优惠券时的排版方式
const couponBoxStyleTwo = {
'display': 'flex',
'justify-content': 'space-around'
};
// 设置背景样式
const bgStyle = computed(() => {
// 直接从 props.styles 解构
const {
bgType,
bgImg,
bgColor
} = props.styles;
// 格式化【折扣类型】
const formatCouponDiscountType = (coupon) => {
if(coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return 'reduce'
}
if(coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return 'percent'
}
return `未知【${coupon.discountType}】`
}
// 根据 bgType 返回相应的样式
return {
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
};
});
// 格式化【折扣类型】
const formatCouponDiscountType = (coupon) => {
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return 'reduce'
}
if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return 'percent'
}
return `未知【${coupon.discountType}】`
}
// 格式化【折扣】
const formatCouponDiscountValue = (coupon) => {
if(coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return floatToFixed2(coupon.discountPrice)
}
if(coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return coupon.discountPercent
}
return `未知【${coupon.discountType}】`
}
// 格式化【折扣】
const formatCouponDiscountValue = (coupon) => {
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return floatToFixed2(coupon.discountPrice)
}
if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return coupon.discountPercent
}
return `未知【${coupon.discountType}】`
}
// 格式化【有效期限】
const formatValidityType = (row) => {
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`
}
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
}
return '未知【' + row.validityType + '】'
}
// 格式化【有效期限】
const formatValidityType = (row) => {
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`
}
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
}
return '未知【' + row.validityType + '】'
}
const couponList = ref([]);
// 立即领取优惠券
async function onGetCoupon(id) {
const { error, msg } = await CouponApi.takeCoupon(id);
if (error === 0) {
uni.showToast({
title: msg,
icon: 'none',
});
return
}
await getCouponTemplateList()
}
const getCouponTemplateList = async () => {
const { data } = await CouponApi.getCouponTemplateListByIds(props.data.couponIds.join(','));
couponList.value = data;
}
onMounted(() => {
getCouponTemplateList()
});
const couponList = ref([]);
// 立即领取优惠券
async function onGetCoupon(id) {
const {
error,
msg
} = await CouponApi.takeCoupon(id);
if (error === 0) {
uni.showToast({
title: msg,
icon: 'none',
});
return
}
await getCouponTemplateList()
}
const getCouponTemplateList = async () => {
const {
data
} = await CouponApi.getCouponTemplateListByIds(props.data.couponIds.join(','));
couponList.value = data;
}
onMounted(() => {
getCouponTemplateList()
});
</script>
<style lang="scss" scoped>
.card-btn {
width: 140rpx;
height: 50rpx;
border-radius: 25rpx;
font-size: 24rpx;
line-height: 50rpx;
&.vertical {
width: 50rpx;
height: 140rpx;
margin: auto 20rpx auto 0;
.card-btn {
width: 140rpx;
height: 50rpx;
border-radius: 25rpx;
font-size: 24rpx;
line-height: 50rpx;
.btn-text {
font-size: 24rpx;
text-align: center;
writing-mode: vertical-lr;
}
}
}
.coupon-item {
&:nth-of-type(1) {
margin-left: 0 !important;
}
}
</style>
&.vertical {
width: 50rpx;
height: 140rpx;
margin: auto 20rpx auto 0;
.btn-text {
font-size: 24rpx;
text-align: center;
writing-mode: vertical-lr;
}
}
}
.coupon-item {
&:nth-of-type(1) {
margin-left: 0 !important;
}
}
</style>
@@ -1,6 +1,6 @@
<!-- 装修用户组件:用户卡券 -->
<template>
<view class="ss-coupon-menu-wrap ss-flex ss-col-center">
<view class="ss-coupon-menu-wrap ss-flex ss-col-center" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="menu-item ss-flex-col ss-row-center ss-col-center" v-for="item in props.list" :key="item.title"
@tap="sheep.$router.go(item.path, { type: item.type })"
:class="item.type === 'all' ? 'menu-wallet' : 'ss-flex-1'">
@@ -15,6 +15,7 @@
* 装修组件 - 优惠券菜单
*/
import sheep from '@/sheep';
import { computed } from 'vue';
// 接收参数
const props = defineProps({
@@ -52,6 +53,28 @@
];
},
},
// 装修数据
data: {
type: Object,
default: () => ({}),
},
// 装修样式
styles: {
type: Object,
default: () => ({}),
},
});
// 设置背景样式
const bgStyle = computed(() => {
// 直接从 props.styles 解构
const { bgType, bgImg, bgColor } = props.styles;
// 根据 bgType 返回相应的样式
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
});
</script>
@@ -37,30 +37,22 @@
<view class="ss-flex ss-row-between ss-m-t-16">
<view
class="sellby-text"
:class=" isDisable ? 'disabled-color' : 'subtitle-color'"
:class="isDisable ? 'disabled-color' : 'subtitle-color'"
v-if="data.validityType === 2"
>
有效期:领取后 {{ data.fixedEndTerm }} 天内可用
</view>
<view
class="sellby-text"
:class=" isDisable ? 'disabled-color' : 'subtitle-color'"
v-else
>
<view class="sellby-text" :class="isDisable ? 'disabled-color' : 'subtitle-color'" v-else>
有效期: {{ sheep.$helper.timeFormat(data.validStartTime, 'yyyy-mm-dd') }} 至
{{ sheep.$helper.timeFormat(data.validEndTime, 'yyyy-mm-dd') }}
</view>
<view
class="value-enough"
:class="isDisable ? 'disabled-color' : 'subtitle-color'"
>
<view class="value-enough" :class="isDisable ? 'disabled-color' : 'subtitle-color'">
满 {{ fen2yuan(data.usePrice) }} 可用
</view>
</view>
</view>
</view>
<!-- TODO 芋艿:可优化,增加优惠劵的描述 -->
<view class="desc ss-flex ss-row-between">
<view>
<view class="desc-title">{{ data.description }}</view>
@@ -76,17 +68,15 @@
</template>
<script setup>
import { computed, reactive } from 'vue';
import { computed } from 'vue';
import { fen2yuan } from '../../hooks/useGoods';
import sheep from '../../index';
const state = reactive({});
const isDisable = computed(() => {
if (props.type === 'coupon') {
return false;
}
return props.data.status !== 1;
return props.disabled;
});
// 接受参数
@@ -202,4 +192,4 @@
.price-text {
color: #ff0000;
}
</style>
</style>
@@ -16,8 +16,12 @@
:scroll-with-animation="false"
:enable-back-to-top="true"
>
<!--可使用的优惠券区域-->
<view class="subtitle ss-m-l-20">可使用优惠券</view>
<view v-for="(item, index) in state.couponInfo" :key="index">
<view
v-for="(item, index) in state.couponInfo.filter((coupon) => coupon.match)"
:key="index"
>
<s-coupon-list :data="item" type="user" :disabled="false">
<template #default>
<label class="ss-flex ss-col-center" @tap="radioChange(item.id)">
@@ -31,19 +35,18 @@
</template>
</s-coupon-list>
</view>
<!-- TODO 芋艿:未来接口需要支持下
<!--不可使用的优惠券区域-->
<view class="subtitle ss-m-t-40 ss-m-l-20">不可使用优惠券</view>
<view v-for="item in state.couponInfo.cannot_use" :key="item.id">
<view v-for="item in state.couponInfo.filter((coupon) => !coupon.match)" :key="item.id">
<s-coupon-list :data="item" type="user" :disabled="true">
<template v-slot:reason>
<view class="ss-flex ss-m-t-24">
<view class="reason-title"> 不可用原因:</view>
<view class="reason-desc">{{ item.cannot_use_msg }}</view>
<view class="reason-desc">{{ item.mismatchReason || '未达到使用门槛' }}</view>
</view>
</template>
</s-coupon-list>
</view>
-->
</scroll-view>
</view>
<view class="modal-footer ss-flex">
@@ -55,7 +58,8 @@
import { computed, reactive } from 'vue';
const props = defineProps({
modelValue: { // 优惠劵列表
modelValue: {
// 优惠劵列表
type: Object,
default() {},
},
@@ -69,13 +73,13 @@
const state = reactive({
couponInfo: computed(() => props.modelValue), // 优惠劵列表
couponId: 0, // 选中的优惠劵编号
couponId: undefined, // 选中的优惠劵编号
});
// 选中优惠劵
function radioChange(couponId) {
if (state.couponId === couponId) {
state.couponId = 0;
state.couponId = undefined;
} else {
state.couponId = couponId;
}
@@ -84,7 +88,7 @@
// 确认优惠劵
const onConfirm = () => {
emits('confirm', state.couponId);
}
};
</script>
<style lang="scss" scoped>
:deep() {
@@ -96,25 +100,30 @@
.model-box {
height: 60vh;
}
.title {
font-size: 36rpx;
height: 80rpx;
font-weight: bold;
color: #333333;
}
.subtitle {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.model-content {
height: 54vh;
}
.modal-footer {
width: 100%;
height: 120rpx;
background: #fff;
}
.confirm-btn {
width: 710rpx;
margin-left: 20rpx;
@@ -123,12 +132,14 @@
border-radius: 40rpx;
color: #fff;
}
.reason-title {
font-weight: 600;
font-size: 20rpx;
line-height: 26rpx;
color: #ff0003;
}
.reason-desc {
font-weight: 600;
font-size: 20rpx;
@@ -6,6 +6,7 @@
v-if="data.type === 'text'"
class="nav-title inline"
:style="[{ color: data.textColor, width: width }]"
@tap="sheep.$router.go(data.url)"
>
{{ data.text }}
</view>
@@ -44,9 +44,9 @@
*
* @property {Number | String} alwaysShow = [0,1] - 是否常驻
* @property {Number | String} styleType = [inner] - 是否沉浸式
* @property {String | Number} type - 标题背景模式
* @property {String} color - 页面背景色
* @property {String} src - 页面背景图片
* @property {String | Number} type - 标题背景模式
* @property {String} color - 页面背景色
* @property {String} src - 页面背景图片
*/
import { computed, unref } from 'vue';
import sheep from '@/sheep';
@@ -77,7 +77,7 @@
});
const navList = computed(() => {
// #ifdef MP
return props.data.mapCells || [];
return props.data.mpCells || [];
// #endif
return props.data.otherCells || [];
});
@@ -117,11 +117,12 @@
const bgStyles = computed(() => {
return {
background:
props.data.bgType === 'img' && props.data.bgImg
props.data.bgType === 'img' && props.data.bgImg
? `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`
: props.data.bgColor
: props.data.bgColor,
};
});
// 左侧按钮:返回上一页或首页
function onClickLeft() {
if (hasHistory) {
@@ -130,6 +131,7 @@
sheep.$router.go('/pages/index/index');
}
}
// 右侧按钮:打开快捷菜单
function onClickRight() {
showMenuTools();
@@ -147,44 +149,53 @@
top: 50%;
transform: translateY(-50%);
}
.nav-icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 20rpx;
.inner-icon-box {
border: 1px solid rgba(#fff, 0.4);
background: none !important;
}
.icon-box {
background: #ffffff;
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08),
0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
border-radius: 30rpx;
width: 134rpx;
height: 56rpx;
margin-left: 8rpx;
.line {
width: 2rpx;
height: 24rpx;
background: #e5e5e7;
}
.sicon-back {
font-size: 32rpx;
}
.sicon-home {
font-size: 32rpx;
}
.sicon-more {
font-size: 32rpx;
}
.icon-button {
width: 67rpx;
height: 56rpx;
&-left:hover {
background: rgba(0, 0, 0, 0.16);
border-radius: 30rpx 0px 0px 30rpx;
}
&-right:hover {
background: rgba(0, 0, 0, 0.16);
border-radius: 0px 30rpx 30rpx 0px;
@@ -15,25 +15,11 @@
:scroll-with-animation="false"
:enable-back-to-top="true"
>
<view v-for="(item, index) in state.orderInfo.promo_infos" :key="index">
<view class="ss-flex ss-m-b-40 subtitle">
<view>共{{ item.goods_ids.length }}件,</view>
<view v-if="item.activity_type === 'full_discount'">
满{{ item.discount_rule.full }}打{{ item.discount_rule.discount }}折,已减
</view>
<view v-if="item.activity_type === 'full_gift'">满赠</view>
<view v-if="item.activity_type === 'full_reduce'">
满{{ item.discount_rule.full }}减{{ item.discount_rule.discount }},已减
</view>
<view class="price-text">¥{{ item.promo_discount_money || '0.00' }}</view>
<view v-for="(item, index) in state.orderInfo.promotions" :key="index">
<!-- 不展示积分、优惠劵、会员折扣,因为它们已经单独展示了 -->
<view class="ss-flex ss-m-b-40 subtitle" v-if="[1, 2, 3, 4, 5].includes(item.type)">
<view> {{ item.description }} </view>
</view>
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
<view class="ss-flex">
<view v-for="i in item.goods_ids" :key="i">
<image class="content-img" :src="sheep.$url.cdn(getGoodsImg(i))" />
</view>
</view>
</scroll-view>
</view>
</scroll-view>
</view>
@@ -44,7 +30,6 @@
</template>
<script setup>
import { computed, reactive } from 'vue';
import sheep from '@/sheep';
const props = defineProps({
promoInfo: {
type: Array,
@@ -67,28 +52,22 @@
const state = reactive({
orderInfo: computed(() => props.modelValue),
});
const getGoodsImg = (e) => {
let goodsImg = '';
state.orderInfo.goods_list.forEach((i) => {
if (e == i.goods_id) {
goodsImg = i.goods.image;
}
});
return goodsImg;
};
</script>
<style lang="scss" scoped>
.model-box {
height: 60vh;
}
.model-content {
height: 54vh;
}
.modal-footer {
width: 100%;
height: 120rpx;
background: #fff;
}
.confirm-btn {
width: 710rpx;
margin-left: 20rpx;
@@ -97,17 +76,20 @@
border-radius: 40rpx;
color: #fff;
}
.content-img {
width: 140rpx;
height: 140rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
}
.subtitle {
font-size: 28rpx;
font-weight: 500;
color: #333333;
}
.price-text {
color: #ff3000;
}
+28 -8
View File
@@ -3,7 +3,10 @@
<!-- 商品卡片 -->
<view>
<!-- 布局1. 单列大图(上图,下内容)-->
<view v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.goodsList.length" class="goods-sl-box">
<view
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.goodsList.length"
class="goods-sl-box"
>
<view
class="goods-box"
v-for="item in state.goodsList"
@@ -100,7 +103,10 @@
</view>
<!-- 布局3. 单列小图(左图,右内容) -->
<view v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.goodsList.length" class="goods-lg-box">
<view
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.goodsList.length"
class="goods-lg-box"
>
<view
class="goods-box"
:style="[{ marginBottom: data.space + 'px' }]"
@@ -138,6 +144,8 @@
import { computed, reactive, onMounted } from 'vue';
import sheep from '@/sheep';
import SpuApi from '@/sheep/api/product/spu';
import OrderApi from '@/sheep/api/trade/order';
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
// 布局类型
const LayoutTypeEnum = {
@@ -147,7 +155,7 @@
TWO_COL: 'twoCol',
// 单列小图
ONE_COL_SMALL_IMG: 'oneColSmallImg',
}
};
const state = reactive({
goodsList: [],
@@ -157,16 +165,16 @@
const props = defineProps({
data: {
type: Object,
default() {},
default: () => ({}),
},
styles: {
type: Object,
default() {},
default: () => ({}),
},
});
const { layoutType, btnBuy, spuIds } = props.data ?? {};
const { marginLeft, marginRight } = props.styles ?? {};
const { layoutType, btnBuy, spuIds } = props.data || {};
const { marginLeft, marginRight } = props.styles || {};
// 购买按钮样式
const buyStyle = computed(() => {
@@ -215,6 +223,7 @@
// 计数
count++;
}
//endregion
/**
@@ -231,8 +240,17 @@
onMounted(async () => {
// 加载商品列表
state.goodsList = await getGoodsListByIds(spuIds.join(','));
// 拼接结算信息(营销)
await OrderApi.getSettlementProduct(state.goodsList.map((item) => item.id).join(',')).then(
(res) => {
if (res.code !== 0) {
return;
}
appendSettlementProduct(state.goodsList, res.data);
},
);
// 只有双列布局时需要
if (layoutType === LayoutTypeEnum.TWO_COL){
if (layoutType === LayoutTypeEnum.TWO_COL) {
// 分列
calculateGoodsColumn();
}
@@ -247,11 +265,13 @@
.goods-list-box {
width: 50%;
box-sizing: border-box;
.left-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
}
}
.right-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
@@ -11,7 +11,7 @@
<view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view>
<image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit"></image>
<image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit" />
<view
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
class="xs-goods-content ss-flex-col ss-row-around"
@@ -23,13 +23,40 @@
>
{{ data.title || data.name }}
</view>
<!-- 活动信息 -->
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
<view class="card" v-if="discountText">{{ discountText }}</view>
<view
class="card2"
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
:key="item"
>
{{ item }}
</view>
</view>
<view
v-if="goodsFields.price?.show"
class="xs-goods-price font-OPPOSANS"
:style="[{ color: goodsFields.price.color }]"
>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
<!-- 活动价格 -->
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="point-text ss-m-r-16">
{{ data.point }}
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
</text>
</view>
<template v-else>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
<text v-else>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</text>
</template>
</view>
</view>
</view>
@@ -39,23 +66,57 @@
<view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view>
<image class="sm-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
<image
class="sm-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
class="sm-goods-content"
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
>
<view v-if="goodsFields.title?.show || goodsFields.name?.show" class="sm-goods-title ss-line-1 ss-m-b-16">
<view
v-if="goodsFields.title?.show || goodsFields.name?.show"
class="sm-goods-title ss-line-1 ss-m-b-16"
>
{{ data.title || data.name }}
</view>
<!-- 活动信息 -->
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
<view class="card" v-if="discountText">{{ discountText }}</view>
<view
class="card2"
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
:key="item"
>
{{ item }}
</view>
</view>
<view
v-if="goodsFields.price?.show"
class="sm-goods-price font-OPPOSANS"
:style="[{ color: goodsFields.price.color }]"
>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
<!-- 活动价格 -->
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="point-text ss-m-r-16">
{{ data.point }}
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
</text>
</view>
<template v-else>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
<text v-else>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</text>
</template>
</view>
</view>
</view>
@@ -63,9 +124,9 @@
<!-- md卡片:竖向,一行放两个,图上内容下 -->
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
<view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)" />
</view>
<image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix"></image>
<image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix" />
<view
class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
:id="elId"
@@ -95,18 +156,47 @@
</view>
</view>
</slot>
<!-- 活动信息 -->
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
<view class="card" v-if="discountText">{{ discountText }}</view>
<view
class="card2"
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
:key="item"
>
{{ item }}
</view>
</view>
<view class="ss-flex ss-col-bottom">
<view
v-if="goodsFields.price?.show"
class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
:style="[{ color: goodsFields.price.color }]"
>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
<!-- 活动价格 -->
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="point-text ss-m-r-16">
{{ data.point }}
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
</text>
</view>
<template v-else>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
<text v-else>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</text>
</template>
</view>
<view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)"
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
:style="[{ color: originPriceColor }]"
>
@@ -137,11 +227,15 @@
<view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view>
<view v-if="seckillTag" class="seckill-tag ss-flex ss-row-center"> 秒杀 </view>
<view v-if="seckillTag" class="seckill-tag ss-flex ss-row-center">秒杀</view>
<view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center">
<view class="tag-icon">拼团</view>
</view>
<image class="lg-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
<image
class="lg-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
/>
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
<view>
<view
@@ -167,18 +261,44 @@
</view>
</view>
</slot>
<view class="ss-flex ss-col-bottom ss-m-t-10">
<!-- 活动信息 -->
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
<view class="card" v-if="discountText">{{ discountText }}</view>
<view
v-if="goodsFields.price?.show"
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: goodsFields.price.color }]"
class="card2"
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
:key="item"
>
<text class="ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
{{ item }}
</view>
</view>
<view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
<view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
<!-- 活动价格 -->
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="point-text ss-m-r-16">
{{ data.point }}
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
</text>
</view>
<template v-else>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
<text v-else>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</text>
</template>
</view>
<view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)"
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
:style="[{ color: originPriceColor }]"
>
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
@@ -192,20 +312,20 @@
</view>
<slot name="cart">
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow">
去购买
</view>
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> 去购买</view>
</slot>
</view>
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
<view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)" />
</view>
<image class="sl-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
<image
class="sl-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
/>
<view class="sl-goods-content">
<view>
<view
@@ -235,13 +355,43 @@
</view>
</view>
</slot>
<!-- 活动信息 -->
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
<view class="card" v-if="discountText">{{ discountText }}</view>
<view
class="card2"
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
:key="item"
>
{{ item }}
</view>
</view>
<view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
<view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
<!-- 活动价格 -->
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<text class="ss-m-r-16">
{{ data.point }}
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
</text>
</view>
<template v-else>
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
<text v-else>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</text>
</template>
</view>
<view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)"
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
:style="[{ color: originPriceColor }]"
>
@@ -255,9 +405,9 @@
</view>
</view>
<slot name="cart"
><view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view></slot
>
<slot name="cart">
<view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view>
</slot>
</view>
</view>
</template>
@@ -267,39 +417,41 @@
* 商品卡片
*
* @property {Array} size = [xs | sm | md | lg | sl ] - 列表数据
* @property {String} tag - md及以上才有
* @property {String} img - 图片
* @property {String} background - 背景色
* @property {String} topRadius - 上圆角
* @property {String} bottomRadius - 下圆角
* @property {String} title - 标题
* @property {String} titleColor - 标题颜色
* @property {String} tag - md及以上才有
* @property {String} img - 图片
* @property {String} background - 背景色
* @property {String} topRadius - 上圆角
* @property {String} bottomRadius - 下圆角
* @property {String} title - 标题
* @property {String} titleColor - 标题颜色
* @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
* @property {String} subTitle - 副标题
* @property {String} subTitleColor - 副标题颜色
* @property {String} subTitleBackground - 副标题背景
* @property {String | Number} price - 价格
* @property {String} priceColor - 价格颜色
* @property {String | Number} originPrice - 原价/划线价
* @property {String} originPriceColor - 原价颜色
* @property {String | Number} sales - 销售数量
* @property {String} salesColor - 销售数量颜色
* @property {String} subTitle - 副标题
* @property {String} subTitleColor - 副标题颜色
* @property {String} subTitleBackground - 副标题背景
* @property {String | Number} price - 价格
* @property {String} priceColor - 价格颜色
* @property {String | Number} originPrice - 原价/划线价
* @property {String} originPriceColor - 原价颜色
* @property {String | Number} sales - 销售数量
* @property {String} salesColor - 销售数量颜色
*
* @slots activity - 活动插槽
* @slots cart - 购物车插槽,默认包含文字,背景色,文字颜色 || 图片 || 行为
*
* @event {Function()} click - 点击卡片
* @event {Function()} click - 点击卡片
*
*/
import { computed, reactive, getCurrentInstance, onMounted, nextTick } from 'vue';
import { computed, getCurrentInstance, nextTick, onMounted } from 'vue';
import sheep from '@/sheep';
import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods';
import { formatStock } from '@/sheep/hooks/useGoods';
import goodsCollectVue from '@/pages/user/goods-collect.vue';
import { isArray } from 'lodash';
// 数据
const state = reactive({});
import {
fen2yuan,
formatExchange,
formatSales,
formatStock,
getRewardActivityRuleItemDescriptions,
} from '@/sheep/hooks/useGoods';
import { isArray } from 'lodash-es';
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
// 接收参数
const props = defineProps({
@@ -308,27 +460,39 @@
default() {
return {
// 商品价格
price: { show: true },
price: {
show: true,
},
// 库存
stock: { show: true },
stock: {
show: true,
},
// 商品名称
name: { show: true },
name: {
show: true,
},
// 商品介绍
introduction: { show: true },
introduction: {
show: true,
},
// 市场价
marketPrice: { show: true },
marketPrice: {
show: true,
},
// 销量
salesCount: { show: true },
salesCount: {
show: true,
},
};
},
},
tagStyle: {
type: Object,
default: {},
default: () => ({}),
},
data: {
type: Object,
default: {},
default: () => ({}),
},
size: {
type: String,
@@ -388,6 +552,17 @@
},
});
// 优惠文案
const discountText = computed(() => {
const promotionType = props.data.promotionType;
if (promotionType === 4) {
return '限时优惠';
} else if (promotionType === 6) {
return '会员价';
}
return undefined;
});
// 组件样式
const elStyles = computed(() => {
return {
@@ -403,10 +578,18 @@
const salesAndStock = computed(() => {
let text = [];
if (props.goodsFields.salesCount?.show) {
text.push(formatSales(props.data.sales_show_type, props.data.salesCount));
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
text.push(formatExchange(props.data.sales_show_type, (props.data.pointTotalStock || 0) - (props.data.pointStock || 0)));
} else {
text.push(formatSales(props.data.sales_show_type, props.data.salesCount));
}
}
if (props.goodsFields.stock?.show) {
text.push(formatStock(props.data.stock_show_type, props.data.stock));
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
text.push(formatStock(props.data.stock_show_type, props.data.pointTotalStock));
} else {
text.push(formatStock(props.data.stock_show_type, props.data.stock));
}
}
return text.join(' | ');
});
@@ -421,10 +604,14 @@
// 获取卡片实时高度
const { proxy } = getCurrentInstance();
const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
function getGoodsPriceCardWH() {
if (props.size === 'md') {
const view = uni.createSelectorQuery().in(proxy);
view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
view.select(`#${elId}`).fields({
size: true,
scrollOffset: true,
});
view.exec((data) => {
let totalHeight = 0;
const goodsPriceCard = data[0];
@@ -439,6 +626,7 @@
});
}
}
onMounted(() => {
nextTick(() => {
getGoodsPriceCardWH();
@@ -452,11 +640,13 @@
left: 0;
top: 0;
z-index: 2;
.tag-icon {
width: 72rpx;
height: 44rpx;
}
}
.seckill-tag {
position: absolute;
left: 0;
@@ -471,6 +661,13 @@
color: #ffffff;
line-height: 32rpx;
}
.point-img {
width: 30rpx;
height: 30rpx;
margin: 0 4rpx;
}
.groupon-tag {
position: absolute;
left: 0;
@@ -485,14 +682,17 @@
color: #ffffff;
line-height: 32rpx;
}
.goods-img {
width: 100%;
height: 100%;
background-color: #f5f5f5;
}
.price-unit {
margin-right: -4px;
}
.sales-text {
display: table;
font-size: 24rpx;
@@ -556,10 +756,12 @@
width: 100%;
height: 208rpx;
}
.sm-goods-content {
padding: 20rpx 16rpx;
box-sizing: border-box;
}
.sm-goods-title {
font-size: 26rpx;
color: #333;
@@ -589,6 +791,7 @@
color: #333;
width: 100%;
}
.md-goods-subtitle {
font-size: 24rpx;
font-weight: 400;
@@ -639,6 +842,7 @@
// line-height: 36rpx;
// width: 410rpx;
}
.lg-goods-subtitle {
font-size: 24rpx;
font-weight: 400;
@@ -665,6 +869,7 @@
font-size: 24rpx;
color: #ffffff;
}
.tag-box {
width: 100%;
}
@@ -678,10 +883,12 @@
z-index: 1;
width: 100%;
background-color: $white;
.sl-goods-content {
padding: 20rpx 20rpx;
box-sizing: border-box;
}
.sl-img-box {
width: 100%;
height: 360rpx;
@@ -692,6 +899,7 @@
color: #333;
font-weight: 500;
}
.sl-goods-subtitle {
font-size: 24rpx;
font-weight: 400;
@@ -718,4 +926,33 @@
color: #ffffff;
}
}
.card {
width: fit-content;
height: fit-content;
padding: 2rpx 10rpx;
background-color: red;
color: #ffffff;
font-size: 24rpx;
margin-top: 5rpx;
}
.card2 {
width: fit-content;
height: fit-content;
padding: 2rpx 10rpx;
background-color: rgb(255, 242, 241);
color: #ff2621;
font-size: 24rpx;
margin: 5rpx 0 5rpx 5rpx;
}
.iconBox {
width: 100%;
height: fit-content;
margin-top: 10rpx;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
</style>
+10 -2
View File
@@ -28,6 +28,14 @@
>
¥{{ fen2yuan(price) }}
</view>
<view v-if="point && Number(price) > 0">+</view>
<view class="price-text ss-flex ss-col-center" v-if="point">
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="point-img"
></image>
<view>{{ point }}</view>
</view>
<view v-if="num" class="total-text ss-flex ss-col-center">x {{ num }}</view>
<slot name="priceSuffix"></slot>
</view>
@@ -88,7 +96,7 @@
type: [String, Number],
default: 0,
},
score: {
point: {
type: [String, Number],
default: '',
},
@@ -113,7 +121,7 @@
</script>
<style lang="scss" scoped>
.score-img {
.point-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
@@ -1,83 +1,166 @@
<!-- 装修组件 - 拼团 -->
<!-- 装修商品组件:【拼团】商品卡片 -->
<template>
<!-- 商品卡片 -->
<view>
<!-- 布局1. 单列大图(上图,下内容)-->
<view
v-if="layoutType === 'threeCol'"
class="goods-sm-box ss-flex ss-flex-wrap"
:style="[{ margin: '-' + data.space + 'rpx' }]"
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
class="goods-sl-box"
>
<view
v-for="product in productList"
:key="product.id"
class="goods-card-box"
:style="[
{
padding: data.space + 'rpx',
},
]"
class="goods-box"
v-for="item in state.spuList"
:key="item.id"
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
>
<s-goods-column
class="goods-card"
size="sm"
class=""
size="sl"
:goodsFields="data.fields"
:tagStyle="tagStyle"
:data="product"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@click="
sheep.$router.go('/pages/goods/groupon', {
id: props.data.activityId,
})
"
></s-goods-column>
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<!-- 样式2 一行一个 图片左 文案右 -->
<view class="goods-box" v-if="layoutType === 'oneCol'">
<!-- 布局2. 单列小图(左图,右内容) -->
<view
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
class="goods-lg-box"
>
<view
class="goods-list"
v-for="(product, index) in productList"
:key="index"
:style="[{ marginBottom: space + 'px' }]"
class="goods-box"
:style="[{ marginBottom: data.space + 'px' }]"
v-for="item in state.spuList"
:key="item.id"
>
<s-goods-column
class="goods-card"
size="lg"
:goodsFields="data.fields"
:tagStyle="tagStyle"
:data="product"
:data="item"
:tagStyle="data.badge"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@click="
sheep.$router.go('/pages/goods/groupon', {
id: props.data.activityId,
})
"
@tap="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy?.type === 'text' ? btnBuy.text : '' }}
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<!-- 布局3. 双列(每一列:上图,下内容)-->
<view
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
>
<view class="goods-list-box">
<view
class="left-list"
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
v-for="item in state.leftSpuList"
:key="item.id"
>
<s-goods-column
class="goods-md-box"
size="md"
:goodsFields="data.fields"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
:titleWidth="330 - marginLeft - marginRight"
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
@getHeight="calculateGoodsColumn($event, 'left')"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<view class="goods-list-box">
<view
class="right-list"
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
v-for="item in state.rightSpuList"
:key="item.id"
>
<s-goods-column
class="goods-md-box"
size="md"
:goodsFields="data.fields"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
:titleWidth="330 - marginLeft - marginRight"
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
@getHeight="calculateGoodsColumn($event, 'right')"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
</view>
</view>
</template>
<script setup>
/**
* 拼团
* 商品卡片
*/
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, reactive, ref } from 'vue';
import sheep from '@/sheep';
import SpuApi from "@/sheep/api/product/spu";
import CombinationApi from "@/sheep/api/promotion/combination";
import CombinationApi from '@/sheep/api/promotion/combination';
import SpuApi from '@/sheep/api/product/spu';
// 接收参数
// 布局类型
const LayoutTypeEnum = {
// 单列大图
ONE_COL_BIG_IMG: 'oneColBigImg',
// 双列
TWO_COL: 'twoCol',
// 单列小图
ONE_COL_SMALL_IMG: 'oneColSmallImg',
};
const state = reactive({
spuList: [],
leftSpuList: [],
rightSpuList: [],
});
const props = defineProps({
data: {
type: Object,
@@ -89,19 +172,19 @@
},
});
let { layoutType, tagStyle, btnBuy, space } = props.data;
let { marginLeft, marginRight } = props.styles;
const { layoutType, btnBuy, activityIds } = props.data || {};
const { marginLeft, marginRight } = props.styles || {};
// 购买按钮样式
const buyStyle = computed(() => {
let btnBuy = props.data.btnBuy;
if (btnBuy?.type === 'text') {
if (btnBuy.type === 'text') {
// 文字按钮:线性渐变背景颜色
return {
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
};
}
if (btnBuy?.type === 'img') {
if (btnBuy.type === 'img') {
// 图片按钮
return {
width: '54rpx',
height: '54rpx',
@@ -111,21 +194,127 @@
}
});
const productList = ref([]);
//region 商品瀑布流布局
// 下一个要处理的商品索引
let count = 0;
// 左列的高度
let leftHeight = 0;
// 右列的高度
let rightHeight = 0;
/**
* 计算商品在左列还是右列
* @param height 商品的高度
* @param where 添加到哪一列
*/
function calculateGoodsColumn(height = 0, where = 'left') {
// 处理完
if (!state.spuList[count]) return;
// 增加列的高度
if (where === 'left') leftHeight += height;
if (where === 'right') rightHeight += height;
// 添加到矮的一列
if (leftHeight <= rightHeight) {
state.leftSpuList.push(state.spuList[count]);
} else {
state.rightSpuList.push(state.spuList[count]);
}
// 计数
count++;
}
//endregion
/**
* 根据商品编号列表,获取商品列表
* @param ids 商品编号列表
* @return {Promise<undefined>} 商品列表
*/
async function getCombinationActivityDetailList(ids) {
const { data } = await CombinationApi.getCombinationActivityListByIds(ids);
return data;
}
/**
* 根据商品编号,获取商品详情
* @param ids 商品编号列表
* @return {Promise<undefined>} 商品列表
*/
async function getSpuDetail(ids) {
const { data: spu } = await SpuApi.getSpuDetail(ids);
return spu;
}
// 初始化
onMounted(async () => {
// todo:@owen 与Yudao结构不一致,待重构
const { data: activity } = await CombinationApi.getCombinationActivity(props.data.activityId);
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId)
productList.value = [spu];
// 加载活动列表
const activityList = await getCombinationActivityDetailList(activityIds.join(','));
// 循环获取活动商品SPU详情并添加到spuList
for (const activity of activityList) {
state.spuList.push(await getSpuDetail(activity.spuId));
}
// 循环活动列表
activityList.forEach((activity) => {
// 提取活动价格
const combinationPrice = activity.combinationPrice || Infinity;
// 查找对应的 spu 并更新价格
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
if (spu) {
// 赋值活动名称
// TODO 芋艿:暂定活动名。会在调研一些类似有赞、淘宝、京东的选择
spu.name = activity.name;
// 赋值最低价格
spu.price = Math.min(combinationPrice, spu.price);
// 赋值活动ID,为了点击跳转详情页
spu.activityId = activity.id;
}
});
// 只有双列布局时需要
if (layoutType === LayoutTypeEnum.TWO_COL) {
// 分列
calculateGoodsColumn();
}
});
</script>
<style lang="scss" scoped>
.goods-list {
.goods-md-wrap {
width: 100%;
}
.goods-list-box {
width: 50%;
box-sizing: border-box;
.left-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
}
}
.right-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
}
}
}
.goods-box {
&:nth-last-of-type(1) {
margin-bottom: 0 !important;
}
}
.goods-md-box,
.goods-sl-box,
.goods-lg-box {
position: relative;
.cart-btn {
position: absolute;
bottom: 10rpx;
bottom: 18rpx;
right: 20rpx;
z-index: 11;
height: 50rpx;
@@ -136,19 +325,4 @@
color: #fff;
}
}
.goods-list {
&:nth-last-of-type(1) {
margin-bottom: 0 !important;
}
}
.goods-sm-box {
margin: 0 auto;
box-sizing: border-box;
.goods-card-box {
flex-shrink: 0;
overflow: hidden;
width: 33.3%;
box-sizing: border-box;
}
}
</style>
+23 -8
View File
@@ -31,7 +31,11 @@
></view>
<!-- 顶部导航栏-情况4:装修组件导航栏-沉浸式 -->
<s-custom-navbar v-if="navbar === 'custom' && navbarMode === 'inner'" :data="navbarStyle" :showLeftButton="showLeftButton" />
<s-custom-navbar
v-if="navbar === 'custom' && navbarMode === 'inner'"
:data="navbarStyle"
:showLeftButton="showLeftButton"
/>
<!-- 页面内容插槽 -->
<slot />
@@ -56,12 +60,11 @@
/**
* 模板组件 - 提供页面公共组件,属性,方法
*/
import { computed, reactive, ref } from 'vue';
import { computed } from 'vue';
import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import { onShow } from '@dcloudio/uni-app';
import { isEmpty } from 'lodash-es';
// #ifdef MP-WEIXIN
import { onShareAppMessage } from '@dcloudio/uni-app';
import { onShareAppMessage, onShareTimeline, onShow } from '@dcloudio/uni-app';
// #endif
const props = defineProps({
@@ -153,7 +156,7 @@
const bgMain = computed(() => {
if (navbarMode.value === 'inner') {
return {
background: `${props.bgStyle.backgroundColor} url(${sheep.$url.cdn(
background: `${props.bgStyle.backgroundColor || props.bgStyle.color} url(${sheep.$url.cdn(
props.bgStyle.backgroundImage,
)}) no-repeat top center / 100% auto`,
};
@@ -165,7 +168,7 @@
const bgBody = computed(() => {
if (navbarMode.value === 'normal') {
return {
background: `${props.bgStyle.backgroundColor} url(${sheep.$url.cdn(
background: `${props.bgStyle.backgroundColor || props.bgStyle.color} url(${sheep.$url.cdn(
props.bgStyle.backgroundImage,
)}) no-repeat top center / 100% auto`,
};
@@ -187,7 +190,11 @@
});
// #ifdef MP-WEIXIN
// 微信小程序分享
uni.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline'],
});
// 微信小程序分享好友
onShareAppMessage(() => {
return {
title: shareInfo.value.title,
@@ -195,6 +202,14 @@
imageUrl: shareInfo.value.image,
};
});
// 微信小程序分享朋友圈
onShareTimeline(() => {
return {
title: shareInfo.value.title,
query: shareInfo.value.path,
imageUrl: shareInfo.value.image,
};
});
// #endif
onShow(() => {
@@ -70,8 +70,8 @@
default() {},
},
});
const { mode, goodsFields, mpliveIds } = props.data ?? {};
const { marginLeft, marginRight } = props.styles ?? {};
const { mode, goodsFields, mpliveIds } = props.data || {};
const { marginLeft, marginRight } = props.styles || {};
async function getLiveListByIds(ids) {
const { data } = await sheep.$api.app.mplive.getRoomList(ids);
+306 -326
View File
@@ -1,363 +1,343 @@
<!-- 装修基础组件:菜单导航(金刚区) -->
<template>
<!-- 包裹层 -->
<view
class="ui-swiper"
:class="[props.mode, props.bg, props.ui]"
:style="[{ height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]"
>
<!-- 轮播 -->
<swiper
:circular="props.circular"
:current="state.cur"
:autoplay="props.autoplay"
:interval="props.interval"
:duration="props.duration"
:style="[{ height: swiperHeight + 'rpx' }]"
@change="swiperChange"
>
<swiper-item
v-for="(arr, index) in menuList"
:key="index"
:class="{ cur: state.cur == index }"
>
<!-- 宫格 -->
<view class="grid-wrap">
<view
v-for="(item, index) in arr"
:key="index"
class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]"
hover-class="ss-hover-btn"
@tap="sheep.$router.go(item.url)"
>
<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
<view
v-if="item.badge.show"
class="tag-box"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]"
>
{{ item.badge.text }}
</view>
<image
v-if="item.iconUrl"
class="menu-icon"
:style="[
<!-- 包裹层 -->
<view class="ui-swiper" :class="[props.mode, props.ui]"
:style="[bgStyle, { height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]">
<!-- 轮播 -->
<swiper :circular="props.circular" :current="state.cur" :autoplay="props.autoplay" :interval="props.interval"
:duration="props.duration" :style="[{ height: swiperHeight + 'rpx' }]" @change="swiperChange">
<swiper-item v-for="(arr, index) in menuList" :key="index" :class="{ cur: state.cur == index }">
<!-- 宫格 -->
<view class="grid-wrap">
<view v-for="(item, index) in arr" :key="index"
class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]" hover-class="ss-hover-btn"
@tap="sheep.$router.go(item.url)">
<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
<view v-if="item.badge.show" class="tag-box"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
{{ item.badge.text }}
</view>
<image v-if="item.iconUrl" class="menu-icon" :style="[
{
width: props.iconSize + 'rpx',
height: props.iconSize + 'rpx',
},
]"
:src="sheep.$url.cdn(item.iconUrl)"
mode="aspectFill"
></image>
<view
v-if="data.layout === 'iconText'"
class="menu-title"
:style="[{ color: item.titleColor }]"
>
{{ item.title }}
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 指示点 -->
<template v-if="menuList.length > 1">
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">
<view
class="line-box"
v-for="(item, index) in menuList.length"
:key="index"
:class="[state.cur == index ? 'cur' : '', props.dotCur]"
></view>
</view>
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
<view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
</view>
</view>
</template>
</view>
]" :src="sheep.$url.cdn(item.iconUrl)" mode="aspectFill"></image>
<view v-if="data.layout === 'iconText'" class="menu-title"
:style="[{ color: item.titleColor }]">
{{ item.title }}
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 指示点 -->
<template v-if="menuList.length > 1">
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">
<view class="line-box" v-for="(item, index) in menuList.length" :key="index"
:class="[state.cur == index ? 'cur' : '', props.dotCur]"></view>
</view>
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
<view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
</view>
</view>
</template>
</view>
</template>
<script setup>
/**
* 轮播menu
*
* @property {Boolean} circular = false - 是否采用衔接滑动,即播放到末尾后重新回到开头
* @property {Boolean} autoplay = true - 是否自动切换
* @property {Number} interval = 5000 - 自动切换时间间隔
* @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持
* @property {Array} list = [] - 轮播数据
* @property {String} ui = '' - 样式class
* @property {String} mode - 模式
* @property {String} dotStyle - 指示点样式
* @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色
* @property {String} bg - 背景
*
* @property {String|Number} col = 4 - 一行数量
* @property {String|Number} row = 1 - 几行
* @property {String} hasBorder - 是否有边框
* @property {String} borderColor - 边框颜色
* @property {String} background - 背景
* @property {String} hoverClass - 按压样式类
* @property {String} hoverStayTime - 动画时间
*
* @property {Array} list - 导航列表
* @property {Number} iconSize - 图标大小
* @property {String} color - 标题颜色
*
*/
/**
* 轮播menu
*
* @property {Boolean} circular = false - 是否采用衔接滑动,即播放到末尾后重新回到开头
* @property {Boolean} autoplay = true - 是否自动切换
* @property {Number} interval = 5000 - 自动切换时间间隔
* @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持
* @property {Array} list = [] - 轮播数据
* @property {String} ui = '' - 样式class
* @property {String} mode - 模式
* @property {String} dotStyle - 指示点样式
* @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色
* @property {String} bg - 背景
*
* @property {String|Number} col = 4 - 一行数量
* @property {String|Number} row = 1 - 几行
* @property {String} hasBorder - 是否有边框
* @property {String} borderColor - 边框颜色
* @property {String} background - 背景
* @property {String} hoverClass - 按压样式类
* @property {String} hoverStayTime - 动画时间
*
* @property {Array} list - 导航列表
* @property {Number} iconSize - 图标大小
* @property {String} color - 标题颜色
*
*/
import { reactive, computed } from 'vue';
import sheep from '@/sheep';
import {
reactive,
computed
} from 'vue';
import sheep from '@/sheep';
// 数据
const state = reactive({
cur: 0,
});
// 数据
const state = reactive({
cur: 0,
});
// 接收参数
// 接收参数
const props = defineProps({
data: {
type: Object,
default() {},
},
styles: {
type: Object,
default() {},
},
circular: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 5000,
},
duration: {
type: Number,
default: 500,
},
const props = defineProps({
// 装修数据
data: {
type: Object,
default: () => ({}),
},
// 装修样式
styles: {
type: Object,
default: () => ({}),
},
circular: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 5000,
},
duration: {
type: Number,
default: 500,
},
ui: {
type: String,
default: '',
},
mode: {
//default
type: String,
default: 'default',
},
dotStyle: {
type: String,
default: 'long', //default long tag
},
dotCur: {
type: String,
default: 'ui-BG-Main',
},
height: {
type: Number,
default: 300,
},
// 是否有边框
hasBorder: {
type: Boolean,
default: true,
},
// 边框颜色
borderColor: {
type: String,
default: 'red',
},
background: {
type: String,
default: 'blue',
},
hoverClass: {
type: String,
default: 'ss-hover-class', //'none'为没有hover效果
},
// 一排宫格数
col: {
type: [Number, String],
default: 3,
},
iconSize: {
type: Number,
default: 80,
},
color: {
type: String,
default: '#000',
},
});
ui: {
type: String,
default: '',
},
mode: {
//default
type: String,
default: 'default',
},
dotStyle: {
type: String,
default: 'long', //default long tag
},
dotCur: {
type: String,
default: 'ui-BG-Main',
},
bg: {
type: String,
default: 'bg-none',
},
height: {
type: Number,
default: 300,
},
// 设置背景样式
const bgStyle = computed(() => {
// 直接从 props.styles 解构
const {
bgType,
bgImg,
bgColor
} = props.styles;
// 是否有边框
hasBorder: {
type: Boolean,
default: true,
},
// 边框颜色
borderColor: {
type: String,
default: 'red',
},
background: {
type: String,
default: 'blue',
},
hoverClass: {
type: String,
default: 'ss-hover-class', //'none'为没有hover效果
},
// 一排宫格数
col: {
type: [Number, String],
default: 3,
},
iconSize: {
type: Number,
default: 80,
},
color: {
type: String,
default: '#000',
},
});
// 根据 bgType 返回相应的样式
return {
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
};
});
// 生成数据
const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));
const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));
const windowWidth = sheep.$platform.device.windowWidth;
// 生成数据
const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));
const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));
const windowWidth = sheep.$platform.device.windowWidth;
// current 改变时会触发 change 事件
const swiperChange = (e) => {
state.cur = e.detail.current;
};
// current 改变时会触发 change 事件
const swiperChange = (e) => {
state.cur = e.detail.current;
};
// 重组数据
const splitData = (oArr = [], length = 1) => {
let arr = [];
let minArr = [];
oArr.forEach((c) => {
if (minArr.length === length) {
minArr = [];
}
if (minArr.length === 0) {
arr.push(minArr);
}
minArr.push(c);
});
// 重组数据
const splitData = (oArr = [], length = 1) => {
let arr = [];
let minArr = [];
oArr.forEach((c) => {
if (minArr.length === length) {
minArr = [];
}
if (minArr.length === 0) {
arr.push(minArr);
}
minArr.push(c);
});
return arr;
};
return arr;
};
</script>
<style lang="scss" scoped>
.grid-wrap {
width: 100%;
display: flex;
position: relative;
box-sizing: border-box;
overflow: hidden;
flex-wrap: wrap;
align-items: center;
}
.menu-box {
position: relative;
z-index: 1;
transform: translate(0, 0);
.grid-wrap {
width: 100%;
display: flex;
position: relative;
box-sizing: border-box;
overflow: hidden;
flex-wrap: wrap;
align-items: center;
}
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: -6rpx;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
.menu-box {
position: relative;
z-index: 1;
transform: translate(0, 0);
.menu-icon {
transform: translate(0, 0);
width: 80rpx;
height: 80rpx;
padding-bottom: 10rpx;
}
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: -6rpx;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
.menu-title {
font-size: 24rpx;
color: #333;
}
}
.menu-icon {
transform: translate(0, 0);
width: 80rpx;
height: 80rpx;
padding-bottom: 10rpx;
}
::v-deep(.ui-swiper) {
position: relative;
z-index: 1;
.menu-title {
font-size: 24rpx;
color: #333;
}
}
.ui-swiper-dot {
position: absolute;
width: 100%;
bottom: 20rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
::v-deep(.ui-swiper) {
position: relative;
z-index: 1;
&.default .line-box {
display: inline-flex;
border-radius: 50rpx;
width: 6px;
height: 6px;
border: 2px solid transparent;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
justify-content: center;
align-items: center;
.ui-swiper-dot {
position: absolute;
width: 100%;
bottom: 20rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
&.cur {
width: 8px;
height: 8px;
opacity: 1;
border: 0px solid transparent;
}
&.default .line-box {
display: inline-flex;
border-radius: 50rpx;
width: 6px;
height: 6px;
border: 2px solid transparent;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
justify-content: center;
align-items: center;
&.cur::after {
content: '';
border-radius: 50rpx;
width: 4px;
height: 4px;
background-color: #fff;
}
}
&.cur {
width: 8px;
height: 8px;
opacity: 1;
border: 0px solid transparent;
}
&.long .line-box {
display: inline-block;
border-radius: 100rpx;
width: 6px;
height: 6px;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
&.cur::after {
content: '';
border-radius: 50rpx;
width: 4px;
height: 4px;
background-color: #fff;
}
}
&.cur {
width: 24rpx;
opacity: 1;
}
&.long .line-box {
display: inline-block;
border-radius: 100rpx;
width: 6px;
height: 6px;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
&.cur::after {
}
}
&.cur {
width: 24rpx;
opacity: 1;
}
&.line {
bottom: 20rpx;
&.cur::after {}
}
.line-box {
display: inline-block;
width: 30px;
height: 3px;
opacity: 0.3;
position: relative;
&.line {
bottom: 20rpx;
&.cur {
opacity: 1;
}
}
}
.line-box {
display: inline-block;
width: 30px;
height: 3px;
opacity: 0.3;
position: relative;
&.tag {
justify-content: flex-end;
position: absolute;
bottom: 20rpx;
right: 20rpx;
}
}
}
</style>
&.cur {
opacity: 1;
}
}
}
&.tag {
justify-content: flex-end;
position: absolute;
bottom: 20rpx;
right: 20rpx;
}
}
}
</style>
+93 -71
View File
@@ -1,82 +1,104 @@
<!-- 装修基础组件:宫格导航 -->
<template>
<uni-grid :showBorder="Boolean(data.border)" :column="data.column">
<uni-grid-item
v-for="(item, index) in data.list"
:key="index"
@tap="sheep.$router.go(item.url)"
>
<view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center">
<view class="img-box">
<view
class="tag-box"
v-if="item.badge.show"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]"
>
{{ item.badge.text }}
</view>
<image class="menu-image" :src="sheep.$url.cdn(item.iconUrl)"></image>
</view>
<view :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<uni-grid :showBorder="Boolean(data.border)" :column="data.column">
<uni-grid-item v-for="(item, index) in data.list" :key="index" @tap="sheep.$router.go(item.url)">
<view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center">
<view class="img-box">
<view class="tag-box" v-if="item.badge.show"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
{{ item.badge.text }}
</view>
<image class="menu-image" :src="sheep.$url.cdn(item.iconUrl)"></image>
</view>
<view class="title-box ss-flex ss-flex-col ss-row-center ss-col-center">
<view class="grid-text" :style="[{ color: item.titleColor }]">
{{ item.title }}
</view>
<view class="grid-tip" :style="[{ color: item.subtitleColor }]">
{{ item.subtitle }}
</view>
</view>
</view>
</uni-grid-item>
</uni-grid>
</view>
<view class="title-box ss-flex ss-flex-col ss-row-center ss-col-center">
<view class="grid-text" :style="[{ color: item.titleColor }]">
{{ item.title }}
</view>
<view class="grid-tip" :style="[{ color: item.subtitleColor }]">
{{ item.subtitle }}
</view>
</view>
</view>
</uni-grid-item>
</uni-grid>
</template>
<script setup>
import sheep from '@/sheep';
import sheep from '@/sheep';
import {
computed
} from 'vue';
const props = defineProps({
data: {
type: Object,
default() {},
},
});
const props = defineProps({
// 装修数据
data: {
type: Object,
default: () => ({}),
},
// 装修样式
styles: {
type: Object,
default: () => ({}),
},
});
// 设置背景样式
const bgStyle = computed(() => {
// 直接从 props.styles 解构
const {
bgType,
bgImg,
bgColor
} = props.styles;
// 根据 bgType 返回相应的样式
return {
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
};
});
</script>
<style lang="scss" scoped>
.menu-image {
width: 24px;
height: 24px;
}
.grid-item-box {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.img-box {
position: relative;
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: 0;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
}
.menu-image {
width: 24px;
height: 24px;
}
.title-box {
.grid-tip {
font-size: 24rpx;
white-space: nowrap;
text-align: center;
}
}
}
</style>
.grid-item-box {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.img-box {
position: relative;
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: 0;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
}
.title-box {
.grid-tip {
font-size: 24rpx;
white-space: nowrap;
text-align: center;
}
}
}
</style>
+26 -2
View File
@@ -1,6 +1,6 @@
<!-- 装修用户组件:用户订单 -->
<template>
<view class="ss-order-menu-wrap ss-flex ss-col-center">
<view class="ss-order-menu-wrap ss-flex ss-col-center" :style="[style, { marginLeft: `${data.space}px` }]">
<view
class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center"
v-for="item in orderMap"
@@ -67,8 +67,32 @@
path: '/pages/order/list',
},
];
// 接收参数
const props = defineProps({
// 装修数据
data: {
type: Object,
default: () => ({}),
},
// 装修样式
styles: {
type: Object,
default: () => ({}),
},
});
// 设置角标
const numData = computed(() => sheep.$store('user').numData);
// 设置背景样式
const style = computed(() => {
// 直接从 props.styles 解构
const { bgType, bgImg, bgColor } = props.styles;
// 根据 bgType 返回相应的样式
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
});
</script>
<style lang="scss" scoped>
@@ -0,0 +1,328 @@
<!-- 装修商品组件:【积分商城】商品卡片 -->
<template>
<!-- 商品卡片 -->
<view>
<!-- 布局1. 单列大图(上图,下内容)-->
<view
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
class="goods-sl-box"
>
<view
class="goods-box"
v-for="item in state.spuList"
:key="item.id"
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
>
<s-goods-column
class=""
size="sl"
:goodsFields="data.fields"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<!-- 布局2. 单列小图(左图,右内容) -->
<view
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
class="goods-lg-box"
>
<view
class="goods-box"
:style="[{ marginBottom: data.space + 'px' }]"
v-for="item in state.spuList"
:key="item.id"
>
<s-goods-column
class="goods-card"
size="lg"
:goodsFields="data.fields"
:data="item"
:tagStyle="data.badge"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@tap="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<!-- 布局3. 双列(每一列:上图,下内容)-->
<view
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
>
<view class="goods-list-box">
<view
class="left-list"
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
v-for="item in state.leftSpuList"
:key="item.id"
>
<s-goods-column
class="goods-md-box"
size="md"
:goodsFields="data.fields"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
:titleWidth="330 - marginLeft - marginRight"
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
@getHeight="calculateGoodsColumn($event, 'left')"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
<view class="goods-list-box">
<view
class="right-list"
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
v-for="item in state.rightSpuList"
:key="item.id"
>
<s-goods-column
class="goods-md-box"
size="md"
:goodsFields="data.fields"
:tagStyle="data.badge"
:data="item"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
:titleWidth="330 - marginLeft - marginRight"
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
@getHeight="calculateGoodsColumn($event, 'right')"
>
<!-- 购买按钮 -->
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
</view>
</view>
</template>
<script setup>
/**
* 商品卡片
*/
import { computed, onMounted, reactive, ref } from 'vue';
import sheep from '@/sheep';
import SpuApi from '@/sheep/api/product/spu';
import PointApi from '@/sheep/api/promotion/point';
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
// 布局类型
const LayoutTypeEnum = {
// 单列大图
ONE_COL_BIG_IMG: 'oneColBigImg',
// 双列
TWO_COL: 'twoCol',
// 单列小图
ONE_COL_SMALL_IMG: 'oneColSmallImg',
};
const state = reactive({
spuList: [],
leftSpuList: [],
rightSpuList: [],
});
const props = defineProps({
data: {
type: Object,
default() {},
},
styles: {
type: Object,
default() {},
},
});
const { layoutType, btnBuy, activityIds } = props.data || {};
const { marginLeft, marginRight } = props.styles || {};
// 购买按钮样式
const buyStyle = computed(() => {
if (btnBuy.type === 'text') {
// 文字按钮:线性渐变背景颜色
return {
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
};
}
if (btnBuy.type === 'img') {
// 图片按钮
return {
width: '54rpx',
height: '54rpx',
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
backgroundSize: '100% 100%',
};
}
});
//region 商品瀑布流布局
// 下一个要处理的商品索引
let count = 0;
// 左列的高度
let leftHeight = 0;
// 右列的高度
let rightHeight = 0;
/**
* 计算商品在左列还是右列
* @param height 商品的高度
* @param where 添加到哪一列
*/
function calculateGoodsColumn(height = 0, where = 'left') {
// 处理完
if (!state.spuList[count]) return;
// 增加列的高度
if (where === 'left') leftHeight += height;
if (where === 'right') rightHeight += height;
// 添加到矮的一列
if (leftHeight <= rightHeight) {
state.leftSpuList.push(state.spuList[count]);
} else {
state.rightSpuList.push(state.spuList[count]);
}
// 计数
count++;
}
//endregion
/**
* 根据商品编号列表,获取商品列表
* @param ids 商品编号列表
* @return {Promise<undefined>} 商品列表
*/
async function getPointActivityDetailList(ids) {
const { data } = await PointApi.getPointActivityListByIds(ids);
return data;
}
/**
* 根据商品编号,获取商品详情
* @param ids 商品编号列表
* @return {Promise<undefined>} 商品列表
*/
async function getSpuDetail(ids) {
const { data: spu } = await SpuApi.getSpuDetail(ids);
return spu;
}
// 初始化
onMounted(async () => {
// 加载活动列表
const activityList = await getPointActivityDetailList(activityIds.join(','));
// 循环获取活动商品SPU详情并添加到spuList
for (const activity of activityList) {
state.spuList.push(await getSpuDetail(activity.spuId));
}
// 循环活动列表
activityList.forEach((activity) => {
// 查找对应的 spu 并更新价格
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
if (spu) {
spu.pointStock = activity.stock
spu.pointTotalStock = activity.totalStock
spu.point = activity.point
spu.pointPrice = activity.price
// 赋值活动ID,为了点击跳转详情页
spu.activityId = activity.id;
// 赋值活动类型
spu.activityType = PromotionActivityTypeEnum.POINT.type;
}
});
// 只有双列布局时需要
if (layoutType === LayoutTypeEnum.TWO_COL) {
// 分列
calculateGoodsColumn();
}
});
</script>
<style lang="scss" scoped>
.goods-md-wrap {
width: 100%;
}
.goods-list-box {
width: 50%;
box-sizing: border-box;
.left-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
}
}
.right-list {
&:nth-last-child(1) {
margin-bottom: 0 !important;
}
}
}
.goods-box {
&:nth-last-of-type(1) {
margin-bottom: 0 !important;
}
}
.goods-md-box,
.goods-sl-box,
.goods-lg-box {
position: relative;
.cart-btn {
position: absolute;
bottom: 18rpx;
right: 20rpx;
z-index: 11;
height: 50rpx;
line-height: 50rpx;
padding: 0 20rpx;
border-radius: 25rpx;
font-size: 24rpx;
color: #fff;
}
}
</style>
Loaded 100 of 134 files, more files were not shown because too many files have changed in this diff. Show more