664 Commits
Author SHA1 Message Date
lifizer 475972e7d0 Fix(云灵AI):[#5193]“我要送礼”推荐结果,点击下单/送礼按钮,规格弹窗弹出时,左上角的返回按钮仍然可以点击-2
(cherry picked from commit a54b674e6f)
2026-05-07 17:07:24 +08:00
lifizer 4c84f63a93 Fix(云灵AI):[#5228]礼品卡片显示调整为供应商名称
(cherry picked from commit 76eea8dcb4)
2026-05-07 14:18:34 +08:00
lifizer f1173ae292 Style(云灵AI):开放以图搜商品入口
(cherry picked from commit a50d274a59)
2026-05-07 14:18:21 +08:00
lifizer 83c34d473e Style(云灵AI):界面调整,屏蔽以图搜商品入口
(cherry picked from commit ced676832e)
2026-05-06 15:15:09 +08:00
lifizer 4708fce5a5 Refactor(云灵AI):无内容时可以展示列表卡片内容
(cherry picked from commit 69e5ba0169)
2026-05-06 15:15:05 +08:00
lifizer 47ab649122 Fix(云灵AI):[#5186]订单查询,盲盒订单显示商品区域,隐藏金额信息-2
(cherry picked from commit 6cb403c5d5)
2026-04-29 16:29:55 +08:00
lifizer e7e9ba0ec5 Fix(云灵AI):[#5193]返回主页,需要关闭规格弹窗
(cherry picked from commit 28ee55f9fd)
2026-04-29 16:29:51 +08:00
lifizer 49a19fe57d Fix(云灵AI):[#5186]订单查询,盲盒订单显示商品区域,隐藏金额信息
(cherry picked from commit c8bf87aa4b)
2026-04-29 16:29:47 +08:00
lifizer 2f29a42f9b Fix(云灵AI):[#5193]“我要送礼”推荐结果,点击下单/送礼按钮,规格弹窗弹出时,左上角的返回按钮仍然可以点击
(cherry picked from commit 53312c889e)
2026-04-29 16:29:43 +08:00
lifizer 1065fc0c37 Fix(云灵AI):[#5192]规格名称较长的订单,查询结果规格数据显示异常,改为规格数据超出一行时超出内容省略号隐藏
(cherry picked from commit e3a69a708c)
2026-04-29 16:29:39 +08:00
lifizer 26ff0d2dd8 Fix(云灵AI):[#5194]店铺封面为视频的店铺,推荐结果店铺封面显示空白
(cherry picked from commit a113312712)
2026-04-29 16:29:34 +08:00
lifizer 856d89a42f Fix(云灵AI):[#5188]订单查询,点击发送的问题修改为“帮我查询我的待发货、待收货订单信息”
(cherry picked from commit 77f803ef10)
2026-04-29 16:29:31 +08:00
lifizer 720b7b330c Fix(云灵AI):[#5185]送礼,点击下单/送礼按钮,弹出的规格弹窗内,立即购买按钮位置太靠下
(cherry picked from commit 52817b48a8)
2026-04-29 16:29:27 +08:00
lifizer a883e7d56f Fix(云灵AI):[#5186]订单查询,盲盒订单显示商品区域,隐藏金额信息
(cherry picked from commit 09a8d1b476)
2026-04-29 16:29:22 +08:00
lifizer d5b7e8e2e8 Style(云灵AI):界面调整
(cherry picked from commit 20ca6c988e)
2026-04-29 09:31:17 +08:00
lifizer 208930ba63 Style(云灵AI):界面调整
(cherry picked from commit 9bf4febea6)
2026-04-28 18:09:10 +08:00
lifizer 318a0f2294 Refactor(云灵AI):卡片列表换一换接口使用新的
(cherry picked from commit 3b8faf1eea)
2026-04-28 11:00:02 +08:00
lifizer f354c9d3b2 Refactor(云灵AI):换一换动态效果实现
(cherry picked from commit 898c3b74bc)
2026-04-28 09:53:37 +08:00
lifizer afc1c9d519 Refactor(云灵AI):换一换动态效果实现
(cherry picked from commit 3b935811e3)
2026-04-28 09:53:30 +08:00
lifizer cd458a6ec6 Chore(入驻):解决合并冲突未完全解决问题
(cherry picked from commit 0d1dfae3bd)
2026-04-27 14:22:44 +08:00
lifizer 05bd367e04 Refactor(首页):优化页面加载,图片使用懒加载
(cherry picked from commit 94b2af52d0)
2026-04-27 14:22:38 +08:00
lifizer 7c7d19bd16 Style(云灵AI):标题调整
(cherry picked from commit 8c1aa62024)
2026-04-27 14:22:32 +08:00
linxi 0202f14073 Fix:体验店显示判断调整
(cherry picked from commit 7dedcf773f)
2026-04-24 18:18:49 +08:00
linxi 320bda6ed8 Fix:体验店显示判断调整
(cherry picked from commit a8907717b2)
2026-04-24 17:45:53 +08:00
lifizer 1f5e9f1a49 Refactor(云灵AI):ws连接key调整为unionId + "-" + conversationId 2026-04-24 09:19:22 +08:00
linxi a863ee7c85 Style:资讯店铺链接优化 2026-04-23 18:28:54 +08:00
linxi 49580bc511 Style:体验店封面优化 2026-04-23 18:19:25 +08:00
linxi 341d4d5c70 Fix:订单列表商品图片调大 2026-04-23 18:15:07 +08:00
linxi a86b416ed8 Fix:团购订单核销后没有同步弹出体验券领取弹窗,用户首次进入该团购订单详情页才会显示弹窗 2026-04-23 12:58:43 +08:00
LinCyJang ef36b27c54 Fix:旅居订单增加轮询 2026-04-22 19:56:55 +08:00
lifizer 9c90600c82 Refactor(云灵AI):列表数据对接 2026-04-22 17:42:58 +08:00
linxi b4b6fed1ab Fix:5173 【商城小程序】首页甄选好礼的轮播,长时间熄屏再打开,首页甄选好礼的轮播会异常卡顿,目前发现有这个现象的机型:iPhone XR、iPhone 16Pro 2026-04-22 15:19:51 +08:00
linxi 9dc63c2207 Fix:5168 【商城小程序】文化图片改为非必填,去除必填标识 2026-04-22 15:08:29 +08:00
linxi 9fa58286e8 Fix:5172 【商城小程序】将左右间距加宽一点 2026-04-22 15:00:24 +08:00
linxi 3e0940f42c Fix:5126 【商城小程序】地址被驳回状态的提示词修改 2026-04-22 14:44:33 +08:00
LinCyJang 0b8d5be0d0 Fix:5168 【商城小程序】文化图片改为非必填,去除必填标识
(cherry picked from commit dac7c96189)
2026-04-21 20:46:00 +08:00
LinCyJang 505d383bff Fix:5115 【商城小程序】店铺链接数据显示混乱 2026-04-21 20:23:48 +08:00
LinCyJang 23af0da8fa Fix:5126 【商城小程序】地址被驳回状态的提示词修改 2026-04-21 19:51:46 +08:00
LinCyJang e3316844e3 Fix:5160 【商城小程序】在订单列表购物“正在加载中”状态下快速切换旅居标签,出现数据错位 2026-04-21 19:31:41 +08:00
linxi d0b0994184 Fix:5162 【商城小程序】待收货订单申请退款,退款中状态地址信息部分显示null 2026-04-21 19:24:28 +08:00
linxi 7cc4cfea35 Fix:5149 【商城小程序】商品详情页,点击「联系商家」弹出的商家电话弹窗,号码显示空白但可正常跳转拨号
(cherry picked from commit 97c4d7fe4f)
2026-04-21 19:08:01 +08:00
linxi ceb2d2e09e Fix:5148 【商城小程序】列表体验店卡片页面样式调整
(cherry picked from commit 29827b250a)
2026-04-21 19:07:57 +08:00
linxi d8725cd843 Fix:5150 【商城小程序】体验券按钮栏左右滑动时,按钮内容被异常遮挡,显示不完整
(cherry picked from commit edaba70c6e)
2026-04-21 19:07:53 +08:00
lifizer bf3a18cb4b Refactor(云灵AI):订单列表数据对接 2026-04-16 15:39:14 +08:00
lifizer e0ba6f7422 Refactor(云灵AI):礼品商品轮播效果细节调整 2026-04-16 10:36:21 +08:00
lifizer 0ad78b11db Refactor(云灵AI):以图搜索接口对接 2026-04-16 10:23:57 +08:00
lifizer b8413ab13f Refactor(云灵AI):各种列表展示通过类型逻辑对接 2026-04-15 23:55:47 +08:00
lifizer bca2513d22 Refactor(云灵AI):礼品商品轮播效果实现 2026-04-15 18:59:18 +08:00
linxi 1d8b75e2d2 Fix:5075 【商城小程序】点击体验店,链接跳转的体验店详情数据错误,获取的是旅居店铺的数据,应获取体验店数据 2026-04-13 19:19:42 +08:00
linxi 8f6b2a23d1 Fix:5085 【商城小程序】用户多笔订单支付成功后,自动跳转到"我的订单"-"待发货"页面 2026-04-13 19:11:09 +08:00
linxi 8c4d588ffa Fix:5081 【商城小程序】用户订单创建后未支付,自动跳转到"我的订单"-“待付款”页面 2026-04-13 18:50:25 +08:00
linxi 9cbf35d693 Fix:5145 【商城小程序】首页海报状态未实时更新,需重启小程序才生效 2026-04-13 18:41:39 +08:00
lifizer 2f5bb204bb Refactor(云灵AI):1、使用新模型逻辑对接;2、订单查询 2026-04-13 15:33:59 +08:00
linxi 2f5b17da92 Fix:5106 【商城小程序】选择地址页面,地址列表默认选中项逻辑错误 2026-04-13 15:29:52 +08:00
linxi 017e4d065a Fix:5125 【商城小程序】订单列表状态未同步刷新 2026-04-13 15:02:15 +08:00
linxi 4f2670ba01 Fix:5125 【商城小程序】订单列表状态未同步刷新 2026-04-13 15:00:10 +08:00
linxi 9e9a058325 Fix:5126 【商城小程序】地址被驳回状态的提示词修改 2026-04-13 14:31:34 +08:00
linxi 4dc16ed4ea Fix:5127 【商城小程序】地址审核中和驳回状态的订单显示问题 2026-04-13 14:29:05 +08:00
linxi 3c2f1bc329 Fix:5130 【商城小程序】修改地址审核中/驳回状态下申请退款,订单页仍显示相应提示信息,状态展示异常 2026-04-13 14:26:12 +08:00
linxi 533a5fa8bd Fix:5129 【商城小程序】体验券订单图片需放大一点且与名称对齐 2026-04-13 14:09:15 +08:00
linxi 7dfd10760d Fix:5133 【商城小程序】切换体验券时,下方的分类未重置,错误继承原体验券分类位置 2026-04-13 13:50:06 +08:00
linxi bf3145a2a7 Fix:5140 【商城小程序】点击店铺名称区域无页面跳转响应 2026-04-13 13:44:19 +08:00
linxi 50a36877e5 Style:排版问题 2026-04-08 09:14:56 +08:00
LinCyJang f97a547bc0 Fix:5106 【商城小程序】选择地址页面,地址列表默认选中项逻辑错误 2026-04-07 23:20:29 +08:00
LinCyJang e829b17e4e Fix:5065 【商城小程序】选择体验券下单跳转到订单列表,返回上一级页面会返回到体验券选择,期望返回到个人中心页面 2026-04-07 23:07:32 +08:00
LinCyJang 8df912d4fb Fix:5107 【商城小程序】旅居店铺详情页,没有显示标签数据 2026-04-07 23:05:41 +08:00
LinCyJang bfec7dcf59 Fix:5075 【商城小程序】点击体验店,链接跳转的体验店详情数据错误,获取的是旅居店铺的数据,应获取体验店数据 2026-04-07 22:52:12 +08:00
LinCyJang 9aeb0a2c7c Fix:5078 【商城小程序】提交修改地址申请后,订单未显示新地址和审核状态 2026-04-07 22:47:42 +08:00
LinCyJang 71803cdf80 Fix:5079 【商城小程序】新增体验店时营业时间数据默认未修改,生成的体验券订单详情页,营业时间没有获取到默认数据 2026-04-07 22:33:42 +08:00
LinCyJang 8b2aa20cf9 Fix:5082 【商城小程序】“我的订单”页面,暂无订单提示图标显示不全 2026-04-07 22:29:44 +08:00
LinCyJang f4c78b5e70 Fix:5105 【商城小程序】订单详情文字保持统一的左对齐 2026-04-07 22:25:09 +08:00
LinCyJang b474d55c8f Fix:5095 【商城小程序】体验店详情页,项目详情、备注、使用须知文本内容的文字大小统一 2026-04-07 22:20:23 +08:00
LinCyJang 24e20b16dd Fix:5086 【商城小程序】修改地址申请被驳回,前端未显示驳回原因 2026-04-07 22:13:47 +08:00
LinCyJang 689fa6ffc1 Feat:一人领取所有礼品 2026-04-07 21:59:38 +08:00
LinCyJang 8c5ef69979 Feat:资讯新增店铺 2026-04-07 21:45:03 +08:00
linxi aee2002a1e Feat:待发货状态订单增加修改地址功能 2026-04-02 20:06:57 +08:00
linxi 057a727c9e Feat:待发货状态订单增加修改地址功能 2026-04-02 20:06:23 +08:00
linxi 14e2fd9104 Fix:5037 【管理后台】旅居店铺详情页,体验店列表只显示了十个店铺,配置的店铺数据没有完全显示 2026-04-02 19:36:38 +08:00
linxi 50f5de6601 Fix:5043 【商城小程序】体验项目简介内容过长会被遮挡 2026-04-02 19:31:41 +08:00
linxi c0afa773a4 Fix:5044 【商城小程序】体验店底部安全区域未适配,出现遮挡 2026-04-02 19:27:05 +08:00
linxi cdf129430d Fix:5045 【商城小程序】团购详情页图文说明,图片优化为可点击放大查看 2026-04-02 19:23:49 +08:00
linxi 9a1f737988 Fix:5048 【商城小程序】点击体验店,增加跳转到对应体验店详情页的链接 2026-04-02 19:17:02 +08:00
linxi d91324c96b Fix:5049 【商城小程序】订单有效期显示问题 2026-04-02 19:06:12 +08:00
linxi 92782f45c0 Fix:5055 【商城小程序】体验项目详情,隐藏“体验项目”标题 2026-04-02 19:05:15 +08:00
linxi 5ef346e40f Fix:5058 【商城小程序】体验店详情页,位置信息下方增加定位距离显示 2026-04-02 19:04:22 +08:00
linxi c4839cbc9a Fix:5059 【商城小程序】体验券选择页面,体验券信息显示效果调整 2026-04-02 18:47:14 +08:00
linxi 4a0e0119e2 Fix:5060 【商城小程序】调整体验店信息位置,向上移动 2026-04-02 18:41:52 +08:00
linxi 204db18f8a Fix:5061 【商城小程序】体验店列表,体验店的简介内容改为超出一行的内容隐藏 2026-04-02 18:40:04 +08:00
linxi 1a1f8f8a69 Fix:5062 【商城小程序】体验店详情页-顶部视频左右两侧内容显示不全 2026-04-02 18:38:10 +08:00
linxi 8c15fd6712 Fix:5063 【商城小程序】体验店详情页,顶部视频播放完成后出现重复播放部分内容的现象 2026-04-02 18:35:35 +08:00
linxi e34a032795 Fix:5064 【商城小程序】旅居店铺,调整体验店列表,体验店内容区域的宽度 2026-04-02 18:33:21 +08:00
linxi b234cb4733 Fix:5064 【商城小程序】旅居店铺,调整体验店列表,体验店内容区域的宽度 2026-04-02 18:31:02 +08:00
linxi 7993549bfd Fix:5066 【商城小程序】体验券订单未显示当前位置与该体验店的距离 2026-04-02 17:56:22 +08:00
linxi 68a215e491 Fix:5067 【商城小程序】体验券订单主题图片需放大一点 2026-04-02 17:31:39 +08:00
linxi 6e581da413 Fix:5073 【商城小程序】已过期体验券订单详情页隐藏到期时间 2026-04-02 17:21:15 +08:00
linxi 4cbeab62dc Fix:5074 【商城小程序】已完成、已过期体验券订单详情页需更换状态栏底色 2026-04-02 17:19:20 +08:00
linxi 00832cbca5 Fix:5068 【商城小程序】体验店名称和订单号间距过大 2026-04-02 17:15:35 +08:00
LinCyJang d823b42a3c Fix:5025 【管理后台】已有入驻通过的体验店,再次点击体验店入驻海报,应弹窗提示是否跳转供应链端 2026-03-30 21:17:21 +08:00
LinCyJang 55cc76f58e Fix:5036 【商城小程序】旅居店铺详情页,文化内容不需要显示 2026-03-30 21:14:13 +08:00
LinCyJang 24796348cc Fix:5038 【商城小程序】配置多个体验项目时,应整体轮播显示 2026-03-30 21:03:45 +08:00
LinCyJang 9234581c6e Fix:5039 【商城小程序】订单详情页,体验店店铺营业时间、定位、导航和客服图标的位置需要调整 2026-03-30 20:43:43 +08:00
LinCyJang 3f55102301 Fix:5041 【商城小程序】订单详情页,体验店店铺图片数据获取错误 2026-03-30 20:38:08 +08:00
linxi cdece3d92f Feat:旅居团购产品详情 2026-03-30 17:13:53 +08:00
lifizer 2d964da4e3 Refactor(AI):暂时屏蔽AI分析请求 2026-03-30 13:55:44 +08:00
LinCyJang c10a4bb722 Fix:4990 【商城小程序】审核中状态,撤销入驻申请,提示撤销成功,但重新进入资料填写页面,仍然显示审核中状态 2026-03-29 12:59:36 +08:00
LinCyJang 7cc08491e8 Fix:5019 【商城小程序】后台未配置体验项目详情内容,前端无需显示详情标题 2026-03-29 12:08:44 +08:00
LinCyJang a2f9ee2377 Fix:5004 【商城小程序】待使用订单核销后,当前页面未自动刷新更新订单状态 2026-03-29 12:03:19 +08:00
LinCyJang cb260c7b56 Fix:4993 【商城小程序】顶部轮播区域高度不足,内容显示不全 2026-03-29 11:17:09 +08:00
LinCyJang 2073526a24 Fix:5012 【商城小程序】待使用订单有效期数据显示错误 2026-03-29 11:05:29 +08:00
LinCyJang 956fa0692d Fix:4965 【商城小程序】体验券选择页面,页面套餐信息位置样式调整 2026-03-29 11:02:06 +08:00
LinCyJang c625ab209a Fix:4956 【管理后台】管理后台新增店铺时未修改默认营业时间数据直接保存,前端体验店的营业时间数据显示与管理后台数据不相符 2026-03-29 10:54:26 +08:00
LinCyJang 8d0847f957 Fix:4956 【管理后台】管理后台新增店铺时未修改默认营业时间数据直接保存,前端体验店的营业时间数据显示与管理后台数据不相符 2026-03-26 23:14:06 +08:00
LinCyJang 754c36b53d Fix:4990 【商城小程序】审核中状态,撤销入驻申请,提示撤销成功,但重新进入资料填写页面,仍然显示审核中状态 2026-03-26 23:11:30 +08:00
LinCyJang 447d7b7445 Fix:4980 【商城小程序】待使用体验券订单详情页,体验项目名称数据获取错误,应获取体验项目-主题名称,当前显示的是体验店店铺名称 2026-03-26 23:00:21 +08:00
LinCyJang 044beb3db5 Fix:4965 【商城小程序】体验券选择页面,页面套餐信息位置样式调整 2026-03-26 22:58:33 +08:00
LinCyJang 1c68c9fb74 Fix:4971 【商城小程序】体验券详情页—店铺分类无数据时隐藏分类 2026-03-26 22:54:27 +08:00
LinCyJang fb3fac914b Fix:4984 【商城小程序】体验券订单详情页,体验店店铺信息-营业时间数据获取错误 2026-03-26 22:48:00 +08:00
LinCyJang 874ec14028 Fix:4985 【商城小程序】体验券订单详情页,体验店店铺名称数据获取错误 2026-03-26 22:44:31 +08:00
LinCyJang 5114132692 Fix:4993 【商城小程序】顶部轮播区域高度不足,内容显示不全 2026-03-26 22:38:08 +08:00
LinCyJang 61f1973663 Fix:4994 【商城小程序】顶部轮播的视频播放时间过短(只播放3秒就切换到下一张轮播图) 2026-03-26 22:36:04 +08:00
LinCyJang 46008c261b Fix:4995 【商城小程序】已完成订单下方“返回主页”按钮点击无反应 2026-03-26 21:59:46 +08:00
LinCyJang 601d2242b7 Fix:5002 【商城小程序】点击“先去体验”/“确认”按钮应跳转到旅居订单列表页 2026-03-26 21:56:56 +08:00
LinCyJang d55f7725a4 Fix:5003 【商城小程序】体验券的选中状态与上方“已选清单”显示不一致 2026-03-26 21:51:57 +08:00
LinCyJang 5f1ea960e2 Fix:5006 【商城小程序】列表体验店卡片尺寸未统一 2026-03-26 21:39:19 +08:00
LinCyJang 685488f14e Fix:4975 【商城小程序】点击蓝色的首页海报,应进入旅居订单列表页 2026-03-23 23:16:15 +08:00
LinCyJang 6546f53a3b Fix:4951 【商城小程序】体验项目-详情信息数据获取错误,当前获取的是管理后台配置的体验项目列表-项目配置的内容,应获取体验项目-详情内容 2026-03-23 22:55:28 +08:00
LinCyJang b37b2bf4b8 Fix:4931 【商城小程序】商家微信改为非必填(当前是必填项) 2026-03-23 22:53:24 +08:00
LinCyJang 2084dc9969 Fix:4932 【商城小程序】未填写地理定位的提示信息“请选择物理位置”改为“请选择地理定位” 2026-03-23 22:47:36 +08:00
LinCyJang 2568f045e5 Fix:4951 【商城小程序】体验项目-详情信息数据获取错误,当前获取的是管理后台配置的体验项目列表-项目配置的内容,应获取体验项目-详情内容 2026-03-23 22:46:27 +08:00
LinCyJang b638a3aa66 Fix:4953 【商城小程序】体验项目详情页,点击营业执照按钮不会跳转显示对应资质内容 2026-03-23 22:41:49 +08:00
LinCyJang 990684840b Fix:4953 【商城小程序】体验项目详情页,点击营业执照按钮不会跳转显示对应资质内容 2026-03-23 22:34:52 +08:00
LinCyJang 23d5726ed1 Fix:4960 【商城小程序】体验店顶部图片数据获取错误,当前显示的是后台配置的体验项目-主题图片数据,应获取体验店配置的视频+轮播图数据 2026-03-23 22:30:46 +08:00
LinCyJang a5edae4020 Fix:4956 【管理后台】管理后台新增店铺时未修改默认营业时间数据直接保存,前端体验店的营业时间数据显示与管理后台数据不相符 2026-03-23 22:23:27 +08:00
LinCyJang e53fbf2fc0 Fix:4965 【商城小程序】体验券选择页面,页面套餐信息位置样式调整 2026-03-23 22:16:55 +08:00
LinCyJang 54bf1c5700 Fix:4965 【商城小程序】体验券选择页面,页面套餐信息位置样式调整 2026-03-23 22:07:06 +08:00
LinCyJang bb052d12fe Fix:4977 【商城小程序】选择部分体验券时下方选择状态应只做展示 2026-03-23 21:57:33 +08:00
LinCyJang b13bd416bb Fix:4982 【商城小程序】待使用订单详情页,没有显示体验券有效期数据 2026-03-23 21:51:49 +08:00
LinCyJang 4e46dccdb8 Fix:4983 【商城小程序】体验券订单列表的“联系商家”按钮应跳转拨号页面 2026-03-23 21:39:17 +08:00
LinCyJang 20cea6ead7 Fix:4988 【商城小程序】待使用体验券订单详情页,核销码以及订单号缺少复制按钮 2026-03-23 21:26:36 +08:00
LinCyJang c941d77c3d Fix:4989 【商城小程序】体验券订单详情页,体验店信息缺少导航和联系客服按钮 2026-03-23 21:17:59 +08:00
linxi d1413066ac Feat:体验店信息 2026-03-18 15:40:57 +08:00
linxi c66fac5992 Feat:体验订单样式优化 2026-03-18 12:22:28 +08:00
linxi e608382075 Feat:体验项目订单流程对接 2026-03-17 19:29:13 +08:00
LinCyJang 7091d7e124 Fix:体验店入驻表单样式统一 2026-03-16 23:43:14 +08:00
linxi 9a647cffd6 Feat:体验券详情 2026-03-16 18:09:41 +08:00
linxi ad70e3afed Feat:体验项目详情 2026-03-15 16:21:45 +08:00
linxi 605cd4f054 Feat:体验券领取 2026-03-12 18:05:38 +08:00
linxi 6b849c5300 Feat:体验店入驻审核 2026-03-12 12:29:27 +08:00
linxi d0ca72c855 Fix:4906 【商城小程序】康养和旅居类目下页面内容偏左,需按效果图调整,并做成瀑布流形式 2026-03-03 14:03:02 +08:00
linxi c33f9dfc12 Fix:4906 【商城小程序】康养和旅居类目下页面内容偏左,需按效果图调整,并做成瀑布流形式 2026-03-03 10:08:56 +08:00
linxi 732a21e17f Fix:4903 【商城小程序】店铺入驻,店铺类型点击范围调整(点击右侧“>”按钮时没有弹出类型选择) 2026-02-28 11:08:38 +08:00
linxi be90afefe5 Fix:4906 【商城小程序】康养和旅居类目下页面内容偏左,需按效果图调整,并做成瀑布流形式 2026-02-28 10:49:06 +08:00
linxi b572976e08 Fix:4903 【商城小程序】店铺入驻,店铺类型点击范围调整(点击右侧“>”按钮时没有弹出类型选择) 2026-02-11 11:54:32 +08:00
linxi d6d8222831 Fix:4897 【商城小程序】用户收到客服消息,消息图标未显示红点提示 2026-02-10 11:30:53 +08:00
linxi 86fc52a62e Fix:4902 【商城小程序】在旅居下搜索店铺,被搜索的店铺未配置简介时,简介区域会显示null 2026-02-10 10:55:16 +08:00
linxi bdfc025094 Fix:4890 【商城小程序】旅居类目下当前显示的是养生圣地所有类目下的店铺数据,应只显示旅居店铺的数据 2026-02-06 18:26:52 +08:00
linxi 0c682398c4 Fix:4890 【商城小程序】旅居类目下当前显示的是养生圣地所有类目下的店铺数据,应只显示旅居店铺的数据 2026-02-06 18:19:08 +08:00
linxi df024b8210 Fix:4901 【商城小程序】购物类订单列表—“待收货/待核销”改为“待收货” 2026-02-06 18:16:32 +08:00
linxi 910a837638 Feat:旅居选择日期这里需要加个左右滑动翻页的功能 2026-02-06 10:52:29 +08:00
linxi 39d9c1a76a Feat:旅居选择日期这里需要加个左右滑动翻页的功能 2026-02-05 17:41:09 +08:00
linxi 23286ae940 Fix:康养背景图显示不全问题 2026-02-05 17:34:55 +08:00
linxi 810ee7f5b8 Fix:4895 【商城小程序】用户看完新消息后,首页红点数字提示未消除 2026-02-05 16:11:17 +08:00
linxi 65ede45e47 Fix:4896 【商城小程序】康养类目,分类类目及数据位置需要上移调整 2026-02-05 14:52:26 +08:00
LinCyJang 4d595d07c1 Fix:4866 【商城小程序】康养类目下,应显示管理后台配置的养生圣地类目及数据(参考图2) 2026-02-05 00:21:45 +08:00
LinCyJang 0ac517f969 Fix:4865 【商城小程序】康养旅居的旅居分类调整 2026-02-04 23:42:09 +08:00
LinCyJang f486d35470 Fix:4839 【商城小程序】已完成的团购订单详情页,去除立即评价按钮 2026-02-04 23:16:54 +08:00
LinCyJang 91f0657470 Fix:4843 【商城小程序】订单列表:旅居订单状态名称修改 2026-02-04 23:10:39 +08:00
LinCyJang 14abdb02b6 Fix:4863 【商城小程序】搜索列表中的旅居商户点击进入时提示“店铺不存在” 2026-02-04 22:51:32 +08:00
LinCyJang b7d6935e31 Fix:4864 【商城小程序】千县万村资讯未简化显示 2026-02-04 22:44:29 +08:00
LinCyJang 969ec9937a Fix:4867 【商城小程序】营业执照名称未设置成必填项 2026-02-04 22:31:25 +08:00
LinCyJang 39edd3da1f Fix:4876 【商城小程序】选择预约时间进行预订后,先弹出团购协议,再跳转结算页面 2026-02-04 22:27:21 +08:00
LinCyJang 2c7fa054d3 Fix:4871 【商城小程序】预约时间选择完成之后,“确定”按钮改为“立即预订” 2026-02-04 22:16:01 +08:00
LinCyJang ad9181e4cb Fix:4877 【商城小程序】进入预约时间选择弹窗的触发范围需要调整 2026-02-04 22:14:41 +08:00
LinCyJang 2b8f75e890 Fix:4868 【商城小程序】古镇列表无需显示推荐店铺轮播图 2026-02-04 22:11:27 +08:00
LinCyJang af347912b2 Fix:4878 【商城小程序】重新进入小程序时,当前定位城市的推荐店铺轮播图不显示 2026-02-04 22:06:33 +08:00
LinCyJang 325df3d6ca Fix:4879 【商城小程序】列表中无店铺数据时无需显示“换一换”按钮 2026-02-04 21:53:06 +08:00
LinCyJang 99be70d7f6 Fix:4881 【商城小程序】城市未配置推荐店铺,店铺列表的“换一换”按钮改为在列表右侧显示 2026-02-04 21:49:56 +08:00
linxi c507176f8f Feat:店铺搜索增加旅居店铺 2026-02-02 11:10:50 +08:00
linxi 86f1443b42 Feat:删除用户商品浏览足迹 2026-02-02 10:55:40 +08:00
LinCyJang 56c00e61aa Feat:如果有新消息,则做红点数字提示 2026-01-31 16:16:56 +08:00
LinCyJang 5983c4d17e Feat:寻千县万村店铺增加2级分类 2026-01-31 16:08:08 +08:00
LinCyJang 2abd6533ba Feat:趣游-轮播图按地区显示 2026-01-31 16:04:42 +08:00
LinCyJang cf4d198682 Feat:寻千县万村店铺增加2级分类 2026-01-31 15:47:40 +08:00
LinCyJang 7b1ac89b5b Feat:寻千县万村店铺增加2级分类 2026-01-31 15:45:51 +08:00
LinCyJang 2682ecaecd Feat:咨询观看量,点赞数显示调整 2026-01-31 14:39:41 +08:00
LinCyJang 285224284f Feat:提交订单时可编辑地址 2026-01-31 14:10:00 +08:00
LinCyJang 0304b93966 Feat:入驻新增营业执照名称 2026-01-31 13:52:10 +08:00
LinCyJang 7eeb313931 Feat:旅居日期选了开始日期自动选择结束日期 2026-01-31 13:41:39 +08:00
LinCyJang 3da6899db0 Feat:旅居页面调整 2026-01-31 13:34:59 +08:00
linxi 0072ccb2d1 Fix:多份盲盒和地址选中样式 2026-01-20 09:59:58 +08:00
linxi cd0123f383 Fix:会展入口屏蔽 2026-01-19 14:04:30 +08:00
linxi 97291ac3ba Fix:默认改旅居名称 2026-01-19 13:59:32 +08:00
LinCyJang eff6581680 Merge branch 'dev' of https://gitee.com/fengyuze178/xdd-uniapp-new into dev
# Conflicts:
#	pkg_user/views/gift/receive.vue
2026-01-17 19:07:09 +08:00
LinCyJang 5e31dde283 Fix:多份盲盒问题 2026-01-17 19:06:05 +08:00
linxi 0593ff0f02 Fix:4825 【商城小程序】在不暂停视频资讯的情况下点击旅居店铺顶部轮播视频,下方视频资讯没有暂停 2026-01-16 10:25:50 +08:00
linxi 06bc1a740f Fix:4819 【商城小程序】已被领取的盲盒礼包,在24小时后查看领取页面会显示为礼包已失效(24小时内、24小时后查看都应该显示为已领取的效果) 2026-01-16 10:11:32 +08:00
LinCyJang 557fd0dfe3 Fix:4216 【商城小程序】领取礼包,选择收货地址时,增加一个选中样式 2026-01-15 00:12:20 +08:00
LinCyJang b6159d03ab Fix:4739 【商城小程序】送礼包界面,点击数量弹出键盘,点击空白处收起键盘时屏幕出现闪屏现象 2026-01-14 23:53:52 +08:00
LinCyJang 4897edce74 Fix:4735 【商城小程序】管理后台未配置养生榜单数据的情况下点击【确认】后,前端无需显示养生榜单模块 2026-01-14 23:21:15 +08:00
LinCyJang 84ddb0790e Fix:4819 【商城小程序】已被领取的盲盒礼包,在24小时后查看领取页面会显示为礼包已失效(24小时内、24小时后查看都应该显示为已领取的效果) 2026-01-14 23:11:42 +08:00
LinCyJang 4332fe91db Fix:4813 【商城小程序】评价内容只有星级,没有填写其他内容时,星级下方增加显示“此用户没有填写评价” 2026-01-14 23:00:15 +08:00
LinCyJang f61e611a51 Fix:4812 【商城小程序】评价内容的星星图标大小调整(调小一点) 2026-01-14 22:54:52 +08:00
LinCyJang e582b521fe Fix:4810 【商城小程序】团购详情页显示了全部评价内容,改为显示最新一条评价内容即可 2026-01-14 22:29:25 +08:00
LinCyJang 7da3e2ae1a Fix:4809 【商城小程序】评价列表,好评率的“%”符号重复显示 2026-01-14 22:20:39 +08:00
LinCyJang 4412103c8a Fix:4808 【商城小程序】团购订单评价完成后详情页未刷新,评价完成后,评价按钮需要隐藏 2026-01-14 22:18:48 +08:00
LinCyJang debcc2010a Fix:4807 【商城小程序】团购订单完成评价后,订单状态内容为空,建议状态调整为“已完成” 2026-01-14 22:10:30 +08:00
LinCyJang eb2fafdc6e Fix:4806 【商城小程序】待评价的团购订单,在订单列表处显示的状态改为“待评价” 2026-01-14 21:57:29 +08:00
LinCyJang 12c32fbf51 Fix:4797 【商城小程序】购物车图标点击效果优化(①单规格商品:库存充足的情况下可以直接加入购物车,②多规格商品:弹出规格选择弹窗。参考首页尖货推荐处的商品加购图标点击效果) 2026-01-14 21:49:01 +08:00
LinCyJang ea79af5bd9 Fix:4796 【商城小程序】礼包封面选择弹窗下方提示信息“红包”改为“礼包” 2026-01-14 20:42:04 +08:00
LinCyJang 692468346d Fix:4791 【商城小程序】特色礼品页面—展开城市列表时,下方商品图片遮挡选择区域 2026-01-14 20:39:57 +08:00
LinCyJang ea5f9cc628 Feat:预览旅居团购协议 2026-01-11 01:34:50 +08:00
LinCyJang 79e23a66cf Feat;旅居评价列表 2026-01-11 01:03:37 +08:00
LinCyJang 6f367223c5 Feat;旅居订单评价 2026-01-10 23:56:33 +08:00
LinCyJang 0038846328 Feat:新增旅居订单分类 2026-01-10 23:35:04 +08:00
LinCyJang 9b5847c4ce Fix:产品故事ios字体 2026-01-05 20:03:19 +08:00
LinCyJang e90c3d38cc Fix:已领取按钮 2026-01-05 19:51:57 +08:00
lifizer 1c06f8af06 Conf(地图):使用企业的更新key 2026-01-05 17:29:23 +08:00
lifizer 1780c4ff0a Conf(地图):使用天地图定位获取省市区 2026-01-05 11:03:48 +08:00
LinCyJang f4fcc7e8f5 Fix:4736 【商城小程序】我的优惠券—“去使用”按钮点击后需跳转至首页 2026-01-04 19:40:33 +08:00
LinCyJang 1b79ae1678 Fix:4778 【商城小程序】产品故事苹果手机字体单独调整,字体加大2个字号 2026-01-04 19:39:03 +08:00
LinCyJang e6c7b3d989 Fix:4786 【商城小程序】领取礼包后的效果调整 2026-01-04 19:20:36 +08:00
lifizer ae2853e60b Conf(地图):使用企业的更新key 2026-01-04 17:29:14 +08:00
linxi f72477a247 Fix:4779 【商城小程序】商品送礼:点击“再送一份”,礼包封面选择弹窗会显示团购类的礼包封面 2026-01-04 10:26:18 +08:00
linxi 97bb3b2ee6 Fix:4778 【商城小程序】产品故事苹果手机字体单独调整,字体加大2个字号 2026-01-04 10:15:49 +08:00
linxi 8f88e1bbab Fix:所有团购详情页2026年1月以后都显示不可预约,但是进入选择日期页面却可以预约 2025-12-31 17:37:57 +08:00
LinCyJang 0c07084775 Fix:4777 【商城小程序】如果某商品有产品故事封面,则将产品故事封面图显示在第一位 2025-12-30 23:21:12 +08:00
LinCyJang 3caa936e1e Fix:4738 【商城小程序】团队管理—推广统计、推广人订单栏点击无反应,无法跳转进入对应详情页 2025-12-28 20:47:47 +08:00
LinCyJang 7153ce8e9e Fix:4736 【商城小程序】我的优惠券—“去使用”按钮点击后需跳转至首页 2025-12-28 20:38:23 +08:00
LinCyJang 6a6da9b3a6 Fix:4740 【商城小程序】店铺封面为视频,封面图在康养顶部不显示(安卓手机) 2025-12-28 20:31:09 +08:00
LinCyJang d70440b948 Task:产品故事 2025-12-28 20:00:42 +08:00
LinCyJang 5ba8a653a7 Fix:4753 【商城小程序】盲盒礼包领取页面-领取按钮未居中,图片被遮挡(未开启好友转赠功能) 2025-12-28 19:45:46 +08:00
LinCyJang 36e9c5fca2 Task:产品故事 2025-12-28 19:30:06 +08:00
LinCyJang 8e22a48789 Fix:4754 ,【商城小程序】非盲盒礼品,增加我也要送礼按钮(收礼方收取礼品后,盲盒礼品会显示我也要送礼按钮,但普通礼品目前没有) 2025-12-27 23:44:05 +08:00
LinCyJang 254a9d0e57 Fix:4753 【商城小程序】盲盒礼包领取页面-领取按钮未居中,图片被遮挡(未开启好友转赠功能) 2025-12-27 22:18:22 +08:00
linxi a86ac4d6e5 Fix:千县店铺提示不存在 2025-12-23 09:39:09 +08:00
linxi 84b8b21799 Fix:4732 【旅居商城小程序】用户评论供应商、特色店铺的资讯,店主收到的用户评论内容会显示null 2025-12-22 15:22:21 +08:00
linxi 93c59abe13 Fix:礼包页不显示 2025-12-22 15:15:06 +08:00
linxi 2512db3e08 Fix:4731 【旅居商城小程序】千县资讯已点赞的情况下,资讯详情页的点赞按钮颜色与列表的不一致 2025-12-22 10:44:37 +08:00
linxi e269a3a7af Fix:4733 【旅居商城小程序】把县城产业项目板块隐藏 2025-12-22 10:38:29 +08:00
linxi 00d630377e Fix:4729 【旅居商城小程序】分类总长度超出页面时,在不划动的情况下,最左侧文本也会被顶出页面(机型:苹果12,在不划动分类的情况下,最左侧文本应顶格显示) 2025-12-22 09:21:39 +08:00
LinCyJang 0fd11fe68d Fix:4721 【旅居商城小程序】已被领取的礼包在24小时后查看领取页面会显示为礼包已失效(应该显示为已领取的效果) 2025-12-16 20:06:59 +08:00
LinCyJang 364972c25b Fix:4722 【旅居商城小程序】千县资讯-点击评论图标没有跳转到资讯详情页 2025-12-16 19:56:48 +08:00
LinCyJang d2e48bc16f Fix:4725 【旅居商城小程序】互动消息界面,回复内容与被回复内容交换位置 2025-12-16 19:52:09 +08:00
LinCyJang 5ec97e99b8 Fix:4726 【旅居商城小程序】点击“产品”类目下方商品的加购图标后,千县攻略、资讯类目的数据加载失败 2025-12-16 19:47:44 +08:00
LinCyJang 0292dbd056 Feat:千县店铺 2025-12-14 11:34:20 +08:00
LinCyJang f055036001 Feat:千县资讯分类&评论功能 2025-12-13 14:49:59 +08:00
LinCyJang 86ae127763 Feat:招商政策移位 2025-12-13 14:49:18 +08:00
linxi e05eb3e389 Fix:收礼页调整 2025-12-11 16:13:46 +08:00
linxi a6735d284e Fix:康养搜索增加 shopType=wellness 2025-12-11 10:06:43 +08:00
linxi 1f4ea5c0c8 Fix:旅居分享问题 2025-12-10 18:23:12 +08:00
lifizer eaa36dcf44 Fix(专题页):尖货&节日分享朋友圈进入小程序渲染错误 2025-12-10 16:57:42 +08:00
linxi 2f4d4510a5 Fix:甄选好礼调整 2025-12-09 14:09:19 +08:00
LinCyJang 07aec60ce9 Fix:4696 【旅居商城小程序】礼包领取页面:商品图尺寸偏小,位置偏下,与管理后台配置效果不一致 2025-12-08 22:17:45 +08:00
LinCyJang 1109861934 Fix:4375 【旅居商城小程序】旅居商家主页分享模板 2025-12-08 21:58:57 +08:00
linxi 1ad946e7be Fix:4696 【旅居商城小程序】礼包领取页面:商品图尺寸偏小,位置偏下,与管理后台配置效果不一致 2025-12-05 18:52:16 +08:00
linxi 52c72c97a7 Fix:4712 【旅居商城小程序】旅居店铺详情页,管家列表-管家信息显示的是管家详情介绍内容,改为管家简介内容 2025-12-05 18:25:08 +08:00
linxi 23491dab19 Fix:4709 【旅居商城小程序】团购礼包订单详情页:“满减优惠”未获取到优惠券金额 2025-12-05 18:24:51 +08:00
linxi 9093167bbd Fix:4690 【旅居商城小程序】送礼方礼包订单详情页增加团购总价、满减 2025-12-05 12:09:11 +08:00
linxi 7485208d31 Fix:4692 【旅居商城小程序】店铺封面为视频,店铺入口图在康养列表中不显示(安卓手机) 2025-12-05 12:02:15 +08:00
linxi 949d33614a Fix:4700 【旅居商城小程序】千县旅居类目:在运营管理-千县名品城市管理配置的旅居店铺数据在前端未显示(目前只显示了旅居商户管理配置的旅居商家数据) 2025-12-05 11:57:41 +08:00
linxi 7afcf039b6 Fix:4696 【旅居商城小程序】礼包领取页面:商品图尺寸偏小,位置偏下,与管理后台配置效果不一致 2025-12-04 18:52:41 +08:00
linxi 702d7c1891 Fix:4702 【旅居商城小程序】我的礼品卡-送出的礼包列表:团购礼包的价格改为使用优惠券后的实付金额 2025-12-04 18:42:06 +08:00
linxi 9720c5d1f0 Fix:4703 【旅居商城小程序】首页轮播商品修改为不需要跳转商品链接,点击后直接进入甄选好礼板块 2025-12-04 18:38:55 +08:00
linxi 88f3223e41 Fix:4705 【旅居商城小程序】礼品卡列表中商品、团购封面图片显示尺寸不统一 2025-12-04 18:35:46 +08:00
linxi 970bfef484 Fix:4706 【旅居商城小程序】管家详情页,管家标签内容超出界面宽度时,调整管家标签换行显示效果 2025-12-04 18:26:08 +08:00
linxi 41b33e3819 Fix:4356 【旅居商城小程序】旅居商户入驻上传视频大小超过500M的提示信息需修改为“视频大小不超过500MB” 2025-12-02 09:28:46 +08:00
linxi 161e53cc15 Fix:4663 【旅居商城小程序】无法搜索到对应旅居商户 2025-12-01 09:21:48 +08:00
LinCyJang d69c41e193 Fix:4678 【旅居商城小程序】团购详情页,套餐详情图片增加点击查看大图的功能 2025-11-30 12:19:54 +08:00
LinCyJang ef3cd4a610 Fix:4671 【旅居商城小程序】分类总长度超出页面时,在不划动的情况下,最左侧文本也会被顶出页面(机型:苹果12,在不划动分类的情况下,最左侧文本应顶格显示) 2025-11-30 12:09:39 +08:00
LinCyJang 7f9b228f64 Fix:4672 【旅居商城小程序】团购送礼:已选择优惠券,但实际付款时并未使用成功 2025-11-30 12:00:05 +08:00
LinCyJang 8ea730c287 Fix:4667 【商城小程序】弹窗设置为点击海报领取优惠券时,领取优惠券后会继续弹窗,可重复领取优惠券,需要限制领取后不再弹窗 2025-11-30 11:58:23 +08:00
LinCyJang c877cf245a Fix:4666 【旅居商城小程序】管理后台新增旅居商户没有配置简介时,前端的简介区域会显示null(没有配置则无需显示) 2025-11-30 11:52:40 +08:00
LinCyJang 9db2bc3b68 Fix:4663 【旅居商城小程序】无法搜索到对应旅居商户 2025-11-30 11:44:18 +08:00
linxi 2df1a79997 Fix:4586 【旅居商城小程序】产生订单后,团购被删除,待确认、待入住状态下修改入住时间失败,应弹窗提示信息 2025-11-28 09:24:12 +08:00
linxi 41a4b19f13 Fix:4666 【旅居商城小程序】管理后台新增旅居商户没有配置简介时,前端的简介区域会显示null(没有配置则无需显示) 2025-11-26 10:51:42 +08:00
linxi a64da0bc21 Fix:需要显示康养圣地数据,且跳转需区分 2025-11-26 10:50:28 +08:00
linxi b82dc02177 Fix:4663 【旅居商城小程序】无法搜索到对应旅居商户 2025-11-26 10:45:04 +08:00
linxi 677bf57d05 Fix:4665 【旅居商城小程序】商品、旅居团购送礼:点击封面选择弹窗外的区域,无法将弹窗收起(改为:①可以收起弹窗,②点外面收起弹窗时,取消保存新选择的封面 2025-11-26 10:30:56 +08:00
linxi 9778bfc1ac Fix:4651 【旅居商城小程序】团购详情页底部交互按钮区域样式优化 2025-11-25 17:29:12 +08:00
linxi 5204db106d Fix:4665 【旅居商城小程序】商品、旅居团购送礼:点击封面选择弹窗外的区域,无法将弹窗收起(改为:①可以收起弹窗,②点外面收起弹窗时,取消保存新选择的封面) 2025-11-25 17:22:10 +08:00
linxi d7864d3a38 Fix:4664 【旅居商城小程序】团购选择其他优惠券无法生效,选择以后又自动改回默认的优惠券了 2025-11-25 17:21:57 +08:00
linxi b53e2d1eaa Fix:4655 【旅居商城小程序】在首页熄屏或切换软件10秒钟以上,甄选好礼轮播商品会出现异常抖动 2025-11-25 11:05:28 +08:00
linxi 642139d0a2 Fix:4658 【旅居商城小程序】iOS端的轮播商品名称、价格以及选购按钮位置整体上调一点,按钮下方留点空白 2025-11-25 10:23:47 +08:00
linxi 824a9f93de Fix:4655 【旅居商城小程序】在首页熄屏或切换软件10秒钟以上,甄选好礼轮播商品会出现异常抖动 2025-11-25 10:18:43 +08:00
linxi b65301baa2 Fix:4651 【旅居商城小程序】团购详情页底部交互按钮区域样式优化 2025-11-25 09:55:27 +08:00
linxi c6e4c88eb7 Fix:4654 【旅居商城小程序】新上传的图,宽度是740,是370的两倍,高度缩短了一点,上传后还是拉伸 2025-11-25 09:45:49 +08:00
linxi 2a32e1f60a Fix:4601 【旅居管理后台,商城小程序】旅居商户管理—修改所属分类为“康养分类”,在前端小程序中不显示 2025-11-25 09:45:31 +08:00
linxi fe596ffff3 Feat:礼品红包封面尺寸调整 2025-11-24 11:56:52 +08:00
linxi 9de1574c86 Fix:4601 【旅居管理后台,商城小程序】旅居商户管理—修改所属分类为“康养分类”,在前端小程序中不显示 2025-11-24 11:31:26 +08:00
linxi 4ff62c50a1 Fix:4638 【旅居商城小程序】个人中心的订单列表—搜索框增加检索条件:团购名称 2025-11-24 11:19:30 +08:00
linxi 94536701e4 Fix:4651 【旅居商城小程序】团购详情页底部交互按钮区域样式优化 2025-11-24 11:18:09 +08:00
linxi 8c7df45451 Fix:4649 【旅居商城小程序】首页点击甄选好礼的轮播商品区域,跳转的是甄选好礼详情页,应跳转对应商品详情页 2025-11-24 11:13:45 +08:00
linxi 3a2de38cfe Fix:4649 【旅居商城小程序】首页点击甄选好礼的轮播商品区域,跳转的是甄选好礼详情页,应跳转对应商品详情页 2025-11-24 11:02:56 +08:00
linxi 3c1578e51f Feat:礼品封面尺寸大小调整 2025-11-22 17:57:32 +08:00
linxi 99f613148b Feat:首页甄选好礼轮播效果优化/ v2 2025-11-22 16:34:10 +08:00
linxi 7a44ed3265 Fix:4004 【商城小程序】在互动消息列表回复资讯评论,内容被顶至屏幕之外 2025-11-22 16:24:49 +08:00
linxi aaa29ef0f6 Feat:首页甄选好礼轮播效果优化 2025-11-22 16:09:49 +08:00
linxi b2121e71ef Fix:4628 【旅居商城小程序】团购订单提交页面——输入框内容,未固定位置。 2025-11-22 15:59:16 +08:00
linxi d43fc253c9 Fix:4646 【旅居商城小程序】团购礼包退款后,收礼方领取时的提示信息改为“礼品卡已失效”(iOS需修改提示,安卓需增加提示) 2025-11-22 15:22:46 +08:00
linxi efd70ae003 Feat:团购礼品支持优惠券 2025-11-22 15:15:13 +08:00
linxi cdf1f24b74 Feat:选择礼包封面按钮背景图根据后台配置获取 2025-11-22 14:37:58 +08:00
linxi 096b0cef0d Merge branch 'dev' of gitee.com:fengyuze178/xdd-uniapp-new into dev 2025-11-22 14:23:51 +08:00
linxi 19e598bce7 Feat:甄选好礼 2025-11-22 14:23:46 +08:00
lifizer 9c022a5a31 Conf(路由):删除无用的页面,方便打包控制主包体积在2M以内 2025-11-21 18:57:51 +08:00
linxi c9f3767cbd Fix:4643 【旅居商城小程序】没开转赠的团购礼包领取页面需要单独关闭右上角转发功能 2025-11-21 12:26:19 +08:00
linxi 889b0de9b7 Fix:4601 【旅居管理后台,商城小程序】旅居商户管理—修改所属分类为“康养分类”,在前端小程序中不显示 2025-11-21 12:22:17 +08:00
linxi 6e316a9f2c Fix:4629 【旅居供应链小程序】供应链小程序/PC端修改资讯,将图片资讯改为视频后,视频无法正常播放 2025-11-21 12:12:58 +08:00
linxi 4686c6d87a Fix:4634 【旅居商城小程序】播放资讯视频时,旅居店铺封面视频未暂停 2025-11-21 12:08:40 +08:00
linxi 896f8dee7f Fix:4632 【旅居商城小程序】团购详情轮播图循环逻辑错误 2025-11-21 12:06:24 +08:00
linxi d9ee41d8f3 Fix:4639 【旅居商城小程序】预约日期包含以下几个时间段时无法预约:1.31-2.1,3.31-4.1,5.31-6.1,8.31-9.1,10.31-11.1(应改为:只要选择的时间段可预约、并且等于团购天数,就可以预约成功) 2025-11-21 11:48:01 +08:00
lifizer 7a49c7c493 Feat(首页):分享朋友圈功能 2025-11-20 12:03:51 +08:00
linxi 13dee18ef1 Fix:4623 【旅居商城小程序】新增的视频资讯无法正常播放
(cherry picked from commit fe0397a483)
2025-11-19 10:48:59 +08:00
linxi 5a1d35bbf2 Fix:4613 【旅居商城小程序】待支付订单进行支付时,取消订单状态导航栏可点击状态
(cherry picked from commit a535c8192e)
2025-11-19 10:48:54 +08:00
linxi ac3be5ad00 Fix:4598 【旅居商城小程序】供应链小程序新增视频资讯,前端视频未显示
(cherry picked from commit 2e1fa524fa)
2025-11-19 10:48:49 +08:00
linxi d5ae4bb1b1 Fix:4610 【旅居商城小程序】资讯正文的文本格式(换行、字体加粗等)在前端未生效
(cherry picked from commit 48284adb6b)
2025-11-19 10:48:45 +08:00
linxi f884d1c764 Fix:4260 【旅居商城小程序】管理后台新增旅居商户后,相应的用户进入商城小程序点击入驻入口,弹出提示窗,即将打开供应链小程序。确认后打开供应链小程序,如下图所示 2025-11-17 15:03:43 +08:00
linxi 31ce97db6a Fix:4432 【旅居商城小程序】订单状态列表,待确认订单未显示确认倒计时 2025-11-17 15:03:29 +08:00
linxi 7786f88896 Fix:4373 【旅居商城小程序】审核中状态下无法撤销入驻申请(参考供应商和特色店铺的入驻流程,【审核中】状态标识提到顶部显示,底部改成【撤销申请】)/接口 2025-11-17 15:03:12 +08:00
linxi b260bb9e34 Fix:4373 【旅居商城小程序】审核中状态下无法撤销入驻申请(参考供应商和特色店铺的入驻流程,【审核中】状态标识提到顶部显示,底部改成【撤销申请】) 2025-11-17 14:04:56 +08:00
linxi 51ff86e4f8 Fix:4519 【旅居商城小程序】送礼—礼包订单类型改为“礼包订单” 2025-11-17 14:03:59 +08:00
linxi 3d8df506e0 Fix:4388 【旅居商城小程序】管理后台配置商家号码在前端小程序未生效 2025-11-17 11:51:15 +08:00
linxi 2d8f2b16a9 Fix:4600 【旅居管理后台,商城小程序】旅居商户管理—修改所属分类为“康养顶部”,在前端小程序所有康养城市顶部都会显示 2025-11-17 11:26:44 +08:00
linxi 1d34f4dd08 Fix:4602 【旅居商城小程序】提交团购订单页面,联系商家-点击立即拨打按钮无效 2025-11-17 11:10:34 +08:00
LinCyJang 6658d8d617 Merge branch 'v18' into dev 2025-11-15 01:35:14 +08:00
LinCyJang ee9d3c1507 Fix:4585 【旅居商城小程序】订单状态列表,待入住的交互按钮显示的是待收货商品订单的交互按钮,应改回待确认交互按钮的样式 2025-11-15 01:12:03 +08:00
LinCyJang 4e3d834820 Fix:4368 【商城小程序】弹窗设置为点击海报领取优惠券时,领取优惠券后会继续弹窗,可重复领取优惠券,需要改为领取后不再弹窗 2025-11-15 00:59:35 +08:00
LinCyJang 8cc32b5a19 Fix:4432 【旅居商城小程序】订单状态列表,待确认订单未显示确认倒计时 2025-11-15 00:44:21 +08:00
LinCyJang a98c1544bd Fix:4422 【旅居商城小程序】团购提交订单页面,点击联系商家按钮,弹出的弹窗内没有获取商家电话 2025-11-15 00:33:55 +08:00
LinCyJang ef2eb0cdeb Fix:4424 【旅居商城小程序】送礼—礼品卡微信支付取消时将提示词改为“支付取消,请在我的礼品卡中完成支付” 2025-11-15 00:28:13 +08:00
LinCyJang fc8257b1e3 Fix:4595 【旅居商城小程序】管家详情页,管家简介与详细介绍内容显示位置不正确 2025-11-15 00:26:10 +08:00
LinCyJang 67d1e7e5ed Fix:4584 【商城小程序】多规格商品,首个规格无库存时,点击商品数量收起键盘后,数量显示为0且数量能减少为负数。 2025-11-15 00:24:59 +08:00
LinCyJang 8ed2563e33 Fix: 【旅居商城小程序】旅居商家PC端上传的主页视频,在安卓和电脑端小程序未显示 2025-11-15 00:22:36 +08:00
LinCyJang 21c6bc90c7 Fix:4596 【旅居商城小程序】已支付团购礼包,订单详情页未同步支付状态 2025-11-14 23:45:46 +08:00
lifizer e475238946 Feat(AI):增加模块未定位进入时不显示天气信息逻辑 2025-11-14 17:59:13 +08:00
lifizer 19b9a26811 Feat(商品详情):增加分享朋友圈 2025-11-14 17:58:37 +08:00
lifizer bc5f60d58c Feat(专题页):增加分享朋友圈 2025-11-14 17:58:07 +08:00
lifizer 036ae02b7b Feat(千县):增加分享朋友圈 2025-11-14 17:57:36 +08:00
lifizer 8910281527 Feat(千县):增加AI入口 2025-11-14 17:57:02 +08:00
lifizer 98472cb048 Feat(非遗文创):图片大小调整 2025-11-14 17:56:07 +08:00
lifizer f12fb1012d Feat(非遗文创):增加AI入口 2025-11-14 17:55:45 +08:00
linxi e2802e8ca2 Fix:orderInfo._status && ['1'].includes(orderInfo._status._type)" 2025-11-13 15:39:00 +08:00
linxi 16d4f7d5e2 Fix:4581 【旅居商城小程序】送礼方列表的礼包订单无需显示入住时间 2025-11-13 15:35:07 +08:00
linxi 9739b0b4f1 Fix:4448 【旅居商城小程序】送礼—礼包已领取页面团购主图和名称显示问题 2025-11-13 15:29:48 +08:00
linxi fcb6b39060 Fix:4216 【商城小程序】领取礼包,选择收货地址时,增加一个选中样式 2025-11-13 15:22:26 +08:00
linxi 80ed47d5eb Fix:4182 【商城小程序】供应商入驻状态为审核中时,隐藏商家资质图片右上角的删除按钮 2025-11-13 14:58:54 +08:00
linxi 7075b1d125 Fix:4432 【旅居商城小程序】订单状态列表,待确认订单未显示确认倒计时 2025-11-13 14:09:17 +08:00
linxi f87cf3277b Fix:4575 【旅居商城小程序】入驻申请须知弹窗尺寸优化 2025-11-13 14:02:09 +08:00
linxi 6fb60ef712 Fix:4446 【旅居商城小程序】填写资料过程中返回上一级页面后重新进入,已填写的数据没有自动保存为草稿 2025-11-13 12:03:50 +08:00
linxi 6a526eb41c Fix:4221 【商城小程序】已被领取的礼包在24小时后查看领取页面会显示为礼包已失效(应该显示为已领取的效果) 2025-11-13 11:41:34 +08:00
linxi 7cc2be3536 Fix:4438 【旅居商城小程序】送礼—已领取礼包的用户查看礼包链接时应进入礼包已领取界面 2025-11-12 17:22:33 +08:00
linxi a2805ca58e Feat:管家分享 2025-11-12 17:08:18 +08:00
linxi 349b3a3baa Fix:4343 【旅居供应链小程序】进入资料填写页面,没有显示入驻申请须知弹窗 2025-11-12 16:27:35 +08:00
linxi 561b0968ab Fix:4423 【旅居商城小程序】送礼—礼品卡封面主图显示有误 2025-11-12 15:53:10 +08:00
linxi b8f9a250fb Fix:4452 【旅居商城小程序】订单状态列表,待使用订单增加入住时间显示,查看详情区域改为修改时间、立即退款、查看券码按钮 2025-11-12 15:33:49 +08:00
linxi 7bdca767d2 Fix:4453 【旅居商城小程序】商品的送礼封面选择弹窗需剔除【团购】类的礼品卡封面 2025-11-12 15:05:57 +08:00
linxi 68eab96693 Fix:4567 【旅居商城小程序】订单详情页-平台客服,点击立即拨打按钮无效,不会跳转到拨号页面 2025-11-12 14:43:59 +08:00
linxi e3e8c37551 Fix:4568 【旅居商城小程序】订单状态列表,点击联系商家按钮返回后,页面显示“拨打成功” 2025-11-12 14:40:43 +08:00
linxi ec042122c8 Fix:4526 【旅居商城小程序】旅居商家PC端上架团购未配置套餐详情时,前端应该隐藏套餐详情模块 2025-11-12 14:37:12 +08:00
linxi a050e39099 Fix:4441 【旅居商城小程序】待付款订单,增加取消订单的按钮 2025-11-12 14:16:57 +08:00
linxi 0c9674e08d Fix:4438 【旅居商城小程序】送礼—已领取礼包的用户查看礼包链接时应进入礼包已领取界面 2025-11-12 09:13:38 +08:00
linxi 8943a83b10 Fix:4438 【旅居商城小程序】送礼—已领取礼包的用户查看礼包链接时应进入礼包已领取界面 2025-11-11 18:02:34 +08:00
linxi 5f367bb408 Fix:4439 【旅居商城小程序】提交团购订单-使用微信支付结算时,未跳转微信支付接口 2025-11-11 17:56:00 +08:00
linxi fe9f229738 Fix:#4444 【旅居商城小程序】团购详情—可约日期右边增加导航标识 2025-11-11 17:47:36 +08:00
linxi bc10ad93c3 Fix:4445 【旅居商城小程序】订单详情页,拨打平台客服电话,拨号页面电话号码数据获取错误 2025-11-11 17:45:50 +08:00
linxi c7672655ea Fix:4447 【旅居商城小程序】送礼—收礼页面入住时间输入框增加日历图标显示 2025-11-11 17:40:50 +08:00
linxi 6ff20d9b2d Fix:4448 【旅居商城小程序】送礼—礼包已领取页面团购主图和名称显示问题 2025-11-11 17:24:16 +08:00
linxi f129d6cde4 Fix:4466 【旅居商城小程序】收到的礼品卡列表—旅居团购礼包状态调整 2025-11-11 17:18:22 +08:00
linxi abe9e4119e Fix:4434 【旅居商城小程序】团购详情—选择预约时间错误时提示词修改为“此套餐预约需连续选择X天” 2025-11-11 17:05:05 +08:00
linxi d169137ba2 Fix:4453 【旅居商城小程序】商品的送礼封面选择弹窗需剔除【团购】类的礼品卡封面 2025-11-11 16:55:10 +08:00
linxi d45b372aaa Fix:4449 【旅居商城小程序】待付款自动取消的订单,订单详情页-支付状态应改为“未支付” 2025-11-11 16:42:06 +08:00
linxi 66cc8bfcbd Fix:4451 【旅居商城小程序】送礼—送礼方礼包订单详情显示问题 2025-11-11 16:35:52 +08:00
linxi b68379eabd Fix:4453 【旅居商城小程序】商品的送礼封面选择弹窗需剔除【团购】类的礼品卡封面 2025-11-11 16:31:19 +08:00
linxi 2748950b81 Fix:4454 【旅居商城小程序】待确认/待入住订单,修改入住时间-选择天数中间有不可预约时间时,没有提示信息 2025-11-11 16:28:16 +08:00
linxi 9151f3326b Fix:4456 【旅居商城小程序】待确认/待入住订单详情页,修改入住时间增加原定入住时间显示 2025-11-11 16:16:26 +08:00
linxi bc53ad76fb Fix:4499 【旅居商城小程序】待确认、待入住退款失败的订单详情页,调整页面状态及退款原因显示 2025-11-11 15:27:46 +08:00
linxi b5b6bdd678 Fix:4505 【旅居商城小程序】旅居主页视频显示问题,未固定显示在第一页 2025-11-11 15:22:26 +08:00
linxi 512915ea2a Fix:4540 【旅居商城小程序】已完成的团购订单,更多按钮下去除申请退款的按钮 2025-11-11 14:43:15 +08:00
linxi c0baf3166d Fix:4505 【旅居商城小程序】旅居主页视频显示问题,未固定显示在第一页 2025-11-11 11:42:43 +08:00
linxi deaea528f3 Fix:4513 【旅居商城小程序】旅居商家PC端发布的视频资讯在前端未显示视频详情 2025-11-11 11:28:47 +08:00
linxi 5829cb53f1 Fix:4530 【旅居商城小程序】旅居主页—“文化”页面增加无数据提示 2025-11-11 11:03:45 +08:00
linxi c2dcc99f1c Fix:4530 【旅居商城小程序】旅居主页—“文化”页面增加无数据提示 2025-11-11 10:58:55 +08:00
linxi 79dacfe3f4 Fix:4526 【旅居商城小程序】旅居商家PC端上架团购未配置套餐详情时,前端应该隐藏套餐详情模块 2025-11-11 10:58:43 +08:00
linxi d4337d862a Fix:4526 【旅居商城小程序】旅居商家PC端上架团购未配置套餐详情时,前端应该隐藏套餐详情模块 2025-11-11 10:02:01 +08:00
linxi 41a4b3f293 Fix:4444 【旅居商城小程序】团购详情—可约日期右边增加导航标识 2025-11-10 18:06:51 +08:00
linxi abadbcee66 Fix:4457 【旅居商城小程序】收礼—【商品】类的填写地址弹窗需改回原来的样式 2025-11-07 15:59:28 +08:00
linxi c2ec209b2e Fix:4473 【旅居商城小程序】已入住的团购订单隐藏“去评价”按钮 2025-11-07 15:40:08 +08:00
linxi e22c96549e Fix:4468 【旅居商城小程序】退款驳回订单详情页,退款驳回原因没有获取商家填写的数据 2025-11-07 15:39:58 +08:00
linxi 08250f2cbc Fix:4459 【旅居商城小程序】送礼—将礼品封面“填写地址并收下”改为“填写信息并收下” 2025-11-07 11:47:41 +08:00
linxi 501f66aeb0 Fix:4462 【旅居商城小程序】订单-申请退款页面,退款原因修改为必填项 2025-11-07 11:46:55 +08:00
linxi ee6e39a4b9 Fix:4450 【旅居商城小程序】订单状态列表,联系商家按钮点击无效,应弹窗显示商家电话 2025-11-07 11:21:34 +08:00
linxi 9f06b47de3 订单倒计时 2025-11-06 18:10:29 +08:00
linxi a084e8f0e3 Fix::4346 【旅居商城小程序】管理后台配置的旅居店铺视频在前端小程序轮播区域未生效 2025-11-06 11:55:17 +08:00
linxi 403f5d8e76 Fix:4432 【旅居商城小程序】订单状态列表,待确认订单未显示确认 2025-11-06 11:18:33 +08:00
linxi 25187f7393 Fix:4431 【旅居商城小程序】订单详情页内,订单编号的复制按钮点击无效 2025-11-06 10:38:54 +08:00
linxi 47b62adb98 Fix:4384 【旅居商城小程序】管家主页—管家形象照颜色比原图暗一些,是否有一层半透明色块覆盖在上面 2025-11-06 10:07:05 +08:00
linxi 9d2f99a5dc Fix:4423 【旅居商城小程序】送礼—礼品卡封面主图显示有误 2025-11-06 10:06:48 +08:00
linxi e242a90f1a Fix:4369 【旅居商城小程序】旅居主页原型图漏了个标签设置,补一个接口,再指派相应人员 2025-11-05 18:02:14 +08:00
linxi 98ff194f6d Fix:4421 【旅居商城小程序】团购详情—查看可预约日期弹窗选择入住时间后点击【确定】和关闭按钮有很大概率无反应 2025-11-05 16:35:12 +08:00
linxi cbd8760e5c Fix:4385 【旅居商城小程序】管理后台配置资质图片在前端小程序未生效 2025-11-05 16:15:08 +08:00
linxi 1429b67c4c Fix:4410 【旅居商城小程序】旅居入驻简介的必填属性没有生效 2025-11-05 14:28:01 +08:00
linxi 92ab44bef6 Fix:4396 【旅居商城小程序】团购名称过长时,在团购列表中显示问题 2025-11-04 18:22:03 +08:00
linxi 0e32575490 Fix:4398 【旅居商城小程序】团购简介字符间距过宽,缩小一点 2025-11-04 18:12:24 +08:00
linxi c10b5ac1fb Fix:4402 【旅居商城小程序】旅居商家入驻资料填写页面缺少【简介】输入框 2025-11-04 18:11:28 +08:00
linxi 7dbb2f51a7 Fix:4315 【旅居商城小程序】商家电话操作按钮统一改为商家微信按钮样式 2025-11-04 17:55:05 +08:00
linxi bd2df37815 Fix:4314 【旅居商城小程序】入驻弹出营业时间后,点击空白处无法收起,会反复弹出来 2025-11-04 17:50:38 +08:00
linxi c975133fb8 Fix:4330 【旅居商城小程序】需隐藏“采购批发商”入驻入口 2025-11-04 17:27:05 +08:00
linxi 33543f99d7 Fix:4319 【商城小程序】首页搜索店铺-店铺商品展示区域:开启了价格面议属性的商品没有显示为“价格面议” 2025-11-04 17:20:22 +08:00
linxi 689d63e402 Fix:4296 【旅居商城小程序】修改入住时间后未生效 2025-11-04 17:19:44 +08:00
linxi 877b8d873c Fix:4317 【商城小程序】古镇—【活动】类目下方只显示了10条数据 2025-11-04 13:35:54 +08:00
linxi 913899ea8b Fix:4332 【旅居商城小程序】入驻资料填写页面无数据时会提示“undefined”(期望:无需显示任何提示信息) 2025-11-04 13:28:14 +08:00
linxi 6ba5f06d0b Fix:4334 【旅居商城小程序】入驻资料填写页面顶部标题改为“旅居商家入驻” 2025-11-04 13:17:48 +08:00
linxi 6ca3e477d6 Fix:4336 【旅居商城小程序】视频需改为非必填 2025-11-04 13:14:34 +08:00
linxi d24c176a89 Fix:4340 【旅居商城小程序】地理定位选择无效(选择后,会变为默认值0) 2025-11-04 13:09:36 +08:00
linxi ac6f854746 Fix:4338 【旅居商城小程序】轮播图、文化、资质需取消数量限制 2025-11-04 13:00:51 +08:00
linxi 9e3c52f9c9 Fix:4339 【旅居商城小程序】上传的视频没有显示封面,改为显示第一帧 2025-11-04 12:59:37 +08:00
linxi 514a648b30 Fix:4345 【旅居商城小程序】视频需限制最多只能上传1个 2025-11-04 12:24:44 +08:00
linxi 6e74542ac1 Fix:4364 【旅居商城小程序】省市区较长时,会超出页面显示(超出页面范围的文字需用...省略显示) 2025-11-04 12:10:23 +08:00
linxi caa5b49ca0 Fix:4352 【旅居商城小程序】“视频”增加【拍摄】上传方式 2025-11-04 12:10:09 +08:00
linxi ba78b712cb Fix:4374 【旅居商城小程序】未配置套餐详情内容时,前端应不显示套餐详情模块 2025-11-04 11:55:36 +08:00
linxi 145a5c0dd1 Fix:4357 【旅居商城小程序】商家电话需限制为11位数 2025-11-03 17:00:39 +08:00
LinCyJang 3dbacb26d1 Fix:4225 【商城小程序】抽中积分并领取成功后弹出的提示弹窗—点击【查看卡包】应该跳转至积分记录列表页面 2025-11-01 15:09:05 +08:00
LinCyJang e7d1eb4dd7 Fix:4226 【商城小程序】总的优惠券被领取完后,前端再次点击领取,会提示undefined,首页弹窗优惠券总数领取完后,提示信息修改为“优惠券已领完” 2025-11-01 14:51:24 +08:00
LinCyJang f7cf56216b Style:UI优化 2025-11-01 14:41:16 +08:00
LinCyJang 76d9d8ef33 Fix:4288 【旅居商城小程序】套餐详情页的图片,交互应按效果图改为显示一行,左右轮播 2025-11-01 13:36:05 +08:00
LinCyJang 869bf15f5c Fix:4289 【旅居商城小程序】商家微信,商家电话下面的复制,取消按钮,像其他按钮一样增加一个边框 2025-11-01 13:19:34 +08:00
LinCyJang f08818a7d7 Fix:4309 【旅居商城小程序】旅居收礼页面无数据 2025-11-01 13:02:38 +08:00
LinCyJang cebe0c75d4 Fxi:4311 【旅居商城小程序】订单列表页面未显示团购图片,价格等信息,此处UI跟商品订单一样就行 2025-11-01 12:38:50 +08:00
LinCyJang 4ec87ffdd5 Fix:4313 【旅居商城小程序】入驻输入框的触发范围延长至整个框,现在只有点文字才能触发,导致很多时候点击无响应,以为是bug 2025-11-01 12:28:42 +08:00
LinCyJang e1a524a499 Fix:4259 【旅居商城小程序】旅居入驻时,上传主图后,其他上传图片/视频的地方会自动变为空白数据 2025-11-01 12:12:59 +08:00
linxi e501bf7564 Fix:4290 【旅居商城小程序】申请退款的退款原因,参照效果图调整为两行显示 2025-10-31 16:52:15 +08:00
linxi eebe0f217b Style:图标更换 2025-10-31 16:44:24 +08:00
linxi 5c1561fe9e Fix:4312 【旅居商城小程序】旅居商家入驻的时候,点击上传视频,会弹出分享页面,没有进入手机视频相册,导致入驻流程无法继续 2025-10-31 16:43:58 +08:00
LinCyJang 9cab437532 Feat:收礼对接 2025-10-31 00:52:27 +08:00
LinCyJang 3f7a366a18 Feat:管家详情/缺管家设置 2025-10-31 00:50:02 +08:00
linxi 7a0073cd62 Feat: 管家详情 2025-10-30 18:40:13 +08:00
linxi ff1c4b3313 Fix:4291 【旅居商城小程序】订单详情团购价格往下移,参考效果图 2025-10-30 15:26:54 +08:00
linxi d142a9df7a Fix:4295 【旅居商城小程序】待使用状态订单进入订单详情还是待确认状态 2025-10-30 15:19:28 +08:00
linxi 9972a3873a Fix:4302 【旅居商城小程序】之前的订单信息有入住信息,现在消失了 2025-10-30 15:07:21 +08:00
linxi 3052c6b920 Fix:4297 【旅居商城小程序】旅居下单选择日期的交互调整 2025-10-30 14:55:17 +08:00
LinCyJang 4ff143ab29 Feat:旅居界面调整,逻辑补充 2025-10-28 00:23:42 +08:00
linxi 670c6385e5 Feat:订单流程调整 2025-10-24 18:03:07 +08:00
linxi db23fb32a4 Feat:旅居礼包收礼 2025-10-24 10:20:23 +08:00
linxi 0fc99561f4 REFATOR:重构时间选择器 2025-10-24 10:19:55 +08:00
linxi f73d7587f7 Feat::调整时间选择器 2025-10-24 10:19:16 +08:00
LinCyJang d1bbea6e9b REFACTOR:礼品功能重构 2025-10-24 01:31:50 +08:00
linxi 705d64d238 Feat:旅居礼包 2025-10-23 15:55:48 +08:00
LinCyJang 25ca6799d5 Feat:旅居礼品下单 2025-10-21 23:23:20 +08:00
linxi bf4861f088 Feat:订单退款,礼品下单 2025-10-21 17:51:40 +08:00
LinCyJang 64db7b8cd4 Feat:订单列表旅居订单处理 2025-10-21 02:04:46 +08:00
LinCyJang 46ef8c90cb Feat:修改入住时间 2025-10-21 01:49:30 +08:00
LinCyJang fe3c8fec6d Feat: 订单列表逻辑调整 2025-10-21 01:49:06 +08:00
LinCyJang e3c74241b4 Feat:订单预约日期 2025-10-21 01:24:12 +08:00
linxi ac3d11ff2a Feat: 旅居订单详情信息处理 2025-10-20 22:14:14 +08:00
LinCyJang 9f529e00b7 Feat: 订单流程 2025-10-19 23:15:06 +08:00
LinCyJang 6ffe3216d8 Fix:省市区无法选中 2025-10-17 00:44:04 +08:00
LinCyJang e559765260 Task:千县旅居入口 2025-10-17 00:43:43 +08:00
LinCyJang 1cffccebf3 Fix:4262 【旅居商城小程序】进入旅居入驻页面,不操作等两秒左右,自动录入上次填写的草稿,无法重新唤起键盘或相册,点击任何地方都没有反应\禁用调整 2025-10-16 00:49:28 +08:00
LinCyJang ad2327a57e Fix:4262 【旅居商城小程序】进入旅居入驻页面,不操作等两秒左右,自动录入上次填写的草稿,无法重新唤起键盘或相册,点击任何地方都没有反应 2025-10-16 00:05:35 +08:00
LinCyJang 15c459de7e Task:旅居主页调整 2025-10-13 23:39:09 +08:00
LinCyJang 77d522c87b Task:管家入驻流程完善 2025-10-13 23:03:23 +08:00
LinCyJang eddc57b835 Fix:管家申请交互优化 2025-10-12 21:58:19 +08:00
LinCyJang 6cfdd452ce Fix:旅居入驻无法获取验证码 2025-10-12 21:48:17 +08:00
LinCyJang 11f665aecc Fix:验证码发送按钮消失 2025-10-12 21:43:59 +08:00
LinCyJang 52adbc07d9 Feat:管家入驻海报读取 2025-10-11 00:14:08 +08:00
LinCyJang 9244c3215d Feat:管家入驻(小程序) 2025-10-10 23:42:37 +08:00
linxi 0dd7b2ed2d Fix:键盘问题 2025-09-18 11:12:46 +08:00
linxi c40ad9eebc Fix:4004 【商城小程序】在互动消息列表回复资讯评论,内容被顶至屏幕之外 2025-09-11 10:49:01 +08:00
linxi 51dea74ea9 Fix:4155 【商城小程序】产品列表页面—点击多规格商品入口的购物车图标,将商品添加至购物车后,此页面右上角购物车角标数量未改变 2025-09-11 10:46:20 +08:00
linxi ca7d004245 Fix:4177 【商城小程序】商品数量显示层覆盖在了结算按钮之上 2025-09-11 10:39:04 +08:00
linxi 5d9c2e9358 Fix:4182 【商城小程序】供应商入驻状态为审核中时,隐藏商家资质图片右上角的删除按钮 2025-09-11 10:37:03 +08:00
linxi 6defc580d1 Fix:4202 【商城小程序】资讯详情—用户回复其他评论时,应不显示“回复xxx” 2025-09-11 10:30:20 +08:00
linxi 02a6351f55 Fix:4203 【商城小程序】资讯详情—用户评论楼主时,应不显示@xxx 2025-09-11 10:28:58 +08:00
linxi 3a6fa798b8 Fix:4201 【商城小程序】我的礼品卡-送出的礼品列表和收到的礼品列表,商品规格较长,文字与规格框显示需适应 2025-09-11 10:10:18 +08:00
linxi eb51a4ad36 Fix:4206 【商城小程序】商品总价未满足优惠券满减条件,点击优惠券选项时页面出现透明灰色蒙版 2025-09-11 09:36:31 +08:00
lifizer 50cfce81db Style(订单):按钮样式调整 2025-08-22 15:56:01 +08:00
lifizer 44bb718431 Refactor(首页):分享使用通用设置 2025-08-21 15:48:09 +08:00
lifizer 810cf318aa Refactor(订单):订单列表和详情状态值判断重构 2025-08-21 15:47:33 +08:00
lifizer 881a1e1477 Refactor(分享):分享标题、页面路径、图片通过接口获取 2025-08-20 17:16:57 +08:00
lifizer 4d4760397d Refactor(专题页):目标跳转链接为空则不进入对应模块 2025-08-20 15:43:10 +08:00
lifizer 3eaa632c71 Fix(订单):预计送达时间不显示问题 2025-08-20 15:33:20 +08:00
lifizer a250630512 Fix(专题页):尖货专题页分享跳转链接参数不对,导致进入之后报错 2025-08-12 16:50:17 +08:00
lifizer 5a5e83564b Conf(拦截器):增加白名单,针对后端返回401不跳转到登录界面 2025-08-12 11:16:24 +08:00
linxi 59489829c6 Feat:新增盲盒分享封面设置 2025-08-12 10:25:26 +08:00
linxi 6b0011d73d Style: 礼品卡定位样式调优 2025-08-11 15:41:08 +08:00
linxi 562739eabd Feat:新增盲盒分享封面设置 2025-08-11 15:40:36 +08:00
linxi c69af507ec Fix:礼物封面商品图调整 2025-08-11 11:34:27 +08:00
LinCyJang 6e49848f9a Fix:礼物封面商品图调整 2025-08-09 17:19:16 +08:00
linxi ca8c08d4fd Fix:4143 【商城小程序】资讯已删除的情况下,点击互动消息中对应资讯的评论和回复内容,无需跳转进入资讯详情页,并增加弹窗提示“该内容已删除” 2025-08-08 11:51:26 +08:00
linxi 6c286900e3 Fix:4140 【商城小程序】礼品卡封面图自适应范围需要调整,包含上一版本封面图上面的商品图高度 2025-08-08 11:45:55 +08:00
linxi 2dd9430c8d Fix:4140 【商城小程序】礼品卡封面图自适应范围需要调整,包含上一版本封面图上面的商品图高度 2025-08-08 11:41:21 +08:00
linxi 5586afec97 Fix:4142 【商城小程序】体验版微信聊天记录长按分享的首页、店铺等非礼包链接,没有转发功能。(只需关闭礼包链接的转发功能,其他页面链接无需关闭) 2025-08-08 10:47:45 +08:00
linxi 55a5adf1d1 Fix:4137 【商城小程序】尖货推荐规则改为推荐可送礼的商品,需要增加角标,增加在左边 2025-08-08 10:37:30 +08:00
linxi 7eb7312ab0 Fix:4134 【商城小程序】待发货页面发货时效时间段显示调整优化 2025-08-08 10:18:12 +08:00
linxi 77264b667e Fix:4068 【商城小程序】自定义输入送礼寄语,从我的订单处点击【送给好友】,所分享礼品卡链接的送礼寄语未同步,显示为默认送礼寄语 2025-08-08 10:11:21 +08:00
linxi cd592355bf Fix:4139 【商城小程序】礼包领取页面转发功能关闭 2025-08-08 10:01:52 +08:00
lifizer f32c04dc01 Fix(云灵AI):[#4135]无数据图片替换 2025-08-07 15:33:26 +08:00
linxi b94068317a Fix:4131 【商城小程序】打开盲盒的礼包,领取礼包页面封面显示的不是盲盒封面 2025-08-06 14:15:03 +08:00
linxi a9a7a46807 Fix:4127 【商城小程序】订单结算页面的优惠详情弹窗—勾选其他优惠券后点击【×】关闭按钮,再次打开弹窗会显示勾选新选择的优惠券 2025-08-05 16:47:21 +08:00
linxi a2250ccc9a Fix:4101 【商城小程序】入驻申请已提交,进入特色店铺入驻页面,没有显示为审核状态,没有撤销申请按钮 2025-08-05 09:57:03 +08:00
linxi c63fdf0f37 Fix:4116 【商城小程序】待收货的盲盒礼包订单详情页【更多】、【确认收货】按钮改为靠右显示 2025-08-05 09:47:45 +08:00
linxi 863b5776b6 Fix:优惠价格计算隐藏先 2025-08-05 09:46:08 +08:00
linxi 33265e771e Fix:4113 【商城小程序】礼包结算选择优惠券弹窗—当前售价、券后价显示不正确(当前售价应该为折扣后的价格,券后价=当前售价-满减) 2025-08-05 09:39:06 +08:00
linxi 51c735f88e Fix:4097 【商城小程序】待收货、待评价、已完成订单详情页报错 2025-08-05 09:18:31 +08:00
linxi 227e5297e2 Fix:4112 【商城小程序】礼包结算页面点击【默认封面】无反应(点击后应该弹出选择封面弹窗) 2025-08-04 11:52:42 +08:00
linxi d7cf3b5a07 Fix:3863 【商城小程序】购物车里的商品数量增加数字键盘输入功能 2025-08-01 11:58:45 +08:00
linxi 15e52bfefe Fix:3880 【商城小程序】搜索界面-增加商品到购物车,红点提示未及时同步更新数量 2025-08-01 11:53:00 +08:00
linxi ea9e442e9b Fix:4073 【商城小程序】商品详情页需要显示管理后台,供应链PC端,供应链小程序设置的发货时效时间段(24小时、7天、15天) 2025-07-31 17:46:07 +08:00
linxi e206235988 Feat:送礼页优惠券优化 2025-07-31 17:36:32 +08:00
linxi 6273dd48a2 Fix:4066 【商城小程序】提交订单付款后,页面内容短暂闪现显示“undefined”文字 2025-07-31 14:27:32 +08:00
linxi e63e6b60c1 Fix:3765 【商城小程序】新增收货地址采用文本识别,从中间长按删除,会跳到文本最后面进行删除。 2025-07-31 12:00:12 +08:00
linxi 17f6867202 Fix:3787 【商城小程序】首页尖货推荐—开启了价格面议的商品价格区域没有显示“价格面议” 2025-07-31 11:47:40 +08:00
linxi 42a5ef715a Fix:3863 【商城小程序】购物车里的商品数量增加数字键盘输入功能 2025-07-31 11:29:48 +08:00
linxi 0186ac0d0e Fix:4107 【商城小程序】盲盒礼包订单详情页,隐藏盲盒礼物下方的商品折扣信息 2025-07-31 11:01:34 +08:00
linxi 914f39c2cf Fix:4105 【商城小程序】领取盲盒礼包后,优化“您已领取礼包,去查看>>”的按钮显示,不需要换行,参考图2 2025-07-31 11:00:29 +08:00
linxi 2882b54e16 Fix:4109 【商城小程序】删除送礼寄语的功能:(送礼包页面、礼包领取页面、我的礼品卡页面) 2025-07-31 11:00:13 +08:00
linxi 2a20c45a4e Fix:4110 【商城小程序】盲盒礼包订单的待发货与待收货列表以及订单详情页,需隐藏【联系商家】按钮 2025-07-31 10:05:04 +08:00
linxi 9870970d28 Fix:3880 【商城小程序】搜索界面-增加商品到购物车,红点提示未及时同步更新数量 2025-07-30 14:54:47 +08:00
linxi f8c348279f Fix:3894 【商城小程序】推荐商品列表开启价格面议的商品价格区域未显示为“价格面议” 2025-07-30 14:47:11 +08:00
linxi 6390bec944 Fix:3862 【商城小程序】已下架的商品增加勾选限制 2025-07-30 14:36:17 +08:00
linxi 615f7d9064 Fix:3982 【商城小程序】送礼页面优惠券改为选填模式 2025-07-30 14:06:25 +08:00
lifizer 6df8cda11c Fix(专题页):[#4096]专题页领取优惠券后,再次点击专题页图片提示“undefine” 2025-07-30 10:03:10 +08:00
LinCyJang 9ddb0b53f3 Fix:4032 【商城小程序】查看资讯图片会刷新返回到店铺顶部 2025-07-29 23:38:37 +08:00
LinCyJang 26d8fb88c5 Fix:4066 【商城小程序】提交订单付款后,页面内容短暂闪现显示“undefined”文字 2025-07-29 22:52:25 +08:00
LinCyJang a99c9afaf1 Fix:4070 【商城小程序】模版显示效果与预览图效果存在位置偏差 2025-07-29 22:42:12 +08:00
LinCyJang db9dba4df8 Fix:4076 【商城小程序】领取盲盒礼品后,隐藏盲盒礼物下方商品总价与实付款信息 2025-07-29 21:49:52 +08:00
LinCyJang 251da432c9 Fix:4077 【商城小程序】选择封面-去除商品图片边缘框 2025-07-29 21:46:45 +08:00
LinCyJang 177f61a969 Fix:4079 【商城小程序】礼包领取后,点击“我也要送礼”跳转到送礼搜索结果页面后会继续跳转到礼包订单详情页 2025-07-29 21:43:58 +08:00
LinCyJang 5a0fbb8e71 Fix:4082 【商城小程序】盲盒礼包订单-隐藏盲盒礼物下方商品总价 2025-07-29 21:42:34 +08:00
LinCyJang 7804f09fea Fix:4085 【商城小程序】礼包领取后,“您已领取礼包”和“查看详情按钮合并”,点击之后跳转订单详情页 2025-07-29 21:37:20 +08:00
LinCyJang 0ef2d7d2ac Fix:4088 【商城小程序】领取盲盒礼包后,礼包订单申请退款页面,应隐藏退款金额信息 2025-07-29 21:33:28 +08:00
LinCyJang 4560b76f75 Fix:4098 【商城小程序】盲盒礼包订单详情页-点击盲盒信息会跳转商品详情页 2025-07-29 21:24:38 +08:00
LinCyJang 53b7bbd9bc Fix:4099 【商城小程序】进入入驻页面时弹出入驻须知,提交申请后先弹窗“入驻申请已提交”又会再次弹窗入驻须知 2025-07-29 21:21:58 +08:00
lifizer 4d95ba8b7b Fix(消息):点击进入AI首页使用首页定位的城市获取天气数据 2025-07-28 17:35:42 +08:00
lifizer d7b8445c27 Feat(专题页):增加县城和古镇跳转对接 2025-07-28 11:49:19 +08:00
LinCyJang 29799b8737 Feat:催发货 2025-07-28 02:08:03 +08:00
LinCyJang 07618635f1 Feat:盲盒模式的收礼界面-我也要送礼 2025-07-28 02:02:50 +08:00
LinCyJang 2640b049b5 Feat:预计到达时间 2025-07-28 01:48:44 +08:00
LinCyJang 7f9af40ab0 Feat:多品送礼,建立送礼清单(盲盒)/ 盲盒订单商品信息隐藏 2025-07-28 01:17:47 +08:00
lifizer 199912c4da Feat(专题页):1、第一张图片点击领取优惠券;2、增加转发标题和图片 2025-07-24 11:22:57 +08:00
LinCyJang 3b3d39f460 Feat:礼品卡图片商品位置自定义/ 封面选择 2025-07-23 23:39:39 +08:00
lifizer 5ea0b9f994 Feat(AI):用户提问增加复制按钮 2025-07-23 16:13:20 +08:00
LinCyJang 7984854b2b Feat:多品送礼,建立送礼清单(盲盒)/ 创建盲盒订单 2025-07-22 00:47:01 +08:00
lifizer 81d3bd0481 Feat(消息):增加AI入口 2025-07-21 14:13:58 +08:00
lifizer 035f7fd668 Feat(趣游):增加AI入口 2025-07-21 14:13:37 +08:00
lifizer a98d504ee9 Fix(评论):[#4018]错误提示未显示 2025-07-17 17:18:41 +08:00
lifizer 443b929d84 Fix(订单):分享使用礼品卡图片,不用封面 2025-07-17 16:35:51 +08:00
lifizer 85c5269ad9 Fix(订单):[#3256]商详页直接进入提交订单页面,默认地址显示为上一次购买商品时选择的地址信息 2025-07-17 11:37:37 +08:00
lifizer 57c62416c5 Fix(订单):[#3256]商详页直接进入提交订单页面,默认地址显示为上一次购买商品时选择的地址信息 2025-07-17 09:52:58 +08:00
lifizer 588fd5e687 Fix(消息):店铺评论不显示@店主名称 2025-07-16 11:04:27 +08:00
lifizer 498e62b289 Fix(评论):苹果13 15 16,点击回复的时候光标是停留在@符号后面,要再点击一次光标才会停留到整个昵称后面 2025-07-16 10:20:27 +08:00
lifizer 4aeed9292b Fix(评论):苹果13 15 16,点击回复的时候光标是停留在@符号后面,要再点击一次光标才会停留到整个昵称后面 2025-07-16 10:07:39 +08:00
lifizer 8bd83e7c7f Fix(订单):[#4056]多份礼品,24h内领取部分,24h后未领取部分自动退回,送礼订单提示修改 2025-07-16 09:54:51 +08:00
lifizer f33507f902 Fix(评论):[#4048]应该显示被回复at的用户昵称 2025-07-16 09:42:55 +08:00
lifizer e96339e7e2 Refactor(评论):1、回复内容应去掉@xxx;2、增加逻辑判断 2025-07-15 17:17:03 +08:00
lifizer 2629e5d849 Fix(商品列表):[#3880]增加商品到购物车,红点提示未及时同步更新数量 2025-07-15 15:14:11 +08:00
lifizer d774abbbc2 Fix(订单详情):[#4026]订单详情显示具体领取记录情况 2025-07-15 15:03:31 +08:00
lifizer 8d393f0ee8 Fix(AI):[#4049]语音播放AI回复内容,点击进入下方推荐商品/店铺/县城/抽奖详情页,语音仍在播放 2025-07-15 14:29:19 +08:00
lifizer a5b7959b13 Fix(AI):[#4049]语音播放AI回复内容,点击进入下方推荐商品/店铺/县城/抽奖详情页,语音仍在播放 2025-07-15 14:28:49 +08:00
lifizer dfc45aceb4 Fix(会话):[#3914]消息内容显示错乱 2025-07-15 14:10:39 +08:00
lifizer 7c87232ce1 Fix(订单):[#4029]商品待发货,申请退款页面,将顶部标题”商品退货“改为”申请退款“ 2025-07-15 13:58:34 +08:00
lifizer 8fa2c055a4 Fix(商品详情):[#4046]补贴和限时两个字调换,正确应该是“平台限时补贴85折” 2025-07-15 09:43:50 +08:00
lifizer 6712cc844d Fix(AI):[#4049]语音播放AI回复内容,点击[重新生成],语音仍在播放 2025-07-15 09:37:01 +08:00
linxi 1ec7c50240 Fix:3987 【商城小程序】礼包订单分享链接错误 2025-07-11 16:45:28 +08:00
linxi 02d4e45c8b Fix:4016 【商城小程序】用户修改昵称,评论列表和互动消息处@XXX(用户昵称),未同步变动 2025-07-11 16:37:50 +08:00
linxi adaaf8e8e3 Fix:4003 【商城小程序】互动消息列表,点击时未跳转至资讯详情/ 增加触发范围 2025-07-11 15:50:24 +08:00
linxi 3d914c8103 Fix:4003 【商城小程序】互动消息列表,点击时未跳转至资讯详情 2025-07-11 15:49:24 +08:00
linxi 4dde92dd2e Fix:4003 【商城小程序】互动消息列表,点击时未跳转至资讯详情 2025-07-11 15:48:46 +08:00
linxi 3208d4b076 Fix:4003 【商城小程序】互动消息列表,点击时未跳转至资讯详情 2025-07-11 15:47:58 +08:00
linxi 0b4337e393 Fix:4003 【商城小程序】互动消息列表,点击时未跳转至资讯详情 2025-07-11 13:52:44 +08:00
linxi c4317ed20b Merge branch 'dev' of https://gitee.com/fengyuze178/xdd-uniapp-new into dev 2025-07-11 10:01:28 +08:00
linxi efb7db993b Fix:4023 【商城小程序】送礼方在我的订单-查看送礼礼包订单详情,具体领取情况未显示(显示效果见图二) 2025-07-11 10:01:22 +08:00
lifizer 693da0f77c Fix(分享):微信小程序分享所用到的图片,服务器上的已替换,但是文件名未变更,小程序分享的时候获取到的图片还是旧的 2025-07-11 09:57:29 +08:00
linxi ae1dc31408 Fix:4019 【商城小程序】资讯详情—资讯图片和视频显示异常 2025-07-11 09:46:21 +08:00
linxi fce887794c Fix:3986 【商城小程序】礼包订单提示调整 2025-07-09 16:57:14 +08:00
linxi c25b73d845 Fix:4007 【商城小程序】评论较长,无法上下滑动查看(超过40字后可以滑动查看) 2025-07-09 16:49:40 +08:00
linxi 5c14eb6e1e Fix:4005 【商城小程序】楼主通过互动消息列表回复评论时,未显示在该评论下方 2025-07-09 16:48:58 +08:00
linxi 5a883ff1dc Fix:3989 【商城小程序】互动消息回复内容为空时将提示消息“reply评论内容不能为空”改为“请输入回复内容” 2025-07-09 16:31:37 +08:00
LinCyJang 933bd0d64e Fix:订单列表分享错误 2025-07-09 01:00:50 +08:00
LinCyJang 8ded90e60d Fix:3952 【商城小程序】评论输入框跟随文本长度自适应变化 2025-07-09 00:37:14 +08:00
LinCyJang 2d2f60a29e Fix:3986 【商城小程序】礼包订单提示调整 2025-07-09 00:30:55 +08:00
LinCyJang e1a9600565 Fix:3985 【商城小程序】待支付礼包订单提示修改 2025-07-09 00:26:57 +08:00
LinCyJang 180bc0c0ad Fix:3987 【商城小程序】礼包订单分享链接错误 2025-07-09 00:07:41 +08:00
LinCyJang 21410c5f06 Fix:3988 【商城小程序】礼包订单详情类型修改 2025-07-08 23:54:41 +08:00
LinCyJang 3f59801805 Fix:3989 【商城小程序】互动消息回复内容为空时将提示消息“reply评论内容不能为空”改为“请输入回复内容” 2025-07-08 23:51:49 +08:00
LinCyJang 69132a4e48 Fix:3992 【商城小程序】我的礼品卡—收礼方订单未显示订单状态 2025-07-08 23:49:25 +08:00
LinCyJang 6de73ad9ea Fix:3995 【商城小程序】“一句话介绍”文本内容超过一行时换行显示。商户入驻及店铺入驻都需要调整 2025-07-08 23:43:50 +08:00
LinCyJang ed354dd14b Fix:3994 【商城小程序】多规格商品入口图添加购物车,多数量长按删除,商品数量显示为空 2025-07-08 23:39:11 +08:00
LinCyJang f7816f0d0c Fix:3993 【商城小程序】在多规格商品入口图点击购物车按钮,会保留上一个商品的选择数量 2025-07-08 23:26:16 +08:00
LinCyJang 4ebce61781 Fix:4001 【商城小程序】店铺资讯列表,跳转资讯详情点击区域扩大 2025-07-08 23:09:07 +08:00
LinCyJang 4f0d7fb9cc Fix:4000 【商城小程序】礼品折扣文字再加大一个字号 2025-07-08 23:06:54 +08:00
LinCyJang 0a667d2b49 Fix:4002 【商城小程序】资讯—评论的@符号颜色改为灰色(如图) 2025-07-08 23:04:38 +08:00
LinCyJang 431b363b3a Fix:3999 【商城小程序】平台折扣把%改成折 2025-07-08 22:54:06 +08:00
lifizer fef3b7d446 Style(AI):输入内容设置最多支持500字和最大高度,超过则出现Y轴滚动条 2025-07-07 17:37:41 +08:00
LinCyJang b767f4193f Fix:Bug
用例
测试单
报告
套件
3984 【商城小程序】未支付1h后自动取消的订单,将礼包订单提示“undefined”改为“订单已取消”,删除【送给朋友】按钮
2025-07-06 22:36:20 +08:00
LinCyJang 26cf65ee50 Fix:3952 【商城小程序】评论输入框跟随文本长度自适应变化 2025-07-06 22:35:50 +08:00
LinCyJang e8886a6050 Fix:3925 【商城小程序】楼主进入互动消息页面回复时,输入框默认显示单引号 2025-07-06 20:40:36 +08:00
linxi b129ed9641 Style:评论样式微调 2025-07-04 09:27:25 +08:00
LinCyJang 34734e0b72 Fix:3862 【商城小程序】已下架的商品增加勾选限制 2025-07-04 00:17:01 +08:00
LinCyJang 6028f3e02a Fix:3878 【商城小程序】隐藏入驻申请须知关闭按钮 2025-07-04 00:11:25 +08:00
LinCyJang 632a17da6f Fix:3951 【商城小程序】收礼方订单详情增加折扣金额项 2025-07-04 00:09:36 +08:00
LinCyJang 383318eb6b Feat:资讯显示最新两条数据 2025-07-04 00:02:41 +08:00
LinCyJang a3fb2e5bcf Fix:3959 【商城小程序】资讯详情页点赞或者取消点赞,评论数刷新为0 2025-07-03 23:42:10 +08:00
LinCyJang 9e7835ce0b Fix:3961 【商城小程序】这部分功能未出接口,出接口后指派给前端 2025-07-03 23:34:08 +08:00
LinCyJang 9d345d525e Fix:3962 【商城小程序】资讯详情—评论时间超过7天显示日期 2025-07-03 22:12:31 +08:00
LinCyJang aff7cfccc0 Fix:3921 【商城小程序】店主资讯被评论时未收到互动消息通知 2025-07-03 22:02:32 +08:00
LinCyJang 8f2d3b6aeb Fix:3980 【商城小程序】互动消息列表只显示最新的十条记录,未全部显示 2025-07-03 21:53:52 +08:00
lifizer 5cf21bf46c Fix(AI):[#3949]语音播放AI回复内容,返回AI主页、小程序首页,语音仍在播放 2025-07-01 15:56:09 +08:00
linxi 8b21a232fe Fix:3919 【商城小程序】资讯—楼主回复未显示在该评论下方 2025-06-30 16:29:41 +08:00
linxi 3a0809dd22 Fix:3946 【商城小程序】评论回复楼主时,应不显示回复楼主 2025-06-30 16:22:22 +08:00
linxi dc5a7deefb Fix:3947 【商城小程序】店铺资讯详情页浏览数、点赞数和评论数变动,在资讯列表未及时同步刷新 2025-06-30 16:13:23 +08:00
linxi b34906964e Merge branch 'test' into dev 2025-06-30 15:42:21 +08:00
linxi e20a5efc17 Fix:3950 【商城小程序】资讯—评论条数较少或无评论时,页面底色显示不一致 2025-06-30 15:40:33 +08:00
linxi a7c320d406 Fix:收礼列表不跳转详情 2025-06-30 15:40:24 +08:00
linxi 19d247c41c Fix:3928 【商城小程序】资讯—用户@楼主评论完成后输入框仍默认显示@楼主功能 2025-06-30 15:35:50 +08:00
lifizer 195cd9405c Style(AI):输入框改为多行文本 2025-06-27 14:48:07 +08:00
linxi 2c19636b81 Merge branch 'dev' into test 2025-06-26 09:33:44 +08:00
linxi a3bf9df15d Style:互动消息图标调整 2025-06-26 09:33:32 +08:00
linxi 262b8c365f Fix: 点击评论图标也可进入资讯详情 2025-06-25 18:01:59 +08:00
linxi cc1b6fcc3f Fix:3918 【商城小程序】资讯—店主评论未显示“店主”标签 2025-06-25 17:42:19 +08:00
linxi 5c2e950bc4 Fix:3917 【商城小程序】资讯评论页面需增加“资讯评论”作为分隔 2025-06-25 17:34:11 +08:00
linxi c6fc97dacc Fix(互动消息):[#3925]楼主进入互动消息页面回复时,输入框默认显示单引号 2025-06-25 17:30:16 +08:00
linxi 03eb6ef677 Feat:礼品折扣计算 2025-06-25 16:37:49 +08:00
linxi 16b8ad101e Feat: 折扣对接 2025-06-24 18:08:06 +08:00
lifizer 88c49d2add Feat(AI):TTS功能实现 2025-06-24 15:56:29 +08:00
LinCyJang b34f481a2f Fix:小程序打开图片加载不出来问题
(cherry picked from commit d1cd9900d3)
2025-06-23 15:13:06 +08:00
LinCyJang d1cd9900d3 Fix:小程序打开图片加载不出来问题 2025-06-23 01:08:15 +08:00
LinCyJang 2e6b2d6c62 Feat:店铺资讯评论 2025-06-23 00:03:04 +08:00
LinCyJang fd7793b3fb Feat:店铺资讯评论 2025-06-22 18:51:27 +08:00
lifizer 491aa8d0a3 Feat(AI):TTS功能实现 2025-06-11 18:00:02 +08:00
linxi db554f0dbc Style:'寻趣味"改’吃喝玩乐‘ 2025-06-05 18:09:07 +08:00
linxi 55ecf029a0 Feat:礼品卡消息不允许转发 2025-06-05 15:52:38 +08:00
linxi fbd33b4276 Style:单词换行样式设置
(cherry picked from commit 1dfabc1697)
2025-06-05 10:54:05 +08:00
linxi 1dfabc1697 Style:单词换行样式设置 2025-06-05 10:53:08 +08:00
linxi df500286e2 Fix:3569 【商城小程序】订单详情、订单列表、礼品卡列表下方出现遮挡,手机型号:红米K40
(cherry picked from commit 29a6be7e38)
2025-06-04 10:09:14 +08:00
linxi 2d2b57d3d0 Fix:订单详情切换地址不需要提示
(cherry picked from commit d28a2ebd20)
2025-06-04 10:09:06 +08:00
linxi d0841f19fc Fix:3854 【商城小程序】顶部分类“养生圣地”改为“养生胜地”
(cherry picked from commit e89b80dee8)
2025-06-04 10:08:59 +08:00
linxi b7cdcb10d3 Feat:3685 【商城小程序】一个小需求,非遗详情增加文本内容
(cherry picked from commit 7be4c52fb0)
2025-06-04 10:08:55 +08:00
linxi 46c91ef920 Fix:3845 【商城小程序】商详页点击【送给朋友】区域没有弹出选择规格弹窗
(cherry picked from commit a1b0dbe456)
2025-06-04 10:01:40 +08:00
linxi a1b0dbe456 Fix:3845 【商城小程序】商详页点击【送给朋友】区域没有弹出选择规格弹窗 2025-06-04 10:01:28 +08:00
linxi 7be4c52fb0 Feat:3685 【商城小程序】一个小需求,非遗详情增加文本内容 2025-06-04 09:42:58 +08:00
linxi e89b80dee8 Fix:3854 【商城小程序】顶部分类“养生圣地”改为“养生胜地” 2025-06-04 09:29:56 +08:00
linxi d28a2ebd20 Fix:订单详情切换地址不需要提示 2025-06-04 09:16:49 +08:00
linxi 29a6be7e38 Fix:3569 【商城小程序】订单详情、订单列表、礼品卡列表下方出现遮挡,手机型号:红米K40 2025-06-04 09:16:18 +08:00
LinCyJang 7fdaeb1c5b Merge branch 'dev' into test 2025-06-02 13:07:03 +08:00
LinCyJang 8f28c33ba2 Fix:3829 【商城小程序】首次进入供应商入驻页面省市区选项显示“null” 2025-06-02 12:51:25 +08:00
LinCyJang f801863241 Fix:3824 【商城小程序】千县旅居—店铺分享保存按钮点击无反应(体验版) 2025-06-02 12:34:53 +08:00
LinCyJang 67ce7f0543 Fix:3854 【商城小程序】顶部分类“养生圣地”改为“养生胜地” 2025-06-02 12:30:00 +08:00
LinCyJang 375b5b1978 Fix:3815 【商城小程序】购买流程出现异常页面 2025-06-02 12:21:23 +08:00
LinCyJang 00ba2a3239 Fix:3828 【商城小程序】店铺主页-切换店铺后,分享店铺,生成的图片为切换前的店铺信息 2025-06-02 11:26:19 +08:00
LinCyJang 0ddb5451e5 Fix:3853 【商城小程序】聊天窗口可输入字数太少 修改为500字 2025-06-02 11:19:20 +08:00
LinCyJang ea5efb50c4 Fix:3762 【商城小程序】商品评价里面的图片压缩,改为正方形矩形 2025-06-02 11:13:35 +08:00
LinCyJang 2ebd257a49 Fix:3569 【商城小程序】订单详情、订单列表、礼品卡列表下方出现遮挡,手机型号:红米K40 2025-06-02 10:08:27 +08:00
LinCyJang 53457ba83c Fix:3615 【商城小程序】历史搜索词条搜索后未显示在最前方 2025-06-02 10:02:45 +08:00
LinCyJang 6e4a217873 READMIE 2025-05-25 20:03:02 +08:00
lifizer d86a2272de Fix(AI):[#3812]查看历史对话详情页无数据 2025-05-22 16:21:17 +08:00
lifizer f0a187f2b1 Feat(AI):天气展示数据调整 2025-05-22 11:06:45 +08:00
lifizer 982fe87cc1 Feat(AI):天气和日历对接 2025-05-22 10:23:49 +08:00
lifizer 243460eb39 Feat(AI):天气和日历对接 2025-05-21 15:39:23 +08:00
lifizer 20fc34673d Style(AI):推荐列表展示交互调整 2025-05-16 14:23:42 +08:00
lifizer b13dd1d368 Feat(AI):店铺搜索显示店铺类型 2025-05-15 17:12:23 +08:00
lifizer e9aae5e8ae Feat(AI):关键词搜索匹配抽奖模块 2025-05-15 16:29:27 +08:00
modendeveloper f4b04d8a0e Fix:旅居分享图片不显示问题
(cherry picked from commit ec794b4d79)
2025-05-15 08:58:37 +08:00
modendeveloper ec794b4d79 Fix:旅居分享图片不显示问题 2025-05-14 17:58:04 +08:00
lifizer f25483bbb7 Feat(AI):关键词搜索匹配相关模块列表
(cherry picked from commit 5f835afd8b)
2025-05-14 12:13:55 +08:00
137 changed files with 25015 additions and 4364 deletions
+3
View File
@@ -9,6 +9,9 @@
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
+4 -2
View File
@@ -23,12 +23,13 @@
</view>
<!-- 输入框 -->
<view class="input-wrap flex-center-between">
<input
<textarea
v-model.trim="prompt"
:show-confirm-bar="false"
:adjust-position="true"
:auto-height="true"
:disabled="loading"
:maxlength="-1"
:maxlength="500"
class="inp"
@confirm="sendHandle"
@focus="promptInputFocusHandle"
@@ -127,6 +128,7 @@ export default {
* 怎么申请地理标志保护产品?
* 想找个像《去有风的地方》那样的治愈县城
* 云南有哪些小众县城适合旅居?
* 查询最近一个月的订单
*/
prompt: '',
recordManager: null,
+4 -3
View File
@@ -37,7 +37,7 @@
</view>
<view v-else class="no-data">
<image
:src="webUrl + '/orderIcon/nodata1.png'"
:src="webUrl + '/aiChat/ai-chat-no-data.png'"
class="img"
mode="widthFix"
/>
@@ -68,7 +68,7 @@
</view>
<view v-else class="no-data">
<image
:src="webUrl + '/orderIcon/nodata1.png'"
:src="webUrl + '/aiChat/ai-chat-no-data.png'"
class="img"
mode="widthFix"
/>
@@ -99,7 +99,7 @@
</view>
<view v-else class="no-data">
<image
:src="webUrl + '/orderIcon/nodata1.png'"
:src="webUrl + '/aiChat/ai-chat-no-data.png'"
class="img"
mode="widthFix"
/>
@@ -170,6 +170,7 @@ export default {
url: '/aiChat/views/history' + paramsStr
})
}
this.$emit('enterHistory')
this.closeLayer()
},
closeLayer() {
+263
View File
@@ -0,0 +1,263 @@
<template>
<view
class="order-item"
>
<view class="order-header">
<view class="order-shop">
<view class="shop-name">
<image
:src="webUrl + '/aiChat/icon-57.png'"
class="icon"
mode="widthFix"
lazy-load
/>
{{ item.merName }}
</view>
<view class="order-id">
订单号{{ item.orderId }}
</view>
</view>
<view class="status">
{{ item.statusName }}
</view>
</view>
<view v-if="item.isGiftCardReceiveBlind === 0" class="product-info-wrap">
<view
v-for="cartItem in item.cartInfo"
:key="cartItem.productId"
class="product-info"
>
<view class="good-img">
<image
:src="cartItem.productInfo.image"
class="img"
mode="widthFix"
lazy-load
/>
</view>
<view class="name-wrap">
<view class="name one-t">
{{ cartItem.productInfo.storeName }}
</view>
<view class="attr">
规格{{ cartItem.productInfo.attrInfo.sku || '' }}
</view>
</view>
<view class="price-num">
<view>
{{ cartItem.truePrice }}
</view>
<view class="num">
x{{ cartItem.cartNum }}
</view>
</view>
</view>
</view>
<view v-else class="product-info-wrap">
<view
class="product-info product-info2"
>
<view class="good-img">
<image
:src="webUrl + '/orderIcon/盲盒礼包.png'"
class="img"
mode="widthFix"
lazy-load
/>
</view>
<view class="name-wrap">
<view class="name one-t">
盲盒礼物
</view>
<view class="attr">
待收货后展示商品信息
</view>
</view>
<view class="price-num">
<view style="color: #fff;">
0.00
</view>
<view class="num">
x1
</view>
</view>
</view>
</view>
<view class="order-bottom">
<view class="time-total">
<view class="time">
{{ formatDateTime(item.createTime) }}
</view>
<view class="total">
{{ item.totalNum }}件商品
<text v-if="item.isGiftCardReceiveBlind === 0" class="total-txt">
&nbsp;&nbsp;&nbsp;&nbsp;总金额<text class="color-red">{{ item.totalPrice }}</text>
</text>
</view>
</view>
<view class="btns">
<view class="btn" @click="viewOrderTrack">查看物流</view>
</view>
</view>
</view>
</template>
<script>
import { formatDateTime } from '@/utils/index'
export default {
name: 'AiHistoryList',
props: {
item: {
type: Object,
default: () => {}
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL
}
},
methods: {
formatDateTime,
viewOrderTrack() {
this.$emit('viewOrderTrack', this.item)
}
}
}
</script>
<style scoped lang="less">
.order-item {
padding: 20rpx;
border-radius: 20rpx;
background-color: #fff;
box-shadow: 0rpx 6rpx 20rpx rgba(101,101,101,0.16);
.order-header {
display: flex;
align-items: center;
justify-content: space-between;
.order-shop {
flex: 1;
width: calc(100% - 120rpx);
.shop-name {
display: flex;
align-items: center;
color: #333;
font-size: 28rpx;
line-height: 40rpx;
.icon {
display: block;
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
}
}
.order-id {
margin-top: 20rpx;
font-size: 24rpx;
color: #666;
}
.status {
width: 100rpx;
height: 40rpx;
border-radius: 10rpx;
color: #fff;
font-size: 24rpx;
line-height: 40rpx;
text-align: center;
background: rgba(197,39,51,1);
}
}
.product-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
font-size: 28rpx;
.good-img {
.img {
display: block;
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
}
}
.name-wrap {
height: 120rpx;
width: calc(100% - 260rpx);
.name {
line-height: 40rpx;
}
.attr {
display: inline-block;
height: 50rpx;
max-width: 100%;
padding: 0 25rpx;
margin: 20rpx 0 0 0;
border-radius: 25rpx;
line-height: 50rpx;
color: #252525;
background: rgba(239,239,239,0.39);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.price-num {
width: 100rpx;
text-align: right;
line-height: 40rpx;
.num {
font-size: 20rpx;
}
}
}
.product-info2 {
.name-wrap {
display: flex;
flex-direction: column;
justify-content: space-between;
.attr {
padding: 0;
font-size: 24rpx;
color: #999;
background-color: #fff;
}
}
}
.order-bottom {
.time-total {
display: flex;
align-items: center;
justify-content: space-between;
color: #999;
font-size: 22rpx;
.total-txt {
color: #333;
font-size: 28rpx;
font-weight: 600;
}
.color-red {
color: #C52733;
}
}
.btns {
display: flex;
align-items: center;
justify-content: flex-end;
margin: 24rpx 0 0 0;
.btn {
height: 44rpx;
padding: 0 24rpx;
border-radius: 22rpx;
border: 1rpx solid #C52733;
color: #C52733;
font-size: 28rpx;
line-height: 44rpx;
text-align: center;
}
}
}
}
</style>
+165 -4
View File
@@ -2,7 +2,7 @@ import {
getSseConfigV1,
getCompletionsV2,
deleteChatItemByConversationId,
getAnalyzeKeywordsV1,
// getAnalyzeKeywordsV1,
getAnalyzeKeywordsChangeV1
} from '@/api/chat/index'
import { VUE_APP_API_URL } from '@/config'
@@ -10,12 +10,37 @@ import settings from '@/config/baseSetting.js'
import { formatAiMsgContent } from '../utils/aiChat'
import cookie from '@/utils/store/cookie'
import { handleLoginFailure } from '@/utils'
const plugin = requirePlugin('WechatSI')
const removeMarkdown = require('remove-markdown')
export const chatMixins = {
data() {
return {
token: cookie.get('login_status'),
userInfo: cookie.get('userInfo'),
webUrl: this.$VUE_APP_RESOURCES_URL,
bottomTools: Object.freeze([
{
icon: 'icon-50',
text: '我要送礼',
type: 'prompt',
prompt: '我要送礼,请给我推荐一些平台的礼品',
url: ''
},
{
icon: 'icon-51',
text: '订单查询',
type: 'prompt',
prompt: '订单查询',
url: ''
},
{
icon: 'icon-52',
text: '拍照识图',
type: 'link',
prompt: '',
url: '/aiChat/views/imageRecognition'
}
]),
scrollData: {},
scrollTop: 0,
isScrollToBottom: false,
@@ -46,7 +71,11 @@ export const chatMixins = {
}
*/
],
errorMsg: Object.freeze('您提的问题未能理解,云灵思想跑偏啦,重新整理一下问题或者重新提问,让云灵再试试!')
errorMsg: Object.freeze('您提的问题未能理解,云灵思想跑偏啦,重新整理一下问题或者重新提问,让云灵再试试!'),
audioAyy: [],
isPlayAudio: false,
audioContext: null,
audioPlayId: ''
}
},
computed: {
@@ -71,9 +100,11 @@ export const chatMixins = {
}.bind(this)).exec()
},
onUnload() {
if (this.audioContext) {
this.audioContext.stop()
}
wx.getBackgroundAudioManager().stop()
this.closeWsFn()
// console.log('在页面卸载时取消监听')
// wx.offKeyboardHeightChange()
},
created() {
const _this = this
@@ -144,6 +175,7 @@ export const chatMixins = {
},
// 重新生成
regenerateHandle(item) {
this.audioStopHandle()
deleteChatItemByConversationId(item.conversationId).then(res => {
const { success } = res
if (success) {
@@ -221,6 +253,7 @@ export const chatMixins = {
_this.chatErrorSetContent()
})
})
/*
getAnalyzeKeywordsV1({
'chatNumber': _this.chatNumber,
conversationId,
@@ -247,6 +280,7 @@ export const chatMixins = {
console.log(_this.chatMessageList[_this.chatMessageListLength - 1])
}
})
*/
} else {
_this.chatErrorSetContent()
}
@@ -278,6 +312,7 @@ export const chatMixins = {
})
},
productItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
@@ -286,6 +321,7 @@ export const chatMixins = {
})
},
hotelItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
@@ -294,6 +330,7 @@ export const chatMixins = {
})
},
ichItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/heritage/details',
query: {
@@ -302,6 +339,7 @@ export const chatMixins = {
})
},
qianxianItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/famousQianxianShop',
query: {
@@ -309,6 +347,10 @@ export const chatMixins = {
}
})
},
prizeItemClickHandle() {
this.audioStopHandle()
this.$yrouter.push({ path: '/pkg-video/views/lottery' })
},
chatErrorSetContent() {
this.chatMessageList[this.chatMessageListLength - 1].isError = true
this.chatMessageList[this.chatMessageListLength - 1].content = this.errorMsg
@@ -399,6 +441,125 @@ export const chatMixins = {
} catch (err) {
console.log('send failed (' + err.code + ')')
}
},
audioStopHandle() {
// Fix bug#4049
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
}
this.isPlayAudio = false
wx.getBackgroundAudioManager().stop()
},
ttsHandle(item) {
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
// this.audioContext.destroy()
}
// 如果正在播放,点击当前播放的则认为是暂停
if (this.isPlayAudio && item.conversationId === this.audioPlayId) {
this.isPlayAudio = false
this.audioPlayId = ''
return
}
this.isPlayAudio = false
this.audioPlayId = item.conversationId
const flag = 1
if (flag === 1) {
console.log(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
uni.showLoading({
title: '合成中...'
})
this.splitAndSynthesize(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
} else {
// 调试数据
this.audioAyy = [
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749075_2f71887cc48d9b3753c39d119862d4bb&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749077_0970508786c5e3edc3a68a5eb9de048c&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749079_e77cec52cc077ba981892887c3df4cdf&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749080_02db4235771f5561cdb73c06d8c67e53&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749082_534e455c5d6ac5e9a4192ecf9952851a&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749083_78c4a2fecad538561f81c48dc4ecd645&filekey=871027724&source=miniapp_plugin'
]
this.playNextChunk()
}
},
splitAndSynthesize(text) {
const _this = this
if (text) {
const textLength = text.length
const chunkSize = 100
const chunk = text.slice(0, textLength > chunkSize ? chunkSize : textLength)
plugin.textToSpeech({
// 语言
lang: 'zh_CN',
tts: true,
// 要转换的文字
content: chunk,
success: function(res) {
console.log("语音文件路径:", res.filename)
_this.audioAyy.push(res.filename)
// 首次就立即播放,增强体验
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
if (textLength > chunkSize) {
// 递归调用,播放一定要按照文本分割顺序
_this.splitAndSynthesize(text.substr(chunkSize, textLength))
} else {
// 最后一条合成,没有正在播放则立即播放
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
}
uni.hideLoading()
},
fail: function(err) {
console.log("转换失败:", err)
uni.hideLoading()
}
})
} else {
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
uni.hideLoading()
}
},
playNextChunk() {
if (this.audioAyy.length > 0 && this.audioPlayId) {
const src = this.audioAyy.shift()
this.isPlayAudio = true
const audioContext = uni.createInnerAudioContext({
// 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项
useWebAudioImplement: false
})
this.audioContext = audioContext
audioContext.src = src
audioContext.onEnded(() => {
audioContext.destroy()
this.playNextChunk()
})
audioContext.play()
console.log('------------src' + src)
} else {
console.log('------------end')
this.isPlayAudio = false
}
},
bottomToolClickHandle(item) {
if (item.type === 'prompt') {
this.streamReq({
prompt: item.prompt,
init: 0
})
}
if (item.type === 'link') {
uni.navigateTo({
url: item.url
})
}
}
}
}
+716
View File
@@ -0,0 +1,716 @@
import {
getSseConfigV1,
getCompletionsV3,
deleteChatItemByConversationId,
// getAnalyzeKeywordsV1,
getAnalyzeKeywordsChangeV3
} from '@/api/chat/index'
import {
postCartAdd
} from '@/api/store'
import { VUE_APP_API_URL } from '@/config'
import settings from '@/config/baseSetting.js'
import { formatAiMsgContent } from '../utils/aiChat'
import cookie from '@/utils/store/cookie'
import { handleLoginFailure } from '@/utils'
const plugin = requirePlugin('WechatSI')
const removeMarkdown = require('remove-markdown')
export const chatMixinsV2 = {
data() {
return {
token: cookie.get('login_status'),
userInfo: cookie.get('userInfo'),
webUrl: this.$VUE_APP_RESOURCES_URL,
bottomTools: Object.freeze([
{
icon: 'icon-50',
text: '我要送礼',
type: 'prompt',
prompt: '我要送礼,请给我推荐一些平台的礼品',
url: ''
},
{
icon: 'icon-51',
text: '订单查询',
type: 'prompt',
prompt: '帮我查询我的待发货、待收货订单信息',
url: ''
},
{
icon: 'icon-52',
text: '拍照识图',
type: 'link',
prompt: '',
url: '/aiChat/views/imageRecognition'
}
]),
scrollData: {},
scrollTop: 0,
isScrollToBottom: false,
// 状态栏高度
statusBarHeight: 20,
bottomSafeDistance: 0,
fileHttpStr: Object.freeze(VUE_APP_API_URL + settings.sysPrefix),
keyboardHeight: 0,
optionsFrom: '',
onFocus: false,
loading: false,
chatNumber: '',
infoResData: {},
chatMessageList: [
/*
{
type: -2,
isError: false,
showCursor: false,
conversationId: '',
prompt: '',
nodes: '-------------监听 WebSocket 接受到服务器的消息事件',
content: '-------------监听 WebSocket 接受到服务器的消息事件',
listQuestion: [
'监听 WebSocket 接受到服务器的消息事件',
'监听 WebSocket 接受到服务器的消息事件',
'监听 WebSocket 接受到服务器的消息事件'
]
}
*/
],
errorMsg: Object.freeze('您提的问题未能理解,云灵思想跑偏啦,重新整理一下问题或者重新提问,让云灵再试试!'),
audioAyy: [],
isPlayAudio: false,
audioContext: null,
audioPlayId: '',
showGiftBtn: false,
buyLoading: false
}
},
computed: {
chatMessageListLength() {
return this.chatMessageList.length
}
},
onPageScroll(e) {
this.scrollData = e
const scrollTop = e.scrollTop
this.scrollTop = scrollTop
const query = wx.createSelectorQuery().in(this)
query.select('#fixTbabarBody').boundingClientRect(function(rect) {
const contentHeight = rect.height
const windowInfo = wx.getWindowInfo()
const windowHeight = windowInfo.windowHeight
if (scrollTop + windowHeight + 30 >= contentHeight) {
this.isScrollToBottom = true
} else {
this.isScrollToBottom = false
}
}.bind(this)).exec()
},
onUnload() {
if (this.audioContext) {
this.audioContext.stop()
}
wx.getBackgroundAudioManager().stop()
this.closeWsFn()
},
created() {
const _this = this
uni.getSystemInfo({
success: (e) => {
let statusBar = 0
// #ifdef MP-WEIXIN
statusBar = e.statusBarHeight
const custom = uni.getMenuButtonBoundingClientRect()
_this.rightDistance = e.windowWidth - custom.left + 10
// #endif
// 状态栏高度
_this.statusBarHeight = statusBar
_this.bottomSafeDistance = e.safeAreaInsets.bottom
}
})
_this.createdCallbak()
},
methods: {
createdCallbak() {
console.log('页面初始话回调')
},
inputFocusHandle(value) {
this.onFocus = value === 1
this.scrollToBottomHandle()
},
historyViewHandle() {
console.log(this.$refs.historyView)
this.$refs.historyView.showView()
},
copyContentHandle(data) {
uni.setClipboardData({
data,
success: () => {
uni.showToast({
title: '已复制'
})
}
})
},
copyMoreInfoHandle(item) {
uni.setClipboardData({
data: JSON.stringify(item.showMoreInfo),
success: () => {
uni.showToast({
title: '已复制'
})
}
})
},
shareHandle() {
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline'],
success: (res) => {
console.log(res)
},
fail: err => {
console.log(err)
}
})
},
scrollToBottomHandle() {
uni.pageScrollTo({
duration: 100,
selector: '#bottom_postion'
})
this.isScrollToBottom = true
},
// 重新生成
regenerateHandle(item) {
this.audioStopHandle()
deleteChatItemByConversationId(item.conversationId).then(res => {
const { success } = res
if (success) {
this.chatMessageList.splice(this.chatMessageListLength - 1, 1)
this.streamReq({
prompt: item.prompt,
init: 1
})
}
})
},
// 建议提问
suggestionItemClickHandle(question) {
this.streamReq({
prompt: question,
init: 0
})
},
// 大家都在问
topicItemClickHandle(question) {
this.streamReq({
prompt: question,
init: 0
})
},
streamReq(params) {
const _this = this
// 需要校验登录状态
if (!_this.token) {
handleLoginFailure()
return
}
const prompt = params.prompt
if (params.init !== 1) {
_this.chatMessageList.push({
prompt,
type: -1
})
}
_this.$nextTick(() => {
_this.scrollToBottomHandle()
})
_this.chatMessageList.push({
content: '',
nodes: '',
isError: false,
showCursor: false,
conversationId: '',
prompt,
listQuestion: [],
// 特产商品
goodsList: [],
// 礼品商品
giftGoodsList: [],
// 非遗文化
ichList: [],
// 千县名品
qianxianList: [],
// 抽奖
prizeList: [],
// 店铺
shopList: [],
// 地标好物--屏蔽原因:实现此功能时,后端通过AI动态返回的图片不满足需求,后面就不要了
landmarksList: [],
// 订单
orderList: [],
type: -2,
showMoreInfo: {},
currentSwiperIndex: 0,
showChangeBtn: true,
changeIsRotate: false
})
_this.loading = true
_this.showCursor = false
getSseConfigV1({
'chatNumber': _this.chatNumber,
prompt
}).then(resConfig => {
const conversationId = resConfig.data.conversationId
_this.chatNumber = resConfig.data.chatNumber || ''
_this.chatMessageList[_this.chatMessageListLength - 1].showCursor = true
_this.chatMessageList[_this.chatMessageListLength - 1].conversationId = conversationId
if (conversationId) {
_this.initWebSocket(conversationId, () => {
getCompletionsV3({
conversationId,
ws: true,
isNoNeedPublicErrorNotification: 1
}).then(sseRes => {
console.log(sseRes)
// _this.closeWsFn()
}).catch(err => {
console.log(err)
_this.chatErrorSetContent()
})
})
} else {
_this.chatErrorSetContent()
}
}).catch(err => {
console.log(err)
_this.chatErrorSetContent()
})
},
initWebSocket(conversationId, cb) {
const _this = this
console.log('------------------创建连接')
uni.connectSocket({
url: VUE_APP_API_URL.replace('https', 'wss') + settings.sysPrefix + `v1/chat/websocket/${_this.userInfo.unionId}-${conversationId}`,
header: {
'content-type': 'application/json'
},
timeout: 10 * 1000,
success: () => {
console.log('------------WebSocket初始化成功')
},
fail: (err) => {
uni.showModal({
showCancel: false,
content: err
})
}
})
uni.onSocketMessage(res => {
_this.websocketOnmessage(res)
})
uni.onSocketOpen(() => {
cb && cb()
_this.websocketOnopen()
})
uni.onSocketError(err => {
_this.websocketOnerror(err)
})
},
websocketOnmessage(res) {
console.log('-------------监听 WebSocket 接受到服务器的消息事件')
const resData = JSON.parse(res.data)
if (resData) {
const message = resData.message
console.log(message)
if (message && this.chatMessageListLength > 1) {
const mesContent = message.content
const contentType = message.contentType
const finish = message.finish
if (contentType === 'text') {
if (!finish && mesContent) {
this.chatMessageList[this.chatMessageListLength - 1].nodes = formatAiMsgContent(mesContent)
this.chatMessageList[this.chatMessageListLength - 1].content = mesContent
this.chatMessageList[this.chatMessageListLength - 1].listQuestion = message.listQuestion || []
}
}
if (contentType === 'order') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].orderList = message.listCards || []
}
}
if (contentType === 'shop') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].shopList = message.listCards || []
}
}
if (contentType === 'gift') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].giftGoodsList = message.listCards || []
}
}
if (contentType === 'discount') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].prizeList = message.listCards || []
}
}
if (contentType === 'goods') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].goodsList = message.listCards || []
}
}
if (contentType === 'famous') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].qianxianList = message.listCards || []
}
}
if (contentType === 'ich') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].ichList = message.listCards || []
}
}
if (contentType === 'landmarks') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].landmarksList = message.listCards || []
}
}
this.$forceUpdate()
this.$nextTick(() => {
this.scrollToBottomHandle()
if (finish) {
this.closeWsFn()
}
})
}
}
},
websocketOnopen() {
console.log('------------------监听 WebSocket 连接打开事件')
// this.websocketSend(JSON.stringify({ msg: '1' }))
},
websocketOnerror(e) {
console.log('-----------------监听 WebSocket 错误事件')
console.log(e)
},
websocketSend(msg) {
// 数据发送
try {
uni.sendSocketMessage({
data: msg,
success: () => {
console.log('-----------发送消息成功了')
}
})
} catch (err) {
console.log('send failed (' + err.code + ')')
}
},
onSwiperChange(index, e) {
this.chatMessageList[index].currentSwiperIndex = e.detail.current
},
analyzeKeywordsChangeHandle(conversationId, item) {
let idx = -1
this.chatMessageList.map((item, index) => {
if (item.conversationId === conversationId) {
idx = index
}
})
if (idx > -1) {
if (this.chatMessageList[idx].changeIsRotate) {
return
}
this.$set(this.chatMessageList[idx], 'changeIsRotate', true)
}
let listKey = ''
// 抽奖和订单查询没有换一换
if (item.goodsList.length > 0) {
listKey = 'goodsList'
}
if (item.giftGoodsList.length > 0) {
listKey = 'giftGoodsList'
}
if (item.ichList.length > 0) {
listKey = 'ichList'
}
if (item.qianxianList.length > 0) {
listKey = 'qianxianList'
}
if (item.shopList.length > 0) {
listKey = 'shopList'
}
getAnalyzeKeywordsChangeV3({
conversationId
}).then(res => {
const { success, data } = res
if (success) {
let changeList = []
this.chatMessageList.map((item) => {
if (item.conversationId === conversationId) {
changeList = data.list || []
}
})
if (idx > -1 && listKey) {
this.$set(this.chatMessageList[idx], listKey, changeList)
this.$forceUpdate()
}
setTimeout(() => {
this.$set(this.chatMessageList[idx], 'changeIsRotate', false)
}, 1000)
}
}).catch(error => {
this.$set(this.chatMessageList[idx], 'changeIsRotate', false)
})
},
productItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
id: item.id
}
})
},
productItemSupplierClickHandle(item) {
if (!item.storeId) {
return
}
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.storeId
}
})
},
productItemBuyNowClickHandle(item) {
this.audioStopHandle()
this.showGiftBtn = false
this.cart_num = 1
this.addToCart(item)
},
productItemGiftBuyClickHandle(item) {
this.audioStopHandle()
this.showGiftBtn = true
this.cart_num = 1
this.addToCart(item)
},
buyNowOrGiftBuyReq(type) {
const _this = this
if (_this.buyLoading) {
return
}
_this.buyLoading = true
postCartAdd({
productId: _this.m_id,
cartNum: _this.attr.productSelect.cart_num,
new: 1,
uniqueId: _this.attr.productSelect !== undefined ? _this.attr.productSelect.unique : ''
}).then(function (res) {
try {
_this.closeAttrWindow()
const { cartId } = res.data
if (type === 1) {
uni.navigateTo({
url: '/pages/order/OrderSubmission/index?id=' + cartId
})
return
}
if(type === 2) {
uni.navigateTo({
url: '/pkg_user/views/gift/gift?cartId=' + cartId
})
return
}
} catch (error) {
console.log(error)
}
}).catch(error => {
uni.showToast({
title: error.msg,
icon: "none",
duration: 5000
})
}).finally(() => {
_this.buyLoading = false
})
},
closeAttrWindow() {
this.$set(this.attr, 'cartAttr', false)
},
hotelItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.id
}
})
},
ichItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/heritage/details',
query: {
id: item.id
}
})
},
qianxianItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/famousQianxianShop',
query: {
id: item.id
}
})
},
prizeItemClickHandle() {
this.audioStopHandle()
this.$yrouter.push({ path: '/pkg-video/views/lottery' })
},
chatErrorSetContent() {
this.chatMessageList[this.chatMessageListLength - 1].isError = true
this.chatMessageList[this.chatMessageListLength - 1].content = this.errorMsg
this.chatMessageList[this.chatMessageListLength - 1].nodes = this.errorMsg
this.closeWsFn()
},
closeWsFn() {
this.loading = false
if (this.chatMessageListLength) {
this.chatMessageList[this.chatMessageListLength - 1].showCursor = false
}
uni.closeSocket({
success: () => {
console.log('-----关闭连接')
}
})
this.$nextTick(() => {
this.scrollToBottomHandle()
})
},
audioStopHandle() {
// Fix bug#4049
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
}
this.isPlayAudio = false
wx.getBackgroundAudioManager().stop()
},
ttsHandle(item) {
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
// this.audioContext.destroy()
}
// 如果正在播放,点击当前播放的则认为是暂停
if (this.isPlayAudio && item.conversationId === this.audioPlayId) {
this.isPlayAudio = false
this.audioPlayId = ''
return
}
this.isPlayAudio = false
this.audioPlayId = item.conversationId
const flag = 1
if (flag === 1) {
console.log(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
uni.showLoading({
title: '合成中...'
})
this.splitAndSynthesize(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
} else {
// 调试数据
this.audioAyy = [
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749075_2f71887cc48d9b3753c39d119862d4bb&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749077_0970508786c5e3edc3a68a5eb9de048c&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749079_e77cec52cc077ba981892887c3df4cdf&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749080_02db4235771f5561cdb73c06d8c67e53&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749082_534e455c5d6ac5e9a4192ecf9952851a&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749083_78c4a2fecad538561f81c48dc4ecd645&filekey=871027724&source=miniapp_plugin'
]
this.playNextChunk()
}
},
splitAndSynthesize(text) {
const _this = this
if (text) {
const textLength = text.length
const chunkSize = 100
const chunk = text.slice(0, textLength > chunkSize ? chunkSize : textLength)
plugin.textToSpeech({
// 语言
lang: 'zh_CN',
tts: true,
// 要转换的文字
content: chunk,
success: function(res) {
console.log("语音文件路径:", res.filename)
_this.audioAyy.push(res.filename)
// 首次就立即播放,增强体验
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
if (textLength > chunkSize) {
// 递归调用,播放一定要按照文本分割顺序
_this.splitAndSynthesize(text.substr(chunkSize, textLength))
} else {
// 最后一条合成,没有正在播放则立即播放
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
}
uni.hideLoading()
},
fail: function(err) {
console.log("转换失败:", err)
uni.hideLoading()
}
})
} else {
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
uni.hideLoading()
}
},
playNextChunk() {
if (this.audioAyy.length > 0 && this.audioPlayId) {
const src = this.audioAyy.shift()
this.isPlayAudio = true
const audioContext = uni.createInnerAudioContext({
// 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项
useWebAudioImplement: false
})
this.audioContext = audioContext
audioContext.src = src
audioContext.onEnded(() => {
audioContext.destroy()
this.playNextChunk()
})
audioContext.play()
console.log('------------src' + src)
} else {
console.log('------------end')
this.isPlayAudio = false
}
},
bottomToolClickHandle(item) {
if (item.type === 'prompt') {
this.streamReq({
prompt: item.prompt,
init: 0
})
}
if (item.type === 'link') {
uni.navigateTo({
url: item.url
})
}
},
viewOrderTrackHandle(order) {
uni.navigateTo({
url: '/pages/order/OrderDetails/index?id=' + order.orderId
})
}
}
}
+260 -41
View File
@@ -33,16 +33,26 @@
{{ item.prompt }}
</view>
</view>
<view class="item-right-tools">
<view class="btn" @click="copyContentHandle(item.prompt)">
<image
:src="webUrl + '/aiChat/icon-02.png'"
class="icon"
mode="widthFix"
/>
复制
</view>
</view>
</view>
<view v-if="item.type === -2" class="chat-message-body">
<view class="avatar">
<view v-if="item.nodes && item.nodes.length" class="avatar">
<image
:src="webUrl + '/aiChat/avatar.png'"
class="chat-avatar"
mode="widthFix"
/>
</view>
<view class="item-left">
<view v-if="item.nodes && item.nodes.length" class="item-left">
<view class="txt">
<view
ref="rich-text-box"
@@ -97,17 +107,90 @@
/>
重新生成
</view>
<view
:class="{
'playing': audioPlayId === item.conversationId && isPlayAudio
}"
class="item"
@click="ttsHandle(item)"
>
<image
:src="webUrl + '/aiChat/' + ((audioPlayId === item.conversationId && isPlayAudio) ? 'audio-on' : 'audio-off') +'.png'"
class="icon"
mode="widthFix"
/>
语音播放
</view>
</view>
</view>
</view>
<!-- 换一换 -->
<view
v-if="item.showMoreInfo.show"
v-if="!item.showCursor &&
!item.isError &&
(item.orderList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.goodsList.length > 0 ||
item.shopList.length > 0 ||
item.prizeList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0
)
"
class="change-list-wrap"
>
<view v-if="item.showMoreInfo.showType === '特产商品'" class="list-wrap">
<!-- 礼品 -->
<view
v-if="item.giftGoodsList.length > 0"
class="recommend-goods-wrap"
>
<swiper
class="goods-swiper"
:indicator-dots="false"
:autoplay="false"
:current="item.currentSwiperIndex"
previous-margin="120rpx"
next-margin="120rpx"
circular
@change="e => onSwiperChange(index, e)"
>
<swiper-item
v-for="(goodItem, goodIndex) in item.giftGoodsList"
:key="goodIndex"
>
<view
:class="{ 'active': item.currentSwiperIndex === goodIndex }"
class="goods-item"
@click="productItemClickHandle(goodItem)"
>
<view class="supplier" @click.stop="productItemSupplierClickHandle(goodItem)">
<image
:src="webUrl + '/aiChat/icon-57.png'"
class="icon"
mode="widthFix"
lazy-load
/>
<text class="s-name">{{ goodItem.merName ||goodItem.storeName }}</text>
<u-icon name="arrow-right" color="#999" size="12"></u-icon>
</view>
<view class="title one-t">{{ goodItem.name }}</view>
<image :src="goodItem.img" class="p-img" mode="aspectFill" lazy-load />
<view class="price">
<text class="unit">¥</text>
<text class="num">{{ goodItem.price }}</text>
</view>
<view class="actions">
<view class="btn-buy" @click.stop="productItemBuyNowClickHandle(goodItem)">立即下单</view>
<view class="btn-gift" @click.stop="productItemGiftBuyClickHandle(goodItem)">送给朋友</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 特产商品 -->
<view v-if="item.goodsList.length > 0" class="list-wrap">
<view
v-for="(productItem, productIndex) in item.showMoreInfo.resultItems"
v-for="(productItem, productIndex) in item.goodsList"
:key="productIndex"
class="product-item"
@click="productItemClickHandle(productItem)"
@@ -115,6 +198,7 @@
<image
:src="productItem.img"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name one-t">
@@ -129,19 +213,21 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '店铺'" class="list-wrap">
<!-- 店铺 -->
<view v-if="item.shopList.length > 0" class="list-wrap">
<view
v-for="(hotel, hotelIndex) in item.showMoreInfo.resultItems"
v-for="(hotel, hotelIndex) in item.shopList"
:key="hotelIndex"
class="product-item"
@click="hotelItemClickHandle(hotel)"
>
<view class="hotel-type">
手工体验
{{ hotel.hotelTypeName }}
</view>
<image
:src="hotel.img"
:src="hotel.img + ((hotel.img && hotel.img.indexOf('.mp4') > -1) ? '?vframe/jpg/offset/1' : '')"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name one-t">
@@ -153,9 +239,10 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '非遗文化'" class="list-wrap">
<!-- 非遗 -->
<view v-if="item.ichList.length > 0" class="list-wrap">
<view
v-for="(ich, ichIndex) in item.showMoreInfo.resultItems"
v-for="(ich, ichIndex) in item.ichList"
:key="ichIndex"
class="product-item"
@click="ichItemClickHandle(ich)"
@@ -174,9 +261,10 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '千县名品'" class="list-wrap">
<!-- 千县 -->
<view v-if="item.qianxianList.length > 0" class="list-wrap">
<view
v-for="(qianxian, qianxianIndex) in item.showMoreInfo.resultItems"
v-for="(qianxian, qianxianIndex) in item.qianxianList"
:key="qianxianIndex"
class="product-item"
@click="qianxianItemClickHandle(qianxian)"
@@ -184,6 +272,7 @@
<image
:src="qianxian.img"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name name2 one-t">
@@ -192,15 +281,59 @@
</view>
</view>
</view>
<view class="change-btn">
<image
:src="webUrl + '/aiChat/icon-change.png'"
class="icon"
mode="widthFix"
@click="analyzeKeywordsChangeHandle(item.conversationId, item.showMoreInfo)"
/>
<!-- 抽奖 -->
<view v-if="item.prizeList.length > 0" class="list-wrap prize-list-wrap">
<view
v-for="(prize, prizeIndex) in item.prizeList"
:key="prizeIndex"
class="prize-item"
@click="prizeItemClickHandle(prize)"
>
<image
:src="prize.img"
class="img"
mode="widthFix"
lazy-load
/>
</view>
</view>
<!-- 订单 -->
<view v-if="item.orderList.length > 0" class="order-list-wrap">
<view
v-for="(order, orderIndex) in item.orderList"
:key="orderIndex"
class="order-item-wrap"
>
<order-item
:item="order"
@viewOrderTrack="viewOrderTrackHandle"
/>
</view>
</view>
<view
v-if="(item.shopList.length > 0 ||
item.goodsList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0 ||
item.shopList.length > 0) && item.showChangeBtn
"
class="change-btn"
>
<view class="change-btn-content" @click="analyzeKeywordsChangeHandle(item.conversationId, item)">
<image
:src="webUrl + '/aiChat/icon-53.png'"
:class="{
'rotate': item.changeIsRotate
}"
class="icon"
mode="widthFix"
/>
换一换
</view>
</view>
</view>
<!-- 建议提问 -->
<view
v-if="index === (chatMessageListLength - 1) && item.listQuestion.length > 0"
class="suggestion-wrap"
@@ -237,24 +370,42 @@
}"
class="page-to-bottom"
>
<view
class="new-chat"
@click="createNewHandle"
>
<view class="down-icon-wrap">
<image
:src="webUrl + '/aiChat/icon-05.png'"
class="btn-icon"
v-if="chatMessageListLength > 0 && !isScrollToBottom && !loading"
:src="webUrl + '/aiChat/down.png'"
class="icon down-icon"
mode="widthFix"
@click="scrollToBottomHandle"
/>
开启新对话
</view>
<image
v-if="chatMessageListLength > 0 && !isScrollToBottom && !loading"
:src="webUrl + '/aiChat/down.png'"
class="icon"
mode="widthFix"
@click="scrollToBottomHandle"
/>
<view class="container">
<view
v-if="chatNumber"
class="new-chat"
@click="createNewHandle"
>
<image
:src="webUrl + '/aiChat/icon-54.png'"
class="btn-icon"
mode="widthFix"
/>
开启新对话
</view>
<view
v-for="(item, index) in bottomTools"
:key="index"
class="bottom-tool-item"
@click="bottomToolClickHandle(item)"
>
<image
:src="webUrl + '/aiChat/' + item.icon + '.png'"
class="tool-icon"
mode="widthFix"
/>
{{ item.text }}
</view>
</view>
</view>
<bottom-send
ref="bottomSend"
@@ -268,14 +419,31 @@
ref="historyView"
:status-bar-height="statusBarHeight"
form-module="history"
@enter-history="audioStopHandle"
/>
<ProductWindow
ref="attrWindow"
:attr="attr"
:cart-num="cart_num"
:show-ok="!showGiftBtn"
:is-gift="showGiftBtn"
:padding-bottom="'60px'"
class-name="ai-product-window"
ok-text="立即购买"
@changeFun="changeFun"
@ok="buyNowOrGiftBuyReq(1)"
@gift="buyNowOrGiftBuyReq(2)"
/>
</view>
</template>
<script>
import { chatMixins } from '../mixins/chatMixins.js'
import { chatMixinsV2 } from '../mixins/chatMixinsV2.js'
import goCartMixin from '@/mixins/goCartMixins'
import AiHistoryList from '../components/historyList'
import BottomSend from '../components/bottomSend.vue'
import OrderItem from '../components/orderItem.vue'
import ProductWindow from '@/components/ProductWindow'
import {
historyChatMessage
} from '@/api/chat/index'
@@ -284,9 +452,11 @@ export default {
name: 'AiChatHistoryPage',
components: {
AiHistoryList,
BottomSend
BottomSend,
OrderItem,
ProductWindow
},
mixins: [chatMixins],
mixins: [chatMixinsV2, goCartMixin],
data() {
return {
pageTitle: ''
@@ -315,19 +485,68 @@ export default {
prompt: item.content
})
} else {
if (item.contentType === 'text') {
this.chatMessageList.push({
if (['text', 'skill_list'].includes(item.contentType)) {
const tempItemData = {
...item,
nodes: formatAiMsgContent(item.content),
prompt: this.getParentPrompt(data, item.parentMessageId),
conversationId: item.messageId,
showCursor: false,
listQuestion: item.listQuestion || [],
type: -2
})
// 特产商品
goodsList: [],
// 礼品商品
giftGoodsList: [],
// 非遗文化
ichList: [],
// 千县名品
qianxianList: [],
// 抽奖
prizeList: [],
// 店铺
shopList: [],
// 地标好物--屏蔽原因:实现此功能时,后端通过AI动态返回的图片不满足需求,后面就不要了
landmarksList: [],
// 订单
orderList: [],
type: -2,
showMoreInfo: {},
currentSwiperIndex: 0,
showChangeBtn: false,
changeIsRotate: false
}
if (item.cardData) {
const { contentType, finish, items = [] } = item.cardData
if (contentType === 'order') {
tempItemData.orderList = items || []
}
if (contentType === 'shop') {
tempItemData.shopList = items || []
}
if (contentType === 'gift') {
tempItemData.giftGoodsList = items || []
}
if (contentType === 'discount') {
tempItemData.prizeList = items || []
}
if (contentType === 'goods') {
tempItemData.goodsList = items || []
}
if (contentType === 'famous') {
tempItemData.qianxianList = items || []
}
if (contentType === 'ich') {
tempItemData.ichList = items || []
}
if (contentType === 'landmarks') {
tempItemData.landmarksList = items || []
}
}
this.chatMessageList.push(tempItemData)
}
}
})
console.log(this.chatMessageList)
} catch (err) {
uni.showModal({
title: '错误提示',
+227
View File
@@ -0,0 +1,227 @@
<template>
<view
:style="{ 'background-image': 'url(' + webUrl + '/aiChat/bg-05.png)' }"
class="image-recognition-page"
>
<hx-navbar
:back="true"
:fixed="true"
:statusBar="true"
left-icon="arrowleft"
color="#333"
transparent="auto"
barPlaceholder="hidden"
title="云灵"
/>
<view
class="main-content"
:style="{ 'padding-top': (44 + statusBarHeight) + 'px' }"
>
<!-- 拍照区域 -->
<view class="camera-section" @click="takePhoto">
<view class="dashed-border-box">
<view class="icon-wrap">
<image
:src="webUrl + '/aiChat/icon-55.png'"
class="camera-icon"
mode="widthFix"
/>
</view>
<view class="main-title">对准商品拍照</view>
<view class="sub-tip">示例特产/零食/茶叶/纪念品</view>
</view>
</view>
<!-- 底部相册上传按钮 -->
<view class="bottom-action">
<button class="album-btn" @click="chooseImage">
<image
:src="webUrl + '/aiChat/icon-56.png'"
class="upload-icon"
mode="widthFix"
/>
<text>从相册上传图片进行识别</text>
</button>
</view>
</view>
</view>
</template>
<script>
import { chatMixins } from '../mixins/chatMixins.js'
import { VUE_APP_API_URL } from '@/config'
export default {
name: 'AiChatImageRecognitionPage',
mixins: [chatMixins],
data() {
return {
}
},
methods: {
createdCallbak() {
// 覆盖 mixins 中的 createdCallbak,避免触发默认逻辑
},
takePhoto() {
if (this.loading) return
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['camera'],
success: (res) => {
this.handleUpload(res.tempFilePaths[0])
}
})
},
chooseImage() {
if (this.loading) return
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album'],
success: (res) => {
this.handleUpload(res.tempFilePaths[0])
}
})
},
handleUpload(filePath) {
const _this = this
uni.showLoading({ title: '识别中...' })
_this.loading = true
uni.uploadFile({
url: `${VUE_APP_API_URL}/api/upload`,
filePath: filePath,
header: {
Authorization: 'Bearer ' + (_this.token || '')
},
name: 'file',
success: (uploadRes) => {
const resData = JSON.parse(uploadRes.data)
if (resData.link) {
_this.performRecognition(resData.link)
} else {
uni.hideLoading()
_this.loading = false
_this.$toast('上传失败,请重试')
}
},
fail: (err) => {
console.log('Upload Error:', err)
uni.hideLoading()
_this.loading = false
_this.$toast('网络异常,请重试')
}
})
},
performRecognition(imageUrl) {
// 存储识别到的图片URL
uni.setStorageSync('recognitionImage', imageUrl)
uni.navigateTo({
url: '/aiChat/views/imageRecognitionResult'
})
uni.hideLoading()
this.loading = false
}
}
}
</script>
<style lang="scss" scoped>
.image-recognition-page {
height: 100vh;
background-size: 100% 100%;
.main-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 60rpx 40rpx;
height: 100vh;
box-sizing: border-box;
}
.camera-section {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 60rpx;
.dashed-border-box {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 600rpx;
width: 100%;
margin-top: 80rpx;
border: 2rpx dashed #DCDFE6;
border-radius: 24rpx;
background-color: rgba(255, 255, 255, 0.5);
.icon-wrap {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
border: 6rpx solid #7A3DF1;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 40rpx;
.camera-icon {
width: 80rpx;
height: 80rpx;
}
}
.main-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.sub-tip {
font-size: 28rpx;
color: #999;
}
}
}
.bottom-action {
width: 100%;
padding-bottom: 60rpx;
.album-btn {
width: 100%;
height: 100rpx;
background: #3D4CF1;
border-radius: 20rpx;
display: flex;
justify-content: center;
align-items: center;
color: #FFFFFF;
font-size: 32rpx;
border: none;
box-shadow: 0 8rpx 20rpx rgba(61, 76, 241, 0.3);
.upload-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
}
&::after {
border: none;
}
&:active {
opacity: 0.8;
}
}
}
}
</style>
+338
View File
@@ -0,0 +1,338 @@
<template>
<view class="image-recognition-result-page">
<view
class="page-body"
>
<view class="header">
<view
class="header-bg"
:style="{ 'background-image': imageUrl ? `url(${imageUrl})` : '' }"
/>
<view class="header-mask" />
<view class="scan-wrap">
<image
v-if="imageUrl"
:src="imageUrl"
class="scan-image"
mode="aspectFill"
/>
<view class="corner tl" />
<view class="corner tr" />
<view class="corner bl" />
<view class="corner br" />
</view>
</view>
<view class="sheet">
<view class="sheet-thumb-row">
<image
v-if="imageUrl && confidenceFlag"
:src="imageUrl"
class="sheet-thumb"
mode="aspectFill"
lazy-load
/>
<view v-else class="empty-state">
<view class="empty-text">未识别到物品请重新尝试</view>
<view class="empty-title">为你推荐</view>
</view>
</view>
<view class="sheet-divider" />
<view class="goods-grid">
<view
v-for="(item, index) in goodsList"
:key="index"
class="goods-card"
@click="goGoods(item)"
>
<image
:src="item.img"
class="goods-img"
mode="aspectFill"
lazy-load
/>
<view class="goods-title">
{{ item.name }}
</view>
<view class="goods-price-row">
<view class="price-left">
<text class="price-symbol"></text>
<text class="price-val">{{ item.price }}</text>
</view>
<image
:src="webUrl + '/home/icon-cart.png'"
class="cart-btn"
mode="aspectFit"
lazy-load
/>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getAnalyzeByImageV3 } from '@/api/chat'
import Recommend from '@/components/Recommend'
export default {
name: 'AiChatImageRecognitionResultPage',
components: {
Recommend
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
statusBarHeight: 20,
imageUrl: '',
goodsList: [],
confidenceFlag: true
}
},
onLoad(options) {
const urlFromQuery = options.imageUrl || ''
const urlFromStorage = uni.getStorageSync('recognitionImage') || ''
this.imageUrl = urlFromQuery || urlFromStorage || ''
if (urlFromStorage) {
// uni.removeStorageSync('recognitionImage')
}
this.init()
},
methods: {
init() {
this.loaded = false
getAnalyzeByImageV3({
imageUrl: this.imageUrl
}).then(res => {
const { data, success } = res
if (success) {
this.goodsList = data.cards || []
this.confidenceFlag = data.success
}
}).finally(() => {
this.loaded = true
})
},
goGoods(item) {
if (!item || !item.id) return
uni.navigateTo({
url: `/pages/shop/GoodsCon/index?id=${item.id}`
})
},
addToCart() {
}
}
}
</script>
<style lang="scss" scoped>
.image-recognition-result-page {
min-height: 100vh;
background: #fff;
.page-body {
min-height: 100vh;
}
.header {
position: relative;
height: 480rpx;
overflow: hidden;
}
.header-bg {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: #DDE6FF;
background-size: cover;
background-position: center;
filter: blur(18rpx);
transform: scale(1.2);
}
.header-mask {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.18);
}
.scan-wrap {
position: absolute;
left: 50%;
top: 45%;
width: 320rpx;
height: 320rpx;
transform: translate(-50%, -52%);
border-radius: 24rpx;
overflow: hidden;
// box-shadow: 0 18rpx 48rpx rgba(0, 0, 0, 0.18);
}
.scan-image {
display: block;
width: 100%;
height: 100%;
border-radius: 12rpx;
transform: scale(0.8);
}
.corner {
position: absolute;
width: 44rpx;
height: 44rpx;
border: 6rpx solid rgba(255, 255, 255, 0.95);
}
.corner.tl {
left: 16rpx;
top: 16rpx;
border-right: 0;
border-bottom: 0;
}
.corner.tr {
right: 16rpx;
top: 16rpx;
border-left: 0;
border-bottom: 0;
}
.corner.bl {
left: 16rpx;
bottom: 16rpx;
border-right: 0;
border-top: 0;
}
.corner.br {
right: 16rpx;
bottom: 16rpx;
border-left: 0;
border-top: 0;
}
.sheet {
position: relative;
margin-top: -70rpx;
background: #FFFFFF;
border-top-left-radius: 42rpx;
border-top-right-radius: 42rpx;
padding-bottom: calc(env(safe-area-inset-bottom) + 28rpx);
}
.sheet-thumb-row {
padding: 28rpx 0 18rpx 0;
display: flex;
justify-content: center;
}
.sheet-thumb {
width: 100rpx;
height: 100rpx;
border-radius: 18rpx;
display: block;
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.12);
}
.sheet-divider {
height: 2rpx;
background: #EDEDED;
margin: 0 32rpx;
}
.empty-state {
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 20rpx;
}
.empty-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.goods-grid {
padding: 28rpx 24rpx 0 24rpx;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.goods-card {
width: 344rpx;
background: #FFFFFF;
border-radius: 22rpx;
overflow: hidden;
margin-bottom: 22rpx;
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.06);
}
.goods-img {
width: 344rpx;
height: 344rpx;
display: block;
}
.goods-title {
padding: 14rpx 14rpx 0 14rpx;
font-size: 28rpx;
line-height: 38rpx;
color: #333;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
min-height: 76rpx;
}
.goods-price-row {
padding: 10rpx 14rpx 16rpx 14rpx;
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.price-left {
display: flex;
align-items: flex-end;
}
.price-symbol {
font-size: 26rpx;
color: #C52733;
line-height: 1;
margin-right: 2rpx;
}
.price-val {
font-size: 40rpx;
font-weight: bold;
color: #C52733;
line-height: 1;
}
.origin-price {
font-size: 24rpx;
color: #999;
line-height: 1;
margin-left: 12rpx;
text-decoration: line-through;
}
.cart-btn {
width: 52rpx;
height: 52rpx;
display: block;
}
}
</style>
+390 -142
View File
@@ -1,6 +1,9 @@
<template>
<view
:class="scrollTop > 0 ? 'page-scroll' : ''"
:class="{
'page-scroll': scrollTop > 0,
'new-chat': chatMessageListLength === 0
}"
class="fix-tabbar-page"
>
<hx-navbar
@@ -12,7 +15,7 @@
color="#333"
transparent="auto"
barPlaceholder="hidden"
title="云灵AI智能助手"
title="云灵"
@click-left="clickBackHandle"
/>
<page-container
@@ -32,65 +35,108 @@
}"
class="fix-tabbar-body"
>
<!-- <button @click="ttsHandle({
content: '是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项。临床试验统计分析结果生成系统是服务于医学研究的数据处理平台。系统聚焦临床试验数据,可对患者入组信息、疗效指标、安全性数据等进行标准化统计分析,自动生成包含描述性统计、假设检验、生存分析等内容的结果报告。支持可视化图表输出,如疗效对比柱状图、不良事件分布饼图等,直观呈现数据特征。具备灵活的参数配置功能,可适配不同试验设计与分析需求,助力研究者高效完成数据解读,为药物研发、临床研究结论输出提供精准的统计支持。',
conversationId: '123'
})">播放</button> -->
<block v-if="chatMessageListLength < 1">
<view class="home-focus-wrap">
<view class="focus-guide-wrap">
<view class="guide-left">
<image
:src="webUrl + '/aiChat/logo-01.gif'"
class="avatar"
mode="widthFix"
/>
</view>
<view class="guide-right">
<view class="trangle" />
<view class="title">{{ settingInfo.helloMessageTitle }}</view>
<view class="content">{{ settingInfo.helloMessageContent }}</view>
</view>
</view>
</view>
<view class="topic-wrap">
<view class="title">
<image
:src="webUrl + '/aiChat/icon-01.png'"
class="icon"
mode="widthFix"
/>
<view class="txt">#大家都在问</view>
<image
:src="webUrl + '/aiChat/icon-01.png'"
class="icon"
mode="widthFix"
/>
</view>
<view class="list-wrap">
<view
:style="{
'width': animationWidth,
'animation': animationStr
}"
class="list-cont"
>
<view
v-for="(item, index) in topicList"
:key="index"
class="list-item"
@click="topicItemClickHandle(item.title)"
>
<view class="home-wrap">
<view class="home-focus-wrap">
<view class="focus-guide-wrap">
<view class="guide-left">
<image
v-if="item.isHot"
:src="webUrl + '/aiChat/hot.png'"
:src="webUrl + '/aiChat/logo-01.gif'"
class="avatar"
mode="widthFix"
/>
</view>
<view class="guide-right">
<view class="title">{{ settingInfo.helloMessageTitle }}</view>
<view class="content">{{ settingInfo.helloMessageContent }}</view>
</view>
</view>
</view>
<!-- 这里实现商品轮播 -->
<!-- <view class="recommend-goods-wrap" v-if="recommendGoodsList.length > 0">
<swiper
class="goods-swiper"
:indicator-dots="false"
:autoplay="false"
previous-margin="120rpx"
next-margin="120rpx"
circular
@change="onSwiperChange"
>
<swiper-item
v-for="(item, index) in recommendGoodsList"
:key="index"
>
<view
class="goods-item"
:class="{ 'active': currentSwiperIndex === index }"
@click="productItemClickHandle(item)"
>
<view class="supplier">
<image
:src="webUrl + '/aiChat/icon-57.png'"
class="icon"
mode="widthFix"
/>
<text class="s-name">{{ item.supplierName || '供应商名称' }}</text>
<u-icon name="arrow-right" color="#999" size="12"></u-icon>
</view>
<view class="title one-t">{{ item.productName || item.name || '商品名称' }}</view>
<image :src="item.image || item.img" class="p-img" mode="aspectFill" />
<view class="price">
<text class="unit"></text>
<text class="num">{{ item.price || '0.00' }}</text>
</view>
<view class="actions">
<view class="btn-buy" @click.stop="productItemBuyNowClickHandle(item)">立即下单</view>
<view class="btn-gift" @click.stop="productItemGiftBuyClickHandle(item)">送给朋友</view>
</view>
</view>
</swiper-item>
</swiper>
</view> -->
<view class="topic-wrap">
<view class="title">
<view class="title-left">大家都在问</view>
<view class="title-right" @click="loadData('click')">
<image
:src="webUrl + '/aiChat/icon-53.png'"
:class="{
'rotate': topicIsRotate
}"
class="icon"
mode="widthFix"
/>
{{ item.title }}
换一换
</view>
</view>
<view class="list-wrap">
<view class="list-cont">
<view
v-for="(item, index) in topicList"
:key="index"
class="list-item"
@click="topicItemClickHandle(item.title)"
>
<image
v-if="item.isHot"
:src="webUrl + '/aiChat/hot.png'"
class="icon"
mode="widthFix"
/>
{{ item.title }}
</view>
</view>
</view>
</view>
</view>
</block>
<view class="chat-wrap">
<view v-if="chatMessageListLength > 0" class="chat-list-wrap">
<view v-if="chatMessageListLength > 0" class="chat-wrap">
<view class="chat-list-wrap">
<view
v-for="(item, index) in chatMessageList"
:key="index"
@@ -102,6 +148,16 @@
{{ item.prompt }}
</view>
</view>
<view class="item-right-tools">
<view class="btn" @click="copyContentHandle(item.prompt)">
<image
:src="webUrl + '/aiChat/icon-02.png'"
class="icon"
mode="widthFix"
/>
复制
</view>
</view>
</view>
<view v-if="item.type === -2" class="chat-message-body">
<view class="avatar">
@@ -166,18 +222,91 @@
/>
重新生成
</view>
<view
:class="{
'playing': audioPlayId === item.conversationId && isPlayAudio
}"
class="item"
@click="ttsHandle(item)"
>
<image
:src="webUrl + '/aiChat/' + ((audioPlayId === item.conversationId && isPlayAudio) ? 'audio-on' : 'audio-off') +'.png'"
class="icon"
mode="widthFix"
/>
语音播放
</view>
<view class="item item2" @click="copyMoreInfoHandle(item)">拷贝</view>
</view>
</view>
</view>
<!-- 换一换 -->
<view
v-if="item.showMoreInfo.show"
v-if="!item.showCursor &&
!item.isError &&
(item.orderList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.goodsList.length > 0 ||
item.shopList.length > 0 ||
item.prizeList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0
)
"
class="change-list-wrap"
>
<view v-if="item.showMoreInfo.showType === '特产商品'" class="list-wrap">
<!-- 礼品 -->
<view
v-if="item.giftGoodsList.length > 0"
class="recommend-goods-wrap"
>
<swiper
class="goods-swiper"
:indicator-dots="false"
:autoplay="false"
:current="item.currentSwiperIndex"
previous-margin="120rpx"
next-margin="120rpx"
circular
@change="e => onSwiperChange(index, e)"
>
<swiper-item
v-for="(goodItem, goodIndex) in item.giftGoodsList"
:key="goodIndex"
>
<view
:class="{ 'active': item.currentSwiperIndex === goodIndex }"
class="goods-item"
@click="productItemClickHandle(goodItem)"
>
<view class="supplier" @click.stop="productItemSupplierClickHandle(goodItem)">
<image
:src="webUrl + '/aiChat/icon-57.png'"
class="icon"
mode="widthFix"
lazy-load
/>
<text class="s-name">{{ goodItem.merName ||goodItem.storeName }}</text>
<u-icon name="arrow-right" color="#999" size="12"></u-icon>
</view>
<view class="title one-t">{{ goodItem.name }}</view>
<image :src="goodItem.img" class="p-img" mode="aspectFill" lazy-load />
<view class="price">
<text class="unit">¥</text>
<text class="num">{{ goodItem.price }}</text>
</view>
<view class="actions">
<view class="btn-buy" @click.stop="productItemBuyNowClickHandle(goodItem)">立即下单</view>
<view class="btn-gift" @click.stop="productItemGiftBuyClickHandle(goodItem)">送给朋友</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 特产商品 -->
<view v-if="item.goodsList.length > 0" class="list-wrap">
<view
v-for="(productItem, productIndex) in item.showMoreInfo.resultItems"
v-for="(productItem, productIndex) in item.goodsList"
:key="productIndex"
class="product-item"
@click="productItemClickHandle(productItem)"
@@ -185,6 +314,7 @@
<image
:src="productItem.img"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name one-t">
@@ -199,19 +329,21 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '店铺'" class="list-wrap">
<!-- 店铺 -->
<view v-if="item.shopList.length > 0" class="list-wrap">
<view
v-for="(hotel, hotelIndex) in item.showMoreInfo.resultItems"
v-for="(hotel, hotelIndex) in item.shopList"
:key="hotelIndex"
class="product-item"
@click="hotelItemClickHandle(hotel)"
>
<view class="hotel-type">
手工体验
{{ hotel.hotelTypeName }}
</view>
<image
:src="hotel.img"
:src="hotel.img + ((hotel.img && hotel.img.indexOf('.mp4') > -1) ? '?vframe/jpg/offset/1' : '')"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name one-t">
@@ -223,9 +355,10 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '非遗文化'" class="list-wrap">
<!-- 非遗 -->
<view v-if="item.ichList.length > 0" class="list-wrap">
<view
v-for="(ich, ichIndex) in item.showMoreInfo.resultItems"
v-for="(ich, ichIndex) in item.ichList"
:key="ichIndex"
class="product-item"
@click="ichItemClickHandle(ich)"
@@ -233,6 +366,7 @@
<image
:src="ich.img"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name one-t">
@@ -244,9 +378,10 @@
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '千县名品'" class="list-wrap">
<!-- 千县 -->
<view v-if="item.qianxianList.length > 0" class="list-wrap">
<view
v-for="(qianxian, qianxianIndex) in item.showMoreInfo.resultItems"
v-for="(qianxian, qianxianIndex) in item.qianxianList"
:key="qianxianIndex"
class="product-item"
@click="qianxianItemClickHandle(qianxian)"
@@ -254,6 +389,7 @@
<image
:src="qianxian.img"
class="img"
lazy-load
/>
<view class="product-info">
<view class="name name2 one-t">
@@ -262,15 +398,59 @@
</view>
</view>
</view>
<view class="change-btn">
<image
:src="webUrl + '/aiChat/icon-change.png'"
class="icon"
mode="widthFix"
@click="analyzeKeywordsChangeHandle(item.conversationId, item.showMoreInfo)"
/>
<!-- 抽奖 -->
<view v-if="item.prizeList.length > 0" class="list-wrap prize-list-wrap">
<view
v-for="(prize, prizeIndex) in item.prizeList"
:key="prizeIndex"
class="prize-item"
@click="prizeItemClickHandle(prize)"
>
<image
:src="prize.img"
class="img"
mode="widthFix"
lazy-load
/>
</view>
</view>
<!-- 订单 -->
<view v-if="item.orderList.length > 0" class="order-list-wrap">
<view
v-for="(order, orderIndex) in item.orderList"
:key="orderIndex"
class="order-item-wrap"
>
<order-item
:item="order"
@viewOrderTrack="viewOrderTrackHandle"
/>
</view>
</view>
<view
v-if="item.shopList.length > 0 ||
item.goodsList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0 ||
item.shopList.length > 0
"
class="change-btn"
>
<view class="change-btn-content" @click="analyzeKeywordsChangeHandle(item.conversationId, item)">
<image
:src="webUrl + '/aiChat/icon-53.png'"
:class="{
'rotate': item.changeIsRotate
}"
class="icon"
mode="widthFix"
/>
换一换
</view>
</view>
</view>
<!-- 建议提问 -->
<view
v-if="index === (chatMessageListLength - 1) && item.listQuestion.length > 0"
class="suggestion-wrap"
@@ -298,7 +478,11 @@
</view>
</view>
</view>
<view id="bottom_postion" class="bottom-postion" />
<view
v-if="chatNumber"
id="bottom_postion"
class="bottom-postion"
/>
</view>
</view>
<view
@@ -307,25 +491,42 @@
}"
class="page-to-bottom"
>
<view
v-if="chatNumber"
class="new-chat"
@click="createNewHandle"
>
<view class="down-icon-wrap">
<image
:src="webUrl + '/aiChat/icon-05.png'"
class="btn-icon"
v-if="chatMessageListLength > 0 && !isScrollToBottom && !loading"
:src="webUrl + '/aiChat/down.png'"
class="icon down-icon"
mode="widthFix"
@click="scrollToBottomHandle"
/>
开启新对话
</view>
<image
v-if="chatMessageListLength > 0 && !isScrollToBottom && !loading"
:src="webUrl + '/aiChat/down.png'"
class="icon"
mode="widthFix"
@click="scrollToBottomHandle"
/>
<view class="container">
<view
v-if="chatNumber"
class="new-chat"
@click="createNewHandle"
>
<image
:src="webUrl + '/aiChat/icon-54.png'"
class="btn-icon"
mode="widthFix"
/>
开启新对话
</view>
<view
v-for="(item, index) in bottomTools"
:key="index"
class="bottom-tool-item"
@click="bottomToolClickHandle(item)"
>
<image
:src="webUrl + '/aiChat/' + item.icon + '.png'"
class="tool-icon"
mode="widthFix"
/>
{{ item.text }}
</view>
</view>
</view>
<bottom-send
ref="bottomSend"
@@ -339,33 +540,58 @@
<AiHistoryList
ref="historyView"
:status-bar-height="statusBarHeight"
@enter-history="audioStopHandle"
/>
<ProductWindow
ref="attrWindow"
:attr="attr"
:cart-num="cart_num"
:show-ok="!showGiftBtn"
:is-gift="showGiftBtn"
:padding-bottom="'60px'"
class-name="ai-product-window"
ok-text="立即购买"
@changeFun="changeFun"
@ok="buyNowOrGiftBuyReq(1)"
@gift="buyNowOrGiftBuyReq(2)"
/>
</view>
</template>
<script>
import { chatMixins } from '../mixins/chatMixins.js'
import { chatMixinsV2 } from '../mixins/chatMixinsV2.js'
import goCartMixin from '@/mixins/goCartMixins'
import {
getAiSystemInfoSetting,
getAiSystemInfoTopic
} from '@/api/public'
import AiHistoryList from '../components/historyList'
import BottomSend from '../components/bottomSend.vue'
import OrderItem from '../components/orderItem.vue'
import ProductWindow from '@/components/ProductWindow'
export default {
name: 'AiChatIndexPage',
components: {
AiHistoryList,
BottomSend
BottomSend,
OrderItem,
ProductWindow
},
mixins: [chatMixins],
mixins: [chatMixinsV2, goCartMixin],
data() {
return {
cityName: '',
animationWidth: '150%',
animationStr: '',
settingInfo: {},
topicList: []
topicList: [],
onlyOneToAddCart: false,
topicIsRotate: false
}
},
onLoad(options) {
this.cityName = options.cityName || ''
},
onShow() {
const addNewChat = uni.getStorageSync('addNewChat') === '1'
// 从历史界面点击新增对话
@@ -375,9 +601,6 @@ export default {
this.createNewHandle()
}
},
onUnload() {
this.closeWsFn()
},
methods: {
createdCallbak() {
this.$nextTick(() => {
@@ -390,16 +613,42 @@ export default {
this.animationStr = `TranslateXSwiper-${data.systemTopicDivMaxWidth * 10} ${data.systemTopicDivScrollDuration}s infinite linear alternate`
}
})
getAiSystemInfoTopic().then(res => {
const { success, data } = res
if (success) {
this.topicList = data || []
this.loadData()
})
},
loadData(type = '') {
if (this.topicIsRotate) return
if (type === 'click') {
this.topicIsRotate = true
}
getAiSystemInfoTopic().then(res => {
const { success, data } = res
if (success) {
// 返回数据先过滤this.topicList,随机保留三条数据
let list = data || []
if (this.topicList && this.topicList.length > 0) {
const currentTitles = this.topicList.map(item => item.title)
const filtered = list.filter(item => !currentTitles.includes(item.title))
// 如果过滤后的数据不够3条,就不进行过滤
if (filtered.length >= 3) {
list = filtered
}
}
})
list.sort(() => Math.random() - 0.5)
this.topicList = list.slice(0, 3)
setTimeout(() => {
this.topicIsRotate = false
}, 1000)
}
})
},
// 创建新会话
createNewHandle() {
this.closeAttrWindow()
if (this.audioContext) {
this.audioContext.stop()
}
wx.getBackgroundAudioManager().stop()
if (!this.chatNumber) return
this.closeWsFn()
this.chatNumber = ''
@@ -407,6 +656,7 @@ export default {
this.loading = false
this.getConfigLoading = false
this.chatMessageList = []
this.loadData()
},
clickBackHandle() {
if (!this.chatNumber) {
@@ -471,43 +721,39 @@ export default {
opacity: 0.0;
}
}
.home-wrap {
display: flex;
flex-direction: column;
justify-content: space-between;
height: calc(100vh - 240rpx);
padding: 0 0 240rpx 0;
box-sizing: border-box;
}
.focus-guide-wrap {
display: flex;
align-items: center;
justify-content: space-between;
padding: 60rpx 40rpx;
flex-direction: column;
.guide-left {
.avatar {
display: block;
width: 267rpx;
height: 530rpx;
width: 230rpx;
height: 404rpx;
}
}
.guide-right {
position: relative;
padding: 30rpx;
margin: 0 0 0 40rpx;
border-radius: 20rpx;
width: 100%;
padding: 30rpx 0;
font-size: 28rpx;
background-color: #fff;
box-shadow: 0rpx 12rpx 30rpx rgba(0,0,0,0.1);
.trangle {
position: absolute;
bottom: 120rpx;
left: -15rpx;
width: 30rpx;
height: 30rpx;
background-color: #fff;
box-shadow: -30rpx 12rpx 30rpx rgba(0,0,0,0.1);
transform: rotate(45deg);
}
text-align: center;
.title {
width: 110%;
font-weight: bold;
font-size: 34rpx;
}
.content {
padding: 30rpx 0 0 0;
color: #999;
padding: 30rpx 24rpx 0 24rpx;
color: #3D4CF0;
}
}
}
@@ -516,46 +762,48 @@ export default {
.title {
display: flex;
align-items: center;
justify-content: center;
.txt {
width: 186rpx;
height: 46rpx;
margin: 0 20rpx;
border-radius: 24rpx;
text-align: center;
line-height: 46rpx;
color: #fff;
font-size: 28rpx;
background-color: #3D4CF1;
justify-content: space-between;
padding: 0 24rpx;
color: #3D4CF0;
font-size: 28rpx;
.title-left {
font-size: 32rpx;
font-weight: bold;
}
.title-right {
display: flex;
align-items: center;
}
.icon {
display: flex;
width: 40rpx;
width: 32rpx;
height: 32rpx;
margin: 0 8rpx 0 0;
&.rotate {
animation: spin-icon 1s linear;
}
}
}
.list-wrap {
width: 100%;
margin: 60rpx 0 0 0;
padding: 40rpx 0 0 0;
overflow-x: hidden;
.list-cont {
display: flex;
flex-wrap: wrap;
width: 750 * 1.5rpx;
padding: 0 0 0 24rpx;
padding: 0 24rpx;
box-sizing: border-box;
// animation: translateXSwiper 10s infinite linear alternate;
display: flex;
flex-direction: column;
align-items: flex-start;
.list-item {
display: flex;
align-items: center;
height: 48rpx;
padding: 0 18rpx;
margin: 0 30rpx 30rpx 0;
border-radius: 24rpx;
padding: 6rpx 12rpx;
margin: 0 0 24rpx 0;
border-radius: 12rpx;
border: 1rpx solid #ddd;
font-size: 28rpx;
color: #666;
background: rgba(255,255,255,0.39);
box-shadow: 0rpx 6rpx 12rpx rgba(0,0,0,0.1);
background-color: #fff;
.icon {
display: block;
width: 24rpx;
+22
View File
@@ -37,3 +37,25 @@ export function historyChatMessage(params) {
export function deleteChatListByType(type) {
return request.delete('/v1/chat/type/' + type, {}, { login: true })
}
export function getWeatherData(params) {
return request.get('/v1/chat/weatherData', params, { login: true })
}
export function getHolidayData(params) {
return request.get('/v1/chat/holidayData', params, { login: true })
}
export function getCompletionsV3(params) {
return request.get('/v1/chat/intent/recognize', params, { login: true })
}
// 通过关键字-搜索列表-换一换
export function getAnalyzeKeywordsChangeV3(data) {
return request.post('/v1/chat/skill/refresh', data, { login: true })
}
// 以图搜索商品
export function getAnalyzeByImageV3(data) {
return request.post('/v1/chat/image/recognition', data, { login: true })
}
+61 -1
View File
@@ -63,4 +63,64 @@ export function getGiftCardResend(data) {
*/
export function getGiftCardSendBackground(data) {
return request.get("giftCard/giftCardBackgroundImage", data);
}
}
/**
* 创建团购礼品
* @param {*} data
*/
export function createGroupBuyGift(data) {
return request.post("/travel/giftCardOrder/confirm", data);
}
/**
* 旅居团购礼品卡订单创建
* @param {*} data
*/
export function createSojoumGroupBuyGift(key, data) {
return request.post("/travel/giftCardOrder/create/" + key, data);
}
/**
* 旅居团购礼品卡订单详情
* @param {*} params
* @returns
*/
export function fetchSojoumGroupBuyGiftDetail(params) {
return request.get("/travel/giftCard/info",params, {
login: true
})
}
/**
* 领取旅居团购礼品卡
* @param {*} params
* @returns
*/
export function receiveSojoumGroupBuyGift(params) {
return request.post("/travel/giftCard/receive", params, {
login: true
})
}
/**
* 旅居礼品卡送礼入口轮播图
* @param {*} params
* @returns
*/
export function fetchSojoumGroupBuyGiftCarousel(params) {
return request.get("/travel/giftCardTravelBanner",params, {
login: true
})
}
/**
* 计算旅居团购礼品订单价格
* @param {*} params
* @returns
*/
export function computedSojoumGroupBuyGiftOrderPrice(params) {
return request.post("/travel/giftCardOrder/computed/" + params.key, params, {
login: true
})
}
+9 -1
View File
@@ -78,4 +78,12 @@ export function getRecipe(params) {
*/
export function getBanner() {
return request.get("/wellness/exhibition/banner")
}
}
/**
* 康养生活-康养旅居顶部配置
* @returns
*/
export function getWellnessTopImage() {
return request.get("/wellness/topImage")
}
+17
View File
@@ -191,6 +191,10 @@ export function getHotelTypeList() {
});
}
export function getHotelBanner(params) {
return request.get("/hotelBanner", params);
}
/**
* 店铺详情海报
* @param id 店铺ID int
@@ -273,4 +277,17 @@ export function getMerchantApply() {
return request.get("/merchantApply/userHotels", {}, {login: true})
}
/**
* 获取用户评论列表
*/
export function getCommentList(params) {
return request.get("/user/reply/list", params, {login: true})
}
/**
* 添加用户评论
*/
export function addComment(data) {
return request.post("/user/reply/add", data, {login: true})
}
+54
View File
@@ -55,7 +55,61 @@ export function saveWholesaler(wholesalerInfo) {
return request.post("/merchantApply/wholesaler/submit", param, {login: true})
}
export function saveExperienceStore(params) {
return request.post("/merchantApply/experienceStore/submit", params, {login: true})
}
export function saveExperienceStoreDraft(params) {
return request.post("/merchantApply/experienceStore/saveDraft", params, {login: true})
}
export function getExperienceStoreCategoryList() {
return request.get("/experienceStore/categoryList", {}, {login: true})
}
export function getExperienceStoreInfo(params) {
return request.get("merchantApply/info/experienceStore", params, {login: true})
}
// 撤销入驻申请
export function removeApply() {
return request.post("/merchantApply/revoke")
}
// 特色店铺入驻信息
export function getHotelInfo(params) {
return request.get("merchantApply/info/hotel", params, {login: true})
}
// 获取千县名品店铺类型列表
export function getCountyFamousHotelType() {
return request.get("/countyFamous/hotelType", {}, {login: true})
}
/**
* 旅居管家入驻申请详情
*/
export function getTravelInfo(params) {
return request.get("merchantApply/info/travel", params, {login: true})
}
/**
* 提交旅居管家入驻申请
*/
export function saveTravel(params) {
return request.post("/merchantApply/travel/submit", params, {login: true})
}
/**
* 旅居管家申请草稿保存
*/
export function saveTravelDraft(params) {
return request.post("/merchantApply/travel/saveDraft", params, {login: true})
}
/**
* 旅居管家撤销申请
*/
export function revokeTravel(params) {
return request.post("/merchantApply/travel/revoke", params, {login: true})
}
+36 -1
View File
@@ -3,6 +3,14 @@
* */
import request from "@/utils/request";
/**
* 获取默认地址
* @returns {*}
*/
export function getAddressDefaultSelected() {
return request.get("/address/defaultSelected")
}
/**
* 通过购物车 id 获取订单信息
* @param cartId
@@ -178,4 +186,31 @@ export function aginConfirm(params) {
*/
export function editAddress(params) {
return request.post('/order/editAddress', params)
}
}
/**
* 申请修改订单收货地址
* @param {*} params
* @returns
*/
export function applyEditAddress(params) {
return request.post('/order/applyEditAddress', params)
}
/**
* 订单预计到达时间
* @param {*} params
* @returns
*/
export function orderExpectedArrivalTime(params) {
return request.post('/order/express/estimatedTime', params)
}
/**
* 旅居团购订单详情
* @param {*} params
* @returns
*/
export function groupOrderDetail(params) {
return request.post('/travel/travelGroupOrder/detail/' + params.key, params)
}
+29 -1
View File
@@ -159,4 +159,32 @@ export function hadView(data) {
export function getShareImg(id) {
return request.get('/landmarkGoods/poster/' + id, {}, {login: false})
}
}
export function getLatestExperienceCoupon() {
return request.get('/experienceCoupon/latest', {}, {login: true})
}
export function getExperienceCouponList() {
return request.get('/experienceCoupon/list', {}, {login: true})
}
export function getExperienceCouponItems(params) {
return request.get('/experienceCoupon/items', params, {login: true})
}
export function getExperienceCouponCategoryList(params) {
return request.get('/experienceCoupon/categoryList', params, {login: true})
}
export function createExperienceCouponOrder(data) {
return request.post('/experienceCoupon/order/create', data, {login: true})
}
export function getExperienceCouponOrderDetail(orderId) {
return request.get(`/experienceCoupon/order/detail/${orderId}`, {}, {login: true})
}
export function checkExperienceCoupon(params) {
return request.get('/experienceCoupon/check', params, {login: true})
}
+5
View File
@@ -222,3 +222,8 @@ export function getAiSystemInfoSetting() {
export function getAiSystemInfoTopic() {
return request.get('/ai/systemTopic', {}, { login: false })
}
// 获取通用分享配置
export function getWeixinShareConfig() {
return request.get('/weixinShareConfig', {}, { login: false })
}
+187 -7
View File
@@ -105,12 +105,12 @@ export function getSaleList(params) {
}
// 旅居列表
export function getJiaLouList(params) {
return request.get('/countyFamous/sojourn', params, {login: true})
}
// export function getJiaLouList(params) {
// return request.get('/countyFamous/sojourn', params, {login: true})
// }
// 旅居详情
export function getJiaLouDetail(id) {
export function getJiaLouDetail1(id) {
return request.get('/countyFamous/sojourn/' + id, {}, {login: true})
}
@@ -141,15 +141,195 @@ export function postJiaLouZan(data) {
// 旅居资讯查看
export function postJiaLouNewsView(data) {
return request.post('/countyFamous/news/view', data, {login: true})
return request.post('/travel/news/view', data, {login: true})
}
// 旅居资讯点赞
export function postJiaLouNewsZan(data) {
return request.post('/countyFamous/news/zan', data, {login: true})
return request.post('/travel/news/zan', data, {login: true})
}
// 分享旅居
export function postJiaLouShare(id) {
return request.get('/countyFamous/sojourn/poster/' + id, {login: true})
}
}
// 分享旅居1
export function postTravelShare(id) {
return request.get('/travel/poster/' + id, {login: true})
}
/**
* 获取旅居列表
* countyId int 县城ID
shopType string 所属板块(countyFamous-千县;wellness-康养)
wellnessPlaceCateId int 康养圣地分类ID
name string 名称
page int 页码
limit int 每页数量
*/
export function getJiaLouList(params) {
return request.get('/travel/merchantList', params, {login: true})
}
/**
* 获取旅居详情
* @param {*} id
*/
export function getJiaLouDetail(id) {
return request.get('/travel/merchant/' + id, {}, {login: true})
}
/**
* 旅居团购产品列表
merId int 商户ID
page int 页码
limit int 每页数量
*/
export function getJiaLouProductList(params) {
return request.get('/travel/products', params, {login: true})
}
/**
* 旅居团购产品详情
* @param {*} id 产品ID
*/
export function getJiaLouProductDetail(id) {
return request.get('/travel/product/' + id, {}, {login: true})
}
/**
* 旅居团购产品评论列表
* travelGroupProductId int 旅居团购产品ID
* type int 0-全部 1-好评 2-中评 3-差评
* page int 页码
* limit int 每页数量
*/
export function getJiaLouProductReply(params) {
return request.get('/travel/product/reply', params, {login: true})
}
/**
* 获取旅居团购产品评论统计
* travelGroupProductId int 旅居团购产品ID
*/
export function getJiaLouProductReplyCount(params) {
return request.get('/travel/product/reply/count', params, {login: true})
}
export function getTravelGroupOrderAgreement() {
return request.get('/travel/travelGroupOrderAgreement', {}, {login: true})
}
/**
* 管家列表
* merId int 商户ID
page int 页码
limit int 每页数量
*/
export function getJiaLouStewardList(params) {
return request.get('/travel/steward', params, {login: true})
}
/**
* 指定月份可预约日期
* @param {*} params
* travelGroupProductId int 旅居团购产品ID
month string 月份(yyyy-MM
*/
export function getJiaLouCalendar(params) {
return request.get('/travel/product/calendar', params, {login: true})
}
/**
* 获取预定必读
*/
export function getJiaLouBookingRules() {
return request.get('/travel/orderRule', {}, {login: true})
}
/**
* 创建订单
* @param {*} params
* travelGroupProductId int 旅居团购产品ID
orderStartDate string 订单开始日期(yyyy-MM-dd
orderEndDate string 订单结束日期(yyyy-MM-dd
*/
export function createJiaLouOrder(params) {
return request.post('/travel/travelGroupOrder/confirm', params, {login: true})
}
/**
* 旅居团购订单创建
* @param {*} params
*/
export function createJiaLouOrderPay(params) {
return request.post('/travel/travelGroupOrder/create/' + params.key, params, {login: true})
}
/**
* 旅居资讯列表
* merId int 旅居管家商户ID
page int 页码
limit int 每页条数
*/
export function getJiaLouNewsList(params) {
return request.get('/travel/news', params, {login: true})
}
/**
* 分享管家
*/
export function getShareHousekeeper(id) {
return request.get('/travel/steward/poster/' + id, {login: true})
}
/**
* 管家详情
*/
export function getJiaLouStewardDetail(id) {
return request.get('/travel/steward/' + id, {}, {login: true})
}
/**
* 获取县城资讯详情
*/
export function getCityNewsDetail(id) {
return request.get('/countyFamous/news/' + id, {}, {login: true})
}
/**
* 获取县城资讯类别
*/
export function getCityNewsCategory() {
return request.get('/countyFamous/newsCategory', {}, {login: true})
}
/**
* 获取县城店铺列表
*/
export function getCityShopList(params) {
return request.get('/countyFamous/hotel', params, {login: true})
}
export function getCountyFamousHotelType() {
return request.get('/countyFamous/hotelType', {}, {login: true})
}
/**
* б
* travelMerchantId int þ̻ID
* experienceStoreCategoryId int ID
*/
export function getExperienceStoreList(params) {
return request.get('/experienceStore/list', params, { login: true })
}
export function getExperienceStoreProject(experienceMerchantId) {
return request.get('/experienceStoreProject/' + experienceMerchantId, {}, { login: true })
}
export function getExperienceStoreDetail(id) {
return request.get('/experienceStore/detail/' + id, {}, { login: true })
}
+29
View File
@@ -0,0 +1,29 @@
import request from "@/utils/request";
/**
* 获取未读消息数量
*/
export function getUnreadMessageCount() {
return request.get("/user/reply/messages/unreadCount", {}, {login: true})
}
/**
* 标记消息为已读
*/
export function markMessagesAsRead() {
return request.post("/user/reply/messages/readAll", {}, {login: true})
}
/**
* 获取消息列表
*/
export function getMessageList(params) {
return request.get("/user/reply/messages", params, {login: true})
}
/**
* 回复消息
*/
export function replyMessage(data) {
return request.post("/user/reply/add", data, {login: true})
}
+74
View File
@@ -0,0 +1,74 @@
import request from "@/utils/request";
/**
* 旅居团购订单详情
* @param {*} params
* @returns
*/
export function fetchSojoumOrderDetail(params) {
return request.get("/travel/travelGroupOrder/detail/" + params.key, params, {
login: true
})
}
/**
* 旅居团购订单退款
* @param {*} params
* @returns
*/
export function sojoumOrderRefund(params) {
return request.post("/travel/travelGroupOrder/refund", params, {
login: true
})
}
/**
* 旅居团购订单修改入住时间
* @param {*} params
* @returns
*/
export function sojoumOrderCheckInTime(params) {
return request.post("/travel/travelGroupOrder/changeDate", params, {
login: true
})
}
/**
* 获取产品指定月份的预约日历
* @param {*} params
* travelGroupProductId int 旅居团购产品ID
month string 月份(yyyy-MM
* @returns
*/
export function sojoumOrderCalendar(params) {
return request.get("/travel/product/calendar", params, {
login: true
})
}
/**
* 旅居团购订单退款信息
* @param {*} params
* @returns
*/
export function sojoumOrderRefundInfo(params) {
return request.post("/travel/travelGroupOrder/refundInfo", params, {
login: true
})
}
/**
* 旅居团购订单检查日期是否可预约
* @param {*} params
* @returns
*/
export function sojoumOrderCheckDate(params) {
return request.post("/travel/travelGroupOrder/checkDate", params, {
login: true
})
}
export function sojoumOrderComment(params) {
return request.post("/travel/travelGroupOrder/comment", params, {
login: true
})
}
+5 -1
View File
@@ -578,6 +578,10 @@ export function getHistory(params) {
return request.get('/history/list', params)
}
export function deleteHistory(id) {
return request.delete(`/history/${id}`)
}
/**
* 购物车猜你喜欢
* @param {*} params
@@ -598,4 +602,4 @@ export function queryUserGuide(params) {
export function setUserGuide(params) {
return request.post('/user/guide/status', params)
}
}
+362 -24
View File
@@ -12,10 +12,14 @@ page {
min-height: 100vh;
background: linear-gradient(155deg, rgba(61, 76, 241, 0.15) 0%, rgba(255,255,255,1) 25%, rgba(255,255,255,1) 75%, rgba(61, 76, 241, 0.15) 100%);
// background-color: #fff;
// background-size: 100% 100vh;
// background-size: 100% calc(100vh - 160rpx);
// background-image: url(https://wxapp.xdd618.com/api/file/pic/aiChat/bg.png);
// background-repeat: no-repeat;
// background-attachment: fixed;
&.new-chat {
background-size: 100% calc(100vh - 160rpx);
background-repeat: no-repeat;
}
.fix-tabbar-header {
position: fixed;
top: 0;
@@ -90,28 +94,36 @@ page {
}
.page-to-bottom {
position: fixed;
bottom: 180rpx;
bottom: 160rpx;
left: 0;
right: 0;
z-index: 8;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 24rpx;
transition: all 0.2s;
&.focus {
transform: translateY(-40rpx);
}
.container {
display: flex;
flex-wrap: nowrap;
align-items: center;
width: 100%;
padding: 24rpx;
box-sizing: border-box;
overflow-x: auto;
}
.new-chat {
display: flex;
align-items: center;
height: 68rpx;
flex-shrink: 0;
height: 64rpx;
padding: 0 24rpx;
border-radius: 40rpx;
line-height: 68rpx;
margin: 0 20rpx 0 0;
border-radius: 16rpx;
line-height: 64rpx;
font-size: 30rpx;
background-color: #fff;
box-shadow: 0rpx 6rpx 12rpx rgba(0,0,0,0.16);
color: #fff;
background-color: #3D4CF1;
box-shadow: 0rpx 0 12rpx rgba(74,74,74,0.2);
.btn-icon {
display: block;
width: 40rpx;
@@ -119,13 +131,45 @@ page {
margin: 0 8rpx 0 0;
}
}
.down-icon-wrap {
display: flex;
justify-content: flex-end;
padding: 0 24rpx 0 0;
}
.icon {
display: block;
width: 68rpx;
height: 68rpx;
margin: 0 0 0 24rpx;
border-radius: 50%;
box-shadow: 0rpx 6rpx 12rpx rgba(0,0,0,0.16);
flex-shrink: 0;
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
background-color: #fff;
box-shadow: 0rpx 0 12rpx rgba(74,74,74,0.2);
}
.bottom-tool-item {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
height: 64rpx;
padding: 0 24rpx;
margin: 0 20rpx 0 0;
border-radius: 16rpx;
font-size: 30rpx;
color: #333;
background-color: #fff;
box-shadow: 0rpx 6rpx 20rpx rgba(74,74,74,0.1);
&:last-child {
margin-right: 0;
}
.tool-icon {
display: block;
width: 32rpx;
height: 32rpx;
padding: 0;
margin: 0 12rpx 0 0;
background-color: none;
box-shadow: none;
}
}
}
.fix-page-bottom {
@@ -159,7 +203,7 @@ page {
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
min-height: 100rpx;
width: calc(100% - 122rpx);
padding: 6rpx 6rpx 6rpx 24rpx;
border-radius: 30rpx;
@@ -182,11 +226,13 @@ page {
z-index: 5;
width: 100%;
height: 88rpx;
max-height: 220rpx;
border: 0;
box-sizing: border-box;
line-height: 88rpx;
line-height: 48rpx;
color: #333;
font-size: 30rpx;
overflow-y: auto;
}
.placeholder-txt {
position: absolute;
@@ -469,7 +515,7 @@ page {
.txt {
max-width: 100%;
padding: 20rpx 32rpx;
border-radius: 40rpx 40rpx 0 40rpx;
border-radius: 40rpx 0 40rpx 40rpx;
box-sizing: border-box;
color: #fff;
font-size: 30rpx;
@@ -487,6 +533,20 @@ page {
}
}
}
.item-right-tools {
display: flex;
justify-content: flex-end;
padding: 20rpx 0 0 0;
.btn {
display: flex;
font-size: 24rpx;
.icon {
display: block;
width: 36rpx;
margin: 0 8rpx 0 0;
}
}
}
.bottom-btns {
position: relative;
display: flex;
@@ -518,6 +578,9 @@ page {
margin: 0;
color: transparent;
}
.playing {
color: #3D4CF1;
}
}
}
}
@@ -1015,15 +1078,290 @@ page {
}
}
}
.prize-list-wrap {
display: block;
.prize-item {
.img {
display: block;
width: 100%;
border-radius: 20rpx;
}
}
}
.change-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0 0 20rpx 0;
.icon {
display: block;
width: 124rpx;
height: 38rpx;
.change-btn-content {
display: flex;
align-items: center;
justify-content: center;
color: #3D4CF0;
font-size: 28rpx;
.icon {
display: block;
width: 32rpx;
height: 32rpx;
margin: 0 8rpx 0 0;
&.rotate {
animation: spin-icon 1s linear;
}
}
}
}
}
}
.weather-holiday-wrap {
display: flex;
align-items: stretch;
justify-content: space-between;
gap: 12px;
padding: 0 24rpx 48rpx 24rpx;
.data-wrap {
flex: 1;
padding: 20rpx;
border-radius: 20rpx;
box-sizing: border-box;
background-color: #fff;
box-shadow: 0rpx 0rpx 20rpx rgba(0,0,0,0.16);
}
.weather-data {
.title {
display: flex;
align-items: center;
color: #3D4CF1;
.icon {
display: block;
width: 100rpx;
height: 100rpx;
margin: 0 12rpx 0 0;
}
.tem-wrap {
display: flex;
align-items: baseline;
}
.tem {
font-size: 70rpx;
line-height: 70rpx;
font-weight: bold;
}
.unit {
font-size: 36rpx;
line-height: 36rpx;
}
}
.loca {
display: flex;
padding: 24rpx 0;
font-size: 26rpx;
line-height: 36rpx;
color: #333;
.addr {
display: flex;
align-items: center;
.icon {
display: block;
width: 24rpx;
height: 24rpx;
margin: 0 4rpx 0 0;
}
}
.tem-range {
padding: 0 0 0 24rpx;
}
}
.content + .content {
padding: 12rpx 0 0 0;
}
.content {
color: #3D4CF1;
font-size: 26rpx;
line-height: 36rpx;
font-weight: bold;
}
.txt-loading {
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
min-height: 200rpx;
}
.no-weather {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
font-size: 26rpx;
color: #999;
line-height: 38rpx;
.img {
display: block;
width: 226rpx;
height: 130rpx;
margin: 0 0 12rpx 0;
}
}
}
.holiday-data {
.title {
display: flex;
font-size: 24rpx;
color: #333;
line-height: 36rpx;
.day {
margin: 0 8rpx 0 0;
color: #3D4CF1;
font-size: 28rpx;
}
}
.desc {
font-size: 24rpx;
color: #333;
line-height: 36rpx;
}
.tip {
display: flex;
flex-wrap: wrap;
padding: 20rpx 0 0 0;
.label {
display: block;
width: 32rpx;
height: 32rpx;
margin: 0 12rpx 0 0;
border-radius: 8rpx;
text-align: center;
line-height: 32rpx;
font-size: 24rpx;
color: #fff;
background-color: #3D4CF1;
}
.label2 {
background-color: #C52733;
}
.content {
width: calc(100% - 44rpx);
margin: -6rpx 0 0 0;
font-size: 24rpx;
}
}
}
}
.recommend-goods-wrap {
width: calc(100% + 48rpx);
padding: 24rpx 0;
margin: 0 -24rpx;
.goods-swiper {
width: 100%;
height: 750rpx;
}
.goods-item {
display: block;
width: 460rpx;
margin: 24rpx auto 0 auto;
padding: 24rpx;
box-sizing: border-box;
background: #fff;
border-radius: 20rpx;
box-shadow: 0rpx 0 24rpx rgba(0,0,0,0.16);
white-space: normal;
transition: all 0.3s ease;
transform: scale(0.9);
opacity: 0.8;
&.active {
transform: scale(1);
opacity: 1;
}
.supplier {
display: flex;
align-items: center;
font-size: 24rpx;
color: #666;
margin-bottom: 16rpx;
.icon {
display: block;
width: 28rpx;
height: 28rpx;
}
.s-name {
flex: 1;
margin: 0 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
line-height: 40rpx;
}
.p-img {
width: 412rpx;
height: 412rpx;
margin-bottom: 16rpx;
border-radius: 12rpx;
background-color: #f5f5f5;
}
.price {
color: #C52733;
text-align: center;
margin-bottom: 16rpx;
.unit {
font-size: 28rpx;
font-weight: bold;
}
.num {
font-size: 36rpx;
font-weight: bold;
}
}
.actions {
display: flex;
justify-content: center;
.btn-buy {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 56rpx;
text-align: center;
background: #C52733;
color: #fff;
font-size: 24rpx;
border-radius: 28rpx;
}
.btn-gift {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 52rpx;
text-align: center;
background: #fff;
color: #C52733;
font-size: 24rpx;
border-radius: 28rpx;
border: 2rpx solid #C52733;
box-sizing: border-box;
}
}
}
}
.order-list-wrap {
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
.order-item-wrap + .order-item-wrap {
margin: 24rpx 0 0 0;
}
}
@keyframes spin-icon {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(720deg);
}
}
+4
View File
@@ -295,4 +295,8 @@ checkbox .wx-checkbox-input.wx-checkbox-input-checked {
font-style: normal;
font-size: 28rpx;
color: #C2C5CC;
}
.bg-color-hui {
background-color: #999 !important;
}
+6
View File
@@ -37,6 +37,12 @@ input{line-height: normal; box-sizing:border-box;}
.line1{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width: 100%;}
.line2{word-break:break-all;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.mask{position:fixed;top:0;left:0;right:0;bottom:0;z-index:55;background-color:rgba(0,0,0,0.5);}
.ai-product-window.product-window {
z-index: 1002;
}
.ai-product-window.mask {
z-index: 1001;
}
+2 -2
View File
@@ -3,8 +3,8 @@
@dark-color: #333333;
@white-color: #fff;
@colors: #C52733, #333333, #fff, #666, #F0F0F0, #999999, #E92727, #FFC543, #69C573;
@classnames: primary, dark, white, secondary-dark, grey, dark1, red, yellow, success;
@colors: #C52733, #333333, #fff, #666, #F0F0F0, #999999, #E92727, #FFC543, #69C573, #A5A5A5;
@classnames: primary, dark, white, secondary-dark, grey, dark1, red, yellow, success, dark2;
/*=====================================*/
each(@colors, {
+5 -1
View File
@@ -6,7 +6,7 @@
class="coupons__cover"
mode="widthFix"
/>
<view @click="selectCoupon" class="check-radio" :class="{'v12-primary': data.checked}">
<view @click="selectCoupon" class="check-radio" :class="{'v12-primary': data.checked}" v-if="showRadio">
<radio
v-if="radioModel && data.status === 0"
:value="data.id"
@@ -50,6 +50,10 @@
export default {
name: "Coupons",
props: {
showRadio: {
type: Boolean,
default: true
},
data: {
type: Object,
default: () => {
+38 -11
View File
@@ -14,7 +14,8 @@
@change="changeNav"
v-if="showTab"
/>
<view class="v12-radius-20 v12-white v12-align-center v12-justify-around v12-pa-3" v-else>
<!-- 先隐藏 -->
<!-- <view class="v12-radius-20 v12-white v12-justify-around v12-pa-3" style="align-items: flex-end;" v-if="!showTab && showRadio">
<view class="">
<view class="v12-text-center v12-font-28">
券后价
@@ -29,7 +30,10 @@
</view>
</view>
<view class="v12-text-center v12-font-44">
=
<view class="v12-text-center v12-font-28">
&nbsp;
</view>
<view class="v12-mt-3 v12-text-center">=</view>
</view>
<view class="">
<view class="v12-text-center v12-font-28">
@@ -45,7 +49,10 @@
</view>
</view>
<view class="v12-text-center v12-font-44">
-
<view class="v12-text-center v12-font-28">
&nbsp;
</view>
<view class="v12-mt-3 v12-text-center">-</view>
</view>
<view class="">
<view class="v12-text-center">
@@ -55,12 +62,13 @@
{{ selectItem.couponPrice || 0 }}
</view>
</view>
</view>
</view> -->
<view class="list-box">
<view class="larg-one v12-mb-3">
<Coupons
:data="largOne"
:showRadio="showRadio"
:radio-model="current === 0"
@change="selectCoupon"
/>
@@ -75,6 +83,7 @@
>
<Coupons
:data="item"
:showRadio="showRadio"
:radio-model="current === 0"
@change="selectCoupon"
v-if="index !== maxIndex"
@@ -93,7 +102,7 @@
</view>
<button disabled></button>
<view
v-if="current === 0"
v-if="current === 0 && showRadio"
:class="{ 'btn-disabled': current === 1 }"
class="btn-done bold flex jc-center ai-center v12-primary"
@click="setCoupon"
@@ -111,6 +120,10 @@ export default {
name: 'CouponsPopup',
components: { SubSection, Coupons },
props: {
showRadio: {
type: Boolean,
default: true
},
twoList: {
type: Object,
default: {
@@ -129,6 +142,10 @@ export default {
id: {
type: Number,
default: 0
},
couponId1: {
type: Number,
default: 0
}
},
data() {
@@ -142,10 +159,11 @@ export default {
}
},
watch: {
id: {
couponId1: {
immediate: true,
handler(value) {
console.log('couponId', value);
console.log(value);
if(value) {
this.selectCoupon(value)
}
@@ -156,7 +174,7 @@ export default {
list() {
const temp = this.current === 0 ? this.twoList['usable'] : this.twoList['unusable']
if (this.current === 0) {
const localCouponId = uni.getStorageSync('couponId') || 0
const localCouponId = this.couponId1 || 0
temp.map(item => {
item.checked = localCouponId === item.id
})
@@ -180,8 +198,8 @@ export default {
},
largOne() {
if(this.list[this.maxIndex]) {
this.selectCoupon(this.list[this.maxIndex].id)
uni.setStorageSync('couponId', this.list[this.maxIndex].id)
// this.selectCoupon(this.list[this.maxIndex].id)
// uni.setStorageSync('couponId', this.list[this.maxIndex].id)
}
// this.$emit('max', this.list[this.maxIndex] || {})
return this.list[this.maxIndex] || {}
@@ -193,7 +211,17 @@ export default {
},
methods: {
close() {
this.couponId = this.couponId1
this.selectItem = this.list.find(e => e.id === this.couponId1) || {}
// this.$emit('select', this.selectItem)
this.list.map(item => {
item.checked = this.couponId1 === item.id
})
this.$emit('close')
// uni.setStorageSync('couponId', this.couponId)
// this.$emit('change', this.selectItem || {})
// this.$emit('ok', this.couponId)
},
force2Decimal(value) {
return this.$force2Decimal(value);
@@ -211,7 +239,6 @@ export default {
},
setCoupon() {
if (this.current === 1) return
console.log(this.current, '====1=======');
uni.setStorageSync('couponId', this.couponId)
this.$emit('change', this.selectItem || {})
this.$emit('ok', this.couponId)
@@ -0,0 +1,813 @@
<template>
<view class="date-range-picker">
<u-popup :show="show" @close="handleClose" mode="center" round="16" closeable>
<view class="picker-container">
<view class="picker-header">
<text class="title">查看可预约日期</text>
</view>
<view class="v12-align-center v12-justify-between">
<view class="v12-my-3">
<text class="v12-font-24 v12-dark-text v12-mr-3">请选择入住日期</text>
<text class="v12-font-24 v12-primary-text">{{ ` 共计${minBookingDays}` }}</text>
</view>
<view v-if="originDate" class="v12-font-24 v12-primary-text">
原定时间{{ originDate }}
</view>
</view>
<view class="calendar" @touchstart="onTouchStart" @touchend="onTouchEnd">
<view class="calendar-header">
<view class="month-nav">
<text class="nav-btn" @click="changeMonth(-1)"></text>
<text class="month-text">{{ currentYear }}{{ currentMonth + 1 }}</text>
<text class="nav-btn" @click="changeMonth(1)"></text>
</view>
<view class="weekdays">
<text v-for="day in ['日', '一', '二', '三', '四', '五', '六']" :key="day" class="weekday v12-dark-text">
{{ day }}
</text>
</view>
</view>
<view class="calendar-body">
<view class="days" :class="[isAnimating ? 'absolute ' + transitionMode + '-enter' : '']">
<view
v-for="(day, index) in calendarDays"
:key="index"
class="day"
:class="{
'empty': !day,
'selected': isSelected(day),
'in-range': isInRange(day),
'start-date': isStartDate(day),
'end-date': isEndDate(day),
'selecting-start': isSelectingStart(day),
'disabled': isDisabled(day),
'booked': getDateStatus(day) === '已被预约',
'unavailable': getDateStatus(day) === '不可预约',
'available': getDateStatus(day) === '可预约'
}"
@click="selectDate(day)"
>
<text>{{ day || '' }}</text>
<view class="v12-font-20" v-if="day && getDateStatus(day) !== null">{{ getDateStatus(day) }}</view>
</view>
</view>
<view class="days absolute" v-if="isAnimating" :class="[transitionMode + '-leave']">
<view
v-for="(day, index) in prevMonthDays"
:key="'prev-' + index"
class="day"
:class="{
'empty': !day,
'selected': isSelected(day, prevYear, prevMonth),
'in-range': isInRange(day, prevYear, prevMonth),
'start-date': isStartDate(day, prevYear, prevMonth),
'end-date': isEndDate(day, prevYear, prevMonth),
'selecting-start': isSelectingStart(day, prevYear, prevMonth),
'disabled': isDisabled(day, prevYear, prevMonth),
'booked': getDateStatus(day, prevYear, prevMonth) === '已被预约',
'unavailable': getDateStatus(day, prevYear, prevMonth) === '不可预约',
'available': getDateStatus(day, prevYear, prevMonth) === '可预约'
}"
>
<text>{{ day || '' }}</text>
<view class="v12-font-20" v-if="day && getDateStatus(day, prevYear, prevMonth) !== null">{{ getDateStatus(day, prevYear, prevMonth) }}</view>
</view>
</view>
</view>
</view>
<view class="picker-footer">
<button class="btn btn-confirm" @click="confirmSelect">立即预订</button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import { sojoumOrderCalendar, sojoumOrderCheckDate } from "@/api/sojoumOrder";
export default {
name: 'DateRangePicker',
props: {
originDate: {
type: String,
default: ''
},
show: {
type: Boolean,
default: false
},
value: {
type: Array,
default: () => [null, null]
},
minDate: {
type: [String, Date],
default: null
},
maxDate: {
type: [String, Date],
default: null
},
format: {
type: String,
default: 'YYYY-MM-DD'
},
travelGroupProductId: {
type: String,
default: ''
},
minBookingDays: {
type: Number,
default: 0
},
},
data() {
return {
currentYear: new Date().getFullYear(),
currentMonth: new Date().getMonth(),
startDate: null,
endDate: null,
activeCalendar: 'start', // 'start' or 'end'
tempStartDate: null,
tempEndDate: null,
dateStatusMap: {}, // 存储日期状态
selectionState: 'initial', // 'initial' | 'selecting' | 'completed'
touchStartX: 0,
touchStartY: 0,
// 动画相关状态
isAnimating: false,
transitionMode: '', // 'next' or 'prev'
prevYear: null,
prevMonth: null,
prevMonthDays: []
}
},
computed: {
calendarDays() {
const days = []
const firstDay = new Date(this.currentYear, this.currentMonth, 1)
const lastDay = new Date(this.currentYear, this.currentMonth + 1, 0)
// 填充月初空白天数
for (let i = 0; i < firstDay.getDay(); i++) {
days.push(null)
}
// 填充当月天数
for (let i = 1; i <= lastDay.getDate(); i++) {
days.push(i)
}
// 填充月末空白天数
const remainingDays = 42 - days.length // 保持6行固定高度
for (let i = 0; i < remainingDays; i++) {
days.push(null)
}
return days
}
},
watch: {
show(newVal) {
if (newVal) {
this.initDates()
this.getDays()
}
},
currentMonth() {
this.getDays()
},
currentYear() {
this.getDays()
},
value: {
handler(newVal) {
if (newVal && newVal.length === 2) {
this.startDate = newVal[0] ? new Date(newVal[0]) : null
this.endDate = newVal[1] ? new Date(newVal[1]) : null
this.tempStartDate = this.startDate
this.tempEndDate = this.endDate
}
},
immediate: true
}
},
methods: {
getDays(){
// uni.showLoading({
// title: '加载中...',
// mask: true,
// })
sojoumOrderCalendar({
travelGroupProductId: this.travelGroupProductId,
month: `${this.currentYear}-${String(this.currentMonth + 1).padStart(2, '0')}`
}).then(res => {
if (res.data) {
// 更新日期状态映射,使用合并而不是覆盖,以支持动画时的旧数据显示
const newStatusMap = {}
res.data.forEach(day => {
// inventoryStatus int 状态 (0:可预约, 1:已被预约, 2:不可预约)
newStatusMap[day.date] = day.inventoryStatus === 0 ? '可预约' : day.inventoryStatus === 1 ? '已被预约' : '不可预约'
})
this.dateStatusMap = { ...this.dateStatusMap, ...newStatusMap }
}
}).finally(() => {
uni.hideLoading()
})
},
getDateStatus(day, year, month) {
if (!day) return null
const date = this.formatDate(this.getDateFromDay(day, year, month))
return this.dateStatusMap[date]
},
isDisabled(day, year, month) {
if (!day) return true
const date = this.getDateFromDay(day, year, month)
// 禁用过去的日期(今天之前的日期)
const today = new Date()
today.setHours(0, 0, 0, 0)
if (date < today) return true
// 检查最小和最大日期限制
if (this.minDate && date < new Date(this.minDate)) return true
if (this.maxDate && date > new Date(this.maxDate)) return true
// 检查预约状态
const status = this.getDateStatus(day, year, month)
// 只允许选择可预约的日期(状态为"可预约"),其他状态都禁用
return status !== '可预约'
},
initDates() {
this.tempStartDate = this.startDate
this.tempEndDate = this.endDate
// 根据当前选择状态设置selectionState
if (this.tempStartDate && this.tempEndDate) {
this.selectionState = 'completed'
this.activeCalendar = 'start'
} else if (this.tempStartDate) {
this.selectionState = 'selecting'
this.activeCalendar = 'end'
} else {
this.selectionState = 'initial'
this.activeCalendar = 'start'
}
if (this.startDate) {
this.currentYear = this.startDate.getFullYear()
this.currentMonth = this.startDate.getMonth()
} else {
const now = new Date()
this.currentYear = now.getFullYear()
this.currentMonth = now.getMonth()
}
},
changeMonth(delta) {
if (this.isAnimating) return
// Setup animation state
this.prevYear = this.currentYear
this.prevMonth = this.currentMonth
this.prevMonthDays = [...this.calendarDays]
this.transitionMode = delta > 0 ? 'next' : 'prev'
this.isAnimating = true
let newMonth = this.currentMonth + delta
if (newMonth < 0) {
this.currentYear--
newMonth = 11
} else if (newMonth > 11) {
this.currentYear++
newMonth = 0
}
this.currentMonth = newMonth
// Reset animation state after transition
setTimeout(() => {
this.isAnimating = false
this.prevMonthDays = []
}, 300)
},
onTouchStart(e) {
if (!e || !e.changedTouches || !e.changedTouches.length) return
this.touchStartX = e.changedTouches[0].clientX
this.touchStartY = e.changedTouches[0].clientY
},
onTouchEnd(e) {
if (!e || !e.changedTouches || !e.changedTouches.length) return
const touchEndX = e.changedTouches[0].clientX
const touchEndY = e.changedTouches[0].clientY
const deltaX = touchEndX - this.touchStartX
const deltaY = touchEndY - this.touchStartY
if (Math.abs(deltaX) <= Math.abs(deltaY)) return
const minDistance = 50
if (Math.abs(deltaX) < minDistance) return
if (deltaX < 0) {
this.changeMonth(1)
} else {
this.changeMonth(-1)
}
},
formatDate(date) {
if (!date) return ''
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
getDateFromDay(day, year, month) {
const y = year !== undefined ? year : this.currentYear
const m = month !== undefined ? month : this.currentMonth
return day ? new Date(y, m, day) : null
},
// isDisabled(day) {
// if (!day) return true
// const date = this.getDateFromDay(day)
// // 禁用过去的日期(今天之前的日期)
// const today = new Date()
// today.setHours(0, 0, 0, 0)
// if (date < today) return true
// // 检查最小和最大日期限制
// if (this.minDate && date < new Date(this.minDate)) return true
// if (this.maxDate && date > new Date(this.maxDate)) return true
// return false
// },
isSelected(day, year, month) {
if (!day) return false
// const date = this.getDateFromDay(day, year, month) // Unused?
return this.isStartDate(day, year, month) || this.isEndDate(day, year, month)
},
isStartDate(day, year, month) {
if (!day || !this.tempStartDate) return false
const date = this.getDateFromDay(day, year, month)
return date.getTime() === this.tempStartDate.getTime()
},
isEndDate(day, year, month) {
if (!day || !this.tempEndDate) return false
const date = this.getDateFromDay(day, year, month)
return date.getTime() === this.tempEndDate.getTime()
},
isInRange(day, year, month) {
if (!day || !this.tempStartDate || !this.tempEndDate) return false
const date = this.getDateFromDay(day, year, month)
return date > this.tempStartDate && date < this.tempEndDate
},
isSelectingStart(day, year, month) {
if (!day || !this.tempStartDate || this.tempEndDate) return false
// 只有在选择状态且只有开始日期时才显示特殊样式
if (this.selectionState !== 'selecting') return false
const date = this.getDateFromDay(day, year, month)
return date.getTime() === this.tempStartDate.getTime()
},
selectDate(day, year, month) {
if (!day || this.isDisabled(day, year, month)) return
const selectedDate = this.getDateFromDay(day, year, month)
// 如果设置了最小预订天数,则自动计算结束日期
if (this.minBookingDays > 0) {
this.tempStartDate = selectedDate
const endDate = new Date(selectedDate)
endDate.setDate(selectedDate.getDate() + this.minBookingDays - 1)
this.tempEndDate = endDate
this.selectionState = 'completed'
return
}
// 如果已有完整的日期区间(开始日期和结束日期都存在),重置选择
if (this.tempStartDate && this.tempEndDate && this.selectionState === 'completed') {
// 重置选择状态,将点击的日期作为新的开始日期
this.tempStartDate = selectedDate
this.tempEndDate = null
this.activeCalendar = 'end'
this.selectionState = 'selecting'
return
}
// 如果点击已选择的开始日期,则取消选择
if (this.tempStartDate && selectedDate.getTime() === this.tempStartDate.getTime()) {
this.tempStartDate = null
this.tempEndDate = null
this.activeCalendar = 'start'
this.selectionState = 'initial'
return
}
// 如果点击已选择的结束日期,则只清除结束日期
if (this.tempEndDate && selectedDate.getTime() === this.tempEndDate.getTime()) {
this.tempEndDate = null
this.activeCalendar = 'end'
this.selectionState = 'selecting'
return
}
if (this.activeCalendar === 'start' || this.selectionState === 'initial') {
// 选择开始日期
this.tempStartDate = selectedDate
this.tempEndDate = null
this.activeCalendar = 'end'
this.selectionState = 'selecting'
} else if (this.activeCalendar === 'end') {
if (selectedDate < this.tempStartDate) {
// 如果选择的结束日期小于开始日期,将其设为开始日期
this.tempStartDate = selectedDate
this.tempEndDate = null
this.selectionState = 'selecting'
} else {
// 设置结束日期,完成选择
this.tempEndDate = selectedDate
this.selectionState = 'completed'
}
}
},
clearDates() {
this.tempStartDate = null
this.tempEndDate = null
this.activeCalendar = 'start'
this.selectionState = 'initial'
},
confirmSelect() {
// 验证选择的日期数量
if (!this.tempStartDate || !this.tempEndDate) {
uni.showToast({
title: '请选择起始和结束日期',
icon: 'none'
})
return
}
// 计算选择的天数
const daysDiff = Math.floor((this.tempEndDate - this.tempStartDate) / (1000 * 60 * 60 * 24)) + 1
// 必须要选够天数
if (daysDiff !== this.minBookingDays) {
uni.showToast({
title: `此套餐预约需连续选择${this.minBookingDays}`,
icon: 'none'
})
return
}
// 验证选择的日期范围内是否包含禁用日期
// const currentDate = new Date(this.tempStartDate)
// while (currentDate <= this.tempEndDate) {
// const day = currentDate.getDate()
// if (this.isDisabled(day)) {
// uni.showToast({
// title: '所选日期范围包含不可预约日期,请重新选择',
// icon: 'none'
// })
// return
// }
// currentDate.setDate(currentDate.getDate() + 1)
// }
sojoumOrderCheckDate({
travelGroupProductId: this.travelGroupProductId,
orderStartDate: this.formatDate(this.tempStartDate),
orderEndDate: this.formatDate(this.tempEndDate)
}).then(res => {
if (res.status === 200) {
// 验证通过,更新日期并关闭弹窗
this.startDate = this.tempStartDate
this.endDate = this.tempEndDate
this.selectionState = 'completed'
this.$emit('input', [
this.startDate ? this.formatDate(this.startDate) : null,
this.endDate ? this.formatDate(this.endDate) : null
])
this.handleClose(false)
} else {
uni.showToast({
title: res.msg || '所选日期范围包含不可预约日期,请重新选择',
icon: 'none'
})
}
})
},
handleClose(shouldClear = false) {
if (shouldClear) {
this.clearDates()
this.startDate = null
this.endDate = null
this.$emit('input', [null, null])
} else {
// 恢复到原始状态
this.tempStartDate = this.startDate
this.tempEndDate = this.endDate
if (this.startDate && this.endDate) {
this.selectionState = 'completed'
} else if (this.startDate) {
this.selectionState = 'selecting'
} else {
this.selectionState = 'initial'
}
}
this.$emit('update:show', false)
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.date-range-picker {
.picker-container {
width: 690rpx;
background: #FFFFFF;
border-radius: 24rpx;
padding: 30rpx;
box-sizing: border-box;
}
.picker-header {
text-align: center;
margin-bottom: 30rpx;
.title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
}
.date-display {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
padding: 20rpx;
background: #F8F8F8;
border-radius: 12rpx;
.date-input {
flex: 1;
.label {
font-size: 24rpx;
color: #999999;
margin-bottom: 8rpx;
display: block;
}
.value {
font-size: 28rpx;
color: #333333;
&.placeholder {
color: #999999;
}
}
}
.separator {
margin: 0 20rpx;
color: #999999;
font-size: 28rpx;
}
}
.calendar {
.calendar-header {
margin-bottom: 20rpx;
.month-nav {
display: flex;
align-items: center;
justify-content: space-evenly;
margin-bottom: 20rpx;
padding: 0 20rpx;
.month-text {
font-size: 28rpx;
color: #333333;
font-weight: bold;
}
.nav-btn {
padding: 10rpx 20rpx;
color: #666666;
font-size: 32rpx;
}
}
.weekdays {
display: flex;
justify-content: space-around;
.weekday {
width: 14.28%;
text-align: center;
font-size: 24rpx;
color: #999999;
}
}
}
.days {
display: flex;
flex-wrap: wrap;
.day {
width: 14.28%;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #333333;
position: relative;
flex-direction: column;
&:not(.selected):not(.disabled) {
color: #4B97EB;
}
.status-dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
margin-top: 4rpx;
}
&.empty {
pointer-events: none;
}
&.disabled {
color: #CCCCCC;
pointer-events: none;
}
&.selected {
background: #C52733;
color: #FFFFFF;
.status-dot {
background: #FFFFFF;
}
}
&.selecting-start {
background: #4B97EB;
color: #FFFFFF;
border-radius: 8rpx;
position: relative;
&::after {
content: '';
position: absolute;
top: -2rpx;
left: -2rpx;
right: -2rpx;
bottom: -2rpx;
border: 2rpx solid #4B97EB;
border-radius: 10rpx;
animation: pulse 1.5s infinite;
}
.status-dot {
background: #FFFFFF;
}
}
&.in-range {
background: rgba(197, 39, 51, 0.1);
}
&.start-date {
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
}
&.end-date {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
&.available .status-dot {
background: #4CAF50;
}
&.booked .status-dot {
background: #FF9800;
}
&.unavailable .status-dot {
background: #F44336;
}
}
}
}
.picker-footer {
margin-top: 30rpx;
display: flex;
justify-content: space-between;
padding: 0 20rpx;
.btn {
width: 100%;
height: 80rpx;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
&.btn-clear {
background: #F8F8F8;
color: #999999;
}
&.btn-confirm {
background: #C52733;
color: #FFFFFF;
}
}
}
}
@keyframes pulse {
0% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.05);
}
100% {
opacity: 1;
transform: scale(1);
}
}
// Animation Styles
.calendar-body {
position: relative;
height: 480rpx; // 6 rows * 80rpx
overflow: hidden;
width: 100%;
}
.days.absolute {
position: absolute;
top: 0;
left: 0;
width: 100%;
// height: 100%;
z-index: 1;
}
// Keyframes for sliding
@keyframes slide-next-enter {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes slide-next-leave {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
@keyframes slide-prev-enter {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
@keyframes slide-prev-leave {
from { transform: translateX(0); }
to { transform: translateX(100%); }
}
.next-enter { animation: slide-next-enter 0.3s forwards; }
.next-leave { animation: slide-next-leave 0.3s forwards; }
.prev-enter { animation: slide-prev-enter 0.3s forwards; }
.prev-leave { animation: slide-prev-leave 0.3s forwards; }
</style>
+37 -6
View File
@@ -1,6 +1,13 @@
<template>
<view>
<view class="product-window" :class="attr.cartAttr === true ? 'on' : ''" :style="{paddingBottom: paddingBottom}">
<view
:class="{
'on': attr.cartAttr === true,
[className]: className
}"
:style="{paddingBottom: paddingBottom}"
class="product-window"
>
<view class="textpic acea-row row-between-wrapper">
<view class="pictrue" @click="previewImg(attrObj.productSelect.image)">
<image :src="attrObj.productSelect.image" class="image" />
@@ -73,7 +80,7 @@
color="#C52733"
@click="$emit('ok')"
:disabled="(attr.productSelect.stock === 0 && attr.cartAttr)"
>加入购物车</u-button>
>{{ okText }}</u-button>
</view>
<view class="v12-px-2 v12-mt-3" v-if="isGift">
<u-button
@@ -84,7 +91,13 @@
>送给朋友</u-button>
</view>
</view>
<view class="mask" @touchmove.prevent :hidden="attr.cartAttr === false" @click="closeAttr"></view>
<view
:hidden="attr.cartAttr === false"
:class="className"
class="mask"
@touchmove.prevent
@click="closeAttr"
></view>
</view>
</template>
<script>
@@ -118,6 +131,14 @@ export default {
cartNum: {
type: Number,
default: () => 1
},
okText: {
type: String,
default: '加入购物车'
},
className: {
type: String,
default: ''
}
},
data() {
@@ -130,12 +151,22 @@ export default {
}
},
watch: {
'attr.cartAttr': {
handler(val) {
console.log(val);
if(!val) {
this.cartNumber = 1
}
},
deep: true,
immediate: true
},
cartNum: {
handler(val) {
console.log(val, 'cardNum');
this.cartNumber = val || 1
},
deep: true
deep: true,
immediate: true
},
cartNumber(val) {
if (val > this.attrObj.productSelect.stock) {
@@ -173,7 +204,7 @@ export default {
this.$emit("changeFun", { action: "changeattr", value: false })
},
CartNumDes() {
if(this.cartNumber === 1) return
if(this.cartNumber <= 1) return
console.log(this.cartNumber, 'cartNumber');
this.cartNumber--
this.$emit("changeFun", { action: "ChangeCartNum", value: this.cartNumber })
+145 -37
View File
@@ -3,66 +3,74 @@
<view
v-for="(item, evaluateWtapperIndex) in reply"
:key="evaluateWtapperIndex"
class="evaluateItem"
class="evaluateItem v12-pt-3"
>
<view class="pic-text acea-row row-middle">
<view class="pictrue">
<image :src="item.avatar" class="image" />
<!-- 用户信息头部 -->
<view class="user-header v12-px-4">
<view class="avatar-box">
<image :src="item.avatar" class="avatar" mode="aspectFill" />
</view>
<view class="acea-row row-middle">
<view class="user-info">
<view class="name line1">{{ item.nickname }}</view>
<view class="time">{{ item.createTime }} </view>
<view class="time">{{ item.createTime }}</view>
</view>
</view>
<view class="v12-px-4">
<view class="v12-secondary-dark-text v12-font-24 attr-bg"> {{ item.sku }}</view>
<view class="start" :class="'star' + item.star"></view>
</view>
<view class="evaluate-infor v12-font-24">{{ item.comment }}</view>
<view class="imgList acea-row">
<!-- 评论内容 -->
<view class="evaluate-infor">{{ item.comment || '此用户没有填写评价'}}</view>
<!-- 图片/视频列表 -->
<view class="imgList">
<!-- 视频 -->
<view v-if="item.video" class="pictrue">
<view
@click="pauseOtherVideo('video' + evaluateWtapperIndex)"
v-if="activeVideo !== 'video' + evaluateWtapperIndex"
class="play-icon"
>
<u-icon name="play-right-fill" color="#fff" size="48"></u-icon>
</view>
>
<u-icon name="play-right-fill" color="#fff" size="48"></u-icon>
</view>
<image
v-if="activeVideo !== 'video' + evaluateWtapperIndex"
:src="item.video + '?vframe/jpg/offset/1'"
class="image"
mode="aspectFill"
@click="pauseOtherVideo('video' + evaluateWtapperIndex)"
>
</image>
/>
<video
v-else
:src="item.video"
:poster="item.video + '?vframe/jpg/offset/1'"
controls
autoplay
play-btn-position="center"
:id="'video' + evaluateWtapperIndex"
class="image"
:id="'video' + evaluateWtapperIndex"
@play="pauseOtherVideo('video' + evaluateWtapperIndex)"
/>
</view>
<!-- 图片 -->
<view
v-for="(itemn, eq) in item.picturesArr"
:key="eq"
v-for="(pic, idx) in item.picturesArr"
:key="idx"
class="pictrue"
>
<image :src="itemn" class="image" @click="previewImage(item.picturesArr, eq)" />
<image :src="pic" class="image" mode="aspectFill" @click="previewImage(item.picturesArr, idx)" />
</view>
</view>
<!-- 商家回复 -->
<view class="reply" v-if="item.merchantReplyContent">
<span class="font-color-red">店小二</span>
<text class="font-color-red">店小二</text>
{{item.merchantReplyContent}}
</view>
</view>
</view>
</template>
<script>
import { dataFormat } from "@/utils";
@@ -105,28 +113,128 @@ export default {
}
};
</script>
<style lang="less" scoped>
.evaluateWtapper {
.evaluateItem {
margin-bottom: 30rpx;
border-bottom: 1px solid #f5f5f5;
padding-bottom: 30rpx;
&:last-child {
border-bottom: none;
}
}
}
.user-header {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.avatar-box {
margin-right: 20rpx;
.avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
}
}
.user-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.name {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 4rpx;
}
.time {
font-size: 22rpx;
color: #999;
}
}
}
.tag-row {
margin-bottom: 12rpx;
.default-tag {
display: inline-block;
background: #FFF0F1;
color: #FE5261;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 6rpx;
}
}
.star-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.star-icon {
margin-right: 4rpx;
}
}
.evaluate-infor {
font-size: 28rpx;
color: #333;
line-height: 1.6;
margin-bottom: 20rpx;
text-align: justify;
}
.imgList {
display: flex;
flex-wrap: wrap;
.pictrue {
width: 220rpx;
height: 220rpx;
margin-right: 15rpx;
margin-bottom: 15rpx;
border-radius: 12rpx;
overflow: hidden;
position: relative;
background: #f5f5f5;
&:nth-child(3n) {
margin-right: 0;
}
.image {
width: 100%;
height: 100%;
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
}
}
.reply {
background: #F5F5F5;
border-radius: 12rpx;
padding: 20rpx;
margin-top: 10rpx;
font-size: 24rpx;
color: #666;
line-height: 1.5;
}
.attr-bg{
background: rgba(197,39,51,0.1);
padding: 7rpx 5px;
width: fit-content;
border-radius: 12rpx;
}
.time{
padding: 0 !important;
}
.evaluateWtapper .evaluateItem .imgList .pictrue{
position: relative;
width: 300rpx;
.play-icon {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
width: 80rpx;
height: 80rpx;
}
}
</style>
+9 -2
View File
@@ -15,10 +15,17 @@ import {
} from '@/api/public'
export default {
name: 'AiEntrance',
props: {
cityName: {
type: String,
default: ''
}
},
data() {
return {
showImg: false,
settingInfo: {}
settingInfo: {},
localCityName: uni.getStorageSync('locationCityName') || ''
}
},
created() {
@@ -36,7 +43,7 @@ export default {
},
enterAiChat() {
uni.navigateTo({
url: '/aiChat/views/index?click=1'
url: '/aiChat/views/index?click=1&cityName=' + (this.cityName || this.localCityName)
})
}
}
+485
View File
@@ -0,0 +1,485 @@
<template>
<view>
<view class="c_total">资讯评论</view>
<template v-if="dataList && dataList.length">
<view class="c_comment" v-for="(item1, index1) in dataList" :key="item1.id">
<!-- 一级评论 -->
<CommonComp
:data="item1"
@likeClick="() => likeClick({ item1, index1 })"
@replyClick="() => replyClick({ item1, index1 })"
@deleteClick="() => deleteClick({ item1, index1 })"
/>
<view class="children_item" v-if="item1.children && item1.children.length">
<!-- 二级评论 -->
<CommonComp
v-for="(item2, index2) in item1.childrenShow"
:key="item2.id"
:data="item2"
:pData="item1"
@likeClick="() => likeClick({ item1, index1, item2, index2 })"
@replyClick="() => replyClick({ item1, index1, item2, index2 })"
@deleteClick="() => deleteClick({ item1, index1, item2, index2 })"
/>
<!-- 展开二级评论 -->
<view
class="expand_reply"
v-if="expandTxtShow({ item1, index1 })"
@tap="() => expandReplyFun({ item1, index1 })"
>
<span class="txt"> 展开{{ item1.children.length - item1.childrenShow.length }}条回复 </span>
<uni-icons type="down" size="24" color="#007aff"></uni-icons>
</view>
<!-- 折叠二级评论 -->
<view
class="shrink_reply"
v-if="shrinkTxtShow({ item1, index1 })"
@tap="() => shrinkReplyFun({ item1, index1 })"
>
<span class="txt"> 收起回复内容 </span>
<uni-icons type="up" size="24" color="#007aff"></uni-icons>
</view>
</view>
</view>
</template>
<!-- 空盒子 -->
<view class="empty_box" v-else>
<uni-icons type="chatboxes" size="36" color="#c0c0c0"></uni-icons>
<view>
<span class="txt"> 这里是一片荒草地, </span>
<span class="txt click" @click="() => newCommentFun()">说点什么...</span>
</view>
</view>
<!-- 评论弹窗 -->
<uni-popup ref="cPopupRef" type="bottom" @change="popChange">
<view class="c_popup_box">
<view class="reply_text">
<template v-if="Object.keys(replyTemp).length">
<span class="text_aid">回复给</span>
<img
class="user_avatar"
:src="replyTemp.item2 ? replyTemp.item2.user_avatar : replyTemp.item1.user_avatar"
/>
<span class="text_main">{{ replyTemp.item2 ? replyTemp.item2.user_name : replyTemp.item1.user_name }}</span>
</template>
<span v-else class="text_main">发表新评论</span>
</view>
<view class="content">
<view class="text_area">
<uni-easyinput
class="text_area"
type="textarea"
v-model="commentValue"
:placeholder="commentPlaceholder"
:focus="focus"
trim
autoHeight
maxlength="300"
></uni-easyinput>
</view>
<view class="send_btn" @tap="() => sendClick()">发送</view>
</view>
</view>
</uni-popup>
<!-- 删除弹窗 -->
<uni-popup ref="delPopupRef" type="dialog">
<uni-popup-dialog
mode="base"
title=""
content="确定删除这条评论吗?"
:before-close="true"
@close="delCloseFun"
@confirm="delConfirmFun"
></uni-popup-dialog>
</uni-popup>
</view>
</template>
<script>
import CommonComp from "./componets/common";
export default {
components: { CommonComp },
props: {
/** 登陆用户信息
* id: number // 登陆用户id
* user_name: number // 登陆用户名
* user_avatar: string // 登陆用户头像地址
*/
myInfo: {
type: Object,
default: () => {},
},
/** 文章作者信息
* id: number // 文章作者id
* user_name: number // 文章作者名
* user_avatar: string // 文章作者头像地址
*/
userInfo: {
type: Object,
default: () => {},
},
/** 评论列表
* id: number // 评论id
* parent_id: number // 父级评论id
* reply_id: number // 被回复人评论id
* reply_name: string // 被回复人名称
* user_name: string // 用户名
* user_avatar: string // 评论者头像地址
* user_content: string // 评论内容
* is_like: boolean // 是否点赞
* like_count: number // 点赞数统计
* create_time: string // 创建时间
*/
tableData: {
type: Array,
default: () => [],
},
// 评论总数
tableTotal: {
type: Number,
default: 0,
},
// 评论删除模式
// bind - 当被删除的一级评论存在回复评论, 那么该评论内容变更显示为[当前评论内容已被移除]
// only - 仅删除当前评论(后端删除相关联的回复评论, 否则总数显示不对)
// all - 删除所有评论包括回复评论
deleteMode: {
type: String,
default: "all",
},
},
data() {
return {
dataList: [], // 渲染数据(前端的格式)
replyTemp: {}, // 回复临时数据
isNewComment: false, // 是否为新评论
focus: false, // 评论弹窗
commentValue: "", // 输入框值
commentPlaceholder: "说点什么...", // 输入框占位符
delTemp: {}, // 删除临时数据
};
},
watch: {
tableData: {
handler(newVal) {
if (newVal.length !== this.dataList.length) {
this.dataList = this.treeTransForm(newVal);
}
},
deep: true,
immediate: true,
},
},
mounted() {},
methods: {
// 数据转换
treeTransForm(data) {
let newData = JSON.parse(JSON.stringify(data));
let result = [];
let map = {};
newData.forEach((item, i) => {
item.owner = item.user_id === this.myInfo.user_id; // 是否为当前登陆用户 可以对自己的评论进行删除 不能回复
item.author = item.user_id === this.userInfo.user_id; // 是否为作者 显示标记
map[item.id] = item;
});
newData.forEach((item) => {
let parent = map[item.parent_id];
if (parent) {
(parent.children || (parent.children = [])).push(item); // 所有回复
if (parent.children.length === 1) {
(parent.childrenShow = []).push(item); // 显示的回复
}
} else {
result.push(item);
}
});
return result;
},
// 点赞
setLike(item) {
item.is_like = !item.is_like;
item.like_count = item.is_like ? item.like_count + 1 : item.like_count - 1;
},
likeClick({ item1, index1, item2, index2 }) {
let item = item2 || item1;
this.setLike(item);
this.$emit("likeFun", { params: item }, (res) => {
// 请求后端失败, 重置点赞
setLike(item);
});
},
// 回复
replyClick({ item1, index1, item2, index2 }) {
this.replyTemp = JSON.parse(JSON.stringify({ item1, index1, item2, index2 }));
this.$refs["cPopupRef"].open();
},
// 发起新评论
newCommentFun() {
this.isNewComment = true;
this.$refs["cPopupRef"].open();
},
// 评论弹窗
popChange(e) {
// 关闭弹窗
if (!e.show) {
this.commentValue = ""; // 清空输入框值
this.replyTemp = {}; // 清空被回复人信息
this.isNewComment = false; // 恢复是否为新评论默认值
}
this.focus = e.show;
},
// 发送评论
sendClick({ item1, index1, item2, index2 } = this.replyTemp) {
let item = item2 || item1;
let params = {};
// 新评论
if (this.isNewComment) {
params = {
id: Math.random(), // 评论id
parent_id: null, // 父级评论id
reply_id: null, // 被回复评论id
reply_name: null, // 被回复人名称
};
} else {
// 回复评论
params = {
id: Math.random(), // 评论id
parent_id: item?.parent_id ?? item.id, // 父级评论id
reply_id: item.id, // 被回复评论id
reply_name: item.user_name, // 被回复人名称
};
}
params = {
...params,
user_id: this.myInfo.user_id, // 用户id
user_name: this.myInfo.user_name, // 用户名
user_avatar: this.myInfo.user_avatar, // 用户头像地址
user_content: this.commentValue, // 用户评论内容
is_like: false, // 是否点赞
like_count: 0, // 点赞数统计
create_time: "刚刚", // 创建时间
owner: true, // 是否为所有者 所有者可以进行删除 管理员默认true
};
uni.showLoading({
title: "正在发送",
mask: true,
});
this.$emit("replyFun", { params }, (res) => {
uni.hideLoading();
// 拿到后端返回的id赋值, 因为删除要用到id
params = { ...params, id: res.id };
// 新评论
if (this.isNewComment) {
this.dataList.push(params);
} else {
// 回复
let c_data = this.dataList[index1];
(c_data.children || (c_data.children = [])).push(params);
// 如果已展开所有回复, 那么此时插入children长度会大于childrenShow长度1, 所以就直接展开显示即可
if (c_data.children.length === (c_data.childrenShow || (c_data.childrenShow = [])).length + 1) {
c_data.childrenShow.push(params);
}
}
this.$emit("update:tableTotal", this.tableTotal + 1);
this.$refs["cPopupRef"].close();
});
},
//删除
deleteClick({ item1, index1, item2, index2 }) {
this.delTemp = JSON.parse(JSON.stringify({ item1, index1, item2, index2 }));
this.$refs["delPopupRef"].open();
},
// 关闭删除弹窗
delCloseFun() {
this.delTemp = {};
this.$refs["delPopupRef"].close();
},
// 确定删除
delConfirmFun({ item1, index1, item2, index2 } = this.delTemp) {
const deleteMode = this.deleteMode;
let c_data = this.dataList[index1];
uni.showLoading({
title: "正在删除",
mask: true,
});
// 删除二级评论
if (index2 >= 0) {
this.$emit("deleteFun", { params: [c_data.children[index2].id], mode: deleteMode }, (res) => {
uni.hideLoading();
this.$emit("update:tableTotal", this.tableTotal - 1);
c_data.children.splice(index2, 1);
c_data.childrenShow.splice(index2, 1);
});
} else {
// 删除一级评论
if (c_data?.children?.length) {
// 如果一级评论包含回复评论
switch (deleteMode) {
case "bind":
// 一级评论内容展示修改为: 当前评论内容已被移除
this.$emit(
"deleteFun",
{
params: [c_data.id],
mode: deleteMode,
},
(res) => {
uni.hideLoading();
c_data.user_content = "当前评论内容已被移除";
}
);
break;
case "only":
// 后端自行根据删除的一级评论id, 查找关联的子评论进行删除
this.$emit(
"deleteFun",
{
params: [c_data.id],
mode: deleteMode,
},
(res) => {
uni.hideLoading();
this.$emit("update:tableTotal", this.tableTotal - c_data.children.length + 1);
this.dataList.splice(index1, 1);
}
);
break;
default:
// all
// 收集子评论id, 提交给后端统一删除
let delIdArr = [c_data.id];
c_data.children.forEach((_, i) => {
delIdArr.push(_.id);
});
this.$emit("deleteFun", { params: delIdArr, mode: deleteMode }, (res) => {
uni.hideLoading();
this.$emit("update:tableTotal", this.tableTotal - c_data.children.length + 1);
this.dataList.splice(index1, 1);
});
break;
}
} else {
// 一级评论无回复, 直接删除
this.$emit("deleteFun", { params: [c_data.id], mode: deleteMode }, (res) => {
uni.hideLoading();
this.$emit("update:tableTotal", this.tableTotal - 1);
this.dataList.splice(index1, 1);
});
}
}
this.delCloseFun();
},
// 展开评论if
expandTxtShow({ item1, index1 }) {
return item1.childrenShow?.length && item1.children.length - item1.childrenShow.length;
},
// 展开更多评论
expandReplyFun({ item1, index1 }) {
let csLen = this.dataList[index1].childrenShow.length;
this.dataList[index1].childrenShow.push(
...this.dataList[index1].children.slice(csLen, csLen + 6) // 截取5条评论
);
},
// 收起评论if
shrinkTxtShow({ item1, index1 }) {
return item1.childrenShow?.length >= 2 && item1.children.length - item1.childrenShow.length === 0;
},
// 收起更多评论
shrinkReplyFun({ item1, index1 }) {
this.dataList[index1].childrenShow = [];
this.dataList[index1].childrenShow.push(
...this.dataList[index1].children.slice(0, 1) // 截取1条评论
);
},
},
};
</script>
<style lang="scss" scoped>
////////////////////////
.center {
display: flex;
align-items: center;
}
////////////////////////
.c_total {
padding: 20rpx 30rpx 0 30rpx;
font-size: 28rpx;
}
.empty_box {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 150rpx 10rpx;
font-size: 28rpx;
.txt {
color: $uni-text-color-disable;
}
.click {
color: $uni-color-primary;
}
}
.c_comment {
padding: 20rpx 30rpx;
font-size: 28rpx;
.children_item {
padding: 20rpx 30rpx;
margin-top: 10rpx;
margin-left: 80rpx;
background-color: $uni-bg-color-grey;
.expand_reply,
.shrink_reply {
margin-top: 10rpx;
margin-left: 80rpx;
.txt {
font-weight: 600;
color: $uni-color-primary;
}
}
}
}
.c_popup_box {
background-color: #fff;
.reply_text {
@extend .center;
padding: 20rpx 20rpx 0 20rpx;
font-size: 26rpx;
.text_aid {
color: $uni-text-color-grey;
margin-right: 5rpx;
}
.user_avatar {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 6rpx;
margin-left: 12rpx;
}
.text_main {
}
}
.content {
@extend .center;
.text_area {
flex: 1;
padding: 20rpx;
}
.send_btn {
@extend .center;
justify-content: center;
width: 120rpx;
height: 60rpx;
border-radius: 20rpx;
font-size: 28rpx;
color: #fff;
background-color: $uni-color-primary;
margin-right: 20rpx;
margin-left: 5rpx;
}
}
}
</style>
+182
View File
@@ -0,0 +1,182 @@
<template>
<view class="comment_item">
<view class="top">
<view class="top_left">
<img class="user_avatar" :src="data.user_avatar" />
<uni-tag v-if="data.author" class="tag" type="primary" :inverted="false" text="作者" size="mini" circle />
<span class="user_name">{{ data.user_name }}</span>
<span class="user_name">{{ cReplyName }}</span>
</view>
<view class="top_right" @click="likeClick(data)">
<span :class="[data.is_like ? 'active' : '', 'like_count']">{{ cLikeCount }}</span>
<uni-icons v-show="data.is_like" type="hand-up-filled" size="24" color="#007aff"></uni-icons>
<uni-icons v-show="!data.is_like" type="hand-up" size="24" color="#999"></uni-icons>
</view>
</view>
<view class="content" @click="replyClick(data)">
{{ c_content }}
<span class="shrink" v-if="isShrink" @click.stop="expandContentFun(data.user_content)">...展开</span>
<span
class="shrink"
v-if="!isShrink && user_content.length > contentShowLength"
@click.stop="shrinkContentFun(data.user_content)"
>
收起</span
>
</view>
<view class="bottom">
<span class="create_time">{{ data.create_time }}</span>
<span v-if="data.owner" class="delete" @click="deleteClick(data)">删除</span>
<!-- <span v-else class="reply" @click="replyClick(props.data)"
>回复</span
> -->
</view>
</view>
</template>
<script>
export default {
props: {
// 评论数据
data: {
type: Object,
default: () => {},
},
},
data() {
return {
// 评论过长处理
contentShowLength: 70, // 默认显示评论字符
user_content: "",
isShrink: false, // 是否收缩评论
c_content: "",
};
},
computed: {
// 被回复人名称
cReplyName: function () {
return this.data?.reply_name ? `` + this.data?.reply_name : "";
},
// 点赞数显示
cLikeCount: function () {
return this.data.like_count === 0 ? "" : this.$formatCount(this.data.like_count);
},
},
watch: {
// 删除变更显示定制
"data.user_content": function (newVal, oldVal) {
if (newVal !== oldVal) {
this.c_content = newVal;
}
},
// 监听isShrink变化,更新c_content
isShrink: function (newVal) {
this.c_content = newVal ? this.user_content.slice(0, this.contentShowLength + 1) : this.user_content;
},
},
methods: {
// 展开文字
expandContentFun() {
this.isShrink = false;
},
// 收起文字
shrinkContentFun() {
this.isShrink = true;
},
// 点赞
likeClick(item) {
this.$emit("likeClick", item);
},
// 回复
replyClick(item) {
// 自己不能回复自己
if (item.owner) return;
this.$emit("replyClick", item);
},
// 删除
deleteClick(item) {
this.$emit("deleteClick", item);
},
},
mounted() {
this.user_content = this.data.user_content;
this.isShrink = this.user_content.length > this.contentShowLength;
this.c_content = this.isShrink ? this.user_content.slice(0, this.contentShowLength + 1) : this.user_content;
},
};
</script>
<style lang="scss" scoped>
////////////////////
.center {
display: flex;
align-items: center;
}
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
////////////////////
.comment_item {
font-size: 28rpx;
.top {
@extend .center;
justify-content: space-between;
.top_left {
display: flex;
align-items: center;
overflow: hidden;
.user_avatar {
width: 68rpx;
height: 68rpx;
border-radius: 50%;
margin-right: 12rpx;
}
.tag {
margin-right: 6rpx;
}
.user_name {
@extend .ellipsis;
max-width: 180rpx;
color: #8c8c8c;
}
}
.top_right {
@extend .center;
.like_count {
color: #8c8c8c;
&.active {
color: #007aff;
}
}
}
}
.content {
padding: 10rpx;
margin-left: 70rpx;
color: #333;
&:active {
background-color: #f2f2f2;
}
.shrink {
padding: 20rpx 20rpx 20rpx 0rpx;
color: #007aff;
}
}
.bottom {
padding-left: 80rpx;
font-size: 24rpx;
.create_time {
color: #8c8c8c;
}
.delete {
padding: 20rpx 20rpx 0 20rpx;
color: #c0c0c0;
}
.reply {
color: #007aff;
}
}
}
</style>
+128
View File
@@ -0,0 +1,128 @@
<template>
<u-popup
:show="showShare"
mode="center"
bgColor="transparent"
closeable
@close="closeShare"
>
<view class="box-share">
<view class="box-img">
<image :src="posterUrl" class="main-img" mode="widthFix" />
</view>
<image
:src="webUrl + '/20230608112210135038.png'"
mode="widthFix"
class="btn"
@click="saveImg"
/>
</view>
</u-popup>
</template>
<script>
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
posterUrl: '',
showShare: false
}
},
methods: {
closeShare() {
this.showShare = false
},
showView(url) {
this.posterUrl = url
this.showShare = true
},
saveImg() {
const _this = this
uni.getSetting({
success: getRes => {
if (getRes.authSetting["scope.writePhotosAlbum"]) {
_this.downloadImage()
} else {
uni.authorize({
scope: "scope.writePhotosAlbum",
success: () => {
_this.downloadImage()
},
fail: () => {
uni.openSetting({
success: openRes => {
console.log(typeof openRes, openRes)
},
fail: () => {
uni.showToast({
title: "请在设置中打开对应权限",
icon: "none"
})
}
})
}
})
}
},
fail: (err) => {
console.log(err, 2)
}
})
},
downloadImage() {
const _this = this
const randomID = () => Math.random().toString(36).substring(2)
uni.downloadFile({
url: this.posterUrl, // 网络图片的地址
filePath: wx.env.USER_DATA_PATH + "/share_" + randomID() + ".png", // 指定的本地文件路径
success: downRes => {
uni.saveImageToPhotosAlbum({
filePath: downRes.filePath, // 临时文件地址
success: function () {
uni.showToast({
title: "保存成功",
icon: "success",
success() {
_this.showShare = false
}
})
},
fail: function (err) {
uni.showToast({
title: err,
icon: "none"
})
}
})
},
fail: function (err) {
uni.showToast({
title: err.errMsg,
icon: "error"
})
}
})
}
}
}
</script>
<style lang="less" scoped>
.box-share {
width: 600rpx;
border-radius: 20rpx;
overflow: hidden;
}
.box-img {
display: flex;
align-items: center;
justify-content: center;
}
.btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
margin-top: 40rpx;
}
</style>
+6 -5
View File
@@ -1,5 +1,5 @@
<template>
<uni-popup ref="popup" type="bottom">
<u-popup :closeOnClickOverlay="true" :show="show" ref="popup" type="bottom">
<view class="tpf-time-range-section">
<view class="tpf-time-range-title-section flex flex-align-center flex-pack-justify">
<text class="tpf-time-range-title-txt tpf-time-range-cancel" @tap="closePopup('cancel')">取消</text>
@@ -30,7 +30,7 @@
</view>
</view>
</view>
</uni-popup>
</u-popup>
</template>
<script>
@@ -98,6 +98,7 @@ export default{
return {
startDefaultTimeArr:[0,0],
endDefaultTimeArr:[0,0],
show: false
}
},
methods:{
@@ -110,11 +111,11 @@ export default{
if(this.compareTwoTimeRange(this.startDefaultTimeArr,e.detail.value)) this.startDefaultTimeArr = e.detail.value;
},
open(){
this.$refs.popup.open();
this.show = true;
},
closePopup(action=""){
if(action == "cancel"){
this.$refs.popup.close();
this.show = false;
return false;
}
if(this.compareTwoTimeRange(this.startDefaultTimeArr , this.endDefaultTimeArr)){
@@ -129,7 +130,7 @@ export default{
this.$emit('timeRange',[
startTime,endTime
]);
this.$refs.popup.close();
this.show = false;
},
compareTwoTimeRange(arr1=[],arr2=[]){
if(arr1[0]>arr2[0] || (arr1[0] == arr2[0] && arr1[1] > arr2[1])) return true;
+35 -7
View File
@@ -5,13 +5,15 @@
v-if="!item.isFarmerShop"
:src="item[imageKey]"
class="img"
lazy-load
/>
<image
:src="item.farmerShopCover"
v-if="item.farmerShopCoverType === 1 && item.isFarmerShop"
class="img"
mode="heightFix|widthFix">
</image>
mode="heightFix|widthFix"
lazy-load
/>
<image
:src="item.farmerShopCover + '?vframe/jpg/offset/1'"
mode="heightFix|widthFix"
@@ -20,24 +22,35 @@
:show-fullscreen-btn="false"
:show-play-btn="false"
class="img"
:poster="item.farmerShopCover + '?vframe/jpg/offset/1'">
</image>
:poster="item.farmerShopCover + '?vframe/jpg/offset/1'"
lazy-load
/>
<image
v-if="item.isOldBrand"
:src="webUrl + '/home/old-mark.png'"
class="mark-img"
lazy-load
/>
<image
v-if="item.isGiftCard"
style="left: 0;width: 120rpx;"
:src="webUrl + '/home/gift-tag.png'"
class="mark-img"
lazy-load
/>
<image
v-if="item.isLandmarkGoods"
:src="webUrl + '/home/mark-land.png'"
class="mark-img"
style="right: 20rpx"
lazy-load
/>
<image
v-if="item.isCountyFamous"
:src="webUrl + '/home/qixian-mark.png'"
class="mark-img"
lazy-load
/>
<image
v-if="item.isIch"
@@ -49,6 +62,7 @@
v-if="item.isFarmerShop"
:src="webUrl + '/orderIcon/nm.png'"
class="mark-img-nm"
lazy-load
/>
</view>
<view v-if="item.isFarmerShop" class="more-t v12-pt-2 v12-font-bold v12-font-28 v12-px-2" >{{ item.farmerShopContent }}</view>
@@ -65,12 +79,26 @@
</view>
<view class="price-wrap">
<view class="price">
<text class="price-txt price-red">{{ item[priceKey] }}</text>
<view
v-if="item.isNegotiable === 1"
class="v12-primary-text v12-font-28 v12-font-weight"
>
价格面议
</view>
<text
v-else
class="price-txt price-red">
<text class="v12-font-20">
</text>
{{ item[priceKey] }}
</text>
</view>
<view class="btn" @click.stop="$emit('add', item)">
<view v-if="item.isNegotiable === 0" class="btn" @click.stop="$emit('add', item)">
<image
:src="webUrl + '/home/icon-cart.png'"
class="img"
lazy-load
/>
</view>
</view>
@@ -79,7 +107,7 @@
v-if="item.isFarmerShop"
class="v12-justify-start v12-align-center v12-pt-2 v12-px-2 v12-pb-2">
<view class="btn v12-mr-2">
<image :src="item.farmerShopLogo" class="nm-img-logo v12-radius-100"></image>
<image :src="item.farmerShopLogo" class="nm-img-logo v12-radius-100" lazy-load />
</view>
<view class="one-t v12-font-24 v12-dark1-text" style="width:230rpx">{{ item.farmerShopName }}/{{ item.farmerShopCityName || '-' }}</view>
</view>
+101 -8
View File
@@ -13,7 +13,7 @@
<view
:class="currIndex === 4 ? 'curr' : ''"
class="name"
>寻趣味</view>
>吃喝玩乐</view>
</view>
<view class="tabbar-list">
<view
@@ -30,7 +30,6 @@
@click="gotoPage(item.url, item.type, item.index)"
>
<view class="img-wrap">
<!-- 预加载两张图片可以在点击切换的时候不闪屏 -->
<image
:src="item.onIcon"
class="img on"
@@ -41,6 +40,12 @@
/>
</view>
<view class="name">{{ item.name }}</view>
<view
v-if="item.name === '消息' && showUnreadBadge"
class="badge"
>
{{ unreadCountText }}
</view>
</view>
</view>
</view>
@@ -48,6 +53,8 @@
</template>
<script>
import store from '@/store'
import { getUnreadMessageCount, getUnreadMessageCountForSeller } from '@/api/rooms'
export default {
name: 'XddTabbar',
props: {
@@ -93,14 +100,57 @@ export default {
type: 1,
index: 3
}
]]
]],
unreadCount: 0
}
},
computed: {
isLogin() {
return store.getters.isLogin
},
showUnreadBadge() {
return this.isLogin && this.unreadCount > 0
},
unreadCountText() {
if (this.unreadCount > 99) {
return '99+'
}
return this.unreadCount
}
},
watch: {
isLogin(val) {
if (val) {
this.fetchUnreadCount()
} else {
this.unreadCount = 0
uni.$emit('updateUnreadMessageCount', 0)
}
}
},
mounted() {
this.getElementData('#quweiIcon')
this.getIconData('#icon1')
if (this.isLogin) {
this.fetchUnreadCount()
}
uni.$on('updateUnreadMessageCount', this.handleUpdateUnreadMessageCount)
},
beforeDestroy() {
uni.$off('updateUnreadMessageCount', this.handleUpdateUnreadMessageCount)
},
methods: {
handleUpdateUnreadMessageCount(count) {
if (!this.isLogin) {
this.unreadCount = 0
return
}
if (typeof count === 'number') {
this.unreadCount = count
} else {
this.fetchUnreadCount()
}
},
getElementData(el) {
const query = uni.createSelectorQuery().in(this)
query.select(el).boundingClientRect().exec((res)=> {
@@ -117,10 +167,37 @@ export default {
}
})
},
/**
* 跳转至目标页面
* type: 1-tabbar页面,2-非tabbar页面
*/
fetchUnreadCount() {
uni.request({
url: this.$SERVICE_API_URL + '/unread-num',
method: 'POST',
data: {
uid: store.getters.userInfo.uid
},
success:(res)=>{
if(res.data.status_code === 200) {
const count = res.data.content || 0
getUnreadMessageCount().then(res => {
const { success, data } = res
if (success) {
const _count = data || 0
this.unreadCount = count + _count
uni.$emit('updateUnreadMessageCount', this.unreadCount)
} else {
this.unreadCount = 0
uni.$emit('updateUnreadMessageCount', 0)
}
}).catch(() => {
this.unreadCount = 0
uni.$emit('updateUnreadMessageCount', 0)
})
} else {
this.unreadCount = 0
}
},
})
},
gotoPage(url, type = 1, index) {
if (this.currIndex === index) return
if (type === 1) {
@@ -149,7 +226,7 @@ export default {
background-repeat: no-repeat;
.center-img {
position: absolute;
top: -16rpx;
top: -20rpx;
left: 50%;
z-index: 5;
transform: translateX(-50%);
@@ -199,6 +276,7 @@ export default {
}
.item {
width: 140rpx;
position: relative;
&.curr {
.name {
color: #C52733;
@@ -211,6 +289,21 @@ export default {
}
}
}
.badge {
position: absolute;
top: -4rpx;
right: 32rpx;
min-width: 28rpx;
padding: 0 6rpx;
height: 28rpx;
line-height: 28rpx;
border-radius: 14rpx;
background-color: #FF564A;
color: #ffffff;
font-size: 20rpx;
text-align: center;
box-sizing: border-box;
}
}
}
}
@@ -257,7 +257,7 @@
}
.keyboard-item {
box-sizing: border-box;
width: 250rpx;
width: 33.333%;
display: flex;
flex-direction: column;
justify-content: center;
+13
View File
@@ -75,6 +75,18 @@ const force2Decimal = function change2Decimal(value) {
return forceToDecimal(value, 2)
}
const formatCount = function(value) {
if (!value) return 0;
const num = parseFloat(value);
if (isNaN(num)) return 0;
if (num >= 10000) {
let val = num / 10000;
val = parseFloat(val.toFixed(1));
return val + '万';
}
return num;
}
const toast = (title = '', position = 'center', success = function() {}) => {
uni.showToast({
title,
@@ -88,6 +100,7 @@ const toast = (title = '', position = 'center', success = function() {}) => {
Vue.prototype.$force2Decimal = force2Decimal
Vue.prototype.$forceToDecimal = forceToDecimal
Vue.prototype.$formatCount = formatCount
Vue.config.productionTip = false
App.mpType = 'app'
+10 -3
View File
@@ -5,6 +5,7 @@
"versionName" : "4.0.0",
"versionCode" : 4,
"transformPx" : false,
"sassImplementationName" : "node-sass",
/* 5+App */
"app-plus" : {
"usingComponents" : true,
@@ -134,7 +135,8 @@
"mp-weixin" : {
"appid" : "wx9417047e1a0c340b",
"setting" : {
"urlCheck" : false
"urlCheck" : true,
"minified" : true
},
"usingComponents" : true,
"permission" : {
@@ -146,11 +148,16 @@
"plugins" : {
"live-player-plugin" : {
"version" : "1.3.5",
//最新直播组件版本号
// 最新直播组件版本号
"provider" : "wx2b03c6e691cd7370"
},
"WechatSI" : {
"version" : "0.3.6",
// TTS版本号
"provider" : "wx069ba97219f66d99"
}
},
//直播appid
// 直播appid
"optimization" : {
"subPackages" : true
}
+26 -9
View File
@@ -14,7 +14,7 @@ export default {
isOpen: false,
attrTxt: '',
attrValue: '',
id: null,
m_id: null,
productValueArr: [],
attr: {
cartAttr: false,
@@ -33,12 +33,27 @@ export default {
unique: ''
}
},
cart_num: 1
cart_num: 1,
CartCount: 0,
// 只有一个规格时直接加入购物车;AI聊天界面则不直接加入购物车,每次都要显示规格选择弹窗
onlyOneToAddCart: true
}
},
methods: {
// 获取购物车数量
getCartCount () {
const isLogin = this.isLogin
if (isLogin) {
getCartCount({
numType: 0
}).then(res => {
this.CartCount = res.data.count
this.$forceUpdate()
})
}
},
getSkuActiveStatus(selectedSku) {
getProductSkuBySelected({ id: this.id, selectedSku }).then(res => {
getProductSkuBySelected({ id: this.m_id, selectedSku }).then(res => {
const { success, data } = res
if (success) {
for (const key in data) {
@@ -62,7 +77,7 @@ export default {
})
},
productCon() {
getProductDetail(this.id).then(res => {
getProductDetail(this.m_id).then(res => {
const { data } = res
this.storeInfo = {...data.storeInfo}
if(data.storeInfo.stock === 0) {
@@ -99,8 +114,8 @@ export default {
// 是否单一规格
const onlyOne = Object.keys(data.productValue).length === 1
if(onlyOne) {
this.handleOk()
if(onlyOne && this.onlyOneToAddCart) {
this.handleOk(() => this.getCartCount())
return
}
this.attr.cartAttr = !this.isOpen ? true : false
@@ -222,7 +237,7 @@ export default {
// 点击加入购物车按钮
addToCart(item, id = 'id') {
console.log(item, 'addToCart --------- item');
this.id = item[id]
this.m_id = item[id]
this.$nextTick(() => {
this.productCon()
@@ -233,7 +248,7 @@ export default {
},
handleOk() {
handleOk(cb) {
const productSelect = this.getAttrItemData(this.attrValue)
// 如果有属性,没有选择,提示用户选择
const hasNo = (this.attr.productAttr.length && productSelect === undefined && this.isOpen) || productSelect.stock === 0
@@ -246,7 +261,7 @@ export default {
return
}
const q = {
productId: this.id,
productId: this.m_id,
cartNum: this.attr.productSelect.cart_num,
new: 0,
uniqueId: this.attr.productSelect !== undefined ? this.attr.productSelect.unique : ''
@@ -261,9 +276,11 @@ export default {
icon: "none",
duration: 2000
})
cb && cb()
})
},
ChangeCartNum(changeValue) {
if(changeValue === 0) return
// changeValue:是否 加|减
// 获取当前变动属性
const productSelect = this.getAttrItemData(this.attrValue)
+34 -23
View File
@@ -1,34 +1,45 @@
import { getWeixinShareConfig } from '@/api/public'
export default{
data(){
return {
//设置默认的分享参数
//设置默认的分享参数
sharePagePath: '/pages/home/index',
share:{
title:'云南香道滇官方商城',
path:'/pages/Loading/index',
imageUrl:'http://admin-api.xdd618.com/file/pic/20210617211221217375.jpg',
desc:'',
content:''
title: '云南香道滇官方商城',
path: this.sharePagePath,
imageUrl: this.$VUE_APP_RESOURCES_URL + '/20210617211221217375.jpg?' + new Date().getTime(),
desc: '',
content: ''
}
}
},
onShareAppMessage(res) {
return {
title:this.share.title,
path:this.share.path,
imageUrl:this.share.imageUrl,
desc:this.share.desc,
content:this.share.content,
success(res){
uni.showToast({
title:'分享成功'
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return new Promise(async (resolve, reject) => {
const { success, data } = await getWeixinShareConfig()
if (success) {
resolve({
title: data.title || this.share.title,
path: data.url || this.share.path,
imageUrl: data.image || this.share.imageUrl,
desc: this.share.desc,
content: this.share.content,
success(res){
uni.showToast({
title:'分享成功'
})
},
fail(res){
uni.showToast({
title:'分享失败',
icon:'none'
})
}
})
},
fail(res){
uni.showToast({
title:'分享失败',
icon:'none'
})
}
}
}
})
}
}
+1721 -1716
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -79,6 +79,7 @@
"miniapp-color-thief": "^1.0.5",
"number-precision": "^1.5.2",
"regenerator-runtime": "^0.12.1",
"remove-markdown": "^0.6.2",
"uview-ui": "2.0.36",
"vconsole": "^3.14.6",
"vue": "^2.6.11",
+131 -87
View File
@@ -22,12 +22,6 @@
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/user/RetrievePassword/index",
"style": {
"navigationBarTitleText": "重置密码"
}
},
{
"path": "pages/home/index",
"style": {
@@ -51,7 +45,7 @@
{
"path": "pages/home/newZone",
"style": {
"navigationBarTitleText": "新品专区",
"navigationBarTitleText": "甄选好礼",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
@@ -94,12 +88,6 @@
"navigationBarTitleText": "搜索"
}
},
{
"path": "pages/NotDefined/index",
"style": {
"navigationBarTitleText": "404"
}
},
{
"path": "pages/cloud/haveFun",
"style": {
@@ -140,18 +128,6 @@
"navigationBarTitleText": "商品评价"
}
},
{
"path": "pages/shop/GoodsPromotion/index",
"style": {
"navigationBarTitleText": "促销商品"
}
},
{
"path": "pages/shop/HotNewGoods/index",
"style": {
"navigationBarTitleText": "热门商品"
}
},
{
"path": "pages/shop/GoodsCon/index",
"style": {
@@ -185,24 +161,6 @@
"navigationBarTitleText": "收货地址"
}
},
{
"path": "pages/user/promotion/Poster/index",
"style": {
"navigationBarTitleText": "推广名片"
}
},
{
"path": "pages/user/signIn/Sign/index",
"style": {
"navigationBarTitleText": "签到"
}
},
{
"path": "pages/user/signIn/SignRecord/index",
"style": {
"navigationBarTitleText": "签到记录"
}
},
{
"path": "pages/user/promotion/CashAudit/index",
"style": {
@@ -255,12 +213,6 @@
"navigationBarTitleText": "我的积分"
}
},
{
"path": "pages/user/UserVip/index",
"style": {
"navigationBarTitleText": "用户vip"
}
},
{
"path": "pages/user/promotion/UserCash/index",
"style": {
@@ -283,7 +235,8 @@
"path": "pages/order/MyOrder/index",
"style": {
"navigationBarTitleText": "我的订单",
"navigationBarBackgroundColor": "#fff"
"navigationBarBackgroundColor": "#fff",
"navigationStyle": "custom"
}
},
{
@@ -314,7 +267,7 @@
{
"path": "pages/order/GoodsReturn/index",
"style": {
"navigationBarTitleText": "商品退货"
"navigationBarTitleText": "申请退款"
}
},
{
@@ -343,13 +296,6 @@
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "pages/user/MemberUpgradeIntro/MemberUpgradeIntro",
"style": {
"navigationBarTitleText": "升级说明",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "pages/VideoPlayBackList/VideoPlayBackList",
"style": {
@@ -364,31 +310,6 @@
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "pages/user/UpgradeElectronicProtocol/UpgradeElectronicProtocol",
"style": {
"navigationBarTitleText": "升级",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "pages/user/UpgradeProtocolDetail/UpgradeProtocolDetail",
"style": {
"navigationBarTitleText": "共享股东协议"
}
},
{
"path": "pages/LiveAndShortVideo/LiveAndShortVideo",
"style": {
"navigationBarTitleText": "直播/视频"
}
},
{
"path": "pages/LiveBroadcastList/LiveBroadcastList",
"style": {
"navigationBarTitleText": "直播列表"
}
},
{
"path": "pages/user/UserFavorite/UserFavorite",
"style": {
@@ -447,7 +368,8 @@
{
"path": "inn/innInfoDetail",
"style": {
"navigationBarTitleText": "资讯详情"
"navigationBarTitleText": "资讯详情",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
@@ -508,6 +430,13 @@
"navigationBarTitleText": "历史会话"
}
},
{
"path": "views/message",
"style": {
"navigationBarTitleText": "互动消息",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/xiaoZhi",
"style": {
@@ -542,6 +471,20 @@
"style": {
"navigationBarTitleText": "采购批发商入驻"
}
},
{
"path": "views/travelResidence",
"style": {
"navigationBarTitleText": "旅居商家入驻",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/experienceStore",
"style": {
"navigationBarTitleText": "体验店入驻",
"navigationBarBackgroundColor": "#FFFFFF"
}
}
]
},
@@ -597,7 +540,7 @@
"path": "views/healthFoods",
"style": {
"navigationBarTitleText": "康养旅居",
"navigationBarBackgroundColor": "#FAEED8"
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
@@ -690,6 +633,12 @@
"navigationStyle": "custom"
}
},
{
"path": "views/newsInfo",
"style": {
"navigationBarTitleText": "资讯详情"
}
},
{
"path": "views/famousQianxianTheme",
"style": {
@@ -711,6 +660,28 @@
"navigationStyle": "custom"
}
},
{
"path": "views/oldSojumStore",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "views/housekeeperDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "views/groupBuyDetails",
"style": {
"navigationBarTitleText": "团购详情",
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/famousQianxianInvestment",
"style": {
@@ -766,6 +737,58 @@
"style": {
"navigationBarTitleText": "时令尝鲜"
}
},
{
"path": "views/experienceCoupon",
"style": {
"navigationBarTitleText": "体验券详情",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/experienceCouponOrderDetail",
"style": {
"navigationBarTitleText": "订单核销",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/experienceProjectDetail",
"style": {
"navigationBarTitleText": "体验项目详情",
"navigationBarBackgroundColor": "#FFFFFF",
"navigationStyle": "custom"
}
},
{
"path": "views/storeLicense",
"style": {
"navigationBarTitleText": "营业执照",
"navigationBarBackgroundColor": "#FFFFFF",
"navigationStyle": "custom"
}
},
{
"path": "views/submitOrder",
"style": {
"navigationBarTitleText": "提交订单",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/sojoumOrderDetails",
"style": {
"navigationBarTitleText": "订单详情",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/sojoumOrderRefund",
"style": {
"navigationBarTitleText": "申请退款",
"navigationBarBackgroundColor": "#FFFFFF"
}
}
]
},
@@ -790,6 +813,13 @@
"navigationBarTitleText": "好友赠送的礼包"
}
},
{
"path": "views/gift/receiveDone",
"style": {
"navigationBarTitleText": "收礼完成",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/myFavorite",
"style": {
@@ -855,7 +885,7 @@
{
"path": "views/index",
"style": {
"navigationBarTitleText": "云灵AI智能助手",
"navigationBarTitleText": "云灵",
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
@@ -863,10 +893,24 @@
{
"path": "views/history",
"style": {
"navigationBarTitleText": "云灵AI智能助手",
"navigationBarTitleText": "云灵",
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "views/imageRecognition",
"style": {
"navigationBarTitleText": "拍照识图",
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "views/imageRecognitionResult",
"style": {
"navigationBarTitleText": "云灵"
}
}
]
},
+64 -9
View File
@@ -102,10 +102,13 @@
<view class="v12-primary-border v12-radius-8 v12-px-1">{{ item.couponName }}</view>
</view>
<view class="v12-justify-between v12-align-center">
<view class="v12-font-40 v12-font-bold v12-primary-text">
<view class="v12-font-40 v12-font-bold v12-primary-text" v-if="!item.isNegotiable">
<text class="">¥</text>
<text class="price-txt price-red">{{ item.price }}</text>
</view>
<view v-if="item.isNegotiable === 1" class="v12-primary-text v12-font-32 v12-font-weight" @clicks.stop="goRoom(item)">
价格面议
</view>
<view class="btn">
<image
:src="webUrl + '/home/icon-cart.png'"
@@ -157,11 +160,18 @@
>
<view class="v12-align-center">
<CheckboxIcon
v-if="good['truePrice'] !== null || isEdit"
:default-state="good['state']"
:mark="[index, gIndex]"
style="margin-right: 24rpx;"
@change="onChange"
/>
<CheckboxIcon
v-else
:default-state="good['state']"
:mark="[index, gIndex]"
style="margin-right: 24rpx;"
/>
<image
v-if="good['productInfo']['attrInfo']['image']"
:src="good['productInfo']['attrInfo']['image']"
@@ -224,7 +234,7 @@
</view>
</template>
<template v-slot:input>
<view class="num-input tc v12-num-input">{{ good['cartNum'] }}</view>
<input type="number" class="num-input tc v12-num-input" v-model="good['cartNum']" @change="e => inputChangeHandle(index, gIndex, good, e)"/>
</template>
<template v-slot:plus>
<view class="v12-plus-btn v12-font-bold flex jc-center ai-center v12-primary">
@@ -430,6 +440,7 @@ export default {
},
onShow() {
this.isEdit = false
uni.$emit('updateUnreadMessageCount')
if (!this.isLoad) {
return
}
@@ -448,6 +459,25 @@ export default {
this.pageToHideHandle()
},
methods: {
goRoom(item) {
return
console.log(item);
const params = {
goodsId: item.id,
goodsName: item.storeName,
skuStr: '',
price: item.price,
seckillId: '',
cover: item.image,
goodsData: '',
seckillData: '',
isNegotiable: item.isNegotiable
}
const name = ''
uni.navigateTo({
url: `/pkg_common/views/room?id=${this.storeInfo.merId}&name=${name}&params=${JSON.stringify(params)}`
})
},
toLoginHandle() {
cookie.set('redirect', '/pages/cart')
uni.reLaunch({
@@ -562,7 +592,7 @@ export default {
this.list = this.list.map(item => {
item.state = state
item.carts = item.carts.map(good => {
good.state = state
good.state = item.isInvalid && !this.isEdit ? false : state
return good
})
return item
@@ -612,7 +642,32 @@ export default {
})
this.isAll = shopCheckNumber === this.list.length
},
inputChangeHandle(index, gIndex, good, e) {
if(+good.cartNum > good['productInfo']['attrInfo']['stock']) {
good.cartNum = good['productInfo']['attrInfo']['stock']
}
if(good.cartNum < 1) {
good.cartNum = 1
}
const value = +good.cartNum
console.log('index', value);
const id = good.id
// uni.showLoading()
changeCartNum(id, value).then(res => {
const { success } = res
if (success) {
// uni.hideLoading()
this.$set(this.list[index].carts[gIndex], 'cartNum', value)
}
}).catch(() => {
setTimeout(() => {
uni.hideLoading()
}, 3000)
})
},
changeNumHandle(index, gIndex, good, e) {
console.log(good);
const { type } = e
const value = good.cartNum + (type === 'plus' ? 1 : -1)
console.log('index', value);
@@ -850,8 +905,8 @@ export default {
border-radius: 28rpx 0rpx 0rpx 28rpx;
border: 1px solid #E6E6E6;
background-color: rgba(208,208,208,0.39);
height: 40rpx;
width: 40rpx;
height: 44rpx;
width: 44rpx;
padding: 0 20rpx;
font-weight: bold;
}
@@ -859,8 +914,8 @@ export default {
border-radius: 0rpx 28rpx 28rpx 0rpx;
border: 1px solid #C52733;
background-color: #C52733;
height: 40rpx;
width: 40rpx;
height: 44rpx;
width: 44rpx;
padding: 0 20rpx;
font-weight: bold;
}
@@ -971,7 +1026,7 @@ view {
right: 0;
margin: auto;
border-radius: 26rpx;
z-index: 9;
.btn-submit {
width: 196rpx;
height: 64rpx;
@@ -1168,4 +1223,4 @@ view {
background-color: #C52733;
}
}
</style>
</style>
+4 -3
View File
@@ -341,10 +341,11 @@ export default {
},
async getMapData() {
const map = await getLocation()
if (map) {
if (map.length > 1) {
const {latitude, longitude} = map[1]
const address = await getCurAddress(latitude, longitude)
this.location = address[1].data.result.ad_info.city
getCurAddress(latitude, longitude, address => {
this.location = address.city
})
}
},
init() {
+11 -48
View File
@@ -74,7 +74,7 @@
</template>
<script>
import config from '@/utils/mapConfig'
import { getCurAddress, getLocation } from "@/utils/common"
import {
getHotelTypeList,
getHotelList
@@ -124,56 +124,19 @@ export default {
})
},
//获取当前定位地址
getCurAddress() {
async getCurAddressFn() {
const that = this
//定位
uni.showLoading({
title: '定位中...'
})
uni.getLocation({
type: 'wgs84',
success: function(res) {
let latitude = res.latitude
let longitude = res.longitude
// #ifdef H5
Vue.jsonp(
'https://apis.map.qq.com/ws/geocoder/v1/?location=' + latitude + ',' + longitude + '&key=' +
config.key, {
output: 'jsonp',
}).then(json => {
// Success.
uni.hideLoading();
that.cityName = json.result.ad_info.city
that.paramCity = json.result.ad_info.city
//定位成功刷新数据
that.fetchList()
}).catch(err => {
uni.hideLoading()
})
// #endif
// #ifndef H5
uni.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/?location=' + latitude + ',' + longitude +
'&key=' + config.key,
success: function(res) {
uni.hideLoading();
that.cityName = res.data.result.ad_info.city
that.paramCity = res.data.result.ad_info.city
//定位成功刷新数据
that.fetchList()
},
fail: function(res) {
uni.hideLoading()
}
})
// #endif
},
fail: function(res) {
uni.hideLoading()
}
})
const map = await getLocation()
if (map.length > 1) {
const {latitude, longitude} = map[1]
getCurAddress(latitude, longitude, address => {
that.cityName = address.city
that.paramCity = address.city
})
}
},
fetchTypeList() {
this.hotelList = []
@@ -195,7 +158,7 @@ export default {
this.fetchList()
uni.removeStorageSync('cityName')
} else {
this.getCurAddress()
this.getCurAddressFn()
}
}
})
+74 -18
View File
@@ -76,7 +76,7 @@
</view>
</view>
</view>
<view class="type-banner-wrap">
<view class="type-banner-wrap" v-if="typeIndex !== -1">
<view v-if="currType.titleImage" class="title">
<image
:src="currType.titleImage"
@@ -84,7 +84,7 @@
class="img"
/>
</view>
<view v-if="currType.carousel" class="slider-wrap">
<view v-if="hotelBanner.length" class="slider-wrap">
<swiper
indicatorDots="true"
indicator-color="rgba(255, 255, 255, .3)"
@@ -94,12 +94,12 @@
style="height: 330rpx;"
>
<block
v-for="(item, bannerIndex) in currType.carousel"
v-for="(item, bannerIndex) in hotelBanner"
:key="bannerIndex"
>
<swiper-item>
<view class="swiper-item" @click="typeBannerItemClick(item)">
<image :src="item.carouselImage" class="img" />
<image :src="item.bannerImage" class="img" />
</view>
</swiper-item>
</block>
@@ -120,9 +120,9 @@
</view>
</view>
<view class="body" v-else>
<view class="v12-justify-between v12-align-center">
<text class="v12-font-bold" v-if="currType.carousel">推荐</text>
<view class="v12-dark1-text v12-font-28 v12-align-center" @click="handleRefresh">
<view class="v12-justify-between v12-align-center" v-if="hotelList.length > 0">
<text class="v12-font-bold" v-if="hotelBanner.length">推荐</text>
<view class="v12-dark1-text v12-font-28 v12-align-center refresh-btn" @click="handleRefresh">
<u-icon name="reload"></u-icon>
换一换
</view>
@@ -163,22 +163,28 @@
/>
</view>
<FunctionGuide @hide="setGuide('findFunIndex')" :maxStep="3" :guideData="functionGuideData" ref="FunctionGuide"></FunctionGuide>
<AiEntrance
:city-name="cityName"
/>
<xdd-tabbar :curr-index="4" />
</view>
</template>
<script>
import { getHotelList, getHotelTypeList, getAncientTownIcon, getAncientTownList } from "@/api/inn.js"
import { getHotelList, getHotelTypeList, getAncientTownIcon, getAncientTownList, getHotelBanner } from "@/api/inn.js"
import { fetchCity } from "@/api/wenwan"
import { getCurAddress, getLocation } from "@/utils/common"
import FunHotelItem from './components/HotelItem'
import { pageListenMixins } from '@/mixins/pageListenMixins'
import XddTabbar from '@/components/xdd-tabbar'
import FunctionGuide from '@/components/FunctionGuide'
import AiEntrance from '@/components/aiChat/entrance'
import GuideMixins from '@/mixins/GuideMixins'
export default {
components: {
FunHotelItem,
XddTabbar,
FunctionGuide
FunctionGuide,
AiEntrance
},
mixins: [pageListenMixins, GuideMixins],
data() {
@@ -197,7 +203,10 @@ export default {
isNext: false,
townConfig: {},
townList: [],
randomSeed: null
randomSeed: null,
hotelBanner: [],
goodCityId: '',
cityOptions: []
}
},
computed: {
@@ -218,8 +227,12 @@ export default {
this.fetchList(random, this.randomSeed)
}
},
onLoad() {
onLoad(options) {
this.currType = {}
if (options && options.city) {
this.goodCityId = options.city
}
this.initGoodCity()
getHotelTypeList().then(({data}) => {
this.typeList = data
this.currType = this.typeIndex === -1 ? {} : data[this.typeIndex]
@@ -229,6 +242,7 @@ export default {
this.queryGuide('findFunIndex')
},
onShow() {
uni.$emit('updateUnreadMessageCount')
const memCityName = uni.getStorageSync('cityName')
// this.typeIndex = 0
this.name = ''
@@ -241,7 +255,8 @@ export default {
watch: {
cityName: {
handler(value) {
if(value) {
if (value) {
this.updateGoodCityId()
this.search()
}
},
@@ -440,6 +455,27 @@ export default {
this.townConfig = res.data
})
},
initGoodCity() {
fetchCity(2).then(({ data }) => {
const group = data && data.group ? data.group : []
const list = []
group.forEach(item => {
if (item.city && item.city.length) {
list.push(...item.city)
}
})
this.cityOptions = list
this.updateGoodCityId()
})
},
updateGoodCityId() {
if (!this.cityOptions.length || !this.cityName) return
const found = this.cityOptions.find(item => item.cityName === this.cityName)
if (found) {
this.goodCityId = found.id || found.cityId || this.goodCityId
this.fetchHotelBanner()
}
},
goCity() {
uni.navigateTo({
url: `/pages/chose-city/chose-city?type=2&city=${this.cityName}`
@@ -452,13 +488,11 @@ export default {
},
async getMapData() {
const map = await getLocation()
if (map.length > 1) {
if (map.length > 1 && !this.cityName) {
const {latitude, longitude} = map[1]
const address = await getCurAddress(latitude, longitude)
if(!this.cityName) {
this.cityName = address[1].data.result.ad_info.city
}
// this.search()
getCurAddress(latitude, longitude, address => {
this.cityName = address.city
})
}
},
tapType(index) {
@@ -528,6 +562,24 @@ export default {
uni.hideLoading()
})
},
fetchHotelBanner() {
if (!this.goodCityId) {
return
}
const params = {
goodCityId: this.goodCityId
}
getHotelBanner(params).then(res => {
const { success, data } = res
if (success && data) {
if (Array.isArray(data.hotelBanner)) {
this.hotelBanner = data.hotelBanner
} else if (Array.isArray(data)) {
this.hotelBanner = data
}
}
})
},
typeBannerItemClick(item) {
if (!item.hotelId) return
uni.navigateTo({ url: `/pagesInn/inn/innHome?id=${item.hotelId}` })
@@ -708,6 +760,10 @@ export default {
background: #f5f5f5;
}
.refresh-btn {
margin-left: auto;
}
.city-box {
display: inline-flex;
align-items: center;
+2 -2
View File
@@ -54,7 +54,7 @@
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt v12-font-28">{{ item.pv }}</text>
<text class="seeNum-txt v12-font-28">{{ $formatCount(item.pv) }}</text>
</view>
<view class="flex">
<image
@@ -73,7 +73,7 @@
v-show="item.zan > 0"
:class="item.hasZan ? 'on' : ''"
class="seeNum-txt v12-font-28 v12-ml-1"
>{{ item.zan }}</text
>{{ $formatCount(item.zan) }}</text
>
</view>
</view>
+256 -21
View File
@@ -45,6 +45,8 @@
autoplay
circular
style="height: 510rpx;"
:duration="500"
@animationfinish="bindSwiperInstance($event, 'banner')"
>
<block
v-for="(item, bannerIndex) in banner"
@@ -83,6 +85,17 @@
</view>
</scroll-view>
</view>
<view
v-if="hasExperienceCoupon && experienceCouponImage"
class="experience-coupon-banner"
@click="goExperienceCoupon"
>
<image
:src="experienceCouponImage"
mode="widthFix"
class="experience-coupon-img"
/>
</view>
<!-- 千县名品和地标好物 -->
<view
v-if="landmarkGoodsImage.image || countyFamousImage.image"
@@ -114,6 +127,8 @@
circular
autoplay
:interval="3000"
:duration="500"
@animationfinish="bindSwiperInstance($event, 'homestay')"
>
<swiper-item
v-for="(stay, stayIndex) in contentHomestay"
@@ -183,7 +198,24 @@
</swiper-item>
</swiper> -->
<view class="new-zone-wrap">
<view v-if="indexVideo" class="kd" id="xkd">
<image @click="toNewZone" class="bg-img" :src="newZone.indexBackgroundImage" mode="aspectFit|aspectFill|widthFix" lazy-load="false"></image>
<view class="swiper-wrap-zx v12-mt-2">
<swiper :key="newZoneSwiperKey" :autoplay="swiperInstances.newZone" @change="changeIndex" :current="currentIndex" next-margin="40rpx" previous-margin="80rpx" circular style="width: 100%;height: 400rpx;" interval="3000" :duration="500" @animationfinish="bindSwiperInstance($event, 'newZone')">
<swiper-item v-for="(item, index) in newZone.indexProducts" :key="index" class="s-item" :class="{'n-actived': currentIndex !== index}">
<view class="zx-item" @click="toNewZone">
<image :src="item.productImage" style="width: 192rpx; height: 192rpx; border-radius:10rpx"></image>
<view class="v12-pa-2">
<view class="v12-font-28 more-t v12-font-bold" style="text-align: center;">{{ item.productName }}</view>
<view class="v12-primary-text v12-font-bold price-wrap" style="text-align: center;">
<text class="v12-font-16"></text>
<text class="v12-font-24">{{ item.productPrice }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- <view v-if="indexVideo" class="kd" id="xkd">
<view class="img-wrap" style="padding-top: 10rpx; box-sizing:border-box">
<image style="width: 112rpx; height:26rpx" :src="webUrl + '/orderIcon/kd.png'" mode="heightFix|widthFix"></image>
</view>
@@ -215,7 +247,7 @@
</swiper-item>
</swiper>
</view>
</view>
</view> -->
</view>
</view>
</view>
@@ -284,6 +316,8 @@
circular
autoplay
:interval="3000"
:duration="500"
@animationfinish="bindSwiperInstance($event, 'productSwiper')"
>
<swiper-item
v-for="(swiperItem, swiperIndex) in item.list"
@@ -312,6 +346,7 @@
<!-- v9 老用户弹窗 -->
<u-popup
v-if="isOldUserPopupShow"
mode="center"
bgColor="transparent"
:show="isOldUser && oldUserPopup.image && lotteryCanDraw"
@@ -353,7 +388,9 @@
paddingBottom="200rpx"
/>
<FunctionGuide :maxStep="15" @hide="setGuide('index')" :guideData="functionGuideData" ref="FunctionGuide"></FunctionGuide>
<AiEntrance />
<AiEntrance
:city-name="cityName"
/>
<xdd-tabbar :curr-index="0" @getElementData="elementData" @getIcon="getIcon"/>
</view>
</template>
@@ -362,7 +399,7 @@
import XddTabbar from '@/components/xdd-tabbar'
import XddProductItem from '@/components/xdd-product-item'
import { getCouponReceive } from '@/api/user'
import { getHomeData, getShareImage } from '@/api/public'
import { getHomeData } from '@/api/public'
import { sharpBannar } from '@/api/goods'
import { pageListenMixins } from '@/mixins/pageListenMixins'
import ProductWindow from '@/components/ProductWindow'
@@ -379,6 +416,7 @@ import {
getLocationSetting,
openLocationSettting
} from "@/utils/common"
import { getWeixinShareConfig } from '@/api/public'
export default {
name: 'IndexPage',
components: {
@@ -391,6 +429,8 @@ export default {
mixins: [pageListenMixins, goCartMixin, GuideMixins],
data() {
return {
isFirstLoad: true,
isOldUserPopupShow: 0,
webUrl: this.$VUE_APP_RESOURCES_URL,
// 状态栏高度
statusBarHeight: 20,
@@ -411,8 +451,10 @@ export default {
bastRightList: [],
// 尖货推荐横幅
bannerList: [],
// 分享图片
shareAppImg: '',
// 体验券
hasExperienceCoupon: 0,
hasUnusedExperienceCoupon: 0,
experienceCouponImage: '',
// 老用户弹窗
isOldUser: true,
oldUserPopup: {},
@@ -431,10 +473,23 @@ export default {
tabIconPostion: {},
messageIcon: {},
recommendKeyword: [],
keywordIndex: 0
keywordIndex: 0,
shareData: {},
currentIndex: 0,
newZoneSwiperKey: 0,
newZoneResumeTimer: null,
// 轮播组件实例
swiperInstances: {
productSwiper: true,
homestay: true,
banner: true,
festival: true,
newZone: true,
}
}
},
onLoad() {
console.time()
// uni.hideTabBar()
const _this = this
_this.init()
@@ -452,21 +507,89 @@ export default {
_this.statusBarHeight = statusBar
}
})
getWeixinShareConfig().then(res => {
const { success, data } = res
if (success) {
this.shareData = data
}
})
},
onShow() {
this.pageToShowHandle()
uni.$emit('updateUnreadMessageCount')
if (!this.isFirstLoad) {
this.refreshPageData()
}
this.isFirstLoad = false
},
onHide() {
this.pageToHideHandle()
},
onShareAppMessage() {
return {
title: '探寻有趣有味的生活方式',
path: '/pages/home/index',
imageUrl: this.shareAppImg
}
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
onShareTimeline() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return {
title: this.shareData.title || this.share.title,
query: '',
imageUrl: this.shareData.image || this.share.imageUrl
}
},
methods: {
changeIndex(e) {
this.currentIndex = e.detail.current
},
// 页面显示时恢复轮播
pageToShowHandle() {
// 恢复所有轮播组件的自动播放,甄选好礼轮播先停后启,避免 iOS 熄屏恢复后卡顿
if (this.newZoneResumeTimer) {
clearTimeout(this.newZoneResumeTimer)
this.newZoneResumeTimer = null
}
this.swiperInstances = {
productSwiper: true,
homestay: true,
banner: true,
festival: true,
newZone: false,
}
this.currentIndex = 0
this.newZoneSwiperKey += 1
this.$nextTick(() => {
this.newZoneResumeTimer = setTimeout(() => {
this.swiperInstances.newZone = true
this.newZoneResumeTimer = null
}, 120)
})
},
// 页面隐藏时暂停轮播
pageToHideHandle() {
if (this.newZoneResumeTimer) {
clearTimeout(this.newZoneResumeTimer)
this.newZoneResumeTimer = null
}
this.swiperInstances = {
productSwiper: false,
homestay: false,
banner: false,
festival: false,
newZone: false,
}
},
// 绑定轮播组件实例
bindSwiperInstance(e, key) {
// this.swiperInstances[key] = e.detail
},
resetNewZoneCurrentIndex() {
const products = (this.newZone && this.newZone.indexProducts) || []
if (!products.length || this.currentIndex >= products.length) {
this.currentIndex = 0
}
},
changeRecommendKeyword(e) {
this.keywordIndex = e
},
@@ -1303,8 +1426,16 @@ export default {
toNewZone() {
this.$yrouter.push('/pages/home/newZone')
},
goExperienceCoupon() {
if (this.hasUnusedExperienceCoupon === 0) {
uni.navigateTo({
url: '/pages/order/MyOrder/index?type=3&tabIndex=1'
})
return
}
this.$yrouter.push('/pkg_product/views/experienceCoupon')
},
init() {
getShareImage().then(({ data }) => this.shareAppImg = data)
sharpBannar().then(res => {
const { data } = res
this.bannerList = data.banner || []
@@ -1323,12 +1454,12 @@ export default {
getLocationPromise().then(mapData => {
if (mapData.errMsg) {
const { latitude, longitude } = mapData
getCurAddress(latitude, longitude).then(address => {
this.cityName = address[1].data.result.ad_info.city
getCurAddress(latitude, longitude, address => {
console.log(address)
this.cityName = address.city
uni.setStorageSync('locationCityName', this.cityName)
this.isLocating = false
this.getPageData()
}).catch(() => {
this.getPageData()
})
} else {
this.isLocating = false
@@ -1352,6 +1483,11 @@ export default {
_this.$set(_this, 'contentHomestay', data.contentHomestayV2 || [])
_this.$set(_this, 'contentFestival', data.contentFestivalV2 || [])
_this.$set(_this, 'newZone', data.newGoods || [])
_this.resetNewZoneCurrentIndex()
_this.$set(_this, 'hasExperienceCoupon', data.hasExperienceCoupon || 0)
_this.$set(_this, 'hasUnusedExperienceCoupon', data.hasUnusedExperienceCoupon || 0)
_this.$set(_this, 'experienceCouponImage', data.experienceCouponImage || '')
_this.$set(_this, 'isOldUserPopupShow', data.isOldUserPopupShow || 0)
_this.indexVideo = data.indexVideo ? data.indexVideo.videoUrl : ''
_this.bastLeftList = []
_this.bastRightList = []
@@ -1380,12 +1516,63 @@ export default {
}
}
}
this.$nextTick(() => {
console.timeEnd()
})
}
uni.hideLoading()
}).catch(() => {
uni.hideLoading()
})
},
refreshPageData() {
getHomeData({ cityName: this.cityName }).then(res => {
const { success, data } = res
if (success && data) {
this.$set(this, 'banner', data.banner || [])
this.$set(this, 'menus', data.menus || [])
this.$set(this, 'landmarkGoodsImage', data.landmarkGoodsImage || {})
this.$set(this, 'countyFamousImage', data.countyFamousImage || {})
this.$set(this, 'oldUserPopup', data.oldUserPopup || {})
this.$set(this, 'contentHomestay', data.contentHomestayV2 || [])
this.$set(this, 'contentFestival', data.contentFestivalV2 || [])
this.$set(this, 'newZone', data.newGoods || [])
this.resetNewZoneCurrentIndex()
this.$set(this, 'hasExperienceCoupon', data.hasExperienceCoupon || 0)
this.$set(this, 'hasUnusedExperienceCoupon', data.hasUnusedExperienceCoupon || 0)
this.$set(this, 'experienceCouponImage', data.experienceCouponImage || '')
this.$set(this, 'isOldUserPopupShow', data.isOldUserPopupShow || 0)
this.indexVideo = data.indexVideo ? data.indexVideo.videoUrl : ''
this.lotteryCanDraw = data.lotteryCanDraw || false
if (data.bastList) {
const newBastLeftList = []
const newBastRightList = []
for (let i = 0; i < data.bastList.length; i++) {
if (i % 2 === 0) {
if (i === 4 && data.hotelList.length > 0) {
newBastLeftList.push({
type: 'hotel',
list: data.hotelList
})
}
newBastLeftList.push(data.bastList[i])
}
if (i % 2 === 1) {
if (i === 3 && data.contentBest.length > 0) {
newBastRightList.push({
type: 'swiper',
list: data.contentBest
})
}
newBastRightList.push(data.bastList[i])
}
}
this.bastLeftList = newBastLeftList
this.bastRightList = newBastRightList
}
}
}).catch(() => {})
},
goRoll(item) {
if (item.uniapp_url) {
this.$yrouter.push(item.uniapp_url)
@@ -1488,10 +1675,16 @@ export default {
success: () => {
}
})
} else {
uni.showToast({
title: res.msg + '',
icon: 'none',
duration: 3000
})
}
}).catch((err) => {
uni.showToast({
title: err.data.msg + '',
title: err.msg + '',
icon: 'none',
duration: 3000
})
@@ -1532,6 +1725,30 @@ export default {
</script>
<style scoped lang="less">
.n-actived{
// bottom: 20rpx !important;
.zx-item {
transform: scale(0.8);
}
}
.s-item{
height: fit-content !important;
bottom: 10rpx;
}
.zx-item{
width: 192rpx;
background: #fff;
border-radius: 16rpx;
transition: all 0.3s ease-in-out;
}
.swiper-wrap-zx{
position: absolute;
bottom: 20rpx;
z-index: 2;
/* height: 50%; */
width: 100%;
left: 0;
}
.swiper-item {
height: 100%;
}
@@ -1694,6 +1911,16 @@ export default {
}
}
}
.experience-coupon-banner {
position: relative;
z-index: 5;
padding-top: 16rpx;
.experience-coupon-img {
display: block;
width: 100%;
border-radius: 20rpx;
}
}
.hotel-list-wrap {
position: relative;
width: 344rpx;
@@ -1815,6 +2042,14 @@ export default {
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
.bg-img{
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
}
.kd{
width: 100%;
height: 266rpx;
@@ -1887,4 +2122,4 @@ export default {
.price-wrap{
}
</style>
</style>
+97 -9
View File
@@ -11,7 +11,7 @@
</swiper-item>
</swiper> -->
<view class="swiper-wrap">
<swiper @change="handleChange" style="height: 100%;" autoplay circular next-margin="460rpx" interval="4000">
<swiper @change="handleChange" style="height: 100%;" autoplay circular next-margin="460rpx" interval="4000">
<swiper-item v-for="(item, index) in details.newProducts" :key="index" @click="toGoods(item)">
<view class="new-card" :class="{'is-actived': current == index}">
<image :src="item.productCover" class="new-card-img" mode="heightFix|widthFix"></image>
@@ -29,8 +29,39 @@
</swiper>
</view>
</view>
<view class="v12-mt-2 v12-align-center">
<view class="v12-align-center tabs-wrap v1-py-1 v12-my-4">
<view class="tab-item v12-font-bold" v-for="(tab, index) in tabs" :key="index" @click="handleClick(index, tab)">
{{ tab.cateName }}
<view class="tab-line" v-if="activeIndex == index"></view>
</view>
</view>
<view class="product-wrap v12-mt-3">
<view class="product-card" v-for="(item, index) in products" :key="index" @click="toGoods(item)">
<image :src="item.productImage" class="product-card-img" mode="heightFix|widthFix"></image>
<view class="new-info">
<view class="v12-font-26 more-t" style="width: 100%;">{{ item.productName }}</view>
<view style="width: 100%;" class="v12-font-bold v12-primary-text v12-align-center v12-justify-between">
<view>
<text></text>
<text class=" v12-font-36">{{ item.productPrice }}</text>
</view>
<view @click.stop="addToCart(item, 'productId')">
<u-icon :name="webUrl + '/home/icon-cart.png'" size="24"></u-icon>
</view>
</view>
</view>
</view>
</view>
<ProductWindow
ref="attrWindow"
:attr="attr"
:cartNum="cart_num"
:showOk="true"
@changeFun="changeFun"
@ok="handleOk(() => this.getCartCount())"
/>
<!-- <view class="v12-mt-2 v12-align-center">
<view class="primary-title" v-if="details.contentTitle">
{{ details.contentTitle }}
<view class="title-line"></view>
@@ -42,30 +73,47 @@
</view>
<view class="cover-img v12-mt-2" v-for="(item, index) in details.contents" :key="index" @click="toGoods(item)">
<image :src="item.contentImage" class="cover-img" mode="heightFix|widthFix"></image>
</view>
</view> -->
</view>
</template>
<script>
import { queryNewZone } from '@/api/activity'
import ProductWindow from '@/components/ProductWindow'
import goCartMixin from '@/mixins/goCartMixins'
import { mapGetters } from 'vuex'
export default{
components: {
ProductWindow
},
mixins: [goCartMixin],
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
details: {},
current: 0,
tabs: [],
activeIndex: 0,
products: []
}
},
onLoad() {
queryNewZone().then(res => {
this.details = res.data
this.tabs = res.data.categories
this.handleClick(0, this.tabs[0])
})
},
computed: {
...mapGetters(['isLogin', 'location', 'userInfo']),
bannerImage() {
return this.details.newProducts && this.details.newProducts[this.current] && this.details.newProducts[this.current].bannerImage
}
},
methods: {
handleClick(index, tab) {
this.activeIndex = index
this.products = tab.products
},
handleChange(e) {
this.current = e.detail.current
},
@@ -78,10 +126,50 @@ export default{
}
</script>
<style lang="scss" scoped>
.product-card{
width: 342rpx;
height: 520rpx;
background: #fff;
border-radius: 20rpx;
margin-bottom: 20rpx;
}
.product-card-img{
width: 344rpx;
height: 344rpx;
background: rgba(169,56,56,0.39);
border-radius: 20rpx;
}
.product-wrap{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.tab-line{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 6rpx;
background: linear-gradient(to right, #C52733 0%, rgba(202,56,67,0.92) 51%, rgba(255,255,255,0) 100%);
border-radius: 6rpx;
}
.tabs-wrap{
overflow: auto;
justify-content: space-between;
}
.tab-item{
margin-right: 20rpx;
white-space: nowrap;
position: relative;
&:last-child{
margin-right: 0;
}
}
.new-info{
display: flex;
align-items: center;
flex-direction: column;
padding: 20rpx;
}
.box-emp{
width: 100%;
@@ -92,7 +180,7 @@ export default{
width: fit-content;
color: #fff;
background: #C52733;
font-size: 28rpx;
font-size: 30rpx;
padding: 4rpx 20rpx;
}
.swiper-wrap{
@@ -104,7 +192,7 @@ export default{
}
.new-card{
width: 220rpx;
height: 240rpx;
// height: 240rpx;
background: #fff;
border-radius: 20rpx;
position: absolute;
@@ -160,7 +248,7 @@ export default{
}
.new-zone-page{
min-height: 100vh;
background-color: #fff;
// background-color: #fff;
padding: 20rpx;
}
.banner-zone{
@@ -169,4 +257,4 @@ export default{
border-radius: 20rpx !important;
position: relative !important;
}
</style>
</style>
+7 -5
View File
@@ -2,11 +2,12 @@
<view class="apply-return">
<view class="goodsStyle acea-row row-between" v-for="cart in orderInfo.cartInfo" :key="cart.id">
<view class="pictrue">
<image :src="cart.productInfo.image" class="image" />
<image v-if="orderInfo.isGiftCardReceiveBlind === 0" :src="cart.productInfo.image" class="image" />
<image v-else class="image" :src="webUrl+'/orderIcon/盲盒礼包.png'"></image>
</view>
<view class="text acea-row row-between">
<view class="name line2">{{ cart.productInfo.storeName }}</view>
<view class="money">
<view class="text acea-row row-between" >
<view class="name line2">{{ orderInfo.isGiftCardReceiveBlind === 0 ? cart.productInfo.storeName : '盲盒礼包' }}</view>
<view class="money" v-if="orderInfo.isGiftCardReceiveBlind === 0">
<view>
{{
cart.productInfo.attrInfo
@@ -23,7 +24,7 @@
<view>退货件数</view>
<view class="num">{{ orderInfo.totalNum }}</view>
</view>
<view class="item acea-row row-between-wrapper">
<view class="item acea-row row-between-wrapper" v-if="orderInfo.isGiftCardReceiveBlind === 0">
<view>退款金额</view>
<view class="num">{{ orderInfo.payPrice }}</view>
</view>
@@ -65,6 +66,7 @@ export default {
mixins: [pageListenMixins],
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
url: `${VUE_APP_API_URL}/upload/image`,
headers: {
Authorization: "Bearer " + this.$store.state.token
File diff suppressed because it is too large Load Diff
+348 -102
View File
@@ -1,16 +1,33 @@
<template>
<view class="order-details" style="padding: 30rpx;" @click="handleBody">
<view class="order-details" style="padding: 30rpx;" @click="handleBody" v-if="!loading && orderInfo.isTickets !== undefined">
<!-- 给header上与data上加on为退款订单-->
<view class=" v12-pa-3 v12-white" :class="refundOrder ? 'on' : ''" style="border-radius: 20rpx;">
<view class=" v12-pa-3 v12-white" :class="refundOrder ? 'on' : ''" style="border-radius: 20rpx;" v-if="!isGift">
<!-- <view class="status-img-box">
<image class="status-img" :src="getStatusImg(orderInfo)" mode=""></image>
</view> -->
<view :class="refundOrder ? 'on' : ''">
<view class="state v12-primary-text v12-font-32 v12-font-bold" v-if="orderInfo.isTickets===1 && orderInfo._status._type == 2">待核销</view>
<view class="state v12-primary-text v12-font-32 v12-font-bold" v-else>{{ getStatus(orderInfo) }}</view>
<view v-if="getStatus(orderInfo)!=='待付款'" class="v12-mt-2 v12-font-24 v12-secondary-dark-text" :class="{'fails-bg': orderInfo._status._type == -3}">
<view
v-if="isGroup && orderInfo._status._type === 2"
class="state v12-primary-text v12-font-32 v12-font-bold"
>待使用</view>
<view
v-else-if="orderInfo.isTickets === 1 && orderInfo._status._type === 2"
class="state v12-primary-text v12-font-32 v12-font-bold"
>待核销</view>
<view
v-else
class="state v12-primary-text v12-font-32 v12-font-bold"
>{{ getStatus(orderInfo) }}</view>
<view v-if="isGroup && orderInfo._status._type === 2" class="v12-mt-2 v12-font-24 v12-secondary-dark-text">
您已下单成功请联系商家确认具体的入住时间
</view>
<view
v-if="getStatus(orderInfo)!=='待付款'"
:class="{'fails-bg': orderInfo._status._type === -3}"
class="v12-mt-2 v12-font-24 v12-secondary-dark-text"
>
<view>{{ orderInfo._status._msg }}</view>
<view v-if="orderInfo._status._type == -3">驳回原因: {{ orderInfo.refundRejectReason }}</view>
<view v-if="orderInfo._status._type === -3">驳回原因: {{ orderInfo.refundRejectReason }}</view>
</view>
<view class="pending-payments flex ai-center v12-mt-2 v12-font-24 v12-secondary-dark-text" v-else>
<text>需支付{{ orderInfo.payPrice }}</text>
@@ -18,20 +35,73 @@
剩余<u-count-down format="HH:mm:ss" autoStart :time="$global.diffSSS(orderInfo.paymentTimeout)" />订单将自动取消
</view>
<u-divider></u-divider>
<div class="name v12-mb-3 v12-font-28 v12-font-bold">
<text class="v12-mr-3"> {{ orderInfo.realName }}</text>
<text class="phone">{{ orderInfo.userPhone }}</text>
</div>
<div class="v12-font-28 v12-secondary-dark-text v12-justify-between">
<text class=" more-t" style="flex: 2;">{{ orderInfo.userAddress }}</text>
<view v-if="getStatus(orderInfo) === '待付款'" @click="goAddress" class="v12-btn cancel v12-dark-text v12-dark-border v12-radius-40" >修改地址</view>
</div>
<template v-if="Number(orderInfo.isAddressUpdate) === 1 && Number(orderInfo.addressUpdateStatus) === 0 && !isrefund">
<div class="name v12-mb-3 v12-font-28 v12-font-bold flex ai-center">
<text class="v12-mr-3"> {{ orderInfo.oldRealName }}</text>
<text class="phone">{{ orderInfo.oldUserPhone }}</text>
<text class="v12-font-22 v12-px-2 v12-ml-2 v12-primary-text v12-primary-border" style="border-radius: 8rpx; padding: 2rpx 10rpx;">原地址</text>
</div>
<div class="v12-font-28 v12-secondary-dark-text v12-justify-between">
<text class=" more-t" style="flex: 2;">{{ orderInfo.oldUserAddress }}</text>
</div>
<div class="name v12-mb-3 v12-mt-4 v12-font-28 v12-font-bold flex ai-center">
<text class="v12-mr-3"> {{ orderInfo.realName }}</text>
<text class="phone">{{ orderInfo.userPhone }}</text>
<text class="v12-font-22 v12-px-2 v12-ml-2 v12-primary-text v12-primary-border" style="border-radius: 8rpx; padding: 2rpx 10rpx;">新地址</text>
</div>
<div class="v12-font-28 v12-secondary-dark-text v12-justify-between">
<text class=" more-t" style="flex: 2;">{{ orderInfo.userAddress }}</text>
</div>
</template>
<template v-else-if="Number(orderInfo.isAddressUpdate) === 1 && Number(orderInfo.addressUpdateStatus) === 2 && isrefund">
<div class="name v12-mb-3 v12-font-28 v12-font-bold flex ai-center">
<text class="v12-mr-3"> {{ orderInfo.oldRealName }}</text>
<text class="phone">{{ orderInfo.oldUserPhone }}</text>
</div>
<div class="v12-font-28 v12-secondary-dark-text v12-justify-between">
<text class=" more-t" style="flex: 2;">{{ orderInfo.oldUserAddress }}</text>
</div>
</template>
<template v-else>
<div class="name v12-mb-3 v12-font-28 v12-font-bold">
<text class="v12-mr-3"> {{ orderInfo.realName }}</text>
<text class="phone">{{ orderInfo.userPhone }}</text>
</div>
<div class="v12-font-28 v12-secondary-dark-text v12-justify-between">
<text class=" more-t" style="flex: 2;">{{ orderInfo.userAddress }}</text>
<view v-if="canShowModifyAddress(orderInfo)" @click="goAddress" class="v12-btn cancel v12-dark-text v12-dark-border v12-radius-40" >修改地址</view>
</div>
</template>
<view
v-if="!isrefund && Number(orderInfo.isAddressUpdate) === 1 && [0, 2].includes(Number(orderInfo.addressUpdateStatus))"
class="v12-mt-3"
>
<view
v-if="Number(orderInfo.addressUpdateStatus) === 0"
class="v12-font-24 v12-primary-text"
style="background: rgba(246,246,246,0.7); border-radius: 40rpx; padding: 12rpx;"
>修改地址正在审核中请耐心等待</view>
<view
v-if="Number(orderInfo.addressUpdateStatus) === 2"
class="v12-font-24 v12-primary-text"
style="background: rgba(246,246,246,0.7); border-radius: 40rpx; padding: 12rpx;"
>修改地址审核未通过商家将按原地址发货<text v-if="orderInfo.isFastPost !== 0">预计{{ getTime(orderInfo.isFastPost) }}发货</text>
(驳回原因{{ orderInfo.addressUpdateReason }})</view>
</view>
</view>
</view>
<!-- <view class="reject-reason" v-if="orderInfo._status._type==-3 && orderInfo.refundRejectReason">驳回原因{{ orderInfo.refundRejectReason }}</view> -->
<view v-if="orderInfo.deliveryId !== null && (orderInfo._status._type == -1 || orderInfo._status._type == -2 || orderInfo._status._type == 4 || orderInfo._status._type == 3 || (orderInfo._status._type == 2 && orderInfo.isTickets!==1))">
<view v-if="index < 2 || showMoreDeliver" v-for="(delivery, index) in orderInfo.deliveryInfo" :key="index" class="v12-radius-20 v12-white v12-pa-3 v12-mt-4">
<view>
<view v-if="orderInfo.deliveryId !== null && ([-1, -2, 3, 4].includes(orderInfo._status._type) || (orderInfo._status._type == 2 && orderInfo.isTickets !== 1))">
<view
v-for="(delivery, index) in orderInfo.deliveryInfo"
:key="index"
:class="{
'order-deliver-item-show': index < 2 || showMoreDeliver
}"
class="v12-radius-20 v12-white v12-pa-3 v12-mt-4"
>
<view class="v12-align-center">
<text class="v12-secondary-dark-text v12-font-28">{{ delivery.deliveryName }}{{ delivery.deliveryId }}</text>
<text class="v12-font-22 v12-px-2 v12-py-1 v12-ml-1 v12-primary-text v12-primary-border v12-radius-40" @click="copyClipboard(delivery.deliveryId)">
复制
@@ -48,8 +118,18 @@
<uni-icons type="right" size="20" color="#707070"></uni-icons>
</view>
</view>
<view class="v12-mt-2">
<view class="v12-mt-2 time-wrap">
<text class="v12-dark-text v12-font-24">{{ logistics[index] && logistics[index].Traces && logistics[index].Traces.length > 0 ? logistics[index] && logistics[index].Traces[logistics[index].Traces.length - 1].acceptTime : '' }}</text>
<view
v-if="delivery.estimatedTime"
:style="{
color: '#297EE0;',
}"
class="v12-font-26 time-box"
>
<image :src="webUrl+'/orderIcon/787.png'" class="time-bg"></image>
{{ delivery.estimatedTime }}
</view>
</view>
</view>
<view class="v12-justify-center v12-mt-3" v-if="orderInfo.deliveryInfo.length > 2">
@@ -57,12 +137,23 @@
</view>
</view>
<template v-if="!refundOrder">
<view class="code-box flex jc-center ai-center" v-if="orderInfo.isTickets===1 && orderInfo._status._type==2">
<view
v-if="orderInfo.isTickets === 1 && orderInfo._status._type === 2"
class="code-box flex jc-center ai-center"
>
<view v-if="isGroup && orderInfo.verifyCode" class="v12-primary-text v12-font-28 v12-mb-2">核销码{{ orderInfo.verifyCode }}</view>
<image class="code-img" :src="orderInfo.code"/>
<view class="v12-justify-center v12-align-center v12-mt-2" v-if="isGroup">
<view class="bnt v12-dark-text v12-dark-border cancel v12-mx-1" @click="call(system_store.phone)">商家电话</view>
<view class="bnt v12-dark-text v12-dark-border cancel v12-mx-1" @click="showMerchantWechat">商家微信</view>
</view>
</view>
<template v-else>
<div class="writeOff" v-if="orderInfo.shippingType === 2 && orderInfo.paid === 1"
style="margin-top: 30rpx;border-radius: 24rpx;">
<div
v-if="orderInfo.shippingType === 2 && orderInfo.paid === 1"
class="writeOff"
style="margin-top: 30rpx;border-radius: 24rpx;"
>
<div class="title">核销信息</div>
<div class="grayBg">
<div class="pictrue">
@@ -111,8 +202,8 @@
<view style="margin-top: 30rpx;border-radius:20rpx;overflow: hidden;" class="v12-white">
<!-- <OrderGoods :evaluate="status.type || 0" :cartInfo="orderInfo.cartInfo || []" title="商品信息"></OrderGoods> -->
<view class="v12-radius-20 v12-pa-3" v-for="(cart, d) in orderInfo.cartInfo" :key="d" @click="goGoodsCon(cart)">
<view class="v12-justify-between">
<view class="v12-radius-20 v12-pa-3" v-for="(cart, d) in orderInfo.cartInfo" :key="d" @click="goGoodsCon(cart, orderInfo.isGiftCardReceiveBlind)">
<view class="v12-justify-between" v-if="orderInfo.isGiftCardReceiveBlind === 0">
<view class="">
<image style="width: 120rpx; height: 120rpx" class="v12-radius-8" :src="cart.productInfo.image"></image>
</view>
@@ -136,8 +227,21 @@
</view>
</view>
</view>
<view class="v12-justify-between" v-else>
<view class="">
<image style="width: 120rpx; height: 120rpx" class="v12-radius-8" :src="webUrl+'/orderIcon/盲盒礼包.png'"></image>
</view>
<view class="v12-justify-between v12-flex-column v12-ml-2 v12-py-2" style="flex: 2">
<view class="v12-font-28 v12-dark-text v12-font-bold more-t">
盲盒礼物
</view>
<view class="more-t v12-secondary-dark-text v12-font-24">
待收货后展示商品信息
</view>
</view>
</view>
<view class="" v-if="orderInfo.cartInfo &&( d === orderInfo.cartInfo.length - 1)">
<view class="v12-justify-between v12-mt-3 v12-dark-text">
<view class="v12-justify-between v12-align-center v12-mt-3 v12-dark-text" v-if="orderInfo.isGiftCardReceiveBlind === 0">
<view class="v12-font-24 v12-dark-text">
商品总价
</view>
@@ -148,7 +252,7 @@
</text>
</view>
</view>
<view class="v12-justify-between v12-mt-3" v-if="orderInfo.payPostage > 0">
<view class="v12-justify-between v12-align-center v12-mt-3" v-if="orderInfo.payPostage > 0">
<view class="v12-font-24 v12-dark-text">
运费
</view>
@@ -159,7 +263,7 @@
</text>
</view>
</view>
<view class="v12-justify-between v12-mt-3" v-if="orderInfo.couponPrice > 0">
<view class="v12-justify-between v12-align-center v12-mt-3" v-if="orderInfo.couponPrice > 0">
<view class="v12-font-24 v12-dark-text">
满减优惠
</view>
@@ -171,7 +275,19 @@
</text>
</view>
</view>
<view class="v12-justify-between v12-mt-3" v-if="orderInfo.useIntegral > 0">
<view class="v12-justify-between v12-align-center v12-mt-3" v-if="orderInfo.discountPrice > 0 && orderInfo.isGiftCardReceiveBlind === 0">
<view class="v12-font-24 v12-dark-text">
商品折扣
</view>
<view class="v12-text-right">
-
<text class="v12-font-22"></text>
<text class="v12-font-28">
{{ force2Decimal(orderInfo.discountPrice) }}
</text>
</view>
</view>
<view class="v12-justify-between v12-align-center v12-mt-3" v-if="orderInfo.useIntegral > 0">
<view class="v12-font-24 v12-dark-text">
积分抵扣
</view>
@@ -184,7 +300,7 @@
</view>
</view>
<u-divider></u-divider>
<view class="v12-justify-between v12-mt-3">
<view class="v12-justify-between v12-align-center v12-mt-3" v-if="orderInfo.isGiftCardReceiveBlind === 0">
<view class="v12-font-28 v12-font-bold v12-dark-text">
实付款
</view>
@@ -316,6 +432,29 @@
<text class="money font-color-red">{{ force2Decimal(orderInfo.payPrice) }}</text>
</view>
</view>
<view
v-if="isGift"
class="gift-wrap"
>
<view class="v12-justify-between v12-align-center">
<view class="v12-font-28 v12-font-bold v12-align-center">
<u-icon :name="webUrl + '/icon/gift-primary.png'" size="24"></u-icon>
{{ `已领取${orderInfo.giftCardInfo.receiveNum}/${orderInfo.giftCardInfo.giftNum}份礼包` }}
</view>
<view
v-if="orderInfo.giftCardInfo.refundNum > 0"
class="v12-font-bold v12-primary-text v12-font-24"
>{{ `剩余${orderInfo.giftCardInfo.refundNum}份已退回` }}</view>
</view>
<view
v-for="(receive, index) in orderInfo.giftCardInfo.receiveInfo"
:key="index"
class="v12-mt-3 v12-justify-between v12-align-center"
>
<view class="v12-font-24">{{ receive.userNickname }}</view>
<view class="v12-font-24">{{ receive.receiveStatus ? '已领取' : '未领取' }}</view>
</view>
</view>
<view style="height:100rpx;" v-if="!refundOrder && offlineStatus"></view>
<view class="footer acea-row row-right row-middle" v-if="!refundOrder && offlineStatus">
<template v-if="status.type == 0">
@@ -342,13 +481,18 @@
</template>
<template v-if="status.type == 1" >
<view class="v12-justify-between " style="width: 100%">
<view class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="v12-justify-between ">
<view v-if="orderInfo.isGiftCardReceiveBlind === 0" class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="bnt v12-dark-text v12-dark-border cancel" @click="delayOrder">催发货</view>
<view v-if="canShowModifyAddress(orderInfo)" class="bnt v12-dark-text v12-dark-border cancel" @click="goAddress">修改地址</view>
</view>
<view class="bnt v12-primary-text v12-primary-border cancel" @click="goGoodsReturn2(orderInfo)">申请退款</view>
</view>
</template>
<template v-if="status.type == 2">
<view class="v12-justify-between " style="width: 100%">
<view class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view v-if="orderInfo.isGiftCardReceiveBlind === 0" class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="" v-else></view>
<view class="v12-justify-between">
<view class="btn-more" style="position: relative">
<view class=" v12-font-24 v12-mr-2" @click.stop="showMore = !showMore">更多</view>
@@ -426,7 +570,7 @@
:src="'https://apis.map.qq.com/uri/v1/geocoder?coord=' + system_store.latitude + ',' +system_store.longitude +'&referer=' +mapKey"></iframe>
</view>
<passkeyborad :money="orderInfo.payPrice" showMoney :show="isShowPayPwdPop" ref='payPwdPop' @close="pwdPopClose"
<passkeyborad v-if="isShowPayPwdPop" :money="orderInfo.payPrice" showMoney :show="isShowPayPwdPop" ref='payPwdPop' @close="pwdPopClose"
@finish="pwdPopFinish"></passkeyborad>
</view>
@@ -435,7 +579,7 @@
<script>
import passkeyborad from '@/components/yzc-paykeyboard/yzc-paykeyboard'
import OrderGoods from "@/components/OrderGoods";
import {orderDetail, orderDelay, aginConfirm, express, editAddress} from "@/api/order";
import {orderDetail, orderDelay, aginConfirm, express, applyEditAddress, orderExpectedArrivalTime, groupOrderDetail} from "@/api/order";
import Payment from "@/components/Payment";
import DataFormat from "@/components/DataFormat";
import {copyClipboard} from "@/utils";
@@ -492,61 +636,128 @@ export default {
payPassword: null,
webUrl: this.$VUE_APP_RESOURCES_URL,
pageKeyId: Object.freeze('userOrderInfo'),
logistics: {}
logistics: {},
isGift: false,
isGroup: false,
key: "",
loading: true,
addressEditOrderId: ""
};
},
computed: {
isrefund() {
const status = Number(this.orderInfo._status._type)
return status< 0
},
refundOrder() {
return this.orderInfo.refund_status > 0;
},
...mapGetters(["userInfo"])
},
onLoad(opts) {
this.isGift = Number(opts.isGift || 0) === 1;
this.isGroup = Number(opts.isGroup || 0) === 1;
this.key = opts.key || ''
},
onShow() {
const _this = this
const chooseAddress = uni.getStorageSync('chooseAddress') || {}
const chooseAddress = uni.getStorageSync('updateOrderAddress') || {}
const addressId = chooseAddress.id || ''
// 监听到选择收货地址
if (addressId) {
uni.setStorageSync('chooseAddress', {})
if (addressId && this.addressEditOrderId) {
uni.setStorageSync('updateOrderAddress', {})
uni.showModal({
title:'确定修改地址吗?',
content: '',
content: '提交后将进入审核流程,请确认是否继续',
showCancel: true,
cancelText: '取消',
confirmText: '确认',
success:(success)=>{
if(success.confirm) {
uni.showLoading()
editAddress({
applyEditAddress({
addressId,
orderId: _this.orderInfo.orderId
}).then(res => {
orderId: this.addressEditOrderId
}).then(() => {
uni.showToast({
icon: 'success',
title: '修改成功!'
title: '已提交审核'
})
_this.getDetail()
this.getDetail()
}).finally(() => {
uni.hideLoading()
})
} else {
this.getDetail()
}
},
complete:(complete)=>{
uni.hideLoading()
this.addressEditOrderId = ''
}
})
} else {
_this.id = _this.$yroute.query.id;
_this.getDetail();
this.id = this.$yroute.query.id;
this.isGroup = Number(this.$yroute.query.isGroup || 0) === 1;
this.key = this.$yroute.query.key || '';
this.getDetail();
}
},
onUnload() {
uni.setStorageSync('needRefreshMyOrderList', 1)
},
// inject: ["app"],
mounted: function () {
this.id = this.$yroute.query.id;
},
methods: {
copyClipboard,
goGoodsCon(item) {
getTime(type) {
const timeMap = {
1:"48小时内",
2:"72小时内",
3:"24小时内",
4: "7天内",
5: "15天内"
}
return timeMap[type]
},
canShowModifyAddress(order) {
if (!order || this.isGroup || Number(order.isTickets || 0) === 1) {
return false
}
const type = Number(order._status && order._status._type)
if (type !== 1) {
return false
}
const isAddressUpdate = Number(order.isAddressUpdate || 0)
const addressUpdateStatus = Number(order.addressUpdateStatus || 0)
if (isAddressUpdate === 0) {
return true
}
return false
},
//催发货
delayOrder() {
uni.showToast({
icon: 'none',
title: '您已催促成功,商家正在抓紧备货'
})
},
// 获取预计送达时间
getDay(delivery, index) {
const params = {
orderId: this.orderInfo.orderId,
deliveryId: delivery.deliveryId,
deliverySn: delivery.deliverySn
}
orderExpectedArrivalTime(params).then(res => {
if(res.success) {
this.orderInfo.deliveryInfo[index].estimatedTime = res.data.deliveryTime || ''
this.$forceUpdate()
}
})
},
goGoodsCon(item, isGiftCardReceiveBlind) {
if(isGiftCardReceiveBlind) return
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
@@ -609,10 +820,17 @@ export default {
})
},
goAddress() {
this.addressEditOrderId = this.orderInfo.orderId
uni.setStorageSync('updateOrderSourceAddress', {
id: this.orderInfo.addressId || this.orderInfo.userAddressId || '',
realName: this.orderInfo.realName || this.orderInfo.oldRealName || '',
phone: this.orderInfo.userPhone || this.orderInfo.oldUserPhone || '',
userAddress: this.orderInfo.userAddress || this.orderInfo.oldUserAddress || ''
})
this.$yrouter.push({
path:'/pages/user/address/AddressManagement/index',
query: {
choosMode: 1
choosMode: 4
}
})
},
@@ -646,14 +864,6 @@ export default {
query: { id: cart.unique }
});
},
goOrderDetails(order) {
this.$yrouter.push({
path: "/pages/order/OrderDetails/index",
query: {
id: order.orderId
}
});
},
goRoom(order) {
const params = {
goodsId: order.cartInfo[0].productId,
@@ -878,65 +1088,78 @@ export default {
if (type == 1 && combination_id > 0) {
status.type = 6;
status.class_status = 1;
} //查看拼团
if (type == 2 && delivery_type == "express") status.class_status = 2; //查看物流
if (type == 2) status.class_status = 3; //确认收货
if (type == 4 || type === 0) status.class_status = 4; //删除订单
} // 查看拼团
if (type == 2 && delivery_type == "express") status.class_status = 2; // 查看物流
if (type == 2) status.class_status = 3; // 确认收货
if (type == 4 || type === 0) status.class_status = 4; // 删除订单
if (
!seckill_id &&
!bargain_id &&
!combination_id &&
(type == 3 || type == 4)
!seckill_id &&
!bargain_id &&
!combination_id &&
(type == 3 || type == 4)
)
status.class_status = 5; //再次购买
status.class_status = 5; // 再次购买
if (type == 9) {
//线下付款
// 线下付款
status.class_status = 0;
this.offlineStatus = false;
this.offlineStatus = false
}
this.status = status;
this.status = status
},
getDetail() {
const id = this.id;
const id = this.id
if (!id) {
uni.showToast({
title: "订单不存在",
icon: "none",
duration: 2000
});
return;
})
return
}
orderDetail(id)
.then(res => {
this.orderInfo = res.data;
this.getOrderStatus();
if (this.orderInfo.combinationId > 0) {
this.orderTypeName = "拼团订单";
this.orderTypeNameStatus = false;
} else if (this.orderInfo.bargainId > 0) {
this.orderTypeName = "砍价订单";
this.orderTypeNameStatus = false;
} else if (this.orderInfo.seckillId > 0) {
this.orderTypeName = "秒杀订单";
this.orderTypeNameStatus = false;
uni.showLoading({
title: '加载中'
})
this.loading = true
orderDetail(id).then(res => {
this.orderInfo = res.data
this.orderInfo._status._type = parseInt(this.orderInfo._status._type)
this.getOrderStatus()
if (this.orderInfo.combinationId > 0) {
this.orderTypeName = "拼团订单"
this.orderTypeNameStatus = false
} else if (this.orderInfo.bargainId > 0) {
this.orderTypeName = "砍价订单"
this.orderTypeNameStatus = false
} else if (this.orderInfo.seckillId > 0) {
this.orderTypeName = "秒杀订单"
this.orderTypeNameStatus = false
} else if (this.orderInfo.isGiftCardReceive === 1 || this.orderInfo.isGiftCardSend === 1) {
this.orderTypeName = "礼包订单"
this.orderTypeNameStatus = false
}
this.system_store = res.data.systemStore || {}
this.mapKey = res.data.mapKay
this.setOfflinePayStatus(this.orderInfo.offlinePayStatus)
const statusType = this.orderInfo._status._type
if([-1, -2, 4, 3].includes(statusType) || (statusType === 2 && this.orderInfo.isTickets !== 1)) {
for(let i = 0; i < this.orderInfo.deliveryInfo.length; i++) {
this.orderInfo.deliveryInfo[i].estimatedTime = ''
this.getExpressInfo(this.orderInfo.deliveryInfo[i], i)
if (statusType === 2) {
this.getDay(this.orderInfo.deliveryInfo[i], i)
}
this.system_store = res.data.systemStore || {};
this.mapKey = res.data.mapKay;
this.setOfflinePayStatus(this.orderInfo.offlinePayStatus);
const orderInfo = this.orderInfo
if(orderInfo._status._type == -1 || orderInfo._status._type == -2 || orderInfo._status._type == 4 || orderInfo._status._type == 3 || (orderInfo._status._type == 2 && orderInfo.isTickets!==1)) {
for(let i = 0; i < orderInfo.deliveryInfo.length; i++) {
this.getExpressInfo(orderInfo.deliveryInfo[i], i)
}
}
})
.catch(err => {
uni.showToast({
title: err.response.data.msg,
icon: "none",
duration: 2000
});
});
}
}
}).catch(err => {
uni.showToast({
title: err.response.data.msg,
icon: "none",
duration: 2000
})
}).finally(() => {
this.loading = false
uni.hideLoading()
})
},
async toPay(type) {
var that = this;
@@ -1186,4 +1409,27 @@ export default {
text-align: left !important;
}
}
.gift-wrap{
background: #fff;
border-radius: 20rpx;
margin-top: 30rpx;
padding: 30rpx;
}
.time-bg{
position: absolute;
height: 38rpx;
width: 100%;
}
.time-box{
position: relative;
margin-left: 10rpx;
}
.order-deliver-item-show {
display: block;
}
.time-wrap {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>
+286 -259
View File
@@ -1,15 +1,16 @@
<template>
<view class="order-submission" style="padding: 30rpx;">
<view class="allAddress" style="background-color: #f6f6f6;">
<view
class="address acea-row v12-align-start"
style="flex-wrap: nowrap;margin: 0 0 30rpx 0;border-radius: 16rpx;"
v-if="shipping_type === 0"
@click="addressTap"
v-if="shipping_type === 0"
class="address acea-row v12-align-start"
style="flex-wrap: nowrap;margin: 0 0 30rpx 0;border-radius: 16rpx;"
@click="addressTap"
>
<image style="flex-shrink: 0;width: 36rpx;height: 36rpx;margin-right: 20rpx;"
:src="webUrl+'/orderIcon/map.png'" mode=""></image>
<image
:src="webUrl+'/orderIcon/map.png'"
style="flex-shrink: 0;width: 36rpx;height: 36rpx;margin-right: 20rpx;"
/>
<view class="acea-row row-between-wrapper" style="flex-wrap: nowrap;flex-grow: 1;">
<view class="addressCon" v-if="addressInfo.realName">
<view class="more-t">
@@ -25,32 +26,35 @@
</view>
</view>
</view>
<div style="flex-wrap: nowrap;" class="address acea-row row-middle" v-else @click="showStoreList">
<image style="flex-shrink: 0;width: 48rpx;height: 48rpx;margin-right: 20rpx;"
:src="webUrl+'/20230304135530688212.png'" mode=""></image>
<div
v-else
style="flex-wrap: nowrap;"
class="address acea-row row-middle"
@click="showStoreList"
>
<image
:src="webUrl+'/20230304135530688212.png'"
style="flex-shrink: 0;width: 48rpx;height: 48rpx;margin-right: 20rpx;"
/>
<view class="acea-row row-between-wrapper" style="flex-wrap: nowrap;flex-grow: 1;">
<div class="addressCon">
<div class="name">
{{ isNullOrEmpty(storeItems) ? systemStore.name : storeItems.name }}
<span
class="phone"
>{{ isNullOrEmpty(storeItems) ? systemStore.phone : storeItems.phone }}</span>
<span class="phone">{{ isNullOrEmpty(storeItems) ? systemStore.phone : storeItems.phone }}</span>
</div>
<div>{{ isNullOrEmpty(storeItems) ? systemStore.address : storeItems.address }}</div>
</div>
<div class="iconfont icon-jiantou"></div>
</view>
</div>
</view>
<OrderGoods :evaluate="0" :cartInfo="orderGroupInfo.cartInfo" title="商品信息"></OrderGoods>
<OrderGoods
:evaluate="0"
:cartInfo="orderGroupInfo.cartInfo"
title="商品信息"
/>
<view class="wrapper" style="margin:0;border-radius:0 0 20rpx 20rpx">
<view class="v12-pa-2 acea-row row-between-wrapper" style="flex-wrap: nowrap;" v-if="shipping_type === 0">
<view v-if="shipping_type === 0" class="v12-pa-2 acea-row row-between-wrapper" style="flex-wrap: nowrap;">
<view class="acea-row row-middle" style="flex-wrap: nowrap;">
<!-- <image style="width: 32rpx;height: 32rpx;margin-right: 12rpx;" :src="webUrl+'/20210807230820761721.png'"
mode=""></image> -->
@@ -64,13 +68,22 @@
<view class="v12-pa-2 acea-row row-between-wrapper">
<view class="v12-font-24 v12-secondary-dark-text v12-pr-3 v12-text-right" style="width: 140rpx">联系人</view>
<view class="discount">
<input class="v12-font-24" type="text" placeholder="请填写您的联系姓名" v-model="contacts"/>
<input
v-model="contacts"
class="v12-font-24"
type="text"
placeholder="请填写您的联系姓名"
/>
</view>
</view>
<view class="v12-pa-2 acea-row row-between-wrapper">
<view class="v12-font-24 v12-secondary-dark-text v12-pr-3 v12-text-right" style="width: 140rpx">联系电话</view>
<view class="discount">
<input type="text" placeholder="请填写您的联系电话" v-model="contactsTel"/>
<input
v-model="contactsTel"
type="text"
placeholder="请填写您的联系电话"
/>
</view>
</view>
</view>
@@ -80,7 +93,11 @@
mode=""></image> -->
<text class="v12-font-24 v12-secondary-dark-text v12-pr-3 v12-text-right" style="width: 140rpx;">备注</text>
</view>
<textarea v-model="mark" placeholder="建议与商家协商一致" class="mark v12-font-24 "></textarea>
<textarea
v-model="mark"
placeholder="建议与商家协商一致"
class="mark v12-font-24 "
/>
</view>
</view>
<!-- v12 -->
@@ -113,7 +130,7 @@
<text class="v12-dark-text v12-font-24" >{{ couponList.usable.length === 0 ? '无' : '去选择' }}</text>
<text class="iconfont icon-jiantou v12-font-24 v12-dark-text"></text>
</view> -->
<view class="v12-secondary-dark-text v12-align-center" @click="changeCoupons">
<view class="v12-secondary-dark-text v12-align-center" @click="showCouponsPopup">
<text class="v12-font-22 ">-</text>
<text class="v12-font-28">{{ force2Decimal(orderPrice.couponPrice) }}</text>
<text class="iconfont icon-jiantou v12-font-24 v12-dark-text"></text>
@@ -180,11 +197,11 @@
</view>
<view class="list" style="margin-top: 0;">
<view
class="payItem acea-row row-middle"
:class="active === 'weixin' ? 'on' : ''"
v-show="isWeixin"
@click="payItem('weixin')"
style="flex-wrap: nowrap; margin-top: 0"
v-show="isWeixin"
:class="active === 'weixin' ? 'on' : ''"
class="payItem acea-row row-middle"
style="flex-wrap: nowrap; margin-top: 0"
@click="payItem('weixin')"
>
<view class="name acea-row v12-font-24">
<!-- <view class="iconfont icon-weixin2" :class="active === 'weixin' ? 'bounceIn' : ''"></view> -->
@@ -198,11 +215,11 @@
</label>
</view>
<view
class="payItem acea-row row-middle"
:class="active === 'weixin' ? 'on' : ''"
@click="payItem('weixin')"
v-show="!isWeixin"
style="flex-wrap: nowrap; margin-top: 20rpx"
v-show="!isWeixin"
:class="active === 'weixin' ? 'on' : ''"
class="payItem acea-row row-middle"
style="flex-wrap: nowrap; margin-top: 20rpx"
@click="payItem('weixin')"
>
<view class="name acea-row v12-font-24">
<!-- <view class="iconfont icon-weixin2" :class="active === 'weixin' ? 'bounceIn' : ''"></view> -->
@@ -214,13 +231,12 @@
<radio style="transform: scale(0.7);" value="" :checked="active === 'weixin'" color="#C52733"/>
<text></text>
</label>
</view>
<view
class="payItem acea-row row-middle v12-justify-between"
:class="active === 'yue' ? 'on' : ''"
@click="payItem('yue')"
style="flex-wrap: nowrap;"
:class="active === 'yue' ? 'on' : ''"
class="payItem acea-row row-middle v12-justify-between"
style="flex-wrap: nowrap;"
@click="payItem('yue')"
>
<view class="name acea-row v12-font-24">
<!-- <view style="background-color: #007AFF;" class="iconfont icon-icon-test"
@@ -233,34 +249,31 @@
<radio style="transform: scale(0.7);" value="" :checked="active === 'yue'" color="#C52733"/>
<text></text>
</label>
</view>
</view>
</view>
</view>
<!-- v9 优惠券开始 -->
<u-popup
v-if="show"
:show="show"
:round="10"
mode="bottom"
close-on-click-overlay
@close="changeCoupons"
@close="close"
>
<CouponsPopup
v-if="couponList.usable.length > 0"
v-if="couponList.usable.length > 0 && show"
:two-list="couponList"
@change="changeCoupons"
@close="close"
:currentPrice="force2Decimal(orderPrice.totalPrice)"
:id="couponId"
@max="couponMax"
:couponId1="couponId"
:show-tab="false"
/>
</u-popup>
<!-- v9 优惠券结束 -->
<view style="height:80rpx"></view>
<view class="footer v12-justify-end v12-px-3" style="height: 120rpx;">
<view class="v12-align-center v12-px-4">
@@ -288,6 +301,7 @@
@redirect="addressRedirect"
/>
<passkeyborad
v-if="isShowPayPwdPop"
ref='payPwdPop'
showMoney
:money="orderPrice.payPrice"
@@ -300,19 +314,24 @@
<script>
import passkeyborad from '@/components/yzc-paykeyboard/yzc-paykeyboard'
import OrderGoods from "@/components/OrderGoods";
import AddressWindow from "@/components/AddressWindow";
import {createOrder, postOrderComputed, postOrderConfirm} from "@/api/order";
import {mapGetters} from "vuex";
import {weappPay} from "@/libs/wechat";
import {isNullOrEmpty, isWeixin} from "@/utils";
import blPaymentPasswordInput from '@/components/blPaymentPasswordInput.vue';
import uniPopup from '@/components/uni-popup/uni-popup.vue';
import OrderGoods from "@/components/OrderGoods"
import AddressWindow from "@/components/AddressWindow"
import {
createOrder,
postOrderComputed,
postOrderConfirm,
getAddressDefaultSelected
} from "@/api/order"
import {mapGetters} from "vuex"
import {weappPay} from "@/libs/wechat"
import {isNullOrEmpty, isWeixin} from "@/utils"
import blPaymentPasswordInput from '@/components/blPaymentPasswordInput.vue'
import uniPopup from '@/components/uni-popup/uni-popup.vue'
import CouponsPopup from "@/components/CouponsPopup.vue"
import { pageListenMixins } from '@/mixins/pageListenMixins'
const NAME = "OrderSubmission",
_isWeixin = isWeixin();
const NAME = "OrderSubmission"
const _isWeixin = isWeixin()
export default {
name: NAME,
components: {
@@ -362,7 +381,7 @@ export default {
unusable: [],
usable: []
},
couponId: uni.getStorageSync('couponId') || 0,
couponId: null,
couponIndex: -1,
show: false,
// 是否使用积分开关
@@ -376,16 +395,16 @@ export default {
pageKeyId: Object.freeze('orderConfirm'),
total: 0,
couponTitle: '',
hasCoupon: false,
};
hasCoupon: false
}
},
computed: mapGetters(["userInfo", "storeItems"]),
watch: {
useIntegral() {
this.computedPrice('useIntegral');
this.computedPrice('useIntegral')
},
shipping_type() {
this.computedPrice('shipping_type');
this.computedPrice('shipping_type')
},
async couponId(value) {
this.couponIndex = this.couponList['usable']?.findIndex(item => item.id === +value)
@@ -394,7 +413,6 @@ export default {
}
},
onLoad(opts) {
console.log(opts);
const that = this
if (that.$yroute.query.pinkid !== undefined) {
that.pinkId = that.$yroute.query.pinkid
@@ -407,42 +425,63 @@ export default {
that.lotteryRecordId = that.$yroute.query.lotteryRecordId
}
that.hasCoupon = opts.couponId !== ''
that.getCartInfo()
},
onShow() {
const _this = this
_this.getCartInfo()
//获取用户信息
_this.$store.dispatch("getUser", true)
// 监听到选择收货地址
const chooseAddress = uni.getStorageSync('chooseAddress') || {}
// const sourceAddress = uni.getStorageSync('sourceAddress') || {}
// 监听到选择收货地址
if (chooseAddress.id) {
_this.addressInfo = chooseAddress
// 地址切换也要重新计算一下
// uni.setStorageSync('sourceAddress', _this.addressInfo || '')
uni.setStorageSync('sourceAddress', {})
_this.computedPrice('onLoad chooseAddress')
setTimeout(() => {
uni.hideLoading()
uni.setStorageSync('chooseAddress', {})
}, 1000)
}
},
methods: {
isNullOrEmpty,
couponMax(e) {
this.couponTitle = e.couponTitle
this.couponId = this.$yroute.query.couponId || uni.getStorageSync('couponId') || 0
// this.couponTitle = e.couponTitle
// this.couponId = this.$yroute.query.couponId || uni.getStorageSync('couponId') || 0
},
showStoreList() {
this.$store.commit("get_to", "orders");
this.$store.commit("get_to", "orders")
this.$yrouter.push({
path: "/pages/shop/StoreList/index"
});
})
},
force2Decimal(value) {
return this.$force2Decimal(value);
return this.$force2Decimal(value)
},
usePointsCheckChange(e) {
this.computedPrice()
},
// 计算订单金额
/**
* 计算订单金额
* @param {*} f 有值时 couponId不取默认值
*/
computedPrice(f) {
let shipping_type = this.shipping_type;
postOrderComputed(this.orderGroupInfo.orderKey, {
addressId: this.addressInfo.id,
useIntegral: this.useIntegral ? 1 : 0,
couponId: this.couponList.usable.length === 0 ? 0 : this.couponId || 0,
couponId: this.couponList.usable.length === 0 ? 0 : this.couponId,
usePoints: this.usePointsCheck ? 1 : 0,
shipping_type: parseInt(shipping_type) + 1
shipping_type: parseInt(this.shipping_type) + 1
}).then(res => {
this.couponList = res.data.result.couponList
if(!f) {
this.couponId = res.data.result.defaultCouponId
}
const data = res.data;
if (data.status === "EXTEND_ORDER") {
this.$yrouter.replace({
@@ -450,18 +489,16 @@ export default {
query: {
id: data.result.orderId
}
});
})
} else {
this.orderPrice = data.result;
console.log(this.orderPrice);
if (this.usePointsCheck) {
this.usePointsNumber = data.result.usedPoints
} else {
this.usePointsNumber = 0
}
}
});
})
},
getCartInfo() {
const _this = this
@@ -501,38 +538,18 @@ export default {
_this.usePointsCheck = true
_this.usePointsNumber = data.maxPoints
}
if(_this.$yroute.query.address !== undefined ) {
_this.addressInfo = JSON.parse(_this.$yroute.query.address) || {}
} else {
_this.addressInfo = uni.getStorageSync('chooseAddress') ? uni.getStorageSync('chooseAddress') : data.addressInfo || {}
}
const chooseAddress = uni.getStorageSync('chooseAddress') || {}
const sourceAddress = uni.getStorageSync('sourceAddress') || data.addressInfo || uni.getStorageSync('chooseAddress')
// 监听到选择收货地址
if (chooseAddress.id && (chooseAddress.id !== sourceAddress.id)) {
uni.showModal({
title:'确定修改地址吗?',
content: '',
showCancel: true,
cancelText: '取消',
confirmText: '确认',
success: (res) => {
if(res.confirm) {
_this.addressInfo = chooseAddress
// 地址切换也要重新计算一下
uni.setStorageSync('sourceAddress', _this.addressInfo || '')
_this.computedPrice('onLoad chooseAddress')
}
},
complete: () => {
uni.hideLoading()
// uni.setStorageSync('chooseAddress', {})
}
})
}
_this.systemStore = data.systemStore || {}
_this.storeSelfMention = data.storeSelfMention
_this.computedPrice()
if(_this.$yroute.query.address !== undefined ) {
_this.addressInfo = JSON.parse(_this.$yroute.query.address) || {}
_this.computedPrice()
} else {
// _this.addressInfo = uni.getStorageSync('sourceAddress') || data.addressInfo || {}
getAddressDefaultSelected().then(res => {
_this.addressInfo = res.data
_this.computedPrice()
})
}
}).catch(err => {
uni.showToast({
title: err.msg,
@@ -542,32 +559,33 @@ export default {
})
},
addressTap: function () {
//改用跳转页面方式
uni.setStorageSync('sourceAddress', this.addressInfo)
// 改用跳转页面方式
this.$yrouter.push({
path: "/pages/user/address/AddressManagement/index",
query: {
choosMode: 1
}
});
})
},
addressRedirect() {
this.addressLoaded = false;
this.showAddress = false;
this.addressLoaded = false
this.showAddress = false
},
payItem: function (index) {
this.active = index;
this.active = index
},
changeAddress(addressInfo) {
this.addressInfo = addressInfo;
this.addressInfo = addressInfo
//地址切换也要重新计算一下
this.computedPrice('changeAddress');
this.computedPrice('changeAddress')
},
pwdPopClose() {
this.isShowPayPwdPop = false;
this.isShowPayPwdPop = false
},
pwdPopFinish(payPwd) {
this.payPassword = payPwd;
this.createOrder(true);
this.payPassword = payPwd
this.createOrder(true)
},
//支付密码输入监听
onInput(e) {
@@ -575,55 +593,56 @@ export default {
},
// 支付密码输入确认
onConfirm(e) {
this.payPassword = e.value;
this.$refs.popup.close();//关闭支付密码弹窗
this.$refs.secrity.clear();//清空支付密码
this.createOrder(true);
this.payPassword = e.value
this.$refs.popup.close()
this.$refs.secrity.clear()
this.createOrder(true)
},
subscribePay() {
uni.requestSubscribeMessage({
tmplIds: ['7O3wKw7_D4t7yfSOlvecKuOQP8QEPY88uBx2FJg43HE', 'TSmTnGibZ8IxWCiRWOvLxha6-0u7tE-V1-WbDCiPkKU', '9Q6xLHe6HyjkU5Zn5k_2zxYPf2y4MxVyUbOSW8pcZRU'],
tmplIds: [
'7O3wKw7_D4t7yfSOlvecKuOQP8QEPY88uBx2FJg43HE',
'TSmTnGibZ8IxWCiRWOvLxha6-0u7tE-V1-WbDCiPkKU',
'9Q6xLHe6HyjkU5Zn5k_2zxYPf2y4MxVyUbOSW8pcZRU'
],
complete: () => this.createOrder(false)
})
},
createOrder(isyue) {
let shipping_type = this.shipping_type;
if (!this.active) {
uni.showToast({
title: "请选择支付方式",
icon: "none",
duration: 2000
});
return;
})
return
}
if (!this.addressInfo.id && !this.shipping_type) {
uni.showToast({
title: "请选择收货地址",
icon: "none",
duration: 2000
});
return;
})
return
}
if (this.shipping_type) {
if (
(this.contacts === "" || this.contactsTel === "") &&
this.shipping_type
(this.contacts === "" || this.contactsTel === "") &&
this.shipping_type
) {
uni.showToast({
title: "请填写联系人或联系人电话",
icon: "none",
duration: 2000
});
})
return;
}
if (!/^1(3|4|5|7|8|9|6)\d{9}$/.test(this.contactsTel)) {
uni.showToast({
title: "请填写正确的手机号",
icon: "none",
duration: 2000
});
})
return;
}
if (!/^[\u4e00-\u9fa5\w]{2,16}$/.test(this.contacts)) {
@@ -631,41 +650,36 @@ export default {
title: "请填写您的真实姓名",
icon: "none",
duration: 2000
});
return;
})
return
}
}
//判断如果是余额支付,先要输入支付密码
// 判断如果是余额支付,先要输入支付密码
if (this.active == 'yue' && !isyue) {
//先判断用户是否绑定过手机
// 先判断用户是否绑定过手机
if (this.userInfo.phone) {
//再判断用户是否设置过支付密码
// 再判断用户是否设置过支付密码
if (this.userInfo.hasPwdPay) {
//显示输入支付密码
this.isShowPayPwdPop = true;
// 显示输入支付密码
this.isShowPayPwdPop = true
} else {
//设置支付密码
this.$yrouter.push("/pkg_user/views/payPassword?isSetMode=true");
// 设置支付密码
this.$yrouter.push("/pkg_user/views/payPassword?isSetMode=true")
}
} else {
//跳转到绑定手机
this.$yrouter.push("/pkg_user/views/bindPhone");
// 跳转到绑定手机
this.$yrouter.push("/pkg_user/views/bindPhone")
}
return;
return
}
uni.showLoading({
title: "生成订单中"
});
let from = {};
})
let from = {}
if (this.$deviceType == "app") {
from.from = "app";
from.from = "app"
}
console.log(this.storeItems, this.systemStore);
var params = {
const params = {
realName: this.contacts,
phone: this.contactsTel,
addressId: this.addressInfo.id,
@@ -679,131 +693,144 @@ export default {
bargainId: this.orderGroupInfo.bargain_id,
from: this.from,
mark: this.mark || "",
shippingType: parseInt(shipping_type) + 1,
shippingType: parseInt(this.shipping_type) + 1,
storeId: this.storeItems ? this.storeItems.id : this.systemStore.id,
...from
}
uni.setStorageSync('chooseAddress', {})
//余额支付的支付密码
if (this.active == 'yue') {
params.passwordPay = this.payPassword;
params.passwordPay = this.payPassword
}
uni.removeStorageSync('couponId')
var that = this;
createOrder(this.orderGroupInfo.orderKey, params)
.then(res => {
uni.hideLoading();
const data = res.data;
switch (data.status) {
case "ORDER_EXIST":
case "EXTEND_ORDER":
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
break;
case "PAY_DEFICIENCY":
break;
case "PAY_ERROR":
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
break;
case "SUCCESS":
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
break;
case "WECHAT_H5_PAY":
// H5支付
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
setTimeout(() => {
// location.href = data.result.jsConfig.mweb_url;
}, 100);
break;
case "WECHAT_PAY":
// 小程序支付
weappPay(data.result.jsConfig).finally(() => {
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
});
break;
case "WECHAT_APP_PAY":
// APP支付
weappPay(data.result.jsConfig).finally(() => {
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
});
});
break;
}
})
.catch(err => {
uni.hideLoading();
var that = this
createOrder(this.orderGroupInfo.orderKey, params).then(res => {
uni.hideLoading()
const data = res.data
switch (data.status) {
case "ORDER_EXIST":
case "EXTEND_ORDER":
uni.showToast({
title:
err.msg ||
err.response.data.msg ||
err.response.data.message ||
"创建订单失败",
title: res.msg,
icon: "none",
duration: 2000
})
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
})
break;
case "PAY_DEFICIENCY":
break;
case "PAY_ERROR":
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
})
this.$yrouter.replace({
path: "/pages/order/MyOrder/index",
query: {
type: 1
}
});
}).finally(function () {
that.isShowPayPwdPop = false;
that.$refs.payPwdPop.clear();//清空支付密码
that.$refs.payPwdPop.close();//关闭支付密码弹窗
});
break;
case "SUCCESS":
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
})
if (this.orderGroupInfo.cartInfo && this.orderGroupInfo.cartInfo.length === 1) {
this.$yrouter.replace({
path: "/pages/order/OrderDetails/index",
query: {
id: data.result.orderId
}
})
} else {
this.$yrouter.replace({
path: "/pages/order/MyOrder/index",
query: {
type: 2
}
})
}
break;
case "WECHAT_H5_PAY":
// H5支付
this.$yrouter.replace({
path: "/pages/order/MyOrder/index",
query: {
type: 1
}
})
break;
case "WECHAT_PAY":
// 小程序支付
weappPay(data.result.jsConfig).finally(() => {
this.$yrouter.replace({
path: "/pages/order/MyOrder/index",
query: {
type: 1
}
})
})
break;
case "WECHAT_APP_PAY":
// APP支付
weappPay(data.result.jsConfig).finally(() => {
this.$yrouter.replace({
path: "/pages/order/MyOrder/index",
query: {
type: 1
}
});
})
break;
}
}).catch(err => {
uni.hideLoading()
uni.showToast({
title: err.msg ||
err.response.data.msg ||
err.response.data.message ||
"创建订单失败",
icon: "none",
duration: 2000
})
}).finally(function () {
that.isShowPayPwdPop = false
that.$refs.payPwdPop.clear()
that.$refs.payPwdPop.close()
})
},
close() {
this.show = !this.show
},
showCouponsPopup() {
if (this.couponList.usable.length === 0) return
this.show = !this.show
},
// v9-2
changeCoupons(item) {
if(!item) return
if (this.couponList.usable.length === 0) return
this.show = !this.show
this.couponId = uni.getStorageSync('couponId') || 0
if(!item.id) {
this.couponId = 0
this.couponTitle = ''
this.computedPrice(1)
return
}
this.couponId = item.id
this.couponTitle = item.couponTitle
},
this.computedPrice(1)
// this.couponId = uni.getStorageSync('couponId') || 0
// this.couponTitle = item.couponTitle
}
}
};
</script>
+3 -3
View File
@@ -1,9 +1,9 @@
<template>
<view class="return-list" ref="container">
<view class="goodWrapper" v-for="(order,orderListIndex) in orderList" :key="orderListIndex">
<view class="iconfont icon-tuikuanzhong powder" v-if="order._status._type === '-1'"></view>
<view class="iconfont icon-yituikuan" v-if="order._status._type === '-2'"></view>
<image class="iconfont" :src="webUrl+'/20230713110051999762.png'" v-if="order._status._type === '-3'"/>
<view class="iconfont icon-tuikuanzhong powder" v-if="parseInt(order._status._type) === -1"></view>
<view class="iconfont icon-yituikuan" v-if="parseInt(order._status._type) === -2"></view>
<image class="iconfont" :src="webUrl+'/20230713110051999762.png'" v-if="parseInt(order._status._type) === -3"/>
<view class="orderNum">订单号{{ order.orderId }}</view>
<view
class="item acea-row row-between-wrapper"
+68 -18
View File
@@ -7,8 +7,8 @@
<view class="start" :class="'star' + replyData.replyStar"></view>
</view>
<view>
<text class="font-color-red">{{ replyData.replyChance || 0 }}%</text>
<text>好评率</text>
<text class="font-color-red">{{ isTravelGroup ? replyData.replyChance || 0 : replyData.replyChance + '%' || 0 }}</text>
<text> {{ ` 好评率` }}</text>
</view>
</view>
<view class="nav acea-row row-middle">
@@ -35,6 +35,7 @@
<script>
import UserEvaluation from "@/components/UserEvaluation";
import { getReplyConfig, getReplyList } from "@/api/store";
import { getJiaLouProductReply, getJiaLouProductReplyCount } from "@/api/qianxian";
import Loading from "@/components/Loading";
export default {
@@ -47,6 +48,7 @@ export default {
data: function() {
return {
product_id: 0,
isTravelGroup: false,
replyData: {},
navList: [
{ evaluate: "全部", num: 0 },
@@ -65,6 +67,7 @@ export default {
},
mounted: function() {
this.product_id = this.$yroute.query.id;
this.isTravelGroup = String(this.$yroute.query.isTravelGroup || '') === '1';
this.getProductReplyCount();
this.getProductReplyList();
},
@@ -74,27 +77,74 @@ export default {
methods: {
getProductReplyCount: function() {
let that = this;
getReplyConfig(that.product_id).then(res => {
that.$set(that, "replyData", res.data);
that.navList[0].num = res.data.sumCount;
that.navList[1].num = res.data.goodCount;
that.navList[2].num = res.data.inCount;
that.navList[3].num = res.data.poorCount;
});
if (that.isTravelGroup) {
getJiaLouProductReplyCount({
travelGroupProductId: that.product_id
}).then(res => {
const data = res.data || {};
that.$set(that, "replyData", data);
that.navList[0].num = data.sumCount || 0;
that.navList[1].num = data.goodCount || 0;
that.navList[2].num = data.inCount || 0;
that.navList[3].num = data.poorCount || 0;
});
} else {
getReplyConfig(that.product_id).then(res => {
const data = res.data || {};
that.$set(that, "replyData", data);
that.navList[0].num = data.sumCount || 0;
that.navList[1].num = data.goodCount || 0;
that.navList[2].num = data.inCount || 0;
that.navList[3].num = data.poorCount || 0;
});
}
},
getProductReplyList: function() {
let that = this;
if (that.loading) return; //阻止下次请求(false可以进行请求);
if (that.loadend) return; //阻止结束当前请求(false可以进行请求);
if (that.loading) return;
if (that.loadend) return;
that.loading = true;
let q = { page: that.page, limit: that.limit, type: that.currentActive };
getReplyList(that.product_id, q).then(res => {
that.loading = false;
//apply();js将一个数组插入另一个数组;
that.reply.push.apply(that.reply, res.data);
that.loadend = res.data.length < that.limit; //判断所有数据是否加载完成;
that.page = that.page + 1;
});
if (that.isTravelGroup) {
getJiaLouProductReply({
travelGroupProductId: that.product_id,
type: q.type,
page: q.page,
limit: q.limit
}).then(res => {
that.loading = false;
const pageData = res.data || {};
const rawList = Array.isArray(pageData.records) ? pageData.records : [];
const list = rawList.map(item => {
const productScore = Number(item.productScore) || 0;
const serviceScore = Number(item.serviceScore) || 0;
let star = productScore || serviceScore || 5;
const skuText = '默认';
const picturesArr = Array.isArray(item.pics) ? item.pics : [];
return {
...item,
star,
sku: item.travelGroupProductName || skuText,
picturesArr
};
});
that.reply.push.apply(that.reply, list);
that.loadend = list.length < that.limit || !pageData.pages || that.page >= pageData.pages;
that.page = that.page + 1;
}).catch(() => {
that.loading = false;
});
} else {
getReplyList(that.product_id, q).then(res => {
that.loading = false;
const data = res.data || [];
that.reply.push.apply(that.reply, data);
that.loadend = data.length < that.limit;
that.page = that.page + 1;
}).catch(() => {
that.loading = false;
});
}
},
changeType: function(index) {
let that = this;
+2 -1
View File
@@ -252,7 +252,8 @@ export default {
setHistory(word) {
const index = this.historyKey.findIndex(item => item.word === word)
if (index > -1) {
return
this.historyKey.splice(index, 1)
// return
}
this.historyKey.unshift({id: this.$global.generateMixed(10), word })
uni.setStorageSync('historyKey', JSON.stringify(this.historyKey))
+64 -12
View File
@@ -73,11 +73,12 @@
:style="giftBg ? 'background: url(' + giftBg + ') no-repeat center/cover' : ''"
:class="giftBg ? '' : 'v12-primary'"
>
<text v-if="storeInfo.discount > 0" class="v12-font-28 v12-yellow-text ll-text">平台限时补贴{{ storeInfo.discount }}</text>
<view class="v12-font-28 v12-align-center" style="height: 74rpx; opacity: 0;">
<image class="gift-img" :src="webUrl + '/orderIcon/gift.png'" mode="widthFix" ></image>
支持礼包赠送
</view>
<view @click="toGift" class="v12-white v12-dark-text v12-radius-20 v12-font-28 gift-btn" style="opacity: 0;">
<view @click="toGift" class="v12-white v12-dark-text v12-radius-20 v12-font-28 gift-btn" v-if="!giftBg">
送给朋友
</view>
</view>
@@ -206,7 +207,8 @@
@ok="setCoupon"
@change="changeCoupons"
@close="changeCoupons()"
:showTab="false"
:showTab="false"
:showRadio="false"
:currentPrice="storeInfo.price" />
</u-popup>
<!-- v9 优惠券结束 -->
@@ -273,6 +275,9 @@
<image class="logo" :src="webUrl+'/orderIcon/地址.png'" mode="" style="margin-right:0; width: 32rpx; height:32rpx; opacity: 0;"/>
<text v-if="storeInfo.isFastPost == 1">预计48小时内发货</text>
<text v-if="storeInfo.isFastPost == 2">预计72小时内发货</text>
<text v-if="storeInfo.isFastPost == 3">预计24小时内发货</text>
<text v-if="storeInfo.isFastPost == 4">预计7天内发货</text>
<text v-if="storeInfo.isFastPost == 5">预计15天内发货</text>
<text v-if="storeInfo.isFastPost == 0">{{ "预计发货时间:" + storeInfo.postTime }}</text>
</view>
</view>
@@ -654,6 +659,10 @@ export default {
},
computed: mapGetters(['isLogin', 'location', 'userInfo']),
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return new Promise(async (resolve, reject) => {
let result;
let params = `?id=${this.id}&partnerId=${this.userInfo.uid}`
@@ -679,6 +688,43 @@ export default {
}
})
},
onShareTimeline() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
const storeName = this.storeInfo.storeName
const promise = new Promise(async (resolve, reject) => {
let result = null
let query = `id=${this.id}&partnerId=${this.userInfo.uid}`
if (this.source === 'famous') {
result = await famousGoodsShareImage(this.id)
}
if (this.source === 'kill') {
result = await secKillGoodsShareImage(this.activityId)
query += `&activityId=${this.activityId}`
}
if (result && result.status === 200) {
resolve({
title: storeName,
query,
imageUrl: result.data
})
} else {
resolve({
title: storeName,
query,
imageUrl: this.storeInfo.image
})
}
})
return {
title: storeName,
query: `id=${this.id}&partnerId=${this.userInfo.uid}`,
imageUrl: this.storeInfo.image,
promise
}
},
onLoad(options) {
// test data
console.log("getLaunchOptionsSync", uni.getLaunchOptionsSync())
@@ -855,17 +901,18 @@ export default {
})
},
async getMapData() {
let location = uni.getStorageSync('location')
const location = uni.getStorageSync('location')
if (location.length === 0) {
const map = await getLocation()
if (map.length > 1) {
const {latitude, longitude} = map[1]
let address = await getCurAddress(latitude, longitude)
address = address[1].data.result.ad_info
location = address.province + address.city + address.district
getCurAddress(latitude, longitude, address => {
this.postAddress = address.formattedAddress
})
}
} else {
this.postAddress = location
}
this.postAddress = location
},
// 获取当前位置经纬度
handleLoacation(toast) {
@@ -877,9 +924,9 @@ export default {
const map = await getLocation();
if (map.length > 1) {
const {latitude, longitude} = map[1]
let address = await getCurAddress(latitude, longitude)
address = address[1].data.result.ad_info
that.postAddress = address.province + address.city + address.district
getCurAddress(latitude, longitude, address => {
that.postAddress = address.formattedAddress
})
}
},
fail: err => {
@@ -1457,7 +1504,7 @@ export default {
path: "/pages/order/OrderSubmission/index",
query: {
id: res.data.cartId,
couponId: that.couponId // 优惠券ID
discount: that.storeInfo.discount,
}
})
return
@@ -1465,7 +1512,8 @@ export default {
if(news === 2) {
const coupId = that.couponId ? that.couponId : (that.couponList.usable[0] && that.couponList.usable[0].id || '')
uni.navigateTo({
url: '/pkg_user/views/gift/gift?cartId=' + res.data.cartId + '&couponId=' + coupId
// + '&couponId=' + coupId
url: '/pkg_user/views/gift/gift?cartId=' + res.data.cartId + (that.storeInfo.discount ? '&discount=' + that.storeInfo.discount : '')
})
}
if(news === 0) {
@@ -2181,4 +2229,8 @@ export default {
left: 0;
width: 100%;
}
.ll-text{
margin-left: 250rpx;
word-break: keep-all;
}
</style>
+50 -10
View File
@@ -1,7 +1,22 @@
<template>
<view class="evaluate-con">
<view
v-if="orderCon.productInfo"
v-if="isTravelGroup && travelOrderInfo && travelOrderInfo.travelGroupOrderInfo && travelOrderInfo.travelGroupOrderInfo.travelGroupProduct"
class="goodsStyle acea-row row-between"
>
<view class="pictrue">
<image :src="travelOrderInfo.travelGroupOrderInfo.travelGroupProduct.mainImage" class="image"/>
</view>
<view class="text acea-row row-between">
<view class="name line2">{{ travelOrderInfo.travelGroupOrderInfo.travelGroupProduct.name }}</view>
<view class="money">
<view>{{ travelOrderInfo.travelGroupOrderInfo.travelGroupProduct.price }}</view>
<view class="num">x1</view>
</view>
</view>
</view>
<view
v-else-if="orderCon.productInfo"
class="goodsStyle acea-row row-between"
>
<view class="pictrue">
@@ -78,6 +93,7 @@
</template>
<script>
import { postOrderComment, postOrderProduct } from '@/api/store'
import { fetchSojoumOrderDetail, sojoumOrderComment } from '@/api/sojoumOrder'
import { trim } from '@/utils'
import { required } from '@/utils/validate'
import { validatorDefaultCatch } from '@/utils/dialog'
@@ -104,6 +120,8 @@ export default {
productInfo: {}
}
},
isTravelGroup: false,
travelOrderInfo: null,
scoreList: [
{
name: '商品质量',
@@ -125,9 +143,19 @@ export default {
},
mounted() {
this.unique = this.$yroute.query.id
this.getOrderProduct()
this.isTravelGroup = this.$yroute.query.isTravelGroup === '1'
if (this.isTravelGroup) {
this.getTravelOrderDetail()
} else {
this.getOrderProduct()
}
},
methods: {
getTravelOrderDetail() {
fetchSojoumOrderDetail({ key: this.unique }).then(res => {
this.travelOrderInfo = res.data
})
},
getOrderProduct() {
postOrderProduct(this.unique).then(res => {
this.orderCon = res.data
@@ -165,14 +193,26 @@ export default {
} catch (e) {
return validatorDefaultCatch(e)
}
postOrderComment({
productScore: product_score,
serviceScore: service_score,
unique: this.unique,
pics: this.picArr.join(','),
comment: expect,
video: this.videoArr.join(',')
}).then(() => {
const pics = this.picArr
const video = this.videoArr[0] || ''
const request = this.isTravelGroup
? sojoumOrderComment({
orderId: this.unique,
comment: expect,
pics,
video,
productScore: product_score,
serviceScore: service_score
})
: postOrderComment({
productScore: product_score,
serviceScore: service_score,
unique: this.unique,
pics: this.picArr.join(','),
comment: expect,
video: this.videoArr.join(',')
})
request.then(() => {
uni.showToast({
title: '评价成功',
icon: 'success',
+42 -26
View File
@@ -119,7 +119,16 @@
{{ item.name }}
</view>
<view class="type">
<view v-if="item.typeName" class="type-tag">
<view
v-if="item.isTravel === 1"
class="type-tag-travel"
>
旅居店铺
</view>
<view
v-else-if="item.typeName"
class="type-tag"
>
{{ item.typeName }}
</view>
<view v-else>&nbsp;</view>
@@ -140,9 +149,17 @@
@click="goGoodsCon(good.productId)"
>
<image :src="good.productImage" class="img" />
<view class="price">
<text class="txt"></text>
{{ force2Decimal(good.productPrice) }}
<view class="price" style="width: fit-content;">
<text v-if="good.isNegotiable !== 1" class="txt"></text>
<text v-if="good.isNegotiable !== 1" class="">
{{ force2Decimal(good.productPrice) }}
</text>
<text
v-if="good.isNegotiable === 1"
class="v12-primary-text v12-font-28 v12-font-weight"
>
价格面议
</text>
</view>
</view>
</view>
@@ -175,7 +192,7 @@
:cartNum="cart_num"
:showOk="true"
@changeFun="changeFun"
@ok="handleOk"
@ok="handleOk(() => this.getCartCount())"
/>
</view>
</template>
@@ -188,9 +205,6 @@ import NP from 'number-precision'
import NoGoodData from '@/components/good/NoData'
import Recommend from '@/components/Recommend'
import goCartMixin from '@/mixins/goCartMixins'
import {
getCartCount,
} from '@/api/store'
export default {
name: 'GoodsList',
components: {
@@ -201,7 +215,6 @@ export default {
mixins: [goCartMixin],
data () {
return {
CartCount: 0,
webUrl: this.$VUE_APP_RESOURCES_URL,
list: [],
query: {
@@ -232,17 +245,7 @@ export default {
!this.loading && this.getList()
},
methods: {
// 获取购物车数量
getCartCount () {
const isLogin = this.isLogin
if (isLogin) {
getCartCount({
numType: 0
}).then(res => {
this.CartCount = res.data.count
})
}
},
goShoppingCart() {
this.$yrouter.switchTab('/pages/cart')
},
@@ -267,12 +270,18 @@ export default {
})
},
hotelItemClick(item) {
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.id
}
})
if (item.isTravel === 1) {
uni.navigateTo({
url: '/pkg_product/views/sojoumStore?id=' + item.id
})
} else {
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.id
}
})
}
},
getList() {
if (this.loading || this.loadend) {
@@ -537,6 +546,13 @@ export default {
color: #946300;
background: linear-gradient(to bottom, #FDF8B9, #E3A72C);
}
.type-tag-travel {
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
color: #fff;
background: linear-gradient(to bottom, #77E49F, #4FA379);
}
}
.desc {
display: flex;
+1 -1
View File
@@ -54,7 +54,7 @@
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt v12-font-28">{{ item.pv }}</text>
<text class="seeNum-txt v12-font-28">{{ $formatCount(item.pv) }}</text>
</view>
</view>
</view>
+3 -1
View File
@@ -211,7 +211,9 @@ export default {
},
getActive() {
const params = {
ancientTownId: this.id
ancientTownId: this.id,
page: 1,
pageSize: 9999
}
getAncientTownActive(params).then(res => {
this.activeties = res.data.records || []
+38 -2
View File
@@ -27,6 +27,13 @@
class="v12-white v12-radius-20 history-card"
@click="goodsDetail(historyItem)"
>
<view class="delete-icon" @click.stop="deleteHistoryItem(historyItem)">
<u-icon
name="close"
color="#fff"
size="16"
/>
</view>
<image
class="goods-img"
:src="historyItem.image"
@@ -83,7 +90,7 @@
</template>
<script>
import { getHistory } from '@/api/user'
import { getHistory, deleteHistory as deleteHistoryApi } from '@/api/user'
export default {
data() {
return {
@@ -179,6 +186,21 @@ export default {
}
})
},
deleteHistoryItem(historyItem) {
const id = historyItem.id
if (!id) return
uni.showModal({
title: '提示',
content: '确认删除该浏览足迹吗?',
success: (res) => {
if (res.confirm) {
deleteHistoryApi(id).then(() => {
this.queryHistory()
})
}
}
})
},
queryHistory() {
this.items = []
this.isLoad = false
@@ -245,6 +267,7 @@ export default {
margin-right: 16rpx;
border-radius: 20rpx;
overflow: hidden;
position: relative;
&:nth-child(3n) {
margin-right: 0;
}
@@ -271,6 +294,19 @@ export default {
}
}
}
.delete-icon{
position: absolute;
top: 8rpx;
right: 8rpx;
z-index: 2;
width: 32rpx;
height: 32rpx;
border-radius: 16rpx;
// background-color: rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
}
.active{
background: #C52733;
color: #fff !important;
@@ -283,4 +319,4 @@ export default {
border-radius: 50%;
}
</style>
</style>
+1
View File
@@ -402,6 +402,7 @@ export default {
}
},
onShow() {
uni.$emit('updateUnreadMessageCount')
if (this.$store.getters.token) {
this.isNoLogin = false
uni.showLoading({
+2 -2
View File
@@ -13,11 +13,11 @@
<view class="acea-row row-left row-middle">
<view class="see-num-box acea-row row-middle" style="margin-right: 20rpx;">
<image :src="webUrl+'/20230304131559277594.png'" class="eye-icon" mode=""></image>
<text class="see-num-text">{{item.pv||0}}</text>
<text class="see-num-text">{{$formatCount(item.pv||0)}}</text>
</view>
<view class="zan-num-box acea-row row-middle">
<img class="zan-icon" :src="webUrl+'/20230304133322439350.png'" />
<text class="zan-num-text">{{item.zan||0}}</text>
<text class="zan-num-text">{{$formatCount(item.zan||0)}}</text>
</view>
</view>
</view>
+45 -107
View File
@@ -20,7 +20,7 @@
:items="district"
></CitySelect>
</view>
<view class="iconfont icon-dizhi font-color-red" @click="getCurAddress()"></view>
<view class="iconfont icon-dizhi font-color-red" @click="getCurAddressFn()"></view>
</view>
</view>
<view class="item acea-row row-between-wrapper" style="flex-wrap: nowrap;">
@@ -32,7 +32,8 @@
<view class="address-analysis-wrap">
<view class="inp">
<textarea
v-model.trim="addressInput"
:value="addressInput"
@input="handleAddressInput"
placeholder="请粘贴收件人手机号、姓名、收货地址并用空格区分,系统将自动识别地址信息"
placeholder-style="font-size:24rpx;color:#C2C5CC;"
class="inp-textarea"
@@ -74,7 +75,7 @@
<script type="text/babel">
import CitySelect from '@/components/CitySelect'
import config from '@/utils/mapConfig'
import {getCurAddress, getLocation} from "@/utils/common"
import { getAddress, getCity, postAddress, analysisAddress } from '@/api/user'
import attrs, { chs_extphone, required } from '@/utils/validate'
import { validatorDefaultCatch } from '@/utils/dialog'
@@ -126,6 +127,12 @@ export default {
this.getUserAddress()
this.getCityList()
},
beforeDestroy() {
// 清除定时器,避免内存泄漏
if (this.inputTimer) {
clearTimeout(this.inputTimer);
}
},
methods: {
getIndex(value, arr) {
var idx = -1
@@ -141,6 +148,17 @@ export default {
if (!this.addressInput) return
this.addressInput = ''
},
handleAddressInput(e) {
// 清除之前的定时器
if (this.inputTimer) {
clearTimeout(this.inputTimer);
}
// 设置新的定时器,延迟300ms执行
this.inputTimer = setTimeout(() => {
this.addressInput = e.target.value.trim();
}, 300);
},
// 识别
distinguishHandle() {
if (!this.addressInput) return
@@ -174,113 +192,33 @@ export default {
})
},
//获取当前定位地址
getCurAddress() {
var that = this;
//定位
async getCurAddressFn() {
const that = this
uni.showLoading({
title: '定位中...'
});
//this.getLocation();
uni.getLocation({
type: 'wgs84',
success: function (res) {
let latitude = res.latitude;
let longitude = res.longitude;
console.log('latitude:' + latitude + 'longitude:' + longitude)
// #ifdef H5
Vue.jsonp(
'https://apis.map.qq.com/ws/geocoder/v1/?location=' + latitude + ',' + longitude + '&key=' + config.key,
{
//callbackName: 'QQmap',
output: 'jsonp',
}).then(json => {
// Success.
uni.hideLoading();
that.addressText =
json.result.ad_info.province + " " + json.result.ad_info.city + " " + json.result.ad_info.district;
that.address.province = json.result.ad_info.province;
that.address.city = json.result.ad_info.city;
that.address.district = json.result.ad_info.district;
that.$set(that.userAddress, 'detail', json.result.formatted_addresses.recommend);
//that.userAddress.detail = json.result.formatted_addresses.recommend;
// that.$refs.cityselect.adjustDefaultValue();
//设置city_id
var pIdx = that.getIndex(that.address.province, that.district);
if (pIdx > -1) {
var citys = that.district[pIdx].c;
var cIdx = that.getIndex(that.address.city, citys);
if (cIdx > -1) {
var city = citys[cIdx];
that.address.city_id = city.v;
console.log('city_id:' + city.v);
}
})
const map = await getLocation()
if (map.length > 1) {
const {latitude, longitude} = map[1]
getCurAddress(latitude, longitude, address => {
that.addressText = address.province + " " + address.city + " " + address.county
that.address.province = address.province
that.address.city = address.city
that.address.district = address.county
that.$set(that.userAddress, 'detail', address.formattedAddress)
// 设置city_id
const pIdx = that.getIndex(that.address.province, that.district)
if (pIdx > -1) {
const citys = that.district[pIdx].c
const cIdx = that.getIndex(that.address.city, citys)
if (cIdx > -1) {
const city = citys[cIdx]
that.address.city_id = city.v
console.log('city_id:' + city.v)
}
}).catch(err => {
// Failed.
console.log('地址解析失败:' + JSON.stringify(err));
uni.hideLoading();
})
// #endif
// #ifndef H5
uni.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/?location=' + latitude + ',' + longitude + '&key=' + config.key,
success: function (res) {
// console.log('res:'+JSON.stringify(res.data));
uni.hideLoading();
that.addressText =
res.data.result.ad_info.province + " " + res.data.result.ad_info.city + " " + res.data.result.ad_info.district;
that.address.province = res.data.result.ad_info.province;
that.address.city = res.data.result.ad_info.city;
that.address.district = res.data.result.ad_info.district;
//that.userAddress.detail = res.data.result.formatted_addresses.recommend;
that.$set(that.userAddress, 'detail', res.data.result.formatted_addresses.recommend);
// that.$refs.cityselect.adjustDefaultValue();
//设置city_id
var pIdx = that.getIndex(that.address.province, that.district);
if (pIdx > -1) {
var citys = that.district[pIdx].c;
var cIdx = that.getIndex(that.address.city, citys);
if (cIdx > -1) {
var city = citys[cIdx];
that.address.city_id = city.v;
console.log('city_id:' + city.v);
}
}
},
fail: function (res) {
console.log('地址解析失败');
uni.hideLoading();
},
complete: function () {
}
});
// #endif
},
fail: function (res) {
uni.hideLoading();
// uni.showToast({
// title:'城市定位失败:'+JSON.stringify(res),
// icon:'none',
// duration:2000
// })
}
});
}
})
}
},
getCityList: function () {
let that = this;
+28 -2
View File
@@ -9,6 +9,9 @@
</view>
<view style="position: relative;" class="item" v-for="(item, addressListIndex) in addressList"
:key="addressListIndex" @click="tapAddress(item)">
<view v-if="choosMode" @click.stop="editAddress(addressListIndex)" style="position: absolute; right: 20rpx; top: 0; z-index: 10; padding: 0;" :style="item.isDefault||item.isDefault=='1' ? 'right: 80rpx' : ''">
<text class="iconfont icon-bianji" style="color: #999; font-size: 32rpx;"></text>
</view>
<view v-if="item.isDefault||item.isDefault=='1' ? true : false" class=""
style="position: absolute;top: 0;right: 0;">
<image style="width: 72rpx;height: 36rpx;" :src="webUrl+'/20210807165228290910.png'" mode=""></image>
@@ -27,7 +30,7 @@
<u-icon name="checkbox-mark" color="#C52733" size="20"></u-icon>
</view>
</view>
<view v-if="!choosMode" class="operation acea-row row-between-wrapper">
<view class="operation acea-row row-between-wrapper" v-if="!choosMode">
<view class="select-btn">
<view class="checkbox-wrapper">
<checkbox-group @change.stop="radioChange(item.id)">
@@ -147,6 +150,11 @@ export default {
uni.setStorageSync('giftChooseAddress', item)
this.$yrouter.back()
}
// 来源修改订单地址(复用订单创建页的choosMode但避免污染chooseAddress
if (this.choosMode === 4) {
uni.setStorageSync('updateOrderAddress', item)
this.$yrouter.back()
}
},
refresh() {
this.addressList = []
@@ -172,7 +180,7 @@ export default {
this.loadend = res.data.length < this.limit
this.page = this.page + 1
if (this.choosMode === 1) {
this.currentId = uni.getStorageSync('chooseAddress') ? uni.getStorageSync('chooseAddress').id : this.addressList.find(e => e.isDefault === 1) ? this.addressList.find(e => e.isDefault === 1).id : null
this.currentId = uni.getStorageSync('sourceAddress') ? uni.getStorageSync('sourceAddress').id : this.addressList.find(e => e.isDefault === 1) ? this.addressList.find(e => e.isDefault === 1).id : null
}
// 来源购物车
if (this.choosMode === 2) {
@@ -182,6 +190,24 @@ export default {
if (this.choosMode === 3) {
this.currentId = uni.getStorageSync('giftChooseAddress') ? uni.getStorageSync('giftChooseAddress').id : this.addressList.find(e => e.isDefault === 1) ? this.addressList.find(e => e.isDefault === 1).id : null
}
// 来源修改订单地址
if (this.choosMode === 4) {
const sourceAddress = uni.getStorageSync('updateOrderSourceAddress') || {}
let currentId = sourceAddress.id || null
if (!currentId && sourceAddress.userAddress) {
const sourceAddressText = String(sourceAddress.userAddress).replace(/\s+/g, '')
const sourceRealName = String(sourceAddress.realName || '').trim()
const sourcePhone = String(sourceAddress.phone || '').trim()
const matched = this.addressList.find(e => {
const addressText = `${e.province || ''}${e.city || ''}${e.district || ''}${e.detail || ''}`.replace(/\s+/g, '')
const sameAddress = sourceAddressText && addressText === sourceAddressText
const samePerson = sourceRealName && sourcePhone && String(e.realName || '').trim() === sourceRealName && String(e.phone || '').trim() === sourcePhone
return sameAddress || samePerson
})
currentId = matched ? matched.id : null
}
this.currentId = currentId || (this.addressList.find(e => e.isDefault === 1) ? this.addressList.find(e => e.isDefault === 1).id : null)
}
})
},
/**
+2 -2
View File
@@ -273,7 +273,7 @@
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt">{{ item.pv }}</text>
<text class="seeNum-txt">{{ $formatCount(item.pv) }}</text>
</view>
<view class="flex">
<image
@@ -292,7 +292,7 @@
v-show="item.zan > 0"
:class="item.zanVo ? 'on' : ''"
class="seeNum-txt"
>{{ item.zan }}</text>
>{{ $formatCount(item.zan) }}</text>
</view>
</view>
</view>
+1 -1
View File
@@ -44,7 +44,7 @@
<view class="">
<CitySelect
ref="cityselect"
:defaultValue="addressText"
:defaultValue="addressText || ''"
@callback="result"
:items="district"
></CitySelect>
+120 -6
View File
@@ -272,7 +272,7 @@
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt">{{ item.pv }}</text>
<text class="seeNum-txt">{{ $formatCount(item.pv) }}</text>
</view>
<view class="flex">
<image
@@ -291,7 +291,15 @@
v-show="item.zan > 0"
:class="item.zanVo ? 'on' : ''"
class="seeNum-txt"
>{{ item.zan }}</text>
>{{ $formatCount(item.zan) }}</text>
</view>
<view class="flex" @click="infoClick(item)">
<!-- <image
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/> -->
<u-icon name="chat" size="24"></u-icon>
<text class="seeNum-txt">{{ $formatCount(item.replyCount || 0) }}</text>
</view>
<view v-if="isMyInn" class="flex more-btn" @click="toggleMoreHandle(index)">
<image
@@ -335,6 +343,77 @@
</view>
</view>
</view>
<view class="comment-list v12-px-3" v-if="item.latestReplyList" @click="infoClick(item)">
<view class="v12-mt-2" v-for="(comment, i) in item.latestReplyList" :key="i">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar :src="comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ comment.userNickName }}</text>
<text v-if="comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(comment.createTime) }}</text>
<text v-if="comment.at">
{{ ' 回复 ' + comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-d-flex" >
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
{{ comment.reply }}
</view>
<view v-if="comment.children">
<view class="v12-mt-2" v-for="(_comment, atIndex) in comment.children" :key="atIndex">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar size="35" :src="_comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ _comment.userNickName }}</text>
<text v-if="_comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(_comment.createTime) }}</text>
<text v-if="_comment.at && _comment.parentReplyId !== comment.id">
{{ ' 回复 ' + _comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-align-center">
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar size="35"></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
{{ _comment.reply }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
@@ -453,7 +532,7 @@ import {
getHotelNewsZan,
getHotelShareBackgroundImage,
hotelNewsView,
getMerchantApply
getMerchantApply,
} from "@/api/inn.js"
import {formatDateTime} from "@/utils"
import {getUrlParam} from "@/utils/common.js"
@@ -508,7 +587,8 @@ export default {
isLoadInfoListSuccess: false,
pageKeyId: '',
shareImg: '',
videoPlayers: []
videoPlayers: [],
isView: false
}
},
// 必须在页面加 onPageScroll(e){} ,才能滑动显示背景
@@ -583,6 +663,10 @@ export default {
})
},
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return {
title: this.inn.name,
path: '/pagesInn/inn/innHome?id=' + this.inn.id,
@@ -590,13 +674,21 @@ export default {
}
},
onShow() {
console.log(this.infoArray);
if (!this.isLoad) {
return
}
this.fetchInnDetail()
if(this.isView) return
if(this.activeIndex === 1) {
this.fetchInnInfoList()
return
}
if (uni.getStorageSync('hotelInfoPublish') === '1') {
this.fetchInnInfoList()
return
}
this.fetchInnDetail()
},
watch: {
activeIndex: {
@@ -611,6 +703,25 @@ export default {
}
},
methods: {
formatterTime(time) {
const now = new Date();
const date = new Date(time);
const diff = now - date;
const diffMin = Math.floor(diff / 1000 / 60);
const diffHour = Math.floor(diff / 1000 / 60 / 60);
const diffDay = Math.floor(diff / 1000 / 60 / 60 / 24);
if (diffMin < 1) {
return "刚刚";
} else if (diffHour < 1) {
return diffMin + "分钟前";
} else if (diffDay < 1) {
return diffHour + "小时前";
} else if (diffDay < 7) {
return diffDay + "天前";
} else {
return date.getFullYear() + "年" + (date.getMonth() + 1) + "月" + date.getDate() + "日";
}
},
goHome(e) {
this.id = e.id
this.isLoad = false
@@ -619,6 +730,7 @@ export default {
} else {
this.fetchInnInfoList()
}
this.initPoster()
this.fetchInnDetail()
// this.fetchGoods(this.id)
},
@@ -722,7 +834,7 @@ export default {
})
},
infoClick: function (info) {
// this.$yrouter.push('/pagesInn/inn/innInfoDetail?id=' + info.id)
this.$yrouter.push('/pagesInn/inn/innInfoDetail?id=' + info.id)
},
coverPlay(){
this.videoPlayers.forEach(player => {
@@ -752,6 +864,7 @@ export default {
}
uni.$u.debounce(() => {
hotelNewsView({ id: item.id }).finally(() => {
this.isView = true
this.infoArray.map(info => {
if (info.id === item.id) {
info.pv += 1
@@ -900,6 +1013,7 @@ export default {
_this.isLoad = true
}
}).finally(() => {
this.isView = false
uni.stopPullDownRefresh()
})
},
+857 -51
View File
@@ -1,83 +1,884 @@
<template>
<view class="pages-inn-info-detail">
<view class="top-box">
<view class="title more-t">{{ info.name }}</view>
<view class="sub flex jc-between ai-center">
<text class="datetime">{{ info.createdTime }}</text>
<view class="flex ai-center">
<view class="flex ai-center" style="margin-right: 28rpx">
<image :src="webUrl+'/20230803152147141807.png'"/>
<view class="num">{{ info.pv }}</view>
<view class="info-section">
<view class="info-item">
<view
:class="item.isTop === 1 ? 'info-item-top' : ''"
:style="{
'background-image': item.isTop
? 'url(' + webUrl + '/page/hotel/info-top-bg.png)'
: 'none',
}"
class="info-item-header"
>
<view class="name">
{{ item.name }}
</view>
<view class="intro">
{{ item.intro }}
</view>
</view>
<view class="info-item-body">
<view v-if="item.type === 'NT_VIDEO'" class="video">
<video
:src="item.video"
:poster="item.video + '?vframe/jpg/offset/1'"
controls
play-btn-position="center"
class="video-item"
:id="'video-item' + index"
/>
</view>
<view v-else>
<img-box
v-if="item.resources"
:imgList="item.resources"
:num="item.resources.length"
:img-radius="10"
style="width: 100%"
/>
</view>
</view>
<view class="info-item-bottom">
<view class="time">
{{
item.publishTime
? item.publishTime.replace(/-/g, ".").substr(0, 10)
: ""
}}
</view>
<view class="btns">
<view class="flex">
<image :src="webUrl + '/20230803152147141807.png'" class="icon" />
<text class="seeNum-txt">{{ $formatCount(item.pv) }}</text>
</view>
<view class="flex ai-center" @click="likeInfo">
<image :src="webUrl+'/20230803151308999742.png'" v-if="info.zanVo != null"/>
<image :src="webUrl+'/20230803151302213857.png'" v-else/>
<view class="num">{{ info.zan }}</view>
<view class="flex">
<image
v-if="!item.zanVo"
:src="webUrl + '/page/hotel/zan-off-2.png'"
class="zan-off-icon"
@click="likeInfo"
/>
<image
v-else
:src="webUrl + '/page/hotel/zan-on-2.png'"
class="zan-on-icon"
@click="likeInfo"
/>
<text
v-show="item.zan > 0"
:class="item.zanVo ? 'on' : ''"
class="seeNum-txt"
>{{ $formatCount(item.zan) }}</text
>
</view>
<view class="flex">
<u-icon name="chat" size="24"></u-icon>
<text class="seeNum-txt">{{ $formatCount(item.replyCount || comments.length) }}</text>
</view>
</view>
</view>
</view>
<view class="video-box" v-if="info.type==='NT_VIDEO'">
<video style="width: 100%;height: 320rpx;" :src="info.video" controls play-btn-position="center"/>
<view class="v12-px-2">资讯评论</view>
<view v-if="comments.length === 0" class="v12-text-center v12-py-2 v12-dark1-text v12-font-24">
暂无评论
</view>
<view class="content-box">
<rich-text :nodes="info.content"/>
<view class="comment-list">
<view class="v12-mt-2" v-for="(comment, index) in comments" :key="index" @click="selectComment(comment)">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar :src="comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ comment.userNickName }}</text>
<text v-if="comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(comment.createTime) }}</text>
<text v-if="comment.at">
{{ ' 回复 ' + comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-d-flex" >
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
{{ comment.reply }}
</view>
<view v-if="comment.children">
<view class="v12-mt-2" v-for="(_comment, atIndex) in comment.children" :key="atIndex" @click.stop="selectComment(_comment)">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar size="35" :src="_comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ _comment.userNickName }}</text>
<text v-if="_comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(_comment.createTime) }}</text>
<text v-if="_comment.at && _comment.parentReplyId !== comment.id">
{{ ' 回复 ' + _comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-align-center">
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar size="35"></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
<!-- <text class="v12-dark1-text" v-if="_comment.at && _comment.parentReplyId !== comment.id">{{ "@" +_comment.at.atNickName }}</text> -->
{{ ' ' + _comment.reply }}
</view>
</view>
</view>
</view>
<view v-if="comment.hasMoreChildren" class="more-text" @click.stop="showMoreComment(comment)">
{{ ' 点击展开更多回复 ' }}
</view>
</view>
</view>
</view>
</view>
</view>
<view class="comment-input-wrap">
<view style="width: 100%;">
<u-textarea
v-model="reply"
:prefixIcon="prefixIcon"
:maxlength="500"
:maxheight="200"
:showConfirmBar="false"
:autoHeight="reply.length < 40 ? true : false"
cursorSpacing="15"
placeholder="分享你的观点,让世界听见你的声音..."
class="comment-input"
@focus="commentInputFocus"
@change="commentInputChange"
>
<template slot="prefix">
<view class="v12-align-center">
<u-icon :name="prefixIcon"></u-icon>
<u--text
v-if="currentComment.userNickName"
:text="'@' + currentComment.userNickName"
margin="0 3px 0 0"
type="tips"
></u--text>
</view>
</template>
</u-textarea>
</view>
<view class="">
<view class="send-btn v12-primary v12-white-text" @click="sendComment">发送</view>
</view>
</view>
</view>
</template>
<script>
import {getHotelNewsDetail, getHotelNewsZan} from "@/api/inn";
import { getHotelNewsDetail, getHotelNewsZan, getCommentList, addComment } from "@/api/inn"
import imgBox from '@/components/imageTypeSet/imagebox.vue'
export default {
components: {
imgBox
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
id: null,
info: null
item: {},
comments: [],
reply: '',
atUid: '',
currentComment: {},
prefixIcon: 'edit-pen'
}
},
onLoad(options) {
this.id = options.id;
this.init();
this.id = options.id
this.init()
},
methods: {
init() {
getHotelNewsDetail(this.id).then(({data}) => this.info = data);
},
likeInfo() {
let that = this;
uni.showLoading();
getHotelNewsZan(this.id).then((res) => {
uni.hideLoading();
uni.showToast({
title: res.msg,
mask: false,
duration: 1500,
success() {
if (res.status === 200) that.info = res.data;
}
})
}).catch((err) => {
uni.hideLoading()
uni.showToast({
title: err.msg + '',
icon: 'none',
mask: false,
duration: 1500
});
showMoreComment(comment) {
const params = {
contentId: this.id,
contentType: "hotelNews",
rootReplyId: comment.id,
page: 1,
limit: 99999
}
getCommentList(params).then((res) => {
comment.children = res.data.records
comment.hasMoreChildren = false
})
},
commentInputChange(e) {
// 若删除直接将@用户名整个删除
if (e === '') {
this.currentComment = {}
return
}
// 若删除@用户名,将@用户名整个删除
const usernameLength = `@${this.currentComment.userNickName} `.length
if(!e.includes(`@${this.currentComment.userNickName} `) && this.currentComment.userNickName) {
this.currentComment = {}
// 去除@用户名
this.reply = e.slice(usernameLength - 1)
}
},
commentInputFocus(e) {
// this.prefixIcon = ''
},
selectComment(comment) {
this.currentComment = comment
this.reply = '@' + comment.userNickName + ' '
},
/**
* 格式化时间
* @param {*} time 时间
* @returns 距离当前时间的时间差
* 1. 小于1分钟,显示刚刚
* 2. 小于1小时,显示xx分钟前
* 3. 小于1天,显示xx小时前
* 4. 超过7天,显示日期 xxxx年xx月xx日
*/
formatterTime(time) {
const now = new Date()
const date = new Date(time)
const diff = now - date
const diffMin = Math.floor(diff / 1000 / 60)
const diffHour = Math.floor(diff / 1000 / 60 / 60)
const diffDay = Math.floor(diff / 1000 / 60 / 60 / 24)
if (diffMin < 1) {
return "刚刚"
} else if (diffHour < 1) {
return diffMin + "分钟前"
} else if (diffDay < 1) {
return diffHour + "小时前"
} else if (diffDay < 7) {
return diffDay + "天前"
} else {
return date.getFullYear() + "年" + (date.getMonth() + 1) + "月" + date.getDate() + "日"
}
},
/**
* 发评论
*/
sendComment() {
if (!this.reply.trim()) {
uni.showToast({
title: '请输入评论内容',
icon: 'none'
})
return
}
if(this.reply.trim() === ('@' + this.currentComment.userNickName)) {
// 请输入回复内容
uni.showToast({
title: '请输入回复内容',
icon: 'none'
})
return
}
uni.showLoading({
mask: true
})
const params = {
contentId: this.id,
contentType: 'hotelNews',
parentReplyId: this.currentComment.parentId || this.currentComment.id,
reply: (this.reply.replace('@' + this.currentComment.userNickName, '')).trim(),
atUid: this.currentComment.uid || null
}
addComment(params).then((res) => {
uni.hideLoading()
this.init()
this.reply = ''
this.currentComment = {}
this.item.replyCount === null ? this.item.replyCount = 1 : this.item.replyCount++
})
},
init() {
getHotelNewsDetail(this.id).then(({ data }) => (this.item = data))
this.getComment()
},
// contentId int 评论的目标内容ID,必传
// contentType string 评论的目标内容类型,必传(店铺资讯传入值hotelNews)
// rootReplyId int 根评论ID
// page int 页码
// limit int 每页数量
getComment() {
const params = {
contentId: this.id,
contentType: "hotelNews",
page: 1,
limit: 99999
}
getCommentList(params).then((res) => {
this.comments = res.data.records
})
},
likeInfo() {
uni.showLoading({
mask: true
})
getHotelNewsZan(this.id).then((res) => {
uni.hideLoading()
getHotelNewsDetail(this.id).then(({ data }) => (this.item = data))
})
}
}
}
</script>
<style lang="less">
@import "@/assets/css/store.less";
.comment-list{
padding-bottom: 100rpx;
}
.more-text{
color:#1B76F0;
font-weight: 400;
font-size: 28rpx;
}
.comment-input-wrap{
padding: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: -webkit-sticky;
position: fixed;
width: 100%;
box-sizing: border-box;
bottom: 0;
background: #fff;
left: 0;
}
.comment-input{
height: 70rpx;
background: rgba(246,246,246,0.39);
border-radius: 12rpx;
}
.send-btn{
border-radius: 16rpx;
width: 96rpx;
height: 70rpx;
line-height: 70rpx;
text-align: center;
margin-left: 30rpx;
}
.my-inn-page {
padding: 0 0 100rpx 0;
background-color: #f6f6f6;
}
.cover-box {
position: relative;
.full-img {
width: 750rpx;
}
.video {
display: block;
width: 100%;
height: 100%;
}
}
.guarantee {
margin-right: 16rpx;
padding: 8rpx 16rpx;
border-radius: 8rpx;
background: #0DC5C5;
color: #fff;
font-size: 22rpx;
line-height: 1;
}
.zan-favorite-wrap {
font-size: 32rpx;
color: #333;
.on {
color: #FF564A;
}
}
.zan-box {
.img {
width: 36rpx;
height: 36rpx;
margin: 0 8rpx 0 0;
vertical-align: middle;
}
}
.favorite-box {
.icon {
width: 36rpx;
height: 36rpx;
margin: 0 8rpx 0 30rpx;
}
}
.store-img {
width: 686rpx;
margin-bottom: 20rpx;
vertical-align: middle;
}
.goods-section {
padding: 40rpx 32rpx 0;
background: #F5F5FA;
.item {
position: relative;
width: 332rpx;
height: 528rpx;
box-sizing: border-box;
margin-right: 22rpx;
margin-bottom: 24rpx;
border-radius: 8rpx;
background: #fff;
.name {
padding: 0 6rpx;
font-size: 26rpx;
color: #333333;
}
.price-line {
padding: 0 10rpx;
font-size: 26rpx;
line-height: 38rpx;
color: #ED0F0F;
}
.sales-line {
position: absolute;
bottom: 0;
width: 100%;
padding: 8rpx 10rpx 18rpx;
border-top: 1rpx solid #EFEFEF;
font-size: 24rpx;
line-height: 34rpx;
color: #8D8D8D;
}
}
.item:nth-child(2n) {
margin-right: 0;
}
.cover {
width: 332rpx;
height: 332rpx;
background: rgba(255, 255, 255, 0.39);
border-radius: 8rpx;
vertical-align: middle;
}
.label {
width: 40rpx;
height: 24rpx;
margin-right: 4rpx;
vertical-align: middle;
}
.icon {
width: 24rpx;
height: 24rpx;
margin-right: 6rpx;
vertical-align: middle;
}
.btn {
display: flex;
padding: 20rpx 0 40rpx 0;
justify-content: center;
.btn-cont {
padding: 13rpx 30rpx;
border: 1px solid #0DC5C5;
border-radius: 8rpx;
color: #0DC5C5;
.iconfont {
margin: 0 0 0 20rpx;
font-size: 24rpx;
}
}
}
}
.container {
position: relative;
background: #FFFFFF;
}
.innInfoWrap {
position: relative;
margin-top: -76rpx;
padding: 20rpx 36rpx;
line-height: 1;
z-index: 1;
.bg-color {
position: absolute;
top: -5px;
left: 0;
right: 0;
z-index: 1;
height: 50%;
background: linear-gradient(-180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%);
.box-mask {
position: absolute;
bottom: 0;
right: 0;
left: 166rpx;
box-sizing: border-box;
padding: 0 32rpx 12rpx 0;
.city-section {
width: 256rpx;
margin-left: 20rpx;
}
}
}
.body-cont {
position: relative;
z-index: 2;
}
}
.inn-logo {
flex-shrink: 0;
width: 56*2rpx;
height: 56*2rpx;
margin-right: 18rpx;
}
.inn-logo image {
width: 100%;
height: 100%;
border-radius: 50%;
}
.inn-name {
width: 280rpx;
font-size: 32rpx;
color: #FFFFFF;
line-height: 48rpx;
}
.inn-owner {
font-size: 24rpx;
color: #8B8F99;
}
.inn-tag-wrap {
margin-top: 10rpx;
}
.inn-signature-wrap {
position: relative;
z-index: 5;
padding: 0 32rpx;
margin: 20rpx 0 0 0;
.triangle {
position: absolute;
top: -20rpx;
left: 72rpx;
width: 0;
height: 0;
border-left: 20rpx solid transparent;
border-right: 20rpx solid transparent;
border-bottom: 24rpx solid rgba(243,243,246,0.39);
}
.inn-signature {
position: relative;
padding: 20rpx;
border-radius: 8rpx;
font-size: 24rpx;
color: #080F1A;
background: rgba(243,243,246,0.39);
box-shadow: 0px 6px 12px rgba(0,0,0,0.12);
}
.share-btn {
position: absolute;
right: 0;
bottom: -68rpx;
.img {
width: 212rpx;
height: 60rpx;
}
}
}
.btLine {
width: 100%;
height: 6rpx;
margin-top: 18rpx;
background: #0DC5C5;
}
.pics-box {
margin: 36rpx;
background: #fff;
}
.pics-list {
position: relative;
padding: 32rpx 0;
flex-wrap: wrap;
}
.pics-list .pics-item {
margin-left: 36rpx;
margin-top: 20rpx;
}
.bottom-view {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
height: 70rpx;
padding: 15rpx;
border-top: 1px solid #f1f1f1;
background: #FFFFFF;
.bottom-btn {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #333;
line-height: 70rpx;
&:first-child {
border-right: 1px solid #d5d5d5;
}
.img {
width: 40rpx;
height: 40rpx;
margin: 0 12rpx 0 0;
}
}
}
.tab-item-active {
color:#080F1A;
font-size:32rpx;
}
.tab-item-no-active {
color:#A6AAB3;
font-size:28rpx;
}
.map-wrapper {
position: relative;
z-index: 2;
height: 200rpx;
margin: 40rpx 0 0 0;
overflow: hidden;
.map-cont {
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
opacity: 0.3;
transform: translateY(-50rpx);
}
.bg {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 3;
height: 60rpx;
background: linear-gradient(180deg, #FFFFFF 0%, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
}
.map-info {
position: relative;
z-index: 5;
display: flex;
align-items: center;
padding: 40rpx 10rpx 40rpx 32rpx;
color: #333;
font-size: 14px;
justify-content: space-between;
.map-info-hd {
.icon {
display: block;
width: 56rpx;
height: 56rpx;
}
}
.map-info-bd {
flex: 1;
padding: 0 60rpx 0 20rpx;
line-height: 60rpx;
}
.map-info-ft {
.item + .item {
margin: 0 0 0 6rpx;
}
.item {
text-align: center;
font-weight: 500;
.img {
display: block;
width: 96rpx;
height: 96rpx;
margin: 0 0 -16rpx 0;
}
}
}
}
}
.top-tab {
margin: 60rpx 0 0 0;
border-bottom: 1px solid #ddd;
.tab {
margin: 0 35rpx;
&.hide {
display: none;
}
}
}
.info-section,
.pics-section,
.qualifications-section {
position: relative;
background-color: #fff;
}
.info-section,
.qualifications-section {
padding: 32rpx;
}
.info-section {
word-break: break-all;
min-height: 100vh;
.info-item + .info-item {
margin: 32rpx 0 0 0;
}
.info-item {
border-radius: 10rpx;
background-color: #fff;
.info-item-header {
position: relative;
padding: 20rpx 20rpx 0 20rpx;
&.info-item-top {
padding-top: 60rpx;
background-size: 100% 80rpx;
background-repeat: no-repeat;
background-image: url();
}
.name {
font-size: 16px;
color: #333;
font-weight: bold;
}
.intro {
margin: 10rpx 0 0 0;
color: #666;
font-size: 14px;
}
}
.info-item-body {
padding: 0 20rpx 20rpx 20rpx;
.video {
padding: 20rpx 0 0 0;
.video-item {
width: 100%;
height: 400rpx;
border-radius: 10rpx;
}
}
}
.info-item-bottom {
display: flex;
align-items: center;
padding: 0 20rpx 20rpx 20rpx;
margin-bottom: 20rpx;
border-bottom: 1px solid #f1f1f1;
font-size: 16px;
.time {
flex: 1;
color: #333;
}
.btns {
display: flex;
color: #999;
.flex + .flex {
margin: 0 0 0 32rpx;
}
.flex {
align-items: center;
}
.icon {
width: 32rpx;
height: 32rpx;
margin: 0 8rpx 0 0;
}
.zan-off-icon {
width: 40rpx;
height: 40rpx;
}
.zan-on-icon {
width: 40rpx;
height: 40rpx;
}
.seeNum-txt {
&.on {
color: #D81E06;
}
}
.more-btn {
position: relative;
.more-wrapper {
position: absolute;
right: -20rpx;
top: -150rpx;
z-index: 5;
width: 200rpx;
border-radius: 12rpx;
background-color: #fff;
box-shadow: 0px 1px 6px rgba(0,0,0,0.16);
.more-item + .more-item {
border-top: 1px solid #d5d5d5;
}
.more-item {
display: flex;
align-items: center;
justify-content: center;
height: 70rpx;
font-size: 14px;
color: #333;
.more-icon {
width: 36rpx;
height: 36rpx;
margin: 0 10rpx 0 0;
}
}
}
}
}
}
}
}
.tab-no-data {
text-align: center;
font-size: 32rpx;
color: #999;
line-height: 200rpx;
}
.pages-inn-info-detail {
.top-box {
padding: 40rpx 32rpx 20rpx;
@@ -123,4 +924,9 @@ export default {
box-sizing: border-box;
}
}
.user-info{
view{
word-break: break-all;
}
}
</style>
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -77,11 +77,15 @@ export default {
},
methods: {
async getMapData() {
const map = await getLocation();
uni.showLoading({
title: '定位中...'
})
const map = await getLocation()
if (map.length > 1) {
const {latitude, longitude} = map[1];
const address = await getCurAddress(latitude, longitude);
this.cityName = address[1].data.result['ad_info']['city']
const {latitude, longitude} = map[1]
getCurAddress(latitude, longitude, address => {
this.cityName = address.city
})
}
},
+8 -1
View File
@@ -40,7 +40,7 @@
v-if="showTake">
<view class="btn-box flex">
<image style="width: 220rpx;height: 66rpx" :src="webUrl+'/20240124210952306625.png'" mode="scaleToFill"
@click="navToCoupons"/>
@click="navToPoint"/>
<image style="width: 220rpx;height: 66rpx;margin-left: 16rpx;" :src="webUrl+'/20240124210928974187.png'"
mode="scaleToFill" @click="closeTakeModel"/>
</view>
@@ -132,6 +132,10 @@ export default {
this.init()
},
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return {
title: '探寻有趣有味的生活方式',
path: '/pkg-video/views/lottery',
@@ -139,6 +143,9 @@ export default {
}
},
methods: {
navToPoint() {
uni.navigateTo({url: '/pages/user/Points/index'})
},
init() {
getDetail().then(res => {
if (res.success) {
+13 -10
View File
@@ -33,18 +33,18 @@
<cover-image style="width: 60rpx;height: 60rpx" :src="webUrl+'/20240113230547221591.png'"
v-if="item['hasLike']"/>
<cover-image style="width: 60rpx;height: 60rpx" :src="webUrl+'/20240113230552152511.png'" v-else/>
<cover-view class="one-t tc">{{ item['likeCount'] }}</cover-view>
<cover-view class="one-t tc">{{ $formatCount(item['likeCount']) }}</cover-view>
</cover-view>
<cover-view class="flex flex-col ai-center" style="width:78rpx;margin-right: 16rpx;"
@click="changeFav(item)">
<cover-image style="width: 60rpx;height: 60rpx" :src="webUrl+'/20240113230535491918.png'"
v-if="item['hasFavorite']"/>
<cover-image style="width: 60rpx;height: 60rpx" :src="webUrl+'/20240113230541207016.png'" v-else/>
<cover-view class="one-t tc">{{ item['favoriteCount'] }}</cover-view>
<cover-view class="one-t tc">{{ $formatCount(item['favoriteCount']) }}</cover-view>
</cover-view>
<cover-view class="flex flex-col ai-center" style="width:78rpx" @click="showDialog=true">
<cover-image style="width: 60rpx;height: 60rpx" :src="webUrl+'/20240113230529202260.png'"/>
<cover-view class="one-t tc">{{ item['replyCount'] }}</cover-view>
<cover-view class="one-t tc">{{ $formatCount(item['replyCount']) }}</cover-view>
</cover-view>
</cover-view>
</cover-view>
@@ -56,7 +56,7 @@
@close="dialogClose" @open="dialogOpen">
<view style="z-index: 100">
<cover-view class="tc" style="padding: 20rpx 0 40rpx;font-size: 28rpx">{{
list[current]['replyCount']
$formatCount(list[current]['replyCount'])
}}条评论
</cover-view>
<scroll-view style="max-height: 740rpx" scroll-y @scrolltolower="fetchReply">
@@ -140,14 +140,17 @@ export default {
},
async getMapData() {
const map = await getLocation();
uni.showLoading({
title: '定位中...'
})
const map = await getLocation()
if (map.length > 1) {
const {latitude, longitude} = map[1];
const address = await getCurAddress(latitude, longitude);
this.cityName = address[1].data.result['ad_info']['city']
const {latitude, longitude} = map[1]
getCurAddress(latitude, longitude, address => {
this.cityName = address.city
this.fetchList()
})
}
await this.fetchList()
},
async fetchList() {
+336
View File
@@ -0,0 +1,336 @@
<template>
<view class=" call-message">
<view class="comment-list v12-pa-3">
<view
v-for="(comment, index) in list"
:key="index"
class="v12-mt-2"
>
<view class="user-info v12-align-center" @click="toInfo(comment)">
<view class="user-avatar">
<u-avatar :src="comment.senderUserAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ comment.senderUserNickname }}</text>
<text v-if="comment.senderIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-dark2-text v12-mt-1">
<text>{{ (comment.isRootReply ? ' 评论了你的资讯内容 ' : '回复了你的评论 ') + timeFormat(comment.senderTime) }}</text>
<text v-if="comment.at">
{{ ' 回复 ' + comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-d-flex" >
<view class="user-avatar" @click="toInfo(comment)">
<view style="opacity: 0;">
<u-avatar></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3" >
<view
class="v12-font-30 v12-my-2 v12-dark-text"
@click="toInfo(comment)"
>
<!-- {{ comment.parentReply }} -->
<!-- <text class="v12-dark1-text">{{ "@" + comment.senderUserNickname }}</text> -->
<text
v-if="comment.replyAt"
class="v12-dark1-text"
>{{ "@" + comment.replyAt.atNickName + ' ' }}</text>
{{ comment.reply }}
</view>
<view v-if="!comment.isRootReply" class="v12-font-28 v12-dark2-text" @click="toInfo(comment)">
{{ comment.parentReply }}
</view>
<view class="v12-mt-3">
<u-button
size="normal"
:customStyle="{
backgroundColor: '#eee',
width: '160rpx',
height: '50rpx',
border: '1px solid #eee',
color: '#777',
margin: 0
}"
plain
icon="chat"
shape="circle"
iconColor="#777"
@click.stop="showSendBtn(comment)"
>回复</u-button>
</view>
</view>
</view>
</view>
</view>
<view v-show="showBtn" class="v12-align-center v12-justify-center v12-pa-3 v12-white sticky-bottom" :style="{ bottom: inputBoxBottom + 'px' }">
<view style="width: 100%">
<u-textarea
id="input"
fixed
v-model="content"
:maxlength="500"
:maxheight="200"
:autoHeight="content.length < 40 ? true : false"
:showConfirmBar="false"
:focus="autoFocus"
:cursor="cursorIndex"
:customStyle="{
backgroundColor: '#f7f7f7',
border: '1px solid #f7f7f7',
borderRadius: '20rpx'
}"
:adjustPosition="false"
cursorSpacing="85"
@blur="onBlur"
@focus="commentInputFocus"
@change="commentInputChange"
>
<template slot="prefix">
<view class="v12-align-center">
<u-icon :name="'edit-pen'"></u-icon>
<u--text
v-if="currentComment.senderUserNickname"
:text="'@' + currentComment.senderUserNickname"
margin="0 3px 0 0"
type="tips"
></u--text>
</view>
</template>
</u-textarea>
</view>
<view class="v12-primary v12-white-text send-btn" @click="replyMessageHandle">
发送
</view>
</view>
</view>
</template>
<script>
import { getMessageList, replyMessage } from '@/api/rooms'
export default {
data() {
return {
// 消息列表
list: [
// parentReply string 被回复的原始评论
// reply string 回复评论内容
// contentId int 评论的目标内容ID
// contentType string 评论的目标内容类型
// senderUid int 发送者ID
// senderUserAvatar string 发送者头像
// senderUserNickname string 发送者昵称
// senderIsAdmin boolean 发送者是否是店主
// senderTime string 发送时间(yyyy-MM-dd HH:mm:ss
// {
// contentId: 1,
// parentReply: '这是一条评论',
// reply: '这是一条回复',
// contentType: 'hotelNews',
// senderUid: 1,
// senderUserAvatar: '',
// senderIsAdmin: true,
// senderUserNickname: '张三',
// senderTime: '2022-01-01 00:00:00',
// }
],
// 是否自动聚焦
autoFocus: false,
// 资源路径
webUrl: this.$VUE_APP_RESOURCES_URL,
// 是否显示发送按钮
showBtn: false,
content: '',
currentComment: {},
// 光标位置
cursorIndex: 0,
inputBoxBottom: 0
}
},
onLoad() {
// 监听键盘高度变化
uni.onKeyboardHeightChange((res) => {
if (res.height) {
this.onKeyboardShow(res.height)
} else {
this.onKeyboardHide()
}
})
},
onShow() {
this.fetchList()
},
methods: {
onKeyboardShow(height) {
this.inputBoxBottom = height
},
onKeyboardHide() {
// 处理键盘收起逻辑
this.inputBoxBottom = 0
},
toInfo(comment) {
if(!comment.contentAvailable) {
// 该内容已删除
uni.showToast({
title: '该内容已删除',
icon: 'none'
})
return
}
if(comment.contentType === 'hotelNews') {
this.$yrouter.push('/pagesInn/inn/innInfoDetail?id=' + comment.contentId)
} else if(comment.contentType === 'countyFamousNews') {
this.$yrouter.push('/pkg_product/views/newsInfo?id=' + comment.contentId)
}
},
/**
* 格式化时间
* @param {*} time
* @returns 返回yyyy-MM-dd 格式
*/
timeFormat(time) {
const date = new Date(time)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${month}-${day}`
},
commentInputChange(e) {
// 若删除直接将@用户名整个删除
if (e === '') {
this.currentComment = {}
return
}
// 若删除@用户名,将@用户名整个删除
const usernameLength = `@${this.currentComment.senderUserNickname} `.length
if(!e.includes(`@${this.currentComment.senderUserNickname} `) && this.currentComment.senderUserNickname) {
this.currentComment = {}
// 去除@用户名
this.content = e.slice(usernameLength - 1)
}
},
commentInputFocus(e) {
// this.prefixIcon = ''
},
/**
* 发送消息
*/
showSendBtn(item) {
// 显示发送按钮
this.showBtn = true
this.currentComment = item
this.content = '@' + item.senderUserNickname + ' '
this.cursorIndex = this.content.length
this.$nextTick(() => {
// 延迟100ms,否则无法聚焦
setTimeout(() => {
// 自动聚焦
this.autoFocus = true
}, 100)
})
},
/**
* 失焦
*/
onBlur() {
// 隐藏发送按钮
this.showBtn = false
// 取消自动聚焦
this.autoFocus = false
},
/**
* 回复消息
* @param {*} item 消息数据
*/
replyMessageHandle() {
if(!this.currentComment.senderUserNickname) {
// 未选择需要回复的用户,提示
uni.showToast({
title: '请选择需要回复的用户',
icon: 'none'
})
return
}
if(this.content.trim() === ('@' + this.currentComment.senderUserNickname)) {
// 请输入回复内容
uni.showToast({
title: '请输入回复内容',
icon: 'none'
})
return
}
// 点击弹出回复框,输入回复内容,点击发送按钮,发送回复消息
uni.showLoading({
mask: true
})
const params = {
// 评论的目标内容ID,必传
contentId: this.currentComment.contentId,
// 评论的目标内容类型,必传(店铺资讯传入值hotelNews)
contentType: this.currentComment.contentType,
// 评论内容,必传
reply: this.content.replace('@' + this.currentComment.senderUserNickname, '').trim(),
// @的用户ID
atUid: this.currentComment.senderUid,
// 回复的评论ID
parentReplyId: this.currentComment.parentReplyId
}
replyMessage(params).then(res => {
uni.hideLoading()
this.fetchList()
})
},
/**
* 查询消息列表
*/
fetchList() {
const params = {
page: 1,
limit: 999999
}
getMessageList(params).then(res => {
this.list = res.data.records
})
}
}
}
</script>
<style lang="less" scoped>
.call-message{
background: #fff;
}
.send-btn{
width: 96rpx;
height: 70rpx;
background: #C52733;
color: #fff;
border-radius: 16rpx;
line-height: 70rpx;
text-align: center;
margin-left: 30rpx;
padding: 0 20rpx;
}
.sticky-bottom{
position: fixed;
bottom: 0;
width: 100%;
background: #fff;
box-sizing: border-box;
}
.user-info{
view{
word-break: break-all;
}
}
</style>
+3 -2
View File
@@ -78,7 +78,7 @@
<view class="input-box">
<textarea
v-model="content"
maxlength="150"
maxlength="500"
:auto-height="true"
:fixed="true"
:show-confirm-bar="false"
@@ -279,7 +279,8 @@ export default {
},
userInput(event) {
this.content = event.detail.value;
// 只取前500字
this.content = event.detail.value.slice(0, 500);
},
userSend(msg_type) {
+132 -25
View File
@@ -1,8 +1,26 @@
<template>
<view class="common-roomLogs">
<view v-if="list.length > 0">
<image
v-if="showImg"
:src="settingInfo.messageEnterImage"
mode="widthFix"
class="common-ai-img"
@click="enterAiChat()"
/>
<view class="v12-align-center" @click="toMessage">
<view class="call-message">
<u-icon :name="webUrl + '/icon/message.png'" size="25"></u-icon>
</view>
<view class="v12-ml-2" style="width: 80%">
<view class="v12-font-bold v12-font-32">互动消息</view>
<view class="v12-secondary-dark-text v12-font-28 v12-mt-3 v12-justify-between">
<text>{{ unreadNum > 0 ? `收到${unreadNum}条回复` : '暂无通知'}}</text>
<view v-if="unreadNum > 0" class="bage-num v12-font-24 v12-primary v12-white-text v12-radius-100">{{ unreadNum > 99 ? '99+' : unreadNum }}</view>
</view>
</view>
</view>
<view v-if="list.length > 0" class="v12-mt-3">
<view
>
<u-swipe-action
class="v12-mt-3"
@@ -15,12 +33,15 @@
style="margin-bottom: 10px;"
@click.stop="removeRoom(item.room_id)"
>
<view class="item v12-pa-3">
<view class="v12-justify-start flex" @click="goRoom(item)">
<image class="cover flex-0" :src="item.store_avatar || webUrl+'/20221114155314348118.png'"/>
<view>
<view class="item">
<view class="v12-justify-start flex item-cont" @click="goRoom(item)">
<image
:src="item.store_avatar || webUrl+'/20221114155314348118.png'"
class="cover flex-0"
/>
<view class="cont">
<view class="flex jc-between ai-center" style="position: relative">
<view class="v12-align-center">
<view class="v12-align-center name-wrap">
<view class="name more-t">{{ item.store_name }}</view>
<view v-if="item.is_official" class="guanfang">官方</view>
<view v-else class="shangjia">商家</view>
@@ -28,8 +49,8 @@
<view class="datetime" v-if="item.last_msg">{{ item.last_msg.created_at }}</view>
</view>
<view class="sub v12-align-center v12-justify-between" v-if="item.last_msg">
<view class="one-t" style="width: 90%;">{{ item.last_msg.msg_type===0?item.last_msg.msg_content:'[图片]' }}</view>
<view v-if="item.unread_num > 0" class="unread-num v12-primary"> {{ item.unread_num < 99 ? item.unread_num : '99+' }}</view>
<view class="one-t" style="width: 80%;">{{ item.last_msg.msg_type=== 0 ? item.last_msg.msg_content : '[图片]' }}</view>
<view v-if="item.unread_num > 0" class="unread-num v12-primary">{{ item.unread_num < 99 ? item.unread_num : '99+' }}</view>
</view>
</view>
</view>
@@ -39,7 +60,7 @@
</view>
</view>
<view class="v4-nodata" v-if="list.length===0">
<image src="@/static/images/img_nodata.png" mode="scaleToFill"/>
<!-- <image src="@/static/images/img_nodata.png" mode="scaleToFill"/> -->
<view class="text">暂无数据</view>
</view>
</view>
@@ -47,23 +68,76 @@
<script>
import { mapGetters } from 'vuex'
import { getUnreadMessageCount, markMessagesAsRead } from '@/api/rooms'
import {
getAiSystemInfoSetting
} from '@/api/public'
export default {
name: 'RoomLogs',
data() {
return {
showImg: false,
settingInfo: {},
webUrl:this.$VUE_APP_RESOURCES_URL,
uid: null,
list: [],
options1: [{text: '删除', style: {color: '#fff', backgroundColor: '#C52733'} }]
options1: [{text: '删除', style: {color: '#fff', backgroundColor: '#C52733'} }],
unreadNum: 0,
webUrl: this.$VUE_APP_RESOURCES_URL
}
},
computed: mapGetters(['userInfo']),
onShow() {
this.uid = this.userInfo.uid
this.fetchList()
this.getUnreadMessageCount()
getAiSystemInfoSetting().then(res => {
const { success, data } = res
if (success) {
this.settingInfo = data
this.showImg = data.showOnAppIndex === 1
}
})
},
methods: {
enterAiChat() {
const cityName = uni.getStorageSync('locationCityName') || ''
uni.navigateTo({
url: `/aiChat/views/index?click=1&cityName=${cityName}`
})
},
/**
* 去消息
*/
toMessage() {
markMessagesAsRead().then(res => {
uni.$emit('updateUnreadMessageCount', 0)
uni.navigateTo({url: '/pkg_common/views/message'})
})
},
/**
* 获取互动消息
* @param {*} item
*/
getUnreadMessageCount(item) {
getUnreadMessageCount().then(res => {
const count = res.data || 0
this.unreadNum = count
uni.request({
url: this.$SERVICE_API_URL + '/customer-chat-list',
method: 'POST',
data: {uid: this.uid},
success: res => {
if (res.data.status_code === 200) {
const _count = res.data.content + count
uni.$emit('updateUnreadMessageCount', _count)
} else {
uni.$emit('updateUnreadMessageCount', count)
}
}
})
})
},
goRoom(item) {
uni.navigateTo({url: `/pkg_common/views/room?id=${item.store_id}&name=${item.store_name}`})
},
@@ -107,7 +181,23 @@ export default {
}
}
</script>
<style>
<style scoped lang="less">
.common-ai-img {
width: 100%;
margin: 0 0 24rpx 0;
}
.call-message{
width: 120rpx;
height: 120rpx;
background: #C52733;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.common-roomLogs{
background: #fff;
}
.common-roomLogs .u-swipe-action-item{
margin-bottom: 20rpx;
}
@@ -147,20 +237,32 @@ export default {
.common-roomLogs {
min-height: 100vh;
box-sizing: border-box;
padding: 32rpx;
background: #F5F5F5;
padding: 24rpx;
background: #fff;
.item {
padding: 24rpx 0;
border-radius: 8rpx;
border-top: 1rpx solid #eee;
background: #FFFFFF;
.cover {
width: 80rpx;
height: 80rpx;
margin-right: 18rpx;
border-radius: 16rpx;
.item-cont {
display: flex;
.cont {
flex: 1;
width: calc(100% - 140rpx);
}
}
.cover {
width: 120rpx;
height: 120rpx;
margin-right: 18rpx;
border-radius: 20rpx;
}
.name-wrap {
width: calc(100% - 240rpx);
}
.name {
font-size: 26rpx;
font-weight: bold;
@@ -172,14 +274,12 @@ export default {
font-size: 22rpx;
line-height: 32rpx;
color: #999999;
width: 240rpx;
flex-wrap: nowrap;
white-space: nowrap;
text-align: center;
}
.sub {
width: 548rpx;
width: 100%;
margin-top: 12rpx;
font-size: 24rpx;
line-height: 34rpx;
@@ -187,4 +287,11 @@ export default {
}
}
}
</style>
.bage-num {
min-width: 40rpx;
height: 40rpx;
text-align: center;
box-sizing: border-box;
padding: 4rpx;
}
</style>
+934
View File
@@ -0,0 +1,934 @@
<template>
<view class="container">
<view
v-if="form.reviewStatus === 2"
class="v12-pa-2 reason-box"
>
驳回原因: {{ form.reviewMsg }}
</view>
<view
v-if="form.isDraft === 0 && form.reviewStatus === 0"
class="v12-pa-2 reason-box"
>
审核中请耐心等待审核结果
</view>
<view class="form-item">
<view class="title">
<text class="required">* </text>
名称:
</view>
<view class="input">
<u-textarea
v-model="form.name"
border="none"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入名称"
:placeholder-style="inputPlaceholderStyle"
/>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start">
<view>
<view class="title">
<text class="required">* </text>
主图:
</view>
<view class="input">
<u-upload
:fileList="coverImg.filter(item => item.url)"
:disabled="form.isDraft === 0 && (coverImg.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="1"
count="1"
name="coverImg"
multiple
@afterRead="afterRead($event, 'coverImg')"
@delete="deletePic($event, 'coverImg')"
/>
</view>
</view>
<view class="v12-ml-2">
<view class="title">
<text class="required">* </text>
头像:
</view>
<view class="input">
<u-upload
:fileList="avatarImg.filter(item => item.url)"
:disabled="form.isDraft === 0 && (avatarImg.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="1"
count="1"
name="avatarImg"
multiple
@afterRead="afterRead($event, 'avatarImg')"
@delete="deletePic($event, 'avatarImg')"
/>
</view>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
轮播图:
</view>
<view class="input">
<u-upload
:fileList="bannerImg"
:disabled="form.isDraft === 0 && (bannerImg.length > 8 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="9"
count="9"
name="bannerImg"
multiple
@afterRead="afterRead($event, 'bannerImg')"
@delete="deletePic($event, 'bannerImg')"
/>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
视频:
</view>
<view class="input">
<u-upload
v-if="videos.length === 0"
accept="video"
multiple
:max-count="1"
:fileList="videos"
:disabled="form.isDraft === 0 && (videos.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
count="1"
name="videoImg"
:previewFullImage="true"
@afterRead="afterRead($event, 'videos', 'video')"
@delete="deletePic($event, 'videos')"
/>
<view
v-else
class="video-wrapper"
>
<video
:src="videos[0].url"
:poster="videos[0].url + '?vframe/jpg/offset/1'"
:controls="false"
play-btn-position="center"
class="video-item"
/>
<view
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
class="delete-badge"
@click.stop="deleteVideo(0)"
>
x
</view>
</view>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
公司名称
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.companyName"
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入公司名称"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
店铺分类
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<picker
:value="experienceStoreCategoryIndex"
:range="experienceStoreCategoryList"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
range-key="name"
mode="selector"
@change="handleCategoryChange"
>
<view class="one-t">
<text :style="{'color': form.experienceStoreCategoryName ? '#333' : '#999999'}">
{{ form.experienceStoreCategoryName || '请选择店铺分类' }}
</text>
</view>
</picker>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
简介
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.intro"
type="text"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入简介"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
联系人
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.contact"
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入联系人姓名"
/>
</view>
</view>
<u-divider />
<view
class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center"
@click="showTimePicker"
>
<view class="title">
<text class="required">* </text>
营业时间
</view>
<view class="v12-ml-2">
<view>
<text :style="{'color': form.serviceTime ? '#333' : '#999999'}">
{{ form.serviceTime || '起始时间' }}
</text>
-
<text :style="{'color': form.serviceEndTime ? '#333' : '#999999'}">
{{ form.serviceEndTime || '结束时间' }}
</text>
</view>
<tpf-time-range
ref="TimePickerPopupRef"
startTime="00:00"
endTime="23:59"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:startDefaultTime="form.serviceTime"
:endDefaultTime="form.serviceEndTime"
@timeRange="timeRange"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
省市区
</view>
<view
class="v12-ml-2 one-t"
style="flex: 1;"
>
<CitySelect
ref="cityselect"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:defaultValue="form.area || '请选择'"
:items="district"
@callback="resultCity"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
详细地址
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.address"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
type="text"
placeholder="请输入详细地址"
/>
</view>
</view>
<u-divider />
<view
class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center"
@click="chooLocation"
>
<view class="title">
<text class="required">* </text>
地理定位
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<u--text
v-if="form.position"
:text="form.position"
/>
<u--text
v-else
text="请选择"
color="#c0c4cc"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
商家电话
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<u-input
v-model="form.phone"
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入商家电话"
maxlength="11"
border="none"
>
<template slot="suffix">
<u-code
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
ref="uCode"
seconds="30"
changeText="X秒重新获取"
@change="codeChange"
/>
<u-button
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:text="tips"
type="success"
color="#4A97FC"
@tap="getCode"
/>
</template>
</u-input>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
填写验证码
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.captcha"
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入验证码"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<!-- <text class="required">* </text> -->
商家微信
</view>
<view
class="v12-ml-2"
style="flex: 1;"
>
<input
v-model="form.wechat"
type="text"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入微信号"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
/>
</view>
</view>
<u-divider />
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
资质:
</view>
<view class="input">
<u-upload
:fileList="qualificationImg"
:disabled="(form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)) || qualificationImg.length > 98"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="99"
count="99"
name="qualificationImg"
multiple
@afterRead="afterRead($event, 'qualificationImg')"
@delete="deletePic($event, 'qualificationImg')"
/>
</view>
</view>
<view v-if="form.isDraft === 0 && form.reviewStatus !== 1">
<view
class="v12-primary v12-white-text submit-btn"
:style="{'background': form.reviewStatus === 0 ? '#FA979F !important' : '#4A97FC'}"
@click="submitForm"
>
{{ form.reviewStatus === 0 ? '撤销申请' : form.reviewStatus === 2 ? '重新提交' : '提交审核' }}
</view>
</view>
<view v-if="form.reviewStatus === null || (form.isDraft === 1 && form.reviewStatus === 0)">
<view
class="v12-primary v12-white-text submit-btn"
@click="submitForm"
>
提交审核
</view>
</view>
<u-popup
:show="show"
mode="center"
round="10"
@close="show=false"
>
<view class="popup-body">
<view class="tc bold">
入驻申请须知
</view>
<view class="content">
<rich-text :nodes="notice" />
</view>
<view class="agree-box v12-mb-2">
<checkbox-group @change="changeAgree">
<label>
<checkbox
value="yes"
style="transform:scale(0.8)"
/>
</label>
<text>我已仔细阅读并同意</text>
</checkbox-group>
</view>
<u-button
type="error"
text="确定"
:disabled="!isAgree"
@click="savePopup"
/>
</view>
</u-popup>
</view>
</template>
<script>
import CitySelect from '@/components/CitySelect'
import { getCity, registerVerify } from '@/api/user'
import { uploadVideo, uploadImage } from '@/utils'
import {
getShopData,
saveExperienceStore,
saveExperienceStoreDraft,
getExperienceStoreCategoryList,
getExperienceStoreInfo,
removeApply
} from '@/api/join'
export default {
components: {
CitySelect
},
data() {
return {
isAgree: false,
show: false,
notice: '',
tips: '获取验证码',
inputPlaceholderStyle: {
fontSize: '32rpx'
},
coverImg: [],
avatarImg: [],
bannerImg: [],
videos: [],
district: [],
qualificationImg: [],
partnerId: 0,
experienceStoreCategoryList: [],
experienceStoreCategoryIndex: 0,
form: {
captcha: '',
name: '',
cover: '',
avatar: '',
banner: '',
video: '',
companyName: '',
experienceStoreCategoryId: null,
experienceStoreCategoryName: '',
serviceTime: '00:00',
serviceEndTime: '23:59',
provinceId: 0,
provinceName: '',
cityId: 0,
cityName: '',
areaId: 0,
areaName: '',
address: '',
longitude: 0,
latitude: 0,
contact: '',
phone: '',
wechat: '',
qualification: '',
intro: '',
reviewMsg: '',
reviewStatus: null,
isDraft: null,
position: '',
area: ''
}
}
},
onLoad(opts) {
this.partnerId = opts.partnerId
this.queryCity()
getShopData().then(({ data }) => {
this.notice = data.merchantApplyNotice
})
getExperienceStoreCategoryList().then(({ data }) => {
this.experienceStoreCategoryList = data || []
if (this.experienceStoreCategoryList.length > 0) {
this.experienceStoreCategoryIndex = 0
}
this.initInfo()
})
},
onUnload() {
this.saveDraft()
},
onHide() {
this.saveDraft()
},
methods: {
initInfo() {
getExperienceStoreInfo().then(res => {
const data = res.data || {}
const info = data.experienceStoreInfo || {}
if (!info || Object.keys(info).length === 0) {
this.form.isDraft = 1
this.show = true
return
}
this.form = { ...this.form, ...info }
this.coverImg = info.cover ? this.stringToMapArr(info.cover) : []
this.avatarImg = info.avatar ? this.stringToMapArr(info.avatar) : []
this.bannerImg = info.banner ? this.stringToMapArr(info.banner) : []
this.videos = info.video ? this.stringToMapArr(info.video) : []
this.qualificationImg = info.qualification ? this.stringToMapArr(info.qualification) : []
this.form.reviewStatus = data.reviewStatus != null ? data.reviewStatus : null
this.form.reviewMsg = data.reviewMsg || ''
this.form.isDraft = data.isDraft != null ? data.isDraft : null
this.form.latitude = info.latitude || 0
this.form.longitude = info.longitude || 0
this.form.position = this.form.longitude && this.form.latitude
? this.$forceToDecimal(this.form.longitude, 6) + ',' + this.$forceToDecimal(this.form.latitude, 6)
: ''
this.form.area = `${this.form.provinceName || ''} ${this.form.cityName || ''} ${this.form.areaName || ''}`.trim()
if (this.experienceStoreCategoryList && this.experienceStoreCategoryList.length > 0) {
const idx = this.experienceStoreCategoryList.findIndex(item => item.id === this.form.experienceStoreCategoryId)
if (idx !== -1) {
this.experienceStoreCategoryIndex = idx
this.form.experienceStoreCategoryName = this.experienceStoreCategoryList[idx].name
}
}
if (this.form.isDraft === 1 && (this.form.reviewStatus === null || this.form.reviewStatus === undefined)) {
this.show = true
}
}).catch(() => {
this.form.isDraft = 1
this.show = true
})
},
changeAgree(e) {
this.isAgree = e.detail.value.includes('yes')
},
savePopup() {
this.show = false
},
stringToMapArr(str, split = ',') {
return str.split(split).map(item => ({ url: item }))
},
saveDraft() {
this.form.cover = this.coverImg.filter(item => item.url).map(item => item.url).join(',')
this.form.avatar = this.avatarImg.filter(item => item.url).map(item => item.url).join(',')
this.form.banner = this.bannerImg.filter(item => item.url).map(item => item.url).join(',')
this.form.video = this.videos.filter(item => item.url).map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.filter(item => item.url).map(item => item.url).join(',')
const params = {
experienceStoreInfo: { ...this.form },
partnerId: this.partnerId
}
saveExperienceStoreDraft(params).catch(err => {
uni.$u.toast(err.msg)
})
},
handleCategoryChange(e) {
const idx = parseInt(e.detail.value)
this.experienceStoreCategoryIndex = idx
const item = this.experienceStoreCategoryList[idx]
if (item) {
this.form.experienceStoreCategoryId = item.id
this.form.experienceStoreCategoryName = item.name
}
},
submitForm() {
if (this.form.reviewStatus === 0 && this.form.isDraft === 0) {
this.revokeExperienceStore()
return
}
this.form.cover = this.coverImg.filter(item => item.url).map(item => item.url).join(',')
this.form.avatar = this.avatarImg.filter(item => item.url).map(item => item.url).join(',')
this.form.banner = this.bannerImg.filter(item => item.url).map(item => item.url).join(',')
this.form.video = this.videos.filter(item => item.url).map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.filter(item => item.url).map(item => item.url).join(',')
if (!this.form.name) {
uni.$u.toast('请输入名称')
return
}
if (!this.form.cover) {
uni.$u.toast('请上传主图')
return
}
if (!this.form.avatar) {
uni.$u.toast('请上传头像')
return
}
if (!this.form.banner) {
uni.$u.toast('请上传轮播图')
return
}
if (!this.form.companyName) {
uni.$u.toast('请输入公司名称')
return
}
if (!this.form.experienceStoreCategoryId) {
uni.$u.toast('请选择店铺分类')
return
}
if (!this.form.intro) {
uni.$u.toast('请输入简介')
return
}
if (!this.form.serviceTime) {
uni.$u.toast('请选择营业开始时间')
return
}
if (!this.form.serviceEndTime) {
uni.$u.toast('请选择营业结束时间')
return
}
if (!this.form.provinceId) {
uni.$u.toast('请选择省份')
return
}
if (!this.form.cityId) {
uni.$u.toast('请选择城市')
return
}
if (!this.form.areaId) {
uni.$u.toast('请选择区域')
return
}
if (!this.form.address) {
uni.$u.toast('请输入详细地址')
return
}
if (!this.form.longitude || !this.form.latitude) {
uni.$u.toast('请选择地理定位')
return
}
if (!this.form.qualification) {
uni.$u.toast('请上传资质')
return
}
if (!this.form.contact) {
uni.$u.toast('请输入联系人姓名')
return
}
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (!this.form.captcha) {
uni.$u.toast('请输入验证码')
return
}
// if (!this.form.wechat) {
// uni.$u.toast('请输入联系微信')
// return
// }
const params = {
experienceStoreInfo: { ...this.form },
partnerId: this.partnerId,
captcha: this.form.captcha
}
saveExperienceStore(params).then(res => {
if (res.status === 200) {
uni.showToast({
title: '您的入驻申请已提交成功,请耐心等待审核',
icon: 'none',
success: () => {
this.form.isDraft = 0
this.form.reviewStatus = 0
this.show = false
}
})
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
revokeExperienceStore() {
uni.showModal({
title: '提示',
content: '确定撤销申请吗?',
success: res => {
if (res.confirm) {
removeApply().then(result => {
if (result.status === 200) {
uni.$u.toast('撤销申请成功')
this.form.reviewStatus = null
this.form.isDraft = 1
this.show = true
this.initInfo()
} else {
uni.$u.toast(result.msg || '撤销失败')
}
}).catch(err => {
uni.$u.toast(err.msg)
})
}
}
})
},
getCode() {
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (this.form.phone.length !== 11) {
uni.$u.toast('请输入11位手机号')
return
}
if (this.$refs.uCode && this.$refs.uCode.canGetCode) {
uni.showLoading({
title: '正在获取验证码'
})
registerVerify({
phone: this.form.phone,
type: 'merchantExperienceStoreApply'
}).then(() => {
uni.hideLoading()
uni.$u.toast('验证码已发送')
this.$refs.uCode.start()
}).catch(err => {
uni.hideLoading()
uni.$u.toast(err.msg)
})
} else {
uni.$u.toast('倒计时结束后再发送')
}
},
codeChange(text) {
this.tips = text
},
chooLocation() {
if (this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
return
}
uni.chooseLocation({
success: res => {
this.form.address = res.address
this.form.longitude = this.$forceToDecimal(res.longitude, 6)
this.form.latitude = this.$forceToDecimal(res.latitude, 6)
this.form.position = this.$forceToDecimal(res.longitude, 6) + ',' + this.$forceToDecimal(res.latitude, 6)
}
})
},
resultCity(values) {
const address = {
province: values.province.name || '',
city: values.city.name || '',
district: values.district.name || '',
provinceId: values.province.id,
cityId: values.city.id,
areaId: values.district.id
}
this.form.area = `${address.province || ''} ${address.city || ''} ${address.district || ''}`
this.form.areaName = address.district || ''
this.form.provinceName = address.province || ''
this.form.cityName = address.city || ''
this.form.provinceId = address.provinceId
this.form.cityId = address.cityId
this.form.areaId = address.areaId
},
queryCity() {
getCity().then(({ data }) => {
this.district = data
})
},
showTimePicker() {
if (this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
return
}
this.$refs.TimePickerPopupRef.open()
},
timeRange(e) {
this.form.serviceTime = e[0]
this.form.serviceEndTime = e[1]
this.$forceUpdate()
},
afterRead(e, imgField, type) {
const uploadFunc = type === 'video' ? uploadVideo : uploadImage
if (e.file.length > 0) {
e.file.forEach(item => {
if (!item.url) return
uploadFunc(item.url, img => {
if (!img) return
this[imgField].push({ url: img })
})
})
}
},
deleteVideo(index) {
if (this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
return
}
uni.showModal({
title: '确认删除',
content: '确定删除该视频?',
success: res => {
if (res.confirm) {
this.deletePic({ index }, 'videos')
}
}
})
},
deletePic(e, imgField) {
if (this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
return
}
this[imgField].splice(e.index, 1)
}
}
}
</script>
<style scoped lang="scss">
.popup-body {
width: 80vw;
height: 80vh;
padding: 30rpx;
overflow: hidden;
}
.popup-body .content {
height: 52vh;
overflow-y: auto;
margin: 30rpx 0;
}
.popup-body .agree-box {
margin: 60rpx 0;
font-size: 28rpx;
color: #666;
}
.popup-body {
.title {
line-height: 80rpx;
text-align: center;
}
}
.container {
padding: 20rpx;
background: #fff;
}
.title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.required {
color: #c52733;
}
.input {
margin-top: 20rpx;
}
.submit-btn {
width: 100%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
}
.reason-box {
background: rgba(255, 160, 167, 0.39);
border-radius: 8rpx;
color: #c52733;
font-size: 30rpx;
margin-bottom: 20rpx;
}
.title {
white-space: nowrap;
}
.video-item {
width: 160rpx;
height: 160rpx;
}
.video-wrapper {
position: relative;
display: inline-block;
}
.delete-badge {
position: absolute;
top: 0rpx;
right: 0rpx;
background: #000;
color: #fff;
padding: 2rpx 8rpx;
border-radius: 0 0 0rpx 200rpx;
font-size: 22rpx;
z-index: 10;
text-align: center;
}
</style>
<style>
.input .u-textarea {
background-color: #F3F3F3 !important;
}
</style>
+38 -1
View File
@@ -26,11 +26,23 @@
mode="widthFix"
@click="goto('shop')"
/>
<image
<!-- <image
:src="images.wholesalerImage"
class="img-item"
mode="widthFix"
@click="goto('wholesaler')"
/> -->
<image
:src="images.travelImage"
class="img-item"
mode="widthFix"
@click="toTravelResidence"
/>
<image
:src="images.experienceStoreImage"
class="img-item"
mode="widthFix"
@click="goto('experience_store')"
/>
</view>
</template>
@@ -50,6 +62,8 @@ export default {
hasHotel: null,
hasSupplier: null,
hasWholesaler: null,
hasExperienceStore: null,
hasTravel: null,
images: null,
pageKeyId: Object.freeze('merchantApplyIndex'),
canApplyHotel: false
@@ -65,9 +79,25 @@ export default {
this.hasSupplier = data.hasSupplier
this.hasWholesaler = data.hasWholesaler
this.canApplyHotel = data.canApplyHotel
this.hasTravel = data.hasTravel
this.hasExperienceStore = data.hasExperienceStore
})
},
methods: {
/**
* 跳转旅游 residences
*/
toTravelResidence() {
if (this.hasTravel === 1) {
this.$global.goSupplierMini()
} else {
this.$yrouter.push('/pkg_join/views/travelResidence?partnerId=' + this.partnerId)
}
},
/**
* 跳转
* @param {string} target - 目标页面
*/
goto(target) {
switch (target) {
case 'goods_supplier':
@@ -94,6 +124,13 @@ export default {
}
this.$yrouter.push('/pkg_join/views/shop?partnerId=' + this.partnerId)
break
case 'experience_store':
if(this.hasExperienceStore === 1) {
this.$global.goSupplierMini()
return
}
this.$yrouter.push('/pkg_join/views/experienceStore?partnerId=' + this.partnerId)
break
case 'wholesaler':
if (this.hasWholesaler === 1) {
this.$global.goSupplierMini()
+132 -26
View File
@@ -121,6 +121,7 @@
:fileList="pics"
:maxCount="99999"
:disabled="reviewing"
:deletable="!reviewing"
name="pics"
multiple
@afterRead="afterRead"
@@ -165,6 +166,7 @@
:maxCount="9"
:fileList="quaPics"
:disabled="reviewing"
:deletable="!reviewing"
name="quaPics"
multiple
@afterRead="quaAfterRead"
@@ -173,27 +175,55 @@
</template>
</u-cell>
</view>
<u-form-item prop="typeText">
<u-cell title="店铺类型" :border="false" isLink>
<u-form-item prop="isCountyFamous">
<u-cell title="县域店铺" :border="false">
<template #icon>
<text class="required">*</text>
</template>
<template #value>
<picker
:value="indexType"
:range="columnsType"
<u-radio-group
v-model="form.isCountyFamous"
:disabled="reviewing"
mode=selector
range-key="name"
class="picker"
@change="typeChange"
placement="row"
@change="countyFamousChange"
>
<u--text v-if="form.typeText" :text="form.typeText"></u--text>
<u--text v-else text="请选择类型" color="#c0c4cc"></u--text>
</picker>
<u-radio
:name="0"
:customStyle="{marginRight: '16px'}"
active-color="red"
label="否"
/>
<u-radio
:name="1"
active-color="red"
label="是"
/>
</u-radio-group>
</template>
</u-cell>
</u-form-item>
<u-form-item prop="typeText">
<picker
:value="indexType"
:range="columnsType"
:disabled="reviewing"
mode="selector"
range-key="name"
class="picker"
style="width: 100%"
@change="typeChange"
>
<u-cell title="店铺类型" :border="false" isLink>
<template #icon>
<text class="required">*</text>
</template>
<template #value>
<u--text v-if="form.typeText" :text="form.typeText"></u--text>
<u--text v-else text="请选择类型" color="#c0c4cc"></u--text>
</template>
</u-cell>
</picker>
</u-form-item>
<u-form-item prop="contact">
<u-cell title="联系人姓名" :border="false">
<template #icon>
@@ -260,7 +290,7 @@
</u-cell>
</u-form-item>
<u-form-item prop="area">
<u-cell title="省市区" :border="false" isLink>
<u-cell title="省市区" :border="false" isLink @click="$refs.cityselect.open()">
<template #icon>
<text class="required">*</text>
</template>
@@ -268,7 +298,7 @@
<view class="u-text__value" :class="{'area-item':form.area && form.area.length===0}">
<CitySelect
ref="cityselect"
:defaultValue="form.area"
:defaultValue="form.area || ''"
:disabled="reviewing"
:items="district"
@callback="result"
@@ -314,7 +344,15 @@
<text class="required">*</text>
</template>
<template #value>
<u--text :text="form.serviceTime + ' - ' + form.serviceEndTime"></u--text>
<u--text
v-if="form.serviceTime && form.serviceEndTime"
:text="form.serviceTime + ' - ' + form.serviceEndTime"
></u--text>
<u--text
v-else
text="请选择营业时间"
color="#c0c4cc"
></u--text>
<tpf-time-range
startTime="00:00"
endTime="23:59"
@@ -337,7 +375,9 @@
<u-cell :border="false">
<template #title>
<view class="line">
<u-input
<u-textarea
:maxlength="-1"
autoHeight
v-model="form.content"
:disabled="reviewing"
border="none"
@@ -375,7 +415,6 @@
:show="show"
mode="center"
round="10"
closeable
@close="show=false"
>
<view class="popup-body">
@@ -410,7 +449,9 @@ import {
getShopData,
getShopInfo,
removeApply,
saveShop
saveShop,
getHotelInfo,
getCountyFamousHotelType
} from '@/api/join'
import {
getCity,
@@ -445,6 +486,8 @@ export default {
qualification: '',
type: 0,
typeText: '',
isCountyFamous: 0,
countyFamousHotelType: null,
phone: '',
captcha: '',
area: '',
@@ -479,6 +522,12 @@ export default {
message: '请上传店铺LOGO',
trigger: ['blur', 'change']
},
'isCountyFamous': {
type: 'number',
required: true,
message: '请选择是否为县域店铺',
trigger: ['blur', 'change']
},
'typeText': {
type: 'string',
required: true,
@@ -535,6 +584,8 @@ export default {
}
},
columnsType: [],
hotelTypeList: [],
countyFamousHotelTypeList: [],
indexType: [0],
district: [],
address: {},
@@ -570,6 +621,8 @@ export default {
qualification: '',
type: 0,
typeText: '',
isCountyFamous: 0,
countyFamousHotelType: null,
phone: '',
captcha: '',
area: '',
@@ -642,21 +695,32 @@ export default {
this.district = data
})
getShopData().then(({data}) => {
this.columnsType = data.hotelTypeList
this.hotelTypeList = data.hotelTypeList || []
this.columnsType = this.hotelTypeList
this.notice = data.merchantApplyNotice
this.initInfo()
getCountyFamousHotelType().then(res => {
const listData = Array.isArray(res.data)
? res.data
: (res.data && (res.data.hotelTypeList || res.data.list || res.data.records)) || []
this.countyFamousHotelTypeList = listData || []
this.initInfo()
}).catch(() => {
this.initInfo()
})
})
},
initInfo() {
getShopInfo().then(({data}) => {
initInfo(flag) {
getHotelInfo().then(({data}) => {
if (data.partnerId != null) {
this.partnerId = data.partnerId
}
this.isDraft = data.isDraft
this.reviewStatus = data.reviewStatus
if(data.isDraft === null || data.isDraft === 1) {
this.show = true
if(!flag) {
this.show = true
}
}
if (data.isDraft !== 1) {
switch (data.reviewStatus) {
@@ -679,6 +743,10 @@ export default {
// 店铺图片
this.pics = []
this.form = data.hotelInfo
if (!this.form.serviceTime || !this.form.serviceEndTime) {
this.form.serviceTime = ''
this.form.serviceEndTime = ''
}
this.form.coverVideo = this.form.coverType === 2 ? this.form.cover : ''
const picsTemp = this.form.pics.split(',')
if (picsTemp.length > 0) {
@@ -698,7 +766,16 @@ export default {
}
})
}
this.indexType = this.columnsType.findIndex(item => item.id === this.form.type)
if (this.form.isCountyFamous !== 1 && this.form.isCountyFamous !== 0) {
this.form.isCountyFamous = 0
}
if (this.form.isCountyFamous === 1) {
this.columnsType = this.countyFamousHotelTypeList
this.indexType = this.columnsType.findIndex(item => item.id === this.form.countyFamousHotelType)
} else {
this.columnsType = this.hotelTypeList
this.indexType = this.columnsType.findIndex(item => item.id === this.form.type)
}
if (this.indexType !== -1) {
this.form.typeText = this.columnsType[this.indexType].name
}
@@ -761,13 +838,42 @@ export default {
})
}
},
countyFamousChange(e) {
const value = parseInt(e)
this.form.isCountyFamous = value
if (value === 1) {
if (this.countyFamousHotelTypeList.length === 0) {
getCountyFamousHotelType().then(res => {
const listData = Array.isArray(res.data)
? res.data
: (res.data && (res.data.hotelTypeList || res.data.list || res.data.records)) || []
this.countyFamousHotelTypeList = listData || []
this.columnsType = this.countyFamousHotelTypeList
})
} else {
this.columnsType = this.countyFamousHotelTypeList
}
this.indexType = 0
this.form.typeText = ''
this.form.countyFamousHotelType = null
} else {
this.columnsType = this.hotelTypeList
this.indexType = 0
this.form.typeText = ''
this.form.type = 0
}
},
typeChange(e) {
const idx = parseInt(e.detail.value)
this.columnsType.map((item, index) => {
if (index === idx) {
this.form.typeText = item.name
this.indexType = index
this.form.type = item.id
if (this.form.isCountyFamous === 1) {
this.form.countyFamousHotelType = item.id
} else {
this.form.type = item.id
}
}
})
},
@@ -863,7 +969,7 @@ export default {
title: '您的入驻申请已提交成功,请耐心等待审核',
icon: 'none',
success: () => {
this.initInfo()
this.initInfo(1)
this.show = false
}
})
+47 -14
View File
@@ -36,6 +36,21 @@
</template>
</u-cell>
</u-form-item>
<u-form-item prop="businessLicense">
<u-cell title="营业执照名称" :border="false">
<template #icon>
<text class="required">*</text>
</template>
<template #value>
<u-input
v-model="formSupplier.businessLicense"
:disabled="reviewing"
border="none"
placeholder="请填写营业执照名称"
/>
</template>
</u-cell>
</u-form-item>
<u-form-item prop="avatar">
<u-cell title="头像" :border="false">
<template #label>
@@ -132,8 +147,10 @@
</template>
<template #label>
<u-upload
v-if="showUpload"
:fileList="quaPics"
:maxCount="9"
:deletable="!reviewing"
:disabled="reviewing"
name="quaPics"
multiple
@@ -144,7 +161,7 @@
</u-cell>
</view>
<u-form-item prop="area">
<u-cell title="省市区" :border="false" isLink>
<u-cell title="省市区" :border="false" isLink @click="$refs.cityselect.open()">
<template #icon>
<text class="required">*</text>
</template>
@@ -152,7 +169,7 @@
<view class="u-text__value" :class="{'area-item':formSupplier.area && formSupplier.area.length===0}">
<CitySelect
ref="cityselect"
:defaultValue="formSupplier.area"
:defaultValue="formSupplier.area || ''"
:disabled="reviewing"
:items="district"
@callback="resultSupplier"
@@ -300,6 +317,7 @@
:fileList="pics"
:maxCount="99999"
:disabled="reviewing"
:deletable="!reviewing"
name="pics"
multiple
@afterRead="afterRead"
@@ -331,7 +349,7 @@
</u-cell>
</u-form-item>
<u-form-item prop="typeText">
<u-cell title="店铺类型" :border="false" isLink>
<u-cell title="店铺类型" :border="false">
<template #icon>
<text class="required">*</text>
</template>
@@ -340,13 +358,16 @@
:value="indexType"
:range="columnsType"
:disabled="reviewing"
mode=selector
mode="selector"
range-key="name"
class="picker"
@change="typeChange"
>
<u--text v-if="form.typeText" :text="form.typeText"></u--text>
<u--text v-else text="请选择类型" color="#c0c4cc"></u--text>
<view style="display: flex; align-items: center; justify-content: flex-end;">
<u--text v-if="form.typeText" :text="form.typeText"></u--text>
<u--text v-else text="请选择类型" color="#c0c4cc"></u--text>
<u-icon name="arrow-right" color="#909399" size="18"></u-icon>
</view>
</picker>
</template>
</u-cell>
@@ -383,7 +404,7 @@
</u-cell>
</u-form-item>
<u-form-item prop="area">
<u-cell title="省市区" :border="false" isLink>
<u-cell title="省市区" :border="false" isLink @click="$refs.cityselect2.open()">
<template #icon>
<text class="required">*</text>
</template>
@@ -391,7 +412,7 @@
<view class="u-text__value" :class="{'area-item':form.area && form.area.length===0}">
<CitySelect
ref="cityselect2"
:defaultValue="form.area"
:defaultValue="form.area || ''"
:disabled="reviewing"
:items="district"
@callback="result"
@@ -432,7 +453,7 @@
<text class="required">*</text>
</template>
<template #value>
<u--text :text="form.serviceTime + ' - ' + form.serviceEndTime"></u--text>
<u--text :text="`${form.serviceTime || ''} - ${form.serviceEndTime || ''}`"></u--text>
<tpf-time-range
startTime="00:00"
endTime="23:59"
@@ -455,7 +476,9 @@
<u-cell :border="false">
<template #title>
<view class="line">
<u-input
<u-textarea
:maxlength="-1"
autoHeight
v-model="form.content"
:disabled="reviewing"
border="none"
@@ -490,7 +513,7 @@
@cancel="changeRemove(false)"
@confirm="onRemove"
/>
<u-popup :show="show" mode="center" round="10" closeable @close="show=false">
<u-popup :show="show" mode="center" round="10" @close="show=false">
<view class="popup-body">
<view class="tc bold">入驻申请须知</view>
<view class="content">
@@ -550,6 +573,7 @@ export default {
quaPics: [],
formSupplier: {
name: '',
businessLicense: '',
avatar: '',
contact: '',
phone: '',
@@ -585,6 +609,12 @@ export default {
message: '请填写供应商名称',
trigger: ['blur', 'change']
},
'businessLicense': {
type: 'string',
required: true,
message: '请填写营业执照名称',
trigger: ['blur', 'change']
},
'contact': {
type: 'string',
required: true,
@@ -649,7 +679,8 @@ export default {
stateType: 'primary',
// 是否为审核中
reviewing: false,
pageKeyId: ''
pageKeyId: '',
showUpload: false
}
},
computed: {
@@ -763,6 +794,7 @@ export default {
}
this.formSupplier = !this.reviewing && local || {
name: '',
businessLicense: '',
avatar: '',
contact: '',
phone: '',
@@ -859,6 +891,7 @@ export default {
initInfo() {
// 是否缺少参数区分文玩和特产?
getShopInfo().then((res) => {
this.showUpload = true
const {data} = res
// console.log(res, 'initInfo--------');
// console.log(data, 'data--------');
@@ -928,9 +961,9 @@ export default {
this.formSupplier = data.supplierInfo
this.formSupplier.typeText = this.columnsSupplierType[this.indexSupplierType].value
this.formSupplier.position = this.$forceToDecimal(this.formSupplier.longitude, 6) + ',' + this.$forceToDecimal(this.formSupplier.latitude, 6)
this.formSupplier.area = `${this.formSupplier.provinceName || ''} ${this.formSupplier.cityName || ''} ${this.formSupplier.areaName || ''}`
this.formSupplier.area = `${this.formSupplier.provinceName || ''} ${this.formSupplier.cityName || ''} ${this.formSupplier.areaName || ''}` || ''
this.indexSupplierType = this.columnsSupplierType.findIndex(item => item.key === this.formSupplier.subType)
// console.log(this.formSupplier);
console.log(this.formSupplier);
}
})
},
+929
View File
@@ -0,0 +1,929 @@
<template>
<view class="container">
<view class="v12-pa-2 reason-box" v-if="form.reviewStatus === 2">
驳回原因: {{ form.reviewMsg }}
</view>
<view class="v12-pa-2 reason-box" v-if="form.isDraft === 0 && form.reviewStatus !== 1 &&form.reviewStatus === 0">
审核中
</view>
<view class="form-item">
<view class="title">
<text class="required">* </text>
名称:
</view>
<view class="input">
<u-textarea
border="none"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入名称"
:placeholder-style="inputPlaceholderStyle"
v-model="form.name"
/>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
营业执照名称:
</view>
<view class="input">
<u-textarea
border="none"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入营业执照名称"
:placeholder-style="inputPlaceholderStyle"
v-model="form.businessLicense"
autoHeight
/>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start">
<view>
<view class="title">
<text class="required">* </text>
主图:
</view>
<view class="input">
<u-upload
:fileList="coverImg.filter(item => item.url)"
:disabled="form.isDraft === 0 && (coverImg.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="1"
count="1"
name="coverImg"
multiple
@afterRead="afterRead($event, 'coverImg')"
@delete="deletePic($event, 'coverImg')"
></u-upload>
</view>
</view>
<view class="v12-ml-2">
<view class="title">
<text class="required">* </text>
头像:
</view>
<view class="input">
<u-upload
:fileList="avatarImg.filter(item => item.url)"
:disabled="form.isDraft === 0 && (avatarImg.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="1"
count="1"
name="avatarImg"
multiple
@afterRead="afterRead($event, 'avatarImg')"
@delete="deletePic($event, 'avatarImg')"
></u-upload>
</view>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
轮播图:
</view>
<view class="input">
<u-upload
:fileList="bannerImg"
:disabled="form.isDraft === 0 && (bannerImg.length > 8 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="9"
count="9"
name="bannerImg"
multiple
@afterRead="afterRead($event, 'bannerImg')"
@delete="deletePic($event, 'bannerImg')"
></u-upload>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
视频:
</view>
<view class="input">
<u-upload
v-if="videos.length === 0"
accept="video"
multiple
:max-count="1"
:fileList="videos"
:disabled="form.isDraft === 0 && (videos.length > 0 || form.reviewStatus === 0 || form.reviewStatus === 1)"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="1"
count="9"
name="bannerImg"
:previewFullImage="true"
@afterRead="afterRead($event, 'videos', 'video')"
@delete="deletePic($event, 'videos')"
></u-upload>
<view v-else class="video-wrapper">
<video
:src="videos[0].url"
:poster="videos[0].url + '?vframe/jpg/offset/1'"
:controls="false"
play-btn-position="center"
class="video-item"
/>
<view
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
class="delete-badge"
@click.stop="deleteVideo(0)"
>
x
</view>
</view>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
公司名称
</view>
<view class="v12-ml-2" style="flex: 1;">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入公司名称"
v-model="form.companyName"
/>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
简介
</view>
<view class="v12-ml-2" style="flex: 1;">
<input
type="text"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入公司简介"
v-model="form.intro"
/>
</view>
</view>
<u-divider></u-divider>
<view @click="showTimePicker" class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
营业时间
</view>
<view class="v12-ml-2">
<view color="#999999" :text="`${form.serviceTime || '起始时间'} - ${form.serviceEndTime || '结束时间'}`">
<text :style="{'color': form.serviceTime? '#333' : '#999999'}">{{form.serviceTime || '起始时间'}}</text> - <text :style="{'color': form.serviceEndTime? '#333' : '#999999'}">{{form.serviceEndTime || '结束时间'}}</text>
</view>
<tpf-time-range
startTime="00:00"
endTime="23:59"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:startDefaultTime="form.serviceTime"
:endDefaultTime="form.serviceEndTime"
ref="TimePickerPopupRef"
@timeRange="timeRange"
>
</tpf-time-range>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
省市区
</view>
<view class="v12-ml-2 one-t" style="flex: 1;">
<CitySelect
ref="cityselect"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
:defaultValue="form.area || '请选择'"
:items="district"
@callback="resultCity"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
详细地址
</view>
<view class="v12-ml-2" style="flex: 1;">
<input
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
type="text"
placeholder="请输入详细地址"
v-model="form.address"
/>
</view>
</view>
<u-divider></u-divider>
<view @click="chooLocation" class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
地理定位
</view>
<view class="v12-ml-2" style="flex: 1;">
<u--text v-if="form.position" :text="form.position"></u--text>
<u--text v-else text="请选择" color="#c0c4cc"></u--text>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
商家电话
</view>
<view class="v12-ml-2" style="flex: 1;">
<u-input
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入商家电话"
v-model="form.phone"
maxlength="11"
border="none"
>
<template slot="suffix" >
<u-code
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
ref="uCode"
@change="codeChange"
seconds="30"
changeText="X秒重新获取"
></u-code>
<u-button
v-if="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
@tap="getCode"
:text="tips"
type="success"
color="#4A97FC"
></u-button>
</template>
</u-input>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
填写验证码
</view>
<view class="v12-ml-2" style="flex: 1;">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
placeholder="请输入验证码"
v-model="form.captcha"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
商家微信
</view>
<view class="v12-ml-2" style="flex: 1;">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入微信号"
v-model="form.wechat"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1)"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2">
<view class="title">
文化:
</view>
<view class="input">
<u-upload
:fileList="cultureImg"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1) || cultureImg.length > 98"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="99"
count="99"
name="cultureImg"
multiple
@afterRead="afterRead($event, 'cultureImg')"
@delete="deletePic($event, 'cultureImg')"
></u-upload>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
资质:
</view>
<view class="input">
<u-upload
:fileList="qualificationImg"
:disabled="form.isDraft === 0 && (form.reviewStatus === 0 || form.reviewStatus === 1) || qualificationImg.length > 98"
:deletable="form.isDraft === 1 || (form.reviewStatus !== 0 && form.reviewStatus !== 1)"
:maxCount="99"
count="99"
name="qualificationImg"
multiple
@afterRead="afterRead($event, 'qualificationImg')"
@delete="deletePic($event, 'qualificationImg')"
></u-upload>
</view>
</view>
<view v-if="form.isDraft === 0 && form.reviewStatus !== 1">
<view class="v12-primary v12-white-text submit-btn" @click="submitForm" :style="{'background': form.reviewStatus === 0 ? '#FA979F !important' : '#4A97FC'}">
{{ form.reviewStatus === 0 ? '撤销申请' : form.reviewStatus === 2 ? '重新提交' : '提交审核' }}
</view>
</view>
<view v-if="form.isDraft === 1">
<view class="v12-primary v12-white-text submit-btn" @click="submitForm">
{{ '提交审核' }}
</view>
</view>
<u-popup :show="show" mode="center" round="10" @close="show=false"
>
<view class="popup-body">
<view class="tc bold">入驻申请须知</view>
<view class="content">
<rich-text :nodes="notice" />
</view>
<view class="agree-box v12-mb-2">
<checkbox-group @change="changeAgree">
<label>
<checkbox value="yes" style="transform:scale(0.8)"/>
</label>
<text>我已仔细阅读并同意</text>
</checkbox-group>
</view>
<u-button
type="error"
text="确定"
:disabled="!isAgree"
@click="save"
/>
</view>
</u-popup>
</view>
</template>
<script>
import CitySelect from '@/components/CitySelect'
import {getCity, registerVerify} from '@/api/user'
import {
chooseImage,
uploadVideo,
uploadImage
} from '@/utils'
import {
getShopData,
getTravelInfo,
saveTravel,
saveTravelDraft,
revokeTravel
} from '@/api/join'
export default {
components: {
CitySelect
},
data() {
return {
isAgree: false,
show: false,
notice: '',
tips: '获取验证码',
inputPlaceholderStyle: {
fontSize: '32rpx'
},
coverImg: [],
avatarImg: [],
//
bannerImg: [],
videos: [],
district: [],
//
cultureImg: [],
//
qualificationImg: [],
partnerId: 0,
captcha: '',
form: {
/**
*
travelInfo object 旅居管家基本信息
|- name string 名称
|- cover string 主图
|- avatar string 头像
|- companyName string 公司名称
|- video string 视频
|- banner string 轮播图逗号分隔字符串
|- serviceTime string 营业开始时间
|- serviceEndTime string 营业结束时间
|- provinceId int 省份ID
|- provinceName string 省份名称
|- cityId int 城市ID
|- cityName string 城市名称
|- areaId int 区域ID
|- areaName string 区域名称
|- address string 详细地址
|- longitude number 经度
|- latitude number 纬度
|- contact string 联系人
|- phone string 联系方式
|- wechat string 联系微信
|- culture string 文化逗号分隔字符串
|- qualification string 资质逗号分隔字符串
*
*/
captcha: '',
name: '',
cover: '',
avatar: '',
companyName: '',
video: '',
banner: '',
serviceTime: '00:00',
serviceEndTime: '23:59',
provinceId: 0,
provinceName: '',
cityId: 0,
cityName: '',
areaId: 0,
areaName: '',
address: '',
longitude: 0,
latitude: 0,
contact: '',
phone: '',
wechat: '',
culture: '',
qualification: '',
reviewMsg: ''
}
}
},
onLoad(opts) {
this.partnerId = opts.partnerId
this.queryCity()
getShopData().then(({data}) => {
this.notice = data.merchantApplyNotice
this.initInfo()
})
},
// onShow() {
// },
onUnload() {
this.saveDraft()
},
onHide() {
// if (this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
// return
// }
this.saveDraft()
},
methods: {
changeAgree(e) {
this.isAgree = e.detail.value.includes('yes')
},
save() {
this.show = false
},
/**
* 字符串转换为数组
* @param {string} str 字符串
* @param {string} split 分隔符
* @returns {array} 数组
*/
stringToMapArr(str, split = ',') {
return str.split(split).map(item => ({url: item}))
},
/**
* 保存草稿
*/
saveDraft() {
this.form.cover = this.coverImg.filter(item => item.url).map(item => item.url).join(',')
this.form.avatar = this.avatarImg.filter(item => item.url).map(item => item.url).join(',')
this.form.banner = this.bannerImg.filter(item => item.url).map(item => item.url).join(',')
this.form.video = this.videos.filter(item => item.url).map(item => item.url).join(',')
this.form.culture = this.cultureImg.filter(item => item.url).map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.filter(item => item.url).map(item => item.url).join(',')
const params = {
travelInfo: {...this.form},
partnerId: this.partnerId,
captcha: this.form.captcha
}
saveTravelDraft(params).then(res => {
if (res.status === 200) {
// uni.$u.toast('稿')
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
/**
* 初始化信息
*/
initInfo() {
getTravelInfo().then(res => {
if(res.data.isDraft === null || res.data.isDraft === 1) {
this.show = true
}
const {travelInfo} = res.data || {}
if (res.status === 200) {
this.form = {...this.form, ...travelInfo}
this.coverImg = travelInfo.cover ? this.stringToMapArr(travelInfo.cover) : []
this.avatarImg = travelInfo.avatar ? this.stringToMapArr(travelInfo.avatar) : []
this.bannerImg = travelInfo.banner ? this.stringToMapArr(travelInfo.banner) : []
this.videos = travelInfo.video ? this.stringToMapArr(travelInfo.video) : []
this.cultureImg = travelInfo.culture ? this.stringToMapArr(travelInfo.culture) : []
this.qualificationImg = travelInfo.qualification ? this.stringToMapArr(travelInfo.qualification) : []
this.form.reviewStatus = res.data.reviewStatus
this.form.reviewMsg = res.data.reviewMsg
this.form.isDraft = res.data.isDraft
this.form.latitude = travelInfo.latitude || 0
this.form.longitude = travelInfo.longitude || 0
this.form.position = this.$forceToDecimal(travelInfo.longitude, 6) + ',' + this.$forceToDecimal(travelInfo.latitude, 6)
this.form.area = `${this.form.provinceName || ''} ${this.form.cityName || ''} ${this.form.areaName || ''}`.trim()
this.captcha = travelInfo.captcha
}
}).catch(err => {
// uni.$u.toast(err.msg)
})
},
/**
* 撤销申请
*/
revokeTravel() {
uni.showModal({
title: '提示',
content: '确定撤销申请吗?',
success: (res) => {
if (res.confirm) {
revokeTravel().then(res => {
if (res.status === 200) {
uni.$u.toast('撤销申请成功')
this.initInfo()
}
}).catch(err => {
uni.$u.toast(err.msg)
})
}
}
})
},
/**
* 提交表单
*/
submitForm() {
if(this.form.reviewStatus === 0 && this.form.isDraft === 0 && this.form.reviewStatus !== 1) {
this.revokeTravel()
return
}
if(this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) return
this.form.cover = this.coverImg.filter(item => item.url).map(item => item.url).join(',')
this.form.avatar = this.avatarImg.filter(item => item.url).map(item => item.url).join(',')
this.form.banner = this.bannerImg.filter(item => item.url).map(item => item.url).join(',')
this.form.video = this.videos.filter(item => item.url).map(item => item.url).join(',')
this.form.culture = this.cultureImg.filter(item => item.url).map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.filter(item => item.url).map(item => item.url).join(',')
if (!this.form.name) {
uni.$u.toast('请输入名称')
return
}
if (!this.form.businessLicense) {
uni.$u.toast('请输入营业执照名称')
return
}
if (!this.form.cover) {
uni.$u.toast('请上传主图')
return
}
if (!this.form.avatar) {
uni.$u.toast('请上传头像')
return
}
if (!this.form.companyName) {
uni.$u.toast('请输入公司名称')
return
}
if (!this.form.intro) {
uni.$u.toast('请输入公司简介')
return
}
if (!this.form.banner) {
uni.$u.toast('请上传轮播图')
return
}
//
if (!this.form.serviceTime) {
uni.$u.toast('请选择营业开始时间')
return
}
if (!this.form.serviceEndTime) {
uni.$u.toast('请选择营业结束时间')
return
}
//
if (!this.form.provinceId) {
uni.$u.toast('请选择省份')
return
}
if (!this.form.cityId) {
uni.$u.toast('请选择城市')
return
}
if (!this.form.areaId) {
uni.$u.toast('请选择区域')
return
}
if (!this.form.address) {
uni.$u.toast('请输入详细地址')
return
}
//
if (!this.form.longitude) {
uni.$u.toast('请选择地理定位')
return
}
if (!this.form.latitude) {
uni.$u.toast('请选择地理定位')
return
}
if (!this.form.qualification) {
uni.$u.toast('请上传资质')
return
}
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (!this.form.wechat) {
uni.$u.toast('请输入联系微信')
return
}
const params = {
travelInfo: {...this.form},
partnerId: this.partnerId,
captcha: this.form.captcha
}
saveTravel(params).then(res => {
if (res.status === 200) {
uni.showToast({
title: '您的入驻申请已提交成功,请耐心等待审核',
icon: 'none',
success: () => {
this.initInfo();
this.show = false;
}
})
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
/**
* 获取验证码
*/
getCode() {
// if (this.reviewing) return
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (this.form.phone.length !== 11) {
uni.$u.toast('请输入11位手机号')
return
}
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: '正在获取验证码'
})
registerVerify({
phone: this.form.phone,
type: 'merchantTravelApply'
}).then(() => {
uni.hideLoading()
// this.start()
uni.$u.toast('验证码已发送')
//
this.$refs.uCode.start()
}).catch(err => {
uni.hideLoading()
uni.$u.toast(err.msg)
})
} else {
uni.$u.toast('倒计时结束后再发送')
}
},
/**
* 验证码按钮回调
*/
codeChange(text) {
this.tips = text
},
/**
* 选择位置
*/
chooLocation() {
if(this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) return
uni.chooseLocation({
success: (res) => {
console.log(res)
this.form.address = res.address
this.form.longitude = this.$forceToDecimal(res.longitude, 6)
this.form.latitude = this.$forceToDecimal(res.latitude, 6)
this.form.position = this.$forceToDecimal(res.longitude, 6) + ',' + this.$forceToDecimal(res.latitude, 6)
}
})
},
/**
* 选择城市回调
* @param {object} e - 事件对象
*/
resultCity(values) {
const address = {
province: values.province.name || '',
city: values.city.name || '',
district: values.district.name || '',
provinceId: values.province.id,
cityId: values.city.id,
areaId: values.district.id
}
this.form.area = `${address.province || ''} ${address.city || ''} ${address.district || ''}`
this.form.areaName = address.district || ''
this.form.provinceName = address.province || ''
this.form.cityName = address.city || ''
this.form.provinceId = address.provinceId
this.form.cityId = address.cityId
this.form.areaId = address.areaId
},
/**
* 选择城市
*/
queryCity() {
getCity().then(({data}) => {
this.district = data
})
},
/**
* 显示时间选择器
*/
showTimePicker() {
if(this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) {
return
}
this.$refs.TimePickerPopupRef.open()
},
/**
* 选择时间范围
* @param {array} e - 时间范围数组包含开始时间和结束时间
*/
timeRange(e) {
this.form.serviceTime = e[0]
this.form.serviceEndTime = e[1]
this.$forceUpdate()
},
/**
* 选择图片
* @param {string} imgField - 图片字段名
*/
chooseImage(imgField) {
chooseImage(img => {
this.form[imgField] = img;
})
},
/**
* 上传图片或视频
* @param {object} e - 事件对象
* @param {string} imgField - 图片字段名
* @param {string} type - 类型'video''image'
*/
afterRead(e, imgField, type) {
const uploadFunc = type == 'video' ? uploadVideo : uploadImage;
if(e.file.length > 0) {
e.file.forEach(item => {
if(!item.url) return
uploadFunc(item.url, img => {
if(!img) return
this[imgField].push({url: img});
console.log(this[imgField], imgField);
})
})
}
},
/**
* 删除视频右上角角标
* @param {number} index - 视频索引
*/
deleteVideo(index) {
if(this.form.isDraft === 0 && (this.form.reviewStatus === 0 || this.form.reviewStatus === 1)) return
uni.showModal({
title: '确认删除',
content: '确定删除该视频?',
success: (res) => {
if (res.confirm) {
this.deletePic({ index }, 'videos')
}
}
})
},
/**
* 删除图片或视频
* @param {object} e - 事件对象
* @param {string} imgField - 图片字段名
*/
deletePic(e, imgField) {
this[imgField].splice(e.index, 1);
console.log(this[imgField]);
}
}
}
</script>
<style scoped lang="scss">
.popup-body {
width: 80vw;
height: 80vh;
padding: 30rpx;
overflow: hidden;
}
.popup-body .content {
height: 52vh;
overflow-y: auto;
margin: 30rpx 0;
}
.popup-body .agree-box {
margin: 60rpx 0;
font-size: 28rpx;
color: #666;
}
.popup-body {
.title {
line-height: 80rpx;
text-align: center;
}
}
.container {
padding: 20rpx;
background: #fff;
}
.title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.required {
color: #C52733;
}
.input{
margin-top: 20rpx;
}
.submit-btn{
width: 100%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
}
.reason-box{
background: rgba(255,160,167,0.39);
border-radius: 8rpx;
color: #C52733;
font-size: 30rpx;
margin-bottom: 20rpx;
}
.title{
white-space: nowrap;
}
.video-item{
width: 160rpx;
height: 160rpx;
}
.video-wrapper{
position: relative;
display: inline-block;
}
.delete-badge{
position: absolute;
top: 0rpx;
right: 0rpx;
background: #000;
color: #fff;
padding: 2rpx 8rpx;
border-radius: 0 0 0rpx 200rpx;
font-size: 22rpx;
z-index: 10;
text-align: center;
}
</style>
<style>
.input .u-textarea{
background-color: #F3F3F3 !important;
}
</style>
+8 -2
View File
@@ -161,7 +161,7 @@
<view class="u-text__value" :class="{'area-item':form.area.length===0}">
<CitySelect
ref="cityselect"
:defaultValue="form.area"
:defaultValue="form.area || ''"
:disabled="reviewing"
:items="district"
@callback="result"
@@ -207,7 +207,7 @@
@cancel="changeRemove(false)"
@confirm="onRemove"
/>
<u-popup :show="show" mode="center" round="10" closeable @close="show=false">
<u-popup :show="show" mode="center" round="10" @close="show=false">
<view class="popup-body">
<view class="tc bold">入驻申请须知</view>
<view class="content">
@@ -265,6 +265,12 @@ export default {
message: '请填写批发商名称',
trigger: ['blur', 'change']
},
'businessLicense': {
type: 'string',
required: true,
message: '请填写营业执照名称',
trigger: ['blur', 'change']
},
'contact': {
type: 'string',
required: true,
@@ -4,6 +4,7 @@
:src="topImage"
class="top-img"
mode="widthFix"
@click="topImageClick"
/>
<view class="page-wrap">
<view class="page-2">
@@ -4,6 +4,7 @@
:src="topImage"
class="top-img"
mode="widthFix"
@click="topImageClick"
/>
<view class="page-wrap">
<view class="page-2">
@@ -4,6 +4,7 @@
:src="topImage"
class="top-img"
mode="widthFix"
@click="topImageClick"
/>
<!-- 第二页 -->
<view class="page-wrap">
@@ -4,6 +4,7 @@
:src="topImage"
class="top-img"
mode="widthFix"
@click="topImageClick"
/>
<!-- 第二页 -->
<view class="page-wrap">
+14
View File
@@ -0,0 +1,14 @@
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
active: 'weixin',
}
},
methods: {
// 支付方式切换
payItem(type) {
this.active = type
}
}
}
+104
View File
@@ -0,0 +1,104 @@
import { getCouponReceive } from '@/api/user'
export const themeMixins = {
data() {
return {
isLoad: false,
pageData: {},
topImage: '',
contents: [],
pageKeyId: '',
templateKey: '',
// 模板数据
templateProperties: {}
}
},
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
if (this.pageData.forwardTitle && this.pageData.forwardImage) {
return {
title: this.pageData.forwardTitle,
path: this.pageKeyId.indexOf('appContentHomestay') > -1 ? `/pkg_product/views/homestay?id=${this.pageData.id}` : `/pkg_product/views/festival?id=${this.pageData.id}&theme=${this.theme || ''}`,
imageUrl: this.pageData.forwardImage
}
} else {
return {
title: this.share.title,
path: this.share.path,
imageUrl: this.share.imageUrl,
desc: this.share.desc,
content: this.share.content,
success(res){
uni.showToast({
title:'分享成功'
})
},
fail(res){
uni.showToast({
title:'分享失败',
icon:'none'
})
}
}
}
},
onShareTimeline() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
const title = this.pageData.forwardTitle || this.share.title
return {
title,
query: `id=${this.pageData.id}&theme=${this.theme || ''}`,
imageUrl: this.pageData.forwardImage || this.share.imageUrl
}
},
methods: {
topImageClick() {
if (this.templateProperties.page1CouponEnable === 1 && this.templateProperties.page1CouponId) {
getCouponReceive(this.templateProperties.page1CouponId).then(res => {
if (res.status === 200) {
uni.showToast({
title: '领取成功',
icon: 'none'
})
}
}).catch((err) => {
uni.showToast({
title: err.msg + '',
icon: 'none',
duration: 3000
})
})
}
},
goGoodsOrShop(item) {
const id = item.linkId || ''
if (!id) return
const type = item.type || 1
if (type === 1) {
uni.navigateTo({
url: `/pages/shop/GoodsCon/index?id=${id}&from=festival`
})
}
if (type === 2) {
uni.navigateTo({
url: `/pagesInn/inn/innHome?id=${id}&from=festival`
})
}
if (type === 3) {
uni.navigateTo({
url: `/pages/town/index?id=${id}&from=festival`
})
}
if (type === 4) {
uni.navigateTo({
url: `/pkg_product/views/famousQianxianShop?id=${id}&from=festival`
})
}
}
}
}
+19
View File
@@ -1,3 +1,4 @@
import { getCouponReceive } from '@/api/user'
export const themeTemplateMixins = {
props: {
topImage: {
@@ -12,6 +13,24 @@ export const themeTemplateMixins = {
}
},
methods: {
topImageClick() {
if (this.templateProperties.page1CouponEnable === 1 && this.templateProperties.page1CouponId) {
getCouponReceive(this.templateProperties.page1CouponId).then(res => {
if (res.status === 200) {
uni.showToast({
title: '领取成功',
icon: 'none'
})
}
}).catch((err) => {
uni.showToast({
title: err.msg + '',
icon: 'none',
duration: 3000
})
})
}
},
itemClick(item) {
if (!item.available) return
this.$emit('view', item)
+865
View File
@@ -0,0 +1,865 @@
<script>
import dayjs from 'dayjs'
import { getLatestExperienceCoupon, getExperienceCouponList, getExperienceCouponItems, createExperienceCouponOrder, getExperienceCouponCategoryList } from '@/api/product'
export default {
data() {
const webUrl = this.$VUE_APP_RESOURCES_URL
return {
webUrl,
headerImage: webUrl + '/experienceCoupon/header.png',
totalCount: 0,
selectedCount: 0,
initialUsedCount: 0,
remainNum: 0,
startTime: '',
endTime: '',
expireDate: '',
couponList: [],
activeCouponId: null,
categoryList: [
{ id: 'all', name: '全部体验' }
],
activeCategoryId: 'all',
experienceList: [],
selectedProjectIds: [],
userLatitude: null,
userLongitude: null,
merId: ''
}
},
computed: {
filteredList() {
if (this.activeCategoryId === 'all') {
return this.experienceList
}
return this.experienceList.filter(item => item.categoryId === this.activeCategoryId)
},
remainCount() {
return Math.max(this.totalCount - this.selectedCount, 0)
}
},
onLoad() {
this.initPage()
},
methods: {
formatDateByTimestamp(value) {
if (value == null || value === '') {
return ''
}
if (typeof value === 'number') {
const ts = value > 1000000000000 ? value : value * 1000
return dayjs(ts).format('YYYY-MM-DD')
}
const num = Number(value)
if (!Number.isNaN(num)) {
const ts = num > 1000000000000 ? num : num * 1000
return dayjs(ts).format('YYYY-MM-DD')
}
return value
},
initPage() {
this.getUserLocation()
this.fetchExperienceCoupon()
this.fetchCouponList()
this.fetchCategoryList()
},
getUserLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
this.userLatitude = res.latitude
this.userLongitude = res.longitude
this.updateDistances()
},
fail: (err) => {
console.error('获取位置失败', err)
}
})
},
updateDistances() {
if (!this.experienceList.length || !this.userLatitude || !this.userLongitude) {
return
}
this.experienceList = this.experienceList.map(item => {
if (item.latitude && item.longitude) {
return {
...item,
distance: this.calculateDistance(this.userLatitude, this.userLongitude, item.latitude, item.longitude)
}
}
return item
})
},
calculateDistance(lat1, lon1, lat2, lon2) {
if (!lat1 || !lon1 || !lat2 || !lon2) return ''
const R = 6371 // km
const dLat = (lat2 - lat1) * Math.PI / 180
const dLon = (lon2 - lon1) * Math.PI / 180
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
const d = R * c
return d.toFixed(1) + 'km'
},
setActiveCoupon(coupon) {
if (!coupon) {
return
}
this.activeCouponId = coupon.id
this.activeCategoryId = 'all'
this.totalCount = coupon.totalNum || 0
const remain = coupon.remainNum != null ? coupon.remainNum : 0
this.remainNum = remain
this.initialUsedCount = Math.max(this.totalCount - remain, 0)
this.selectedProjectIds = []
this.selectedCount = this.initialUsedCount
this.startTime = coupon.startTime || ''
this.endTime = coupon.endTime || ''
this.expireDate = this.endTime || this.expireDate
this.headerImage = coupon.experienceCouponImage || (this.webUrl + '/experienceCoupon/header.png')
this.fetchCategoryList()
this.fetchExperienceItems()
},
fetchExperienceCoupon() {
getLatestExperienceCoupon().then(({ data }) => {
if (!data) {
return
}
const latest = {
...data,
startTime: this.formatDateByTimestamp(data.startTime),
endTime: this.formatDateByTimestamp(data.endTime)
}
this.merId = latest.merId || ''
this.setActiveCoupon(latest)
if (!this.couponList.length) {
this.couponList = [latest]
}
}).catch(() => {})
},
fetchCouponList() {
getExperienceCouponList().then(({ data }) => {
const list = (data || []).map(item => ({
...item,
startTime: this.formatDateByTimestamp(item.startTime),
endTime: this.formatDateByTimestamp(item.endTime)
}))
this.couponList = list
if (!this.activeCouponId && list.length > 0) {
this.setActiveCoupon(list[0])
}
}).catch(() => {})
},
fetchCategoryList() {
if (!this.activeCouponId) {
this.categoryList = [{ id: 'all', name: '全部体验' }]
return
}
const params = { couponId: this.activeCouponId }
getExperienceCouponCategoryList(params).then(({ data }) => {
const list = data || []
this.categoryList = [
{ id: 'all', name: '全部体验' },
...list.map(item => ({
id: item.id,
name: item.name
}))
]
}).catch(() => {
this.categoryList = [{ id: 'all', name: '全部体验' }]
})
},
fetchExperienceItems() {
if (!this.activeCouponId) {
this.experienceList = []
return
}
const params = {
couponId: this.activeCouponId
}
if (this.activeCategoryId && this.activeCategoryId !== 'all') {
params.experienceStoreCategoryId = this.activeCategoryId
}
getExperienceCouponItems(params).then(({ data }) => {
const list = data || []
const selectedSet = new Set(this.selectedProjectIds)
this.experienceList = list.map(item => {
const lat = Number(item.latitude || 0)
const lon = Number(item.longitude || 0)
let distance = ''
if (this.userLatitude && this.userLongitude && lat && lon) {
distance = this.calculateDistance(this.userLatitude, this.userLongitude, lat, lon)
}
const id = item.id != null ? item.id : (item.projectId != null ? item.projectId : item.merId)
return {
id,
experienceMerchantId: item.merId || item.experienceMerchantId || item.storeId || '',
title: item.name || '',
merId: item.merId || '',
subTitle: item.projectName || item.intro || '',
distance,
latitude: lat,
longitude: lon,
businessTime: item.serviceTime && item.serviceEndTime ? `${item.serviceTime}-${item.serviceEndTime}` : (item.serviceTime || '00:00-23:59'),
tags: item.intro || '',
categoryId: this.activeCategoryId === 'all' ? 'all' : this.activeCategoryId,
image: item.image || item.cover || '',
selected: selectedSet.has(id),
rawData: item
}
})
this.selectedCount = this.initialUsedCount + this.selectedProjectIds.length
}).catch(() => {
this.experienceList = []
})
},
changeCoupon(coupon) {
this.setActiveCoupon(coupon)
},
changeCategory(id) {
this.activeCategoryId = id
this.fetchExperienceItems()
},
toggleSelect(item) {
if (!item.selected && this.remainCount === 0) {
uni.showToast({
title: '体验券数量已达上限',
icon: 'none'
})
return
}
item.selected = !item.selected
const id = item.id
if (item.selected) {
if (!this.selectedProjectIds.includes(id)) {
this.selectedProjectIds.push(id)
}
} else {
this.selectedProjectIds = this.selectedProjectIds.filter(pid => pid !== id)
}
this.selectedCount = this.initialUsedCount + this.selectedProjectIds.length
},
goExperienceStoreDetail(item) {
console.log(item);
const merchantId = item.merId
if (!merchantId) {
uni.showToast({
title: '未找到体验店信息',
icon: 'none'
})
return
}
const query = []
query.push('experienceMerchantId=' + encodeURIComponent(merchantId))
uni.navigateTo({
url: '/pkg_product/views/experienceProjectDetail?' + query.join('&')
})
},
submitOrder() {
if (!this.activeCouponId) {
uni.showToast({
title: '当前无可用体验券',
icon: 'none'
})
return
}
if (!this.selectedProjectIds.length) {
uni.showToast({
title: '请先选择体验项目',
icon: 'none'
})
return
}
const projectIds = this.selectedProjectIds.slice()
const data = {
couponId: this.activeCouponId,
projectIds
}
createExperienceCouponOrder(data).then((res) => {
if (res.success || (res.data && res.data.status === 'SUCCESS')) {
uni.showToast({
title: '下单成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/order/MyOrder/index?type=3&tabIndex=1'
})
}, 1500)
} else {
uni.showToast({
title: res.msg || '下单失败,请稍后重试',
icon: 'none'
})
}
}).catch(() => {
uni.showToast({
title: '下单失败,请稍后重试',
icon: 'none'
})
})
},
handleContinueSelect() {
this.submitOrder()
},
handleGoExperience() {
this.submitOrder()
},
handleConfirm() {
this.submitOrder()
}
}
}
</script>
<template>
<view class="experience-coupon-page">
<view class="header">
<image
:src="headerImage"
class="header-bg"
mode="aspectFill"
/>
<view class="header-mask" />
<view class="header-blur-transition" />
<view class="header-content">
<scroll-view
v-if="couponList.length > 1"
scroll-x
class="coupon-tabs-scroll"
:show-scrollbar="false"
>
<view class="coupon-tabs">
<view
v-for="(coupon, index) in couponList"
:key="coupon.id"
class="header-tag-main"
:class="{ active: activeCouponId === coupon.id }"
@click="changeCoupon(coupon)"
>
体验券{{ index + 1}}
</view>
</view>
</scroll-view>
<view class="header-tag-main active" v-if="couponList.length === 1">请选择您的免费体验券</view>
<view class="header-main">
<view class="line-info flex ai-center">
根据您的套餐您有 <view class="num-badge">{{ totalCount }}</view> 张体验券可以选择
</view>
<view class="line-status flex ai-center">
<view class="status-pill flex ai-center" style="flex-shrink: 0;">
<u-icon name="checkmark-circle" color="#fff" size="14" style="margin-left: 6rpx; flex-shrink: 0;" />
<text class="status-text" style="white-space: nowrap;">已选清单</text>
<view class="count-badge" style="white-space: nowrap;">{{ selectedCount }}/{{ totalCount }}</view>
</view>
<view class="date-info flex ai-center" style="flex-shrink: 0;">
<text style="white-space: nowrap;">请在</text>
<view class="date-badge" style="white-space: nowrap;">{{ expireDate }}</view>
<text style="white-space: nowrap;">内选择使用</text>
</view>
</view>
<view class="line-library flex ai-center">
<image :src="webUrl + '/piaoquan.png'" class="icon-shop" mode="widthFix" />
<text class="v12-font-32">可用体验券库</text>
</view>
</view>
</view>
</view>
<view class="content">
<scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
<view class="tabs flex">
<view
v-for="item in categoryList"
:key="item.id"
class="tab-item"
:class="{ active: activeCategoryId === item.id }"
@click="changeCategory(item.id)"
>
{{ item.name }}
</view>
</view>
</scroll-view>
<view
v-if="filteredList.length === 0"
class="empty-state"
>
<image
:src="webUrl + '/orderIcon/nodata.png'"
class="empty-image"
mode="widthFix"
/>
<view class="empty-text">
暂无数据
</view>
</view>
<view
v-else
class="card-list"
>
<view
v-for="item in filteredList"
:key="item.id"
class="card"
@click="goExperienceStoreDetail(item)"
>
<image
:src="item.image"
class="card-image"
mode="aspectFill"
/>
<view class="card-body">
<view class="card-title">
{{ item.title }}
</view>
<view class="info-row flex jc-between ai-center">
<view class="card-subtitle more-t">
{{ item.subTitle }}
</view>
<view class="meta-distance" v-if="item.distance">
<u-icon name="map" color="#999" size="14" style="margin-right: 4rpx;" />
<text>距该地{{ item.distance }}</text>
</view>
</view>
<view class="card-meta">
<text class="meta-time">营业时间{{ item.businessTime }}</text>
</view>
<view class="card-footer flex jc-center ai-center">
<view
class="card-btn"
:class="{ selected: item.selected }"
@click.stop="toggleSelect(item)"
>
{{ item.selected ? '取消选择' : '选择' }}
<view v-if="item.selected" class="check-icon-wrapper">
<u-icon name="checkmark" color="#C52733" size="14" bold />
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="bottom-bar" v-if="selectedCount > 0">
<view
v-if="remainCount > 0"
class="bottom-group flex jc-between ai-center"
>
<view
class="btn-outline"
>
继续选择还可以选{{ remainCount }}
</view>
<view
class="btn-primary"
@click="handleGoExperience"
>
先去体验
</view>
</view>
<view
v-else
class="bottom-single"
>
<view
class="btn-primary"
@click="handleConfirm"
>
确认
</view>
</view>
</view>
</view>
</template>
<style scoped lang="less">
.experience-coupon-page {
min-height: 100vh;
padding-bottom: 140rpx;
background: #f5f5f5;
color: #333;
}
.header {
position: relative;
height: 500rpx;
overflow: hidden;
}
.header-bg {
width: 100%;
height: 100%;
}
.header-mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.6) 100%);
z-index: 1;
}
.header-blur-transition {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 200rpx; /* 增加过渡高度让渐变更平缓 */
background: linear-gradient(to bottom, rgba(245, 245, 245, 0) 0%, rgba(245, 245, 245, 0.4) 40%, rgba(245, 245, 245, 0.8) 70%, #f5f5f5 100%);
pointer-events: none;
z-index: 2;
}
.header-content {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 32rpx 0;
padding-bottom: 120rpx; /* 为下方内容上移留出空间 */
box-sizing: border-box;
color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
z-index: 3;
}
.header-tag-main {
align-self: flex-start;
padding: 12rpx 32rpx 12rpx 24rpx;
background: #fff;
font-weight: bold;
color: #333;
font-size: 28rpx;
border-radius: 40rpx;
flex-shrink: 0;
}
.header-tag-main.active {
background: #C52733;
color: #fff;
}
.header-main {
margin-bottom: 20rpx;
padding: 0 24rpx;
}
.coupon-tabs-scroll {
width: 100%;
white-space: nowrap;
margin-bottom: 20rpx;
}
.coupon-tabs {
display: flex;
gap: 16rpx;
padding-right: 24rpx;
flex-wrap: nowrap;
padding: 0 24rpx;
}
.coupon-tab-item {
padding: 8rpx 24rpx;
border-radius: 32rpx;
background: rgba(255, 255, 255, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.4);
font-size: 24rpx;
color: #fff;
&.active {
background: #fff;
color: #C52733;
border-color: #fff;
}
}
.line-info {
font-size: 26rpx;
margin-bottom: 16rpx;
justify-content: flex-end;
}
.num-badge {
background: #C52733;
color: #fff;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
margin: 0 8rpx;
font-weight: bold;
}
.line-status {
justify-content: space-between;
gap: 16rpx;
margin-bottom: 24rpx;
flex-wrap: nowrap;
overflow-x: auto;
white-space: nowrap;
}
/* 隐藏滚动条 */
.line-status::-webkit-scrollbar {
display: none;
}
.status-pill {
background: #C52733;
padding: 6rpx 6rpx 6rpx 20rpx;
border-radius: 32rpx;
color: #fff;
font-size: 22rpx;
white-space: nowrap;
}
.status-text {
margin:0 12rpx;
}
.icon-check {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
border: 2rpx solid #fff;
margin-right: 8rpx;
position: relative;
}
.icon-check::after {
content: '';
position: absolute;
left: 6rpx;
top: 4rpx;
width: 8rpx;
height: 4rpx;
border-left: 2rpx solid #fff;
border-bottom: 2rpx solid #fff;
transform: rotate(-45deg);
}
.count-badge {
background: #fff;
color: #333;
padding: 4rpx 16rpx;
border-radius: 24rpx;
font-size: 24rpx;
}
.date-info {
font-size: 26rpx;
color: #fff;
white-space: nowrap;
}
.date-badge {
background: #C52733;
padding: 6rpx 16rpx;
border-radius: 100rpx;
margin: 0 8rpx;
font-size: 32rpx;
font-weight: bold;
}
.line-library {
font-size: 28rpx;
font-weight: 500;
.icon-shop {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
}
.content {
padding: 0;
margin-top: -120rpx; /* 将下方内容整体上移,覆盖在渐变区域上 */
position: relative;
z-index: 4;
}
.tabs-scroll {
width: 100%;
white-space: nowrap;
margin-bottom: 24rpx;
}
.tabs {
display: flex;
gap: 16rpx;
margin: 0 24rpx;
padding-right: 24rpx;
}
.tab-item {
padding: 12rpx 32rpx;
border-radius: 32rpx;
background: #fff;
color: #666;
font-size: 26rpx;
flex-shrink: 0;
font-style: italic;
font-weight: bold;
&.active {
background: #C52733;
color: #fff;
}
}
.card-list {
padding-bottom: 24rpx;
column-count: 2;
column-gap: 20rpx;
padding: 0 24rpx 24rpx;
}
.card {
margin-bottom: 24rpx;
border-radius: 20rpx;
overflow: hidden;
background: #fff;
break-inside: avoid;
}
.card-image {
width: 100%;
height: 438rpx;
display: block;
border-radius: 20rpx;
}
.card-body {
padding: 16rpx;
}
.card-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.card-subtitle {
font-size: 22rpx;
color: #999;
flex: 1;
min-width: 0;
margin-right: 16rpx;
}
.info-row {
margin-bottom: 8rpx;
}
.card-meta {
font-size: 22rpx;
margin-bottom: 16rpx;
}
.meta-distance {
display: flex;
align-items: center;
color: #999;
flex-shrink: 0;
font-size: 22rpx;
}
.icon-location {
width: 20rpx;
height: 20rpx;
border: 2rpx solid #666;
border-radius: 50%;
margin-right: 4rpx;
position: relative;
}
.icon-location::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 6rpx;
height: 6rpx;
background: #666;
border-radius: 50%;
}
.meta-time {
display: block;
color: #333;
font-weight: 500;
}
.card-footer {
margin-top: 12rpx;
}
.card-btn {
width: 100%;
height: 64rpx;
border-radius: 32rpx;
font-size: 26rpx;
border: 1rpx solid #C52733;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
position: relative;
background: #C52733;
color: #fff;
}
.check-icon-wrapper {
position: absolute;
right: -8rpx;
top: -8rpx;
background: #fff;
border-radius: 50%;
width: 28rpx;
height: 28rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
/* 缺口在图标的左下角方向 (225deg左右设为透明) */
background: linear-gradient(#fff, #fff) padding-box,
conic-gradient(from 225deg, transparent 0deg 90deg, #C52733 90deg 360deg) border-box;
border: 2rpx solid transparent;
box-shadow: 0 0 0 2rpx #fff; /* 外层白边 */
}
/* 移除之前的伪元素遮挡方案 */
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 24rpx 32rpx 48rpx;
background: #fff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
z-index: 10;
}
.bottom-group {
gap: 24rpx;
}
.btn-outline {
flex: 1.2;
height: 88rpx;
border-radius: 44rpx;
border: 2rpx solid #333;
text-align: center;
font-size: 28rpx;
line-height: 84rpx;
color: #333;
background: #fff;
font-weight: bold;
font-style: italic;
}
.btn-primary {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
text-align: center;
font-size: 30rpx;
line-height: 88rpx;
color: #fff;
background: #C52733;
font-weight: bold;
font-style: italic;
}
.bottom-single .btn-primary {
width: 100%;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 0;
}
.empty-image {
width: 320rpx;
height: 320rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
}
</style>
@@ -0,0 +1,649 @@
<template>
<view class="coupon-order-detail">
<view class="status-banner" :class="{ 'completed': isCompleted || isExpired }">
<view v-if="isCompleted">
<view class="status-text v12-dark-text">已完成</view>
<view class="status-subtext v12-dark-text">期待再次光临</view>
</view>
<view v-else-if="isExpired">
<view class="status-text v12-dark-text">已过期</view>
<view class="status-subtext v12-dark-text">很遗憾你的体验券已过期</view>
</view>
<text v-else class="status-text">{{ statusText }}</text>
</view>
<view class="qrcode-card" v-if="!isCompleted && !isExpired">
<view class="merchant-header" v-if="orderInfo.merchantName" @click="goStoreDetail">
<image :src="webUrl + '/orderIcon/shop.png'" class="shop-icon" />
<text class="merchant-name">{{ orderInfo.merchantName }}</text>
<image :src="webUrl + '/orderIcon/arrow.png'" class="arrow-icon" />
</view>
<view class="project-name v12-font-34">
{{ orderInfo.experienceItemInfo.themeName || '' }}
</view>
<view class="qrcode-wrap" v-if="orderInfo.code">
<image :src="orderInfo.code" class="qrcode-image" mode="aspectFit" />
</view>
<view class="verify-code">
<text class="label">核销码</text>
<text class="value">{{ orderInfo.verifyCode || '-' }}</text>
<image :src="webUrl + '/orderIcon/copy.png'" class="copy-icon" @click="copyVerifyCode" v-if="orderInfo.verifyCode" />
</view>
<view v-if="orderInfo.validityPeriod" class="valid-time">
有效期 {{ orderInfo.validityPeriod || '-' }}
</view>
</view>
<view class="merchant-card">
<view class="merchant-card-top">
<image :src="orderInfo.experienceItemInfo.themeImage " class="merchant-avatar" mode="aspectFill" />
<view class="merchant-info">
<view class="merchant-name-large">{{ orderInfo.merchantName }}</view>
<view class="merchant-time" v-if="formattedBusinessTime">营业时间{{ formattedBusinessTime }}</view>
<view class="merchant-actions">
<view class="action-btn" @click="openLocation">
<u-icon :name="webUrl + '/page/qianxian/icon-addr.png'" color="#fff" size="24"></u-icon>
</view>
<view class="action-btn" @click="callMerchant">
<u-icon :name="webUrl + '/page/qianxian/icon-phone.png'" color="#fff" size="24"></u-icon>
</view>
</view>
<view class="merchant-location-wrap one-t v12-mb-2">
<image :src="webUrl + '/orderIcon/map.png'" class="location-icon" />
<text class="distance-text" v-if="distanceText">距该地{{ distanceText }}km</text>
<text class="address-text">{{ orderInfo.address }}</text>
</view>
</view>
</view>
</view>
<view class="detail-section">
<view class="section-title">订单详情</view>
<view class="info-row" v-if="isCompleted || isExpired">
<text class="label"> </text>
<text class="value">{{ orderInfo.experienceItemInfo && orderInfo.experienceItemInfo.themeName ? orderInfo.experienceItemInfo.themeName : '' }}</text>
</view>
<view class="info-row">
<text class="label">订单号</text>
<view class="value with-copy">
<text>{{ orderInfo.orderId || '' }}</text>
<image :src="webUrl + '/orderIcon/copy.png'" class="copy-icon" @click="copyOrderId" v-if="orderInfo.orderId" />
</view>
</view>
<view class="info-row">
<text class="label">订单类型</text>
<text class="value">{{ orderInfo.orderTypeDesc || '免费体验券' }}</text>
</view>
<view class="info-row" v-if="isCompleted">
<text class="label">核销时间</text>
<text class="value">{{ formatTime(orderInfo.verifyTime) || '-' }}</text>
</view>
<view class="info-row" v-if="isCompleted">
<text class="label">订单状态</text>
<text class="value">核销完成</text>
</view>
<view class="info-row" v-if="isExpired && orderInfo.endTime">
<text class="label">到期时间</text>
<text class="value">{{ orderInfo.endTime || '-' }}</text>
</view>
<view class="info-row" v-if="isExpired">
<text class="label">订单状态</text>
<text class="value">已过期</text>
</view>
</view>
<view class="notice-section" v-if="!isCompleted && !isExpired">
<view class="section-title">使用须知</view>
<view class="rich-text-content">
<u-parse :content="orderInfo.experienceItemInfo && orderInfo.experienceItemInfo.useNotice ? orderInfo.experienceItemInfo.useNotice : ''"></u-parse>
</view>
</view>
<view class="bottom-bar" v-if="isCompleted || isExpired">
<view class="home-btn" @click="goHome">
<u-icon name="home" color="#C52733" size="38rpx" class="home-icon"></u-icon>
<text>返回主页</text>
</view>
</view>
</view>
</template>
<script>
import { getExperienceCouponOrderDetail } from '@/api/product'
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
orderId: '',
orderInfo: {},
userLatitude: null,
userLongitude: null,
distanceText: '',
pollTimer: null
}
},
computed: {
statusText() {
const status = this.orderInfo.experienceOrderStatus
if (status === 0) {
return '待使用'
}
if (status === 1) {
return '已完成'
}
if (status === 2) {
return '已过期'
}
return ''
},
isCompleted() {
return this.orderInfo.experienceOrderStatus === 1
},
isExpired() {
return this.orderInfo.experienceOrderStatus === 2
},
formattedBusinessTime() {
return (this.orderInfo.serviceTime || '00:00') + '~' + (this.orderInfo.serviceEndTime || '23:59')
}
},
onLoad(options) {
if (options && options.orderId) {
this.orderId = options.orderId
this.fetchDetail()
}
this.getUserLocation()
},
onUnload() {
uni.setStorageSync('needRefreshMyOrderList', 1)
this.stopOrderPoll()
},
onHide() {
this.stopOrderPoll()
},
methods: {
fetchDetail() {
if (!this.orderId) {
return
}
getExperienceCouponOrderDetail(this.orderId).then(({ data }) => {
this.orderInfo = data || {}
this.updateDistance()
if (!this.isCompleted && !this.isExpired) {
this.startOrderPoll()
} else {
this.stopOrderPoll()
}
}).catch(() => {})
},
startOrderPoll() {
if (this.pollTimer || !this.orderId) {
return
}
this.pollTimer = setInterval(() => {
if (this.isCompleted || this.isExpired) {
this.stopOrderPoll()
return
}
this.fetchDetail()
}, 5000)
},
stopOrderPoll() {
if (this.pollTimer) {
clearInterval(this.pollTimer)
this.pollTimer = null
}
},
getUserLocation() {
uni.getLocation({
type: 'gcj02',
success: res => {
this.userLatitude = res.latitude
this.userLongitude = res.longitude
this.updateDistance()
}
})
},
formatTimestamp(timestamp) {
if (!timestamp) return ''
const date = new Date(Number(timestamp))
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
formatTime(timestamp) {
if (!timestamp) return ''
const date = new Date(Number(timestamp))
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}${month}${day}`
},
goHome() {
uni.switchTab({
url: '/pages/home/index'
})
},
updateDistance() {
if (
this.userLatitude == null ||
this.userLongitude == null ||
this.orderInfo.latitude == null ||
this.orderInfo.longitude == null
) {
return
}
const lat1 = Number(this.userLatitude)
const lon1 = Number(this.userLongitude)
const lat2 = Number(this.orderInfo.latitude)
const lon2 = Number(this.orderInfo.longitude)
const R = 6371
const dLat = (lat2 - lat1) * Math.PI / 180
const dLon = (lon2 - lon1) * Math.PI / 180
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
const d = R * c
this.distanceText = d.toFixed(1)
},
callMerchant() {
const phone = this.orderInfo.merchantPhone || this.orderInfo.merPhone || (this.orderInfo.experienceItemInfo && this.orderInfo.experienceItemInfo.phone) || this.orderInfo.companyTel;
if (phone) {
uni.makePhoneCall({
phoneNumber: phone
})
} else {
uni.showToast({ title: '暂无商家电话', icon: 'none' })
}
},
openLocation() {
if (this.orderInfo.latitude && this.orderInfo.longitude) {
uni.openLocation({
latitude: Number(this.orderInfo.latitude),
longitude: Number(this.orderInfo.longitude),
name: this.orderInfo.merchantName,
address: this.orderInfo.address
})
} else {
uni.showToast({ title: '暂无位置信息', icon: 'none' })
}
},
goStoreDetail() {
const merchantId = this.orderInfo.merId || this.orderInfo.merchantId || this.orderInfo.hotelId
if (!merchantId) {
uni.showToast({ title: '未找到店铺信息', icon: 'none' })
return
}
uni.navigateTo({
url: '/pkg_product/views/experienceProjectDetail?experienceMerchantId=' + encodeURIComponent(merchantId)
})
},
copyVerifyCode() {
if (!this.orderInfo.verifyCode) {
return
}
uni.setClipboardData({
data: this.orderInfo.verifyCode,
success: () => {
uni.showToast({
title: '已复制核销码',
icon: 'none'
})
}
})
},
copyOrderId() {
if (!this.orderInfo.orderId) {
return
}
uni.setClipboardData({
data: this.orderInfo.orderId,
success: () => {
uni.showToast({
title: '已复制订单号',
icon: 'none'
})
}
})
}
}
}
</script>
<style scoped lang="less">
.coupon-order-detail {
min-height: 100vh;
padding: 24rpx;
box-sizing: border-box;
background: #f5f5f5;
padding-bottom: 120rpx;
}
.status-banner {
background: #C52733;
border-radius: 16rpx;
padding: 24rpx 32rpx;
margin-bottom: 24rpx;
}
.status-banner.completed {
background: #fff;
padding: 32rpx;
}
.status-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.status-text.dark-text {
color: #333;
font-size: 36rpx;
margin-bottom: 8rpx;
}
.status-subtext {
font-size: 24rpx;
color: #666;
}
.qrcode-card {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.merchant-header {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 24rpx;
align-self: flex-start;
}
.shop-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
}
.merchant-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.arrow-icon {
width: 24rpx;
height: 24rpx;
margin-left: 8rpx;
}
.project-name {
font-size: 36rpx;
color: #333;
font-weight: bold;
margin-bottom: 24rpx;
text-align: center;
}
.qrcode-wrap {
width: 336rpx;
height: 336rpx;
margin-bottom: 24rpx;
border-radius: 20px;
border: 1px solid #B8B8B8;
padding: 20rpx;
box-sizing: border-box;
}
.qrcode-image {
width: 100%;
height: 100%;
}
.verify-code {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.verify-code .label {
font-size: 28rpx;
color: #666;
}
.verify-code .value {
font-size: 40rpx;
color: #333;
font-weight: bold;
margin: 0 16rpx;
}
.copy-icon {
width: 32rpx;
height: 32rpx;
}
.valid-time {
font-size: 24rpx;
color: #999;
}
.merchant-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
}
.merchant-card-top {
display: flex;
position: relative;
margin-bottom: 24rpx;
}
.merchant-avatar {
width: 220rpx;
height: 220rpx;
border-radius: 12rpx;
margin-right: 24rpx;
flex-shrink: 0;
}
.merchant-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
}
.merchant-name-large {
font-size: 32rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
}
.merchant-card-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.merchant-time {
font-size: 24rpx;
color: #666;
margin-bottom: 0;
line-height: 1.4;
word-break: break-all;
flex: 1;
padding-right: 24rpx;
}
.merchant-location-wrap {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
}
.location-icon {
width: 24rpx;
height: 24rpx;
margin-right: 4rpx;
flex-shrink: 0;
}
.distance-text {
margin-right: 12rpx;
flex-shrink: 0;
}
.address-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.merchant-actions {
display: flex;
flex-direction: row;
align-items: center;
flex-shrink: 0;
justify-content: flex-end;
margin: 20rpx 0;
}
.action-btn {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: #C52733;
display: flex;
align-items: center;
justify-content: center;
margin-left: 16rpx;
}
.detail-section {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
}
.section-title {
font-size: 32rpx;
color: #333;
font-weight: bold;
margin-bottom: 24rpx;
}
.info-row {
display: flex;
margin-bottom: 16rpx;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-row .label {
font-size: 28rpx;
color: #666;
}
.info-row .value {
font-size: 28rpx;
color: #333;
flex: 1;
}
.info-row .value.with-copy {
display: flex;
align-items: center;
}
.info-row .value.with-copy .copy-icon {
margin-left: 16rpx;
}
.notice-section {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
}
.notice-item {
display: flex;
align-items: flex-start;
margin-bottom: 12rpx;
}
.notice-item:last-child {
margin-bottom: 0;
}
.notice-item .dot {
color: #666;
margin-right: 8rpx;
font-size: 24rpx;
}
.notice-item .text {
color: #666;
font-size: 26rpx;
line-height: 1.5;
}
.rich-text-content {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
.bottom-bar {
position: fixed;
bottom: 40rpx;
left: 0;
right: 0;
display: flex;
justify-content: center;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.home-btn {
width: 686rpx;
height: 88rpx;
// background: #E8EBF0;
border: 2rpx solid #C52733;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #C52733;
font-weight: bold;
}
.home-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
}
</style>
@@ -0,0 +1,739 @@
<template>
<view class="experience-project-page">
<hx-navbar
:fixed="true"
:background-color="[0, [0, 0, 0, 0], [0, 0, 0, 0]]"
statusBarFontColor="#ffffff"
barPlaceholder="hidden"
transparent="auto"
color="#ffffff"
/>
<scroll-view scroll-y class="page-scroll">
<view class="hero-wrap">
<swiper v-if="bannerList.length > 0 || project.video" class="hero-img" circular :autoplay="swiperAutoplay" :interval="3000" @change="handleSwiperChange">
<swiper-item v-if="project.video">
<video :src="project.video" id="hero-video" class="hero-img" :autoplay="currentSwiperIndex === 0" object-fit="contain" :controls="false" :enable-progress-gesture="false" @play="onVideoPlay" @ended="onVideoEnd" @pause="onVideoEnd" @click="toggleVideo"></video>
</swiper-item>
<swiper-item v-for="(item, index) in bannerList" :key="index">
<image :src="item" class="hero-img" mode="heightFix|widthFix" />
</swiper-item>
</swiper>
<image
v-else-if="project.cover"
:src="project.cover"
class="hero-img"
mode="aspectFill"
/>
</view>
<view class="content">
<view class="store-card">
<view class="store-name">
{{ project.name }}
</view>
<view class="store-intro">
{{ project.intro }}
</view>
<view class="store-tags" style="display: flex; flex-wrap: wrap; align-items: center;">
<view style="display: flex; align-items: center; margin-right: 16rpx; border-radius: 44rpx;">
<view style="border-radius: 44rpx 0 0 44rpx;font-size: 24rpx; color: #fff; background-color: #C52733; height: 44rpx; display: flex; align-items: center; padding: 0 20rpx; position: relative; z-index: 1;">营业时间</view>
<view style="font-size: 24rpx; color: #333; background-color: #fff; border: 2rpx solid #C52733; border-radius: 44rpx; height: 44rpx; display: flex; align-items: center; padding: 0 20rpx 0 16rpx; box-sizing: border-box; margin-left: -20rpx; position: relative; z-index: 2;">
{{ project.serviceTime || '00:00' }}~{{ project.serviceEndTime || '23:59' }}
</view>
</view>
<view class="license-tag" @click="handleViewLicense">营业执照</view>
</view>
<view class="store-meta">
<view class="meta-left">
<!-- <u-icon
:name="webUrl + '/page/qianxian/icon-location.png'"
size="20"
color="#C52733"
/> -->
<view class="meta-info">
<text class="meta-text">
{{ project.cityName }}{{ project.address }}
</text>
</view>
<view v-if="distanceText" class="distance-row">
<u-icon :name="webUrl + '/orderIcon/map.png'" size="16"></u-icon>
<text class="distance-text">距该地{{ distanceText }}</text>
</view>
</view>
<view class="meta-actions">
<view class="action-item" @click="handleNavigate">
<view class="action-icon">
<u-icon :name="webUrl + '/page/qianxian/icon-addr.png'" color="#fff" size="24"></u-icon>
</view>
<text class="action-text">导航</text>
</view>
<view class="action-item" @click="handleContact">
<view class="action-icon">
<u-icon :name="webUrl + '/page/qianxian/icon-phone.png'" color="#fff" size="24"></u-icon>
</view>
<text class="action-text">客服</text>
</view>
</view>
</view>
</view>
<view class="experience-badge-wrap">
<view class="experience-badge">
<view class="badge-diamond">
<view class="badge-icon">
<u-icon :name="webUrl + '/icon/zuanshi.png'" color="#F9D24D" size="16"></u-icon>
</view>
</view>
<view class="badge-bg">
<text class="badge-text">超值体验</text>
</view>
</view>
</view>
<view class="section">
<!-- <view class="section-title">体验项目</view> -->
<swiper
v-if="projectList.length > 0"
class="experience-swiper"
:style="{ height: experienceSwiperHeight ? (experienceSwiperHeight + 'px') : '520rpx' }"
circular
:autoplay="true"
:interval="3000"
:indicator-dots="false"
indicator-color="rgba(255, 255, 255, .4)"
indicator-active-color="#ffffff"
@change="handleExperienceSwiperChange"
>
<swiper-item v-for="(item, index) in projectList" :key="index">
<view :id="'experience-card-' + index" class="experience-card">
<image
v-if="item.projectImage"
:src="item.projectImage"
class="experience-image"
mode="widthFix|heightFix"
@load="onExperienceImageLoad"
/>
<view class="experience-info">
<view class="experience-title-wrap">
<view class="experience-title">
{{ item.themeName || item.projectName }}
</view>
</view>
<view class="experience-subtitle">
{{ item.projectIntro || item.detail }}
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<view class="section" v-if="project.detailText || project.remark">
<view class="section-title" v-if="project.detailText">详情</view>
<view class="detail-list">
<view class="v12-font-26" style="color:#666">
<u-parse :content="project.detailText"></u-parse>
</view>
</view>
<template v-if="project.remark">
<view class="section-title" style="margin-top: 24rpx;">备注</view>
<view class="section-text with-dot">
<text class="dot"></text>
<text>{{ project.remark }}</text>
</view>
</template>
</view>
<view class="section" v-if="usageNotice">
<view class="section-title">使用须知</view>
<view class="section-content">
<u-parse :content="usageNotice"></u-parse>
</view>
</view>
<view class="bottom-space" />
</view>
</scroll-view>
</view>
</template>
<script>
import { getExperienceStoreDetail } from '@/api/qianxian'
export default {
data() {
const webUrl = this.$VUE_APP_RESOURCES_URL
return {
webUrl,
project: {
id: '',
name: '',
cover: '',
intro: '',
serviceTime: '',
serviceEndTime: '',
cityName: '',
address: '',
latitude: '',
longitude: '',
phone: '',
wechat: '',
video: '',
banner: '',
experienceTitle: '',
experienceSubTitle: '',
detailText: '',
remark: '',
qualification: ''
},
detailList: [],
experienceMerchantId: '',
totalPrice: 0,
validFrom: '',
validTo: '',
usageNotice: '',
currentSwiperIndex: 0,
videoContext: null,
swiperAutoplay: true,
isVideoPlaying: false,
projectList: [],
userLatitude: null,
userLongitude: null,
distanceText: '',
currentExperienceIndex: 0,
experienceSwiperHeight: 0
}
},
computed: {
bannerList() {
if (!this.project.banner) return []
return this.project.banner.split(',').filter(item => item)
}
},
onLoad(query) {
const decode = v => decodeURIComponent(v || '')
const id = query.experienceMerchantId || query.id || ''
this.experienceMerchantId = id
this.project.id = id
this.project.name = decode(query.name)
this.project.cover = decode(query.cover)
this.project.video = decode(query.video)
this.project.banner = decode(query.banner)
this.project.intro = decode(query.intro)
this.project.serviceTime = query.serviceTime || ''
this.project.serviceEndTime = query.serviceEndTime || ''
this.project.cityName = decode(query.cityName)
this.project.address = decode(query.address)
this.project.latitude = query.latitude || ''
this.project.longitude = query.longitude || ''
this.project.experienceTitle = decode(query.experienceTitle || '')
this.project.experienceSubTitle = decode(query.experienceSubTitle || '')
this.project.detailText = decode(query.detailText || '')
this.project.remark = decode(query.remark || '')
this.validFrom = query.validFrom || ''
this.validTo = query.validTo || ''
this.usageNotice = decode(query.usageNotice || '')
this.getUserLocation()
if (query.detailList) {
try {
const list = JSON.parse(decode(query.detailList))
if (Array.isArray(list)) {
this.detailList = list.map(item => ({
name: item.name || '',
count: item.count || ''
}))
}
} catch (e) {}
}
this.fetchProjectDetail()
},
onReady() {
this.videoContext = uni.createVideoContext('hero-video', this)
},
methods: {
handleSwiperChange(e) {
this.currentSwiperIndex = e.detail.current
if (this.project.video && this.videoContext) {
if (this.currentSwiperIndex === 0) {
this.videoContext.play()
} else {
this.videoContext.pause()
}
}
},
onVideoPlay() {
this.swiperAutoplay = false
this.isVideoPlaying = true
},
onVideoEnd() {
this.swiperAutoplay = true
this.isVideoPlaying = false
},
toggleVideo() {
if (!this.videoContext) return
if (this.isVideoPlaying) {
this.videoContext.pause()
this.swiperAutoplay = true
} else {
this.videoContext.play()
}
},
handleExperienceSwiperChange(e) {
this.currentExperienceIndex = e.detail.current || 0
this.updateExperienceSwiperHeight(this.currentExperienceIndex)
},
onExperienceImageLoad() {
this.updateExperienceSwiperHeight(this.currentExperienceIndex)
},
updateExperienceSwiperHeight(index) {
const targetIndex = Number.isInteger(index) ? index : this.currentExperienceIndex
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this)
query.select('#experience-card-' + targetIndex).boundingClientRect(rect => {
if (rect && rect.height) {
this.experienceSwiperHeight = rect.height
}
}).exec()
})
},
fetchProjectDetail() {
if (!this.experienceMerchantId) {
return
}
uni.showLoading({
title: '加载中',
mask: true
})
getExperienceStoreDetail(this.experienceMerchantId).then(res => {
if (res && res.status === 200) {
const data = res.data || {}
this.project.name = data.name || this.project.name
this.project.cover = data.cover || this.project.cover
this.project.video = data.video || this.project.video
this.project.banner = data.banner || this.project.banner
this.project.intro = data.intro || this.project.intro
this.project.serviceTime = data.serviceTime || this.project.serviceTime
this.project.serviceEndTime = data.serviceEndTime || this.project.serviceEndTime
this.project.cityName = data.cityName || this.project.cityName
this.project.address = data.address || this.project.address
this.project.latitude = data.latitude || this.project.latitude
this.project.longitude = data.longitude || this.project.longitude
this.updateDistance()
this.project.phone = data.phone || this.project.phone
this.project.wechat = data.wechat || this.project.wechat
this.project.qualification = data.qualification || this.project.qualification
const project = data.project || {}
this.project.experienceTitle = project.themeName || this.project.experienceTitle || this.project.name
if (project.themeImage) {
this.project.cover = project.themeImage
}
this.project.detailText = project.detail || this.project.detailText
this.project.remark = project.remark || this.project.remark
this.usageNotice = project.useNotice || this.usageNotice
console.log('this.usageNotice', this.usageNotice)
if (project.totalPrice != null) {
this.totalPrice = project.totalPrice
}
if (data.totalPrice != null && this.totalPrice === 0) {
this.totalPrice = data.totalPrice
}
if (project.validityStartTime) {
this.validFrom = project.validityStartTime
}
if (project.validityEndTime) {
this.validTo = project.validityEndTime
}
if (project.projectList && project.projectList.length) {
this.projectList = project.projectList
this.currentExperienceIndex = 0
this.detailList = project.projectList.map(item => ({
name: item.projectName || '',
count: item.projectIntro || ''
}))
this.updateExperienceSwiperHeight(0)
setTimeout(() => {
this.updateExperienceSwiperHeight(0)
}, 200)
}
}
}).finally(() => {
uni.hideLoading()
})
},
getUserLocation() {
uni.getLocation({
type: 'gcj02',
success: res => {
this.userLatitude = Number(res.latitude)
this.userLongitude = Number(res.longitude)
this.updateDistance()
}
})
},
calculateDistance(lat1, lon1, lat2, lon2) {
if (!lat1 || !lon1 || !lat2 || !lon2) {
return ''
}
const R = 6371
const dLat = (lat2 - lat1) * Math.PI / 180
const dLon = (lon2 - lon1) * Math.PI / 180
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
const d = R * c
return d.toFixed(1) + 'km'
},
updateDistance() {
const storeLatitude = Number(this.project.latitude)
const storeLongitude = Number(this.project.longitude)
if (
this.userLatitude == null ||
this.userLongitude == null ||
!storeLatitude ||
!storeLongitude
) {
this.distanceText = ''
return
}
this.distanceText = this.calculateDistance(
this.userLatitude,
this.userLongitude,
storeLatitude,
storeLongitude
)
},
handleNavigate() {
if (!this.project.latitude || !this.project.longitude) {
uni.showToast({
title: '暂无位置信息',
icon: 'none'
})
return
}
uni.openLocation({
latitude: Number(this.project.latitude),
longitude: Number(this.project.longitude),
name: this.project.name,
address: this.project.address
})
},
handleContact() {
if (!this.project.phone) {
uni.showToast({
title: '暂无联系方式',
icon: 'none'
})
return
}
uni.makePhoneCall({
phoneNumber: this.project.phone
})
},
handleViewLicense() {
if (this.project.qualification) {
uni.navigateTo({
url: `/pkg_product/views/storeLicense?name=${encodeURIComponent(this.project.name)}&images=${encodeURIComponent(this.project.qualification)}`
})
} else {
uni.showToast({
title: '暂无营业执照',
icon: 'none'
})
}
}
}
}
</script>
<style scoped lang="less">
.section-content {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
.experience-project-page {
min-height: 100vh;
background: #f5f5f5;
}
.page-scroll {
// height: calc(100vh - 120rpx);
}
.hero-wrap {
position: relative;
width: 100%;
height: 700rpx;
overflow: hidden;
}
.hero-img {
width: 100%;
height: 100%;
}
.content {
margin-top: -100rpx;
padding: 0 24rpx 40rpx;
box-sizing: border-box;
position: relative;
z-index: 2;
}
.store-card {
padding: 32rpx 24rpx;
border-radius: 24rpx;
background: #ffffff;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
}
.store-name {
color: #333;
font-size: 36rpx;
font-weight: bold;
}
.store-intro {
margin-top: 16rpx;
color: #666;
font-size: 26rpx;
line-height: 1.5;
}
.store-tags {
display: flex;
align-items: center;
margin-top: 24rpx;
gap: 16rpx;
}
.time-tag {
display: flex;
align-items: center;
border: 1rpx solid #C52733;
border-radius: 24rpx;
overflow: hidden;
height: 40rpx;
}
.time-tag-label {
background: #C52733;
color: #ffffff;
font-size: 22rpx;
padding: 0 16rpx;
height: 100%;
line-height: 40rpx;
}
.time-tag-value {
color: #333;
font-size: 22rpx;
padding: 0 16rpx;
height: 100%;
line-height: 40rpx;
background: #ffffff;
}
.license-tag {
border: 1rpx solid #C52733;
color: #C52733;
font-size: 22rpx;
padding: 0 16rpx;
border-radius: 24rpx;
height: 40rpx;
line-height: 40rpx;
}
.store-meta {
margin-top: 32rpx;
padding-top: 32rpx;
// border-top: 1rpx solid #f0f0f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.meta-left {
flex: 1;
// display: flex;
align-items: center;
padding-right: 32rpx;
}
.meta-info {
display: flex;
flex-direction: column;
row-gap: 8rpx;
}
.distance-row {
margin-left: 4rpx;
display: flex;
align-items: center;
column-gap: 8rpx;
}
.distance-text {
font-size: 22rpx;
color: #666;
margin-top: 8rpx;
}
.meta-text {
margin-left: 12rpx;
font-size: 26rpx;
color: #333;
font-weight: 500;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.meta-actions {
display: flex;
align-items: center;
gap: 32rpx;
flex-shrink: 0;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
}
.action-icon {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: #C52733;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
.action-text {
font-size: 22rpx;
color: #333;
}
.section-experience {
margin-top: 40rpx;
position: relative;
}
.experience-badge-wrap {
z-index: 10;
margin-top: 40rpx;
margin-left: 20rpx;
}
.experience-badge {
display: flex;
align-items: center;
position: relative;
height: 44rpx;
}
.badge-diamond {
width: 44rpx;
height: 44rpx;
background: #ffffff;
border: 4rpx solid #C52733;
transform: rotate(45deg);
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
box-sizing: border-box;
margin-left: -14rpx;
}
.badge-icon {
transform: rotate(-45deg);
display: flex;
align-items: center;
justify-content: center;
}
.badge-bg {
background: #C52733;
border-radius: 0 24rpx 24rpx 0;
height: 100%;
display: flex;
align-items: center;
padding: 0 20rpx 0 24rpx;
margin-left: -16rpx;
}
.badge-text {
color: #ffffff;
font-size: 24rpx;
font-weight: bold;
font-style: italic;
line-height: 1;
}
.experience-card {
border-radius: 24rpx;
background: #C52733;
overflow: hidden;
box-shadow: 0 8rpx 24rpx rgba(197, 39, 51, 0.15);
margin-bottom: 24rpx;
}
.experience-swiper {
margin-top: 16rpx;
width: 100%;
}
.experience-image {
width: 100%;
height: 360rpx;
border-radius: 24rpx;
display: block;
}
.experience-info {
padding: 24rpx;
}
.experience-title-wrap {
display: inline-block;
background: #ffffff;
border-radius: 40rpx;
padding: 8rpx 32rpx;
margin-bottom: 16rpx;
}
.experience-title {
color: #C52733;
font-size: 32rpx;
font-weight: bold;
}
.experience-subtitle {
color: #ffffff;
font-size: 26rpx;
opacity: 0.9;
}
.section {
margin-top: 24rpx;
padding: 32rpx 24rpx;
border-radius: 24rpx;
background: #ffffff;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 24rpx;
}
.detail-list {
background: transparent;
}
.detail-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
font-size: 26rpx;
color: #666;
margin-bottom: 16rpx;
}
.detail-name {
flex: 1;
display: flex;
align-items: flex-start;
line-height: 1.5;
}
.detail-count {
margin-left: 24rpx;
color: #999;
}
.dot {
display: inline-block;
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #999;
margin-top: 14rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
.section-text {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
.with-dot {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
}
.bottom-space {
height: 40rpx;
}
</style>
+4
View File
@@ -213,6 +213,10 @@ export default {
},
computed: mapGetters(['userInfo', 'isLogin']),
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return {
title: '探寻有趣有味的生活方式',
path: '/pkg_product/views/famousList?partnerId=' + this.userInfo.uid,
@@ -66,6 +66,10 @@ export default {
},
computed: mapGetters(['userInfo', 'isLogin']),
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return new Promise(async (resolve, reject) => {
const {status, data} = await famousShareImage();
if (status === 200) {
+428 -70
View File
@@ -64,11 +64,11 @@
<view class="zan-box flex flex-0 ai-end" @click="zanInn">
<template v-if="isLike">
<image :src="webUrl + '/icon/icon-like.png'" class="img" />
<text class="on">{{ inn.zan }}</text>
<text class="on">{{ $formatCount(inn.zan) }}</text>
</template>
<template v-else>
<image :src="webUrl + '/icon/icon-like.png'" class="img" />
<text>{{ inn.zan }}</text>
<text>{{ $formatCount(inn.zan) }}</text>
</template>
</view>
<view class="flex flex-0 ai-end favorite-box" @click="doneFavorite">
@@ -84,7 +84,7 @@
mode="scaleToFill"
class="icon"
/>
<text>{{ favoriteCount }}</text>
<text>{{ $formatCount(favoriteCount) }}</text>
</view>
</view>
</view>
@@ -148,6 +148,21 @@
v-if="activeIndex === 3"
class="info-section"
>
<view class="news-category">
<view scroll-x class="news-category-scroll" show-scrollbar="false">
<view class="news-category-wrapper">
<view
v-for="(item, index) in newsCategoryList"
:key="index"
class="category-item v12-mt-2"
:class="{ active: newsCurrent === index }"
@click="changeNewsTab(index)"
>
{{ item.name }}
</view>
</view>
</view>
</view>
<view>
<view v-if="infoArray.length > 0" class="info-wrapper">
<view class="info-list">
@@ -192,6 +207,24 @@
:id="'video-item' + index"
:poster="item.video + '?vframe/jpg/offset/1'"></video>
</view>
<view
v-if="item.linkHotelId"
class="info-shop-entry"
@click.stop="handleInfoHotelClick(item)"
>
<view class="info-shop-logo">
<image :src="item.linkHotelLogo" mode="aspectFill" />
</view>
<view class="info-shop-main">
<view class="info-shop-name">
{{ item.linkHotelName || '' }}
</view>
<view class="info-shop-btn">
<text style="margin-right: 6rpx;">点击进入</text>
<u-icon name="arrow-rightward" size="14" color="#fff"></u-icon>
</view>
</view>
</view>
<view class="info-item-bottom">
<view class="time">
{{ item.createTime ? item.createTime.replace(/-/g, '.').substr(0, 10) : '' }}
@@ -202,7 +235,7 @@
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt">{{ item.pv }}</text>
<text class="seeNum-txt">{{ $formatCount(item.pv) }}</text>
</view>
<view class="flex">
<image
@@ -221,8 +254,84 @@
v-show="item.zan > 0"
:class="item.hasZan ? 'on' : ''"
class="seeNum-txt"
>{{ item.zan }}</text>
>{{ $formatCount(item.zan) }}</text>
</view>
<view class="flex" @click="infoClick(item)">
<u-icon name="chat" size="24"></u-icon>
<text class="seeNum-txt">{{ $formatCount(item.replyCount) || 0}}</text>
</view>
</view>
</view>
<view class="comment-list v12-px-3" v-if="item.latestReplyList" @click="infoClick(item)">
<view class="v12-mt-2" v-for="(comment, i) in item.latestReplyList" :key="i">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar :src="comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ comment.userNickName }}</text>
<text v-if="comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(comment.createTime) }}</text>
<text v-if="comment.at">
{{ ' 回复 ' + comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-d-flex" >
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
{{ comment.reply }}
</view>
<view v-if="comment.children">
<view class="v12-mt-2" v-for="(_comment, atIndex) in comment.children" :key="atIndex">
<view class="user-info v12-align-center">
<view class="user-avatar">
<u-avatar size="35" :src="_comment.userAvatar"></u-avatar>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-bold v12-dark-text v12-font-32">
<text>{{ _comment.userNickName }}</text>
<text v-if="_comment.userIsAdmin" class="v12-ml-1 v12-primary v12-white-text v12-font-24 v12-font-weight-400 v12-px-1 v12-radius-8">
{{ '店主' }}
</text>
</view>
<view class="v12-font-22 v12-secondary-dark-text v12-mt-1">
<text>{{ formatterTime(_comment.createTime) }}</text>
<text v-if="_comment.at && _comment.parentReplyId !== comment.id">
{{ ' 回复 ' + _comment.at.atNickName }}
</text>
</view>
</view>
</view>
<view class="user-info v12-align-center">
<view class="user-avatar">
<view style="opacity: 0;">
<u-avatar size="35"></u-avatar>
</view>
</view>
<view class="user-name v12-ml-3">
<view class="v12-font-28 v12-dark-text">
{{ _comment.reply }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
@@ -374,54 +483,24 @@
</view>
</view>
</view>
<view class="project-wrap">
<view class="title">
<image
:src="webUrl + '/page/qianxian/icon-01.png'"
class="icon"
/>
产业项目
</view>
<view v-if="industryProjectList.length > 0" class="list">
<view
v-for="(item, index) in industryProjectList"
:key="index"
class="item"
>
<image
:src="item.image"
class="img"
@click="gotoInvestment(item.title, 'project', item.id)"
/>
</view>
</view>
<view v-else class="no-data">暂无产业项目</view>
</view>
</view>
<view v-if="industryTabIndex === 1">
<view class="card-wrap">
<!-- <image
:src="webUrl + '/page/qianxian/card-bg2.png'"
class="bg"
/> -->
<view class="card-wrap v12-mt-3">
<view class="card-cont">
<view class="tab-wrap">
<view
v-for="(tab, tabIndex) in industryTab"
:key="tabIndex"
:class="industryTabIndex === tabIndex ? 'tab-item1' : ''"
class="item"
@click="industryTabItemClick(tabIndex)"
>
<!-- <image
:src="webUrl + tab.icon"
class="icon"
/> -->
{{ tab.name }}
</view>
</view>
<view class="file-wrap" :class="[{'v12-align-center': investmentFileList.length === 0}, {'v12-justify-center': investmentFileList.length === 0}]">
<view class="file-wrap v12-radius-20" :class="[{'v12-align-center': investmentFileList.length === 0}, {'v12-justify-center': investmentFileList.length === 0}]">
<view v-if="investmentFileList.length > 0" class="file-list">
<view class="title v12-justify-between">
<view class="title">
<image
:src="webUrl + '/page/qianxian/icon-02.png'"
class="icon"
/>
招商政策
</view>
<view v-if="investmentFileList.length > 0" >
<view @click="gotoInvestment('', 'investment')">
更多政策>
</view>
</view>
</view>
<view class="list-cont">
<view
v-for="(file, fileIndex) in investmentFileList"
@@ -443,18 +522,109 @@
</view>
</view>
</view>
<view v-if="investmentFileList.length > 0" class="more-btn-wrap">
<view class="more-btn" @click="gotoInvestment('', 'investment')">
更多政策
<!-- <view class="project-wrap">
<view class="title">
<image
:src="webUrl + '/page/qianxian/icon-01.png'"
class="icon"
/>
产业项目
</view>
</view>
<view v-if="industryProjectList.length > 0" class="list">
<view
v-for="(item, index) in industryProjectList"
:key="index"
class="item"
>
<image
:src="item.image"
class="img"
@click="gotoInvestment(item.title, 'project', item.id)"
/>
</view>
</view>
<view v-else class="no-data">暂无产业项目</view>
</view> -->
</view>
<view v-if="industryTabIndex === 2">
<view v-if="industryTabIndex === 1">
<view class="card-wrap">
<!-- <image
:src="webUrl + '/page/qianxian/card-bg3.png'"
class="bg bg3"
/> -->
<view class="card-cont">
<view class="tab-wrap">
<view
v-for="(tab, tabIndex) in industryTab"
:key="tabIndex"
:class="industryTabIndex === tabIndex ? 'tab-item0' : ''"
class="item"
@click="industryTabItemClick(tabIndex)"
>
<!-- <image
:src="webUrl + tab.icon"
class="icon"
/> -->
{{ tab.name }}
</view>
</view>
<view class="village-wrap v12-pt-3">
<view class="v12-pa-2 sale-wrap">
<view
v-for="(type, typeIndex) in shopTypeList"
:key="typeIndex"
:class="{ saleActive: shopTypeActive === type.id }"
@click="handleShopType(type)"
>
{{ type.name }}
</view>
</view>
<view v-if="shopList.length > 0" class="village-list">
<view
v-for="(shop, shopIndex) in shopList"
:key="shopIndex"
class="village-item v12-white"
@click="hotelItemClick(shop)"
>
<view class="img-wrap">
<image
:src="shop.cover + (shop.coverType === 2 ? '?vframe/jpg/offset/1' : '')"
class="img"
/>
<view class="location">
<image
:src="webUrl + '/page/qianxian/icon-08.png'"
class="icon"
/>
<view class="addr one-t">
{{ shop.address }}
</view>
</view>
</view>
<view class="info">
<view class="intro more-t">
{{ shop.content }}
</view>
<view class="name-wrap">
<image
:src="shop.logo"
class="logo"
/>
<view class="name one-t">
{{ shop.name }}
</view>
</view>
</view>
</view>
</view>
<view v-else class="no-data">暂无店铺</view>
</view>
</view>
</view>
</view>
<view v-if="industryTabIndex === 2">
<view class="card-wrap">
<view class="card-cont">
<view class="tab-wrap">
<view
@@ -499,11 +669,11 @@
</view>
<view class="info">
<view class="intro more-t">
{{ jiaLou.shopIntro }}
{{ jiaLou.intro || '' }}
</view>
<view class="name-wrap">
<image
:src="jiaLou.logo"
:src="jiaLou.avatar"
class="logo"
/>
<view class="name one-t">
@@ -711,6 +881,7 @@
@ok="handleOk"
/>
<FunctionGuide @hide="setGuide('countyFamousIndex')" :maxStep="2" :guideData="functionGuideData" ref="FunctionGuide"></FunctionGuide>
<AiEntrance />
</view>
</template>
@@ -732,6 +903,9 @@ import {
getShareImg,
getSaleList,
getJiaLouList,
getCityNewsCategory,
getCityShopList,
getCountyFamousHotelType
} from '@/api/qianxian'
import ProductWindow from '@/components/ProductWindow'
import goCartMixin from '@/mixins/goCartMixins'
@@ -743,12 +917,14 @@ import cookie from '@/utils/store/cookie'
import { pageListenMixins } from '@/mixins/pageListenMixins'
import FunctionGuide from '@/components/FunctionGuide'
import GuideMixins from '@/mixins/GuideMixins'
import AiEntrance from '@/components/aiChat/entrance'
export default {
components: {
imgBox,
XddProductItem,
ProductWindow,
FunctionGuide
FunctionGuide,
AiEntrance
},
mixins: [pageListenMixins, goCartMixin, GuideMixins],
data() {
@@ -805,7 +981,7 @@ export default {
villageList: [],
industryTab: [
{ name: '项目', icon: '/page/qianxian/icon-04.png' },
{ name: '招商', icon: '/page/qianxian/icon-03.png' },
{ name: '店铺', icon: '/page/qianxian/icon-03.png' },
{ name: '旅居', icon: '/page/qianxian/icon-03.png' },
{ name: '租售', icon: '/page/qianxian/icon-07.png' }
],
@@ -815,6 +991,23 @@ export default {
rightList: [],
videoPlayers:[],
jiaLouList: [],
countyId: null,
newsCategoryList: [],
newsCurrent: 0,
shopList: [],
shopTypeList: [],
shopTypeActive: '',
}
},
onShareAppMessage() {
uni.updateShareMenu({
isPrivateMessage: false,
withShareTicket: false
})
return {
title: this.inn.countyName || this.share.title,
path: '/pkg_product/views/famousQianxianShop?id=' + this.id,
imageUrl: this.inn.cover || this.share.imageUrl
}
},
// onPageScroll(e){}
@@ -824,6 +1017,7 @@ export default {
onLoad(options) {
const id = options.id || ''
this.id = id
if (id) {
this.partnerId = options.partnerId || null
} else {
@@ -832,6 +1026,7 @@ export default {
const query = options ? decodeURIComponent(options.scene) : decodeURIComponent(obj.query.scene)
this.partnerId = getUrlParam(query, 'partnerId') || null
this.id = getUrlParam(query, 'id') || null
}
}
if (this.partnerId) cookie.set('spread', this.partnerId)
@@ -842,8 +1037,46 @@ export default {
this.getListReq()
this.getsaleList()
this.queryGuide('countyFamousIndex')
this.getCityNewsCategoryReq()
},
methods: {
/**
* 获取县城资讯类别
*/
getCityNewsCategoryReq() {
getCityNewsCategory().then(res => {
if(res.status === 200) {
this.newsCategoryList = [{name: '全部', id: ''}].concat(res.data || [])
}
})
},
changeNewsTab(index) {
if (this.newsCurrent === index) return
this.newsCurrent = index
this.getInfoReq()
},
formatterTime(time) {
const now = new Date();
const date = new Date(time);
const diff = now - date;
const diffMin = Math.floor(diff / 1000 / 60);
const diffHour = Math.floor(diff / 1000 / 60 / 60);
const diffDay = Math.floor(diff / 1000 / 60 / 60 / 24);
if (diffMin < 1) {
return "刚刚";
} else if (diffHour < 1) {
return diffMin + "分钟前";
} else if (diffDay < 1) {
return diffHour + "小时前";
} else if (diffDay < 7) {
return diffDay + "天前";
} else {
return date.getFullYear() + "年" + (date.getMonth() + 1) + "月" + date.getDate() + "日";
}
},
infoClick(info) {
this.$yrouter.push('/pkg_product/views/newsInfo?id=' + info.id)
},
showFunctionGuide() {
if(this._step == this.functionGuideData.step) return
if(this.functionGuideData.step == 1) {
@@ -1074,6 +1307,7 @@ export default {
getCountyFamousCityDetail(this.id).then(res => {
const { success, data } = res
if (success) {
this.countyId = data.countyId
const products = data.products || []
const farmerShops = data.farmerShops || []
this.leftList = this.newSort(products, farmerShops)
@@ -1163,10 +1397,12 @@ export default {
_this.isLoadInfoListSuccess = false
_this.loading = true
_this.infoArray = []
const categoryId = this.newsCategoryList[this.newsCurrent] ? this.newsCategoryList[this.newsCurrent].id : ''
getCountyFamousCityNews({
page: 1,
limit: 9999,
countyFamousDataId: _this.id
countyFamousDataId: _this.id,
categoryId: categoryId
}).then(res => {
const { data } = res
_this.loading = false
@@ -1327,9 +1563,6 @@ export default {
})
})
},
infoClick(info) {
// this.$yrouter.push('/pagesInn/inn/innInfoDetail?id=' + info.id)
},
coverPlay(){
this.videoPlayers.forEach(player => {
const playerCtx = uni.createVideoContext(player, this)
@@ -1424,30 +1657,75 @@ export default {
}
})
},
handleInfoHotelClick(item) {
if (!item || !item.linkHotelId) return
const { linkType, linkHotelId } = item
if (linkType === 'travel') {
uni.navigateTo({ url: `/pkg_product/views/sojoumStore?id=${linkHotelId}` })
return
}
this.hotelItemClick({ id: linkHotelId })
},
guideItemClickHandle(item) {
//
if (!item.isContentPage) return
uni.navigateTo({ url: `/pkg_product/views/famousQianxianTheme?id=${item.id}` })
},
sujoumeClickHandle(item) {
console.log(item);
uni.navigateTo({ url: '/pkg_product/views/sojoumStore?id=' + item.id })
if(item.dataType === 'countyFamousSojourn') {
uni.navigateTo({ url: '/pkg_product/views/oldSojumStore?id=' + item.id })
return
}
uni.navigateTo({ url: '/pkg_product/views/sojoumStore?id=' + item.id })
},
industryTabItemClick(index) {
if (index === this.industryTabIndex) return
this.industryTabIndex = index
if(index === 2) {
if (index === 2) {
const params = {
countyFamousDataId: this.id,
countyId: this.countyId,
shopType: 'countyFamous',
name: '',
page: 1,
limit: 9999
}
console.log(params);
getJiaLouList(params).then(res => {
this.jiaLouList = res.data.records || []
})
}
if (index === 1) {
this.shopTypeActive = ''
if (this.shopTypeList.length === 0) {
this.getShopTypeList()
}
this.getShopList()
}
},
getShopTypeList() {
getCountyFamousHotelType().then(res => {
const listData = Array.isArray(res.data)
? res.data
: (res.data && (res.data.hotelTypeList || res.data.list || res.data.records)) || []
this.shopTypeList = [{ name: '全部', id: '' }].concat(listData || [])
})
},
getShopList() {
const params = {
countyFamousDataId: this.id,
page: 1,
limit: 9999
}
if (this.shopTypeActive) {
params.countyFamousHotelType = this.shopTypeActive
}
getCityShopList(params).then(res => {
this.shopList = (res.data && (res.data.records || res.data.list)) || []
})
},
handleShopType(type) {
if (this.shopTypeActive === type.id) return
this.shopTypeActive = type.id
this.getShopList()
},
fileItemClick(file) {
const url = file.attachment || ''
@@ -1468,6 +1746,14 @@ export default {
gotoInvestment(title = '', type = 'investment', id = '') {
uni.navigateTo({ url: `/pkg_product/views/famousQianxianInvestment?id=${id || this.id}&title=${title}&type=${type}` })
},
hotelItemClick(item) {
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.id
}
})
},
villageItemClick(item) {
uni.navigateTo({ url: `/pkg_product/views/famousQianxianVillageShop?id=${item.id}` })
}
@@ -1851,6 +2137,31 @@ export default {
padding: 22rpx;
}
.info-section {
.news-category {
padding-bottom: 20rpx;
.news-category-scroll {
white-space: nowrap;
width: 100%;
}
.news-category-wrapper {
overflow: scroll;
display: flex;
justify-content: space-between;
}
.category-item {
display: inline-block;
padding: 10rpx 30rpx;
margin-right: 20rpx;
border-radius: 16rpx;
font-size: 28rpx;
font-weight: bold;
color: #333;
&.active {
color: #fff;
background: #C52733
}
}
}
.info-item + .info-item {
margin: 22rpx 0 0 0;
}
@@ -1888,6 +2199,53 @@ export default {
}
}
}
.info-shop-entry {
margin: 0 20rpx 16rpx 20rpx;
padding: 16rpx 20rpx;
border-radius: 12rpx;
background-color: #fafafa;
display: flex;
align-items: center;
column-gap: 16rpx;
}
.info-shop-logo {
width: 88rpx;
height: 88rpx;
border-radius: 12rpx;
overflow: hidden;
flex-shrink: 0;
}
.info-shop-logo image {
width: 100%;
height: 100%;
}
.info-shop-main {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 16rpx;
}
.info-shop-name {
flex: 1;
font-size: 28rpx;
color: #333;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 300rpx;
}
.info-shop-btn {
padding: 8rpx 16rpx 8rpx 24rpx;
border-radius: 999rpx;
background-color: #C52733;
color: #fff;
font-size: 24rpx;
flex-shrink: 0;
display: flex;
align-items: center;
}
.info-item-bottom {
display: flex;
align-items: center;

Some files were not shown because too many files have changed in this diff Show More