Compare commits

...

257 Commits

Author SHA1 Message Date
a2616c9757 chore: 修改路由base路径为/hwappx/common/ 2026-05-09 09:17:38 +08:00
fb47da8095 refactor(privacy-popup): 优化隐私弹窗逻辑结构 2026-05-09 08:58:48 +08:00
37958cbe39 feat(privacy): 移除隐私弹窗配置检查逻辑 2026-05-09 08:47:24 +08:00
9f53f147ee chore: 移除所有调试日志 2026-05-08 16:09:19 +08:00
2342a14f2e fix(mp-weixin): 将 import 语句改为 require 2026-05-07 17:36:43 +08:00
36b0c22e47 fix: 修复二维码长按识别属性 2026-05-07 16:38:53 +08:00
4a52b9c229 fix(util): 去除URL中的双斜杠 2026-05-07 16:25:37 +08:00
9ec90a906f perf(diy-tab): optimize tab rendering with v-if 2026-05-07 16:14:28 +08:00
e125a5f39c chore: 添加修复长按菜单语法错误的脚本 2026-05-07 15:43:57 +08:00
8c0ec46dd8 style: 统一图片组件属性格式 2026-05-07 15:43:33 +08:00
b65ec35d3c chore: 更新 appid 2026-05-07 15:13:06 +08:00
d113d6125b feat(components): 在图片组件中添加长按显示菜单功能 2026-05-07 15:12:59 +08:00
2f24661cd9 fix(ai-service): 统一用户标识默认值为anonymous 2026-05-06 11:26:10 +08:00
50908a274b refactor(ai-service): 使用getUniacid方法统一获取uniacid 2026-05-06 11:13:08 +08:00
9e68562de5 chore(ai): 增加调试日志输出 2026-05-06 10:53:52 +08:00
8e5561c2a7 refactor(ai-service): 提取域名配置参数以减少重复代码 2026-04-30 16:39:55 +08:00
8d5c543a4f chore:扩大按钮【】 2026-04-28 17:51:54 +08:00
cf411407dd chore:名片可以分享了 2026-04-28 17:35:40 +08:00
86e4be302b chore:商品分类可以分享了 2026-04-28 11:31:32 +08:00
2fd06796e1 chore:加了输入框扩大按钮 2026-04-28 09:59:52 +08:00
60c51e551a chore:修改表情位置 2026-04-28 09:12:15 +08:00
d0d3ac47c6 chore:修改表情位置 2026-04-28 08:59:34 +08:00
af677f6565 chore:资源详情改为商品详情 2026-04-25 16:53:40 +08:00
f889efeb16 chore: 2026-04-25 16:45:41 +08:00
d026bb6180 chore:增加一个后台控制首页弹出的隐私权限 2026-04-25 16:15:17 +08:00
82b856f5d9 chore:表情的布局改为横板 2026-04-25 14:36:31 +08:00
7f33b6948a chore:min-height: 300px; 替换成 min-height: 0px; 2026-04-25 14:26:48 +08:00
3489b89a0b chore:智能客服加了表情 2026-04-25 14:15:03 +08:00
dc4f2769f9 chore:资源详情里的立即咨询可以跳转到智能客服 2026-04-25 11:58:03 +08:00
5cf87e4d56 chore:资源详情的背景设改为绿色 2026-04-25 11:27:32 +08:00
c9ca63cff5 chore:联系我们改为立即质询 2026-04-25 11:09:47 +08:00
a6072f4895 chore: 2026-04-25 10:22:27 +08:00
38947ddc62 chore:立即咨询点击会弹出对话框 2026-04-25 10:09:38 +08:00
cfe83ada38 chore:点击商品分类中的立即咨询不要跳转到智能客服,跳转到资源详情页面 2026-04-25 09:59:45 +08:00
1e9e0ffea8 chore:资源详情智能客服按钮同步智能客服 2026-04-25 09:50:45 +08:00
28acdfb621 chore:红色背景收一些 2026-04-25 09:29:23 +08:00
ad9f1c495b chore:联系我们改为立即质询 2026-04-25 09:22:33 +08:00
7c2a7468e7 chore:调整红色背景 2026-04-25 09:03:13 +08:00
b2051dcf92 chore:立即质询背景改红色 2026-04-25 08:48:06 +08:00
e3c86de84a chore:删除红色的立即质询 2026-04-25 08:42:03 +08:00
6e0eb5d4f3 chore:询底价改为立即咨询 2026-04-25 08:38:15 +08:00
9d186ff094 chore:发送字体按钮调整变大 2026-04-24 16:49:01 +08:00
8611ec4dc3 chore:发送按钮改为微信的发送按钮 2026-04-24 16:38:29 +08:00
89bfd94a07 chore:上传image-config.js 2026-04-24 15:46:42 +08:00
5cf8b7b467 chore:发送按钮调整变大 2026-04-23 15:24:32 +08:00
2cc0162435 chore:输入框不被遮挡了 2026-04-22 11:59:58 +08:00
d8e75a0c9e chore: 2026-04-21 14:15:46 +08:00
8af3c0ccb9 chore:微信小程序真机版本可以运行,微信小程序客服图标显示正常 2026-04-20 17:50:37 +08:00
d8b78168a8 chore:改了头像的地址 2026-04-20 15:44:58 +08:00
7675e8e04d chore: 2026-04-20 09:52:27 +08:00
88d05048a6 chore:长按图片可以识别图中二维码 2026-04-20 09:36:08 +08:00
28a92c0635 chore:ai-service.js 的代码url: 'wss://dev.aigc-quickapp.com/ws/aikefu',被替换 2026-04-18 11:36:18 +08:00
b5a47b8b4e chore:聊天页面的我和时间换位置 2026-04-17 16:03:30 +08:00
4f99e8b5f2 chore:H5的发送按钮变成正圆形 2026-04-17 15:18:31 +08:00
87fc49146c chore:时间居中 2026-04-17 14:19:43 +08:00
826fcfb024 chore:时间和智能助手在同一水平线上 2026-04-17 11:49:33 +08:00
7174d87b79 chore:用户的气泡中商品名称,商品ID,价格都左对齐 2026-04-17 10:13:07 +08:00
4bc747cd9c chore:增加了用户默认头像,自定义头像 2026-04-17 09:48:46 +08:00
e046868665 chore:用户点击自己头像可以自定义头像 2026-04-17 09:21:10 +08:00
19c3ec4f9f chore:小程序输入框向下移动 2026-04-17 08:44:01 +08:00
ffbc15929d chore:智能客服的历史时间回复正常 2026-04-16 14:03:37 +08:00
524280d816 chore:智能助手加了时间 2026-04-16 11:00:50 +08:00
6e736e0733 chore:资源详情页面的智能客服头像变为圆形 2026-04-16 10:02:50 +08:00
4891566793 chore:智能客服改了头像 2026-04-16 09:34:56 +08:00
b02f5fbc21 chore:智能客服换了头像 2026-04-15 17:46:06 +08:00
4f5a300afa chore:H5的智能客服聊天页面上面导航栏自定义的删除 2026-04-15 17:18:22 +08:00
aae6e76797 chore:智能客服聊天不乱窜了 2026-04-15 10:23:43 +08:00
3162d6a9f2 chore:H5的智能客服聊天正常 2026-04-15 09:08:10 +08:00
8010ac45a6 chore:智能客服图标换了 2026-04-14 17:29:17 +08:00
baebba4cef chore:导航栏删除在线客服 2026-04-14 16:30:42 +08:00
1be7155fe8 chore:智能客服的头像换了 2026-04-14 09:42:09 +08:00
611c0b930b chore:删除喜欢不喜欢有帮助没帮助 2026-04-13 17:29:13 +08:00
ec1903519d chore:用户提问和客服回答可以看见了 2026-04-13 09:30:08 +08:00
b1953a020d chore:喜欢不喜欢和👍水平 2026-04-10 15:10:41 +08:00
3ce26322e0 chore: 2026-04-09 16:04:11 +08:00
588ec60948 chore:发送按钮向上调动 2026-04-08 10:10:47 +08:00
962ea124fa chore:退出聊天界面后,在进入聊天页面后没有多余的气泡 2026-04-08 09:14:57 +08:00
0ca0fe3b0d chore:修改了客服样式 2026-04-07 15:21:29 +08:00
cef2b6e02e chore:客服回答不重复 2026-04-07 15:10:26 +08:00
e018f9a8b9 chore:修改H5商品价格样式 2026-03-31 09:38:16 +08:00
def8a6cf6f chore:横竖屏 2026-03-30 14:49:24 +08:00
857f295923 chore:H5和小程序都可以自己分辨横竖屏 2026-03-28 17:38:26 +08:00
50ee885b74 chore:微信小程序实现竖屏是竖屏,横屏是横屏 2026-03-25 14:44:25 +08:00
410f248510 chore:H5视频改比例 2026-03-25 09:39:48 +08:00
ba5fcfaab1 chore:微信小程序的视频改为竖屏播放 2026-03-24 16:43:00 +08:00
5a46bb8d69 chore:智能客服回答不重复 2026-03-19 14:26:32 +08:00
e33ad16d10 chore:商品详情页面右上角有跳转到智能客服 2026-03-17 09:53:24 +08:00
d8ba7ace0f chore:跳转智能客服的更多详情按钮放到商品页面里面 2026-03-16 17:21:33 +08:00
875072bb68 chore:商品可以分享给智能客服进行解答 2026-03-14 10:53:55 +08:00
0912b9b031 chore:智能客服加了商品的图标 2026-03-13 16:41:00 +08:00
7ffe4acc0f chore:隐藏了更多功能,录音图标 2026-03-13 13:57:24 +08:00
4bcf0cbd2d chore:可以正常清空聊天记录 2026-03-12 18:29:11 +08:00
fdf85dfec7 chore:更换头像正常 2026-03-12 18:25:12 +08:00
1d62b37060 chore:头像功能从相册选择功能正常了 2026-03-12 15:09:27 +08:00
4ad93ceace refactor: 将视频标题样式绑定从对象改为数组 2026-02-07 18:11:42 +08:00
294856d9ef Merge branch 'test/diy-tabs' into dev/1.0 2026-02-07 17:53:30 +08:00
ba2c7d6d64 Revert "chore:点击中英文按钮不跳到‘我的’了"
This reverts commit 19a53ef414.
2026-02-07 17:51:10 +08:00
ec9e76fda7 chore:改了中英文模式 2026-02-07 16:53:40 +08:00
5dba8071e4 refactor(diy-tab): 统一样式绑定语法为数组形式
将模板中的样式绑定从对象展开语法改为数组形式,保持代码风格一致
2026-02-07 16:16:43 +08:00
19a53ef414 chore:点击中英文按钮不跳到‘我的’了 2026-02-07 16:10:55 +08:00
e943b11c8f fix(组件): 修复tab组件中子组件moduleIndex冲突问题
为tab组件中的子组件添加tabIndex前缀,避免不同tab页中子组件的moduleIndex冲突
2026-02-07 15:57:49 +08:00
e9db54980e refactor(diy-tab): 使用模板字符串和可选链优化key生成逻辑 2026-02-07 14:59:26 +08:00
3ddcc665b8 refactor(diy-tab): 统一使用生成函数获取tab和panel的key
使用getTabKey和getPanelKey方法统一生成key,提高代码可维护性
2026-02-07 14:11:51 +08:00
a981c02baf chore:智能客服页面右上角改成齿轮图标,更多功能图标改为(...) 2026-02-07 13:59:43 +08:00
1c3f759b35 feat: 为DIY标签组件添加可配置的动画效果 2026-02-07 11:41:59 +08:00
bb58866192 feat: 为标签组件添加悬停动画和对齐选项 2026-02-07 11:34:45 +08:00
069b3c2c07 feat: 为DIY标签页组件添加滚动容器和样式 2026-02-06 17:55:44 +08:00
c6fd13956a chore:H5和微信小程序的第三方客服悬浮按钮应该会显示 👤 2026-02-06 17:49:48 +08:00
a489e24988 chore:改了企业微信客服,智能客服的悬浮按钮 2026-02-06 17:18:58 +08:00
6897fc631b feat: 重构DIY标签页组件,支持多种布局和样式配置 2026-02-06 16:47:32 +08:00
6d23a64aef chore:智能客服的页面全部显示出来 2026-02-06 16:30:33 +08:00
f34533b0c7 chore:进入智能客服的页面加了智能客服的头像 2026-02-06 16:08:50 +08:00
72c74f44f0 chore:删除弹出的框的一个叉 2026-02-06 16:02:11 +08:00
7f3d9c0f10 chore:将更改头像和昵称的按钮放在...的设置中 2026-02-06 15:56:00 +08:00
37f95e7267 chore:改了客服的图标 2026-02-05 18:30:16 +08:00
d3c630e860 chore:后台点击什么,页面显示什么 2026-02-05 16:58:54 +08:00
c5b57cedeb chore:后台点击不启用,H5和微信小程序不显示 2026-02-05 16:34:33 +08:00
bf24746a7c chore;更改了客服和中英文按钮的顺序 2026-02-05 10:14:27 +08:00
5ff2925ba1 chore: 2026-02-05 09:56:37 +08:00
3dc81e7f53 chore:优化了hover 2026-02-05 09:50:36 +08:00
0417c5c5ed chore:优化了hover 2026-02-05 09:27:43 +08:00
db26a85c8b chore:商品,名片,我的都能正常跳转了 2026-02-04 17:09:57 +08:00
54c39259a7 chore:代码合并,整理功能 2026-02-02 18:04:59 +08:00
15361211b1 Merge pull request 'tjh/merge' (#3) from tjh/merge into dev/1.0
Reviewed-on: #3
2026-02-02 09:52:19 +00:00
e301ddc6ec chore:微信小程序标签栏点击可以跳转 2026-01-31 17:04:50 +08:00
a560ad4a5d chore:后台点击什么页面就显示什么 2026-01-31 10:24:12 +08:00
7f7a18803f chore:智能客服正常运行 2026-01-31 10:06:59 +08:00
42563d7184 chore:改了pages-tool/ai-chat/ai-chat-message.vue 2026-01-31 09:47:41 +08:00
14a903f42a chore:改了components/chat-message/chat-message.vue 2026-01-31 09:44:05 +08:00
c9f8614927 chore:改了common/js/lang.js 2026-01-31 09:23:36 +08:00
fe9303bf4c chore:改了store/index.js 2026-01-31 09:20:31 +08:00
e4dfd0ae11 chore:改了project.config.json 2026-01-31 09:17:57 +08:00
7037d0b083 chore:改了pages-tool/ai-chat/index.vue 2026-01-31 09:14:00 +08:00
e8a79bd245 chore:改了golbalconfig.js 2026-01-31 09:11:00 +08:00
047cf765da chore:改了pages.json 2026-01-31 09:07:48 +08:00
3a30b0817d chore:替换了pages/index/index.vue 2026-01-31 09:03:58 +08:00
a464a8731f chore:替换了customer-service.js 2026-01-31 08:59:30 +08:00
7599a80943 chore:替换了ai-service.js 2026-01-31 08:55:57 +08:00
992a01cc76 chore:替了chat-message.vue 2026-01-31 08:52:55 +08:00
dd09c66215 chore:替了hover 2026-01-31 08:48:18 +08:00
e2b89c348f refactor(diy-tab): 优化标签组件结构和默认配置
- 使用 tab.id 作为 v-for 的 key 提高渲染性能
- 移除默认标签数据,改为空数组
- 添加 activeTabIndex 配置项
- 调整代码格式和注释
2026-01-30 17:41:23 +08:00
475edc93a6 refactor(diy-tab): 将默认配置拆分为多个独立对象以提高可维护性
将原本单一的defaults对象拆分为多个独立的配置对象(tabsConfig、baseConfig等),并通过展开运算符合并。这样修改使代码结构更清晰,便于后续维护和扩展。
2026-01-27 14:48:40 +08:00
cb86cba389 feat(components): 新增可自定义的diy-tab组件,支持多种样式和布局
添加一个高度可配置的tab组件,支持顶部、底部、左侧、右侧四种布局方式
提供默认、下划线和卡片三种tab样式,支持自定义颜色、间距、指示器等样式
组件包含导航栏和内容区域,支持动态切换和动画效果
- 添加自定义样式配置功能,允许通过 customStyles 完全覆盖组件样式
- 重构代码结构,使用计算属性合并默认值和传入值
- 优化样式处理逻辑,增加 mixin 复用
- 完善注释和文档说明
- 改进响应式动画效果
- 新增 getTabTitle 方法,支持根据当前语言环境显示对应的标签标题。该方法处理对象形式的标题(按语言键值匹配)和字符串形式的标题(支持国际化键翻译),提升组件的多语言适配能力。
2026-01-27 14:36:42 +08:00
0dc4dec616 refactor(diy-components): 统一使用@tap.stop替换@click事件处理
将组件中的@click事件统一替换为@tap.stop,避免在小程序环境中可能出现的点击事件冒泡问题,提升交互体验的一致性。同时移除冗余的@tap事件绑定,保持代码简洁。

主要修改包括:
- 表单提交按钮
- 分享功能
- 热区点击
- 富文本点击
- 视频播放
- 直播入口
- 商品品牌
- 客服功能
- 快捷导航
- 公告弹窗
- 文章列表
- 底部导航
- 商品列表
- 浮动按钮
- 优惠券
- 搜索功能
- 店铺相关
- 文本组件
- 分类页面
- 魔方组件
- 秒杀功能
- 拼团功能
- 预售功能
- 砍价功能
- 分销商品
- 图片广告
- 支付二维码
- 图片导航
- 音频控制
- 地图导航
- 笔记功能
- 商户列表
- 图片组件
- 会员订单
- 图文导航
- 首页分类
- 商品推荐
- 团购功能
- 限时折扣
- 商品分类
- 商品列表
- 优惠券
- 搜索功能
- 店铺相关
- 文本组件
- 分类页面
- 魔方组件
- 秒杀功能
- 拼团功能
- 预售功能
- 砍价功能
- 分销商品
- 图片广告
- 支付二维码
- 图片导航
- 音频控制
- 地图导航
- 笔记功能
- 商户列表
- 图片组件
- 会员订单
- 图文导航
- 首页分类
- 商品推荐
- 团购功能
- 限时折扣
- 商品分类
2026-01-26 15:05:36 +08:00
2a5214df11 feat(图片预览): 将图片预览功能提取到mixin并优化点击处理
重构图片预览功能,将其从diy-picture组件移至mixin实现,便于复用
优化diy-rubik-cube组件的点击处理逻辑,支持无链接时预览图片
统一处理图片预览和页面跳转的条件判断
2026-01-26 14:07:45 +08:00
09e410df00 docs: 更新readme文档结构和内容
重构readme文档结构,使用更清晰的markdown格式和emoji图标
添加详细的开发调试说明和发布流程
补充构建脚本说明和版本历史记录
2026-01-24 17:40:40 +08:00
Zhukj
75dfe80bde Merge remote-tracking branch 'remotes/origin/dev/1.0' into zhu-kaijiie/dev-1.0 2026-01-24 16:59:15 +08:00
Zhukj
2a1f33323c chore:修改readme.md小程序和快应用发布说明文档 2026-01-24 16:51:31 +08:00
Zhukj
b441c46993 chore:更新小程序发布说明文档 2026-01-24 11:19:53 +08:00
38ade75046 fix(diy-map): 替换cover-view为div解决z-index失效问题
移除ns-login组件中不必要的z-index设置
2026-01-24 09:20:01 +08:00
1e51abd7cd docs: 更新本地配置示例文件添加IVD数商模式配置
添加2812环境的IVD数商模式配置,并增加本地开发测试平台选项
2026-01-23 17:09:42 +08:00
0939449aa7 fix(ns-login): 修复complete-info-wrap弹窗层级问题 2026-01-23 17:09:05 +08:00
29b5cfda6f fix: 在页面加载时统一隐藏首页按钮
在多个页面的onLoad生命周期中添加this.$util.hideHomeButton()调用,确保页面加载时即隐藏首页按钮,避免显示不一致问题。同时调整category.vue中onShow的逻辑顺序。
2026-01-23 11:54:31 +08:00
ceca4e5956 fix: 在多个页面添加隐藏首页按钮功能
为统一处理首页按钮显示逻辑,在会员页、联系页和商品分类页的onShow生命周期中添加了hideHomeButton调用
2026-01-23 11:36:15 +08:00
aa9d2e64d2 feat(util): 添加隐藏微信小程序首页按钮功能
添加 hideHomeButton 方法用于在微信小程序环境中隐藏首页按钮,提升用户体验
2026-01-23 11:18:55 +08:00
e8ccb87266 feat(脚本): 增强微信小程序补丁脚本功能
- 添加新的 npm scripts 支持不同模式运行补丁脚本
- 支持命令行参数 --no-zip 和 --mode 控制 ZIP 文件生成和运行模式
- 自动复制项目配置文件到目标目录
- 更新使用说明文档
2026-01-23 10:12:30 +08:00
153c84266a chore: 更新项目配置文件和微信小程序名称
- 将项目名称从'lucky_shop'改为'mp-weixin'
- 在project.config.json中添加include数组和多个新配置项
- 启用enhance功能并添加编译相关设置
2026-01-23 09:25:58 +08:00
7ae7a1d3bd chore(uniapp): 增加拷贝文本超时检测提示功能 2026-01-17 15:30:02 +08:00
1ebb94e9e2 chore: 调整隐私协议及注册服务展示样式 2026-01-17 13:52:19 +08:00
9b53540f91 Merge branch 'dev/1.0' of http://git.aigc-quickapp.com/Uniapp/lucky_shop into dev/1.0 2026-01-16 18:11:00 +08:00
2a36fa2b60 chore(build): 调整默认小程序AppID及site.js文件 2026-01-16 18:07:59 +08:00
2223636184 Merge branch 'dev/1.0' of http://git.aigc-quickapp.com/Uniapp/lucky_shop into dev/1.0 2026-01-16 18:00:31 +08:00
6144dc72b8 chore:非 h5 平台 :key 不支持表达式的问题 2026-01-16 18:00:03 +08:00
47e1c2372d chore(sass): 编译器从node-sass转移成dart-sass 2026-01-16 17:52:33 +08:00
5b9bef9214 chore(build): 调整个别组件的位置,减少对主包尺寸的影响 2026-01-16 15:34:04 +08:00
08880a15df fix(电子名片): 再使用视频号组件的时候,严格转换数据类型 2026-01-16 10:47:52 +08:00
e40e6e73b2 chore(build): 调整ai-chat-message组件的位置,减少对主包尺寸的影响 2026-01-16 10:34:00 +08:00
dd4176998b Merge branch 'feat/personnel_channel' into dev/1.0 2026-01-16 10:16:40 +08:00
2896817435 chore: 删除不需要的页面及demo组件 2026-01-16 08:49:46 +08:00
dc956761a0 Merge branch 'feat/personnel_channel' into dev/1.0 2026-01-15 18:14:50 +08:00
36fd0621fd fix(视频号组件): padding 影响布局 2026-01-15 18:05:30 +08:00
1e6cd55f0a chore:中英文切换按钮能切换英文 2026-01-15 15:10:46 +08:00
f7fcf7fb27 chore:点击英文按钮,页面不是空白了 2026-01-15 15:01:39 +08:00
28359f2f16 fix(视频号组件): 整体控制是否显示观看次数没有生效 2026-01-15 14:58:36 +08:00
b3cc86d088 chore: .local.config.js.example 2026-01-15 14:37:39 +08:00
c6a8bc04f2 chore:解决了siteInfo,bgUrl错误 2026-01-15 14:08:32 +08:00
0ed0789528 chore(视频号组件): 支持轮播模式 2026-01-15 11:40:34 +08:00
16d1a3a945 feat(电子名片): 支持通过后台控制显示样式及封面尺寸 2026-01-15 09:44:54 +08:00
82df9ddbac chore:加了中英文切换按钮 2026-01-14 18:24:42 +08:00
392a8e2952 revert(视频号组件): 精简代码,调整样式以和后台设计统一 2026-01-14 16:37:44 +08:00
db7a8d7202 chore(视频号组件): 与后端设计器同步样式布局 2026-01-14 13:46:47 +08:00
bb87f4b1d2 fix(视频号组件): 用户取消的监听错误,不弹出错误提示 2026-01-14 09:41:41 +08:00
fe039ed126 revert(视频号组件): 精简代码,去除不用的组件 2026-01-14 09:31:22 +08:00
03c1a8d71a fix(视频号组件): 修复视频标题全是数字字符串不会自动换行的问题 2026-01-14 08:56:25 +08:00
e8fb35b310 chore:智能体客服正常运行 2026-01-13 18:26:11 +08:00
286fcc2142 chore:合并了之前智能客服的代码 2026-01-13 16:42:06 +08:00
2280ff68fa Merge branch 'feat-ai-agent-kefu' into xindeznkf
chore:合并了提交
2026-01-13 15:57:35 +08:00
9e0cab7ec4 chore(视频号组件): 针对微信视频号资源,点击播放的时候吧,弹出不能播放的原因 2026-01-13 14:51:09 +08:00
a31c004508 chore(视频号组件): 去除不需要的属性 2026-01-13 14:47:46 +08:00
ee8c24cdf8 chore(util): 格式化util.js 2026-01-13 14:39:22 +08:00
b043601a77 chore(视频号组件): 不仅支持16:9的封面,还支持3:4的 2026-01-13 14:26:10 +08:00
e20968aee2 chore(电子名片): 支持点击视频号视频,非嵌入模式可以播放了 2026-01-13 11:27:59 +08:00
8ceeca33b4 chore(manifest.json): "lazyCodeLoading": "requiredComponents", 2026-01-13 08:33:24 +08:00
02e9cfa313 chore(电子名片): 支持点击视频号视频,观看次数+1 2026-01-12 17:46:15 +08:00
3f894bcb20 chore(电子名片): 针对上传的封面及头像,转换图片路径 2026-01-12 16:25:02 +08:00
8d5d6e3da8 chore(视频号组件): 是否支持嵌入式播放,由使用者决定 2026-01-12 16:24:14 +08:00
ce1ea6eadf chore(电子名片): 更新企业文件及企业视频相关代码 2026-01-12 16:11:53 +08:00
191c803f66 chore(组件): 更新ns-video-player-popup 组件 2026-01-12 16:05:25 +08:00
4e1448d2c4 chore(js): 新增common/js/share.js,专注分享 2026-01-12 15:35:23 +08:00
ce26a9be5f feat(页面): 新增文件预览页面 2026-01-12 15:28:04 +08:00
8b01d8f8ec chore(视频号组件): 非微信小程序环境,发送消息,让父组件决定来怎么操作 2026-01-12 15:03:28 +08:00
6ab2834fdc feat(组件): 新增ns-video-player-popup 组件 2026-01-12 15:02:28 +08:00
85816e416c chore(视频号组件): 支持rowCount=1 的布局 2026-01-12 13:50:26 +08:00
bf8e177c30 chore(视频号组件): 支持2-3-4列布局 2026-01-12 13:47:13 +08:00
57111a00c2 fix(视频号组件): 让原生channel-video 组件样式与普通模式组件模式保持一致 2026-01-12 11:56:18 +08:00
aa7c7cbe60 chore(电子名片): 添加企业文件及企业视频部分模板及样式代码 2026-01-12 11:33:10 +08:00
d012c7fff7 chore(视频号组件): 优化视频号组件展示及播放 2026-01-12 11:32:31 +08:00
128d75b921 chore(视频号组件): 增加对auto-play 的控制 2026-01-12 08:57:07 +08:00
7e0d61c7e5 chore: 根据后端API设置相关模块的显示 2026-01-10 18:05:54 +08:00
26af083839 chore(电子名片): 支持展示视频号 2026-01-10 17:38:55 +08:00
68bc853dff chore(视频号组件): 更新文档及注释内容 2026-01-10 17:38:13 +08:00
13166132c7 chore(视频号组件): 更新视频号样式,控制标题行数,播放按钮等样式 2026-01-10 17:03:41 +08:00
f87c7d963e chore(视频号组件): 更新视频号样式 2026-01-10 16:12:36 +08:00
6f6899deaa chore(视频号组件): 重构视频号组件,不单单是微信视频号 2026-01-10 15:14:44 +08:00
7f54666629 chore(util): 增加蛇形命名转驼峰命名(递归处理对象中的所有属性名) 2026-01-10 13:44:50 +08:00
5747b256c6 chore:改了webd的基础路径为common 2026-01-09 17:54:18 +08:00
09b285016c chore:修改了快应用发行文档 2026-01-09 17:34:06 +08:00
72c12a79fd chore:修改了快应用的发行文档 2026-01-09 17:18:43 +08:00
84f4ad83c2 chore:增加了快应用打包步骤 2026-01-09 15:45:19 +08:00
1512c60109 chore:修改快应用发布 2026-01-09 11:52:29 +08:00
5b41dee46c Merge remote-tracking branch 'remotes/origin/dev/1.0' into dev/1.0 2026-01-09 11:34:19 +08:00
9ed337b59a chore(build): 增加对老服务器xcx20.5g-quickapp.com 网站的支持 2026-01-09 11:17:10 +08:00
8fedeafe63 chore:修改了小程序打包步骤 2026-01-09 10:41:45 +08:00
31336f9a80 chore:增加了快应用的打包过程 2026-01-09 10:33:50 +08:00
2f7c9bf0af Merge remote-tracking branch 'remotes/origin/dev/1.0' into dev/1.0 2026-01-08 16:50:24 +08:00
f8257361b5 chore:新加了快应用发行的文档 2026-01-08 16:08:00 +08:00
ec05534ff1 Merge branch 'dev/1.0' of https://git.aigc-quickapp.com/Uniapp/lucky_shop into dev/1.0 2026-01-08 15:36:57 +08:00
4f9c353e9a chore(用户中心): 账号退出或者注销,清理用户隐私协议授权设置 2026-01-08 14:58:47 +08:00
52b5f5b006 chore:重新合并了dev 2026-01-08 14:58:10 +08:00
5a8663eb48 fix: 解决在快应用环境中不显示用户隐私提示窗体的问题 2026-01-08 14:52:01 +08:00
8108802204 chore:手动和dev合并 2026-01-08 11:30:35 +08:00
6fe3bceb4b chore:手动合并和dev的代码 2026-01-08 11:18:58 +08:00
ee4fbb277c test(隐私): 测试 2026-01-08 09:39:15 +08:00
6718010ceb chore: common/js/util.js 增加判断是快应用的函数 2026-01-08 09:04:36 +08:00
faf1f6d3d8 chore: 增加判断是快应用环境的API函数 2026-01-07 18:28:51 +08:00
2d98a32efc fix(微信视频号组件): 宏导致的H5环境编译出错 2026-01-07 17:43:23 +08:00
7fde30063b chore(微信视频号): 优化channel-video组件的显示,更新技术文档 2026-01-07 17:27:42 +08:00
10465b7cf2 chore(微信视频号): 添加MP-WEIXIN 宏限制channel-video 2026-01-07 17:17:24 +08:00
c7e9b497ae chore(微信视频号): 优化微信视频号的配置及显示 2026-01-07 17:12:38 +08:00
de63cd4c6a feat(微信视频号): 增加微信视频号和微信视频号列表组件 2026-01-07 16:42:28 +08:00
2f230d3b2c chore: 使用常量来处理登录的路由页面路径 2026-01-07 15:51:24 +08:00
6f01b3c1c6 chore: 使用常量来处理首页的路由页面路径 2026-01-07 15:40:13 +08:00
b10fcf02d4 chore: 使用常量来处理会员中心的路由页面路径判断 2026-01-07 15:09:49 +08:00
6956841c8b chore(会员中心): 购物车、足迹、关注,优化副标题 2026-01-07 14:24:02 +08:00
90820ade30 fix(路由): 修复订单列表页面,验证登录页面,而没办法后退 2026-01-07 14:02:03 +08:00
8df59abd03 fix(关注/足迹): 优化交互逻辑 2026-01-07 11:35:28 +08:00
f3bb6a1782 chore:智能体客服和原来的客服都在 2025-12-17 15:43:37 +08:00
2a0489d4b2 chore:能正常运行 2025-12-17 11:17:50 +08:00
ca74d4f8e5 chore: 要明确集成微信及支付宝原生客服 2025-12-17 09:19:19 +08:00
d9c9599cb2 chore: 集成统一客服服务 2025-12-16 16:54:39 +08:00
142a97d65c revert: pages/contact/contact.vue 回退到最初状态 2025-12-16 15:52:18 +08:00
b945583857 chore: 优化公共端都支持打开企业微信客服的方法 2025-12-16 15:41:15 +08:00
88debacf8c chore: 使用全局的wxworkConfig配置作为参数 2025-12-16 14:41:45 +08:00
9d12b02463 chore: 将wxworkConfig放到全局配置中 2025-12-16 14:41:15 +08:00
23e81114e9 chore: 后端init接口数据为wxwork_config 2025-12-16 10:54:57 +08:00
08583aa8aa chore: 企业微信客服组件完全独立 2025-12-15 15:04:45 +08:00
5e536afeae feat: 新增打开企业微信客服组件 2025-12-15 14:26:01 +08:00
280 changed files with 13516 additions and 4180 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
/.hbuilderx
/.idea
/node_modules
/iconfont-preview.html

View File

@@ -38,6 +38,22 @@ const localDevConfig = ({
uniacid: 1,
domain: 'https://test.aigc-quickapp.com',
},
'local-2': { // 测试平台
uniacid: 2,
domain: 'http://localhost:8050/',
},
'100': {
uniacid: 100,
domain: 'https://5g.aigc-quickapp.com/',
},
'1': {
uniacid: 1,
domain: 'https://5g.aigc-quickapp.com/',
},
'115': {
uniacid: 115,
domain: 'https://5g.aigc-quickapp.com/',
},
})['2811']; // 选择要使用的环境配置
export default localDevConfig;

View File

@@ -18,6 +18,10 @@ const localDevConfig = ({
uniacid: 2811,
domain: 'https://xcx6.aigc-quickapp.com/',
},
'2812': { // IVD数商模式
uniacid: 2812,
domain: 'https://xcx6.aigc-quickapp.com/',
},
'2724': { // 生物菌肥
uniacid: 2724,
domain: 'https://xcx.aigc-quickapp.com/',
@@ -38,6 +42,14 @@ const localDevConfig = ({
uniacid: 1,
domain: 'https://test.aigc-quickapp.com',
},
})['2811']; // 选择要使用的环境配置
'local-2': { // 测试平台
uniacid: 2,
domain: 'http://localhost:8050/',
},
'local-2-dev': { // 本地开发测试平台
uniacid: 2,
domain: 'http://localhost:8050/',
},
})['2812']; // 选择要使用的环境配置
export default localDevConfig;

View File

@@ -266,7 +266,6 @@
imgUrl: this.$util.img(share_data.imgUrl)
},
res => {
console.log(res);
}
);
}
@@ -314,7 +313,7 @@
@import './common/css/iconfont.css';
@import './common/css/icondiy.css'; // 自定义图标库
@import './common/css/icon/extend.css'; // 扩展图标库
page{
page {
background: #f4f6fa;
}
</style>

View File

@@ -123,7 +123,7 @@ image {
}
.choose-store {
/deep/ .uni-popup__wrapper{
::v-deep .uni-popup__wrapper{
background: none!important;
}
}

View File

@@ -870,13 +870,13 @@
// 海报
// .uni-popup__wrapper-box
.poster-layer {
/deep/ .uni-popup__wrapper.center {
::v-deep .uni-popup__wrapper.center {
width: 100vw!important;
height: 100vh!important;
background: none!important;
}
/deep/ .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
max-width: 100vw!important;
max-height: 100vh!important;
background: none!important;

View File

@@ -559,7 +559,7 @@ scroll-view ::-webkit-scrollbar {
background-color: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
@@ -609,7 +609,7 @@ scroll-view ::-webkit-scrollbar {
font-weight: 500!important;
}
/deep/ .reward-popup .uni-popup__wrapper-box {
::v-deep .reward-popup .uni-popup__wrapper-box {
background: none !important;
max-width: unset !important;
max-height: unset !important;
@@ -618,7 +618,7 @@ scroll-view ::-webkit-scrollbar {
// #ifdef H5
// 下拉加载动画【页面】
/deep/ body uni-page-refresh div{
::v-deep body uni-page-refresh div{
width: 14rpx !important;
height: 14rpx !important;
background-color: #ccc;
@@ -626,7 +626,7 @@ scroll-view ::-webkit-scrollbar {
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
animation:.6s backgroundChange linear infinite;
}
/deep/ body uni-page-refresh div::after{
::v-deep body uni-page-refresh div::after{
content: "";
position: absolute;
left: -22rpx;
@@ -636,7 +636,7 @@ scroll-view ::-webkit-scrollbar {
background-color: #ccc;
animation:.5s backgroundChange linear infinite;
}
/deep/ body uni-page-refresh div::before{
::v-deep body uni-page-refresh div::before{
content: "";
position: absolute;
right: -22rpx;
@@ -646,15 +646,15 @@ scroll-view ::-webkit-scrollbar {
background-color: #ccc;
animation:.7s backgroundChange linear infinite;
}
/deep/ body uni-page-refresh > div > div{
::v-deep body uni-page-refresh > div > div{
display: none !important;
}
// 下拉加载动画【scroll-view】
/deep/ body .uni-scroll-view-refresher{
::v-deep body .uni-scroll-view-refresher{
background-color: transparent !important;
}
/deep/ body .uni-scroll-view-refresher div{
::v-deep body .uni-scroll-view-refresher div{
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
@@ -666,7 +666,7 @@ scroll-view ::-webkit-scrollbar {
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
animation:.6s backgroundChange linear infinite;
}
/deep/ body .uni-scroll-view-refresher div::after{
::v-deep body .uni-scroll-view-refresher div::after{
content: "";
position: absolute;
left: -22rpx;
@@ -676,7 +676,7 @@ scroll-view ::-webkit-scrollbar {
background-color: #ccc;
animation:.5s backgroundChange linear infinite;
}
/deep/ body .uni-scroll-view-refresher div::before{
::v-deep body .uni-scroll-view-refresher div::before{
content: "";
position: absolute;
right: -22rpx;
@@ -686,7 +686,7 @@ scroll-view ::-webkit-scrollbar {
background-color: #ccc;
animation:.7s backgroundChange linear infinite;
}
/deep/ body .uni-scroll-view-refresher > div > div{
::v-deep body .uni-scroll-view-refresher > div > div{
display: none !important;
}
@keyframes backgroundChange {

View File

@@ -1,4 +1,4 @@
// 修复图片垂直对齐问题,解决两张图片上下有空白缝隙问题
/deep/ ._img {
::v-deep ._img {
vertical-align: top;
}

View File

@@ -377,7 +377,7 @@ view {
}
}
/deep/ .goods-form {
::v-deep .goods-form {
display: flex;
align-items: center;
position: relative;
@@ -1354,7 +1354,7 @@ view {
border-bottom: 2rpx solid #F4F4F6;
}
/deep/ .form-wrap {
::v-deep .form-wrap {
margin: 0 24rpx;
.icon-right {

View File

@@ -5,6 +5,17 @@ import store from '@/store/index.js'
let currentConversationId = null;
const CONVERSATION_KEY = 'ai_conversation_id';
/**
* 获取域名配置参数(避免重复代码)
*/
function getDomainParams() {
return {
imgDomain: Config.imgDomain,
h5Domain: Config.h5Domain,
baseUrl: Config.baseUrl
};
}
// 初始化时从本地读取会话ID
try {
const saved = uni.getStorageSync(CONVERSATION_KEY);
@@ -16,12 +27,19 @@ try {
}
export default {
/**
* 获取uniacid
* @returns {number} uniacid
*/
getUniacid() {
return uni.getStorageSync('uniacid') || Config.uniacid; // 从缓存中获取uniacid,或使用默认uniacid
},
/**
* 发送普通消息(blocking 模式)
*/
async sendMessage(message, options = {}) {
try {
const aiConfig = store.getters.globalAIKefuConfig;
const params = {
url: '/api/kefu/chat',
data: {
@@ -31,16 +49,14 @@ export default {
query: message,
response_mode: 'blocking', // 强制 blocking
user: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || ''
conversation_id: this.getConversationId() || '',
...getDomainParams()
},
header: {
'Content-Type': 'application/json'
}
};
console.log('Sending blocking request to:', params.url);
console.log('Request data:', params.data);
const response = await http.sendRequest({
...params,
async: false
@@ -52,7 +68,14 @@ export default {
}
return result;
} catch (error) {
console.error('Dify API请求失败:', error);
console.error('[AI Service] Dify API请求失败,详细错误:', error);
console.error('[AI Service] 错误堆栈:', error.stack);
// 打印更多信息帮助调试
console.error('[AI Service] store 状态:', {
memberInfo: store.state.memberInfo,
uniacid: this.getUniacid(),
token: store.state.token
});
throw new Error('AI服务暂时不可用,请稍后重试');
}
},
@@ -62,53 +85,104 @@ export default {
*/
async sendStreamMessage(message, onChunk, onComplete) {
// #ifdef MP-WEIXIN
// 微信小程序:降级为普通请求 + 前端打字模拟
try {
const result = await this.sendMessage(message);
const content = result.content || '';
const conversationId = result.conversationId || '';
return new Promise((resolve, reject) => {
const match = Config.baseUrl.match(/^(?:https?:\/\/|wss?:\/\/)([^\/\s:]+)/);
const domain = match ? match[1] : '';
const wssUrl = `wss://${domain}/ws/aikefu`;
const socketTask = wx.connectSocket({
url: wssUrl,
header: {}
});
// 保存会话ID(确保连续对话)
let content = '';
let conversationId = '';
let isAuthenticated = false;
socketTask.onOpen(() => {
socketTask.send({
data: JSON.stringify({
action: 'auth',
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
user_id: store.state.memberInfo?.id || 'anonymous'
})
});
});
socketTask.onMessage((res) => {
try {
const data = JSON.parse(res.data);
if (data.type === 'auth_success') {
isAuthenticated = true;
socketTask.send({
data: JSON.stringify({
action: 'chat',
uniacid: this.getUniacid(),
query: message,
user_id: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || '',
...getDomainParams()
})
});
} else if (data.type === 'auth_failed') {
const errorMsg = '认证失败,请重新登录';
console.error(errorMsg, data);
reject(new Error(errorMsg));
if (onComplete) onComplete({ error: errorMsg });
socketTask.close();
}
// 处理流式消息块
else if (data.event === 'message') {
const text = data.answer || data.content || data.text || '';
content += text;
if (onChunk) onChunk(text);
}
// 处理流结束
else if (data.event === 'message_end') {
conversationId = data.conversation_id || '';
if (conversationId) {
this.setConversationId(conversationId);
}
// 模拟打字效果
let index = 0;
const chunkSize = 2; // 每次显示2个字符
return new Promise((resolve) => {
const timer = setInterval(() => {
if (index < content.length) {
const chunk = content.substring(index, index + chunkSize);
index += chunkSize;
if (onChunk) onChunk(chunk);
} else {
clearInterval(timer);
if (onComplete) {
onComplete({
content: content,
conversation_id: conversationId
});
onComplete({ content, conversation_id: conversationId });
}
resolve({ content, conversation_id: conversationId });
socketTask.close();
}
// 可选:处理 done
else if (data.type === 'done') {
}
} catch (e) {
console.error('WebSocket 消息解析失败:', e, '原始数据:', res.data);
}
}, 80); // 打字速度:80ms/次
});
} catch (error) {
console.error('小程序流式消息降级失败:', error);
if (onComplete) {
onComplete({ error: error.message || '发送失败' });
}
throw error;
}
// #endif
// #ifdef H5
// H5:使用真实流式(EventSource / Fetch)
return this.sendHttpStream(message, onChunk, onComplete);
// #endif
},
socketTask.onError((err) => {
const errorMsg = 'WebSocket 连接失败';
console.error(errorMsg, err);
reject(new Error(errorMsg));
if (onComplete) onComplete({ error: errorMsg });
});
socketTask.onClose(() => {
});
const timeout = setTimeout(() => {
if (!isAuthenticated || content === '') {
console.warn('WebSocket 超时,强制关闭');
socketTask.close();
reject(new Error('AI服务响应超时'));
if (onComplete) onComplete({ error: 'AI服务响应超时' });
}
}, 10000);
});
// #endif
},
/**
* HTTP 流式请求(仅 H5 使用)
*/
@@ -123,20 +197,19 @@ export default {
'Accept': 'text/event-stream',
},
body: JSON.stringify({
uniacid: store.state.uniacid || '1',
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
inputs: {},
query: message,
response_mode: 'streaming',
user_id: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || ''
conversation_id: this.getConversationId() || '',
...getDomainParams()
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
if (!response.body) {
throw new Error('响应体不可用');
if (!response.ok || !response.body) {
throw new Error('无效响应');
}
const reader = response.body.getReader();
@@ -145,50 +218,64 @@ export default {
let content = '';
let conversationId = '';
function processBuffer(buf, callback) {
const lines = buf.split('\n');
buf = lines.pop() || '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 按行分割,保留不完整的最后一行
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // 未完成的行留到下次
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('data:')) {
const jsonStr = trimmed.slice(5).trim();
if (jsonStr) {
try {
const jsonStr = trimmed.slice(5).trim();
if (jsonStr && jsonStr !== '[DONE]') {
const data = JSON.parse(jsonStr);
if (data.event === 'message') {
const text = data.answer || data.text || '';
content += text;
callback(text);
if (onChunk) onChunk(text);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
if (data.event === 'message_end') {
// 可选:提前完成
// 可提前结束
}
}
} catch (e) {
console.warn('解析流数据失败:', e);
console.warn('解析失败:', e, line);
}
}
}
}
return buf;
}
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
buffer = processBuffer(buffer, (chunk) => {
if (onChunk) onChunk(chunk);
});
// 处理最后残留的 buffer(如果有)
if (buffer.trim().startsWith('data:')) {
try {
const jsonStr = buffer.trim().slice(5);
if (jsonStr) {
const data = JSON.parse(jsonStr);
if (data.event === 'message') {
const text = data.answer || '';
content += text;
if (onChunk) onChunk(text);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
}
} catch (e) {
console.warn('最后 buffer 解析失败:', e);
}
}
if (onComplete) {
onComplete({
content,
conversation_id: conversationId
});
onComplete({ content, conversation_id: conversationId });
}
return { content, conversation_id: conversationId };
} catch (error) {
@@ -196,11 +283,6 @@ export default {
throw error;
}
// #endif
// #ifdef MP-WEIXIN
// 理论上不会执行到这里,但防止 fallback
return this.sendStreamMessage(message, onChunk, onComplete);
// #endif
},
/**
@@ -259,9 +341,11 @@ export default {
const params = {
url: '/api/kefu/createConversation',
data: {
uniacid: store.state.uniacid,
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
user_id: store.state.memberInfo?.id || 'anonymous',
member_id: store.state.memberInfo?.id || '',
member_id: store.state.memberInfo?.id || 'anonymous',
...getDomainParams()
},
header: {
'Content-Type': 'application/json'
@@ -305,7 +389,10 @@ export default {
try {
const response = await http.sendRequest({
url: '/api/kefu/clear-conversation',
data: { conversation_id: conversationId },
data: {
conversation_id: conversationId,
...getDomainParams()
},
async: false
});
return response.success;
@@ -324,13 +411,14 @@ export default {
throw new Error('会话ID(conversation_id)是必填参数');
}
const requestData = {
uniacid: params.uniacid || store.state.uniacid || '1',
uniacid: params.uniacid || this.getUniacid(),
conversation_id: params.conversation_id,
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
limit: params.limit || 20,
offset: params.offset || 0,
member_id: params.member_id || store.state.memberInfo?.id || '',
token: params.token || ''
member_id: params.member_id || store.state.memberInfo?.id || 'anonymous',
token: params.token || '',
...getDomainParams()
};
const response = await http.sendRequest({
url: '/api/kefu/getHistory',
@@ -369,13 +457,17 @@ export default {
if (window.currentEventSource) {
window.currentEventSource.close();
}
const params = new URLSearchParams({
uniacid: store.state.uniacid || '1',
user_id: store.state.memberInfo?.id || '123456',
const paramsObj = {
uniacid: this.getUniacid(),
user_id: store.state.memberInfo?.id || 'anonymous',
member_id: store.state.memberInfo?.id || 'anonymous',
token: store.state.token || 'test_token',
query: message,
conversation_id: conversationId || '',
stream: 'true'
});
stream: 'true',
...getDomainParams()
};
const params = new URLSearchParams(paramsObj);
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
try {
const eventSource = new EventSource(url);

View File

@@ -116,7 +116,7 @@ class ConfigExternal {
try {
// 动态加载主题配置
const themeData = require(`@/common/js/style_color.js`)['default'][theme];
console.log('async themeData => ', themeData);
// console.log('async themeData => ', themeData);
this.loadedConfigs[`theme_${theme}`] = themeData;
resolve(themeData);
} catch (error) {

View File

@@ -20,6 +20,10 @@ try {
// 默认域名, 自定义发行时可以修改
let defaultDomain = '';
// #ifdef H5_XCX_5G_QUICKAPP_COM
defaultDomain = 'https://xcx20.5g-quickapp.com';
// #endif
// #ifdef H5_XCX_AIGC_QUICKAPP_COM
defaultDomain = 'https://xcx.aigc-quickapp.com/';
// #endif
@@ -55,7 +59,6 @@ if (process?.env?.NODE_ENV === 'development') {
// 如果本地配置文件不存在或加载失败,保持使用默认配置
// 只在开发模式下输出提示信息
// #ifdef WEB
console.log('本地配置文件 .local.config.js 不存在,使用默认配置');
// #endif
}
}

View File

@@ -2,6 +2,8 @@
* 客服统一处理服务
* 整合各种客服方式,提供统一的调用接口
*/
import IMAGE_CONFIG from './image-config.js';
export class CustomerService {
constructor(vueInstance, externalConfig = null) {
this.vm = vueInstance;
@@ -28,28 +30,28 @@ export class CustomerService {
return this.latestPlatformConfig;
}
// 优先级:外部传入的最新配置 > vuex配置 > 空对象
// 优先级:外部传入 > vuex store > 空对象
const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {};
console.log(`【实时客服配置】`, servicerConfig);
let platformConfig = null;
// #ifdef H5
platformConfig = servicerConfig.h5 ? (typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null) : null;
platformConfig = servicerConfig.h5 && typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null;
// #endif
// #ifdef MP-WEIXIN
platformConfig = servicerConfig.weapp ? (typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null) : null;
platformConfig = servicerConfig.weapp && typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null;
// #endif
// #ifdef MP-ALIPAY
platformConfig = servicerConfig.aliapp ? (typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null) : null;
platformConfig = servicerConfig.aliapp && typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null;
// #endif
// #ifdef PC
platformConfig = servicerConfig.pc ? (typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null) : null;
platformConfig = servicerConfig.pc && typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null;
// #endif
// 处理空数组情况(你的配置中pc/aliapp是空数组,转为null)
// 防止空数组被当作有效配置
if (Array.isArray(platformConfig)) {
platformConfig = null;
}
@@ -75,6 +77,15 @@ export class CustomerService {
return config && typeof config === 'object' && config.type;
}
/**
* 检查是否有有效的客服配置(用于判断是否显示客服按钮)
* @returns {Boolean}
*/
hasValidKefuConfig() {
const config = this.getPlatformConfig();
return config && typeof config === 'object' && config.type && config.type !== 'none';
}
/**
* 验证客服配置完整性
* @returns {Object} 验证结果
@@ -89,32 +100,18 @@ export class CustomerService {
warnings: []
};
if (!config) {
result.isValid = false;
result.errors.push('客服配置不存在');
return result;
}
if (config.type === 'aikefu') {
return result;
}
if (!config.type) {
if (!config || !config.type) {
result.isValid = false;
result.errors.push('客服类型未配置');
return result;
}
if (config.type === 'wxwork') {
if (!wxworkConfig) {
result.isValid = false;
result.errors.push('企业微信配置不存在');
} else {
if (!wxworkConfig.enable) {
result.warnings.push('企业微信功能未启用');
if (!wxworkConfig || !wxworkConfig.enable) {
result.warnings.push('企业微信未启用');
}
if (!wxworkConfig.contact_url) {
result.warnings.push('企业微信活码链接未配置,将使用原有客服方式');
}
result.warnings.push('企业微信活码链接未配置');
}
}
@@ -122,40 +119,39 @@ export class CustomerService {
}
/**
* 跳转到Dify客服页面
* 跳转到 AI 客服页面(Dify)
*/
openDifyService() {
try {
if (this.vm.setAiUnreadCount) {
// 清除未读数(如果存在)
if (typeof this.vm.setAiUnreadCount === 'function') {
this.vm.setAiUnreadCount(0);
}
// 强制跳转,忽略框架层的封装
uni.redirectTo({
url: '/pages_tool/ai-chat/index',
// ✅ 修正路径:必须与 pages.json 中注册的路径一致
const aiChatUrl = '/pages_tool/ai-chat/index';
// ✅ 使用 navigateTo 保留返回栈(体验更好)
uni.navigateTo({
url: aiChatUrl,
fail: (err) => {
// 兜底:使用window.location跳转(H5)
console.error('跳转 AI 客服失败:', err);
// H5 兜底
// #ifdef H5
window.location.href = '/pages_tool/ai-chat/index';
window.location.href = aiChatUrl;
// #endif
console.error('跳转Dify客服失败:', err);
uni.showToast({
title: '跳转客服失败',
icon: 'none'
});
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
});
} catch (e) {
console.error('跳转Dify客服异常:', e);
uni.showToast({
title: '跳转客服失败',
icon: 'none'
});
console.error('跳转 AI 客服异常:', e);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
}
/**
* 处理客服点击事件
* @param {Object} options 选项参数
* 处理客服点击事件(统一入口)
* @param {Object} options 选项参数(用于消息卡片等)
*/
handleCustomerClick(options = {}) {
const validation = this.validateConfig();
@@ -170,6 +166,7 @@ export class CustomerService {
}
const config = this.getPlatformConfig();
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
if (config.type === 'none') {
@@ -177,7 +174,7 @@ export class CustomerService {
return;
}
// 核心分支:根据最新的type处理
// 核心路由:根据 type 决定行为
switch (config.type) {
case 'aikefu':
this.openDifyService();
@@ -188,6 +185,9 @@ export class CustomerService {
case 'third':
this.openThirdService(config);
break;
case 'miniprogram':
this.openThirdService(config);
break;
case 'niushop':
this.openNiushopService(niushop);
break;
@@ -198,36 +198,40 @@ export class CustomerService {
this.openAliappService(config);
break;
default:
console.error('【未知客服类型】', config.type);
this.makePhoneCall();
}
}
/**
* 打开企业微信客服
* @param {boolean} useOriginalService 是否使用原有客服方式
* @param {Object} servicerConfig 客服配置
* @param {Object} options 选项参数
*/
// ================== 各类型客服实现 ==================
openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) {
const config = servicerConfig || this.getPlatformConfig();
const wxworkConfig = this.getWxworkConfig();
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
// #ifdef MP-WEIXIN
if (wxworkConfig?.enable && wxworkConfig?.contact_url && !useOriginalService) {
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
wx.navigateToMiniProgram({
appId: 'wxeb490c6f9b154ef9',
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`,
success: () => console.log('跳转企业微信成功'),
success: () => {},
fail: (err) => {
console.error('跳转企业微信失败:', err);
this.fallbackToOriginalService();
}
});
} else {
// 检查是否有企业微信配置
if (!config.wxwork_url && !config.corpid) {
console.error('企业微信配置不完整,缺少 wxwork_url 或 corpid');
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
this.fallbackToPhoneCall();
return;
}
wx.openCustomerServiceChat({
extInfo: { url: config.wxwork_url },
corpId: config.corpid,
extInfo: { url: config.wxwork_url || '' },
corpId: config.corpid || '',
showMessageCard: true,
sendMessageTitle,
sendMessagePath,
@@ -237,120 +241,120 @@ export class CustomerService {
// #endif
// #ifdef H5
if (wxworkConfig?.enable && wxworkConfig?.contact_url) {
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
window.location.href = wxworkConfig.contact_url;
} else if (config.wxwork_url) {
location.href = config.wxwork_url;
window.location.href = config.wxwork_url;
} else {
this.fallbackToPhoneCall();
}
// #endif
}
/**
* 打开第三方客服
* @param {Object} config 客服配置
*/
openThirdService(config) {
// 支持多种可能的字段名
const miniAppId = config.mini_app_id || config.miniAppId || config.appid || config.appId || config.app_id;
const miniAppPath = config.mini_app_path || config.miniAppPath || config.path || config.page_path || '';
// 优先处理第三方微信小程序客服
if (miniAppId) {
// #ifdef MP-WEIXIN
wx.navigateToMiniProgram({
appId: miniAppId,
path: miniAppPath,
success: () => {
},
fail: (err) => {
console.error('【跳转第三方小程序失败】', err);
uni.showToast({ title: '跳转失败,请稍后重试', icon: 'none' });
}
});
// #endif
// #ifdef H5
uni.showToast({ title: '第三方小程序客服仅在微信小程序中可用', icon: 'none' });
// #endif
return;
}
// 处理第三方链接客服
if (config.third_url) {
// #ifdef H5
window.location.href = config.third_url;
// #endif
// #ifdef MP-WEIXIN
uni.setClipboardData({
data: config.third_url,
success: () => {
uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' });
}
});
// #endif
} else {
console.error('【第三方客服配置不完整】缺少 mini_app_id 或 third_url');
this.fallbackToPhoneCall();
}
}
/**
* 打开牛商客服
* @param {Object} niushop 牛商参数
*/
openNiushopService(niushop) {
if (Object.keys(niushop).length > 0) {
if (Object.keys(niushop).length > 0 && this.vm.$util?.redirectTo) {
this.vm.$util.redirectTo('/pages_tool/chat/room', niushop);
} else {
this.makePhoneCall();
}
}
/**
* 打开微信小程序客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
openWeappService(config, options = {}) {
if (!this.shouldUseCustomService(config)) {
console.log('使用官方微信小程序客服');
// 如果 useOfficial 为 true 或 undefined,则使用原生系统客服(由 button open-type="contact" 触发)
// 此方法仅用于自定义跳转(如 useOfficial: false)
if (config.useOfficial !== false) {
// 不做任何事,应由 <button open-type="contact"> 触发
return;
}
console.log('使用自定义微信小程序客服');
this.handleCustomWeappService(config, options);
}
/**
* 处理自定义微信小程序客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
handleCustomWeappService(config, options = {}) {
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
if (config.customServiceUrl) {
let url = config.customServiceUrl;
const params = [];
const { sendMessageTitle, sendMessagePath, sendMessageImg } = options;
if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`);
if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`);
if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`);
if (params.length > 0) {
url += (url.includes('?') ? '&' : '?') + params.join('&');
}
uni.navigateTo({
url: url,
fail: (err) => {
console.error('跳转自定义客服页面失败:', err);
this.tryThirdPartyService(config, options);
}
});
uni.navigateTo({ url });
return;
}
this.tryThirdPartyService(config, options);
}
/**
* 尝试使用第三方客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
tryThirdPartyService(config, options = {}) {
if (config.thirdPartyServiceUrl) {
// #ifdef H5
window.open(config.thirdPartyServiceUrl, '_blank');
// #endif
// 支持第三方微信小程序客服
if (config.thirdPartyMiniAppId || config.mini_app_id) {
// #ifdef MP-WEIXIN
if (config.thirdPartyMiniAppId) {
wx.navigateToMiniProgram({
appId: config.thirdPartyMiniAppId,
path: config.thirdPartyMiniAppPath || '',
fail: (err) => {
console.error('跳转第三方小程序失败:', err);
this.fallbackToPhoneCall();
}
});
} else {
uni.setClipboardData({
data: config.thirdPartyServiceUrl,
success: () => {
uni.showModal({
title: '客服链接已复制',
content: '客服链接已复制到剪贴板,请在浏览器中粘贴访问',
showCancel: false
});
}
appId: config.thirdPartyMiniAppId || config.mini_app_id,
path: config.thirdPartyMiniAppPath || config.mini_app_path || ''
});
// #endif
return;
}
// 支持第三方链接客服
if (config.thirdPartyServiceUrl || config.third_url) {
const serviceUrl = config.thirdPartyServiceUrl || config.third_url;
// #ifdef H5
window.open(serviceUrl, '_blank');
// #endif
// #ifdef MP-WEIXIN
uni.setClipboardData({ data: serviceUrl });
uni.showToast({ title: '客服链接已复制', icon: 'none' });
// #endif
return;
}
@@ -358,103 +362,55 @@ export class CustomerService {
this.fallbackToPhoneCall();
}
/**
* 降级到电话客服
*/
fallbackToPhoneCall() {
uni.showModal({
title: '联系客服',
content: '在线客服暂时不可用,是否拨打电话联系客服?',
success: (res) => {
if (res.confirm) {
this.makePhoneCall();
}
}
});
}
/**
* 打开支付宝小程序客服
* @param {Object} config 客服配置
*/
openAliappService(config) {
console.log('支付宝小程序客服', config);
switch (config.type) {
case 'aikefu':
if (config.type === 'aikefu') {
this.openDifyService();
break;
case 'third':
} else if (config.type === 'third') {
this.openThirdService(config);
break;
default:
console.log('使用支付宝官方客服');
break;
} else {
// 支付宝原生客服由 button open-type="contact" 触发,此处不处理
}
}
/**
* 拨打电话
*/
// ================== 辅助方法 ==================
makePhoneCall() {
this.vm.$api.sendRequest({
url: '/api/site/shopcontact',
success: res => {
if (res.code === 0 && res.data?.mobile) {
uni.makePhoneCall({
phoneNumber: res.data.mobile
});
uni.makePhoneCall({ phoneNumber: res.data.mobile });
} else {
uni.showToast({
title: '暂无客服电话',
icon: 'none'
});
uni.showToast({ title: '暂无客服电话', icon: 'none' });
}
},
fail: () => {
uni.showToast({
title: '获取客服电话失败',
icon: 'none'
});
uni.showToast({ title: '获取客服电话失败', icon: 'none' });
}
});
}
/**
* 显示无客服弹窗
*/
showNoServicePopup() {
const siteInfo = this.vm.$store.state.siteInfo || {};
const message = siteInfo?.site_tel
? `请联系客服,客服电话是${siteInfo.site_tel}`
? `请联系客服,客服电话是 ${siteInfo.site_tel}`
: '抱歉,商家暂无客服,请线下联系';
uni.showModal({
title: '联系客服',
content: message,
showCancel: false
});
uni.showModal({ title: '联系客服', content: message, showCancel: false });
}
/**
* 显示配置错误弹窗
* @param {Array} errors 错误列表
*/
showConfigErrorPopup(errors) {
const message = errors.join('\n');
uni.showModal({
title: '配置错误',
content: `客服配置有误:\n${message}`,
title: '客服配置错误',
content: `配置有误:\n${errors.join('\n')}`,
showCancel: false
});
}
/**
* 降级处理:使用原有客服方式
*/
fallbackToOriginalService() {
uni.showModal({
title: '提示',
content: '无法直接添加企业微信客服,是否使用其他方式联系客服?',
content: '无法直接添加企业微信,是否使用其他方式联系客服?',
success: (res) => {
if (res.confirm) {
this.openWxworkService(true);
@@ -463,9 +419,19 @@ export class CustomerService {
});
}
fallbackToPhoneCall() {
uni.showModal({
title: '提示',
content: '在线客服不可用,是否拨打电话联系客服?',
success: (res) => {
if (res.confirm) this.makePhoneCall();
}
});
}
/**
* 获取客服按钮配置
* @returns {Object} 按钮配置
* 获取按钮配置(用于 template 中 v-if / open-type 判断)
* @returns {Object}
*/
getButtonConfig() {
const config = this.getPlatformConfig();
@@ -473,11 +439,10 @@ export class CustomerService {
let openType = '';
// #ifdef MP-WEIXIN
if (config.type === 'weapp') {
openType = config.useOfficial !== false ? 'contact' : '';
if (config.type === 'weapp' && config.useOfficial !== false) {
openType = 'contact';
}
// #endif
// #ifdef MP-ALIPAY
if (config.type === 'aliapp') openType = 'contact';
// #endif
@@ -486,25 +451,234 @@ export class CustomerService {
}
/**
* 判断是否应该使用自定义客服处理
* @param {Object} config 客服配置
* @returns {boolean} 是否使用自定义客服
* 获取客服按钮图标
* @returns {String} 图标URL
*/
shouldUseCustomService(config) {
getKefuButtonIcon() {
const shopInfo = this.vm.shopInfo || {};
const config = this.getPlatformConfig();
// 优先使用指定的智能客服头像
if (config?.type === 'aikefu' || config?.type === 'wxwork' || config?.type === 'qyweixin') {
return this.vm.$util.img(IMAGE_CONFIG.aiAvatar);
}
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
}
/**
* 判断是否为微信官方客服
* @returns {Boolean}
*/
isWeappOfficialKefu() {
const config = this.getPlatformConfig();
return config?.type === 'weapp';
}
/**
* 判断是否为智能客服
* @returns {Boolean}
*/
isAIKefu() {
const config = this.getPlatformConfig();
return config?.type === 'aikefu';
}
/**
* 判断是否为企业微信客服
* @returns {Boolean}
*/
isWxworkKefu() {
const config = this.getPlatformConfig();
return config?.type === 'wxwork' || config?.type === 'qyweixin';
}
/**
* 判断是否为第三方客服
* @returns {Boolean}
*/
isThirdKefu() {
const config = this.getPlatformConfig();
return config?.type === 'third';
}
/**
* 判断是否需要同时显示小程序系统客服
* @returns {Boolean}
*/
shouldShowExtraWeappSystemKefu() {
const config = this.getPlatformConfig();
return (config?.show_system_service === true || config?.show_system_service === '1') && config?.type !== 'weapp';
}
/**
* 计算容器高度
* @param {Boolean} isLanguageSwitchEnabled 是否启用语言切换
* @param {Boolean} showWeappSystemKefu 是否显示小程序系统客服
* @param {Boolean} fixBtnShow 是否显示按钮
* @returns {String} 高度值(px)
*/
getContainerHeight(isLanguageSwitchEnabled, showWeappSystemKefu, fixBtnShow) {
if (!fixBtnShow) return '160px';
let buttonCount = 1;
if (isLanguageSwitchEnabled) buttonCount++;
if (showWeappSystemKefu) buttonCount++;
const totalRpx = 94 * buttonCount - 14;
const pxValue = Math.round(totalRpx * 0.5);
return `${pxValue}px`;
}
/**
* 获取未读消息数量
* @returns {Number}
*/
getUnreadCount() {
return this.vm.$store?.state?.aiUnreadCount || 0;
}
/**
* 拨打电话联系客服
* @param {String} phoneNumber 电话号码
*/
makePhoneCallByNumber(phoneNumber) {
if (phoneNumber) {
uni.makePhoneCall({ phoneNumber });
} else {
this.makePhoneCall();
}
}
/**
* 打开 AI 智能助手
*/
openAIChat() {
try {
if (typeof this.vm.setAiUnreadCount === 'function') {
this.vm.setAiUnreadCount(0);
}
const aiChatUrl = '/pages_tool/ai-chat/index';
uni.navigateTo({
url: aiChatUrl,
fail: (err) => {
console.error('跳转 AI 客服失败:', err);
// #ifdef H5
window.location.href = aiChatUrl;
// #endif
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
});
} catch (e) {
console.error('跳转 AI 客服异常:', e);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
}
/**
* 打开客服选择对话框(预留方法)
*/
openCustomerSelectPopupDialog() {
uni.showToast({ title: '客服选择功能开发中', icon: 'none' });
}
/**
* 打开客服选择弹出层(ActionSheet)
* @param {Array} kefuList 客服列表
*/
openKefuSelectPopup(kefuList) {
const kefuNames = kefuList.map(item => item.name);
uni.showActionSheet({
itemList: kefuNames,
success: (res) => {
const selectedKefu = kefuList[res.tapIndex];
const cs = createCustomerService(this.vm, selectedKefu);
if (selectedKefu.isOfficial) {
uni.openCustomerServiceConversation({
sessionFrom: 'weapp',
showMessageCard: true
});
} else if (selectedKefu.id === 'qyweixin-kefu') {
if (uni.getSystemInfoSync().platform === 'wechat') {
uni.navigateTo({
url: '/pages_tool/qyweixin-kefu/index'
});
} else {
const qyweixinUrl = this.vm.shopInfo?.qyweixinUrl;
if (qyweixinUrl) {
window.location.href = qyweixinUrl;
} else {
uni.showToast({ title: '企业微信客服未配置', icon: 'none' });
}
}
} else {
cs.handleCustomerClick();
}
}
});
}
/**
* 核心方法:统一客服入口(带验证和配置检查)
* @param {Object} options 选项参数
*/
handleUnifiedKefuClick(options = {}) {
const validation = this.validateConfig();
if (!validation.isValid) {
console.error('客服配置无效:', validation.errors);
uni.showToast({ title: '客服暂不可用', icon: 'none' });
return;
}
if (validation.warnings.length > 0) {
console.warn('客服配置警告:', validation.warnings);
}
const platformConfig = this.getPlatformConfig();
// 检查企业微信配置
if (platformConfig.type === 'wxwork') {
const wxworkConfig = this.getWxworkConfig();
// #ifdef MP-WEIXIN
if (config?.type === 'weapp') {
return config.useOfficial === false;
if (!wxworkConfig?.enable || !wxworkConfig?.contact_url) {
console.warn('企业微信配置不完整,使用原生客服');
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
}
// #endif
return true;
// #ifdef H5
if (!wxworkConfig?.contact_url && !platformConfig.wxwork_url) {
console.error('企业微信链接未配置');
uni.showToast({ title: '企业微信链接未配置', icon: 'none' });
return;
}
// #endif
}
// 直接调用统一处理方法,由 CustomerService 内部根据配置路由
try {
this.handleCustomerClick({
sendMessageTitle: options.sendMessageTitle || '来自悬浮按钮的咨询',
sendMessagePath: options.sendMessagePath || '/pages/index/index'
});
} catch (error) {
console.error('客服处理失败:', error);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
}
}
/**
* 创建客服服务实例
* @param {Object} vueInstance Vue实例
* @param {Object} externalConfig 外部最新配置
* @returns {CustomerService} 客服服务实例
* @param {Object} vueInstance Vue 实例(通常是 this)
* @param {Object} externalConfig 可选:外部传入的最新配置(如从 DIY 数据中提取)
* @returns {CustomerService}
*/
export function createCustomerService(vueInstance, externalConfig = null) {
return new CustomerService(vueInstance, externalConfig);

View File

@@ -200,7 +200,6 @@ export default {
},
methods: {
play(){
console.log(123)
},
async init() {
@@ -609,10 +608,10 @@ export default {
},
// 分享给好友
onShareAppMessage() {
return this.mpShareData.appMessage;
return this.mpShareData?.appMessage;
},
// 分享到朋友圈
onShareTimeline() {
return this.mpShareData.timeLine;
return this.mpShareData?.timeLine;
}
}

View File

@@ -7,7 +7,7 @@ export default {
computed: {
// 是否是英文环境
isEnEnv() {
return uni.getStorageSync('lang') === 'en-us';
return this.$langConfig.getCurrentLocale() === 'en-us';
},
themeStyle() {
return this.$store.state.themeStyle;
@@ -65,10 +65,18 @@ export default {
componentRefresh() {
return this.$store.state.componentRefresh;
},
// AI客服配置
globalAIKefuConfig() {
return this.$store.state.globalAIKefuConfig;
},
// 客服配置
servicerConfig() {
return this.$store.state.servicerConfig;
},
// 企业微信配置
wxworkConfig() {
return this.$store.state.wxworkConfig;
},
diySeckillInterval() {
return this.$store.state.diySeckillInterval;
},
@@ -145,7 +153,7 @@ export default {
if (isJump && route != 'pages/index/index') {
uni.setStorageSync('manual_change_store', true); // 手动切换门店
this.$store.dispatch('getCartNumber'); //重新获取购物车数据
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
},

View File

@@ -164,7 +164,6 @@ export default {
// 商品详情
if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = (this.goodsSkuDetail.goods_content);
console.log(this.goodsSkuDetail.goods_content)
// if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = htmlParser(this.goodsSkuDetail.goods_content);
//商品服务
@@ -240,7 +239,7 @@ export default {
},
goHome() {
if (this.preview) return; // 开启预览,禁止任何操作和跳转
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
},
goCart() {
if (this.preview) return; // 开启预览,禁止任何操作和跳转

View File

@@ -316,11 +316,9 @@ function replaceVideo(html){
let rep = `<video style="width:100% !important;display:block;max-width: ${screenWidth} !important;"`;
var html = html.replace(/\\/g, '').replace(/<video/g, rep);
console.log(html)
html = html.replace(/<video [^>]*src=['"]([^'"]+)[^>]*>/gi, (match, capture) => {
return rep + ' src="' + util.img(capture) + '"/>';
});
// console.log(html)
return html;
}

15
common/js/image-config.js Normal file
View File

@@ -0,0 +1,15 @@
/**
* 图片配置文件
* 将大图片上传到服务器后,请在此处配置网络URL
*/
// 替换为你上传后的实际图片URL
const IMAGE_CONFIG = {
// AI客服头像
aiAvatar: 'public/static/img/s-agent.jpg',
// 用户默认头像
userAvatar: 'public/static/img/s-user.jpg'
};
export default IMAGE_CONFIG;

View File

@@ -54,27 +54,40 @@ function loadLangPackSync(lang, path) {
}
}
function getCurrentLocale() {
return uni.getStorageSync('lang') || "zh-cn";
}
export default {
langList: langConfig.langList,
/**
* 获得当前本地语言
* @returns
*/
getCurrentLocale,
/**
* * 解析多语言
* @param {Object} field
*/
lang(field) {
let _this = getCurrentPages()[getCurrentPages().length - 1];
if (!_this) return;
let _page = getCurrentPages()[getCurrentPages().length - 1];
if (!_page) return;
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
const locale = getCurrentLocale(); // 获得当前本地语言
let value = ''; // 存放解析后的语言值
let langPath = ''; // 存放当前页面语言包路径
var value = '';
try {
//公共语言包(同步加载)
var lang = loadLangPackSync(locale, 'common');
//当前页面语言包(同步加载)
let route = _this.route;
let langPath = processRoutePath(route);
console.log(`当前语言: ${locale}, 当前页面语言包路径: ${langPath}`);
let route = _page.route;
langPath = processRoutePath(route);
// 加载当前页面语言包
let currentPageLang = loadLangPackSync(locale, langPath);
@@ -102,7 +115,7 @@ export default {
}
} catch (e) {
console.error('解析语言包失败:', e);
console.error('解析语言包失败:', e, { langPath, field, locale });
value = field;
}
@@ -115,7 +128,10 @@ export default {
if (value == undefined || (value == 'title' && field == 'title')) value = ''; // field
// 多语言调试,注释后可以关闭控制台输出
console.log(`字段: ${field}, 值: ${value}`)
if (field == value) {
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
}
return value;
},
/**
@@ -124,11 +140,11 @@ export default {
* @param {String} url 切换后跳转的页面url
*/
change(value, url = '/pages_tool/member/index') {
let _this = getCurrentPages()[getCurrentPages().length - 1];
if (!_this) return;
let _page = getCurrentPages()[getCurrentPages().length - 1];
if (!_page) return;
uni.setStorageSync("lang", value);
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
const locale = getCurrentLocale();
// 清空已加载的语言包缓存
for (let key in loadedLangPacks) {
@@ -145,9 +161,10 @@ export default {
},
//刷新标题、tabbar
refresh() {
let _this = getCurrentPages()[getCurrentPages().length - 1];
if (!_this) return;
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
let _page = getCurrentPages()[getCurrentPages().length - 1];
if (!_page) return;
const locale = getCurrentLocale();
this.title(this.lang("title"));

View File

@@ -89,16 +89,13 @@ export default {
let platform = util.getDeviceInfo().platform;
switch (platform) {
case 'android':
console.log('运行Android上')
openMapByAndroid(arr[1], arr[0], name)
break;
case 'ios':
console.log('运行iOS上')
openMapByIos(arr[1], arr[0], name)
break;
default:
openMapByDefault(arr[1], arr[0], name)
console.log('运行在开发者工具上')
break;
}
// #endif

View File

@@ -43,7 +43,6 @@ export class NavigationHelper {
query.select('.uni-page-head').boundingClientRect((rect) => {
if (rect && rect.height > 0) {
console.log('直接查询导航栏高度成功:', rect.height)
resolve(rect.height)
} else {
console.warn('未找到 uni-page-head 元素或高度为0')
@@ -64,7 +63,6 @@ export class NavigationHelper {
const height = menuButtonInfo.bottom +
(menuButtonInfo.top - systemInfo.statusBarHeight)
console.log('微信小程序导航栏高度:', height)
resolve(height)
} catch (error) {
console.error('微信小程序高度计算失败:', error)
@@ -174,7 +172,6 @@ export class NavigationHelper {
// 处理服务请求
async handleServiceRequest(event, component) {
console.log('处理导航相关服务请求:', event.type)
// 安全检查事件目标
if (this.shouldProcessNavigationRequest(event)) {
@@ -278,7 +275,6 @@ export class NavigationHelper {
// 清理缓存
clearCache() {
this.navigationCache.clear()
console.log('导航缓存已清理')
}
}

100
common/js/share.js Normal file
View File

@@ -0,0 +1,100 @@
// 文件分享工具函数
export default {
/**
* 生成文件预览链接
* @param {Object} file - 文件对象,包含 name 和 url 属性
* @returns {string} - 生成的文件预览链接
*/
generateFilePreviewUrl(file) {
// 推断文件类型
let fileType = '';
if (file.name) {
const ext = file.name.split('.').pop().toLowerCase();
if (['pdf'].includes(ext)) {
fileType = 'pdf';
} else if (['doc', 'docx'].includes(ext)) {
fileType = 'word';
} else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'].includes(ext)) {
fileType = 'video';
}
}
// 构建文件预览链接
const previewUrl = `/pages_tool/file-preview/file-preview?fileName=${encodeURIComponent(file.name)}&fileUrl=${encodeURIComponent(file.url || '')}&fileType=${encodeURIComponent(fileType)}`;
return previewUrl;
},
/**
* 分享文件
* @param {Object} file - 文件对象,包含 name 和 url 属性
* @param {string} title - 分享标题,默认使用文件名
* @param {string} desc - 分享描述,默认使用固定文案
*/
shareFile(file, title = file.name, desc = '查看企业文件:' + file.name) {
// 生成文件预览链接
const filePreviewUrl = this.generateFilePreviewUrl(file);
// #ifdef H5
// H5 平台分享
if (navigator.share) {
// 使用 Web Share API
navigator.share({
title: title,
text: desc,
url: filePreviewUrl
}).catch(err => {
console.error('分享失败:', err);
uni.showToast({ title: '分享失败', icon: 'none' });
});
} else {
// 不支持 Web Share API 的浏览器
uni.setClipboardData({
data: filePreviewUrl,
success: () => {
uni.showToast({ title: '链接已复制,请粘贴分享', icon: 'success' });
},
fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' });
}
});
}
// #endif
// #ifdef MP-WEIXIN
// 微信小程序分享
uni.showActionSheet({
itemList: ['发送给朋友', '分享到朋友圈'],
success: (res) => {
if (res.tapIndex === 0) {
// 发送给朋友
uni.shareAppMessage({
title: title,
path: filePreviewUrl,
success: () => {
uni.showToast({ title: '分享成功', icon: 'success' });
},
fail: () => {
uni.showToast({ title: '分享失败', icon: 'none' });
}
});
} else if (res.tapIndex === 1) {
// 分享到朋友圈
uni.shareTimeline({
title: desc,
path: filePreviewUrl,
success: () => {
uni.showToast({ title: '分享成功', icon: 'success' });
},
fail: () => {
uni.showToast({ title: '分享失败', icon: 'none' });
}
});
}
},
fail: () => {
// 用户取消分享
}
});
// #endif
}
};

View File

@@ -21,53 +21,35 @@ export default {
methods: {
// 判断是否已连接
checkOpenSocket() {
console.log('判断是否已连接');
// alert('判断是否已连接')
let self = this;
uni.sendSocketMessage({
data: 'ping',
success: (res) => {
console.log('连接成功,检查');
// alert('连接成功,检查')
// self.getChatList();
},
fail: (err) => { // 未连接打开websocket连接
console.log('连接失败');
// alert('连接失败')
self.openConnection();
}
});
},
openConnection() { // 打开连接
console.log('打开连接');
// alert('打开连接')
// uni.closeSocket(); // 确保已经关闭后再重新打开
uni.connectSocket({
url: config.webSocket,
method: 'POST',
success(res) {
console.log('连接成功 connectSocket=', res);
// alert('连接成功 connectSocket=', res);
},
fail(err) {
console.log('连接失败 connectSocket=', err);
}
});
// uni.onSocketOpen((res) => {
// console.log('连接成功', res);
// });
this.onSocketMessage(); // 打开成功监听服务器返回的消息
},
// 打开成功监听服务器返回的消息
onSocketMessage() { // 消息
console.log("开始监听");
let that = this;
this.pingInterval = config.pingInterval;
this.timeoutObj = null;
uni.onSocketMessage((res) => { //type:init,connect,close,string,order,goods
let msg = JSON.parse(res.data);
console.log("监听该服务器消息", res);
if (msg.type == 'close') {
clearInterval(that.timeoutObj);
that.timeoutObj = null;
@@ -141,22 +123,18 @@ export default {
},
// 检测心跳reset
reset() {
console.log("检测心跳");
clearInterval(this.timeoutObj);
this.start(); // 启动心跳
},
// 启动心跳 start
start() {
console.log("启动心跳");
let self = this;
this.timeoutObj = setInterval(function () {
uni.sendSocketMessage({
data: 'ping',
success: (res) => {
console.log('连接中....');
},
fail: (err) => {
console.log('连接失败重新连接....');
self.openConnection();
}
});

147
common/js/uniapp.utils.js Normal file
View File

@@ -0,0 +1,147 @@
/**
* 将常用的Uniapp提供的函数,存放到这里,按需引用
*/
/**
* 显示错误信息
* @param {Exception} err
* @param {Boolean} useModal
*/
const showError = (err, useModal = false) => {
const content = err?.message || err?.errMsg || err?.toString();
if (!useModal) {
uni.showToast({
title: content,
icon: 'none',
duration: 3000
});
} else {
uni.showModal({
title: '错误提示',
content,
showCancel: false,
})
}
}
/**
* 打电话
* @param {string} mobile 电话号码
*/
export const makePhoneCall = (mobile) => {
try {
uni.makePhoneCall({
phoneNumber: `${mobile}`,
success(e) {
}
});
} catch (err) {
showError(err);
}
}
/**
* 拷贝文本(返回 Promise)
* @param {*} text
* @param {*} options
* @returns {Promise} 返回 Promise,成功时 resolve,失败时 reject
*/
export const copyTextAsync = (text, { copySuccess = '', copyFailed = '' } = {}) => {
return new Promise((resolve, reject) => {
// 输入验证
if (!text && text !== '') {
const error = new Error('复制文本不能为空');
showError(error);
reject(error);
return;
}
// 超时监测
const timeoutId = setTimeout(() => {
let error = new Error('复制操作长时间无响应,请检查相关权限及配置是否正确');
// #ifdef MP-WEIXIN
error = new Error([
'复制操作长时间无响应!',
'原因:',
'1.微信平台->用户隐私保护指引->"剪贴板"功能未添加或审核未通过;',
'2.微信平台对剪贴板API调用频率有限制'
].join('\r\n'));
// #endif
showError(error, true);
reject(error);
}, 5000);
try {
uni.setClipboardData({
data: `${text}`,
success: (res) => {
clearTimeout(timeoutId);
try {
if (copySuccess) {
uni.showToast({
title: copySuccess,
icon: 'success',
duration: 2000
});
}
} catch (e) {
showError(e);
}
resolve(res);
},
fail: (err) => {
clearTimeout(timeoutId);
try {
uni.showToast({
title: err.message || err.errMsg || copyFailed || '复制失败',
icon: 'none',
duration: 2000
});
} catch (e) {
showError(e);
}
reject(err);
}
});
} catch (err) {
clearTimeout(timeoutId);
showError(err);
reject(err);
}
});
}
/**
* 拷贝文本(回调形式,兼容旧代码)
* @param {*} text
* @param {*} options
* @param {Function} callback 回调函数,接收 (success, error) 参数
*/
export const copyText = (text, options = {}, callback) => {
copyTextAsync(text, options)
.then(res => {
if (callback) callback(true, null);
})
.catch(err => {
if (callback) callback(false, err);
});
}
/**
* 打开定位
* @param {Object} options
*/
export const openLocation = ({ latitude, longitude, name } = {}) => {
try {
uni.openLocation({
latitude,
longitude,
name,
});
} catch (err) {
showError(err);
}
}

View File

@@ -7,15 +7,20 @@ import {
// 首页路径
// 关于页面路径,使用常量导出
export const INDEX_PAGE_URL = '/pages/index/index';
export const CATEGORY_PAGE_URL = '/pages_goods/category';
export const CONTACT_PAGE_URL = '/pages_tool/contact/contact';
export const MEMBER_PAGE_URL = '/pages_tool/member/index';
export const LOGIN_PAGE_URL = '/pages_tool/login/login';
export const AI_CHAT_PAGE_URL = '/pages_tool/ai-chat/index';
// 当前最新的tabBar.list (参见pages.json 中的tabBar.list 配置)
export const systemTabBarList = [
INDEX_PAGE_URL,
'/pages_goods/category',
'/pages_tool/contact/contact',
'/pages_tool/member/index'
CATEGORY_PAGE_URL,
CONTACT_PAGE_URL,
MEMBER_PAGE_URL
];
/**
@@ -89,7 +94,7 @@ export const checkTabBarActive = (linkUrl, currentPageRoute) => {
const linkUrlParts = adaptSubpackageUrl(linkUrl).split('/');
const currentPageRouteParts = currentPageRoute.split('/');
console.log('diy-bottom-nav verify:', { linkUrlParts, currentPageRouteParts});
// console.log('diy-bottom-nav verify:', { linkUrlParts, currentPageRouteParts});
try {
// 二级页面
@@ -109,19 +114,25 @@ export const checkTabBarActive = (linkUrl, currentPageRoute) => {
}
export default {
/** 导出页面URL常量 */
MEMBER_PAGE_URL,
CATEGORY_PAGE_URL,
CONTACT_PAGE_URL,
INDEX_PAGE_URL,
LOGIN_PAGE_URL,
AI_CHAT_PAGE_URL,
/**
* 页面跳转
* @param {string} to 跳转链接 /pages/idnex/index
* @param {Object} param 参数 {key : value, ...}
* @param {string} mode 模式
* @param {Object} param 参数 {key : value, ...}, 默认{}
* @param {string} mode 模式 navigateTo、redirectTo、reLaunch、tabbar, 默认navigateTo
*/
redirectTo(to, param, mode) {
redirectTo(to, param = {}, mode = 'navigateTo') {
let url = to;
// 替换url中的前缀
console.log('redirectTo', to, param, mode);
url = adaptSubpackageUrl(url);
console.log('adaptSubpackageUrl', url);
if (param != undefined) {
Object.keys(param).forEach(function (key) {
@@ -146,26 +157,25 @@ export default {
// 如果url不包含tabBarList中的路径,根据mode判断跳转方式
switch (mode) {
case 'tabbar':
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。重置页面栈,仅保留 tabBar 页面
uni.switchTab({
url
});
break;
case 'redirectTo':
// 关闭当前页面,跳转到应用内的某个页面。
// 关闭当前页面,跳转到应用内的某个页面。替换栈顶页面,长度不变(上限10层)
uni.redirectTo({
url
});
break;
case 'reLaunch':
// 关闭所有页面,打开到应用内的某个页面。
// 关闭所有页面,打开到应用内的某个页面。清空栈后打开新页面,长度=1
uni.reLaunch({
url
});
break;
default:
// 保留当前页面,跳转到应用内的某个页面
console.log('保留当前页面,跳转到应用内的某个页面', url);
// 保留当前页面,跳转到应用内的某个页面,新增页面到栈顶,长度+1(上限10层)
uni.navigateTo({
url
});
@@ -200,6 +210,8 @@ export default {
// console.log(img_path)
path = img_path;
}
// 修复:去除 URL 中的双斜杠,避免微信 downloadFile 域名匹配失败
path = path.replace(/([^:]\/)\/+/g, "$1");
}
// path += '?t=' + parseInt(new Date().getTime() / 1000);
@@ -592,6 +604,31 @@ export default {
}
return cloneObj
},
/**
* 打开微信企业客服
* @param {Function} fallbackFunc 降级处理函数
*/
openWxWorkServiceChat(fallbackFunc) {
// #ifdef MP-WEIXIN
let wxworkConfig = store.state?.wxworkConfig
if (wxworkConfig?.enable && wxworkConfig?.contact_url) {
// 直接使用活码链接跳转
wx.navigateToMiniProgram({
appId: 'wxeb490c6f9b154ef9', // 是企业微信官方小程序的AppID(固定值, 由腾讯企业微信团队维护, 不需要修改,用于展示"添加企业微信联系人"的官方页面)
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig?.contact_url)}`,
success: () => {
},
fail: (err) => {
});
} else {
fallbackFunc && fallbackFunc();
}
// #endif
},
/**
* 自定义模板的跳转链接
* @param {Object} link
@@ -614,7 +651,6 @@ export default {
} else if (link.appid) {
// 跳转其他小程序
uni.navigateToMiniProgram({
appId: link.appid,
path: link.page
@@ -622,7 +658,6 @@ export default {
} else if (link.name == 'MOBILE' && !link.wap_url) {
// 拨打电话
uni.makePhoneCall({
phoneNumber: link.mobile,
success: (res) => {
@@ -655,6 +690,7 @@ export default {
// #endif
// #ifdef MP-WEIXIN
this.openWxWorkServiceChat(() => {
wx.openCustomerServiceChat({
extInfo: {
url: config.wxwork_url
@@ -665,6 +701,8 @@ export default {
sendMessagePath: 'this.sendMessagePath',
sendMessageImg: 'this.sendMessageImg'
});
});
// #endif
break;
case 'third':
@@ -792,13 +830,22 @@ export default {
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
return routes.length ? routes[routes.length - 1].route : '';
},
goBack(backUrl = '/pages/index/index') {
goBack(backUrl = INDEX_PAGE_URL) {
if (getCurrentPages().length == 1) {
this.redirectTo(backUrl);
} else {
uni.navigateBack();
}
},
/**
* 隐藏“返回首页/小房子”按钮
* 这个函数,用到页面show, onshow 的生命周期时
*/
hideHomeButton() {
// #ifdef MP-WEIXIN
wx.hideHomeButton();
// #endif
},
/**
*
* @param val 转化时间字符串 (转化时分秒)
@@ -857,7 +904,6 @@ export default {
uni.removeStorage({
key: 'source_member',
success: res => {
console.log('删除成功', res)
}
})
}
@@ -886,10 +932,8 @@ export default {
uni.requestSubscribeMessage({
tmplIds: res.data,
success: (res) => {
console.log("res", res)
},
fail: (res) => {
console.log('fail', res)
}
})
// #endif
@@ -898,10 +942,8 @@ export default {
my.requestSubscribeMessage({
entityIds: res.data,
success: res => {
console.log("res", res)
},
fail: res => {
console.log('fail', res)
}
});
// #endif
@@ -1148,5 +1190,73 @@ export default {
verifyMobile(mobile) {
var parse = /^\d{11}$/.test(mobile);
return parse;
},
/**
* 判断是否为快应用环境
* @returns {boolean} 是否为快应用
*/
isQuickApp() {
// #ifndef QUICKAPP-WEBVIEW
let _isQuickApp = false;
try {
const ua = navigator?.userAgent?.toLowerCase();
_isQuickApp = ua.indexOf('quickapp') !== -1;
if (!_isQuickApp) {
// 特殊说明由于网站是嵌入到快应用的Webview组件中<web>,UA中不会包含quickapp字符串,
// 但是会包含uniacid参数,所以这里通过uniacid参数来判断是否为快应用环境
_isQuickApp = ua.indexOf('uniacid=') !== -1;
}
} catch (e) { }
if (!_isQuickApp) {
const systemInfo = this.getDeviceInfo();
_isQuickApp = systemInfo?.platform === 'quickapp' || process?.env?.UNI_PLATFORM === 'quickapp';
}
return _isQuickApp;
// #endif
return true;
},
/**
* 判断是否为华为快应用环境
* @returns {boolean} 是否为华为快应用
*/
isHuaweiQuickApp() {
if (!this.isQuickApp()) return false;
// #ifndef QUICKAPP-WEBVIEW-HUAWEI
const systemInfo = this.getDeviceInfo();
return systemInfo.brand === 'HUAWEI' || systemInfo.manufacturer === 'HUAWEI' ||
(typeof qh !== 'undefined' && qh.platform === 'huawei');
// #endif
return true;
},
/**
* 蛇形命名转驼峰命名
* @param {string} str 蛇形命名字符串
* @returns {string} 驼峰命名字符串
*/
snakeToCamel(str) {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
},
/**
* 蛇形命名转驼峰命名(递归处理对象中的所有属性名)
* @param {Object} obj 包含蛇形命名字符串属性的对象
* @returns {Object} 驼峰命名字符串属性的对象
*/
snakeToCamelForObj(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (Array.isArray(obj)) return obj.map(this.snakeToCamelForObj.bind(this));
const newObj = {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const newKey = this.snakeToCamel(key);
newObj[newKey] = this.snakeToCamelForObj(obj[key]);
}
}
return newObj;
}
}

View File

@@ -55,7 +55,6 @@ let Weixin = function () {
typeof callback == 'function' && callback(res);
},
fail: (res) => {
console.log('获取收货地址 fail',res);
alert(JSON.stringify(res))
}
});

View File

@@ -0,0 +1,38 @@
/**
* 微信小程序图片长按识别二维码 mixin
* 自动为页面中的所有 image 组件添加 show-menu-by-longpress 属性
*/
export default {
data() {
return {
// 需要排除的图片类名(不需要长按识别的)
excludeImageClass: ['no-longpress', 'swiper-image', 'banner-image']
}
},
mounted() {
// #ifdef MP-WEIXIN
this.$nextTick(() => {
this.addLongpressToImages()
})
// #endif
},
methods: {
/**
* 为页面中的所有 image 添加长按识别支持
*/
addLongpressToImages() {
// 获取当前页面所有 image 组件
const images = document.querySelectorAll && document.querySelectorAll('image')
if (images) {
images.forEach(img => {
// 检查是否需要排除
const className = img.className || ''
const shouldExclude = this.excludeImageClass.some(cls => className.includes(cls))
if (!shouldExclude) {
img.setAttribute('show-menu-by-longpress', 'true')
}
})
}
}
}
}

View File

@@ -0,0 +1,143 @@
// 公共微信视频号样式
// CSS 变量
:root {
// 尺寸变量
--channel-play-btn-size: 80rpx;
--channel-play-btn-small-size: 60rpx;
--channel-play-btn-icon-size: 40rpx;
--channel-play-btn-icon-small-size: 30rpx;
--channel-border-radius: 12rpx;
--channel-stats-padding: 10rpx;
--channel-info-wrap-padding: 10rpx 0;
// 字体变量
--channel-name-font-size: 28rpx;
--video-title-font-size: 28rpx;
--video-title-small-font-size: 24rpx;
--video-stats-font-size: 24rpx;
--channel-stats-font-size: 20rpx;
// 颜色变量
--channel-name-color: #333;
--video-title-color: #333;
--video-title-small-color: #666;
--video-stats-color: #999;
--channel-stats-color: #fff;
--channel-border-color: #f0f0f0;
--channel-play-btn-bg: rgba(0, 0, 0, 0.4);
--channel-stats-bg: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
// 间距变量
--channel-name-margin-bottom: 4rpx;
--video-title-margin-bottom: 8rpx;
}
// 响应式设计
@media (max-width: 375px) {
:root {
--channel-play-btn-size: 70rpx;
--channel-play-btn-small-size: 50rpx;
--channel-play-btn-icon-size: 35rpx;
--channel-play-btn-icon-small-size: 25rpx;
--channel-name-font-size: 24rpx;
--video-title-font-size: 24rpx;
}
}
@media (min-width: 750px) {
:root {
--channel-play-btn-size: 90rpx;
--channel-play-btn-small-size: 70rpx;
--channel-play-btn-icon-size: 45rpx;
--channel-play-btn-icon-small-size: 35rpx;
--channel-name-font-size: 32rpx;
--video-title-font-size: 32rpx;
}
}
// 播放按钮样式
.channel-play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
width: var(--channel-play-btn-size);
height: var(--channel-play-btn-size);
background-color: var(--channel-play-btn-bg);
border-radius: 50%;
.play-icon {
width: var(--channel-play-btn-icon-size);
height: var(--channel-play-btn-icon-size);
}
}
// 小尺寸播放按钮(用于列表)
.channel-play-btn.small {
width: var(--channel-play-btn-small-size);
height: var(--channel-play-btn-small-size);
.play-icon {
width: var(--channel-play-btn-icon-small-size);
height: var(--channel-play-btn-icon-small-size);
}
}
// 视频统计信息
.channel-stats {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: var(--channel-stats-bg);
padding: var(--channel-stats-padding);
color: var(--channel-stats-color);
font-size: var(--channel-stats-font-size);
}
// 视频信息容器
.channel-info-wrap {
display: flex;
flex-direction: column;
flex: 1;
padding: var(--channel-info-wrap-padding);
.channel-name {
margin-bottom: var(--channel-name-margin-bottom);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: var(--channel-name-font-size);
font-weight: 500;
color: var(--channel-name-color);
}
.video-title {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: var(--video-title-small-font-size);
color: var(--video-title-small-color);
}
}
// 视频标题
.video-title {
font-size: var(--video-title-font-size);
font-weight: 500;
color: var(--video-title-color);
margin-bottom: var(--video-title-margin-bottom);
line-height: 1.4;
}
// 视频统计信息(非绝对定位版本)
.video-stats {
font-size: var(--video-stats-font-size);
color: var(--video-stats-color);
}

View File

@@ -3,16 +3,16 @@
<view class="article-wrap" :style="warpCss">
<view :class="['list-wrap', value.style]" :style="warpCss">
<!-- 自动垂直滚动 -->
<swiper class="auto-scroll-swiper" :style="swiperStyle" :vertical="swiperConfig.vertical !== false"
<swiper class="auto-scroll-swiper" :style="[swiperStyle]" :vertical="swiperConfig.vertical !== false"
:autoplay="swiperConfig.autoplay !== false" :circular="swiperConfig.circular !== false"
:interval="swiperConfig.interval || 3000" :duration="swiperConfig.duration || 500"
:display-multiple-items="safeDisplayMultipleItems">
<swiper-item v-for="(item, index) in list" :key="index" @click="toDetail(item)">
<swiper-item v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)">
<view class="swiper-item-content">
<view :class="['item', value.ornament.type]" :style="itemCss">
<view class="article-img">
<image class="cover-img" :src="$util.img(item.cover_img)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress/>
</view>
<view class="info-wrap">
<text class="title">{{ item.article_title }}</text>

View File

@@ -11,13 +11,13 @@
<view class="time" :style="{ color: value.timecolor }">
{{ audiotime }}
</view>
<view @click="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"
<view @tap.stop="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"
style="padding-top: 18rpx"></view>
</view>
<view class="fui-audio style3" :style="{ background: value.background }" v-else>
<!-- <audio src="/static/audio/bgm.mp3" controls loop></audio> -->
<view class="img">
<image :src="$util.img(value.imageUrl)"></image>
<image :src="$util.img(value.imageUrl)" show-menu-by-longpress></image>
</view>
<view class="content">
<view class="name" :style="{ color: value.textcolor }">{{ value.text }}</view>
@@ -30,7 +30,7 @@
<!-- {{audios[value.id].audiotime}} -->
{{ audiotime }}
</view>
<view @click="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"></view>
<view @tap.stop="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"></view>
</view>
</view>
</template>
@@ -98,9 +98,7 @@ export default {
uni.setStorageSync('audio_list', audio_list)
}
}
console.log(uni.getStorageSync('audio_list'))
var n = this;
// console.log(a)
a.onPlay(function () {
var e = setInterval(function () {
var i = a.currentTime / a.duration * 100 + "%", s = Math.floor(Math.ceil(a.currentTime) / 60), o = (Math.ceil(a.currentTime) % 60 / 100).toFixed(2).slice(-2), r = Math.ceil(a.currentTime);
@@ -113,13 +111,12 @@ export default {
var s = n.$util.img(n.value.audioUrl), o = n.audios[n.value.id].seconds || 0, r = 0, u = 1;
0 == u && a.onEnded(function (e) {
c[t].status = !1, n.status = !1, c[t].seconds = 0, console.log(c), n.audios = c;
c[t].status = !1, n.status = !1, c[t].seconds = 0, n.audios = c;
});
var c = n.audios;
c[t] || (c[t] = {}), a.paused && 0 == o ? (a.src = s, a.play(), 1 == u && (a.loop = !0),
c[t].status = !0, n.status = !0, n.pauseOther(t)) : a.paused && o > 0 ? (a.play(), 0 == r ? a.seek(o) : a.seek(0),
c[t].status = !0, n.status = !0, n.pauseOther(t)) : (a.pause(), c[t].status = !1, n.status = !1), n.audios = c;
console.log(n.audios)
},
pauseOther: function (e) {
var t = this;
@@ -190,18 +187,14 @@ export default {
var u = s + ":" + o, c = that.audios;
c[t].audiowidth = i, c[t].Time = e, c[t].audiotime = u, c[t].seconds = r
that.audios = c
// console.log(c)
console.log(that.audios[that.value.id].audiotime)
that.audiotime = that.audios[that.value.id].audiotime
that.audiowidth = that.audios[that.value.id].audiowidth
console.log(i)
that.lyg = i
}, 1e3);
});
this.audioContext.onEnded(() => {
console.log('播放结束');
this.status = 0
});
}

View File

@@ -10,12 +10,12 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix"></image>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress></image>
<view class="head-content" v-if="value.titleStyle.style == 'style-1'"
:style="{ color: value.titleStyle.textColor }">低至0元免费拿</view>
<view class="head-right"
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
@click="$util.redirectTo('/pages_promotion/bargain/list')">
@tap.stop="$util.redirectTo('/pages_promotion/bargain/list')">
<text>{{ value.titleStyle.more }}</text>
<text class="iconfont icon-right"></text>
</view>
@@ -23,12 +23,12 @@
<!-- 商品列表 -->
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.btnStyle.control">
@@ -41,7 +41,7 @@
<view class="bg">
<view class="curr" :style="{ width: progress(item) * 2 + 'rpx' }">
<image class="progress-bar" mode="widthFix"
:src="$util.img('public/uniapp/bargain/progress_bar_01.png')" />
:src="$util.img('public/uniapp/bargain/progress_bar_01.png')" show-menu-by-longpress/>
</view>
</view>
<view class="num" v-if="item.is_bargaining">
@@ -84,12 +84,12 @@
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" :lazy-load="true" />
@error="imageError(index)" :lazy-load="true" show-menu-by-longpress/>
<image class="bg"
v-if="value.saleStyle.control && value.template == 'horizontal-slide' && value.style != 'style-2'"
:src="$util.img('public/uniapp/bargain/bg.png')" mode="widthFix" />
@@ -130,11 +130,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" :lazy-load="true" />
@error="imageError(dataIndex)" :lazy-load="true" show-menu-by-longpress/>
<image class="bg"
v-if="value.saleStyle.control && value.template == 'horizontal-slide' && value.style != 'style-2'"
:src="$util.img('public/uniapp/bargain/bg.png')" mode="widthFix" />
@@ -365,7 +365,7 @@ export default {
</script>
<style lang="scss">
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -374,7 +374,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -2,7 +2,7 @@
<view data-component-name="diy-bottom-nav" v-if="tabBarList && tabBarList.list">
<view class="tab-bar" :style="{ backgroundColor: tabBarList.backgroundColor }">
<view class="tabbar-border"></view>
<view class="item" v-for="(item, index) in tabBarList.list" :key="item.id" @click="redirectTo(item.link)">
<view class="item" v-for="(item, index) in tabBarList.list" :key="item.id" @tap.stop="redirectTo(item.link)">
<view class="bd">
<block v-if="item.link.wap_url == '/pages_goods/cart'">
<view class="icon" v-if="tabBarList.type == 1 || tabBarList.type == 2"
@@ -145,7 +145,7 @@ export default {
let linkUrl = link.wap_url;
// 首页特殊处理
if (linkUrl === '/pages/index/index' && this.name === 'DIY_VIEW_INDEX') {
if (linkUrl === this.$util.INDEX_PAGE_URL && this.name === 'DIY_VIEW_INDEX') {
return true;
}

View File

@@ -1,17 +1,17 @@
<template>
<view data-component-name="diy-category-item" class="item-wrap" :class="type">
<block v-if="type == 'category' && category.child_list && category.child_list.length">
<view class="category-adv" v-if="category.image_adv" @click="diyRedirectTo(category.link_url)">
<image :src="$util.img(category.image_adv)" mode="widthFix" />
<view class="category-adv" v-if="category.image_adv" @tap.stop="diyRedirectTo(category.link_url)">
<image :src="$util.img(category.image_adv)" mode="widthFix" show-menu-by-longpress />
</view>
<block v-if="value.level == 2">
<view class="category-title">{{ category.category_name }}</view>
<view class="category-list">
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
@click="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
<view class="img-box">
<image :src="$util.img(one.image)" mode="widthFix" />
<image :src="$util.img(one.image)" mode="widthFix" show-menu-by-longpress />
</view>
<view class="name">{{ one.category_name }}</view>
</view>
@@ -23,9 +23,9 @@
<view class="category-title">{{ one.category_name }}</view>
<view class="category-list">
<view class="category-item" v-for="(two, twoIndex) in one.child_list" :key="twoIndex"
@click="$util.redirectTo('/pages_goods/list', { category_id: two.category_id })">
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: two.category_id })">
<view class="img-box">
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" />
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" show-menu-by-longpress />
</view>
<view class="name">{{ two.category_name }}</view>
</view>
@@ -44,22 +44,22 @@
:class="{ 'screen-category-4': value.template == 4 }" :scroll-with-animation="true"
:scroll-into-view="scrollIntoView">
<view class="item" id="category-2--1" :class="{ selected: categoryId == -1 }"
@click="selectCategory(-1)">全部</view>
@tap.stop="selectCategory(-1)">全部</view>
<view class="item" :id="'category-2-' + oneIndex"
:class="{ selected: categoryId == oneIndex }" @click="selectCategory(oneIndex)"
:class="{ selected: categoryId == oneIndex }" @tap.stop="selectCategory(oneIndex)"
v-for="(one, oneIndex) in category.child_list" :key="oneIndex">
{{ one.category_name }}
</view>
</scroll-view>
<view class="iconfont icon-unfold" @click="$refs.screenCategoryPopup.open()"></view>
<view class="iconfont icon-unfold" @tap.stop="$refs.screenCategoryPopup.open()"></view>
</view>
<uni-popup type="top" ref="screenCategoryPopup">
<view class="screen-category-popup" @click="$refs.screenCategoryPopup.close()">
<view class="screen-category-popup" @tap.stop="$refs.screenCategoryPopup.close()">
<scroll-view scroll-y="true" class="screen-category"
:class="{ 'screen-category-4': value.template == 4 }">
<view class="title">全部</view>
<view class="item" :class="{ selected: categoryId == oneIndex }"
@click="selectCategory(oneIndex)" v-for="(one, oneIndex) in category.child_list"
@tap.stop="selectCategory(oneIndex)" v-for="(one, oneIndex) in category.child_list"
:key="oneIndex">
{{ one.category_name }}
</view>
@@ -81,13 +81,13 @@
:data-template="value.template">
<block v-if="goodsList.length">
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
<view class="goods-img" @click="toDetail(item)">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" />
<view class="goods-img" @tap.stop="toDetail(item)">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
</view>
</view>
<view class="info-wrap">
<view class="name-wrap" @click="toDetail(item)">
<view class="name-wrap" @tap.stop="toDetail(item)">
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
</view>
<view class="price-wrap">
@@ -99,17 +99,14 @@
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</block>
<block v-else>
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
</block>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress />
</view>
</view>
<view class="footer-wrap">
@@ -120,27 +117,33 @@
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
<block v-if="item.is_virtual">
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
</block>
<block v-else>
<view v-if="item.goods_spec_format" class="color-base-bg select-sku" @click="selectSku(item)">
<view v-if="item.goods_spec_format" class="color-base-bg select-sku" @tap.stop="selectSku(item)">
<text>选规格</text>
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
</view>
<block v-else>
<block v-if="cartList['goods_' + item.goods_id]&&cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
<view class="num-action reduce" @click="reduce(item)">
<view class="num-action reduce" @tap.stop="reduce(item)">
<text class="iconfont icon-jian"></text>
</view>
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' + item.sku_id].num }}</view>
<view class="num-action" :id="'cart-num-' + index" @click="increase($event, item)">
<view class="num-action" :id="'cart-num-' + index" @tap.stop="increase($event, item)">
<text class="iconfont icon-jia"></text>
<view class="click-event"></view>
</view>
</block>
<view class="num-action" v-else :id="'cart-num-' + index" @click="increase($event, item, 0)">
<view class="num-action" v-else :id="'cart-num-' + index" @tap.stop="increase($event, item, 0)">
<text class="iconfont icon-jia"></text>
<view class="click-event"></view>
</view>
@@ -148,17 +151,17 @@
</block>
</view> -->
<!-- <view class="right-wrap" v-if="value.template == 3">
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
</view> -->
</view>
</view>
</view>
</block>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress />
</view>
<!-- <view class="end-tips" ref="endTips" v-if="last && (categoryId == -1 || !category.child_list || (category.child_list && categoryId == category.child_list.length - 1))">已经到底了~</view>
<view class="end-tips" ref="endTips" v-else @click="switchCategory('next')">
<view class="end-tips" ref="endTips" v-else @tap.stop="switchCategory('next')">
<text class="iconfont icon-xiangshangzhanhang"></text>
上滑查看下一分类
</view> -->
@@ -173,13 +176,13 @@
<view class="goods-list" :class="{ 'double-column': !isList, 'single-column': isList }"
:data-template="value.template">
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
<view class="goods-img" @click="toDetail(item)">
<view class="goods-img" @tap.stop="toDetail(item)">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
:lazy-load="true" />
:lazy-load="true" show-menu-by-longpress />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
</view>
<view class="info-wrap">
<view class="name-wrap" @click="toDetail(item)">
<view class="name-wrap" @tap.stop="toDetail(item)">
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
</view>
<view class="price-wrap">
@@ -191,17 +194,13 @@
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</block>
<block v-else>
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
</block>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix" show-menu-by-longpress />
</view>
</view>
<view class="footer-wrap">
@@ -212,32 +211,38 @@
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
<view class="right-wrap" v-if="value.template == 2">
<block v-if="item.is_virtual">
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
</block>
<block v-else>
<view v-if="item.goods_spec_format" class="color-base-bg select-sku"
@click="selectSku(item)">
@tap.stop="selectSku(item)">
<text>选规格</text>
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
</view>
<block v-else>
<block
v-if="cartList['goods_' + item.goods_id] && cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
<view class="num-action reduce" @click="reduce(item)">
<view class="num-action reduce" @tap.stop="reduce(item)">
<text class="iconfont icon-jian"></text>
</view>
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' +
item.sku_id].num }}</view>
<view class="num-action" :id="'cart-num-' + index"
@click="increase($event, item)">
@tap.stop="increase($event, item)">
<text class="iconfont icon-jia"></text>
<view class="click-event"></view>
</view>
</block>
<view class="num-action" v-else :id="'cart-num-' + index"
@click="increase($event, item, 0)">
@tap.stop="increase($event, item, 0)">
<text class="iconfont icon-jia"></text>
<view class="click-event"></view>
</view>
@@ -245,7 +250,7 @@
</block>
</view>
<view class="right-wrap" v-if="value.template == 3">
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
</view>
</view>
</view>
@@ -718,6 +723,15 @@ export default {
}
}
},
/**
* 跳转到商品详情
* @param {Object} item 商品信息
*/
sendToAIChat(item) {
this.$util.redirectTo('/pages_goods/detail', {
goods_id: item.goods_id
});
},
diyRedirectTo(link) {
if (!link) return;
this.$util.diyRedirectTo(JSON.parse(link));
@@ -1160,7 +1174,7 @@ export default {
.right-wrap {
display: flex;
align-items: center;
justify-content: end;
justify-content: flex-end;
.num {
width: auto;
@@ -1314,7 +1328,7 @@ export default {
}
}
/deep/ .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper-box {
border-radius: 0;
}
@@ -1348,7 +1362,7 @@ export default {
justify-content: center;
}
/deep/ .loading-layer {
::v-deep .loading-layer {
background: #fff !important;
}
@@ -1384,4 +1398,25 @@ export default {
color: var(--main-color);
}
}
.ai-chat-btn-wrap {
display: flex;
justify-content: flex-end;
margin-top: auto;
padding-top: 10rpx;
}
.ai-chat-btn {
background-color: #ff0000;
color: #fff;
padding: 12rpx 30rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 500;
line-height: 1.2;
text-align: center;
white-space: nowrap;
display: inline-block;
}
</style>

View File

@@ -3,9 +3,9 @@
:style="{ height: 'calc(100vh - ' + tabBarHeight + ')' }">
<!-- #ifdef MP-WEIXIN -->
<!-- <block v-if="value.template == 4">
<view class="search-box" v-if="value.search" @click="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
<view class="search-box" v-if="value.search" @tap.stop="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
<view class="search-content">
<input type="text" class="uni-input font-size-tag" maxlength="50" :placeholder="$lang('search')" confirm-type="search" @click.stop="onClickSearch()" @tap.stop="onClickSearch()" disabled="true" />
<input type="text" class="uni-input font-size-tag" maxlength="50" :placeholder="$lang('search')" confirm-type="search" @tap.stop="onClickSearch()" disabled="true" />
<text class="iconfont icon-sousuo3"></text>
</view>
</view>
@@ -13,61 +13,61 @@
</block> -->
<block v-if="value.template != 4">
<!-- <view :style="navbarInnerStyle">商品分类</view> -->
<view class="search-box" v-if="value.search" @click="onClickSearch()" @tap.stop="onClickSearch()"
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()"
:style="wxSearchHeight">
<view class="search-content">
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')"
confirm-type="search" @click.stop="onClickSearch()" @tap.stop="onClickSearch()"
confirm-type="search" @tap.stop="onClickSearch()"
disabled="true" />
<text class="iconfont icon-sousuo3"></text>
</view>
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
@click.stop.prevent="changeListStyle()"></view>
@tap.stop.prevent="changeListStyle()"></view>
</view>
</block>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="search-box" v-if="value.search" @click="onClickSearch()" @tap.stop="onClickSearch()">
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()">
<view class="search-content">
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')" confirm-type="search"
@click.stop="onClickSearch()" @tap.stop="onClickSearch()" disabled="true" />
@tap.stop="onClickSearch()" disabled="true" />
<text class="iconfont icon-sousuo3"></text>
</view>
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
@click.stop.prevent="changeListStyle()"></view>
@tap.stop.prevent="changeListStyle()"></view>
</view>
<!-- #endif -->
<view class="template-four wx" v-if="value.template == 4">
<scroll-view scroll-x="true" class="template-four-wrap" :scroll-with-animation="true"
:scroll-into-view="'category-one-' + oneCategorySelect" enable-flex="true">
<view class="category-item" :id="'category-one-' + index" v-for="(item, index) in templateFourData"
:key="index" :class="{ select: oneCategorySelect == index }" @click="templateFourOneFn(index)">
:key="index" :class="{ select: oneCategorySelect == index }" @tap.stop="templateFourOneFn(index)">
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
<image :src="$util.img(item.image)" mode="aspectFill" />
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress/>
</view>
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{ item.category_name }}
</view>
</view>
</scroll-view>
<view class="category-item-all" @click="$refs.templateFourPopup.open()">
<view class="category-item-all" @tap.stop="$refs.templateFourPopup.open()">
<view class="category-item-all-wrap">
<text class="text">展开</text>
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill"></image>
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill" show-menu-by-longpress></image>
</view>
</view>
<uni-popup type="top" ref="templateFourPopup" :top="uniPopupTop">
<view class="template-four-popup">
<scroll-view scroll-y="true" class="template-four-scroll" enable-flex="true">
<view class="item" :class="{ selected: oneCategorySelect == index }"
@click="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
@tap.stop="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
<image :src="$util.img(item.image)" mode="aspectFill"></image>
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress></image>
</view>
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{
item.category_name }}</view>
</view>
</scroll-view>
<view class="pack-up" @click="$refs.templateFourPopup.close()">
<view class="pack-up" @tap.stop="$refs.templateFourPopup.close()">
<text>点击收起</text>
<text class="iconfont icon-iconangledown-copy"></text>
</view>
@@ -83,7 +83,7 @@
{ select: select == index },
{ 'border-bottom': value.template == 4 && select + 1 === index },
{ 'border-top': value.template == 4 && select - 1 === index }
]" @click="switchOneCategory(index)">
]" @tap.stop="switchOneCategory(index)">
<view class="">{{ item.category_name }}</view>
</view>
</view>
@@ -119,18 +119,18 @@
</view>
</block>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="tips">暂时没有分类哦!</view>
</view>
</view>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="tips">暂时没有分类哦!</view>
</view>
<!-- <view class="cart-box" v-if="(value.template == 2 || value.template == 4) && value.quickBuy && storeToken && categoryTree && categoryTree.length">
<view class="left-wrap">
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation" @click="$util.redirectTo('/pages_goods/cart')">
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation" @tap.stop="$util.redirectTo('/pages_goods/cart')">
<text class="iconfont icon-ziyuan1"></text>
<view class="num" v-if="cartNumber">{{ cartNumber < 99 ? cartNumber : '99+' }}</view>
</view>
@@ -141,7 +141,7 @@
<text class="unit font-size-tag price-font">.{{ cartTotalMoney[1] ? cartTotalMoney[1] : '00' }}</text>
</view>
</view>
<view class="right-wrap"><button type="primary" class="settlement-btn" @click="settlement">去结算</button>
<view class="right-wrap"><button type="primary" class="settlement-btn" @tap.stop="settlement">去结算</button>
</view>
</view> -->
@@ -548,7 +548,7 @@ export default {
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.category-page-wrap {
width: 100vw;
// height: calc(100vh - var(--tab-bar-height, 0));
@@ -818,7 +818,7 @@ export default {
z-index: 2;
}
/deep/ .template-four {
::v-deep .template-four {
position: relative;
z-index: 1;

View File

@@ -0,0 +1,631 @@
<template>
<view class="channel-list-container" :style="[componentStyle, { '--row-count': value.rowCount }]">
<!-- 轮播模式 -->
<swiper v-if="value.showStyle == 'carousel' && carouselConfig.type != 'hide'" :indicator-dots="isIndicatorDots"
:autoplay="carouselConfig.autoplay || false" :interval="carouselConfig.interval || 3000"
:duration="carouselConfig.duration || 500" :circular="carouselConfig.circular || false"
:style="swiperHeight" class="channel-swiper">
<swiper-item v-for="(slide, slideIndex) in swiperSlides" :key="slideIndex">
<view :class="['swiper-slide-content', 'row1-of' + value.rowCount]">
<view v-for="(item, index) in slide" :key="index" :class="['channel-item', value.mode]">
<!-- 视频号视频卡片,轮播模式 -->
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
</view>
</view>
</swiper-item>
</swiper>
<!-- 固定布局模式 -->
<view v-else-if="value.showStyle == 'fixed'" :class="['channel-list', 'row1-of' + value.rowCount]">
<view v-for="(item, index) in value.list" :key="index" class="channel-item">
<!-- 视频号视频卡片,列表模式 -->
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
</view>
</view>
<!-- 其他布局模式(如滚动布局) -->
<scroll-view v-else
:class="['channel-nav', value.showStyle == 'fixed' ? 'fixed-layout' : 'singleSlide', 'row1-of' + value.rowCount]"
:scroll-x="true"
:scroll-y="false"
:enhanced="true"
:bounces="false">
<view class="uni-scroll-view-content">
<view v-for="(item, index) in value.list" :key="index"
:class="['channel-nav-item', value.mode, 'row1-of' + value.rowCount]">
<!-- 视频号视频卡片,滚动模式 -->
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
</view>
</view>
</scroll-view>
</view>
</template>
<script>
/**
* 微信视频号列表组件
* 支持多种布局模式,包括固定布局和滚动布局
* 可配置列数、视频高度、标题行数等属性
* 用于在页面中展示多个微信视频号视频
*/
import DiyMinx from './minx.js'
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
export default {
name: 'diy-channel-list',
props: {
/**
* 组件配置数据
* @type {Object}
* @default () => ({})
* @property {string} showStyle - 显示样式,可选值:carousel, fixed, singleSlide
* @property {number} rowCount - 每行显示的视频数量
* @property {Array} list - 视频列表数据
* @property {string} componentBgColor - 组件背景色
* @property {string} componentAngle - 组件圆角类型,可选值:round
* @property {number} topAroundRadius - 顶部圆角半径
* @property {number} bottomAroundRadius - 底部圆角半径
* @property {Object} ornament - 装饰效果配置
* @property {boolean} showViewCount - 是否显示播放量,可选值:true, false
* @property {number} titleLineClamp - 标题显示行数
* @property {string} aspectRatio - 视频比例,可选值:16:9, 3:4
* @property {boolean} showPlayBtn - 是否显示播放按钮
* @property {Object} coverStyle - 视频封面图样式
* @property {Object} playBtnStyle - 播放按钮样式
* @property {string} mode - 显示模式, 可选值:video, text, image, graphic
* @property {number} imageSize - 图片尺寸(仅在特定模式下使用)
* @property {number} pageCount - 每页显示的视频数量
* @property {Object} carousel - 轮播配置
* @property {string} carousel.type - 轮播类型,可选值:default, hide
* @property {boolean} carousel.autoplay - 是否自动播放,默认false
* @property {number} carousel.interval - 自动播放间隔,默认3000ms
* @property {number} carousel.duration - 切换动画时长,默认500ms
* @property {boolean} carousel.circular - 是否循环播放,默认false
*/
value: {
type: Object,
default: () => ({})
}
},
mixins: [DiyMinx],
data() {
return {
pageWidth: '', // 页面宽度
indicatorDots: false, // 是否显示轮播指示器
swiperCurrent: 0 // 当前轮播索引
}
},
created() {
// 组件创建时的逻辑
// 可以在这里进行初始化操作,如获取页面宽度等
},
mounted() {
// 组件挂载后添加鼠标拖拽滚动功能
if (!['fixed', 'carousel'].includes(this.value?.showStyle)) {
this.$nextTick(() => {
this.addMouseDragScroll();
});
}
},
watch: {
/**
* 组件刷新监听
* 当组件需要刷新时触发
* @param {*} newValue - 新值
*/
componentRefresh(newValue) {
// 监听组件刷新
// 可以在这里处理组件刷新时的逻辑
}
},
computed: {
carouselConfig() {
return this.value?.carousel || {
type: 'default',
autoplay: false,
interval: 3000,
duration: 500,
circular: true
}
},
/**
* 组件样式
* 根据配置动态生成样式字符串,包括背景色、圆角、阴影和边框
* @returns {string} 样式字符串
*/
componentStyle() {
const style = {};
// 背景色
if (this.value?.componentBgColor) {
style.backgroundColor = this.value?.componentBgColor;
}
// 圆角样式
if (this.value?.componentAngle == 'round') {
style.borderTopLeftRadius = (2 * this.value?.topAroundRadius) + 'rpx';
style.borderTopRightRadius = (2 * this.value?.topAroundRadius) + 'rpx';
style.borderBottomLeftRadius = (2 * this.value?.bottomAroundRadius) + 'rpx';
style.borderBottomRightRadius = (2 * this.value?.bottomAroundRadius) + 'rpx';
}
// 装饰效果:阴影
style.boxShadow = this.value?.ornament?.type == 'shadow' ? '0 0 10rpx ' + this.value?.ornament?.color : '';
// 装饰效果:边框
style.border = this.value?.ornament?.type == 'stroke' ? '2rpx solid ' + this.value?.ornament?.color : '';
return style;
},
/**
* 轮播高度
* 根据模式和配置计算轮播高度
* @returns {string} 轮播高度样式
*/
swiperHeight() {
let height = 0;
const pageCount = this.value?.pageCount || 1;
// 根据不同模式计算高度
if (this.value?.mode == 'graphic') {
height = (49 + this.value?.imageSize) * pageCount;
} else if (this.value?.mode == 'img') {
height = (22 + this.value?.imageSize) * pageCount;
} else if (this.value?.mode == 'text') {
height = 43 * pageCount;
} else { // 默认视频模式
if (this.value?.aspectRatio == '16:9') {
height = [250, 160, 140, 120][this.value.rowCount - 1];
} else {
height = [510, 280, 220, 180][this.value.rowCount - 1];
}
}
return 'height:' + (2 * height) + 'rpx';
},
/**
* 是否显示指示器
* 根据轮播配置和列表长度判断是否显示指示器
* @returns {boolean} 是否显示指示器
*/
isIndicatorDots() {
// 当轮播类型不是隐藏,且视频数量超过一页时显示指示器
const pageCount = this.value?.pageCount || 1;
return this.value?.carousel?.type != 'hide' && Math.ceil(this.value?.list?.length / (pageCount * this.value?.rowCount)) > 1
},
/**
* 轮播幻灯片数据
* 将视频列表分割成轮播所需的幻灯片数据
* @returns {Array} 轮播幻灯片数据
*/
swiperSlides() {
const slides = [];
const list = this.value?.list || [];
const pageSize = (this.value?.pageCount || 1) * (this.value?.rowCount || 1);
// 将列表数据分割成每页显示的数量
for (let i = 0; i < list.length; i += pageSize) {
slides.push(list.slice(i, i + pageSize));
}
return slides;
},
/**
* 视频封面样式
* 根据aspectRatio属性动态计算封面样式
* @returns {Object} 封面样式对象
*/
computedCoverStyle() {
const aspectRatio = this.value?.aspectRatio || '16:9';
let paddingTop = '56.25%'; // 默认 16:9 比例
if (aspectRatio === '3:4') {
paddingTop = '133.33%'; // 3:4 比例
}
return {
width: '100%',
height: '0',
paddingTop: paddingTop
};
}
},
methods: {
/**
* 播放视频
* 触发 video-play 事件,并在微信小程序中调用视频播放接口
* @param {Object} item - 视频数据对象
*/
async playVideo(item) {
await this.__$emitEvent({
eventName: 'video-play',
data: item,
promiseCallback: async (event, handler, awaitedResult) => {
if (!awaitedResult) return;
try {
// 发送视频被点击播放事件
this.$emit('channel-video-click-play', item);
// #ifdef MP-WEIXIN
// 在微信小程序环境中调用视频播放接口
await wechatChannelUtil.playVideo(item);
// #endif
// #ifndef MP-WEIXIN
if (item?.channelType === 'wechat') {
uni.showToast({
title: '视频号仅支持在微信小程序环境中播放',
icon: 'none',
duration: 2000
});
} else {
// 在非微信小程序环境中,直接触发事件
this.$emit('video-play', item);
}
// #endif
} catch (err) {
console.error('打开视频号失败', err);
}
}
})
},
/**
* 图片加载错误处理
* 当图片加载失败时,设置默认图片
* @param {number} index - 图片索引
*/
imgError(index) {
// 图片加载失败的处理逻辑
// 为失败的图片设置默认图片
const item = this.value.list[index];
if (item) {
// 使用默认图片替代加载失败的图片
// #ifdef MP-WEIXIN
item.coverUrl = wechatChannelConfig.video.defaultCoverUrl;
// #endif
}
},
/**
* 添加鼠标拖拽滚动功能
* 在Web环境中实现与微信小程序相同的拖拽滚动效果
*/
addMouseDragScroll() {
// 只在Web环境中添加
// #ifndef MP
let isDragging = false;
let startX = 0;
let startScrollLeft = 0;
let currentScrollElement = null;
// 查找最近的可滚动祖先元素
const findScrollableParent = (element) => {
while (element && element !== document) {
const style = window.getComputedStyle(element);
if (style.overflowX === 'auto' || style.overflowX === 'scroll') {
return element;
}
element = element.parentElement;
}
return null;
};
// 鼠标按下事件
const handleMouseDown = (e) => {
// 检查是否点击在组件内
if (this.$el.contains(e.target)) {
// 查找可滚动元素
currentScrollElement = findScrollableParent(e.target);
if (currentScrollElement) {
isDragging = true;
startX = e.pageX;
startScrollLeft = currentScrollElement.scrollLeft;
currentScrollElement.style.cursor = 'grabbing';
}
}
};
// 鼠标移动事件
const handleMouseMove = (e) => {
if (!isDragging || !currentScrollElement) return;
e.preventDefault();
const dx = e.pageX - startX;
currentScrollElement.scrollLeft = startScrollLeft - dx;
};
// 鼠标释放事件
const handleMouseUp = () => {
if (isDragging && currentScrollElement) {
currentScrollElement.style.cursor = 'grab';
}
isDragging = false;
currentScrollElement = null;
};
// 添加全局事件监听器
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
document.addEventListener('mouseleave', handleMouseUp);
// 组件销毁时移除事件监听器
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mouseleave', handleMouseUp);
});
// 为所有.channel-nav元素添加必要的样式
setTimeout(() => {
const channelNavs = document.querySelectorAll('.channel-nav');
channelNavs.forEach(element => {
element.style.overflowX = 'auto';
element.style.overflowY = 'hidden';
element.style.whiteSpace = 'nowrap';
element.style.width = '100%';
element.style.maxWidth = '100%';
element.style.cursor = 'grab';
element.style.userSelect = 'none'; // 防止文本选择
});
}, 100); // 延迟执行,确保DOM已完全渲染
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import './css/common-channel.scss';
.channel-list-container {
padding: 16px 16px 0px;
}
/**
* 列表布局样式
*/
.channel-list {
display: grid;
grid-template-columns: repeat(var(--row-count, 2), 1fr);
gap: 8px;
}
/**
* 导航布局样式
* 支持固定布局和滚动布局
*/
.channel-nav {
box-sizing: border-box;
padding: 16rpx;
// 确保在H5环境中可以水平滚动
overflow-x: auto;
white-space: nowrap;
// 隐藏滚动条但保留滚动功能
::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
// 启用触摸滚动和鼠标拖拽滚动
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
.uni-scroll-view-content {
/* #ifdef MP-WEIXIN */
display: flex;
flex-wrap: nowrap;
gap: 16rpx;
/* #endif */
/* #ifndef MP-WEIXIN */
display: grid;
grid-template-columns: repeat(var(--row-count, 2), 1fr);
gap: 16rpx;
/* #endif */
}
// 单滑动模式
&.singleSlide {
.uni-scroll-view-content {
display: flex;
flex-wrap: nowrap;
gap: 16rpx;
white-space: nowrap;
}
.channel-nav-item {
flex-shrink: 0;
width: 280rpx;
}
}
.channel-nav-item {
display: flex;
flex-direction: column;
box-sizing: border-box;
/* #ifdef MP-WEIXIN */
&.row1-of1 {
width: 100%;
}
&.row1-of2 {
width: calc(50% - 8rpx);
}
&.row1-of3 {
width: calc(33.333% - 10.67rpx);
}
&.row1-of4 {
width: calc(25% - 12rpx);
}
/* #endif */
}
// 1 列布局
&.row1-of1 {
.uni-scroll-view-content {
// #ifndef MP-WEIXIN
grid-template-columns: 1fr;
// #endif
}
}
// 2 列布局
&.row1-of2 {
.uni-scroll-view-content {
// #ifndef MP-WEIXIN
grid-template-columns: repeat(2, 1fr);
// #endif
}
}
// 3 列布局
&.row1-of3 {
.uni-scroll-view-content {
// #ifndef MP-WEIXIN
grid-template-columns: repeat(3, 1fr);
// #endif
}
}
// 4 列布局
&.row1-of4 {
.uni-scroll-view-content {
// #ifndef MP-WEIXIN
grid-template-columns: repeat(4, 1fr);
// #endif
}
}
}
/**
* 确保所有视频卡片高度一致
*/
.channel-item,
.channel-nav-item {
display: flex;
flex-direction: column;
}
/**
* 轮播样式
* 支持轮播模式的布局和样式
*/
.channel-swiper {
width: 100%;
box-sizing: border-box;
.swiper-slide-content {
display: grid;
grid-template-columns: repeat(var(--row-count, 2), 1fr);
gap: 16rpx;
box-sizing: border-box;
.channel-item {
display: flex;
flex-direction: column;
box-sizing: border-box;
}
// 轮播模式下的 1 列布局
&.row1-of1 {
grid-template-columns: 1fr;
}
// 轮播模式下的 2 列布局
&.row1-of2 {
grid-template-columns: repeat(2, 1fr);
}
// 轮播模式下的 3 列布局
&.row1-of3 {
grid-template-columns: repeat(3, 1fr);
}
// 轮播模式下的 4 列布局
&.row1-of4 {
grid-template-columns: repeat(4, 1fr);
}
}
}
/**
* 响应式调整
* 在小屏幕设备上调整布局和间距
*/
@media (max-width: 375px) {
.channel-list,
.channel-nav,
.channel-swiper .swiper-slide-content {
gap: 12rpx;
padding: 12rpx;
}
// 小屏幕上的单滑动模式
.channel-nav {
&.singleSlide {
.uni-scroll-view-content {
gap: 12rpx;
}
.channel-nav-item {
width: 240rpx;
}
}
}
}
/**
* 视频比例样式调整
* 根据不同的视频比例调整布局
*/
// // 3:4 比例的视频卡片样式
// .channel-video.ratio-3-4 {
// // 调整视频卡片的整体高度
// ::v-deep .video-cover-wrap {
// padding-top: 133.33%; // 3:4 比例
// }
// // 列表模式下的3:4比例调整
// &.list-mode {
// ::v-deep .video-cover-wrap {
// padding-top: 133.33%; // 3:4 比例
// }
// }
// }
// // 16:9 比例的视频卡片样式(默认)
// .channel-video.ratio-16-9 {
// // 保持默认的16:9比例
// ::v-deep .video-cover-wrap {
// padding-top: 56.25%; // 16:9 比例
// }
// // 列表模式下的16:9比例保持默认
// &.list-mode {
// ::v-deep .video-cover-wrap {
// padding-top: 56.25%; // 16:9 比例
// }
// }
// }</style>

View File

@@ -0,0 +1,488 @@
<template>
<view class="channel-video" :class="{ 'list-mode': listMode }">
<!-- 嵌入式视频播放 -->
<view v-if="canUseEmbedMode" class="embed-video-container">
<view class="video-cover-wrap" :style="[coverStyle]">
<!-- #ifdef MP-WEIXIN -->
<native-component>
<!-- 嵌入式视频播放组件 -->
<channel-video :feed-id="value.feedId" :finder-user-name="value.finderUserName"
:feed-token="value.feedToken" :auto-play="autoPlay">
</channel-video>
</native-component>
<!-- #endif -->
</view>
<view class="video-info">
<!-- 视频标题,支持多行显示控制 -->
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
</view>
<!-- 视频统计信息 -->
<view class="video-stats" v-if="showViewCount && value.showViewCount">{{ value.viewCount }}次观看</view>
</view>
</view>
<!-- 跳转式视频播放 -->
<view v-else @tap.stop="playVideo" class="video-container">
<view class="video-cover-wrap" :style="[coverStyle]">
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill" show-menu-by-longpress></image>
<view class="channel-play-btn" v-if="showPlayBtn" :style="[playBtnStyle]">
<view class="play-icon-css"></view>
</view>
<view class="video-stats-overlay" v-if="showViewCount && value.showViewCount">
{{ value.viewCount }}次观看
</view>
</view>
<view class="video-info">
<!-- 视频标题,支持多行显示控制 -->
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
</view>
<!-- 视频统计信息,非列表模式下显示 -->
<view class="video-stats" v-if="showViewCount && value.showViewCount && !listMode">{{ value.viewCount }}次观看</view>
</view>
</view>
</view>
</template>
<script>
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
/**
* 微信视频号视频卡片组件
* 支持嵌入式播放和跳转式播放两种模式
* 可配置列表模式、视频高度、标题行数等属性
*/
export default {
name: 'diy-channel-video',
props: {
/**
* 视频数据对象
* @type {Object}
* @required
* @property {string} feedId - 视频 feedId
* @property {string} feedToken - 视频 token
* @property {string} coverUrl - 视频封面图
* @property {string} videoTitle - 视频标题
* @property {number} viewCount - 观看次数
* @property {boolean} showViewCount - 是否显示观看次数
* @property {boolean} embedMode - 是否启用嵌入式播放
*/
value: {
type: Object,
required: true
},
/**
* 是否为列表模式
* @type {boolean}
* @default false
*/
listMode: {
type: Boolean,
default: false
},
/** 是否显示观看次数,由父组件可以整体配置 */
showViewCount: {
type: Boolean,
default: true
},
/**
* 标题显示行数
* @type {number}
* @default 1
*/
titleLineClamp: {
type: Number,
default: 1
},
/** 是否显示播放按钮 */
showPlayBtn: {
type: Boolean,
default: true
},
/**
* 视频封面图样式
* 采用 16:9 比例的响应式高度
*/
coverStyle: {
type: Object,
default: () => ({
width: '100%',
height: '0',
paddingTop: '56.25%' // 16:9 比例
})
},
/**
* 播放按钮样式
*/
playBtnStyle: {
type: Object,
default: () => ({
width: '60rpx',
height: '60rpx',
borderRadius: '30rpx',
backgroundColor: 'rgba(0, 0, 0, 0.6)'
})
},
/** 是否自动播放 */
autoPlay: {
type: Boolean,
default: false
},
},
computed: {
/**
* 是否支持嵌入式播放
* @returns {boolean}
*/
canUseEmbedMode() {
// #ifdef MP-WEIXIN
const enableEmbedMode = Boolean(this.value?.embedMode)
&& Boolean(this.value?.feedToken)
&& wechatChannelUtil.isEmbedModeSupported();
return enableEmbedMode;
// #endif
return false
}
},
methods: {
/**
* 播放视频
* 触发 video-play 事件,由父组件处理具体播放逻辑
*/
async playVideo() {
this.$emit('video-play', this.value);
}
}
};
</script>
<style scoped lang="scss">
@import './css/common-channel.scss';
/**
* 视频卡片容器样式
* 包含卡片基础样式、悬停效果和列表模式样式
*/
.channel-video {
position: relative;
background-color: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
height: 100%;
// 悬停效果
&:hover {
transform: translateY(-4rpx);
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
}
// 列表模式样式调整
&.list-mode {
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
.video-info {
padding: 14rpx;
.video-title {
font-size: 24rpx;
margin-bottom: 6rpx;
}
}
}
}
/**
* 视频容器样式
* 用于跳转式播放模式
*/
.video-container {
display: flex;
flex-direction: column;
height: 100%;
}
/**
* 嵌入式视频容器样式
* 与视频容器样式保持一致
*/
.embed-video-container {
display: flex;
flex-direction: column;
height: 100%;
}
/**
* 视频封面容器
*/
.video-cover-wrap {
position: relative;
width: 100%;
padding-top: 56.25%;
/* 16:9 比例 */
height: 0;
overflow: hidden;
border-radius: 12rpx 12rpx 0 0;
transition: transform 0.3s ease;
// 悬停时缩放效果
.channel-video:hover & {
transform: scale(1.02);
}
// 列表模式下的边框圆角调整
.channel-video.list-mode & {
border-radius: 10rpx 10rpx 0 0;
}
}
/**
* 视频封面图片
* 绝对定位填充整个容器
*/
.video-cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
/**
* 视频统计信息遮罩
* 显示在视频封面底部
*/
.video-stats-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
padding: 12rpx 16rpx;
color: #fff;
font-size: 20rpx;
text-align: right;
font-weight: 500;
transition: opacity 0.3s ease;
// 悬停时的透明度变化
.channel-video:hover & {
opacity: 1;
}
.channel-video:not(:hover) & {
opacity: 0.8;
}
}
/**
* 视频信息区域
* 包含标题和统计信息
*/
.video-info {
padding: 16rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: #fff;
transition: background-color 0.3s ease;
// 悬停时的背景色变化
.channel-video:hover & {
background-color: #fafafa;
}
}
/**
* 视频标题
* 支持多行显示控制和渐变遮罩效果
*/
.video-title {
font-size: 28rpx;
font-weight: 600;
color: #222;
margin-bottom: 10rpx;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: var(--title-line-clamp, 2);
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.3s ease;
position: relative;
word-break: break-all;
word-wrap: break-word;
white-space: normal;
// 悬停时的颜色变化
.channel-video:hover & {
color: #000;
}
// 列表模式下的样式调整
.channel-video.list-mode & {
-webkit-line-clamp: var(--title-line-clamp, 1);
font-size: 24rpx;
margin-bottom: 6rpx;
font-weight: 500;
color: #333;
line-height: 1.3;
}
// 添加渐变遮罩效果,让过长的文字有柔和的结束
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 40rpx;
height: 1.4em;
background: linear-gradient(to right, transparent, #fff 90%);
pointer-events: none;
}
// 列表模式下的遮罩高度调整
.channel-video.list-mode &::after {
height: 1.3em;
}
// 根据行数调整遮罩高度
&[style*="--title-line-clamp: 1"]::after {
height: 1.4em;
}
&[style*="--title-line-clamp: 3"]::after {
height: 4.2em;
}
&[style*="--title-line-clamp: 4"]::after {
height: 5.6em;
}
// 列表模式下的多行遮罩高度调整
.channel-video.list-mode &[style*="--title-line-clamp: 1"]::after {
height: 1.3em;
}
.channel-video.list-mode &[style*="--title-line-clamp: 3"]::after {
height: 3.9em;
}
.channel-video.list-mode &[style*="--title-line-clamp: 4"]::after {
height: 5.2em;
}
}
/**
* 视频统计信息
* 显示观看次数等数据
*/
.video-stats {
font-size: 22rpx;
color: #999;
margin-top: 6rpx;
transition: color 0.3s ease;
// 悬停时的颜色变化
.channel-video:hover & {
color: #666;
}
}
/**
* 列表模式下的播放按钮样式
* 更小的尺寸和悬停效果
*/
.channel-video.list-mode .channel-play-btn {
width: 50rpx;
height: 50rpx;
background-color: rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
// 悬停效果
&:hover {
background-color: rgba(0, 0, 0, 0.7);
transform: scale(1.1);
}
// 播放图标尺寸
.play-icon {
width: 25rpx;
height: 25rpx;
}
}
/**
* 通用播放按钮样式优化
* 添加悬停效果
*/
.channel-play-btn {
transition: all 0.3s ease;
// 悬停效果
&:hover {
background-color: rgba(0, 0, 0, 0.6);
transform: scale(1.05);
}
}
/**
* 纯 CSS 播放按钮图标
*/
.play-icon-css {
width: 0;
height: 0;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
border-left: 18rpx solid #fff;
margin-left: 4rpx;
transition: transform 0.3s ease;
// 悬停时的缩放效果
.channel-play-btn:hover & {
transform: scale(1.1);
}
// 列表模式下的尺寸调整
.channel-video.list-mode & {
border-top: 10rpx solid transparent;
border-bottom: 10rpx solid transparent;
border-left: 15rpx solid #fff;
margin-left: 3rpx;
}
}
/**
* 原生组件样式控制
* 确保 native-component 和 channel-video 正确填充容器
*/
native-component {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
/**
* channel-video 组件样式控制
* 避免受到 wx-channel-video 全局样式的影响
*/
channel-video {
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
max-width: 100%;
max-height: 100%;
object-fit: cover;
}
</style>

View File

@@ -16,7 +16,7 @@
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style1-bg.png') + ')',
marginRight: couponItemHeight + 'px',
marginLeft: couponItemHeight + 'px'
}" @click="couponAction(item, index)">
}" @tap.stop="couponAction(item, index)">
<view class="coupon-info">
<view class="coupon-num" :style="{ color: value.moneyColor }"
@@ -52,7 +52,7 @@
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_bg1.png') + ')',
marginRight: couponItemHeight + 'px',
marginLeft: couponItemHeight + 'px'
}" @click="couponAction(item, index)">
}" @tap.stop="couponAction(item, index)">
<view class="coupon-info">
<view class="coupon-num" :style="{ color: value.moneyColor }"
v-if="!parseInt(item.discount)">
@@ -87,7 +87,7 @@
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_shu.png') + ')',
marginRight: couponItemHeight + 'px',
marginLeft: couponItemHeight + 'px'
}" @click="couponAction(item, index)">
}" @tap.stop="couponAction(item, index)">
<view class="coupon-num" :style="{ color: value.moneyColor }"
v-if="!parseInt(item.discount)">
<text class="font-size-tag coupon-sign">¥</text>
@@ -124,7 +124,7 @@
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style4_bg.png') + ')',
marginRight: couponItemHeight + 'px',
marginLeft: couponItemHeight + 'px'
}" @click="couponAction(item, index)">
}" @tap.stop="couponAction(item, index)">
<view class="coupon-info">
<view class="coupon-num" :style="{ color: value.moneyColor }"
v-if="!parseInt(item.discount)">
@@ -153,7 +153,7 @@
<view class="coupon-all">
<view class="coupon-box">
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
@click="couponAction(item, index)">
@tap.stop="couponAction(item, index)">
<image :src="$util.img('public/uniapp/coupon/style5_bg.png')"></image>
<view class="coupon">
<view class="coupon-info">
@@ -199,7 +199,7 @@
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-1.png') + ')',
marginRight: couponItemHeight + 'px',
marginLeft: couponItemHeight + 'px'
}" @click="couponAction(item, index)">
}" @tap.stop="couponAction(item, index)">
<view class="coupon-content">
<view class="price-wrap">
<text class="price" :style="{ color: value.moneyColor }">{{ (item.discount == '0.00'
@@ -229,7 +229,7 @@
<text class="limit" :style="{ color: value.limitColor }" v-else>无门槛使用</text>
</view>
<div v-if="computedCouponList.length <= 2" @click="$util.redirectTo('/pages_goods/category')"
<div v-if="computedCouponList.length <= 2" @tap.stop="$util.redirectTo('/pages_goods/category')"
class="coupon coupon-null" :style="{
color: value.moneyColor,
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-2.png') + ')',
@@ -250,7 +250,7 @@
<scroll-view class="coupon-style-seven" scroll-x="true">
<view class="wrap">
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
@click="couponAction(item, index)">
@tap.stop="couponAction(item, index)">
<image :src="$util.img('public/uniapp/coupon/style7_bg.png')"></image>
<view class="coupon">
<view class="coupon-info">
@@ -454,7 +454,7 @@ export default {
}
});
} else {
this.$refs.login.open('/pages/index/index');
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
this.couponBtnSwitch = false;
}
},
@@ -488,7 +488,7 @@ export default {
}
// 风格一
/deep/.coupon-style-one {
::v-deep .coupon-style-one {
height: 110rpx;
.coupon-item-box {
@@ -534,7 +534,7 @@ export default {
}
// 风格二
/deep/.coupon-style-two {
::v-deep .coupon-style-two {
height: 96rpx;
.coupon-item-box {
@@ -591,7 +591,7 @@ export default {
background-size: 100% 100%;
}
/deep/.coupon-style-three {
::v-deep .coupon-style-three {
height: 284rpx;
padding: 20rpx;
box-sizing: border-box;
@@ -638,7 +638,7 @@ export default {
}
// 风格四
/deep/.coupon-style-four {
::v-deep .coupon-style-four {
height: 108rpx;
.coupon-item-box {
@@ -997,7 +997,7 @@ export default {
}
//风格七
/deep/ .coupon-style-seven {
::v-deep .coupon-style-seven {
.wrap {
display: flex;
}
@@ -1075,7 +1075,7 @@ export default {
}
}
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -1084,7 +1084,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -90,8 +90,7 @@ export default {
// 页面跳转
redirectTo(item) {
if (!item.wap_url || this.$util.getCurrRoute() != 'pages/member/index' || this.storeToken) {
console.log(item);
if (!item.wap_url || this.$util.getCurrRoute() != this.$util.MEMBER_PAGE_URL || this.storeToken) {
this.$util.diyRedirectTo(item);
} else {
this.$refs.login.open(item.wap_url);

View File

@@ -1,11 +1,11 @@
<template>
<view data-component-name="diy-fenxiao-goods-list" class="diy-fenxiao" v-if="list.length"
:class="['goods-list', value.template, value.style]" :style="goodsListWarpCss">
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)" />
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)" show-menu-by-longpress/>
</view>
<view class="info-wrap"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">
@@ -32,14 +32,14 @@
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
}" @click.stop="followGoods(item, index)">
}" @tap.stop="followGoods(item, index)">
关注
</view>
<view class="sale-btn" v-if="value.btnStyle.control && item.is_collect == 1" :style="{
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
}" @click.stop="delFollowTip(item, index)">
}" @tap.stop="delFollowTip(item, index)">
取消关注
</view>
</view>
@@ -75,7 +75,7 @@ export default {
this.currentRoute = '/' + currentPage.route;
if (!this.storeToken) {
this.$util.redirectTo(
'/pages_tool/login/login',
this.$util.LOGIN_PAGE_URL,
{
back: this.currentRoute
},

View File

@@ -3,10 +3,10 @@
:class="{ left_top: value.bottomPosition == 1, right_top: value.bottomPosition == 2, left_bottom: value.bottomPosition == 3, right_bottom: value.bottomPosition == 4 }"
:style="style">
<block v-for="(item, index) in value.list" :key="index">
<view class="button-box" @click="$util.diyRedirectTo(item.link)"
<view class="button-box" @tap.stop="$util.diyRedirectTo(item.link)"
:style="{ width: value.imageSize + 'px', height: value.imageSize + 'px', fontSize: value.imageSize + 'px' }">
<image v-if="!item.iconType || item.iconType == 'img'" :src="$util.img(item.imageUrl)" mode="aspectFit"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-else-if="item.iconType && item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"></diy-icon>
</view>

View File

@@ -33,7 +33,7 @@
</view>
</view>
</view>
<view @click="submitform" class="fui-btn btn-danger block mtop">提交信息</view>
<view @tap.stop="submitform" class="fui-btn btn-danger block mtop">提交信息</view>
</view>
</template>

View File

@@ -8,8 +8,8 @@
<view class="ul-wrap">
<view class="li-item" v-for="(item, index) in list" :key="index">
<image class="brand-pic" :src="$util.img(item.image_url)" mode="aspectFit"
@click="handlerClick(item)" @tap="handlerClick(item)" @error="imgError(index)"
:style="itemCss" />
@tap.stop="handlerClick(item)" @error="imgError(index)"
:style="itemCss" show-menu-by-longpress/>
</view>
</view>
</view>

View File

@@ -2,13 +2,14 @@
<x-skeleton data-component-name="diy-goods-list" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
<view :class="['goods-list', goodsValue.template, goodsValue.style]" :style="goodsListWarpCss">
<template v-if="goodsValue.template != 'horizontal-slide'">
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
@tap="handlerClick(item)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
<view class="goods-img-wrap">
<image class="goods-img"
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
mode="widthFix" @error="imgError(index)"
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }" />
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }"
show-menu-by-longpress />
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -48,9 +49,9 @@
<view class="member-price"
v-if="(item.member_price && item.member_price == showPrice(item)) || item.promotion_type == 1">
<image v-if="item.member_price && item.member_price == showPrice(item)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="item.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(item)"
class="delete-price price-font"
@@ -70,7 +71,7 @@
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
@@ -79,7 +80,7 @@
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
@@ -90,7 +91,7 @@
fontWeight: goodsValue.btnStyle.theme == 'diy' ? (goodsValue.btnStyle.fontWeight ? 'bold' : 'normal') : '',
padding: goodsValue.btnStyle.theme == 'diy' ? '0 ' + goodsValue.btnStyle.padding * 2 + 'rpx' : ''
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
{{ goodsValue.btnStyle.text }}
<view class="click-event"></view>
<!-- <text class="cart-num" v-if="cartList['goods_' + item.goods_id]">{{ cartList['goods_' + item.goods_id].num }}</text> -->
@@ -100,7 +101,7 @@
<view v-else-if="goodsValue.btnStyle.style == 'icon-diy'" :style="{
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
<view class="click-event"></view>
<diy-icon :icon="goodsValue.btnStyle.iconDiy.icon"
:value="goodsValue.btnStyle.iconDiy.style ? goodsValue.btnStyle.iconDiy.style : null"></diy-icon>
@@ -112,12 +113,12 @@
</template>
<scroll-view v-if="goodsValue.template == 'horizontal-slide' && goodsValue.slideMode == 'scroll'"
class="scroll" :scroll-x="true">
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
@tap="handlerClick(item)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
<view class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
:lazy-load="true" />
:lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -156,9 +157,9 @@
<view class="member-price"
v-if="(item.member_price && item.member_price == showPrice(item)) || item.promotion_type == 1">
<image v-if="item.member_price && item.member_price == showPrice(item)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="item.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(item)"
class="delete-price price-font"
@@ -179,12 +180,12 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="goods-item" v-for="(dataItem, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="handlerClick(dataItem)" @tap="handlerClick(dataItem)"
@tap.stop="handlerClick(dataItem)"
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
<view class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
@error="imgError(dataIndex)" :lazy-load="true" />
@error="imgError(dataIndex)" :lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="dataItem.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -226,9 +227,9 @@
v-if="(dataItem.member_price && dataItem.member_price == showPrice(dataItem)) || dataItem.promotion_type == 1">
<image
v-if="dataItem.member_price && dataItem.member_price == showPrice(dataItem)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="dataItem.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(dataItem)"
class="delete-price"

View File

@@ -13,11 +13,11 @@
<swiper-item v-for="(item, index) in page" :key="index"
:class="['swiper-item', [list[index].length / 3] >= 1 && 'flex-between']">
<view class="goods-item" v-for="(dataItem, dataIndex) in list[index]" :key="dataIndex"
@click="toDetail(dataItem)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(dataItem)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
<div class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
@error="imgError(index, dataIndex)" :lazy-load="true" />
@error="imgError(index, dataIndex)" :lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="dataItem.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -33,7 +33,7 @@
<view class="label-wrap" v-if="goodsValue.labelStyle.support"
:style="{ background: goodsValue.labelStyle.bgColor, color: goodsValue.labelStyle.color }">
<image :src="$util.img('app/component/view/goods_recommend/img/label.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress />
<text>{{ goodsValue.labelStyle.title }}</text>
</view>
<view class="discount-price">

View File

@@ -9,13 +9,13 @@
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
:key="index"
v-if="index >= [(numItem) * (value.pageCount * value.rowCount)] && index < [(numItem + 1) * (value.pageCount * value.rowCount)]"
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
:key="index"
v-if="index >= [(numItem - 1) * (value.pageCount * value.rowCount)] && index < [numItem * (value.pageCount * value.rowCount)]"
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
<!-- #endif -->
<view class="graphic-img" v-if="value.mode != 'text'"
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
@@ -23,7 +23,7 @@
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', borderRadius: value.aroundRadius * 2 + 'rpx' }"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"
@@ -62,14 +62,14 @@
<!-- #endif -->
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list" :key="index"
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
<view class="graphic-img" v-if="value.mode != 'text'"
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
<image v-if="item.iconType == 'img'"
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', borderRadius: value.aroundRadius * 2 + 'rpx' }"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', width: '100%', height: '100%' }"></diy-icon>
@@ -159,12 +159,11 @@ export default {
redirectTo(link) {
if (link.wap_url) {
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
this.$refs.login.open(link.wap_url);
return;
}
}
console.log(link)
this.$util.diyRedirectTo(link);
},
swiperChange(e) {

View File

@@ -251,6 +251,16 @@
<diy-icon :value="item"></diy-icon>
</template>
<template v-if="item.componentName == 'Tab'">
<!-- Tab 组件 -->
<diy-tab :value="item" :diyGlobal="diyGlobalData"></diy-tab>
</template>
<template v-if="['ChannelList', 'WechatChannel'].includes(item.componentName)">
<!-- 视频号列表 -->
<diy-channel-list :value="item"></diy-channel-list>
</template>
<!-- 自定义扩展组件 -->
<diy-comp-extend :value="item"></diy-comp-extend>
</view>
@@ -260,8 +270,9 @@
<script>
// 组件组展示
import DiyMinx from './minx.js'
export default {
components: {},
name: 'diy-group',
props: {
diyData: {
type: Object
@@ -306,7 +317,12 @@ export default {
this.diyGlobalData.value.forEach((item, index) => {
item.pageStyle = '';
// 给每个组件增加位置属性,用于定位,搜索、分类导航等定位
// 如果是Tab组件内部的子组件,添加tabIndex前缀避免moduleIndex冲突
if (this.diyData.tabIndex !== undefined) {
item.moduleIndex = `${this.diyData.tabIndex}-${index + 1}`;
} else {
item.moduleIndex = index + 1;
}
// 特殊处理搜索框 当显示位置为滚动至顶部固定时,只设置背景颜色
if (item.componentName == 'Search' && item.positionWay == 'fixed') {
@@ -341,6 +357,7 @@ export default {
}
});
} else data = this.setPagestyle;
return data;
}
},

View File

@@ -2,12 +2,12 @@
<x-skeleton data-component-name="diy-groupbuy" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
<view class="diy-groupbuy" :class="[value.template, value.style]" :style="warpCss">
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)">
@error="imageError(index)" show-menu-by-longpress>
</image>
</view>
<view class="content"
@@ -39,12 +39,12 @@
</template>
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
<image class="bg" v-if="value.saleStyle.control"
:src="$util.img('public/uniapp/groupbuy/bg.png')" mode="widthFix" />
<view class="num" v-if="value.saleStyle.control"
@@ -81,11 +81,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
<image class="bg" v-if="value.saleStyle.control"
:src="$util.img('public/uniapp/groupbuy/bg.png')" mode="widthFix" />
<view class="num" v-if="value.saleStyle.control"
@@ -274,7 +274,7 @@ export default {
</script>
<style lang="scss">
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -283,7 +283,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -2,14 +2,14 @@
<view data-component-name="diy-hot-area" :style="hotAreaWarp" class="hot-area-box">
<view class="simple-graph-wrap">
<image :style="{ height: value.imgHeight }" :src="$util.img(value.imageUrl)" mode="widthFix"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<!-- 热区功能 -->
<view class="heat-map" v-for="(mapItem, mapIndex) in value.heatMapData" :key="mapIndex" :style="{
width: mapItem.width + '%',
height: mapItem.height + '%',
left: mapItem.left + '%',
top: mapItem.top + '%'
}" @click.stop="$util.diyRedirectTo(mapItem.link)"></view>
}" @tap.stop="$util.diyRedirectTo(mapItem.link)"></view>
</view>
</view>
</template>

View File

@@ -15,12 +15,12 @@
'width': (item.imgWidth / 2 + 'rpx') + ';',
'height': (item.imgHeight / 2 + 'rpx') + ';'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
:show-menu-by-longpress="true" @tap="redirectTo(item.link)"></image>
show-menu-by-longpress @tap.stop="redirectTo(item.link)"></image>
<image v-else :style="{
'width': (item.imgWidth / 2 + 'rpx') + ';',
'height': (item.imgHeight / 2 + 'rpx') + ';'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
:show-menu-by-longpress="true" @tap="previewImg(item.imageUrl)"></image>
show-menu-by-longpress @tap.stop="previewImg(item.imageUrl)"></image>
</view>
<!-- 文字部分 -->
@@ -103,20 +103,9 @@ export default {
}
},
methods: {
// 预览图片
previewImg(imageUrl) {
uni.previewImage({
current: 0,
urls: [this.$util.img(imageUrl)],
success: (res) => { },
fail: (res) => { },
complete: (res) => { }
});
},
// 页面跳转
redirectTo(link) {
if (!link.wap_url || this.$util.getCurrRoute() != 'pages/member/index' || this.storeToken) {
if (!link.wap_url || this.$util.getCurrRoute() != this.$util.MEMBER_PAGE_URL || this.storeToken) {
this.$util.diyRedirectTo(link);
} else {
this.$refs.login.open(link.wap_url);

View File

@@ -2,10 +2,9 @@
<view data-component-name="diy-img-ads" class="single-graph">
<view :style="imgAdsMarginWarp" class="swiper-box">
<block v-if="imgAdsValue.list.length == 1">
<view class="simple-graph-wrap" :style="imgAdsSwiper" @click="handlerClick(imgAdsValue.list[0].link)"
@tap="handlerClick(imgAdsValue.list[0].link)">
<view class="simple-graph-wrap" :style="imgAdsSwiper" @tap.stop="handlerClick(imgAdsValue.list[0].link)">
<image :style="{ height: imgAdsValue.list[0].imgHeight }"
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" :show-menu-by-longpress="true" />
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" show-menu-by-longpress />
</view>
</block>
<swiper v-else class="swiper" :style="{ height: swiperHeight }" :class="{
@@ -16,10 +15,10 @@
indicator-color="rgba(130, 130, 130, .5)" :indicator-active-color="imgAdsValue.indicatorColor"
@change="swiperChange">
<swiper-item class="swiper-item" :style="imgAdsSwiper" v-for="(item, index) in imgAdsValue.list"
:key="index" v-if="item.imageUrl" @click="handlerClick(item.link)" @tap="handlerClick(item.link)">
:key="index" v-if="item.imageUrl" @tap.stop="handlerClick(item.link)">
<view class="item" :style="imgAdsSwiper + 'height: ' + item.imgHeight">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
</view>
</swiper-item>
</swiper>
@@ -280,39 +279,39 @@ export default {
}
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
/deep/.uni-scroll-view::-webkit-scrollbar {
::v-deep .uni-scroll-view::-webkit-scrollbar {
display: none;
}
.swiper /deep/ .uni-swiper-dots-horizontal {
.swiper ::v-deep .uni-swiper-dots-horizontal {
bottom: 25rpx;
}
.swiper-left /deep/ .uni-swiper-dots-horizontal {
.swiper-left ::v-deep .uni-swiper-dots-horizontal {
left: 40rpx;
transform: translate(0);
}
.swiper-right /deep/ .uni-swiper-dots-horizontal {
.swiper-right ::v-deep .uni-swiper-dots-horizontal {
right: 40rpx;
display: flex;
justify-content: flex-end;
transform: translate(0);
}
.carousel-angle /deep/ .uni-swiper-dots-horizontal .uni-swiper-dot {
.carousel-angle ::v-deep .uni-swiper-dots-horizontal .uni-swiper-dot {
width: 24rpx;
border-radius: 0;
height: 8rpx;
}
.swiper.ns-indicator-dots /deep/ .uni-swiper-dot {
.swiper.ns-indicator-dots ::v-deep .uni-swiper-dot {
width: 18rpx;
height: 6rpx;
border-radius: 4rpx;
}
.swiper.ns-indicator-dots /deep/ .uni-swiper-dot-active {
.swiper.ns-indicator-dots ::v-deep .uni-swiper-dot-active {
width: 36rpx;
}
</style>

View File

@@ -8,7 +8,7 @@
:style="{ background: value.backgroundColor ? value.backgroundColor : '', width: 'calc(100% - 48rpx)' }"
@touchmove.stop>
<view class="item" :id="'a' + index" v-for="(item, index) in cateList" :key="index"
@click="changePageIndex(index)" :class="{ fill: value.styleType == 'fill' }"
@tap.stop="changePageIndex(index)" :class="{ fill: value.styleType == 'fill' }"
:style="{ background: index == pageIndex && value.styleType == 'fill' ? value.selectColor : '' }">
<view class="text-con" :class="index == pageIndex ? 'active' : ''" :style="{
color: index == pageIndex ? '' : value.noColor
@@ -25,13 +25,13 @@
</view>
</scroll-view>
<text class="iconfont icon-unfold unfold-arrows" :style="{ color: value.moreColor }"
@click="unfoldMenu"></text>
@tap.stop="unfoldMenu"></text>
</view>
<uni-popup ref="navTopCategoryPop" type="top" :top="uniPopTop">
<view class="nav-topcategory-pop">
<text v-for="(item, index) in cateList" :key="index"
:class="['category-item', { 'color-base-text color-base-border active': pageIndex == index }]"
@click="changePageIndex(index)">
@tap.stop="changePageIndex(index)">
{{ item.short_name ? item.short_name : item.category_name }}
</text>
</view>
@@ -55,12 +55,12 @@
<view class="twoCategory min" v-if="twoCategorylist.length <= 5">
<view class="twoCategory-page">
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
:key="index" @click="toCateGoodsList(item.category_id_2, 2)">
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="$util.img(item.image)" v-if="item.image"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.getDefaultImage().goods" v-else
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -70,12 +70,12 @@
v-if="twoCategorylist.length > 5 && twoCategorylist.length <= 10">
<view class="twoCategory-page">
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
:key="index" @click="toCateGoodsList(item.category_id_2, 2)">
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="$util.img(item.image)" v-if="item.image"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.getDefaultImage().goods" v-else
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -86,9 +86,9 @@
<swiper-item class="twoCategory-page" v-for="page in maxPage" :key="page">
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
:key="index" v-if="index >= (page - 1) * 10 && index < page * 10"
@click="toCateGoodsList(item.category_id_2, 2)">
@tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="item.image" mode="aspectFill" />
<image :src="item.image" mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -104,19 +104,19 @@
<!-- 分类广告 -->
<image class="category_adv" v-if="cateList[pageIndex].image_adv"
:src="$util.img(cateList[pageIndex].image_adv)" mode="widthFix" />
:src="$util.img(cateList[pageIndex].image_adv)" mode="widthFix" show-menu-by-longpress/>
<view class="goods-list double-column" v-if="goodsList[pageIndex].list.length">
<view class="goods-item" v-for="(item, index) in goodsList[pageIndex].list"
:key="index" @click="toDetail(item)">
:key="index" @tap.stop="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress/>
<view class="color-base-bg goods-tag"
v-if="value.goodsTag == 'default' && goodsTag(item) != ''">{{
goodsTag(item) }}</view>
<view class="goods-tag-img" v-if="value.goodsTag == 'diy'">
<image :src="$util.img(value.tagImg.imageUrl)" />
<image :src="$util.img(value.tagImg.imageUrl)" show-menu-by-longpress/>
</view>
</view>
<view class="info-wrap">
@@ -133,11 +133,11 @@
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
</view>
</view>
<view class="pro-info">
@@ -732,7 +732,7 @@ export default {
margin-top: 100rpx;
}
/deep/.uni-scroll-view::-webkit-scrollbar {
::v-deep .uni-scroll-view::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
}

View File

@@ -1,9 +1,9 @@
<template>
<view data-component-name="diy-kefu" class="diy-kefu" :style="style">
<view class="fui-list-group merchgroup" v-for="(item, index) in value.list">
<view class="fui-list-group merchgroup" v-for="(item, index) in value.list" :key="index">
<view class="fui-list jump" v-if="index == 0">
<view class="fui-list-media">
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx"></image>
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx" show-menu-by-longpress></image>
</view>
<view class="fui-list-inner">
<view class="title" style="height: 75rpx;">
@@ -14,7 +14,7 @@
</view>
<view class="fui-remark jump" style="padding-right: 20rpx; text-align: center; line-height: 140rpx;">
<span style="font-size:24rpx;padding: 14rpx 18rpx;border-radius:8rpx"
:style="{ background: item.BtBgColor, color: item.BtColor }" @click="previewSqs()">立即添加</span>
:style="{ background: item.BtBgColor, color: item.BtColor }" @tap.stop="previewSqs()">立即添加</span>
</view>
</view>
</view>

View File

@@ -1,9 +1,9 @@
<template>
<view data-component-name="diy-listmenu" class="diy-listmenu" :style="style">
<view class="fui-cell-group">
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)"></image> -->
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)" show-menu-by-longpress></image> -->
<view v-for="(item, index) in value.list" @click="redirectTo(item.link)" class="fui-cell"
<view v-for="(item, index) in value.list" @tap.stop="redirectTo(item.link)" class="fui-cell"
:class="item.iconType == 'img' ? 'img-cell' : ''">
<view class="fui-cell-icon" :style="{ 'color': item.style ? item.style.iconColor : '#333' }">
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon" :value="item.style ? item.style : null"
@@ -62,8 +62,8 @@ export default {
methods: {
redirectTo(link) {
if (link.wap_url) {
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
this.$refs.login.open(link.wap_url);
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
this.$refs?.login?.open(link.wap_url);
return;
}
}

View File

@@ -1,6 +1,6 @@
<template>
<x-skeleton data-component-name="diy-live" type="banner" :loading="loading" :configs="skeletonConfig">
<view class="live-wrap" @click="handlerClick(liveInfo.roomid)" @tap="handlerClick(liveInfo.roomid)"
<view class="live-wrap" @tap.stop="handlerClick(liveInfo.roomid)"
v-if="liveInfo">
<view class="banner-wrap">
<image
@@ -25,7 +25,7 @@
<block v-if="value.isShowAnchorInfo">
<image
:src="liveInfo.anchor_img != '' ? $util.img(liveInfo.anchor_img) : $util.getDefaultImage().head"
class="anchor-img" @error="liveInfo.anchor_img = $util.getDefaultImage().head" />
class="anchor-img" @error="liveInfo.anchor_img = $util.getDefaultImage().head" show-menu-by-longpress/>
<text class="anchor-name">主播:{{ liveInfo.anchor_name }}</text>
</block>
<text class="separate" v-if="value.isShowAnchorInfo && value.isShowLiveGood">|</text>
@@ -109,7 +109,6 @@ export default {
}
})
.catch(err => {
console.log('get live status', err);
});
// 往后间隔1分钟或更慢的频率去轮询获取直播状态
@@ -128,7 +127,6 @@ export default {
}
})
.catch(err => {
console.log('get live status', err);
});
}, 60000);
// #endif

View File

@@ -3,7 +3,7 @@
<scroll-view scroll-x="true" class="many-goods-list-head" :scroll-into-view="'a' + cateIndex"
:style="manyWrapCss">
<view v-for="(item, index) in value.list" class="scroll-item" :class="{ active: index == cateIndex }"
:id="'a' + index" :key="index" @click="handlerClick({ item, index })" @tap="handlerClick({ item, index })">
:id="'a' + index" :key="index" @tap.stop="handlerClick({ item, index })">
<view class="split-line" v-if="index > 0"></view>
<view class="cate">
<view class="name" :style="{ color: value.headStyle.titleColor }">{{ item.title }}</view>

View File

@@ -3,11 +3,18 @@
<view class="fui-list-group merchgroup" style="margin-top:0" v-for="(item, index) in value.list">
<map id="map" style="width: 100%; height:600rpx" scale="12" :markers="markerst" bindupdated="bindupdated"
:longitude="item.lng" :latitude="item.lat" show-location>
<cover-view
<!-- <cover-view
style="position:absolute;right:10px;bottom:30rpx;z-index:99999;background:#4390FF;padding:5px 10px;wxcs_style_padding:10rpx 20rpx;border-radius:8rpx;color: #fff;"
@click="handlerClick(item)" @tap="handlerClick(item)">
@tap.stop="handlerClick(item)" @tap="handlerClick(item)">
<cover-view style="font-size:24rpx">一键导航</cover-view>
</cover-view>
</cover-view> -->
<!-- 使用非原生cover-view, 解决原生cover-view组件渲染机制z-index失效的问题 -->
<div
style="position:absolute;right:12rpx;bottom:48rpx;z-index:1;background:#4390FF;padding:0rpx 20rpx;border-radius:8rpx;color: #fff;"
@tap.stop="handlerClick(item)">
<span style="font-size:24rpx;color: #fff;">一键导航</span>
</div>
</map>
</view>

View File

@@ -4,18 +4,18 @@
<view class="common-wrap info-wrap" :class="[`data-style-${value.style}`]">
<view class="member-info" :style="memberInfoStyle">
<view class="info-wrap" :style="infoStyle" v-if="memberInfo">
<view class="headimg" @click="getWxAuth">
<view class="headimg" @tap.stop="getWxAuth">
<image :src="memberInfo.headimg ? $util.img(memberInfo.headimg) : $util.getDefaultImage().head"
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" />
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" show-menu-by-longpress/>
</view>
<view class="info">
<!-- #ifdef MP -->
<block
v-if="(memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile">
<view class="nickname"><text class="name" @click="getWxAuth">点击授权头像昵称</text></view>
<view class="nickname"><text class="name" @tap.stop="getWxAuth">点击授权头像昵称</text></view>
</block>
<view class="nickname" v-else>
<text class="name" @click="getWxAuth">{{ memberInfo.nickname }}</text>
<text class="name" @tap.stop="getWxAuth">{{ memberInfo.nickname }}</text>
<view class="member-level"
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
<!-- <text class="icondiy icon-system-huangguan"></text> -->
@@ -36,10 +36,10 @@
<!-- #ifdef H5 -->
<block
v-if="$util.isWeiXin() && ((memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile)">
<view class="nickname"><text class="name" @click="getWxAuth">点击获取微信头像</text></view>
<view class="nickname"><text class="name" @tap.stop="getWxAuth">点击获取微信头像</text></view>
</block>
<view class="nickname" v-else>
<text class="name" @click="redirect('/pages_tool/member/info')">{{ memberInfo.nickname
<text class="name" @tap.stop="redirect('/pages_tool/member/info')">{{ memberInfo.nickname
}}</text>
<view class="member-level"
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
@@ -61,12 +61,12 @@
</view>
<view v-if="ischina == 1"
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
@click.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
@tap.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
</view>
<view class="info-wrap" v-else :style="infoStyle" @click="redirect('/pages/member/index')">
<view class="info-wrap" v-else :style="infoStyle" @tap.stop="redirect($util.MEMBER_PAGE_URL)">
<view class="headimg">
<image :src="$util.getDefaultImage().head" mode="widthFix"></image>
<image :src="$util.getDefaultImage().head" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="nickname"><text class="name">{{ $lang('login') }}</text></view>
@@ -75,12 +75,12 @@
<view v-if="ischina == 1"
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
@click.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
@tap.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
</view>
<view class="account-info" v-show="value.style == 1"
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
<view class="account-item" @click="redirect('/pages_tool/member/balance')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance')">
<view class="value price-font">
{{ memberInfo ? (parseFloat(memberInfo.balance) +
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
@@ -88,12 +88,12 @@
<view class="title">{{ $lang('balance') }}</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
<view class="title">{{ $lang('point') }}</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
<view class="value price-font">
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
</view>
@@ -110,19 +110,19 @@
<text>超级会员</text>
</view>
<view class="super-text">
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">会员可享更多权益</text>
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">会员可享更多权益</text>
<text class="iconfont icon-right"></text>
</view>
</block>
<block v-if="value.style == 3">
<view class="super-info">
<image :src="$util.img('public/uniapp/member/open_member.png')" class="title" mode="heightFix" />
<image :src="$util.img('public/uniapp/member/open_member.png')" class="title" mode="heightFix" show-menu-by-longpress/>
<view class="desc">开通可享更多权益</view>
</view>
<view class="super-text">
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
</view>
</block>
</view>
@@ -130,29 +130,29 @@
<view class="member-info-style4" v-show="value.style == 4">
<view class="super-member" v-if="superMember" :style="superMemberStyle">
<view class="super-info">
<image :src="$util.img('app/component/view/member_info/img/style_4_vip_huangguan.png')" class="title" mode="widthFix" />
<image :src="$util.img('app/component/view/member_info/img/style_4_vip_huangguan.png')" class="title" mode="widthFix" show-menu-by-longpress/>
<view class="desc">开通可享更多权益</view>
</view>
<view class="super-text" :class="{ 'more' : memberInfo && memberInfo.member_level_type }">
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看更多权益</text>
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看更多权益</text>
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
</view>
</view>
<view class="account-info" :style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
<view class="account-item" @click="redirect('/pages_tool/member/balance_detail')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance_detail')">
<view class="value price-font">
{{ memberInfo ? (parseFloat(memberInfo.balance) + parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
</view>
<view class="title">余额</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}
</view>
<view class="title">积分</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
<view class="value price-font">
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
</view>
@@ -161,7 +161,7 @@
</view>
<view class="style4-other">
<view class="style4-btn-wrap">
<view @click="redirect('/pages_tool/recharge/list')" class="recharge-btn">余额充值</view>
<view @tap.stop="redirect('/pages_tool/recharge/list')" class="recharge-btn">余额充值</view>
</view>
</view>
</view> -->
@@ -169,18 +169,18 @@
<view class="account-info" v-show="value.style == 2"
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
<view class="account-item" @click="redirect('/pages_tool/member/balance')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance')">
<view class="value price-font">{{ memberInfo ? (parseFloat(memberInfo.balance) +
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}</view>
<view class="title">{{ $lang('balance') }}</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
<view class="title">{{ $lang('point') }}</view>
</view>
<view class="solid"></view>
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
<view class="value price-font">
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
</view>
@@ -196,14 +196,14 @@
<view class="head">
<text class="title">获取您的昵称、头像</text>
<text class="color-tip tips">获取用户头像、昵称完善个人资料,主要用于向用户提供具有辨识度的用户中心界面</text>
<text class="iconfont icon-close color-tip" @click="cancelCompleteInfo"></text>
<text class="iconfont icon-close color-tip" @tap.stop="cancelCompleteInfo"></text>
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="item-wrap">
<text class="label">头像</text>
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -218,7 +218,7 @@
<button open-type="getAuthorize" scope="userInfo" @getAuthorize="aliappGetUserinfo"
:plain="true" class="border-0">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -227,7 +227,7 @@
<input type="nickname" placeholder="请输入昵称" v-model="nickName" @blur="blurNickName" />
</view>
<!-- #endif -->
<button type="default" class="save-btn" @click="saveCompleteInfo" :disabled="isDisabled">保存</button>
<button type="default" class="save-btn" @tap.stop="saveCompleteInfo" :disabled="isDisabled">保存</button>
</view>
</uni-popup>
</view>
@@ -545,7 +545,7 @@ export default {
*/
redirectBeforeAuth(url) {
if (!this.storeToken) {
this.$refs.login.open('/pages/member/index');
this.$refs.login.open(this.$util.MEMBER_PAGE_URL);
return;
}
@@ -583,7 +583,7 @@ export default {
url: '/wechat/api/wechat/authcode',
data: {
scopes: 'snsapi_userinfo',
redirect_url: this.$config.h5Domain + '/pages/member/index'
redirect_url: this.$config.h5Domain + this.$util.MEMBER_PAGE_URL
},
success: res => {
if (res.code >= 0) {
@@ -1380,11 +1380,11 @@ export default {
}
}
/deep/ .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
background: none !important;
}
/deep/ .member-info-style4 .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
::v-deep .member-info-style4 .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
background: #fff !important;
}
@@ -1484,8 +1484,8 @@ export default {
}
</style>
<style scoped>
.member-complete-info-popup /deep/ .uni-popup__wrapper.bottom,
.member-complete-info-popup /deep/ .uni-popup__wrapper.bottom .uni-popup__wrapper-box {
.member-complete-info-popup ::v-deep .uni-popup__wrapper.bottom,
.member-complete-info-popup ::v-deep .uni-popup__wrapper.bottom .uni-popup__wrapper-box {
border-top-left-radius: 30rpx !important;
border-top-right-radius: 30rpx !important;
}

View File

@@ -2,11 +2,11 @@
<view data-component-name="diy-member-my-order" class="common-wrap" :style="warpCss">
<view class="order-wrap">
<view class="status-wrap">
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitpay')"
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitpay')"
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_pay.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_pay.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_pay_shade.png') + ')'">
</view>
@@ -21,11 +21,11 @@
</view>
<view class="title">{{ $lang('waitpay') }}</view>
</view>
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitsend')"
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitsend')"
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_send.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/member/order/wait_send.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_send_shade.png') + ')'">
</view>
@@ -40,11 +40,11 @@
</view>
<view class="title">{{ $lang('waitsend') }}</view>
</view>
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitconfirm')"
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitconfirm')"
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_confirm.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_confirm.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_confirm_shade.png') + ')'">
</view>
@@ -59,11 +59,11 @@
</view>
<view class="title">{{ $lang('waitconfirm') }}</view>
</view>
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitrate')"
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitrate')"
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_use.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_use.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_rate_shade.png') + ')'">
</view>
@@ -76,10 +76,10 @@
</view>
<view class="title">{{ $lang('completed') }}</view>
</view>
<view class="item-wrap" @click="redirect('/pages_tool/order/activist')">
<view class="item-wrap" @tap.stop="redirect('/pages_tool/order/activist')">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/refunding_shade.png') + ')'">
</view>

View File

@@ -4,10 +4,10 @@
<view class="merch-wrap" :style="warpCss">
<view :class="['list-wrap', value.style]" :style="warpCss">
<view :class="['item', value.ornament.type]" v-for="(item, index) in list" :key="index"
:style="itemCss" @click="handlerClick(item)" @tap="handlerClick(item)">
:style="itemCss" @tap.stop="handlerClick(item)">
<view class="merch-img">
<image class="cover-img" :src="$util.img(item.merch_image)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress />
</view>
<view class="info-wrap">
<text class="title">{{ item.merch_name }}</text>
@@ -25,12 +25,12 @@
<!-- #endif -->
<view class="merch-nav-item graphic" v-for="(item, index) in list" :key="index"
:style="{ width: 100 / 4 + '%' }" @click="handlerClick(item)" @tap="handlerClick(item)">
:style="{ width: 100 / 4 + '%' }" @tap.stop="handlerClick(item)">
<view class="graphic-img" v-if="value.mode != 'text'"
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
<image
:src="$util.img(item.merch_image) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill" :show-menu-by-longpress="true"
mode="aspectFill" show-menu-by-longpress
style="max-width: 80rpx; max-height: 80rpx; border-radius: 50rpx;" />
</view>
<text class="graphic-text"
@@ -119,7 +119,6 @@ export default {
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
}
console.log(obj)
return obj;
},
// 子项样式
@@ -143,11 +142,9 @@ export default {
},
methods: {
getList() {
console.log(121)
var data = {
num: this.value.count
};
console.log(this.value)
if (this.value.sources == 'diy') {
data.num = 0;
data.merch_id_arr = this.value.merchIds.toString();

View File

@@ -3,13 +3,12 @@
<view class="diy-notes" :style="{ backgroundColor: value.componentBgColor }">
<view class="diy-notes-top">
<view class="notes-title" :style="{ color: value.titleTextColor }">{{ value.title }}</view>
<view class="notes-more" @click="toMore()" :style="{ color: value.moreTextColor }">{{ value.more }}
<view class="notes-more" @tap.stop="toMore()" :style="{ color: value.moreTextColor }">{{ value.more }}
</view>
</view>
<scroll-view class="diy-notes-box" scroll-x="true" show-scrollbar="true">
<view class="notes-box-item" v-for="(item, i) in dataList" :key="i" @click="handlerClick(item)"
@tap="handlerClick(item)" :style="notesItemStyle">
<view class="notes-box-item" v-for="(item, i) in dataList" :key="i" @tap.stop="handlerClick(item)" :style="notesItemStyle">
<view class="notes-item" v-if="item.status == 1">
<view class="notes-item-con">
<view class="notes-title">{{ item.note_title }}</view>
@@ -26,10 +25,10 @@
<view class="notes-img-wrap" :class="{ 'notes-img-wrap-list': item.cover_type == 1 }">
<image v-if="item.cover_type == 0" :src="$util.img(item.img)" @error="imageError(i)"
mode="aspectFill" class="notes-item-image" />
mode="aspectFill" class="notes-item-image" show-menu-by-longpress/>
<image v-else v-for="(imgItem, imgIndex) in item.img" :key="imgIndex"
:src="$util.img(imgItem)" @error="imageError(i)" mode="aspectFit"
class="notes-item-image-li" />
class="notes-item-image-li" show-menu-by-longpress/>
</view>
<view class="notes-item-con">
@@ -157,7 +156,7 @@ export default {
/* 点赞 */
giveLike(noteId, index) {
if (!this.storeToken) {
this.$refs.login.open('/pages/index/index');
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
return;
}

View File

@@ -41,12 +41,12 @@
<view @touchmove.prevent.stop>
<uni-popup ref="noticePopup" type="center">
<view class="notice-popup">
<view class="head-wrap" @click="closeNoticePopup">
<view class="head-wrap" @tap.stop="closeNoticePopup">
<text>公告</text>
<text class="iconfont icon-close"></text>
</view>
<view class="content-wrap">{{ notice }}</view>
<button type="primary" @click="closeNoticePopup">我知道了</button>
<button type="primary" @tap.stop="closeNoticePopup">我知道了</button>
</view>
</uni-popup>
</view>

View File

@@ -7,11 +7,11 @@
<!-- <text class="iconfont icon-shuaxin"></text> -->
</view>
<view class="qrocde-action">
<button type="primary" @click="toLink">
<button type="primary" @tap.stop="toLink">
<text class="iconfont icon-fukuanma"></text>
<text class="action-name">付款码</text>
</button>
<button type="primary" @click="openPaymentPopup">
<button type="primary" @tap.stop="openPaymentPopup">
<text class="iconfont icon-saomafu"></text>
<text class="action-name">扫码付</text>
</button>
@@ -26,12 +26,12 @@
<view @touchmove.prevent.stop>
<uni-popup ref="paymentPopup" type="center">
<view class="payment-popup">
<view class="head-wrap" @click="closePaymentPopup">
<view class="head-wrap" @tap.stop="closePaymentPopup">
<text>提示</text>
<text class="iconfont icon-close"></text>
</view>
<view class="content-wrap">扫码付请退出程序后直接使用微信扫一扫或返回上一页使用付款码进行支付</view>
<button type="primary" @click="closePaymentPopup">我知道了</button>
<button type="primary" @tap.stop="closePaymentPopup">我知道了</button>
</view>
</uni-popup>
</view>
@@ -75,8 +75,6 @@ export default {
// 允许从相机和相册扫码,h5端不起作用
uni.scanCode({
success: function (res) {
console.log('条码类型:' + res.scanType);
console.log('条码内容:' + res.result);
}
});
// #endif

View File

@@ -3,9 +3,9 @@
<view class="fui-picture">
<view v-for="(item, index) in value.list" style="line-height: 0;">
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)"
v-if="item.link.wap_url" @click="handlerClick(item)" @tap="handlerClick(item)"></image>
v-if="item.link.wap_url" @tap.stop="handlerClick(item)" show-menu-by-longpress></image>
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)" v-else
@click="handlerClick(item)" @tap="handlerClick(item)"></image>
@tap.stop="handlerClick(item)" show-menu-by-longpress></image>
</view>
<!-- <view wx:if="{{!childitem.linkurl}}" bindtap="previewImg" data-src="{{childitem.imgurl}}" style="padding:{{diyitem.style.paddingtop==0?0:diyitem.style.paddingtop+'rpx'}} {{diyitem.style.paddingleft==0?0:diyitem.style.paddingleft+'rpx'}}" wx:for="{{diyitem.data}}" wx:for-index="childid" wx:for-item="childitem" wx:key="{{childid}}">
<image mode="widthFix" src="{{childitem.imgurl}}" style="{{bannerheight?'height:'+bannerheight+'px':'height:auto'}}"></image>
@@ -52,20 +52,9 @@ export default {
}
},
methods: {
previewImg(img) {
// #ifdef MP-WEIXIN
uni.previewImage({
current: 0,
urls: [this.$util.img(img)],
success: function (res) { },
fail: function (res) { },
complete: function (res) { },
})
// #endif
},
redirectTo(link) {
if (link.wap_url) {
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
this.$refs.login.open(link.wap_url);
return;
}
@@ -77,7 +66,9 @@ export default {
await this.__$emitEvent({
eventName: 'picture-tap', data: item, promiseCallback: (event, handler, awaitedResult) => {
if (!awaitedResult) return;
if (item.link.wap_url) {
const link = item.link;
if (link?.name || link?.wap_url || link?.appid) {
this.redirectTo(item.link);
} else {
this.previewImg(item.imageUrl);

View File

@@ -2,12 +2,12 @@
<x-skeleton data-component-name="diy-pinfan" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
<view class="diy-pinfan" :class="[value.template, value.style]" :style="warpCss">
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">
@@ -61,12 +61,12 @@
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -113,11 +113,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -319,7 +319,7 @@ export default {
</script>
<style lang="scss">
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -328,7 +328,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -10,18 +10,18 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" />
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress/>
<view class="head-content">
<view class="img-warp">
<block v-if="headData && headData.member_list && headData.member_list.length">
<image v-for="(item, index) in headData.member_list" :key="index"
:src="$util.img(item.member_img || 'public/static/img/default_img/square.png')"
mode="aspectFill" @error="headImageError(index)" />
mode="aspectFill" @error="headImageError(index)" show-menu-by-longpress/>
</block>
<block v-else>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
</block>
</view>
<view :style="{ color: value.titleStyle.textColor }">
@@ -31,19 +31,19 @@
</view>
<view class="head-right"
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
@click="$util.redirectTo('/pages_promotion/pintuan/list')">
@tap.stop="$util.redirectTo('/pages_promotion/pintuan/list')">
<text>{{ value.titleStyle.more }}</text>
<text class="iconfont icon-right"></text>
</view>
</view>
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">
@@ -100,12 +100,12 @@
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
<view v-if="value.groupStyle.control && value.style == 'style-1'" class="num" :style="{
color: value.theme == 'diy' ? value.groupStyle.color : '',
background: value.theme == 'diy' ? 'linear-gradient(to right,' + value.groupStyle.bgColorStart + ',' + value.groupStyle.bgColorEnd + ')' : ''
@@ -161,11 +161,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
<view v-if="value.groupStyle.control && value.style != 'style-2'" class="num" :style="{
color: value.theme == 'diy' ? value.groupStyle.color : '',
background: value.theme == 'diy' ? 'linear-gradient(to right,' + value.groupStyle.bgColorStart + ',' + value.groupStyle.bgColorEnd + ')' : ''
@@ -391,7 +391,7 @@ export default {
</script>
<style lang="scss">
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -400,7 +400,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -2,12 +2,12 @@
<x-skeleton data-component-name="diy-presale" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
<view class="diy-presale" v-if="list.length" :class="[value.template, value.style]" :style="warpCss">
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
@tap="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.btnStyle.control">
@@ -40,12 +40,12 @@
</template>
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
<view class="item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
@tap="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -75,12 +75,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
@click="handlerClick(item)" @tap="handlerClick(item)" :class="[value.ornament.type]"
:style="goodsItemCss">
@tap.stop="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -276,7 +275,7 @@ export default {
</script>
<style lang="scss">
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -285,7 +284,7 @@ export default {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -5,12 +5,12 @@
<view class="uni-scroll-view-content">
<!-- #endif -->
<view class="quick-nav-item" v-for="(item, index) in value.list" :key="index"
@click="handlerClick(item)" @tap="handlerClick(item)"
@tap.stop="handlerClick(item)"
:style="{ background: 'linear-gradient(to right,' + item.bgColorStart ? item.bgColorStart : '' + ',' + item.bgColorEnd ? item.bgColorEnd : '' + ')' }">
<view class="quick-img" v-if="item.imageUrl || item.icon">
<image v-if="item.iconType == 'img'"
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="heightFix" :show-menu-by-longpress="true"></image>
mode="heightFix" show-menu-by-longpress ></image>
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null" :style="{ fontSize: '60rpx' }"></diy-icon>
</view>
@@ -61,7 +61,7 @@ export default {
methods: {
redirectTo(link) {
if (link.wap_url) {
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
this.$refs.login.open(link.wap_url);
return;
}

View File

@@ -1,6 +1,6 @@
<template>
<view data-component-name="diy-rich-text" class="rich-text-box" :style="richTextWarpCss">
<rich-text :nodes="html" @click="handlerClick" @tap="handlerClick"></rich-text>
<rich-text :nodes="html" @tap.stop="handlerClick"></rich-text>
</view>
</template>

View File

@@ -8,22 +8,20 @@
<!-- 1左2右 -->
<template v-if="value.mode == 'row1-lt-of2-rt'">
<view class="template-left">
<view :class="['item', value.mode]" @click="handlerClick(value.list[0].link)"
@tap="handlerClick(value.list[0].link)"
<view :class="['item', value.mode]" @tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)"
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }">
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[0].pageItemStyle" show-menu-by-longpress />
</view>
</view>
<view class="template-right">
<template v-for="(item, index) in list">
<template v-if="index > 0">
<view :key="index" :class="['item', value.mode]" @click="handlerClick(item.link)"
@tap="handlerClick(item.link)"
<view :key="index" :class="['item', value.mode]" @tap.stop="handlerClick(item.link, item.imageUrl)"
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: item.imgWidth, height: item.imgHeight + 'px' }">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</template>
@@ -35,30 +33,29 @@
<view class="template-left">
<view :class="['item', value.mode]"
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }"
@click="handlerClick(value.list[0].link)" @tap="handlerClick(value.list[0].link)">
@tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)">
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[0].pageItemStyle" show-menu-by-longpress />
</view>
</view>
<view class="template-right">
<view :class="['item', value.mode]"
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: list[1].imgWidth, height: list[1].imgHeight + 'px' }"
@click="handlerClick(value.list[1].link)" @tap="handlerClick(value.list[1].link)">
@tap.stop="handlerClick(value.list[1].link, value.list[1].imageUrl)">
<image :src="$util.img(value.list[1].imageUrl)" :mode="list[1].imageMode || 'scaleToFill'"
:style="list[1].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[1].pageItemStyle" show-menu-by-longpress />
</view>
<view class="template-bottom">
<template v-for="(item, index) in list">
<template v-if="index > 1">
<view :key="index" :class="['item', value.mode]" @click="handlerClick(item.link)"
@tap="handlerClick(item.link)" :style="{
<view :key="index" :class="['item', value.mode]" @tap.stop="handlerClick(item.link, item.imageUrl)" :style="{
marginRight: value.imageGap * 2 + 'rpx',
width: item.imgWidth,
height: item.imgHeight + 'px'
}">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</template>
@@ -68,10 +65,10 @@
<template v-else>
<view :class="['item', value.mode]" v-for="(item, index) in list" :key="index"
@click="handlerClick(item.link)" @tap="handlerClick(item.link)"
@tap.stop="handlerClick(item.link, item.imageUrl)"
:style="{ marginRight: value.imageGap * 2 + 'rpx', marginBottom: value.imageGap * 2 + 'rpx', width: item.widthStyle, height: item.imgHeight + 'px' }">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</view>
@@ -374,11 +371,16 @@ export default {
return obj;
},
async handlerClick(link) {
async handlerClick(link, imageUrl) {
await this.__$emitEvent({
eventName: 'rubik-cube-tap', data: link, promiseCallback: (event, handler, awaitedResult) => {
if (!awaitedResult) return;
if (link?.name || link?.wap_url || link?.appid) {
this.$util.diyRedirectTo(link);
} else if (imageUrl){
this.previewImg(imageUrl);
}
}
})
}

View File

@@ -2,10 +2,10 @@
<view data-component-name="diy-search" class="diy-search">
<view class="diy-search-wrap" :class="value.positionWay" :style="fixedCss">
<view :class="['search-box', 'search-box-' + value.searchStyle]" :style="searchWrapCss"
@click="handlerSearchClick" @tap="handlerSearchClick">
@tap.stop="handlerSearchClick">
<block v-if="[1, 2].includes(value.searchStyle)">
<view class="img" v-if="value.searchStyle == 2 && value.iconType == 'img'">
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
<image :src="$util.img(value.imageUrl)" mode="heightFix" show-menu-by-longpress/>
</view>
<diy-icon class="icon" v-if="value.searchStyle == 2 && value.iconType == 'icon'" :icon="value.icon"
:value="value.style ? value.style : 'null'"
@@ -14,32 +14,30 @@
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
:placeholderStyle="placeholderStyle" />
<text class="iconfont icon-sousuo3" @click.stop="handlerSearchClick" @tap="handlerSearchClick"
<text class="iconfont icon-sousuo3" @tap.stop="handlerSearchClick"
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
</view>
</block>
<block v-if="value.searchStyle == 3">
<view class="search-content" :style="inputStyle" @click.stop="handlerSearchClick"
@tap="handlerSearchClick">
<view class="search-content" :style="inputStyle"
@tap.stop="handlerSearchClick">
<text class="iconfont icon-sousuo3"
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
@click.stop="handlerSearchClick" @tap="handlerSearchClick"
@tap.stop="handlerSearchClick"
:placeholderStyle="placeholderStyle" />
<text class="search-content-btn" @click.stop="handlerSearchClick" @tap="handlerSearchClick"
<text class="search-content-btn" @tap.stop="handlerSearchClick"
:style="{ 'backgroundColor': value.pageBgColor ? value.pageBgColor : 'rgba(0,0,0,0)' }">搜索</text>
</view>
<view class="img" v-if="value.iconType == 'img'"
@click.stop="handlerRedirectToClick(value.searchLink)"
@tap="handlerRedirectToClick(value.searchLink)">
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
@tap.stop="handlerRedirectToClick(value.searchLink)">
<image :src="$util.img(value.imageUrl)" mode="heightFix" show-menu-by-longpress/>
</view>
<diy-icon class="icon" v-if="value.iconType == 'icon'" :icon="value.icon"
:value="value.style ? value.style : 'null'"
:style="{ maxWidth: 30 * 2 + 'rpx', maxHeight: 30 * 2 + 'rpx' }"
@click.stop="handlerRedirectToClick(value.searchLink)"
@tap="handlerRedirectToClick(value.searchLink)"></diy-icon>
@tap.stop="handlerRedirectToClick(value.searchLink)"></diy-icon>
</block>
</view>
</view>
@@ -198,7 +196,7 @@ export default {
},
redirectTo(link) {
if (link.wap_url) {
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
this.$refs.login.open(link.wap_url);
return;
}
@@ -257,7 +255,7 @@ export default {
</script>
<style lang="scss">
/deep/ .uni-input-placeholder {
::v-deep .uni-input-placeholder {
overflow: initial;
}

View File

@@ -11,7 +11,7 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="title-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" />
<image v-else class="title-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress/>
<text v-if="value.titleStyle.style == 'style-2' && isFuture"
:style="{ color: value.titleStyle.textColor }" class="seckill-style-name">距离开始</text>
<view class="time" :style="{ background: value.titleStyle.timeBgColor }">
@@ -27,7 +27,7 @@
</view>
<view class="marketimg-box-title-right" v-if="value.titleStyle.moreSupport"
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
@click="toMore">
@tap.stop="toMore">
<text>{{ value.titleStyle.more }}</text>
<text class="iconfont icon-right"></text>
</view>
@@ -35,12 +35,12 @@
<view class="content-wrap">
<template v-if="value.template == 'row1-of1'">
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -57,7 +57,7 @@
}">
<image v-if="value.style == 'style-2'" class="progress-bar"
:src="$util.img('public/uniapp/seckill/progress_bar_01.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
</view>
</view>
<view class="num"
@@ -105,12 +105,12 @@
</template>
<template v-if="value.template == 'row1-of2'">
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content" v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -150,12 +150,12 @@
<template v-if="value.template == 'horizontal-slide'">
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true"
:show-scrollbar="false">
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content" v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -166,7 +166,7 @@
<view class="discount-price" v-if="value.priceStyle.mainControl">
<image v-if="value.style == 'style-2'" class="tag"
:src="$util.img('addon/seckill/component/view/seckill/img/style_4_tag.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
<text class="unit price-style small"
:style="{ color: value.theme == 'diy' ? value.priceStyle.mainColor : '' }">¥</text>
<text class="price price-style large"
@@ -190,11 +190,11 @@
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
:class="['swiper-item', dataList[pageIndex] && [dataList[pageIndex].length / 3].length >= 1 && 'flex-between']">
<view class="item" v-for="(item, dataIndex) in dataList[pageIndex]" :key="dataIndex"
@click="toDetail(item.id)" :class="[value.ornament.type]" :style="goodsItemCss">
@tap.stop="toDetail(item.id)" :class="[value.ornament.type]" :style="goodsItemCss">
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex, pageIndex)" />
@error="imageError(dataIndex, pageIndex)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -206,7 +206,7 @@
<view class="discount-price" v-if="value.priceStyle.mainControl">
<image v-if="value.style == 'style-2'" class="tag"
:src="$util.img('addon/seckill/component/view/seckill/img/style_4_tag.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
<text class="unit price-style small"
:style="{ color: value.theme == 'diy' ? value.priceStyle.mainColor : '' }">¥</text>
<text class="price price-style large"
@@ -536,7 +536,7 @@ scroll-view ::-webkit-scrollbar {
touch-action: none;
}
/deep/.uni-scroll-view ::-webkit-scrollbar {
::v-deep .uni-scroll-view ::-webkit-scrollbar {
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
display: none;
width: 0;
@@ -545,7 +545,7 @@ scroll-view ::-webkit-scrollbar {
background: transparent;
}
/deep/::-webkit-scrollbar {
::v-deep ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;

View File

@@ -140,7 +140,7 @@ export default {
display: flex;
align-items: baseline;
/deep/ .uni-scroll-view-content {
::v-deep .uni-scroll-view-content {
display: flex;
align-items: center;
}
@@ -148,7 +148,7 @@ export default {
&.between {
justify-content: space-between;
/deep/.uni-scroll-view-content {
::v-deep .uni-scroll-view-content {
justify-content: space-between;
}
}

View File

@@ -3,7 +3,7 @@
<block v-if="value.style == 1">
<view class="store-box store-one">
<view class="store-info">
<view class="info-box" :style="{ color: value.textColor }" @click="toStoreList()">
<view class="info-box" :style="{ color: value.textColor }" @tap.stop="toStoreList()">
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
<text class="title">{{ globalStoreInfo.store_name }}</text>
<text>
@@ -15,26 +15,26 @@
</view>
<view class="address-wrap" :style="{ color: value.textColor }">
<text class="iconfont icon-dizhi"></text>
<text v-if="globalStoreInfo && globalStoreInfo.store_id" @click="mapRoute" class="address">{{
<text v-if="globalStoreInfo && globalStoreInfo.store_id" @tap.stop="mapRoute" class="address">{{
globalStoreInfo.show_address }}</text>
<text v-else>获取当前位置...</text>
</view>
</view>
<view class="store-image" @click="selectStore()">
<view class="store-image" @tap.stop="selectStore()">
<image :src="$util.img(globalStoreInfo.store_image)"
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill" show-menu-by-longpress></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill" show-menu-by-longpress></image>
</view>
</view>
</block>
<block v-if="value.style == 2">
<view class="store-box store-three" @click="toStoreList()">
<view class="store-box store-three" @tap.stop="toStoreList()">
<view class="store-info">
<view class="store-image" @click="selectStore()">
<view class="store-image" @tap.stop="selectStore()">
<image :src="$util.img(globalStoreInfo.store_image)"
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill" show-menu-by-longpress></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill" show-menu-by-longpress></image>
</view>
<view class="info-box" :style="{ color: value.textColor }">
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
@@ -47,13 +47,13 @@
<text class="title" v-else>定位中...</text>
</view>
</view>
<view class="store-icon" @click.stop="search()"><text class="iconfont icon-sousuo3"
<view class="store-icon" @tap.stop="search()"><text class="iconfont icon-sousuo3"
:style="{ color: value.textColor }"></text></view>
</view>
</block>
<block v-if="value.style == 3">
<view class="store-box store-four" @click="toStoreList()">
<view class="store-box store-four" @tap.stop="toStoreList()">
<view class="store-left-wrap">
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
<text class="iconfont icon-weizhi" :style="{ color: value.textColor }"></text>
@@ -64,8 +64,8 @@
</view>
<view class="store-right-search">
<input type="text" class="uni-input font-size-tag" disabled placeholder="商品搜索"
@click.stop="search()" />
<text class="iconfont icon-sousuo3" @click.stop="search()"></text>
@tap.stop="search()" />
<text class="iconfont icon-sousuo3" @tap.stop="search()"></text>
</view>
</view>
</block>

1287
components-diy/diy-tab.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
<template>
<view data-component-name="diy-text" class="diy-text" @click="handlerClick(value.link)"
@tap="handlerClick(value.link)" :style="warpCss">
<view data-component-name="diy-text" class="diy-text" @tap="handlerClick(value.link)" :style="warpCss">
<view :class="value.style == 'style-8' ? 'title2' : 'title'"
:style="{ fontSize: value.fontSize * 2 + 'rpx', color: value.textColor }">
<block v-if="value.style == 'style-0'" style="height: 40rpx; line-height: 40rpx;">
@@ -90,17 +89,17 @@
<view class="style9-box">
<view class="style9-center">
<view class="left-img">
<image :src="$util.img('public/uniapp/diy/style9-1.png')" />
<image :src="$util.img('public/uniapp/diy/style9-1.png')" show-menu-by-longpress/>
</view>
<text class="style9-title"
:style="{ fontSize: value.fontSize * 2 + 'rpx', color: value.textColor, fontWeight: value.fontWeight }">
{{ value.text }}
</text>
<view class="right-img">
<image :src="$util.img('public/uniapp/diy/style9-2.png')" />
<image :src="$util.img('public/uniapp/diy/style9-2.png')" show-menu-by-longpress/>
</view>
<view class="style9-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@click.stop="handlerClick(value.more.link)" @tap="handlerClick(value.more.link)">
@tap.stop="handlerClick(value.more.link)">
{{ value.more.text }}
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
</view>
@@ -115,7 +114,7 @@
<view class="style10-box">
<view class="style10-center">
<view class="left-img">
<image :src="$util.img('public/uniapp/diy/style10-1.png')"></image>
<image :src="$util.img('public/uniapp/diy/style10-1.png')" show-menu-by-longpress></image>
</view>
<text class="style10-title" v-if="$util.img('public/uniapp/diy/style10-3.png')" :style="{
backgroundImage: 'url(' + $util.img('public/uniapp/diy/style10-3.png') + ')',
@@ -129,10 +128,10 @@
{{ value.text }}
</text>
<view class="right-img">
<image :src="$util.img('public/uniapp/diy/style10-2.png')"></image>
<image :src="$util.img('public/uniapp/diy/style10-2.png')" show-menu-by-longpress></image>
</view>
<view class="style10-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@click.stop="handlerClick(value.more.link)" @tap="handlerClick(value.more.link)">
@tap.stop="handlerClick(value.more.link)">
{{ value.more.text }}
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
</view>
@@ -159,16 +158,16 @@
value.subTitle.text }}</view>
</view>
<view class="style11-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@click.stop="$util.diyRedirectTo(value.more.link)">
@tap.stop="$util.diyRedirectTo(value.more.link)">
{{ value.more.text }}
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
</view>
<image class="center-img" :src="$util.img('public/uniapp/diy/style11-1.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress></image>
<image class="right-img" :src="$util.img('public/uniapp/diy/style11-2.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress></image>
</view>
</view>
</view>
@@ -185,13 +184,13 @@
<text class="style12-sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
}}</text>
<view class="style12-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@click.stop="$util.diyRedirectTo(value.more.link)">
@tap.stop="$util.diyRedirectTo(value.more.link)">
<text>{{ value.more.text }}</text>
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
</view>
</view>
<view class="style13" v-else-if="value.style == 'style-13'">
<image class="left-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix"></image>
<image class="left-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="style13-title" :style="{
fontSize: value.fontSize * 2 + 'rpx',
color: value.textColor,
@@ -199,7 +198,7 @@
}">
{{ value.text }}
</view>
<image class="right-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix"></image>
<image class="right-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="style14" v-else-if="value.style == 'style-14'">
<view class="title-wrap">
@@ -278,7 +277,7 @@
<text :style="{ fontWeight: value.subTitle.fontWeight }">{{ value.subTitle.text }}</text>
</view>
<view class="style16-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@click.stop="$util.diyRedirectTo(value.more.link)">
@tap.stop="$util.diyRedirectTo(value.more.link)">
<text>{{ value.more.text }}</text>
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
</view>

View File

@@ -6,10 +6,10 @@
<view v-for="(item, index) in value.list" :key="index" class="goods-item" @tap="showVideo(item)">
<view class="goods-img-wrap">
<image class="goods-img" style="border-radius:10rpx 10rpx 0 0;" :src="$util.img(item.imageUrl)"
mode="widthFix" @error="imgError(index)"></image>
mode="widthFix" @error="imgError(index)" show-menu-by-longpress ></image>
<view style="position:absolute;top:10rpx;right:10rpx;">
<image style="width:30rpx;" :src="$util.img('addon/personnel/shop/view/enterprise/play.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress ></image>
</view>
</view>
<view class="info-wrap">
@@ -34,10 +34,10 @@
'max-height': '200rpx;',
'border-radius': '8rpx;'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')" mode="widthFix"
:show-menu-by-longpress="true"></image>
show-menu-by-longpress ></image>
<view style="position:absolute;top:10rpx;right:10rpx;">
<image style="width:30rpx;"
:src="$util.img('addon/personnel/shop/view/enterprise/play.png')" mode="widthFix">
:src="$util.img('addon/personnel/shop/view/enterprise/play.png')" mode="widthFix" show-menu-by-longpress >
</image>
</view>
</view>
@@ -53,9 +53,19 @@
<!-- 视频播放弹窗 -->
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
<view class="video-container" style="position:fixed;top:30%;width:100%;left:0;">
<video class="adaptive-video" :autoPauseIfNavigate="true" :autoPauseIfOpenNative="true"
:autoplay="false" :enableAutoRotation="true" id="myVideo" :src="video_url" :controls="true"></video>
<view class="video-container" :style="popupVideoStyle">
<video
ref="popupVideoRef"
class="adaptive-video"
:autoPauseIfNavigate="true"
:autoPauseIfOpenNative="true"
:autoplay="false"
:enableAutoRotation="true"
id="myVideo"
:src="video_url"
:controls="true"
@loadedmetadata="onPopupVideoLoaded"
></video>
</view>
</uni-popup>
</view>
@@ -104,7 +114,6 @@ export default {
style += 'box-shadow:' + (this.value.ornament.type == 'shadow' ? '0 0 10rpx ' + this.value.ornament.color : '') + ';';
style += 'border:' + (this.value.ornament.type == 'stroke' ? '2rpx solid ' + this.value.ornament.color : '') + ';';
console.log(this.value);
return style;
},
@@ -126,19 +135,76 @@ export default {
isIndicatorDots() {
return this.value.carousel.type != 'hide' &&
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
},
// 弹窗视频容器样式
popupVideoStyle() {
let style = 'position:fixed;width:100%;left:0;';
if (this.popupVideoRatio) {
// 根据视频比例设置位置
if (this.popupVideoRatio === 'portrait') {
// 竖屏视频:居中显示,使用9/16比例
style += 'top:50%;transform:translateY(-50%);';
style += 'aspect-ratio: 9 / 16;';
} else {
// 横屏视频:靠近顶部显示,使用16/9比例
style += 'top:20%;';
style += 'aspect-ratio: 16 / 9;';
}
} else {
// 默认:居中显示,使用竖屏比例
style += 'top:50%;transform:translateY(-50%);';
style += 'aspect-ratio: 9 / 16;';
}
return style;
}
},
methods: {
// 显示视频播放弹窗
showVideo(item) {
this.video_url = item.videoUrl;
// 重置视频比例为null,等待新的检测结果
this.popupVideoRatio = null;
this.$refs.videoPopup.open();
},
// 图片加载错误处理
imgError(index) {
// 图片加载失败的处理逻辑
console.log('图片加载失败:', index);
},
// 弹窗视频加载完成事件
onPopupVideoLoaded(event) {
this.$nextTick(() => {
let width = 0;
let height = 0;
// H5 端:获取真实视频宽高
// #ifdef H5
const video = this.$refs.popupVideoRef;
if (video) {
width = video.videoWidth || 0;
height = video.videoHeight || 0;
}
// #endif
// 微信小程序端:通过事件对象获取视频宽高
// #ifdef MP-WEIXIN
if (event && event.detail) {
width = event.detail.width || 0;
height = event.detail.height || 0;
}
// #endif
if (width && height) {
this.popupVideoRatio = width >= height ? 'landscape' : 'portrait';
} else {
// 获取失败,默认竖屏
this.popupVideoRatio = 'portrait';
}
});
}
}
}

View File

@@ -1,7 +1,36 @@
<template>
<video data-component-name="diy-video" class="diy-video" :src="$util.img(value.videoUrl)"
:poster="$util.img(value.imageUrl)" :style="videoWarpCss" objectFit="cover"
@click="handlerClick(value.videoUrl)" @tap="handlerClick(value.videoUrl)"></video>
<view class="video-container" :style="containerStyle">
<!-- H5端使用原生video标签 -->
<!-- #ifdef H5 -->
<video
ref="videoRef"
data-component-name="diy-video"
class="diy-video h5-video"
:src="videoUrl"
:poster="posterUrl"
:style="videoWarpCss"
controls
preload="metadata"
@loadedmetadata="onVideoLoaded"
@click="handlerClick(videoUrl)"
></video>
<!-- #endif -->
<!-- 微信小程序端使用uni-app的video组件 -->
<!-- #ifdef MP-WEIXIN -->
<video
ref="videoRef"
data-component-name="diy-video"
class="diy-video mp-video"
:src="videoUrl"
:poster="posterUrl"
:style="videoWarpCss"
controls
@loadedmetadata="onVideoLoaded"
@error="onVideoError"
></video>
<!-- #endif -->
</view>
</template>
<script>
@@ -15,27 +44,149 @@ export default {
}
},
data() {
return {};
return {
videoRatio: null, // 视频比例:'landscape'(横屏)或 'portrait'(竖屏)
videoUrl: '',
posterUrl: ''
};
},
created() {
this.initVideoData();
},
created() { },
mixins: [DiyMinx],
watch: {
// 组件刷新监听
componentRefresh: function (nval) { }
value: {
immediate: true,
handler(newVal) {
if (newVal) {
this.initVideoData();
}
}
}
},
computed: {
videoWarpCss: function () {
var obj = '';
if (this.value.componentAngle == 'round') {
if (this.value && this.value.componentAngle == 'round') {
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
}
return obj;
},
// 动态计算容器比例
containerStyle() {
let style = '';
if (this.videoRatio === 'landscape') {
// 横屏视频:16/9比例
style += 'aspect-ratio: 16 / 9;';
} else if (this.videoRatio === 'portrait') {
// 竖屏视频:9/16比例
style += 'aspect-ratio: 9 / 16;';
} else {
// 未加载完成 → 默认使用竖屏比例
style += 'aspect-ratio: 9 / 16;';
}
return style;
}
},
methods: {
// 初始化视频数据
initVideoData() {
this.videoUrl = this.getVideoUrl();
this.posterUrl = this.getPosterUrl();
// H5端:预加载视频获取宽高
// #ifdef H5
this.preloadVideoForRatio();
// #endif
},
// 获取视频URL
getVideoUrl() {
if (!this.value || !this.value.videoUrl) return '';
const videoUrl = this.value.videoUrl;
// 如果是完整的http/https链接,直接使用
if (videoUrl.indexOf('http://') === 0 || videoUrl.indexOf('https://') === 0) {
return videoUrl;
}
// 否则使用$util.img()处理
return this.$util.img(videoUrl);
},
// 获取封面图URL
getPosterUrl() {
if (!this.value || !this.value.imageUrl) return '';
return this.$util.img(this.value.imageUrl);
},
// H5端:预加载视频获取宽高比例
preloadVideoForRatio() {
const videoUrl = this.videoUrl;
if (!videoUrl) return;
const video = document.createElement('video');
video.preload = 'metadata';
video.src = videoUrl;
video.muted = true;
video.playsInline = true;
video.addEventListener('loadedmetadata', () => {
const width = video.videoWidth;
const height = video.videoHeight;
if (width && height) {
this.videoRatio = width >= height ? 'landscape' : 'portrait';
}
});
video.addEventListener('error', (e) => {
console.error('H5端预加载视频失败:', e);
});
// 触发加载
video.load();
},
// 视频加载完成事件(组件内video标签)
onVideoLoaded(event) {
let width = 0;
let height = 0;
// #ifdef H5
if (event && event.target) {
width = event.target.videoWidth || 0;
height = event.target.videoHeight || 0;
}
// #endif
// #ifdef MP-WEIXIN
if (event && event.detail) {
width = event.detail.width || 0;
height = event.detail.height || 0;
}
// #endif
if (width && height) {
this.videoRatio = width >= height ? 'landscape' : 'portrait';
}
},
// 视频播放事件
onVideoPlay() {
},
// 视频暂停事件
onVideoPause() {
},
// 视频错误事件
onVideoError(event) {
console.error('视频加载失败:', event);
},
async handlerClick(videoUrl) {
await this.__$emitEvent({
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
@@ -48,11 +199,32 @@ export default {
</script>
<style scoped>
video {
.video-container {
width: 100%;
position: relative;
overflow: hidden;
background-color: #000;
}
.diy-video>>>.uni-video-container {
background-color: transparent;
/* 基础样式 */
.diy-video {
width: 100%;
height: 100%;
display: block;
}
/* H5端样式 */
/* #ifdef H5 */
.h5-video {
object-fit: cover;
}
/* #endif */
/* 微信小程序端样式 */
/* #ifdef MP-WEIXIN */
.mp-video {
width: 100%;
height: 100%;
}
/* #endif */
</style>

View File

@@ -0,0 +1,187 @@
// 微信视频号组件配置
export const wechatChannelConfig = {
// 图标相关
icon: {
defaultSize: 80, // 默认图标尺寸(rpx)
smallSize: 60, // 小图标尺寸(rpx)
channelArrowSize: 24, // 频道箭头图标尺寸(rpx)
channelArrow: 'addon/personnel/shop/view/enterprise/arrow.png', // 频道箭头图标路径
},
// 视频相关配置
video: {
defaultHeight: 320, // 默认视频高度(rpx)
minHeight: 200, // 最小视频高度(rpx)
maxHeight: 500, // 最大视频高度(rpx)
defaultCoverUrl: 'addon/personnel/shop/view/enterprise/default-video-cover.png', // 默认视频封面
},
// 播放按钮配置
playButton: {
size: 80, // 标准尺寸(rpx)
smallSize: 60, // 小尺寸(rpx)
iconSize: 40, // 标准图标尺寸(rpx)
smallIconSize: 30, // 小图标尺寸(rpx)
background: 'rgba(0, 0, 0, 0.4)', // 背景颜色
},
// 布局配置
layout: {
borderRadius: 12, // 圆角(rpx)
padding: 16, // 内边距(rpx)
margin: 10, // 外边距(rpx)
},
// 字体配置
font: {
channelNameSize: 28, // 频道名称字体大小(rpx)
videoTitleSize: 28, // 视频标题字体大小(rpx)
statsSize: 24, // 统计信息字体大小(rpx)
},
// 颜色配置
color: {
channelName: '#333', // 频道名称颜色
videoTitle: '#333', // 视频标题颜色
stats: '#999', // 统计信息颜色
border: '#f0f0f0', // 边框颜色
},
// 微信相关配置
wechat: {
minSdkVersion: '2.19.2', // 最小微信基础库版本
embedComponent: 'channel-video', // 嵌入式视频组件名称
},
// 错误提示配置
error: {
notWechat: '当前环境不是微信小程序',
lowVersion: '当前微信基础库版本过低,需要 2.19.2 或以上版本',
notSupported: '当前环境不支持微信视频号',
missingFields: '缺少必要字段: {field}',
},
};
// 获取配置项的辅助函数
export const getwechatChannelConfig = (key, defaultValue = null) => {
const keys = key.split('.');
let config = wechatChannelConfig;
for (const k of keys) {
if (config[k] === undefined) {
return defaultValue;
}
config = config[k];
}
return config;
};
// 微信视频号工具函数
export const wechatChannelUtil = {
// 版本比较
versionCompare(v1, v2) {
const arr1 = v1.split('.');
const arr2 = v2.split('.');
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
const num1 = parseInt(arr1[i] || 0);
const num2 = parseInt(arr2[i] || 0);
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
},
// 检查是否支持嵌入式播放
isEmbedModeSupported() {
return typeof wx !== 'undefined' && wx.canIUse(wechatChannelConfig.wechat.embedComponent);
},
// 获取默认视频封面
getDefaultCoverUrl() {
return wechatChannelConfig.video.defaultCoverUrl;
},
// 数据校验
validateVideoData(item) {
const requiredFields = ['feedId', 'finderUserName'];
for (const field of requiredFields) {
if (!item[field]) {
throw new Error(`缺少必要字段: ${field}`);
}
}
return true;
},
// 播放视频
playVideo(item) {
return new Promise((resolve, reject) => {
try {
// 数据校验
this.validateVideoData(item);
} catch (err) {
this.handleError(err, item);
reject(err);
return;
}
// 检查微信环境
if (typeof wx === 'undefined') {
const err = new Error(wechatChannelConfig.error.notWechat);
this.handleError(err, item);
reject(err);
return;
}
// 检查基础库版本
const systemInfo = wx.getSystemInfoSync();
const SDKVersion = systemInfo.SDKVersion;
if (this.versionCompare(SDKVersion, wechatChannelConfig.wechat.minSdkVersion) < 0) {
const err = new Error(wechatChannelConfig.error.lowVersion);
this.handleError(err, item);
reject(err);
return;
}
// 调用微信视频号播放API
if (wx.openChannelsActivity) {
wx.openChannelsActivity({
feedId: item.feedId,
finderUserName: item.finderUserName,
success: (res) => {
resolve(res);
},
fail: (err) => {
this.handleError(err, item);
reject(err);
}
});
} else {
const err = new Error(wechatChannelConfig.error.notSupported);
this.handleError(err, item);
reject(err);
}
});
},
// 统一错误处理
handleError(err, item) {
console.error('微信视频号错误:', err);
const { message = '', errMsg = '微信视频播放失败!', errCode = -1 } = err;
// 错误码5: 表示用户点击了取消. openChannelsActivity:fail cancel
if ([5].includes(errCode)) {
return;
}
const showErrorToast = (otherMsgs = []) => {
uni.showToast({
title: [errMsg, message,...otherMsgs].join('\n'),
icon: 'none',
duration: 2000
});
}
showErrorToast();
}
};

View File

@@ -2,8 +2,20 @@ export default {
methods: {
// 异步触发事件
async __$emitEvent(payload = {eventName: '__unnamedEvent', data: {}, promiseCallback: null}) {
// console.log('__$emitEvent', payload)
await this.$eventBus.emit(payload.eventName, payload.data, payload.promiseCallback)
},
// 预览图片
previewImg(img) {
// #ifdef MP-WEIXIN
uni.previewImage({
current: 0,
urls: [this.$util.img(img)],
success: function (res) { },
fail: function (res) { },
complete: function (res) { },
})
// #endif
},
}
}

View File

@@ -1,6 +0,0 @@
{
"component": true,
"usingComponents": {
"ns-loading": "../ns-loading/ns-loading"
}
}

View File

@@ -13,14 +13,14 @@
<view class="message-item " :class="message.isItMe ? 'right' : 'left'">
<block v-if="message.isItMe">
<view class="head_img">
<image class="img" :src="myHeadImg" v-if="myHeadImg" @error="myHeadImgError" mode="aspectFit"/>
<image class="img" :src="defaultHead" mode="aspectFit" v-else/>
<image class="img" :src="myHeadImg" v-if="myHeadImg" @error="myHeadImgError" mode="aspectFit" show-menu-by-longpress/>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress/>
</view>
</block>
<block v-else>
<view class="head_img">
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<view class="chat_text">
@@ -34,20 +34,20 @@
<view class="message-item " :class="message.isItMe ? 'right' : 'left'">
<block v-if="message.isItMe">
<view class="head_img">
<image class="img" :src="myHeadImg" v-if="myHeadImg" mode="aspectFit"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="myHeadImg" v-if="myHeadImg" mode="aspectFit" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<block v-else>
<view class="head_img">
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<view class="chat_img">
<text class="iconfont icon-warn margin-right color-base-text" v-if="message.isItMe && !message.sendStatus"></text>
<view class="content_img" @click="previewMedia($util.img(message.image))" :style="{ backgroundImage: 'url(' + $util.img(message.image) + ')' }">
<!-- <image class="img_img" :src="$util.img(message.image)" mode="aspectFit"></image> -->
<!-- <image class="img_img" :src="$util.img(message.image)" mode="aspectFit" show-menu-by-longpress></image> -->
</view>
<!-- <text class="iconfont icon-warn margin-left" v-if="!message.isItMe && !message.sendStatus"></text> -->
</view>
@@ -64,7 +64,7 @@
</view>
<uni-popup ref="imgPopup" type="center">
<view class="imagePop">
<image :src="$util.img(currImg)" mode="aspectFit"></image>
<image :src="$util.img(currImg)" mode="aspectFit" show-menu-by-longpress></image>
</view>
</uni-popup>
</view>
@@ -156,11 +156,11 @@
</script>
<style lang="scss">
/deep/.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
background-color: #000;
}
/deep/.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
max-width: 100%;
width: 100%;
}
@@ -179,6 +179,7 @@
.chat-message {
width: 100%;
height: 100%;
background: white; /* 白色 */
.message {
padding: 13rpx 20rpx;
@@ -270,7 +271,7 @@
flex-direction: row-reverse;
.content {
background-color: #4cd964;
background-color: #c4e0ff; /* 浅蓝色 */
margin-right: 28rpx;
word-break: break-all;
line-height: 36rpx;

View File

@@ -1,128 +1,211 @@
<template>
<!-- 悬浮按钮 -->
<view v-if="pageCount == 1 || need" class="fixed-box"
:style="[{ height: fixBtnShow ? '400rpx' : '320rpx' }, customContainerStyle]">
<!-- <view class="btn-item" v-if="fixBtnShow" @click="$util.redirectTo('/pages/index/index')"> -->
<!-- 切换语言按钮 -->
<button class="btn-item" v-if="fixBtnShow && isLanguageSwitchEnabled" @click="toggleLanguage()"
:style="[{ backgroundSize: '100% 100%' }, customButtonStyle]">
<text :style="customTextStyle">{{ currentLangDisplayName }}</text>
</button>
<!-- 客服按钮 -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-item" v-if="fixBtnShow" hoverClass="none" openType="contact" sessionFrom="weapp"
showMessageCard="true"
:style="[{ backgroundImage: 'url(' + (kefuimg ? kefuimg : '') + ')', backgroundSize: '100% 100%' }, customButtonStyle]">
<view v-if="pageCount === 1 || need" class="fixed-box"
:style="[customContainerStyle, {
height: containerHeight,
backgroundImage: bgUrl ? `url(${bgUrl})` : '',
backgroundSize: 'cover'
}]">
<!-- 中英文切换按钮 -->
<view
v-if="isLanguageSwitchEnabled && fixBtnShow"
class="btn-item common-bg"
@click="toggleLanguage"
>
<text>{{ currentLangDisplayName }}</text>
</view>
<!-- 微信官方客服按钮-->
<button
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
class="btn-item common-bg"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
<!-- <view>首页</view> -->
</button>
<!-- #endif -->
<view class="btn-item" v-if="fixBtnShow" @click="call()"
:style="[{ backgroundImage: 'url(' + (phoneimg ? phoneimg : '') + ')', backgroundSize: '100% 100%' }, customButtonStyle]">
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
<!-- <view>我的</view> -->
<!-- 其他类型客服按钮(智能客服、企业微信等)-->
<view
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
class="btn-item common-bg"
@click="handleUnifiedKefuClick"
>
<image
v-if="currentKefuImg"
:src="currentKefuImg"
mode="aspectFill"
class="kefu-img"
show-menu-by-longpress></image>
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
</view>
<!-- <view class="btn-item icon-xiala" v-if="fixBtnShow" @click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
<text class="iconfont icon-unfold"></text>
<!-- 智能客服按钮(当同时显示小程序系统客服时)-->
<view
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
class="btn-item common-bg"
@click="handleUnifiedKefuClick"
>
<image
v-if="currentKefuImg"
:src="currentKefuImg"
mode="aspectFill"
class="kefu-img"
show-menu-by-longpress></image>
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
</view>
<view class="btn-item switch" v-else :class="{ show: fixBtnShow }"
@click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
<view class="">快捷</view>
<view>导航</view>
</view> -->
<!-- 小程序系统客服按钮 -->
<button
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
class="btn-item common-bg"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- 电话按钮 -->
<view
v-if="fixBtnShow"
class="btn-item common-bg"
@click="call"
:style="[{ backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
</view>
</view>
</template>
<script>
import { createCustomerService } from '@/common/js/customer-service.js';
export default {
name: 'hover-nav',
props: {
need: {
type: Boolean,
default: false
}
need: { type: Boolean, default: false }
},
data() {
return {
pageCount: 0,
fixBtnShow: true,
tel: '',
kefuimg: '',
phoneimg: '',
shopInfo: null, // 店铺信息对象
// --- 语言切换相关 ---
currentLangIndex: 0, // 当前选中的语言索引
langIndexMap: {}, // 语言索引到语言值的映射
isLanguageSwitchEnabled: false, // 是否启用语言切换
// --- 其他 ---
shopInfo: null,
currentLangIndex: 0,
langIndexMap: {},
customerService: null,
};
},
computed: {
bgUrl() {
return this.shopInfo?.bgUrl || '';
},
aiAgentimg() {
return this.shopInfo?.aiAgentimg || '';
},
kefuimg() {
return this.shopInfo?.kefuimg || this.$util.getDefaultImage().kefu;
},
phoneimg() {
return this.shopInfo?.phoneimg || this.$util.getDefaultImage().phone;
},
tel() {
return this.shopInfo?.mobile || '';
},
isLanguageSwitchEnabled() {
return !!this.shopInfo?.ischina;
},
currentLangDisplayName() {
const lang = this.langIndexMap[this.currentLangIndex];
return lang === 'zh-cn' ? 'EN' : 'CN';
},
customContainerStyle() {
return this.shopInfo?.floatingButton?.container || {};
},
customButtonStyle() {
return this.shopInfo?.floatingButton?.button || {};
},
unreadCount() {
const customerService = createCustomerService(this);
return customerService.getUnreadCount();
},
currentKefuImg() {
const customerService = createCustomerService(this);
return customerService.getKefuButtonIcon();
},
isWeappOfficialKefu() {
const customerService = createCustomerService(this);
return customerService.isWeappOfficialKefu();
},
isAIKefu() {
const customerService = createCustomerService(this);
return customerService.isAIKefu();
},
isWxworkKefu() {
const customerService = createCustomerService(this);
return customerService.isWxworkKefu();
},
isThirdKefu() {
const customerService = createCustomerService(this);
return customerService.isThirdKefu();
},
showWeappSystemKefu() {
const customerService = createCustomerService(this);
return customerService.shouldShowExtraWeappSystemKefu();
},
hasValidKefuConfig() {
const customerService = createCustomerService(this);
return customerService.hasValidKefuConfig();
},
//根据显示的按钮数量动态计算容器高度
containerHeight() {
if (!this.fixBtnShow) return '320rpx';
let buttonCount = 1;
if (this.isLanguageSwitchEnabled) buttonCount++;
if (this.showWeappSystemKefu) buttonCount++;
buttonCount++;
const totalRpx = 94 * buttonCount - 14;
const pxValue = Math.round(totalRpx * 0.5);
return ` $ {pxValue}px`;
}
},
watch: {
shopInfo: {
handler(newVal) {
},
immediate: true
}
},
created() {
// 初始化语言设置
this.initLanguage();
this.kefuimg = this.$util.getDefaultImage().kefu
this.phoneimg = this.$util.getDefaultImage().phone
this.pageCount = getCurrentPages().length;
// 从店铺信息中获取相关信息
uni.getStorage({
key: 'shopInfo',
success: (e) => {
console.log(`获取到的shopInfo:${JSON.stringify(e.data)}`)
this.shopInfo = e.data;
// 从店铺信息中获取手机号
this.tel = e.data.mobile
// 从店铺信息中获取是否启用语言切换
this.isLanguageSwitchEnabled = e.data.ischina;
}
})
},
computed: {
currentLangDisplayName() {
// 语言切换按钮显示名称,当前语言为中文时显示EN,否则显示CN
// 根据 langIndexMap 获取当前语言值
const lang = this.langIndexMap[this.currentLangIndex];
return lang == 'zh-cn' ? 'EN' : 'CN';
},
// 获取容器的自定义样式
customContainerStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.container || {};
},
// 获取按钮的自定义样式
customButtonStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.button || {};
},
// 获取文本的自定义样式
customTextStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.text || {};
},
fail: () => {
console.warn('未获取到 shopInfo,使用默认设置');
}
});
// 初始化 customerService 实例
this.customerService = createCustomerService(this);
},
methods: {
initLanguage() {
// 初始化语言列表
this.langList = this.$langConfig.list();
// 初始化语言索引映射
this.langIndexMap = {};
for (let i = 0; i < this.langList.length; i++) {
this.langIndexMap[i] = this.langList[i].value;
}
// 获取存储的语言设置
const savedLang = uni.getStorageSync('lang');
const savedLang = this.$langConfig.getCurrentLocale();
if (savedLang) {
for (let i = 0; i < this.langList.length; i++) {
if (this.langList[i].value == savedLang) {
if (this.langList[i].value === savedLang) {
this.currentLangIndex = i;
break;
}
@@ -131,55 +214,44 @@ export default {
this.currentLangIndex = 0;
}
},
//拨打电话
call() {
uni.makePhoneCall({
phoneNumber: this.tel + ''
})
this.customerService.makePhoneCallByNumber(this.tel);
},
toggleLanguage() {
// 切换语言索引
this.currentLangIndex = this.currentLangIndex == 0 ? 1 : 0;
this.currentLangIndex = this.currentLangIndex === 0 ? 1 : 0;
const targetLang = this.langIndexMap[this.currentLangIndex];
// 获取当前页面路由
let currentRoute = this.$util.getCurrentRoute().path;
// 切换语言并重新加载当前页面
this.$langConfig.change(targetLang, currentRoute);
uni.setStorageSync("lang", targetLang);
}
}
};
</script>
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentRoute = currentPage.route;
<style lang="scss">
.container-box {
width: 100%;
uni.reLaunch({
url: '/' + currentRoute
});
},
.item-wrap {
border-radius: 10rpx;
.image-box {
border-radius: 10rpx;
}
image {
width: 100%;
height: auto;
border-radius: 10rpx;
will-change: transform;
handleUnifiedKefuClick() {
this.customerService.handleUnifiedKefuClick({
sendMessageTitle: '来自悬浮按钮的咨询',
sendMessagePath: '/pages/index/index'
});
}
}
}
</script>
//悬浮按钮
<style lang="scss" scoped>
.fixed-box {
position: fixed;
right: 0rpx;
bottom: 240rpx;
// #ifdef H5
/* #ifdef H5 */
bottom: 320rpx;
// #endif
/* #endif */
z-index: 10;
border-radius: 120rpx;
padding: 20rpx 0;
@@ -190,8 +262,9 @@ export default {
box-sizing: border-box;
transition: 0.3s;
overflow: hidden;
}
.btn-item {
.btn-item {
display: flex;
justify-content: center;
text-align: center;
@@ -199,19 +272,24 @@ export default {
line-height: 1;
margin: 14rpx 0;
transition: 0.1s;
background: var(--hover-nav-bg-color);
color: var(--hover-nav-text-color);
border-radius: 40rpx;
border-radius: 50%;
width: 80rpx;
height: 80rpx;
padding: 0;
overflow: hidden;
}
text {
.common-bg {
background-color: var(--hover-nav-bg-color);
}
.btn-item text {
font-size: 28rpx;
}
.iconfont,
.icox {
}
.iconfont,
.icox {
font-size: 36rpx;
font-weight: bold;
width: 100%;
@@ -219,25 +297,33 @@ export default {
display: flex;
justify-content: center;
align-items: center;
border-radius: 40rpx;
/* 确保图标本身也是圆形 */
}
margin: 14rpx 0;
border-radius: 50rpx;
width: 80rpx;
height: 80rpx;
padding: 0;
position: relative;
}
view {
font-size: 26rpx;
.iconfont text,
.icox text {
font-size: 36rpx;
font-weight: bold;
}
}
&.show {
transform: rotate(180deg);
}
.kefu-img {
width: 100%;
height: 100%;
display: block;
border-radius: 50%;
}
&.switch {}
&.icon-xiala {
margin: 0;
margin-top: 0.1rpx;
}
}
.ai-icon {
font-size: 40rpx;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -8,7 +8,7 @@
:src="mOption.src"
mode="widthFix"
@click="toTopClick"
/>
show-menu-by-longpress/>
</template>
<script>

View File

@@ -3,12 +3,12 @@
<swiper :indicator-dots="advList.length > 1" indicator-active-color="#ffffff" :autoplay="true" :interval="3000" :duration="1000" v-if="advList.length > 1" @change="changeSwiper" :current="currentIndex" :style="{ height: swiperHeight + 'px' }" class="item-wrap">
<swiper-item v-for="(item, index) in advList" :key="index" @click="jumppage(item.adv_url)">
<view class="image-box">
<image :src="$util.img(item.adv_image)" mode="widthFix" :id="'content-wrap' + index"/>
<image :src="$util.img(item.adv_image)" mode="widthFix" :id="'content-wrap' + index" show-menu-by-longpress/>
</view>
</swiper-item>
</swiper>
<view v-else class="container-box item-wrap">
<image :src="$util.img(advList[0]['adv_image'])" mode="widthFix" lazy-load="true" @load="imageLoad" @click="jumppage(advList[0].adv_url)"/>
<image :src="$util.img(advList[0]['adv_image'])" mode="widthFix" lazy-load="true" @load="imageLoad" @click="jumppage(advList[0].adv_url)" show-menu-by-longpress/>
</view>
</view>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<view class="message">
<view class="goods-item" v-if="goodsInfo.goods_name || goodsInfo.en_goods_name">
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ goodsInfo.sku_name ? goodsInfo.sku_name : (isEnEnv ? goodsInfo.en_goods_name : goodsInfo.goods_name) }}</view>
<view class="goods-bottom">
@@ -14,7 +14,7 @@
</view>
</view>
<view class="goods-item" v-else-if="goodsDetail">
<image :src="$util.img(goodsDetail.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsDetail.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ goodsDetail.sku_name ? goodsDetail.sku_name : (isEnEnv ? goodsDetail.en_goods_name : goodsDetail.goods_name) }}</view>
<view class="goods-bottom">

View File

@@ -1,7 +1,7 @@
<template>
<view class="message">
<view class="goods-item" v-if="orderdetails">
<image :src="$util.img(orderdetails.order_goods ? orderdetails.order_goods[0].sku_image : '')" mode="aspectFill"></image>
<image :src="$util.img(orderdetails.order_goods ? orderdetails.order_goods[0].sku_image : '')" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ orderdetails.order_goods ? orderdetails.order_goods[0].sku_name : '' }}</view>
<view class="font-size-goods-tag">订单状态:{{ orderdetails.order_status_name }}</view>
@@ -16,7 +16,7 @@
</view>
</view>
<view class="goods-item" v-else-if="orderInfo">
<image :src="$util.img(orderInfo.order_goods ? orderInfo.order_goods[0].sku_image : '')" mode="aspectFill"></image>
<image :src="$util.img(orderInfo.order_goods ? orderInfo.order_goods[0].sku_image : '')" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ orderInfo.order_goods ? orderInfo.order_goods[0].sku_name : '' }}</view>
<view class="font-size-goods-tag">订单状态:{{ orderInfo.order_status_name }}</view>

View File

@@ -1,7 +1,7 @@
<template>
<view class="goods">
<view class="goods-msg">
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-item">
<view class="title">{{ isEnEnv ? goodsInfo.en_goods_name : goodsInfo.goods_name }}</view>
<view class="goods-sku">

View File

@@ -3,28 +3,37 @@
<slot></slot>
<!-- #ifdef MP-ALIPAY -->
<view class="contact-button" @click="contactServicer">
<contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx" v-if="config.type == 'aliapp'"/>
<contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx"
v-if="config.type == 'aliapp'" />
</view>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<button
type="default"
hover-class="none"
:open-type="openType"
class="contact-button"
@click="contactServicer"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="true"></button>
<!-- #ifdef MP-WEIXIN -->
<!-- 微信小程序官方客服按钮 -->
<button v-if="useOfficialService" type="default" hover-class="none" open-type="contact"
class="contact-button" sessionFrom="weapp" showMessageCard="true"
:send-message-title="sendMessageTitle" :send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"></button>
<!-- 微信小程序自定义客服按钮 -->
<button v-else type="default" hover-class="none" class="contact-button" @click="contactServicer"></button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN && MP-ALIPAY -->
<!-- 其他平台保持原逻辑 -->
<button type="default" hover-class="none" :open-type="openType" class="contact-button" @click="contactServicer"
:send-message-title="sendMessageTitle" :send-message-path="sendMessagePath"
:send-message-img="sendMessageImg" :show-message-card="true"></button>
<!-- #endif -->
<uni-popup ref="servicePopup" type="center">
<view class="service-popup-wrap">
<view class="head-wrap" @click="$refs.servicePopup.close()">
<text>联系客服</text>
<text class="iconfont icon-close"></text>
</view>
<view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}</view>
<view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}
</view>
</view>
</uni-popup>
</view>
@@ -32,12 +41,14 @@
<!-- 客服组件 -->
<script>
export default {
import { createCustomerService } from '@/common/js/customer-service.js';
export default {
name: 'ns-contact',
props: {
niushop: {
type: Object,
default: function() {
default: function () {
return {};
}
},
@@ -56,83 +67,71 @@
},
data() {
return {
config: null,
openType: ''
customerService: null,
buttonConfig: null
};
},
created() {
if (this.servicerConfig) {
// #ifdef H5
this.config = this.servicerConfig.h5;
// #endif
computed: {
/**
* 是否使用官方客服
*/
useOfficialService() {
if (!this.buttonConfig) return true;
// #ifdef MP-WEIXIN
this.config = this.servicerConfig.weapp;
if (this.config.type == 'weapp') this.openType = 'contact';
// 如果是微信小程序,检查配置
if (this.buttonConfig.type === 'weapp') {
// 默认使用官方客服,除非明确设置为false
return this.buttonConfig.useOfficial !== false;
}
// #endif
// #ifdef MP-ALIPAY
this.config = this.servicerConfig.aliapp;
if (this.config.type == 'aliapp') this.openType = 'contact';
// #endif
return false;
},
/**
* 客服配置
*/
config() {
return this.customerService?.getPlatformConfig() || {};
},
/**
* 打开类型
*/
openType() {
return this.buttonConfig?.openType || '';
}
},
created() {
// 初始化客服服务
this.customerService = createCustomerService(this);
this.buttonConfig = this.customerService.getButtonConfig();
},
methods: {
/**
* 联系客服
*/
contactServicer() {
if (this.config.type == 'none') {
this.$refs.servicePopup.open();
// 如果是微信/支付宝小程序客服,由系统自动处理
if (this.buttonConfig.openType === 'contact') {
return;
}
if (this.openType == 'contact') return;
switch (this.config.type) {
case 'wxwork':
// #ifdef H5
location.href = this.config.wxwork_url;
// #endif
// #ifdef MP-WEIXIN
wx.openCustomerServiceChat({
extInfo: { url: this.config.wxwork_url },
corpId: this.config.corpid,
showMessageCard: true,
// 使用统一客服处理
this.customerService.handleCustomerClick({
niushop: this.niushop,
sendMessageTitle: this.sendMessageTitle,
sendMessagePath: this.sendMessagePath,
sendMessageImg: this.sendMessageImg
});
// #endif
break;
case 'third':
location.href = this.config.third_url;
break;
case 'niushop':
this.$util.redirectTo('/pages_tool/chat/room', this.niushop);
break;
default:
this.makePhoneCall();
}
},
/**
* 店铺联系方式
*/
makePhoneCall() {
this.$api.sendRequest({
url: '/api/site/shopcontact',
success: res => {
if (res.code == 0 && res.data.mobile) uni.makePhoneCall({
phoneNumber: res.data
.mobile
});
}
});
}
}
};
};
</script>
<style lang="scss">
.contact-wrap {
.contact-wrap {
width: 100%;
height: 100%;
position: relative;
@@ -149,9 +148,9 @@
opacity: 0;
overflow: hidden;
}
}
}
.service-popup-wrap {
.service-popup-wrap {
width: 600rpx;
.head-wrap {
@@ -167,5 +166,5 @@
padding: 30rpx;
height: 100rpx;
}
}
}
</style>

View File

@@ -10,7 +10,7 @@
</view>
<view v-if="copyright && copyright.gov_record" class="gov-wrap" @click="toHref(copyright.gov_url)" target="_blank">
<image :src="$util.img('public/uniapp/common/gov_record.png')" alt="公安备案" />
<image :src="$util.img('public/uniapp/common/gov_record.png')" alt="公安备案" show-menu-by-longpress/>
<text>{{ copyright.gov_record }}</text>
</view>
</view> -->

View File

@@ -1,6 +1,6 @@
<template>
<view class="empty" :class="{ fixed: fixed }">
<view class="empty_img"><image :src="$util.img('public/uniapp/common/common-empty.png')" mode="aspectFit"></image></view>
<view class="empty_img"><image :src="$util.img('public/uniapp/common/common-empty.png')" mode="aspectFit" show-menu-by-longpress></image></view>
<view class="color-tip margin-top title" :style="{ color: textColor + ' !important' }">{{ text }}</view>
<view class="color-tip margin-bottom font-size-sub">{{ subText }}</view>
<button type="primary" size="mini" class="button mini" @click="goIndex()" v-if="isIndex">{{ emptyBtn.text }}</button>
@@ -45,7 +45,7 @@ export default {
if (this.emptyBtn.url) {
this.$util.redirectTo(this.emptyBtn.url, {}, 'redirectTo');
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
},
re(text) {

View File

@@ -89,7 +89,7 @@
</view>
<view class="box img-boxs">
<view class="img-box" v-for="(v, k) in item.img_lists" :key="k" @click="uploadImg(index)">
<image :src="$util.img(v)" mode="aspectFill"></image>
<image :src="$util.img(v)" mode="aspectFill" show-menu-by-longpress></image>
<text class="iconfont icon-guanbi" @click.stop="delImg(k, index)"></text>
</view>
<view class="img-box" @click="addImg(index)"><text class="iconfont icon-add1"></text></view>

View File

@@ -6,7 +6,7 @@
<view class="sku-info" :style="{ height: systemInfo.windowHeight * 2 + 'rpx' }">
<view class="header">
<view class="img-wrap" @click="previewMedia()">
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" />
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" show-menu-by-longpress/>
</view>
<view class="main">
<view class="goodname">{{ goodsDetail.sku_name ? goodsDetail.sku_name : (isEnEnv ?
@@ -33,7 +33,7 @@
@click="change(item_value.sku_id, item_value.spec_id)">
<image v-if="item_value.image"
:src="$util.img(item_value.image, { size: 'small' })"
@error="valueImageError(index, index_value)" />
@error="valueImageError(index, index_value)" show-menu-by-longpress/>
<text>{{ item_value.spec_value_name }}</text>
</view>
</view>
@@ -843,15 +843,15 @@ export default {
z-index: 5;
}
/deep/ .uni-popup__wrapper {
::v-deep .uni-popup__wrapper {
background: transparent !important;
}
/deep/ .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
background: transparent !important;
}
/deep/ .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
background: transparent !important;
}
</style>

View File

@@ -28,7 +28,7 @@ export default {
*/
addCart(config, data, event) {
if (!this.storeToken) {
this.$refs.login.open('/pages/index/index')
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
return;
}
if (config == "detail" || data.is_virtual) {

View File

@@ -5,14 +5,14 @@
<view class="header">
<block v-if="type == 'point' && goodsDetail.type && goodsDetail.type != 1">
<view class="img-wrap" v-if="goodsDetail.type == 2">
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/coupon.png')" mode="aspectFit" />
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/coupon.png')" mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="img-wrap" v-if="goodsDetail.type == 3">
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/hongbao.png')" mode="aspectFit" />
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/hongbao.png')" mode="aspectFit" show-menu-by-longpress/>
</view>
</block>
<view v-else class="img-wrap" @click="previewMedia()">
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFit" />
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="main">
@@ -151,7 +151,7 @@
selected: item_value['selected'] || skuId == item_value.sku_id,
disabled: item_value['disabled'] || (!item_value['selected'] && disabled)
}" class="items" @click="change(item_value.sku_id, item_value.spec_id)">
<image v-if="item_value.image" :src="$util.img(item_value.image, { size: 'small' })" @error="valueImageError(index, index_value)" />
<image v-if="item_value.image" :src="$util.img(item_value.image, { size: 'small' })" @error="valueImageError(index, index_value)" show-menu-by-longpress/>
<text>{{ item_value.spec_value_name }}</text>
</view>
</view>
@@ -413,7 +413,7 @@
this.isLoad = false;
if (callback) callback();
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -441,7 +441,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -467,7 +467,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -500,7 +500,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -527,7 +527,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -553,7 +553,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -579,7 +579,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -606,7 +606,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});
@@ -630,7 +630,7 @@
this.skuId = res.data[0].sku_id;
this.goodsSkuInfo = obj;
} else {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}
}
});

View File

@@ -0,0 +1,80 @@
<template>
<image
:src="src"
:mode="mode"
:lazy-load="lazyLoad"
:fade-show="fadeShow"
:webp="webp"
:show-menu-by-longpress="enableLongpress"
:class="imageClass"
:style="imageStyle"
@load="onLoad"
@error="onError"
@tap="onClick"
>
<slot></slot>
</image>
</template>
<script>
export default {
name: 'NsImage',
props: {
// 图片路径
src: {
type: String,
required: true
},
// 图片裁剪模式
mode: {
type: String,
default: 'aspectFill'
},
// 是否开启长按识别二维码(默认开启)
enableLongpress: {
type: Boolean,
default: true
},
// 是否懒加载
lazyLoad: {
type: Boolean,
default: false
},
// 图片显示动画
fadeShow: {
type: Boolean,
default: true
},
// 支持webp
webp: {
type: Boolean,
default: false
},
// 自定义类名
imageClass: {
type: [String, Array, Object],
default: ''
},
// 自定义样式
imageStyle: {
type: [String, Object],
default: ''
}
},
methods: {
onLoad(e) {
this.$emit('load', e)
},
onError(e) {
this.$emit('error', e)
},
onClick(e) {
this.$emit('tap', e)
}
}
}
</script>
<style lang="scss" scoped>
/* 可添加全局图片样式 */
</style>

View File

@@ -18,7 +18,7 @@
<view class="item-wrap" v-if="isOpenCaptcha">
<text class="label">验证码</text>
<input type="number" placeholder="请输入验证码" v-model="formData.vercode" maxlength="4" />
<image :src="captcha.img" class="captcha" @click="getCaptcha"></image>
<image :src="captcha.img" class="captcha" @click="getCaptcha" show-menu-by-longpress></image>
</view>
<view class="item-wrap">
<text class="label">动态码</text>
@@ -42,26 +42,22 @@
<!-- 协议勾选 -->
<view style="display: flex;">
<view
class="iconfont"
<view class="iconfont"
:class="isAgree ? 'icon-fuxuankuang1 color-base-text' : 'icon-fuxuankuang2'"
@click="isAgree = !isAgree"
></view>
@click="isAgree = !isAgree"></view>
<view style="text-align: left; margin-left: 10rpx; padding-top: 2rpx; font-size: 28rpx;">
我同意
<text style="color: #4395ff;" @click="tourl('/pages_tool/agreement/contenr?type=0')">《隐私条款》</text>
<text style="color: #4395ff;" @click="tourl('/pages_tool/agreement/contenr?type=1')">《用户服务协议》</text>
<text style="color: #4395ff;"
@click="tourl('/pages_tool/agreement/contenr?type=0')">《隐私条款》</text>
<text style="color: #4395ff;"
@click="tourl('/pages_tool/agreement/contenr?type=1')">《用户服务协议》</text>
</view>
</view>
<!-- 手机号授权按钮 -->
<block v-if="isAgree">
<button
class="auth-login save-btn border-0"
style="border: none;"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
<button class="auth-login save-btn border-0" style="border: none;" open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber">
<text style="color: #fff; font-size: 30rpx;">同意隐私协议并授权手机号</text>
</button>
</block>
@@ -71,7 +67,8 @@
</button>
</block>
<view @click="closeLogin" style="font-size: 30rpx; color: #888; text-align: center; padding-top: 10rpx;">
<view @click="closeLogin"
style="font-size: 30rpx; color: #888; text-align: center; padding-top: 10rpx;">
暂不登录
</view>
@@ -82,7 +79,7 @@
<text class="label">头像</text>
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -99,7 +96,7 @@
<button open-type="getAuthorize" scope="userInfo" @getAuthorize="aliappGetUserinfo"
:plain="true" class="border-0">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -254,7 +251,7 @@ export default {
},
// 打开弹窗
open(url) {
open(url = '', mode = 'navigateTo') {
if (url) this.url = url;
// #ifdef MP
this.getCode(authData => {
@@ -266,28 +263,33 @@ export default {
if (this.$util.isWeChatMiniProgram()) {
// 微信小程序环境,使用微信授权登录
let authData = uni.getStorageSync('authInfo');
if (authData) this.authLogin(authData);
else this.getCode();
if (authData) {
this.authLogin(authData);
} else {
this.getCode(authData => {
this.authLogin(authData, 'authOnlyLogin');
});
}
} else if (this.$util.isWeiXin()) {
// 微信浏览器环境,显示登录页面
this.toLogin();
this.toLogin(mode);
} else {
// 其他浏览器环境,显示登录页面
this.toLogin();
this.toLogin(mode);
}
// #endif
// #ifndef MP || H5
this.toLogin();
this.toLogin(mode);
// #endif
},
// 跳转去登录页
toLogin() {
if (this.url) this.$util.redirectTo('/pages_tool/login/login', {
toLogin(mode = 'navigateTo') {
if (this.url) this.$util.redirectTo(this.$util.LOGIN_PAGE_URL, {
back: encodeURIComponent(this.url)
});
else this.$util.redirectTo('/pages_tool/login/login');
}, mode);
else this.$util.redirectTo(this.$util.LOGIN_PAGE_URL, {}, mode);
},
// 取消完善信息
@@ -535,7 +537,8 @@ export default {
getPhoneNumber(e) {
if (e.detail.errMsg === 'getPhoneNumber:ok') {
const authInfo = uni.getStorageSync('authInfo');
if (authInfo) { Object.assign(this.authMobileData, authInfo, e.detail);
if (authInfo) {
Object.assign(this.authMobileData, authInfo, e.detail);
}
if (uni.getStorageSync('source_member')) {
this.authMobileData.source_member = uni.getStorageSync('source_member');

View File

@@ -17,7 +17,7 @@
<style lang="scss">
.mp-html{
/deep/ img{
::v-deep img{
width:100% !important;
display:block;
}

View File

@@ -16,7 +16,7 @@
<view v-if="data.navStyle == 2" class="content-wrap" @click="toLink(data.moreLink.wap_url)">
<view class="title-wrap" :style="{ color: data.textNavColor }">
<view>
<image :src="$util.img(data.topNavImg)" mode="heightFix"></image>
<image :src="$util.img(data.topNavImg)" mode="heightFix" show-menu-by-longpress></image>
</view>
<view :style="{ color: data.textNavColor }">{{ data.title }}</view>
</view>
@@ -24,7 +24,7 @@
<view v-if="data.navStyle == 3" class="content-wrap">
<view class="title-wrap" @click="toLink(data.moreLink.wap_url)">
<image :src="$util.img(data.topNavImg)" mode="aspectFit"></image>
<image :src="$util.img(data.topNavImg)" mode="aspectFit" show-menu-by-longpress></image>
</view>
<view class="search" @click="$util.redirectTo('/pages_tool/goods/search')" :style="{ height: menuButtonInfo.height - 2 + 'px', lineHeight: menuButtonInfo.height - 2 + 'px' }">
<text class="iconfont icon-sousuo3"></text>

View File

@@ -185,7 +185,7 @@ export default {
title: '未获取到支付信息!'
});
setTimeout(() => {
this.$util.redirectTo('/pages/index/index');
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
}, 1500);
}
}

View File

@@ -0,0 +1,145 @@
<template>
<view class="video-player-popup">
<uni-popup ref="videoPopup" type="center" :mask-click="true" @change="onVideoPopupChange">
<view class="video-popup-content">
<view class="video-popup-header">
<text class="video-popup-title">{{ videoTitle }}</text>
<text class="iconfont icon-close" @click="close"></text>
</view>
<view class="video-popup-body">
<video
v-if="videoUrl"
:src="videoUrl"
controls
autoplay
class="video-player"
></video>
<view v-else class="video-error">视频地址不存在</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
name: 'ns-video-player-popup',
props: {
/**
* 当前播放的视频信息
* @type {Object}
* @property {string} title - 视频标题
* @property {string} videoUrl - 视频地址
*/
currentVideo: {
type: Object,
default: null
}
},
computed: {
videoUrl() {
return this.currentVideo?.videoUrl || '';
},
videoTitle() {
return this.currentVideo?.title || '';
},
/**
* 视频弹窗是否显示
* @type {boolean}
*/
isVisible() {
return this.$refs.videoPopup?.visible || false;
}
},
methods: {
/**
* 打开视频弹窗
*/
open() {
if (this.$refs.videoPopup) {
this.$refs.videoPopup.open();
}
},
/**
* 关闭视频弹窗
*/
close() {
if (this.$refs.videoPopup) {
this.$refs.videoPopup.close();
}
},
/**
* 视频弹窗状态变化
* @param {Object} e - 弹窗状态变化事件
*/
onVideoPopupChange(e) {
this.$emit('popup-change', e);
}
}
};
</script>
<style lang="scss" scoped>
/* 视频播放弹窗样式 */
.video-player-popup {
.video-popup-content {
width: 90%;
max-width: 600rpx;
background-color: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
}
.video-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fafafa;
.video-popup-title {
font-size: 28rpx;
font-weight: 600;
color: #333;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon-close {
font-size: 32rpx;
color: #999;
padding: 0 10rpx;
}
}
.video-popup-body {
padding: 20rpx;
.video-player {
width: 100%;
height: 400rpx;
background-color: #000;
border-radius: 8rpx;
}
.video-error {
width: 100%;
height: 400rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 8rpx;
color: #999;
font-size: 28rpx;
}
}
}
</style>

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